Print this page
NEX-14295 'bootadm update-archive' silently fails if /etc/system.d/ doesn't exist
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Alexander Eremin <alexander.eremin@nexenta.com>
NEX-13052 bootadm misses changes in existing /etc/system.d/ files (message fix)
NEX-13052 bootadm misses changes in existing /etc/system.d/ files
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Alex Deiter <alex.deiter@nexenta.com>
NEX-10318 add /etc/system.d support
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Cynthia Eastham <cynthia.eastham@nexenta.com>
NEX-3025 support root pools on EFI labeled disks
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/boot/bootadm/bootadm.c
+++ new/usr/src/cmd/boot/bootadm/bootadm.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
|
↓ open down ↓ |
16 lines elided |
↑ open up ↑ |
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 * Copyright 2012 Milan Jurik. All rights reserved.
25 25 * Copyright (c) 2015 by Delphix. All rights reserved.
26 26 * Copyright 2016 Toomas Soome <tsoome@me.com>
27 - * Copyright 2016 Nexenta Systems, Inc.
27 + * Copyright 2017 Nexenta Systems, Inc.
28 28 */
29 29
30 30 /*
31 31 * bootadm(1M) is a new utility for managing bootability of
32 32 * Solaris *Newboot* environments. It has two primary tasks:
33 33 * - Allow end users to manage bootability of Newboot Solaris instances
34 34 * - Provide services to other subsystems in Solaris (primarily Install)
35 35 */
36 36
37 37 /* Headers */
38 38 #include <stdio.h>
39 39 #include <errno.h>
40 40 #include <stdlib.h>
41 41 #include <string.h>
42 42 #include <unistd.h>
43 43 #include <sys/types.h>
44 44 #include <sys/stat.h>
45 45 #include <alloca.h>
46 46 #include <stdarg.h>
47 47 #include <limits.h>
48 48 #include <signal.h>
49 49 #include <sys/wait.h>
50 50 #include <sys/mnttab.h>
51 51 #include <sys/mntent.h>
52 52 #include <sys/statvfs.h>
53 53 #include <libnvpair.h>
54 54 #include <ftw.h>
55 55 #include <fcntl.h>
56 56 #include <strings.h>
57 57 #include <utime.h>
58 58 #include <sys/systeminfo.h>
59 59 #include <sys/dktp/fdisk.h>
60 60 #include <sys/param.h>
61 61 #include <dirent.h>
62 62 #include <ctype.h>
63 63 #include <libgen.h>
64 64 #include <sys/sysmacros.h>
65 65 #include <sys/elf.h>
66 66 #include <libscf.h>
67 67 #include <zlib.h>
68 68 #include <sys/lockfs.h>
69 69 #include <sys/filio.h>
70 70 #include <libbe.h>
71 71 #include <deflt.h>
72 72 #ifdef i386
73 73 #include <libfdisk.h>
74 74 #endif
75 75
76 76 #if !defined(_OBP)
77 77 #include <sys/ucode.h>
78 78 #endif
79 79
80 80 #include <pwd.h>
81 81 #include <grp.h>
82 82 #include <device_info.h>
83 83 #include <sys/vtoc.h>
84 84 #include <sys/efi_partition.h>
85 85 #include <regex.h>
86 86 #include <locale.h>
87 87 #include <sys/mkdev.h>
88 88
89 89 #include "bootadm.h"
90 90
91 91 #ifndef TEXT_DOMAIN
92 92 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
93 93 #endif /* TEXT_DOMAIN */
94 94
95 95 /* Type definitions */
96 96
97 97 /* Primary subcmds */
98 98 typedef enum {
99 99 BAM_MENU = 3,
100 100 BAM_ARCHIVE,
101 101 BAM_INSTALL
102 102 } subcmd_t;
103 103
104 104 #define LINE_INIT 0 /* lineNum initial value */
105 105 #define ENTRY_INIT -1 /* entryNum initial value */
106 106 #define ALL_ENTRIES -2 /* selects all boot entries */
107 107
108 108 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
109 109 #define PARTNO_EFI -2 /* EFI partition table found */
110 110
111 111 #define GRUB_DIR "/boot/grub"
112 112 #define GRUB_STAGE2 GRUB_DIR "/stage2"
113 113 #define GRUB_MENU "/boot/grub/menu.lst"
114 114 #define MENU_TMP "/boot/grub/menu.lst.tmp"
115 115 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
116 116 #define RAMDISK_SPECIAL "/devices/ramdisk"
117 117 #define STUBBOOT "/stubboot"
118 118 #define MULTIBOOT "/platform/i86pc/multiboot"
119 119 #define GRUBSIGN_DIR "/boot/grub/bootsign"
120 120 #define GRUBSIGN_BACKUP "/etc/bootsign"
121 121 #define GRUBSIGN_UFS_PREFIX "rootfs"
122 122 #define GRUBSIGN_ZFS_PREFIX "pool_"
123 123 #define GRUBSIGN_LU_PREFIX "BE_"
124 124 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
125 125 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
126 126
127 127 /* BE defaults */
128 128 #define BE_DEFAULTS "/etc/default/be"
129 129 #define BE_DFLT_BE_HAS_GRUB "BE_HAS_GRUB="
130 130
131 131 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
132 132
133 133 /* lock related */
134 134 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
135 135 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
136 136
137 137 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
138 138 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
139 139 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
140 140 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
141 141
142 142 #define GRUB_slice "/etc/lu/GRUB_slice"
143 143 #define GRUB_root "/etc/lu/GRUB_root"
144 144 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
145 145 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
146 146 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
|
↓ open down ↓ |
109 lines elided |
↑ open up ↑ |
147 147 #define LULIB "/usr/lib/lu/lulib"
148 148 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
149 149 #define CKSUM "/usr/bin/cksum"
150 150 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
151 151 #define BOOTADM "/sbin/bootadm"
152 152
153 153 #define INSTALLGRUB "/sbin/installgrub"
154 154 #define STAGE1 "/boot/grub/stage1"
155 155 #define STAGE2 "/boot/grub/stage2"
156 156
157 +#define ETC_SYSTEM_DIR "etc/system.d"
158 +#define SELF_ASSEMBLY "etc/system.d/.self-assembly"
159 +
157 160 /*
158 161 * Default file attributes
159 162 */
160 163 #define DEFAULT_DEV_MODE 0644 /* default permissions */
161 164 #define DEFAULT_DEV_UID 0 /* user root */
162 165 #define DEFAULT_DEV_GID 3 /* group sys */
163 166
164 167 /*
165 168 * Menu related
166 169 * menu_cmd_t and menu_cmds must be kept in sync
167 170 */
168 171 char *menu_cmds[] = {
169 172 "default", /* DEFAULT_CMD */
170 173 "timeout", /* TIMEOUT_CMD */
171 174 "title", /* TITLE_CMD */
172 175 "root", /* ROOT_CMD */
173 176 "kernel", /* KERNEL_CMD */
174 177 "kernel$", /* KERNEL_DOLLAR_CMD */
175 178 "module", /* MODULE_CMD */
176 179 "module$", /* MODULE_DOLLAR_CMD */
177 180 " ", /* SEP_CMD */
178 181 "#", /* COMMENT_CMD */
179 182 "chainloader", /* CHAINLOADER_CMD */
180 183 "args", /* ARGS_CMD */
181 184 "findroot", /* FINDROOT_CMD */
182 185 "bootfs", /* BOOTFS_CMD */
183 186 NULL
184 187 };
185 188
186 189 #define OPT_ENTRY_NUM "entry"
187 190
188 191 /*
189 192 * exec_cmd related
190 193 */
191 194 typedef struct {
192 195 line_t *head;
193 196 line_t *tail;
194 197 } filelist_t;
195 198
196 199 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
197 200 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
198 201
199 202 #define FILE_STAT "boot/solaris/filestat.ramdisk"
200 203 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
201 204 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
202 205 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
203 206
204 207 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
205 208
206 209 /* Globals */
207 210 int bam_verbose;
208 211 int bam_force;
209 212 int bam_debug;
210 213 static char *prog;
211 214 static subcmd_t bam_cmd;
212 215 char *bam_root;
213 216 int bam_rootlen;
214 217 static int bam_root_readonly;
215 218 int bam_alt_root;
216 219 static int bam_extend = 0;
217 220 static int bam_purge = 0;
218 221 static char *bam_subcmd;
219 222 static char *bam_opt;
|
↓ open down ↓ |
53 lines elided |
↑ open up ↑ |
220 223 static char **bam_argv;
221 224 static char *bam_pool;
222 225 static int bam_argc;
223 226 static int bam_check;
224 227 static int bam_saved_check;
225 228 static int bam_smf_check;
226 229 static int bam_lock_fd = -1;
227 230 static int bam_zfs;
228 231 static int bam_mbr;
229 232 char rootbuf[PATH_MAX] = "/";
233 +static char self_assembly[PATH_MAX];
230 234 static int bam_update_all;
231 235 static int bam_alt_platform;
232 236 static char *bam_platform;
233 237 static char *bam_home_env = NULL;
234 238
235 239 /* function prototypes */
236 240 static void parse_args_internal(int, char *[]);
237 241 static void parse_args(int, char *argv[]);
238 242 static error_t bam_menu(char *, char *, int, char *[]);
239 243 static error_t bam_install(char *, char *);
240 244 static error_t bam_archive(char *, char *);
241 245
242 246 static void bam_lock(void);
243 247 static void bam_unlock(void);
244 248
245 249 static int exec_cmd(char *, filelist_t *);
246 250 static error_t read_globals(menu_t *, char *, char *, int);
247 251 static int menu_on_bootdisk(char *os_root, char *menu_root);
248 252 static menu_t *menu_read(char *);
249 253 static error_t menu_write(char *, menu_t *);
250 254 static void linelist_free(line_t *);
251 255 static void menu_free(menu_t *);
252 256 static void filelist_free(filelist_t *);
253 257 static error_t list2file(char *, char *, char *, line_t *);
254 258 static error_t list_entry(menu_t *, char *, char *);
255 259 static error_t list_setting(menu_t *, char *, char *);
256 260 static error_t delete_all_entries(menu_t *, char *, char *);
257 261 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
|
↓ open down ↓ |
18 lines elided |
↑ open up ↑ |
258 262 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
259 263
260 264 static error_t install_bootloader(void);
261 265 static error_t update_archive(char *, char *);
262 266 static error_t list_archive(char *, char *);
263 267 static error_t update_all(char *, char *);
264 268 static error_t read_list(char *, filelist_t *);
265 269 static error_t set_option(menu_t *, char *, char *);
266 270 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
267 271 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
272 +static error_t build_etc_system_dir(char *);
268 273 static char *expand_path(const char *);
269 274
270 275 static long s_strtol(char *);
271 276 static int s_fputs(char *, FILE *);
272 277
273 278 static int is_amd64(void);
274 279 static char *get_machine(void);
275 280 static void append_to_flist(filelist_t *, char *);
276 281 static int ufs_add_to_sign_list(char *sign);
277 282 static error_t synchronize_BE_menu(void);
278 283
279 284 #if !defined(_OBP)
280 285 static void ucode_install();
281 286 #endif
282 287
283 288 /* Menu related sub commands */
284 289 static subcmd_defn_t menu_subcmds[] = {
285 290 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
286 291 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
287 292 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
288 293 "update_entry", OPT_REQ, update_entry, 0, /* menu */
289 294 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
290 295 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
291 296 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
292 297 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
293 298 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
294 299 NULL, 0, NULL, 0 /* must be last */
295 300 };
296 301
297 302 /* Archive related sub commands */
298 303 static subcmd_defn_t arch_subcmds[] = {
299 304 "update", OPT_ABSENT, update_archive, 0, /* PUB */
300 305 "update_all", OPT_ABSENT, update_all, 0, /* PVT */
301 306 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */
302 307 NULL, 0, NULL, 0 /* must be last */
303 308 };
304 309
305 310 /* Install related sub commands */
306 311 static subcmd_defn_t inst_subcmds[] = {
307 312 "install_bootloader", OPT_ABSENT, install_bootloader, 0, /* PUB */
308 313 NULL, 0, NULL, 0 /* must be last */
309 314 };
310 315
311 316 enum dircache_copy_opt {
312 317 FILE32 = 0,
313 318 FILE64,
314 319 CACHEDIR_NUM
315 320 };
316 321
317 322 /*
318 323 * Directory specific flags:
319 324 * NEED_UPDATE : the specified archive needs to be updated
320 325 * NO_MULTI : don't extend the specified archive, but recreate it
321 326 */
322 327 #define NEED_UPDATE 0x00000001
323 328 #define NO_MULTI 0x00000002
324 329
325 330 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
326 331 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
327 332 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
328 333
329 334 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
330 335 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
331 336 #define get_count(id) (walk_arg.dirinfo[id].count)
332 337 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
333 338 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
334 339
335 340 /*
336 341 * dirinfo_t (specific cache directory information):
337 342 * cdir_path: path to the archive cache directory
338 343 * update_path: path to the update directory (contains the files that will be
339 344 * used to extend the archive)
340 345 * has_dir: the specified cache directory is active
341 346 * count: the number of files to update
342 347 * flags: directory specific flags
343 348 */
344 349 typedef struct _dirinfo {
345 350 char cdir_path[PATH_MAX];
346 351 char update_path[PATH_MAX];
347 352 int has_dir;
348 353 int count;
349 354 int flags;
350 355 } dirinfo_t;
351 356
352 357 /*
353 358 * Update flags:
354 359 * NEED_CACHE_DIR : cache directory is missing and needs to be created
355 360 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
356 361 * UPDATE_ERROR : an error occourred while traversing the list of files
357 362 * RDONLY_FSCHK : the target filesystem is read-only
358 363 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
359 364 */
360 365 #define NEED_CACHE_DIR 0x00000001
361 366 #define IS_SPARC_TARGET 0x00000002
362 367 #define UPDATE_ERROR 0x00000004
363 368 #define RDONLY_FSCHK 0x00000008
364 369 #define INVALIDATE_CACHE 0x00000010
365 370
366 371 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
367 372 #define set_flag(flag) (walk_arg.update_flags |= flag)
368 373 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
369 374
370 375 /*
371 376 * struct walk_arg :
372 377 * update_flags: flags related to the current updating process
373 378 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
374 379 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
375 380 */
376 381 static struct {
377 382 int update_flags;
378 383 nvlist_t *new_nvlp;
379 384 nvlist_t *old_nvlp;
380 385 FILE *sparcfile;
381 386 dirinfo_t dirinfo[CACHEDIR_NUM];
382 387 } walk_arg;
383 388
384 389 struct safefile {
385 390 char *name;
386 391 struct safefile *next;
387 392 };
388 393
389 394 static struct safefile *safefiles = NULL;
390 395
391 396 /*
392 397 * svc:/system/filesystem/usr:default service checks for this file and
393 398 * does a boot archive update and then reboot the system.
394 399 */
395 400 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
396 401
397 402 /*
398 403 * svc:/system/boot-archive-update:default checks for this file and
399 404 * updates the boot archive.
400 405 */
401 406 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
402 407
403 408 /* Thanks growisofs */
404 409 #define CD_BLOCK ((off64_t)2048)
405 410 #define VOLDESC_OFF 16
406 411 #define DVD_BLOCK (32*1024)
407 412 #define MAX_IVDs 16
408 413
409 414 struct iso_pdesc {
410 415 unsigned char type [1];
411 416 unsigned char id [5];
412 417 unsigned char void1 [80-5-1];
413 418 unsigned char volume_space_size [8];
414 419 unsigned char void2 [2048-80-8];
415 420 };
416 421
417 422 /*
418 423 * COUNT_MAX: maximum number of changed files to justify a multisession update
419 424 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
420 425 * update
421 426 */
422 427 #define COUNT_MAX 50
423 428 #define BA_SIZE_MAX (50 * 1024 * 1024)
424 429
425 430 #define bam_nowrite() (bam_check || bam_smf_check)
426 431
427 432 static int sync_menu = 1; /* whether we need to sync the BE menus */
428 433
429 434 static void
430 435 usage(void)
431 436 {
432 437 (void) fprintf(stderr, "USAGE:\n");
433 438
434 439 /* archive usage */
435 440 (void) fprintf(stderr,
436 441 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
437 442 (void) fprintf(stderr,
438 443 "\t%s list-archive [-R altroot [-p platform]]\n", prog);
439 444 #if defined(_OBP)
440 445 (void) fprintf(stderr,
441 446 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
442 447 #else
443 448 (void) fprintf(stderr,
444 449 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
445 450 #endif
446 451 #if !defined(_OBP)
447 452 /* x86 only */
448 453 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
449 454 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
450 455 #endif
451 456 }
452 457
453 458 /*
454 459 * Best effort attempt to restore the $HOME value.
455 460 */
456 461 static void
457 462 restore_env()
458 463 {
459 464 char home_env[PATH_MAX];
460 465
461 466 if (bam_home_env) {
462 467 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
463 468 bam_home_env);
464 469 (void) putenv(home_env);
465 470 }
466 471 }
467 472
468 473
469 474 #define SLEEP_TIME 5
470 475 #define MAX_TRIES 4
471 476
472 477 /*
473 478 * Sanitize the environment in which bootadm will execute its sub-processes
474 479 * (ex. mkisofs). This is done to prevent those processes from attempting
475 480 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
476 481 * or, potentially, insecure.
477 482 */
478 483 static void
479 484 sanitize_env()
480 485 {
481 486 int stry = 0;
482 487
483 488 /* don't depend on caller umask */
484 489 (void) umask(0022);
485 490
486 491 /* move away from a potential unsafe current working directory */
487 492 while (chdir("/") == -1) {
488 493 if (errno != EINTR) {
489 494 bam_print("WARNING: unable to chdir to /");
490 495 break;
491 496 }
492 497 }
493 498
494 499 bam_home_env = getenv("HOME");
495 500 while (bam_home_env != NULL && putenv("HOME=/") == -1) {
496 501 if (errno == ENOMEM) {
497 502 /* retry no more than MAX_TRIES times */
498 503 if (++stry > MAX_TRIES) {
499 504 bam_print("WARNING: unable to recover from "
500 505 "system memory pressure... aborting \n");
501 506 bam_exit(EXIT_FAILURE);
502 507 }
503 508 /* memory is tight, try to sleep */
504 509 bam_print("Attempting to recover from memory pressure: "
505 510 "sleeping for %d seconds\n", SLEEP_TIME * stry);
506 511 (void) sleep(SLEEP_TIME * stry);
507 512 } else {
508 513 bam_print("WARNING: unable to sanitize HOME\n");
509 514 }
510 515 }
511 516 }
512 517
513 518 int
514 519 main(int argc, char *argv[])
515 520 {
516 521 error_t ret = BAM_SUCCESS;
517 522
518 523 (void) setlocale(LC_ALL, "");
519 524 (void) textdomain(TEXT_DOMAIN);
520 525
521 526 if ((prog = strrchr(argv[0], '/')) == NULL) {
522 527 prog = argv[0];
523 528 } else {
524 529 prog++;
525 530 }
526 531
527 532 INJECT_ERROR1("ASSERT_ON", assert(0))
528 533
529 534 sanitize_env();
530 535
531 536 parse_args(argc, argv);
532 537
533 538 switch (bam_cmd) {
534 539 case BAM_MENU:
535 540 if (is_grub(bam_alt_root ? bam_root : "/")) {
536 541 ret = bam_menu(bam_subcmd, bam_opt,
537 542 bam_argc, bam_argv);
538 543 } else {
539 544 ret = bam_loader_menu(bam_subcmd, bam_opt,
540 545 bam_argc, bam_argv);
541 546 }
542 547 break;
543 548 case BAM_ARCHIVE:
544 549 ret = bam_archive(bam_subcmd, bam_opt);
545 550 break;
546 551 case BAM_INSTALL:
547 552 ret = bam_install(bam_subcmd, bam_opt);
548 553 break;
549 554 default:
550 555 usage();
551 556 bam_exit(1);
552 557 }
553 558
554 559 if (ret != BAM_SUCCESS)
555 560 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
556 561
557 562 bam_unlock();
558 563 return (0);
559 564 }
560 565
561 566 /*
562 567 * Equivalence of public and internal commands:
563 568 * update-archive -- -a update
564 569 * list-archive -- -a list
565 570 * set-menu -- -m set_option
566 571 * list-menu -- -m list_entry
567 572 * update-menu -- -m update_entry
568 573 * install-bootloader -- -i install_bootloader
569 574 */
570 575 static struct cmd_map {
571 576 char *bam_cmdname;
572 577 int bam_cmd;
573 578 char *bam_subcmd;
574 579 } cmd_map[] = {
575 580 { "update-archive", BAM_ARCHIVE, "update"},
576 581 { "list-archive", BAM_ARCHIVE, "list"},
577 582 { "set-menu", BAM_MENU, "set_option"},
578 583 { "list-menu", BAM_MENU, "list_entry"},
579 584 { "update-menu", BAM_MENU, "update_entry"},
580 585 { "install-bootloader", BAM_INSTALL, "install_bootloader"},
581 586 { NULL, 0, NULL}
582 587 };
583 588
584 589 /*
585 590 * Commands syntax published in bootadm(1M) are parsed here
586 591 */
587 592 static void
588 593 parse_args(int argc, char *argv[])
589 594 {
590 595 struct cmd_map *cmp = cmd_map;
591 596
592 597 /* command conforming to the final spec */
593 598 if (argc > 1 && argv[1][0] != '-') {
594 599 /*
595 600 * Map commands to internal table.
596 601 */
597 602 while (cmp->bam_cmdname) {
598 603 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
599 604 bam_cmd = cmp->bam_cmd;
600 605 bam_subcmd = cmp->bam_subcmd;
601 606 break;
602 607 }
603 608 cmp++;
604 609 }
605 610 if (cmp->bam_cmdname == NULL) {
606 611 usage();
607 612 bam_exit(1);
608 613 }
609 614 argc--;
610 615 argv++;
611 616 }
612 617
613 618 parse_args_internal(argc, argv);
614 619 }
615 620
616 621 /*
617 622 * A combination of public and private commands are parsed here.
618 623 * The internal syntax and the corresponding functionality are:
619 624 * -a update -- update-archive
620 625 * -a list -- list-archive
621 626 * -a update-all -- (reboot to sync all mnted OS archive)
622 627 * -i install_bootloader -- install-bootloader
623 628 * -m update_entry -- update-menu
624 629 * -m list_entry -- list-menu
625 630 * -m update_temp -- (reboot -- [boot-args])
626 631 * -m delete_all_entries -- (called from install)
627 632 * -m enable_hypervisor [args] -- cvt_to_hyper
628 633 * -m disable_hypervisor -- cvt_to_metal
629 634 * -m list_setting [entry] [value] -- list_setting
630 635 *
631 636 * A set of private flags is there too:
632 637 * -F -- purge the cache directories and rebuild them
633 638 * -e -- use the (faster) archive update approach (used by
634 639 * reboot)
635 640 */
636 641 static void
637 642 parse_args_internal(int argc, char *argv[])
638 643 {
639 644 int c, error;
640 645 extern char *optarg;
641 646 extern int optind, opterr;
642 647 #if defined(_OBP)
643 648 const char *optstring = "a:d:fi:m:no:veFCR:p:P:XZ";
644 649 #else
645 650 const char *optstring = "a:d:fi:m:no:veFCMR:p:P:XZ";
646 651 #endif
647 652
648 653 /* Suppress error message from getopt */
649 654 opterr = 0;
650 655
651 656 error = 0;
652 657 while ((c = getopt(argc, argv, optstring)) != -1) {
653 658 switch (c) {
654 659 case 'a':
655 660 if (bam_cmd) {
656 661 error = 1;
657 662 bam_error(
658 663 _("multiple commands specified: -%c\n"), c);
659 664 }
660 665 bam_cmd = BAM_ARCHIVE;
661 666 bam_subcmd = optarg;
662 667 break;
663 668 case 'd':
664 669 if (bam_debug) {
665 670 error = 1;
666 671 bam_error(
667 672 _("duplicate options specified: -%c\n"), c);
668 673 }
669 674 bam_debug = s_strtol(optarg);
670 675 break;
671 676 case 'f':
672 677 bam_force = 1;
673 678 break;
674 679 case 'F':
675 680 bam_purge = 1;
676 681 break;
677 682 case 'i':
678 683 if (bam_cmd) {
679 684 error = 1;
680 685 bam_error(
681 686 _("multiple commands specified: -%c\n"), c);
682 687 }
683 688 bam_cmd = BAM_INSTALL;
684 689 bam_subcmd = optarg;
685 690 break;
686 691 case 'm':
687 692 if (bam_cmd) {
688 693 error = 1;
689 694 bam_error(
690 695 _("multiple commands specified: -%c\n"), c);
691 696 }
692 697 bam_cmd = BAM_MENU;
693 698 bam_subcmd = optarg;
694 699 break;
695 700 #if !defined(_OBP)
696 701 case 'M':
697 702 bam_mbr = 1;
698 703 break;
699 704 #endif
700 705 case 'n':
701 706 bam_check = 1;
702 707 /*
703 708 * We save the original value of bam_check. The new
704 709 * approach in case of a read-only filesystem is to
705 710 * behave as a check, so we need a way to restore the
706 711 * original value after the evaluation of the read-only
707 712 * filesystem has been done.
708 713 * Even if we don't allow at the moment a check with
709 714 * update_all, this approach is more robust than
710 715 * simply resetting bam_check to zero.
711 716 */
712 717 bam_saved_check = 1;
713 718 break;
714 719 case 'o':
715 720 if (bam_opt) {
716 721 error = 1;
717 722 bam_error(
718 723 _("duplicate options specified: -%c\n"), c);
719 724 }
720 725 bam_opt = optarg;
721 726 break;
722 727 case 'v':
723 728 bam_verbose = 1;
724 729 break;
725 730 case 'C':
726 731 bam_smf_check = 1;
727 732 break;
728 733 case 'P':
729 734 if (bam_pool != NULL) {
730 735 error = 1;
731 736 bam_error(
732 737 _("duplicate options specified: -%c\n"), c);
733 738 }
734 739 bam_pool = optarg;
735 740 break;
736 741 case 'R':
737 742 if (bam_root) {
738 743 error = 1;
739 744 bam_error(
740 745 _("duplicate options specified: -%c\n"), c);
741 746 break;
742 747 } else if (realpath(optarg, rootbuf) == NULL) {
743 748 error = 1;
744 749 bam_error(_("cannot resolve path %s: %s\n"),
745 750 optarg, strerror(errno));
746 751 break;
747 752 }
748 753 bam_alt_root = 1;
749 754 bam_root = rootbuf;
750 755 bam_rootlen = strlen(rootbuf);
751 756 break;
752 757 case 'p':
753 758 bam_alt_platform = 1;
754 759 bam_platform = optarg;
755 760 if ((strcmp(bam_platform, "i86pc") != 0) &&
756 761 (strcmp(bam_platform, "sun4u") != 0) &&
757 762 (strcmp(bam_platform, "sun4v") != 0)) {
758 763 error = 1;
759 764 bam_error(_("invalid platform %s - must be "
760 765 "one of sun4u, sun4v or i86pc\n"),
761 766 bam_platform);
762 767 }
763 768 break;
764 769 case 'X':
765 770 bam_is_hv = BAM_HV_PRESENT;
766 771 break;
767 772 case 'Z':
768 773 bam_zfs = 1;
769 774 break;
770 775 case 'e':
771 776 bam_extend = 1;
772 777 break;
773 778 case '?':
774 779 error = 1;
775 780 bam_error(_("invalid option or missing option "
776 781 "argument: -%c\n"), optopt);
777 782 break;
778 783 default :
779 784 error = 1;
780 785 bam_error(_("invalid option or missing option "
781 786 "argument: -%c\n"), c);
782 787 break;
783 788 }
784 789 }
785 790
786 791 /*
787 792 * An alternate platform requires an alternate root
788 793 */
789 794 if (bam_alt_platform && bam_alt_root == 0) {
790 795 usage();
791 796 bam_exit(0);
792 797 }
793 798
794 799 /*
795 800 * A command option must be specfied
796 801 */
797 802 if (!bam_cmd) {
798 803 if (bam_opt && strcmp(bam_opt, "all") == 0) {
799 804 usage();
800 805 bam_exit(0);
801 806 }
802 807 bam_error(_("a command option must be specified\n"));
803 808 error = 1;
804 809 }
805 810
806 811 if (error) {
807 812 usage();
808 813 bam_exit(1);
809 814 }
810 815
811 816 if (optind > argc) {
812 817 bam_error(_("Internal error: %s\n"), "parse_args");
813 818 bam_exit(1);
814 819 } else if (optind < argc) {
815 820 bam_argv = &argv[optind];
816 821 bam_argc = argc - optind;
817 822 }
818 823
819 824 /*
820 825 * mbr and pool are options for install_bootloader
821 826 */
822 827 if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
823 828 usage();
824 829 bam_exit(0);
825 830 }
826 831
827 832 /*
828 833 * -n implies verbose mode
829 834 */
830 835 if (bam_check)
831 836 bam_verbose = 1;
832 837 }
833 838
834 839 error_t
835 840 check_subcmd_and_options(
836 841 char *subcmd,
837 842 char *opt,
838 843 subcmd_defn_t *table,
839 844 error_t (**fp)())
840 845 {
841 846 int i;
842 847
843 848 if (subcmd == NULL) {
844 849 bam_error(_("this command requires a sub-command\n"));
845 850 return (BAM_ERROR);
846 851 }
847 852
848 853 if (strcmp(subcmd, "set_option") == 0) {
849 854 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
850 855 bam_error(_("missing argument for sub-command\n"));
851 856 usage();
852 857 return (BAM_ERROR);
853 858 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
854 859 bam_error(_("invalid trailing arguments\n"));
855 860 usage();
856 861 return (BAM_ERROR);
857 862 }
858 863 } else if (strcmp(subcmd, "update_all") == 0) {
859 864 /*
860 865 * The only option we accept for the "update_all"
861 866 * subcmd is "fastboot".
862 867 */
863 868 if (bam_argc > 1 || (bam_argc == 1 &&
864 869 strcmp(bam_argv[0], "fastboot") != 0)) {
865 870 bam_error(_("invalid trailing arguments\n"));
866 871 usage();
867 872 return (BAM_ERROR);
868 873 }
869 874 if (bam_argc == 1)
870 875 sync_menu = 0;
871 876 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
872 877 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
873 878 /*
874 879 * Of the remaining subcommands, only "enable_hypervisor" and
875 880 * "list_setting" take trailing arguments.
876 881 */
877 882 bam_error(_("invalid trailing arguments\n"));
878 883 usage();
879 884 return (BAM_ERROR);
880 885 }
881 886
882 887 if (bam_root == NULL) {
883 888 bam_root = rootbuf;
884 889 bam_rootlen = 1;
885 890 }
886 891
887 892 /* verify that subcmd is valid */
888 893 for (i = 0; table[i].subcmd != NULL; i++) {
889 894 if (strcmp(table[i].subcmd, subcmd) == 0)
890 895 break;
891 896 }
892 897
893 898 if (table[i].subcmd == NULL) {
894 899 bam_error(_("invalid sub-command specified: %s\n"), subcmd);
895 900 return (BAM_ERROR);
896 901 }
897 902
898 903 if (table[i].unpriv == 0 && geteuid() != 0) {
899 904 bam_error(_("you must be root to run this command\n"));
900 905 return (BAM_ERROR);
901 906 }
902 907
903 908 /*
904 909 * Currently only privileged commands need a lock
905 910 */
906 911 if (table[i].unpriv == 0)
907 912 bam_lock();
908 913
909 914 /* subcmd verifies that opt is appropriate */
910 915 if (table[i].option != OPT_OPTIONAL) {
911 916 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
912 917 if (opt)
913 918 bam_error(_("this sub-command (%s) does not "
914 919 "take options\n"), subcmd);
915 920 else
916 921 bam_error(_("an option is required for this "
917 922 "sub-command: %s\n"), subcmd);
918 923 return (BAM_ERROR);
919 924 }
920 925 }
921 926
922 927 *fp = table[i].handler;
923 928
924 929 return (BAM_SUCCESS);
925 930 }
926 931
927 932 /*
928 933 * NOTE: A single "/" is also considered a trailing slash and will
929 934 * be deleted.
930 935 */
931 936 void
932 937 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
933 938 {
934 939 size_t dstlen;
935 940
936 941 assert(src);
937 942 assert(dst);
938 943
939 944 (void) strlcpy(dst, src, dstsize);
940 945
941 946 dstlen = strlen(dst);
942 947 if (dst[dstlen - 1] == '/') {
943 948 dst[dstlen - 1] = '\0';
944 949 }
945 950 }
946 951
947 952 static int
948 953 is_safe_exec(char *path)
949 954 {
950 955 struct stat sb;
951 956
952 957 if (lstat(path, &sb) != 0) {
953 958 bam_error(_("stat of file failed: %s: %s\n"), path,
954 959 strerror(errno));
955 960 return (BAM_ERROR);
956 961 }
957 962
958 963 if (!S_ISREG(sb.st_mode)) {
959 964 bam_error(_("%s is not a regular file, skipping\n"), path);
960 965 return (BAM_ERROR);
961 966 }
962 967
963 968 if (sb.st_uid != getuid()) {
964 969 bam_error(_("%s is not owned by %d, skipping\n"),
965 970 path, getuid());
966 971 return (BAM_ERROR);
967 972 }
968 973
969 974 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
970 975 bam_error(_("%s is others or group writable, skipping\n"),
971 976 path);
972 977 return (BAM_ERROR);
973 978 }
974 979
975 980 return (BAM_SUCCESS);
976 981 }
977 982
978 983 static error_t
979 984 list_setting(menu_t *mp, char *which, char *setting)
980 985 {
981 986 line_t *lp;
982 987 entry_t *ent;
983 988
984 989 char *p = which;
985 990 int entry;
986 991
987 992 int found;
988 993
989 994 assert(which);
990 995 assert(setting);
991 996
992 997 if (*which != NULL) {
993 998 /*
994 999 * If "which" is not a number, assume it's a setting we want
995 1000 * to look for and so set up the routine to look for "which"
996 1001 * in the default entry.
997 1002 */
998 1003 while (*p != NULL)
999 1004 if (!(isdigit((int)*p++))) {
1000 1005 setting = which;
1001 1006 which = mp->curdefault->arg;
1002 1007 break;
1003 1008 }
1004 1009 } else {
1005 1010 which = mp->curdefault->arg;
1006 1011 }
1007 1012
1008 1013 entry = atoi(which);
1009 1014
1010 1015 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1011 1016 ent = ent->next)
1012 1017 ;
1013 1018
1014 1019 if (!ent) {
1015 1020 bam_error(_("no matching entry found\n"));
1016 1021 return (BAM_ERROR);
1017 1022 }
1018 1023
1019 1024 found = (*setting == NULL);
1020 1025
1021 1026 for (lp = ent->start; lp != NULL; lp = lp->next) {
1022 1027 if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
1023 1028 bam_print("%s\n", lp->line);
1024 1029 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1025 1030 bam_print("%s\n", lp->arg);
1026 1031 found = 1;
1027 1032 }
1028 1033
1029 1034 if (lp == ent->end)
1030 1035 break;
1031 1036 }
1032 1037
1033 1038 if (!found) {
1034 1039 bam_error(_("no matching entry found\n"));
1035 1040 return (BAM_ERROR);
1036 1041 }
1037 1042
1038 1043 return (BAM_SUCCESS);
1039 1044 }
1040 1045
1041 1046 static error_t
1042 1047 install_bootloader(void)
1043 1048 {
1044 1049 nvlist_t *nvl;
1045 1050 uint16_t flags = 0;
1046 1051 int found = 0;
1047 1052 struct extmnttab mnt;
1048 1053 struct stat statbuf = {0};
1049 1054 be_node_list_t *be_nodes, *node;
1050 1055 FILE *fp;
1051 1056 char *root_ds = NULL;
1052 1057 int ret = BAM_ERROR;
1053 1058
1054 1059 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1055 1060 bam_error(_("out of memory\n"));
1056 1061 return (ret);
1057 1062 }
1058 1063
1059 1064 /*
1060 1065 * if bam_alt_root is set, the stage files are used from alt root.
1061 1066 * if pool is set, the target devices are pool devices, stage files
1062 1067 * are read from pool bootfs unless alt root is set.
1063 1068 *
1064 1069 * use arguments as targets, stage files are from alt or current root
1065 1070 * if no arguments and no pool, install on current boot pool.
1066 1071 */
1067 1072
1068 1073 if (bam_alt_root) {
1069 1074 if (stat(bam_root, &statbuf) != 0) {
1070 1075 bam_error(_("stat of file failed: %s: %s\n"), bam_root,
1071 1076 strerror(errno));
1072 1077 goto done;
1073 1078 }
1074 1079 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1075 1080 bam_error(_("failed to open file: %s: %s\n"),
1076 1081 MNTTAB, strerror(errno));
1077 1082 goto done;
1078 1083 }
1079 1084 resetmnttab(fp);
1080 1085 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1081 1086 if (mnt.mnt_major == major(statbuf.st_dev) &&
1082 1087 mnt.mnt_minor == minor(statbuf.st_dev)) {
1083 1088 found = 1;
1084 1089 root_ds = strdup(mnt.mnt_special);
1085 1090 break;
1086 1091 }
1087 1092 }
1088 1093 (void) fclose(fp);
1089 1094
1090 1095 if (found == 0) {
1091 1096 bam_error(_("alternate root %s not in mnttab\n"),
1092 1097 bam_root);
1093 1098 goto done;
1094 1099 }
1095 1100 if (root_ds == NULL) {
1096 1101 bam_error(_("out of memory\n"));
1097 1102 goto done;
1098 1103 }
1099 1104
1100 1105 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1101 1106 bam_error(_("No BE's found\n"));
1102 1107 goto done;
1103 1108 }
1104 1109 for (node = be_nodes; node != NULL; node = node->be_next_node)
1105 1110 if (strcmp(root_ds, node->be_root_ds) == 0)
1106 1111 break;
1107 1112
1108 1113 if (node == NULL)
1109 1114 bam_error(_("BE (%s) does not exist\n"), root_ds);
1110 1115
1111 1116 free(root_ds);
1112 1117 root_ds = NULL;
1113 1118 if (node == NULL) {
1114 1119 be_free_list(be_nodes);
1115 1120 goto done;
1116 1121 }
1117 1122 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1118 1123 node->be_node_name);
1119 1124 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1120 1125 node->be_root_ds);
1121 1126 be_free_list(be_nodes);
1122 1127 if (ret != 0) {
1123 1128 ret = BAM_ERROR;
1124 1129 goto done;
1125 1130 }
1126 1131 }
1127 1132
1128 1133 if (bam_force)
1129 1134 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1130 1135 if (bam_mbr)
1131 1136 flags |= BE_INSTALLBOOT_FLAG_MBR;
1132 1137 if (bam_verbose)
1133 1138 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1134 1139
1135 1140 if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1136 1141 bam_error(_("out of memory\n"));
1137 1142 ret = BAM_ERROR;
1138 1143 goto done;
1139 1144 }
1140 1145
1141 1146 /*
1142 1147 * if altroot was set, we got be name and be root, only need
1143 1148 * to set pool name as target.
1144 1149 * if no altroot, need to find be name and root from pool.
1145 1150 */
1146 1151 if (bam_pool != NULL) {
1147 1152 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1148 1153 if (ret != 0) {
1149 1154 ret = BAM_ERROR;
1150 1155 goto done;
1151 1156 }
1152 1157 if (found) {
1153 1158 ret = be_installboot(nvl);
1154 1159 if (ret != 0)
1155 1160 ret = BAM_ERROR;
1156 1161 goto done;
1157 1162 }
1158 1163 }
1159 1164
1160 1165 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1161 1166 bam_error(_("No BE's found\n"));
1162 1167 ret = BAM_ERROR;
1163 1168 goto done;
1164 1169 }
1165 1170
1166 1171 if (bam_pool != NULL) {
1167 1172 /*
1168 1173 * find active be_node in bam_pool
1169 1174 */
1170 1175 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1171 1176 if (strcmp(bam_pool, node->be_rpool) != 0)
1172 1177 continue;
1173 1178 if (node->be_active_on_boot)
1174 1179 break;
1175 1180 }
1176 1181 if (node == NULL) {
1177 1182 bam_error(_("No active BE in %s\n"), bam_pool);
1178 1183 be_free_list(be_nodes);
1179 1184 ret = BAM_ERROR;
1180 1185 goto done;
1181 1186 }
1182 1187 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1183 1188 node->be_node_name);
1184 1189 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1185 1190 node->be_root_ds);
1186 1191 be_free_list(be_nodes);
1187 1192 if (ret != 0) {
1188 1193 ret = BAM_ERROR;
1189 1194 goto done;
1190 1195 }
1191 1196 ret = be_installboot(nvl);
1192 1197 if (ret != 0)
1193 1198 ret = BAM_ERROR;
1194 1199 goto done;
1195 1200 }
1196 1201
1197 1202 /*
1198 1203 * get dataset for "/" and fill up the args.
1199 1204 */
1200 1205 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1201 1206 bam_error(_("failed to open file: %s: %s\n"),
1202 1207 MNTTAB, strerror(errno));
1203 1208 ret = BAM_ERROR;
1204 1209 be_free_list(be_nodes);
1205 1210 goto done;
1206 1211 }
1207 1212 resetmnttab(fp);
1208 1213 found = 0;
1209 1214 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1210 1215 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1211 1216 found = 1;
1212 1217 root_ds = strdup(mnt.mnt_special);
1213 1218 break;
1214 1219 }
1215 1220 }
1216 1221 (void) fclose(fp);
1217 1222
1218 1223 if (found == 0) {
1219 1224 bam_error(_("alternate root %s not in mnttab\n"), "/");
1220 1225 ret = BAM_ERROR;
1221 1226 be_free_list(be_nodes);
1222 1227 goto done;
1223 1228 }
1224 1229 if (root_ds == NULL) {
1225 1230 bam_error(_("out of memory\n"));
1226 1231 ret = BAM_ERROR;
1227 1232 be_free_list(be_nodes);
1228 1233 goto done;
1229 1234 }
1230 1235
1231 1236 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1232 1237 if (strcmp(root_ds, node->be_root_ds) == 0)
1233 1238 break;
1234 1239 }
1235 1240
1236 1241 if (node == NULL) {
1237 1242 bam_error(_("No such BE: %s\n"), root_ds);
1238 1243 free(root_ds);
1239 1244 be_free_list(be_nodes);
1240 1245 ret = BAM_ERROR;
1241 1246 goto done;
1242 1247 }
1243 1248 free(root_ds);
1244 1249
1245 1250 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1246 1251 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1247 1252 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1248 1253 be_free_list(be_nodes);
1249 1254
1250 1255 if (ret != 0)
1251 1256 ret = BAM_ERROR;
1252 1257 else
1253 1258 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1254 1259 done:
1255 1260 nvlist_free(nvl);
1256 1261
1257 1262 return (ret);
1258 1263 }
1259 1264
1260 1265 static error_t
1261 1266 bam_install(char *subcmd, char *opt)
1262 1267 {
1263 1268 error_t (*f)(void);
1264 1269
1265 1270 /*
1266 1271 * Check arguments
1267 1272 */
1268 1273 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1269 1274 BAM_ERROR)
1270 1275 return (BAM_ERROR);
1271 1276
1272 1277 return (f());
1273 1278 }
1274 1279
1275 1280 static error_t
1276 1281 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1277 1282 {
1278 1283 error_t ret;
1279 1284 char menu_path[PATH_MAX];
1280 1285 char clean_menu_root[PATH_MAX];
1281 1286 char path[PATH_MAX];
1282 1287 menu_t *menu;
1283 1288 char menu_root[PATH_MAX];
1284 1289 struct stat sb;
1285 1290 error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1286 1291 char *special = NULL;
1287 1292 char *pool = NULL;
1288 1293 zfs_mnted_t zmnted;
1289 1294 char *zmntpt = NULL;
1290 1295 char *osdev;
1291 1296 char *osroot;
1292 1297 const char *fcn = "bam_menu()";
1293 1298
1294 1299 /*
1295 1300 * Menu sub-command only applies to GRUB (i.e. x86)
1296 1301 */
1297 1302 if (!is_grub(bam_alt_root ? bam_root : "/")) {
1298 1303 bam_error(_("not a GRUB 0.97 based Illumos instance. "
1299 1304 "Operation not supported\n"));
1300 1305 return (BAM_ERROR);
1301 1306 }
1302 1307
1303 1308 /*
1304 1309 * Check arguments
1305 1310 */
1306 1311 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1307 1312 if (ret == BAM_ERROR) {
1308 1313 return (BAM_ERROR);
1309 1314 }
1310 1315
1311 1316 assert(bam_root);
1312 1317
1313 1318 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1314 1319 osdev = osroot = NULL;
1315 1320
1316 1321 if (strcmp(subcmd, "update_entry") == 0) {
1317 1322 assert(opt);
1318 1323
1319 1324 osdev = strtok(opt, ",");
1320 1325 assert(osdev);
1321 1326 osroot = strtok(NULL, ",");
1322 1327 if (osroot) {
1323 1328 /* fixup bam_root so that it points at osroot */
1324 1329 if (realpath(osroot, rootbuf) == NULL) {
1325 1330 bam_error(_("cannot resolve path %s: %s\n"),
1326 1331 osroot, strerror(errno));
1327 1332 return (BAM_ERROR);
1328 1333 }
1329 1334 bam_alt_root = 1;
1330 1335 bam_root = rootbuf;
1331 1336 bam_rootlen = strlen(rootbuf);
1332 1337 }
1333 1338 }
1334 1339
1335 1340 /*
1336 1341 * We support menu on PCFS (under certain conditions), but
1337 1342 * not the OS root
1338 1343 */
1339 1344 if (is_pcfs(bam_root)) {
1340 1345 bam_error(_("root <%s> on PCFS is not supported\n"), bam_root);
1341 1346 return (BAM_ERROR);
1342 1347 }
1343 1348
1344 1349 if (stat(menu_root, &sb) == -1) {
1345 1350 bam_error(_("cannot find GRUB menu\n"));
1346 1351 return (BAM_ERROR);
1347 1352 }
1348 1353
1349 1354 BAM_DPRINTF(("%s: menu root is %s\n", fcn, menu_root));
1350 1355
1351 1356 /*
1352 1357 * We no longer use the GRUB slice file. If it exists, then
1353 1358 * the user is doing something that is unsupported (such as
1354 1359 * standard upgrading an old Live Upgrade BE). If that
1355 1360 * happens, mimic existing behavior i.e. pretend that it is
1356 1361 * not a BE. Emit a warning though.
1357 1362 */
1358 1363 if (bam_alt_root) {
1359 1364 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1360 1365 GRUB_slice);
1361 1366 } else {
1362 1367 (void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1363 1368 }
1364 1369
1365 1370 if (bam_verbose && stat(path, &sb) == 0)
1366 1371 bam_error(_("unsupported GRUB slice file (%s) exists - "
1367 1372 "ignoring.\n"), path);
1368 1373
1369 1374 if (is_zfs(menu_root)) {
1370 1375 assert(strcmp(menu_root, bam_root) == 0);
1371 1376 special = get_special(menu_root);
1372 1377 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1373 1378 if (special == NULL) {
1374 1379 bam_error(_("cant find special file for "
1375 1380 "mount-point %s\n"), menu_root);
1376 1381 return (BAM_ERROR);
1377 1382 }
1378 1383 pool = strtok(special, "/");
1379 1384 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1380 1385 if (pool == NULL) {
1381 1386 free(special);
1382 1387 bam_error(_("cant find pool for mount-point %s\n"),
1383 1388 menu_root);
1384 1389 return (BAM_ERROR);
1385 1390 }
1386 1391 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
1387 1392
1388 1393 zmntpt = mount_top_dataset(pool, &zmnted);
1389 1394 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1390 1395 if (zmntpt == NULL) {
1391 1396 bam_error(_("cannot mount pool dataset for pool: %s\n"),
1392 1397 pool);
1393 1398 free(special);
1394 1399 return (BAM_ERROR);
1395 1400 }
1396 1401 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
1397 1402
1398 1403 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1399 1404 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
1400 1405 }
1401 1406
1402 1407 elide_trailing_slash(menu_root, clean_menu_root,
1403 1408 sizeof (clean_menu_root));
1404 1409
1405 1410 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
1406 1411
1407 1412 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1408 1413 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1409 1414
1410 1415 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
1411 1416
1412 1417 /*
1413 1418 * If listing the menu, display the menu location
1414 1419 */
1415 1420 if (strcmp(subcmd, "list_entry") == 0)
1416 1421 bam_print(_("the location for the active GRUB menu is: %s\n"),
1417 1422 menu_path);
1418 1423
1419 1424 if ((menu = menu_read(menu_path)) == NULL) {
1420 1425 bam_error(_("cannot find GRUB menu file: %s\n"), menu_path);
1421 1426 free(special);
1422 1427
1423 1428 return (BAM_ERROR);
1424 1429 }
1425 1430
1426 1431 /*
1427 1432 * We already checked the following case in
1428 1433 * check_subcmd_and_suboptions() above. Complete the
1429 1434 * final step now.
1430 1435 */
1431 1436 if (strcmp(subcmd, "set_option") == 0) {
1432 1437 assert(largc == 1 && largv[0] && largv[1] == NULL);
1433 1438 opt = largv[0];
1434 1439 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1435 1440 (strcmp(subcmd, "list_setting") != 0)) {
1436 1441 assert(largc == 0 && largv == NULL);
1437 1442 }
1438 1443
1439 1444 ret = get_boot_cap(bam_root);
1440 1445 if (ret != BAM_SUCCESS) {
1441 1446 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1442 1447 goto out;
1443 1448 }
1444 1449
1445 1450 /*
1446 1451 * Once the sub-cmd handler has run
1447 1452 * only the line field is guaranteed to have valid values
1448 1453 */
1449 1454 if (strcmp(subcmd, "update_entry") == 0) {
1450 1455 ret = f(menu, menu_root, osdev);
1451 1456 } else if (strcmp(subcmd, "upgrade") == 0) {
1452 1457 ret = f(menu, bam_root, menu_root);
1453 1458 } else if (strcmp(subcmd, "list_entry") == 0) {
1454 1459 ret = f(menu, menu_path, opt);
1455 1460 } else if (strcmp(subcmd, "list_setting") == 0) {
1456 1461 ret = f(menu, ((largc > 0) ? largv[0] : ""),
1457 1462 ((largc > 1) ? largv[1] : ""));
1458 1463 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1459 1464 if (is_sparc()) {
1460 1465 bam_error(_("%s operation unsupported on SPARC "
1461 1466 "machines\n"), subcmd);
1462 1467 ret = BAM_ERROR;
1463 1468 } else {
1464 1469 ret = f(menu, bam_root, NULL);
1465 1470 }
1466 1471 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1467 1472 if (is_sparc()) {
1468 1473 bam_error(_("%s operation unsupported on SPARC "
1469 1474 "machines\n"), subcmd);
1470 1475 ret = BAM_ERROR;
1471 1476 } else {
1472 1477 char *extra_args = NULL;
1473 1478
1474 1479 /*
1475 1480 * Compress all arguments passed in the largv[] array
1476 1481 * into one string that can then be appended to the
1477 1482 * end of the kernel$ string the routine to enable the
1478 1483 * hypervisor will build.
1479 1484 *
1480 1485 * This allows the caller to supply arbitrary unparsed
1481 1486 * arguments, such as dom0 memory settings or APIC
1482 1487 * options.
1483 1488 *
1484 1489 * This concatenation will be done without ANY syntax
1485 1490 * checking whatsoever, so it's the responsibility of
1486 1491 * the caller to make sure the arguments are valid and
1487 1492 * do not duplicate arguments the conversion routines
1488 1493 * may create.
1489 1494 */
1490 1495 if (largc > 0) {
1491 1496 int extra_len, i;
1492 1497
1493 1498 for (extra_len = 0, i = 0; i < largc; i++)
1494 1499 extra_len += strlen(largv[i]);
1495 1500
1496 1501 /*
1497 1502 * Allocate space for argument strings,
1498 1503 * intervening spaces and terminating NULL.
1499 1504 */
1500 1505 extra_args = alloca(extra_len + largc);
1501 1506
1502 1507 (void) strcpy(extra_args, largv[0]);
1503 1508
1504 1509 for (i = 1; i < largc; i++) {
1505 1510 (void) strcat(extra_args, " ");
1506 1511 (void) strcat(extra_args, largv[i]);
1507 1512 }
1508 1513 }
1509 1514
1510 1515 ret = f(menu, bam_root, extra_args);
1511 1516 }
1512 1517 } else
1513 1518 ret = f(menu, NULL, opt);
1514 1519
1515 1520 if (ret == BAM_WRITE) {
1516 1521 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
1517 1522 fcn, clean_menu_root));
1518 1523 ret = menu_write(clean_menu_root, menu);
1519 1524 }
1520 1525
1521 1526 out:
1522 1527 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1523 1528 assert((is_zfs(menu_root)) ^ (pool == NULL));
1524 1529 if (pool) {
1525 1530 (void) umount_top_dataset(pool, zmnted, zmntpt);
1526 1531 free(special);
1527 1532 }
1528 1533 menu_free(menu);
1529 1534 return (ret);
1530 1535 }
1531 1536
1532 1537
1533 1538 static error_t
1534 1539 bam_archive(
1535 1540 char *subcmd,
1536 1541 char *opt)
1537 1542 {
1538 1543 error_t ret;
1539 1544 error_t (*f)(char *root, char *opt);
1540 1545 const char *fcn = "bam_archive()";
1541 1546
1542 1547 /*
1543 1548 * Add trailing / for archive subcommands
1544 1549 */
1545 1550 if (rootbuf[strlen(rootbuf) - 1] != '/')
1546 1551 (void) strcat(rootbuf, "/");
1547 1552 bam_rootlen = strlen(rootbuf);
1548 1553
1549 1554 /*
1550 1555 * Check arguments
1551 1556 */
1552 1557 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1553 1558 if (ret != BAM_SUCCESS) {
1554 1559 return (BAM_ERROR);
1555 1560 }
1556 1561
1557 1562 ret = get_boot_cap(rootbuf);
1558 1563 if (ret != BAM_SUCCESS) {
1559 1564 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1560 1565 return (ret);
1561 1566 }
1562 1567
1563 1568 /*
1564 1569 * Check archive not supported with update_all
1565 1570 * since it is awkward to display out-of-sync
1566 1571 * information for each BE.
1567 1572 */
1568 1573 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1569 1574 bam_error(_("the check option is not supported with "
1570 1575 "subcmd: %s\n"), subcmd);
1571 1576 return (BAM_ERROR);
1572 1577 }
1573 1578
1574 1579 if (strcmp(subcmd, "update_all") == 0)
1575 1580 bam_update_all = 1;
1576 1581
1577 1582 #if !defined(_OBP)
1578 1583 ucode_install(bam_root);
1579 1584 #endif
1580 1585
1581 1586 ret = f(bam_root, opt);
1582 1587
1583 1588 bam_update_all = 0;
1584 1589
1585 1590 return (ret);
1586 1591 }
1587 1592
1588 1593 /*PRINTFLIKE1*/
1589 1594 void
1590 1595 bam_error(char *format, ...)
1591 1596 {
1592 1597 va_list ap;
1593 1598
1594 1599 va_start(ap, format);
1595 1600 (void) fprintf(stderr, "%s: ", prog);
1596 1601 (void) vfprintf(stderr, format, ap);
1597 1602 va_end(ap);
1598 1603 }
1599 1604
1600 1605 /*PRINTFLIKE1*/
1601 1606 void
1602 1607 bam_derror(char *format, ...)
1603 1608 {
1604 1609 va_list ap;
1605 1610
1606 1611 assert(bam_debug);
1607 1612
1608 1613 va_start(ap, format);
1609 1614 (void) fprintf(stderr, "DEBUG: ");
1610 1615 (void) vfprintf(stderr, format, ap);
1611 1616 va_end(ap);
1612 1617 }
1613 1618
1614 1619 /*PRINTFLIKE1*/
1615 1620 void
1616 1621 bam_print(char *format, ...)
1617 1622 {
1618 1623 va_list ap;
1619 1624
1620 1625 va_start(ap, format);
1621 1626 (void) vfprintf(stdout, format, ap);
1622 1627 va_end(ap);
1623 1628 }
1624 1629
1625 1630 /*PRINTFLIKE1*/
1626 1631 void
1627 1632 bam_print_stderr(char *format, ...)
1628 1633 {
1629 1634 va_list ap;
1630 1635
1631 1636 va_start(ap, format);
1632 1637 (void) vfprintf(stderr, format, ap);
1633 1638 va_end(ap);
1634 1639 }
1635 1640
1636 1641 void
1637 1642 bam_exit(int excode)
1638 1643 {
1639 1644 restore_env();
1640 1645 bam_unlock();
1641 1646 exit(excode);
1642 1647 }
1643 1648
1644 1649 static void
1645 1650 bam_lock(void)
1646 1651 {
1647 1652 struct flock lock;
1648 1653 pid_t pid;
1649 1654
1650 1655 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1651 1656 if (bam_lock_fd < 0) {
1652 1657 /*
1653 1658 * We may be invoked early in boot for archive verification.
1654 1659 * In this case, root is readonly and /var/run may not exist.
1655 1660 * Proceed without the lock
1656 1661 */
1657 1662 if (errno == EROFS || errno == ENOENT) {
1658 1663 bam_root_readonly = 1;
1659 1664 return;
1660 1665 }
1661 1666
1662 1667 bam_error(_("failed to open file: %s: %s\n"),
1663 1668 BAM_LOCK_FILE, strerror(errno));
1664 1669 bam_exit(1);
1665 1670 }
1666 1671
1667 1672 lock.l_type = F_WRLCK;
1668 1673 lock.l_whence = SEEK_SET;
1669 1674 lock.l_start = 0;
1670 1675 lock.l_len = 0;
1671 1676
1672 1677 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1673 1678 if (errno != EACCES && errno != EAGAIN) {
1674 1679 bam_error(_("failed to lock file: %s: %s\n"),
1675 1680 BAM_LOCK_FILE, strerror(errno));
1676 1681 (void) close(bam_lock_fd);
1677 1682 bam_lock_fd = -1;
1678 1683 bam_exit(1);
1679 1684 }
1680 1685 pid = 0;
1681 1686 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1682 1687 bam_print(
1683 1688 _("another instance of bootadm (pid %lu) is running\n"),
1684 1689 pid);
1685 1690
1686 1691 lock.l_type = F_WRLCK;
1687 1692 lock.l_whence = SEEK_SET;
1688 1693 lock.l_start = 0;
1689 1694 lock.l_len = 0;
1690 1695 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1691 1696 bam_error(_("failed to lock file: %s: %s\n"),
1692 1697 BAM_LOCK_FILE, strerror(errno));
1693 1698 (void) close(bam_lock_fd);
1694 1699 bam_lock_fd = -1;
1695 1700 bam_exit(1);
1696 1701 }
1697 1702 }
1698 1703
1699 1704 /* We own the lock now */
1700 1705 pid = getpid();
1701 1706 (void) write(bam_lock_fd, &pid, sizeof (pid));
1702 1707 }
1703 1708
1704 1709 static void
1705 1710 bam_unlock(void)
1706 1711 {
1707 1712 struct flock unlock;
1708 1713
1709 1714 /*
1710 1715 * NOP if we don't hold the lock
1711 1716 */
1712 1717 if (bam_lock_fd < 0) {
1713 1718 return;
1714 1719 }
1715 1720
1716 1721 unlock.l_type = F_UNLCK;
1717 1722 unlock.l_whence = SEEK_SET;
1718 1723 unlock.l_start = 0;
1719 1724 unlock.l_len = 0;
1720 1725
1721 1726 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1722 1727 bam_error(_("failed to unlock file: %s: %s\n"),
1723 1728 BAM_LOCK_FILE, strerror(errno));
1724 1729 }
1725 1730
1726 1731 if (close(bam_lock_fd) == -1) {
1727 1732 bam_error(_("failed to close file: %s: %s\n"),
1728 1733 BAM_LOCK_FILE, strerror(errno));
1729 1734 }
1730 1735 bam_lock_fd = -1;
1731 1736 }
1732 1737
1733 1738 static error_t
1734 1739 list_archive(char *root, char *opt)
1735 1740 {
1736 1741 filelist_t flist;
1737 1742 filelist_t *flistp = &flist;
1738 1743 line_t *lp;
1739 1744
1740 1745 assert(root);
1741 1746 assert(opt == NULL);
1742 1747
1743 1748 flistp->head = flistp->tail = NULL;
1744 1749 if (read_list(root, flistp) != BAM_SUCCESS) {
1745 1750 return (BAM_ERROR);
1746 1751 }
1747 1752 assert(flistp->head && flistp->tail);
1748 1753
1749 1754 for (lp = flistp->head; lp; lp = lp->next) {
1750 1755 bam_print(_("%s\n"), lp->line);
1751 1756 }
1752 1757
1753 1758 filelist_free(flistp);
1754 1759
1755 1760 return (BAM_SUCCESS);
1756 1761 }
1757 1762
1758 1763 /*
1759 1764 * This routine writes a list of lines to a file.
1760 1765 * The list is *not* freed
1761 1766 */
1762 1767 static error_t
1763 1768 list2file(char *root, char *tmp, char *final, line_t *start)
1764 1769 {
1765 1770 char tmpfile[PATH_MAX];
1766 1771 char path[PATH_MAX];
1767 1772 FILE *fp;
1768 1773 int ret;
1769 1774 struct stat sb;
1770 1775 mode_t mode;
1771 1776 uid_t root_uid;
1772 1777 gid_t sys_gid;
1773 1778 struct passwd *pw;
1774 1779 struct group *gp;
1775 1780 const char *fcn = "list2file()";
1776 1781
1777 1782 (void) snprintf(path, sizeof (path), "%s%s", root, final);
1778 1783
1779 1784 if (start == NULL) {
1780 1785 /* Empty GRUB menu */
1781 1786 if (stat(path, &sb) != -1) {
1782 1787 bam_print(_("file is empty, deleting file: %s\n"),
1783 1788 path);
1784 1789 if (unlink(path) != 0) {
1785 1790 bam_error(_("failed to unlink file: %s: %s\n"),
1786 1791 path, strerror(errno));
1787 1792 return (BAM_ERROR);
1788 1793 } else {
1789 1794 return (BAM_SUCCESS);
1790 1795 }
1791 1796 }
1792 1797 return (BAM_SUCCESS);
1793 1798 }
1794 1799
1795 1800 /*
1796 1801 * Preserve attributes of existing file if possible,
1797 1802 * otherwise ask the system for uid/gid of root/sys.
1798 1803 * If all fails, fall back on hard-coded defaults.
1799 1804 */
1800 1805 if (stat(path, &sb) != -1) {
1801 1806 mode = sb.st_mode;
1802 1807 root_uid = sb.st_uid;
1803 1808 sys_gid = sb.st_gid;
1804 1809 } else {
1805 1810 mode = DEFAULT_DEV_MODE;
1806 1811 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1807 1812 root_uid = pw->pw_uid;
1808 1813 } else {
1809 1814 bam_error(_("getpwnam: uid for %s failed, "
1810 1815 "defaulting to %d\n"),
1811 1816 DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1812 1817 root_uid = (uid_t)DEFAULT_DEV_UID;
1813 1818 }
1814 1819 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1815 1820 sys_gid = gp->gr_gid;
1816 1821 } else {
1817 1822 bam_error(_("getgrnam: gid for %s failed, "
1818 1823 "defaulting to %d\n"),
1819 1824 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1820 1825 sys_gid = (gid_t)DEFAULT_DEV_GID;
1821 1826 }
1822 1827 }
1823 1828
1824 1829 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1825 1830
1826 1831 /* Truncate tmpfile first */
1827 1832 fp = fopen(tmpfile, "w");
1828 1833 if (fp == NULL) {
1829 1834 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1830 1835 strerror(errno));
1831 1836 return (BAM_ERROR);
1832 1837 }
1833 1838 ret = fclose(fp);
1834 1839 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1835 1840 if (ret == EOF) {
1836 1841 bam_error(_("failed to close file: %s: %s\n"),
1837 1842 tmpfile, strerror(errno));
1838 1843 return (BAM_ERROR);
1839 1844 }
1840 1845
1841 1846 /* Now open it in append mode */
1842 1847 fp = fopen(tmpfile, "a");
1843 1848 if (fp == NULL) {
1844 1849 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1845 1850 strerror(errno));
1846 1851 return (BAM_ERROR);
1847 1852 }
1848 1853
1849 1854 for (; start; start = start->next) {
1850 1855 ret = s_fputs(start->line, fp);
1851 1856 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1852 1857 if (ret == EOF) {
1853 1858 bam_error(_("write to file failed: %s: %s\n"),
1854 1859 tmpfile, strerror(errno));
1855 1860 (void) fclose(fp);
1856 1861 return (BAM_ERROR);
1857 1862 }
1858 1863 }
1859 1864
1860 1865 ret = fclose(fp);
1861 1866 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1862 1867 if (ret == EOF) {
1863 1868 bam_error(_("failed to close file: %s: %s\n"),
1864 1869 tmpfile, strerror(errno));
1865 1870 return (BAM_ERROR);
1866 1871 }
1867 1872
1868 1873 /*
1869 1874 * Set up desired attributes. Ignore failures on filesystems
1870 1875 * not supporting these operations - pcfs reports unsupported
1871 1876 * operations as EINVAL.
1872 1877 */
1873 1878 ret = chmod(tmpfile, mode);
1874 1879 if (ret == -1 &&
1875 1880 errno != EINVAL && errno != ENOTSUP) {
1876 1881 bam_error(_("chmod operation on %s failed - %s\n"),
1877 1882 tmpfile, strerror(errno));
1878 1883 return (BAM_ERROR);
1879 1884 }
1880 1885
1881 1886 ret = chown(tmpfile, root_uid, sys_gid);
1882 1887 if (ret == -1 &&
1883 1888 errno != EINVAL && errno != ENOTSUP) {
1884 1889 bam_error(_("chgrp operation on %s failed - %s\n"),
1885 1890 tmpfile, strerror(errno));
1886 1891 return (BAM_ERROR);
1887 1892 }
1888 1893
1889 1894 /*
1890 1895 * Do an atomic rename
1891 1896 */
1892 1897 ret = rename(tmpfile, path);
1893 1898 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1894 1899 if (ret != 0) {
1895 1900 bam_error(_("rename to file failed: %s: %s\n"), path,
1896 1901 strerror(errno));
1897 1902 return (BAM_ERROR);
1898 1903 }
1899 1904
1900 1905 BAM_DPRINTF(("%s: wrote file successfully: %s\n", fcn, path));
1901 1906 return (BAM_SUCCESS);
1902 1907 }
1903 1908
1904 1909 /*
1905 1910 * Checks if the path specified (without the file name at the end) exists
1906 1911 * and creates it if not. If the path exists and is not a directory, an attempt
1907 1912 * to unlink is made.
1908 1913 */
1909 1914 static int
1910 1915 setup_path(char *path)
1911 1916 {
1912 1917 char *p;
1913 1918 int ret;
1914 1919 struct stat sb;
1915 1920
1916 1921 p = strrchr(path, '/');
1917 1922 if (p != NULL) {
1918 1923 *p = '\0';
1919 1924 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1920 1925 /* best effort attempt, mkdirp will catch the error */
1921 1926 (void) unlink(path);
1922 1927 if (bam_verbose)
1923 1928 bam_print(_("need to create directory "
1924 1929 "path for %s\n"), path);
1925 1930 ret = mkdirp(path, DIR_PERMS);
1926 1931 if (ret == -1) {
1927 1932 bam_error(_("mkdir of %s failed: %s\n"),
1928 1933 path, strerror(errno));
1929 1934 *p = '/';
1930 1935 return (BAM_ERROR);
1931 1936 }
1932 1937 }
1933 1938 *p = '/';
1934 1939 return (BAM_SUCCESS);
1935 1940 }
1936 1941 return (BAM_SUCCESS);
1937 1942 }
1938 1943
1939 1944 typedef union {
1940 1945 gzFile gzfile;
1941 1946 int fdfile;
1942 1947 } outfile;
1943 1948
1944 1949 typedef struct {
1945 1950 char path[PATH_MAX];
1946 1951 outfile out;
1947 1952 } cachefile;
1948 1953
1949 1954 static int
1950 1955 setup_file(char *base, const char *path, cachefile *cf)
1951 1956 {
1952 1957 int ret;
1953 1958 char *strip;
1954 1959
1955 1960 /* init gzfile or fdfile in case we fail before opening */
1956 1961 if (bam_direct == BAM_DIRECT_DBOOT)
1957 1962 cf->out.gzfile = NULL;
1958 1963 else
1959 1964 cf->out.fdfile = -1;
1960 1965
1961 1966 /* strip the trailing altroot path */
1962 1967 strip = (char *)path + strlen(rootbuf);
1963 1968
1964 1969 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1965 1970 if (ret >= sizeof (cf->path)) {
1966 1971 bam_error(_("unable to create path on mountpoint %s, "
1967 1972 "path too long\n"), rootbuf);
1968 1973 return (BAM_ERROR);
1969 1974 }
1970 1975
1971 1976 /* Check if path is present in the archive cache directory */
1972 1977 if (setup_path(cf->path) == BAM_ERROR)
1973 1978 return (BAM_ERROR);
1974 1979
1975 1980 if (bam_direct == BAM_DIRECT_DBOOT) {
1976 1981 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1977 1982 bam_error(_("failed to open file: %s: %s\n"),
1978 1983 cf->path, strerror(errno));
1979 1984 return (BAM_ERROR);
1980 1985 }
1981 1986 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1982 1987 Z_DEFAULT_STRATEGY);
1983 1988 } else {
1984 1989 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1985 1990 == -1) {
1986 1991 bam_error(_("failed to open file: %s: %s\n"),
1987 1992 cf->path, strerror(errno));
1988 1993 return (BAM_ERROR);
1989 1994 }
1990 1995 }
1991 1996
1992 1997 return (BAM_SUCCESS);
1993 1998 }
1994 1999
1995 2000 static int
1996 2001 cache_write(cachefile cf, char *buf, int size)
1997 2002 {
1998 2003 int err;
1999 2004
2000 2005 if (bam_direct == BAM_DIRECT_DBOOT) {
2001 2006 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
2002 2007 bam_error(_("failed to write to %s\n"),
2003 2008 gzerror(cf.out.gzfile, &err));
2004 2009 if (err == Z_ERRNO && bam_verbose) {
2005 2010 bam_error(_("write to file failed: %s: %s\n"),
2006 2011 cf.path, strerror(errno));
2007 2012 }
2008 2013 return (BAM_ERROR);
2009 2014 }
2010 2015 } else {
2011 2016 if (write(cf.out.fdfile, buf, size) < 1) {
2012 2017 bam_error(_("write to file failed: %s: %s\n"),
2013 2018 cf.path, strerror(errno));
2014 2019 return (BAM_ERROR);
2015 2020 }
2016 2021 }
2017 2022 return (BAM_SUCCESS);
2018 2023 }
2019 2024
2020 2025 static int
2021 2026 cache_close(cachefile cf)
2022 2027 {
2023 2028 int ret;
2024 2029
2025 2030 if (bam_direct == BAM_DIRECT_DBOOT) {
2026 2031 if (cf.out.gzfile) {
2027 2032 ret = gzclose(cf.out.gzfile);
2028 2033 if (ret != Z_OK) {
2029 2034 bam_error(_("failed to close file: %s: %s\n"),
2030 2035 cf.path, strerror(errno));
2031 2036 return (BAM_ERROR);
2032 2037 }
2033 2038 }
2034 2039 } else {
2035 2040 if (cf.out.fdfile != -1) {
2036 2041 ret = close(cf.out.fdfile);
2037 2042 if (ret != 0) {
2038 2043 bam_error(_("failed to close file: %s: %s\n"),
2039 2044 cf.path, strerror(errno));
2040 2045 return (BAM_ERROR);
2041 2046 }
2042 2047 }
2043 2048 }
2044 2049
2045 2050 return (BAM_SUCCESS);
2046 2051 }
2047 2052
2048 2053 static int
2049 2054 dircache_updatefile(const char *path, int what)
2050 2055 {
2051 2056 int ret, exitcode;
2052 2057 char buf[4096 * 4];
2053 2058 FILE *infile;
2054 2059 cachefile outfile, outupdt;
2055 2060
2056 2061 if (bam_nowrite()) {
2057 2062 set_dir_flag(what, NEED_UPDATE);
2058 2063 return (BAM_SUCCESS);
2059 2064 }
2060 2065
2061 2066 if (!has_cachedir(what))
2062 2067 return (BAM_SUCCESS);
2063 2068
2064 2069 if ((infile = fopen(path, "rb")) == NULL) {
2065 2070 bam_error(_("failed to open file: %s: %s\n"), path,
2066 2071 strerror(errno));
2067 2072 return (BAM_ERROR);
2068 2073 }
2069 2074
2070 2075 ret = setup_file(get_cachedir(what), path, &outfile);
2071 2076 if (ret == BAM_ERROR) {
2072 2077 exitcode = BAM_ERROR;
2073 2078 goto out;
2074 2079 }
2075 2080 if (!is_dir_flag_on(what, NO_MULTI)) {
2076 2081 ret = setup_file(get_updatedir(what), path, &outupdt);
2077 2082 if (ret == BAM_ERROR)
2078 2083 set_dir_flag(what, NO_MULTI);
2079 2084 }
2080 2085
2081 2086 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2082 2087 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2083 2088 exitcode = BAM_ERROR;
2084 2089 goto out;
2085 2090 }
2086 2091 if (!is_dir_flag_on(what, NO_MULTI))
2087 2092 if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2088 2093 set_dir_flag(what, NO_MULTI);
2089 2094 }
2090 2095
2091 2096 set_dir_flag(what, NEED_UPDATE);
2092 2097 get_count(what)++;
2093 2098 if (get_count(what) > COUNT_MAX)
2094 2099 set_dir_flag(what, NO_MULTI);
2095 2100 exitcode = BAM_SUCCESS;
2096 2101 out:
2097 2102 (void) fclose(infile);
2098 2103 if (cache_close(outfile) == BAM_ERROR)
2099 2104 exitcode = BAM_ERROR;
2100 2105 if (!is_dir_flag_on(what, NO_MULTI) &&
2101 2106 cache_close(outupdt) == BAM_ERROR)
2102 2107 exitcode = BAM_ERROR;
2103 2108 if (exitcode == BAM_ERROR)
2104 2109 set_flag(UPDATE_ERROR);
2105 2110 return (exitcode);
2106 2111 }
2107 2112
2108 2113 static int
2109 2114 dircache_updatedir(const char *path, int what, int updt)
2110 2115 {
2111 2116 int ret;
2112 2117 char dpath[PATH_MAX];
2113 2118 char *strip;
2114 2119 struct stat sb;
2115 2120
2116 2121 strip = (char *)path + strlen(rootbuf);
2117 2122
2118 2123 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2119 2124 get_updatedir(what) : get_cachedir(what), strip);
2120 2125
2121 2126 if (ret >= sizeof (dpath)) {
2122 2127 bam_error(_("unable to create path on mountpoint %s, "
2123 2128 "path too long\n"), rootbuf);
2124 2129 set_flag(UPDATE_ERROR);
2125 2130 return (BAM_ERROR);
2126 2131 }
2127 2132
2128 2133 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2129 2134 return (BAM_SUCCESS);
2130 2135
2131 2136 if (updt) {
2132 2137 if (!is_dir_flag_on(what, NO_MULTI))
2133 2138 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2134 2139 set_dir_flag(what, NO_MULTI);
2135 2140 } else {
2136 2141 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2137 2142 set_flag(UPDATE_ERROR);
2138 2143 return (BAM_ERROR);
2139 2144 }
2140 2145 }
2141 2146
2142 2147 set_dir_flag(what, NEED_UPDATE);
2143 2148 return (BAM_SUCCESS);
2144 2149 }
2145 2150
2146 2151 #define DO_CACHE_DIR 0
2147 2152 #define DO_UPDATE_DIR 1
2148 2153
2149 2154 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2150 2155 typedef Elf64_Ehdr _elfhdr;
2151 2156 #else
2152 2157 typedef Elf32_Ehdr _elfhdr;
2153 2158 #endif
2154 2159
2155 2160 /*
2156 2161 * This routine updates the contents of the cache directory
2157 2162 */
2158 2163 static int
2159 2164 update_dircache(const char *path, int flags)
2160 2165 {
2161 2166 int rc = BAM_SUCCESS;
2162 2167
2163 2168 switch (flags) {
2164 2169 case FTW_F:
2165 2170 {
2166 2171 int fd;
2167 2172 _elfhdr elf;
2168 2173
2169 2174 if ((fd = open(path, O_RDONLY)) < 0) {
2170 2175 bam_error(_("failed to open file: %s: %s\n"),
2171 2176 path, strerror(errno));
2172 2177 set_flag(UPDATE_ERROR);
2173 2178 rc = BAM_ERROR;
2174 2179 break;
2175 2180 }
2176 2181
2177 2182 /*
2178 2183 * libelf and gelf would be a cleaner and easier way to handle
2179 2184 * this, but libelf fails compilation if _ILP32 is defined &&
2180 2185 * _FILE_OFFSET_BITS is != 32 ...
2181 2186 */
2182 2187 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2183 2188 bam_error(_("read failed for file: %s: %s\n"),
2184 2189 path, strerror(errno));
2185 2190 set_flag(UPDATE_ERROR);
2186 2191 (void) close(fd);
2187 2192 rc = BAM_ERROR;
2188 2193 break;
2189 2194 }
2190 2195 (void) close(fd);
2191 2196
2192 2197 /*
2193 2198 * If the file is not an executable and is not inside an amd64
2194 2199 * directory, we copy it in both the cache directories,
2195 2200 * otherwise, we only copy it inside the 64-bit one.
2196 2201 */
2197 2202 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2198 2203 if (strstr(path, "/amd64")) {
2199 2204 rc = dircache_updatefile(path, FILE64);
2200 2205 } else {
2201 2206 rc = dircache_updatefile(path, FILE32);
2202 2207 if (rc == BAM_SUCCESS)
2203 2208 rc = dircache_updatefile(path, FILE64);
2204 2209 }
2205 2210 } else {
2206 2211 /*
2207 2212 * Based on the ELF class we copy the file in the 32-bit
2208 2213 * or the 64-bit cache directory.
2209 2214 */
2210 2215 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
2211 2216 rc = dircache_updatefile(path, FILE32);
2212 2217 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
2213 2218 rc = dircache_updatefile(path, FILE64);
2214 2219 } else {
2215 2220 bam_print(_("WARNING: file %s is neither a "
2216 2221 "32-bit nor a 64-bit ELF\n"), path);
2217 2222 /* paranoid */
2218 2223 rc = dircache_updatefile(path, FILE32);
2219 2224 if (rc == BAM_SUCCESS)
2220 2225 rc = dircache_updatefile(path, FILE64);
2221 2226 }
2222 2227 }
2223 2228 break;
2224 2229 }
2225 2230 case FTW_D:
2226 2231 if (strstr(path, "/amd64") == NULL) {
2227 2232 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
2228 2233 if (rc == BAM_SUCCESS)
2229 2234 rc = dircache_updatedir(path, FILE32,
2230 2235 DO_CACHE_DIR);
2231 2236 } else {
2232 2237 if (has_cachedir(FILE64)) {
2233 2238 rc = dircache_updatedir(path, FILE64,
2234 2239 DO_UPDATE_DIR);
2235 2240 if (rc == BAM_SUCCESS)
2236 2241 rc = dircache_updatedir(path, FILE64,
2237 2242 DO_CACHE_DIR);
2238 2243 }
2239 2244 }
2240 2245 break;
2241 2246 default:
2242 2247 rc = BAM_ERROR;
2243 2248 break;
2244 2249 }
2245 2250
2246 2251 return (rc);
2247 2252 }
2248 2253
2249 2254 /*ARGSUSED*/
2250 2255 static int
2251 2256 cmpstat(
2252 2257 const char *file,
2253 2258 const struct stat *st,
2254 2259 int flags,
2255 2260 struct FTW *ftw)
2256 2261 {
2257 2262 uint_t sz;
2258 2263 uint64_t *value;
2259 2264 uint64_t filestat[2];
2260 2265 int error, ret, status;
2261 2266
2262 2267 struct safefile *safefilep;
2263 2268 FILE *fp;
2264 2269 struct stat sb;
2265 2270 regex_t re;
2266 2271
2267 2272 /*
2268 2273 * On SPARC we create/update links too.
2269 2274 */
2270 2275 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2271 2276 !is_flag_on(IS_SPARC_TARGET)))
2272 2277 return (0);
2273 2278
2274 2279 /*
2275 2280 * Ignore broken links
2276 2281 */
2277 2282 if (flags == FTW_SL && stat(file, &sb) < 0)
2278 2283 return (0);
2279 2284
2280 2285 /*
2281 2286 * new_nvlp may be NULL if there were errors earlier
2282 2287 * but this is not fatal to update determination.
2283 2288 */
2284 2289 if (walk_arg.new_nvlp) {
2285 2290 filestat[0] = st->st_size;
2286 2291 filestat[1] = st->st_mtime;
2287 2292 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2288 2293 file + bam_rootlen, filestat, 2);
2289 2294 if (error)
2290 2295 bam_error(_("failed to update stat data for: %s: %s\n"),
2291 2296 file, strerror(error));
2292 2297 }
2293 2298
2294 2299 /*
2295 2300 * If we are invoked as part of system/filesystem/boot-archive, then
2296 2301 * there are a number of things we should not worry about
2297 2302 */
2298 2303 if (bam_smf_check) {
2299 2304 /* ignore amd64 modules unless we are booted amd64. */
2300 2305 if (!is_amd64() && strstr(file, "/amd64/") != 0)
2301 2306 return (0);
2302 2307
2303 2308 /* read in list of safe files */
2304 2309 if (safefiles == NULL) {
2305 2310 fp = fopen("/boot/solaris/filelist.safe", "r");
2306 2311 if (fp != NULL) {
2307 2312 safefiles = s_calloc(1,
2308 2313 sizeof (struct safefile));
2309 2314 safefilep = safefiles;
2310 2315 safefilep->name = s_calloc(1, MAXPATHLEN +
2311 2316 MAXNAMELEN);
2312 2317 safefilep->next = NULL;
2313 2318 while (s_fgets(safefilep->name, MAXPATHLEN +
2314 2319 MAXNAMELEN, fp) != NULL) {
2315 2320 safefilep->next = s_calloc(1,
2316 2321 sizeof (struct safefile));
2317 2322 safefilep = safefilep->next;
2318 2323 safefilep->name = s_calloc(1,
2319 2324 MAXPATHLEN + MAXNAMELEN);
2320 2325 safefilep->next = NULL;
2321 2326 }
2322 2327 (void) fclose(fp);
2323 2328 }
2324 2329 }
2325 2330 }
2326 2331
2327 2332 /*
2328 2333 * On SPARC we create a -path-list file for mkisofs
2329 2334 */
2330 2335 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2331 2336 if (flags != FTW_D) {
2332 2337 char *strip;
2333 2338
2334 2339 strip = (char *)file + strlen(rootbuf);
2335 2340 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2336 2341 file);
2337 2342 }
2338 2343 }
2339 2344
2340 2345 /*
2341 2346 * We are transitioning from the old model to the dircache or the cache
2342 2347 * directory was removed: create the entry without further checkings.
2343 2348 */
2344 2349 if (is_flag_on(NEED_CACHE_DIR)) {
2345 2350 if (bam_verbose)
2346 2351 bam_print(_(" new %s\n"), file);
2347 2352
2348 2353 if (is_flag_on(IS_SPARC_TARGET)) {
2349 2354 set_dir_flag(FILE64, NEED_UPDATE);
2350 2355 return (0);
2351 2356 }
2352 2357
2353 2358 ret = update_dircache(file, flags);
2354 2359 if (ret == BAM_ERROR) {
2355 2360 bam_error(_("directory cache update failed for %s\n"),
2356 2361 file);
2357 2362 return (-1);
2358 2363 }
2359 2364
2360 2365 return (0);
2361 2366 }
2362 2367
2363 2368 /*
2364 2369 * We need an update if file doesn't exist in old archive
2365 2370 */
2366 2371 if (walk_arg.old_nvlp == NULL ||
2367 2372 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2368 2373 file + bam_rootlen, &value, &sz) != 0) {
2369 2374 if (bam_smf_check) /* ignore new during smf check */
2370 2375 return (0);
2371 2376
2372 2377 if (is_flag_on(IS_SPARC_TARGET)) {
2373 2378 set_dir_flag(FILE64, NEED_UPDATE);
2374 2379 } else {
2375 2380 ret = update_dircache(file, flags);
2376 2381 if (ret == BAM_ERROR) {
2377 2382 bam_error(_("directory cache update "
2378 2383 "failed for %s\n"), file);
2379 2384 return (-1);
2380 2385 }
2381 2386 }
2382 2387
2383 2388 if (bam_verbose)
2384 2389 bam_print(_(" new %s\n"), file);
|
↓ open down ↓ |
2107 lines elided |
↑ open up ↑ |
2385 2390 return (0);
2386 2391 }
2387 2392
2388 2393 /*
2389 2394 * If we got there, the file is already listed as to be included in the
2390 2395 * iso image. We just need to know if we are going to rebuild it or not
2391 2396 */
2392 2397 if (is_flag_on(IS_SPARC_TARGET) &&
2393 2398 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
2394 2399 return (0);
2400 +
2401 +
2395 2402 /*
2396 2403 * File exists in old archive. Check if file has changed
2397 2404 */
2398 2405 assert(sz == 2);
2399 2406 bcopy(value, filestat, sizeof (filestat));
2400 2407
2401 2408 if (flags != FTW_D && (filestat[0] != st->st_size ||
2402 2409 filestat[1] != st->st_mtime)) {
2403 2410 if (bam_smf_check) {
2404 2411 safefilep = safefiles;
2405 2412 while (safefilep != NULL &&
2406 2413 safefilep->name[0] != '\0') {
2407 2414 if (regcomp(&re, safefilep->name,
2408 2415 REG_EXTENDED|REG_NOSUB) == 0) {
2409 2416 status = regexec(&re,
2410 2417 file + bam_rootlen, 0, NULL, 0);
2411 2418 regfree(&re);
2412 2419 if (status == 0) {
2413 2420 (void) creat(
2414 2421 NEED_UPDATE_SAFE_FILE,
2415 2422 0644);
2416 2423 return (0);
2417 2424 }
2418 2425 }
2419 2426 safefilep = safefilep->next;
2420 2427 }
2421 2428 }
2422 2429
2423 2430 if (is_flag_on(IS_SPARC_TARGET)) {
|
↓ open down ↓ |
19 lines elided |
↑ open up ↑ |
2424 2431 set_dir_flag(FILE64, NEED_UPDATE);
2425 2432 } else {
2426 2433 ret = update_dircache(file, flags);
2427 2434 if (ret == BAM_ERROR) {
2428 2435 bam_error(_("directory cache update failed "
2429 2436 "for %s\n"), file);
2430 2437 return (-1);
2431 2438 }
2432 2439 }
2433 2440
2441 + /*
2442 + * Update self-assembly file if there are changes in
2443 + * /etc/system.d directory
2444 + */
2445 + if (strstr(file, ETC_SYSTEM_DIR)) {
2446 + ret = update_dircache(self_assembly, flags);
2447 + if (ret == BAM_ERROR) {
2448 + bam_error(_("directory cache update failed "
2449 + "for %s\n"), file);
2450 + return (-1);
2451 + }
2452 + }
2453 +
2434 2454 if (bam_verbose) {
2435 2455 if (bam_smf_check)
2436 2456 bam_print(" %s\n", file);
2437 2457 else
2438 2458 bam_print(_(" changed %s\n"), file);
2439 2459 }
2440 2460 }
2441 2461
2442 2462 return (0);
2443 2463 }
2444 2464
2445 2465 /*
2446 2466 * Remove a directory path recursively
2447 2467 */
2448 2468 static int
2449 2469 rmdir_r(char *path)
2450 2470 {
2451 2471 struct dirent *d = NULL;
2452 2472 DIR *dir = NULL;
2453 2473 char tpath[PATH_MAX];
2454 2474 struct stat sb;
2455 2475
2456 2476 if ((dir = opendir(path)) == NULL)
2457 2477 return (-1);
2458 2478
2459 2479 while ((d = readdir(dir)) != NULL) {
2460 2480 if ((strcmp(d->d_name, ".") != 0) &&
2461 2481 (strcmp(d->d_name, "..") != 0)) {
2462 2482 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
2463 2483 path, d->d_name);
2464 2484 if (stat(tpath, &sb) == 0) {
2465 2485 if (sb.st_mode & S_IFDIR)
2466 2486 (void) rmdir_r(tpath);
2467 2487 else
2468 2488 (void) remove(tpath);
2469 2489 }
2470 2490 }
2471 2491 }
2472 2492 return (remove(path));
2473 2493 }
2474 2494
2475 2495 /*
2476 2496 * Check if cache directory exists and, if not, create it and update flags
2477 2497 * accordingly. If the path exists, but it's not a directory, a best effort
2478 2498 * attempt to remove and recreate it is made.
2479 2499 * If the user requested a 'purge', always recreate the directory from scratch.
2480 2500 */
2481 2501 static int
2482 2502 set_cache_dir(char *root, int what)
2483 2503 {
2484 2504 struct stat sb;
2485 2505 int ret = 0;
2486 2506
2487 2507 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
2488 2508 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
2489 2509 "/amd64" : "", CACHEDIR_SUFFIX);
2490 2510
2491 2511 if (ret >= sizeof (get_cachedir(what))) {
2492 2512 bam_error(_("unable to create path on mountpoint %s, "
2493 2513 "path too long\n"), rootbuf);
2494 2514 return (BAM_ERROR);
2495 2515 }
2496 2516
2497 2517 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2498 2518 (void) rmdir_r(get_cachedir(what));
2499 2519
2500 2520 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2501 2521 /* best effort unlink attempt, mkdir will catch errors */
2502 2522 (void) unlink(get_cachedir(what));
2503 2523
2504 2524 if (bam_verbose)
2505 2525 bam_print(_("archive cache directory not found: %s\n"),
2506 2526 get_cachedir(what));
2507 2527 ret = mkdir(get_cachedir(what), DIR_PERMS);
2508 2528 if (ret < 0) {
2509 2529 bam_error(_("mkdir of %s failed: %s\n"),
2510 2530 get_cachedir(what), strerror(errno));
2511 2531 get_cachedir(what)[0] = '\0';
2512 2532 return (ret);
2513 2533 }
2514 2534 set_flag(NEED_CACHE_DIR);
2515 2535 set_dir_flag(what, NO_MULTI);
2516 2536 }
2517 2537
2518 2538 return (BAM_SUCCESS);
2519 2539 }
2520 2540
2521 2541 static int
2522 2542 set_update_dir(char *root, int what)
2523 2543 {
2524 2544 struct stat sb;
2525 2545 int ret;
2526 2546
2527 2547 if (is_dir_flag_on(what, NO_MULTI))
2528 2548 return (BAM_SUCCESS);
2529 2549
2530 2550 if (!bam_extend) {
2531 2551 set_dir_flag(what, NO_MULTI);
2532 2552 return (BAM_SUCCESS);
2533 2553 }
2534 2554
2535 2555 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2536 2556 ret = snprintf(get_updatedir(what),
2537 2557 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2538 2558 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2539 2559 else
2540 2560 ret = snprintf(get_updatedir(what),
2541 2561 sizeof (get_updatedir(what)), "%s%s%s%s", root,
2542 2562 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2543 2563
2544 2564 if (ret >= sizeof (get_updatedir(what))) {
2545 2565 bam_error(_("unable to create path on mountpoint %s, "
2546 2566 "path too long\n"), rootbuf);
2547 2567 return (BAM_ERROR);
2548 2568 }
2549 2569
2550 2570 if (stat(get_updatedir(what), &sb) == 0) {
2551 2571 if (S_ISDIR(sb.st_mode))
2552 2572 ret = rmdir_r(get_updatedir(what));
2553 2573 else
2554 2574 ret = unlink(get_updatedir(what));
2555 2575
2556 2576 if (ret != 0)
2557 2577 set_dir_flag(what, NO_MULTI);
2558 2578 }
2559 2579
2560 2580 if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2561 2581 set_dir_flag(what, NO_MULTI);
2562 2582
2563 2583 return (BAM_SUCCESS);
2564 2584 }
2565 2585
2566 2586 static int
2567 2587 is_valid_archive(char *root, int what)
2568 2588 {
2569 2589 char archive_path[PATH_MAX];
2570 2590 char timestamp_path[PATH_MAX];
2571 2591 struct stat sb, timestamp;
2572 2592 int ret;
2573 2593
2574 2594 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2575 2595 ret = snprintf(archive_path, sizeof (archive_path),
2576 2596 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2577 2597 ARCHIVE_SUFFIX);
2578 2598 else
2579 2599 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2580 2600 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2581 2601
2582 2602 if (ret >= sizeof (archive_path)) {
2583 2603 bam_error(_("unable to create path on mountpoint %s, "
2584 2604 "path too long\n"), rootbuf);
2585 2605 return (BAM_ERROR);
2586 2606 }
2587 2607
2588 2608 if (stat(archive_path, &sb) != 0) {
2589 2609 if (bam_verbose && !bam_check)
2590 2610 bam_print(_("archive not found: %s\n"), archive_path);
2591 2611 set_dir_flag(what, NEED_UPDATE);
2592 2612 set_dir_flag(what, NO_MULTI);
2593 2613 return (BAM_SUCCESS);
2594 2614 }
2595 2615
2596 2616 /*
2597 2617 * The timestamp file is used to prevent stale files in the archive
2598 2618 * cache.
2599 2619 * Stale files can happen if the system is booted back and forth across
2600 2620 * the transition from bootadm-before-the-cache to
2601 2621 * bootadm-after-the-cache, since older versions of bootadm don't know
2602 2622 * about the existence of the archive cache.
2603 2623 *
2604 2624 * Since only bootadm-after-the-cache versions know about about this
2605 2625 * file, we require that the boot archive be older than this file.
2606 2626 */
2607 2627 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2608 2628 FILE_STAT_TIMESTAMP);
2609 2629
2610 2630 if (ret >= sizeof (timestamp_path)) {
2611 2631 bam_error(_("unable to create path on mountpoint %s, "
2612 2632 "path too long\n"), rootbuf);
2613 2633 return (BAM_ERROR);
2614 2634 }
2615 2635
2616 2636 if (stat(timestamp_path, ×tamp) != 0 ||
2617 2637 sb.st_mtime > timestamp.st_mtime) {
2618 2638 if (bam_verbose && !bam_check)
2619 2639 bam_print(
2620 2640 _("archive cache is out of sync. Rebuilding.\n"));
2621 2641 /*
2622 2642 * Don't generate a false positive for the boot-archive service
2623 2643 * but trigger an update of the archive cache in
2624 2644 * boot-archive-update.
2625 2645 */
2626 2646 if (bam_smf_check) {
2627 2647 (void) creat(NEED_UPDATE_FILE, 0644);
2628 2648 return (BAM_SUCCESS);
2629 2649 }
2630 2650
2631 2651 set_flag(INVALIDATE_CACHE);
2632 2652 set_dir_flag(what, NEED_UPDATE);
2633 2653 set_dir_flag(what, NO_MULTI);
2634 2654 return (BAM_SUCCESS);
2635 2655 }
2636 2656
2637 2657 if (is_flag_on(IS_SPARC_TARGET))
2638 2658 return (BAM_SUCCESS);
2639 2659
2640 2660 if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2641 2661 if (bam_verbose && !bam_check)
2642 2662 bam_print(_("archive %s is bigger than %d bytes and "
2643 2663 "will be rebuilt\n"), archive_path, BA_SIZE_MAX);
2644 2664 set_dir_flag(what, NO_MULTI);
2645 2665 }
2646 2666
2647 2667 return (BAM_SUCCESS);
2648 2668 }
2649 2669
2650 2670 /*
2651 2671 * Check flags and presence of required files and directories.
2652 2672 * The force flag and/or absence of files should
2653 2673 * trigger an update.
2654 2674 * Suppress stdout output if check (-n) option is set
2655 2675 * (as -n should only produce parseable output.)
2656 2676 */
2657 2677 static int
2658 2678 check_flags_and_files(char *root)
2659 2679 {
2660 2680
2661 2681 struct stat sb;
2662 2682 int ret;
2663 2683
2664 2684 /*
2665 2685 * If archive is missing, create archive
2666 2686 */
2667 2687 if (is_flag_on(IS_SPARC_TARGET)) {
2668 2688 ret = is_valid_archive(root, FILE64);
2669 2689 if (ret == BAM_ERROR)
2670 2690 return (BAM_ERROR);
2671 2691 } else {
2672 2692 int what = FILE32;
2673 2693 do {
2674 2694 ret = is_valid_archive(root, what);
2675 2695 if (ret == BAM_ERROR)
2676 2696 return (BAM_ERROR);
2677 2697 what++;
2678 2698 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2679 2699 }
2680 2700
2681 2701 if (bam_nowrite())
2682 2702 return (BAM_SUCCESS);
2683 2703
2684 2704
2685 2705 /*
2686 2706 * check if cache directories exist on x86.
2687 2707 * check (and always open) the cache file on SPARC.
2688 2708 */
2689 2709 if (is_sparc()) {
2690 2710 ret = snprintf(get_cachedir(FILE64),
2691 2711 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2692 2712 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2693 2713
2694 2714 if (ret >= sizeof (get_cachedir(FILE64))) {
2695 2715 bam_error(_("unable to create path on mountpoint %s, "
2696 2716 "path too long\n"), rootbuf);
2697 2717 return (BAM_ERROR);
2698 2718 }
2699 2719
2700 2720 if (stat(get_cachedir(FILE64), &sb) != 0) {
2701 2721 set_flag(NEED_CACHE_DIR);
2702 2722 set_dir_flag(FILE64, NEED_UPDATE);
2703 2723 }
2704 2724
2705 2725 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2706 2726 if (walk_arg.sparcfile == NULL) {
2707 2727 bam_error(_("failed to open file: %s: %s\n"),
2708 2728 get_cachedir(FILE64), strerror(errno));
2709 2729 return (BAM_ERROR);
2710 2730 }
2711 2731
2712 2732 set_dir_present(FILE64);
2713 2733 } else {
2714 2734 int what = FILE32;
2715 2735
2716 2736 do {
2717 2737 if (set_cache_dir(root, what) != 0)
2718 2738 return (BAM_ERROR);
2719 2739
2720 2740 set_dir_present(what);
2721 2741
2722 2742 if (set_update_dir(root, what) != 0)
2723 2743 return (BAM_ERROR);
2724 2744 what++;
2725 2745 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2726 2746 }
2727 2747
2728 2748 /*
2729 2749 * if force, create archive unconditionally
2730 2750 */
2731 2751 if (bam_force) {
2732 2752 if (!is_sparc())
2733 2753 set_dir_flag(FILE32, NEED_UPDATE);
2734 2754 set_dir_flag(FILE64, NEED_UPDATE);
2735 2755 if (bam_verbose)
2736 2756 bam_print(_("forced update of archive requested\n"));
2737 2757 return (BAM_SUCCESS);
2738 2758 }
2739 2759
2740 2760 return (BAM_SUCCESS);
2741 2761 }
2742 2762
2743 2763 static error_t
2744 2764 read_one_list(char *root, filelist_t *flistp, char *filelist)
2745 2765 {
2746 2766 char path[PATH_MAX];
2747 2767 FILE *fp;
2748 2768 char buf[BAM_MAXLINE];
2749 2769 const char *fcn = "read_one_list()";
2750 2770
2751 2771 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2752 2772
2753 2773 fp = fopen(path, "r");
2754 2774 if (fp == NULL) {
2755 2775 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2756 2776 fcn, path, strerror(errno)));
2757 2777 return (BAM_ERROR);
2758 2778 }
2759 2779 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2760 2780 /* skip blank lines */
2761 2781 if (strspn(buf, " \t") == strlen(buf))
2762 2782 continue;
2763 2783 append_to_flist(flistp, buf);
2764 2784 }
2765 2785 if (fclose(fp) != 0) {
2766 2786 bam_error(_("failed to close file: %s: %s\n"),
2767 2787 path, strerror(errno));
2768 2788 return (BAM_ERROR);
2769 2789 }
2770 2790 return (BAM_SUCCESS);
2771 2791 }
2772 2792
2773 2793 static error_t
2774 2794 read_list(char *root, filelist_t *flistp)
2775 2795 {
2776 2796 char path[PATH_MAX];
2777 2797 char cmd[PATH_MAX];
2778 2798 struct stat sb;
2779 2799 int n, rval;
2780 2800 const char *fcn = "read_list()";
2781 2801
2782 2802 flistp->head = flistp->tail = NULL;
2783 2803
2784 2804 /*
2785 2805 * build and check path to extract_boot_filelist.ksh
2786 2806 */
2787 2807 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2788 2808 if (n >= sizeof (path)) {
2789 2809 bam_error(_("archive filelist is empty\n"));
2790 2810 return (BAM_ERROR);
2791 2811 }
2792 2812
2793 2813 if (is_safe_exec(path) == BAM_ERROR)
2794 2814 return (BAM_ERROR);
2795 2815
2796 2816 /*
2797 2817 * If extract_boot_filelist is present, exec it, otherwise read
2798 2818 * the filelists directly, for compatibility with older images.
2799 2819 */
2800 2820 if (stat(path, &sb) == 0) {
2801 2821 /*
2802 2822 * build arguments to exec extract_boot_filelist.ksh
2803 2823 */
2804 2824 char *rootarg, *platarg;
2805 2825 int platarglen = 1, rootarglen = 1;
2806 2826 if (strlen(root) > 1)
2807 2827 rootarglen += strlen(root) + strlen("-R ");
2808 2828 if (bam_alt_platform)
2809 2829 platarglen += strlen(bam_platform) + strlen("-p ");
2810 2830 platarg = s_calloc(1, platarglen);
2811 2831 rootarg = s_calloc(1, rootarglen);
2812 2832 *platarg = 0;
2813 2833 *rootarg = 0;
2814 2834
2815 2835 if (strlen(root) > 1) {
2816 2836 (void) snprintf(rootarg, rootarglen,
2817 2837 "-R %s", root);
2818 2838 }
2819 2839 if (bam_alt_platform) {
2820 2840 (void) snprintf(platarg, platarglen,
2821 2841 "-p %s", bam_platform);
2822 2842 }
2823 2843 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2824 2844 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2825 2845 free(platarg);
2826 2846 free(rootarg);
2827 2847 if (n >= sizeof (cmd)) {
2828 2848 bam_error(_("archive filelist is empty\n"));
2829 2849 return (BAM_ERROR);
2830 2850 }
2831 2851 if (exec_cmd(cmd, flistp) != 0) {
2832 2852 BAM_DPRINTF(("%s: failed to open archive "
2833 2853 "filelist: %s: %s\n", fcn, path, strerror(errno)));
2834 2854 return (BAM_ERROR);
2835 2855 }
2836 2856 } else {
2837 2857 /*
2838 2858 * Read current lists of files - only the first is mandatory
2839 2859 */
2840 2860 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2841 2861 if (rval != BAM_SUCCESS)
2842 2862 return (rval);
2843 2863 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2844 2864 }
2845 2865
2846 2866 if (flistp->head == NULL) {
2847 2867 bam_error(_("archive filelist is empty\n"));
2848 2868 return (BAM_ERROR);
2849 2869 }
2850 2870
2851 2871 return (BAM_SUCCESS);
2852 2872 }
2853 2873
2854 2874 static void
2855 2875 getoldstat(char *root)
2856 2876 {
2857 2877 char path[PATH_MAX];
2858 2878 int fd, error;
2859 2879 struct stat sb;
2860 2880 char *ostat;
2861 2881
2862 2882 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2863 2883 fd = open(path, O_RDONLY);
2864 2884 if (fd == -1) {
2865 2885 if (bam_verbose)
2866 2886 bam_print(_("failed to open file: %s: %s\n"),
2867 2887 path, strerror(errno));
2868 2888 goto out_err;
2869 2889 }
2870 2890
2871 2891 if (fstat(fd, &sb) != 0) {
2872 2892 bam_error(_("stat of file failed: %s: %s\n"), path,
2873 2893 strerror(errno));
2874 2894 goto out_err;
2875 2895 }
2876 2896
2877 2897 ostat = s_calloc(1, sb.st_size);
2878 2898
2879 2899 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2880 2900 bam_error(_("read failed for file: %s: %s\n"), path,
2881 2901 strerror(errno));
2882 2902 free(ostat);
2883 2903 goto out_err;
2884 2904 }
2885 2905
2886 2906 (void) close(fd);
2887 2907 fd = -1;
2888 2908
2889 2909 walk_arg.old_nvlp = NULL;
2890 2910 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2891 2911
2892 2912 free(ostat);
2893 2913
2894 2914 if (error) {
2895 2915 bam_error(_("failed to unpack stat data: %s: %s\n"),
2896 2916 path, strerror(error));
2897 2917 walk_arg.old_nvlp = NULL;
2898 2918 goto out_err;
2899 2919 } else {
2900 2920 return;
2901 2921 }
2902 2922
2903 2923 out_err:
2904 2924 if (fd != -1)
2905 2925 (void) close(fd);
2906 2926 if (!is_flag_on(IS_SPARC_TARGET))
2907 2927 set_dir_flag(FILE32, NEED_UPDATE);
2908 2928 set_dir_flag(FILE64, NEED_UPDATE);
2909 2929 }
2910 2930
2911 2931 /* Best effort stale entry removal */
2912 2932 static void
2913 2933 delete_stale(char *file, int what)
2914 2934 {
2915 2935 char path[PATH_MAX];
2916 2936 struct stat sb;
2917 2937
2918 2938 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2919 2939 if (!bam_check && stat(path, &sb) == 0) {
2920 2940 if (sb.st_mode & S_IFDIR)
2921 2941 (void) rmdir_r(path);
2922 2942 else
2923 2943 (void) unlink(path);
2924 2944
2925 2945 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2926 2946 }
2927 2947 }
2928 2948
2929 2949 /*
2930 2950 * Checks if a file in the current (old) archive has
2931 2951 * been deleted from the root filesystem. This is needed for
2932 2952 * software like Trusted Extensions (TX) that switch early
2933 2953 * in boot based on presence/absence of a kernel module.
2934 2954 */
2935 2955 static void
2936 2956 check4stale(char *root)
2937 2957 {
2938 2958 nvpair_t *nvp;
2939 2959 nvlist_t *nvlp;
2940 2960 char *file;
2941 2961 char path[PATH_MAX];
2942 2962
2943 2963 /*
2944 2964 * Skip stale file check during smf check
2945 2965 */
2946 2966 if (bam_smf_check)
2947 2967 return;
2948 2968
2949 2969 /*
2950 2970 * If we need to (re)create the cache, there's no need to check for
2951 2971 * stale files
2952 2972 */
2953 2973 if (is_flag_on(NEED_CACHE_DIR))
2954 2974 return;
2955 2975
2956 2976 /* Nothing to do if no old stats */
2957 2977 if ((nvlp = walk_arg.old_nvlp) == NULL)
2958 2978 return;
2959 2979
2960 2980 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2961 2981 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2962 2982 file = nvpair_name(nvp);
2963 2983 if (file == NULL)
2964 2984 continue;
2965 2985 (void) snprintf(path, sizeof (path), "%s/%s",
2966 2986 root, file);
2967 2987 if (access(path, F_OK) < 0) {
2968 2988 int what;
2969 2989
2970 2990 if (bam_verbose)
2971 2991 bam_print(_(" stale %s\n"), path);
2972 2992
2973 2993 if (is_flag_on(IS_SPARC_TARGET)) {
2974 2994 set_dir_flag(FILE64, NEED_UPDATE);
2975 2995 } else {
2976 2996 for (what = FILE32; what < CACHEDIR_NUM; what++)
2977 2997 if (has_cachedir(what))
2978 2998 delete_stale(file, what);
2979 2999 }
2980 3000 }
2981 3001 }
2982 3002 }
2983 3003
2984 3004 static void
2985 3005 create_newstat(void)
2986 3006 {
2987 3007 int error;
2988 3008
2989 3009 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2990 3010 if (error) {
2991 3011 /*
2992 3012 * Not fatal - we can still create archive
2993 3013 */
2994 3014 walk_arg.new_nvlp = NULL;
2995 3015 bam_error(_("failed to create stat data: %s\n"),
2996 3016 strerror(error));
2997 3017 }
2998 3018 }
2999 3019
3000 3020 static int
3001 3021 walk_list(char *root, filelist_t *flistp)
3002 3022 {
3003 3023 char path[PATH_MAX];
3004 3024 line_t *lp;
3005 3025
3006 3026 for (lp = flistp->head; lp; lp = lp->next) {
3007 3027 /*
3008 3028 * Don't follow symlinks. A symlink must refer to
3009 3029 * a file that would appear in the archive through
3010 3030 * a direct reference. This matches the archive
3011 3031 * construction behavior.
3012 3032 */
3013 3033 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
3014 3034 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
3015 3035 if (is_flag_on(UPDATE_ERROR))
3016 3036 return (BAM_ERROR);
3017 3037 /*
3018 3038 * Some files may not exist.
3019 3039 * For example: etc/rtc_config on a x86 diskless system
3020 3040 * Emit verbose message only
3021 3041 */
3022 3042 if (bam_verbose)
3023 3043 bam_print(_("cannot find: %s: %s\n"),
3024 3044 path, strerror(errno));
3025 3045 }
3026 3046 }
3027 3047
3028 3048 return (BAM_SUCCESS);
3029 3049 }
3030 3050
3031 3051 /*
3032 3052 * Update the timestamp file.
3033 3053 */
3034 3054 static void
3035 3055 update_timestamp(char *root)
3036 3056 {
3037 3057 char timestamp_path[PATH_MAX];
3038 3058
3039 3059 /* this path length has already been checked in check_flags_and_files */
3040 3060 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
3041 3061 FILE_STAT_TIMESTAMP);
3042 3062
3043 3063 /*
3044 3064 * recreate the timestamp file. Since an outdated or absent timestamp
3045 3065 * file translates in a complete rebuild of the archive cache, notify
3046 3066 * the user of the performance issue.
3047 3067 */
3048 3068 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
3049 3069 bam_error(_("failed to open file: %s: %s\n"), timestamp_path,
3050 3070 strerror(errno));
3051 3071 bam_error(_("failed to update the timestamp file, next"
3052 3072 " archive update may experience reduced performance\n"));
3053 3073 }
3054 3074 }
3055 3075
3056 3076
3057 3077 static void
3058 3078 savenew(char *root)
3059 3079 {
3060 3080 char path[PATH_MAX];
3061 3081 char path2[PATH_MAX];
3062 3082 size_t sz;
3063 3083 char *nstat;
3064 3084 int fd, wrote, error;
3065 3085
3066 3086 nstat = NULL;
3067 3087 sz = 0;
3068 3088 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3069 3089 NV_ENCODE_XDR, 0);
3070 3090 if (error) {
3071 3091 bam_error(_("failed to pack stat data: %s\n"),
3072 3092 strerror(error));
3073 3093 return;
3074 3094 }
3075 3095
3076 3096 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3077 3097 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3078 3098 if (fd == -1) {
3079 3099 bam_error(_("failed to open file: %s: %s\n"), path,
3080 3100 strerror(errno));
3081 3101 free(nstat);
3082 3102 return;
3083 3103 }
3084 3104 wrote = write(fd, nstat, sz);
3085 3105 if (wrote != sz) {
3086 3106 bam_error(_("write to file failed: %s: %s\n"), path,
3087 3107 strerror(errno));
3088 3108 (void) close(fd);
3089 3109 free(nstat);
3090 3110 return;
3091 3111 }
3092 3112 (void) close(fd);
3093 3113 free(nstat);
3094 3114
3095 3115 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3096 3116 if (rename(path, path2) != 0) {
3097 3117 bam_error(_("rename to file failed: %s: %s\n"), path2,
3098 3118 strerror(errno));
3099 3119 }
3100 3120 }
3101 3121
3102 3122 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3103 3123
3104 3124 static void
3105 3125 clear_walk_args(void)
3106 3126 {
3107 3127 nvlist_free(walk_arg.old_nvlp);
3108 3128 nvlist_free(walk_arg.new_nvlp);
3109 3129 if (walk_arg.sparcfile)
3110 3130 (void) fclose(walk_arg.sparcfile);
3111 3131 walk_arg.old_nvlp = NULL;
3112 3132 walk_arg.new_nvlp = NULL;
3113 3133 walk_arg.sparcfile = NULL;
3114 3134 }
3115 3135
3116 3136 /*
3117 3137 * Returns:
3118 3138 * 0 - no update necessary
3119 3139 * 1 - update required.
3120 3140 * BAM_ERROR (-1) - An error occurred
3121 3141 *
3122 3142 * Special handling for check (-n):
3123 3143 * ================================
3124 3144 * The check (-n) option produces parseable output.
3125 3145 * To do this, we suppress all stdout messages unrelated
3126 3146 * to out of sync files.
3127 3147 * All stderr messages are still printed though.
3128 3148 *
3129 3149 */
3130 3150 static int
3131 3151 update_required(char *root)
3132 3152 {
3133 3153 struct stat sb;
3134 3154 char path[PATH_MAX];
3135 3155 filelist_t flist;
3136 3156 filelist_t *flistp = &flist;
3137 3157 int ret;
3138 3158
3139 3159 flistp->head = flistp->tail = NULL;
3140 3160
3141 3161 if (is_sparc())
3142 3162 set_flag(IS_SPARC_TARGET);
3143 3163
3144 3164 /*
3145 3165 * Check if cache directories and archives are present
3146 3166 */
3147 3167
3148 3168 ret = check_flags_and_files(root);
3149 3169 if (ret < 0)
3150 3170 return (BAM_ERROR);
3151 3171
3152 3172 /*
3153 3173 * In certain deployment scenarios, filestat may not
3154 3174 * exist. Do not stop the boot process, but trigger an update
3155 3175 * of the archives (which will recreate filestat.ramdisk).
3156 3176 */
3157 3177 if (bam_smf_check) {
3158 3178 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3159 3179 if (stat(path, &sb) != 0) {
3160 3180 (void) creat(NEED_UPDATE_FILE, 0644);
3161 3181 return (0);
3162 3182 }
3163 3183 }
3164 3184
3165 3185 getoldstat(root);
3166 3186
3167 3187 /*
3168 3188 * Check if the archive contains files that are no longer
3169 3189 * present on the root filesystem.
3170 3190 */
3171 3191 check4stale(root);
3172 3192
3173 3193 /*
3174 3194 * read list of files
3175 3195 */
3176 3196 if (read_list(root, flistp) != BAM_SUCCESS) {
3177 3197 clear_walk_args();
3178 3198 return (BAM_ERROR);
3179 3199 }
3180 3200
3181 3201 assert(flistp->head && flistp->tail);
3182 3202
3183 3203 /*
3184 3204 * At this point either the update is required
3185 3205 * or the decision is pending. In either case
3186 3206 * we need to create new stat nvlist
3187 3207 */
3188 3208 create_newstat();
3189 3209 /*
3190 3210 * This walk does 2 things:
3191 3211 * - gets new stat data for every file
3192 3212 * - (optional) compare old and new stat data
3193 3213 */
3194 3214 ret = walk_list(root, &flist);
3195 3215
3196 3216 /* done with the file list */
3197 3217 filelist_free(flistp);
3198 3218
3199 3219 /* something went wrong */
3200 3220
3201 3221 if (ret == BAM_ERROR) {
3202 3222 bam_error(_("Failed to gather cache files, archives "
3203 3223 "generation aborted\n"));
3204 3224 return (BAM_ERROR);
3205 3225 }
3206 3226
3207 3227 if (walk_arg.new_nvlp == NULL) {
3208 3228 if (walk_arg.sparcfile != NULL)
3209 3229 (void) fclose(walk_arg.sparcfile);
3210 3230 bam_error(_("cannot create new stat data\n"));
3211 3231 }
3212 3232
3213 3233 /* If nothing was updated, discard newstat. */
3214 3234
3215 3235 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
3216 3236 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
3217 3237 clear_walk_args();
3218 3238 return (0);
3219 3239 }
3220 3240
3221 3241 if (walk_arg.sparcfile != NULL)
3222 3242 (void) fclose(walk_arg.sparcfile);
3223 3243
3224 3244 return (1);
3225 3245 }
3226 3246
3227 3247 static int
3228 3248 flushfs(char *root)
3229 3249 {
3230 3250 char cmd[PATH_MAX + 30];
3231 3251
3232 3252 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3233 3253 LOCKFS_PATH, root);
3234 3254
3235 3255 return (exec_cmd(cmd, NULL));
3236 3256 }
3237 3257
3238 3258 static int
3239 3259 do_archive_copy(char *source, char *dest)
3240 3260 {
3241 3261
3242 3262 sync();
3243 3263
3244 3264 /* the equivalent of mv archive-new-$pid boot_archive */
3245 3265 if (rename(source, dest) != 0) {
3246 3266 (void) unlink(source);
3247 3267 return (BAM_ERROR);
3248 3268 }
3249 3269
3250 3270 if (flushfs(bam_root) != 0)
3251 3271 sync();
3252 3272
3253 3273 return (BAM_SUCCESS);
3254 3274 }
3255 3275
3256 3276 static int
3257 3277 check_cmdline(filelist_t flist)
3258 3278 {
3259 3279 line_t *lp;
3260 3280
3261 3281 for (lp = flist.head; lp; lp = lp->next) {
3262 3282 if (strstr(lp->line, "Error:") != NULL ||
3263 3283 strstr(lp->line, "Inode number overflow") != NULL) {
3264 3284 (void) fprintf(stderr, "%s\n", lp->line);
3265 3285 return (BAM_ERROR);
3266 3286 }
3267 3287 }
3268 3288
3269 3289 return (BAM_SUCCESS);
3270 3290 }
3271 3291
3272 3292 static void
3273 3293 dump_errormsg(filelist_t flist)
3274 3294 {
3275 3295 line_t *lp;
3276 3296
3277 3297 for (lp = flist.head; lp; lp = lp->next)
3278 3298 (void) fprintf(stderr, "%s\n", lp->line);
3279 3299 }
3280 3300
3281 3301 static int
3282 3302 check_archive(char *dest)
3283 3303 {
3284 3304 struct stat sb;
3285 3305
3286 3306 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3287 3307 sb.st_size < 10000) {
3288 3308 bam_error(_("archive file %s not generated correctly\n"), dest);
3289 3309 (void) unlink(dest);
3290 3310 return (BAM_ERROR);
3291 3311 }
3292 3312
3293 3313 return (BAM_SUCCESS);
3294 3314 }
3295 3315
3296 3316 static boolean_t
3297 3317 is_be(char *root)
3298 3318 {
3299 3319 zfs_handle_t *zhp;
3300 3320 libzfs_handle_t *hdl;
3301 3321 be_node_list_t *be_nodes = NULL;
3302 3322 be_node_list_t *cur_be;
3303 3323 boolean_t be_exist = B_FALSE;
3304 3324 char ds_path[ZFS_MAX_DATASET_NAME_LEN];
3305 3325
3306 3326 if (!is_zfs(root))
3307 3327 return (B_FALSE);
3308 3328 /*
3309 3329 * Get dataset for mountpoint
3310 3330 */
3311 3331 if ((hdl = libzfs_init()) == NULL)
3312 3332 return (B_FALSE);
3313 3333
3314 3334 if ((zhp = zfs_path_to_zhandle(hdl, root,
3315 3335 ZFS_TYPE_FILESYSTEM)) == NULL) {
3316 3336 libzfs_fini(hdl);
3317 3337 return (B_FALSE);
3318 3338 }
3319 3339
3320 3340 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3321 3341
3322 3342 /*
3323 3343 * Check if the current dataset is BE
3324 3344 */
3325 3345 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
3326 3346 for (cur_be = be_nodes; cur_be != NULL;
3327 3347 cur_be = cur_be->be_next_node) {
3328 3348
3329 3349 /*
3330 3350 * Because we guarantee that cur_be->be_root_ds
3331 3351 * is null-terminated by internal data structure,
3332 3352 * we can safely use strcmp()
3333 3353 */
3334 3354 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3335 3355 be_exist = B_TRUE;
3336 3356 break;
3337 3357 }
3338 3358 }
3339 3359 be_free_list(be_nodes);
3340 3360 }
3341 3361 zfs_close(zhp);
3342 3362 libzfs_fini(hdl);
3343 3363
3344 3364 return (be_exist);
3345 3365 }
3346 3366
3347 3367 /*
3348 3368 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
3349 3369 */
3350 3370 static int
3351 3371 is_mkisofs()
3352 3372 {
3353 3373 if (access(MKISOFS_PATH, X_OK) == 0)
3354 3374 return (1);
3355 3375 return (0);
3356 3376 }
3357 3377
3358 3378 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3359 3379
3360 3380 static int
3361 3381 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3362 3382 {
3363 3383 int ret;
3364 3384 char cmdline[3 * PATH_MAX + 64];
3365 3385 filelist_t flist = {0};
3366 3386 const char *func = "create_sparc_archive()";
3367 3387
3368 3388 if (access(bootblk, R_OK) == 1) {
3369 3389 bam_error(_("unable to access bootblk file : %s\n"), bootblk);
3370 3390 return (BAM_ERROR);
3371 3391 }
3372 3392
3373 3393 /*
3374 3394 * Prepare mkisofs command line and execute it
3375 3395 */
3376 3396 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3377 3397 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3378 3398 tempname, list);
3379 3399
3380 3400 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3381 3401
3382 3402 ret = exec_cmd(cmdline, &flist);
3383 3403 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3384 3404 dump_errormsg(flist);
3385 3405 goto out_err;
3386 3406 }
3387 3407
3388 3408 filelist_free(&flist);
3389 3409
3390 3410 /*
3391 3411 * Prepare dd command line to copy the bootblk on the new archive and
3392 3412 * execute it
3393 3413 */
3394 3414 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3395 3415 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3396 3416 bootblk, tempname);
3397 3417
3398 3418 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3399 3419
3400 3420 ret = exec_cmd(cmdline, &flist);
3401 3421 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3402 3422 goto out_err;
3403 3423
3404 3424 filelist_free(&flist);
3405 3425
3406 3426 /* Did we get a valid archive ? */
3407 3427 if (check_archive(tempname) == BAM_ERROR)
3408 3428 return (BAM_ERROR);
3409 3429
3410 3430 return (do_archive_copy(tempname, archive));
3411 3431
3412 3432 out_err:
3413 3433 filelist_free(&flist);
3414 3434 bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline);
3415 3435 (void) unlink(tempname);
3416 3436 return (BAM_ERROR);
3417 3437 }
3418 3438
3419 3439 static unsigned int
3420 3440 from_733(unsigned char *s)
3421 3441 {
3422 3442 int i;
3423 3443 unsigned int ret = 0;
3424 3444
3425 3445 for (i = 0; i < 4; i++)
3426 3446 ret |= s[i] << (8 * i);
3427 3447
3428 3448 return (ret);
3429 3449 }
3430 3450
3431 3451 static void
3432 3452 to_733(unsigned char *s, unsigned int val)
3433 3453 {
3434 3454 int i;
3435 3455
3436 3456 for (i = 0; i < 4; i++)
3437 3457 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3438 3458 }
3439 3459
3440 3460 /*
3441 3461 * creates sha1 hash of archive
3442 3462 */
3443 3463 static int
3444 3464 digest_archive(const char *archive)
3445 3465 {
3446 3466 char *archive_hash;
3447 3467 char *hash;
3448 3468 int ret;
3449 3469 FILE *fp;
3450 3470
3451 3471 (void) asprintf(&archive_hash, "%s.hash", archive);
3452 3472 if (archive_hash == NULL)
3453 3473 return (BAM_ERROR);
3454 3474
3455 3475 if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
3456 3476 free(archive_hash);
3457 3477 return (ret);
3458 3478 }
3459 3479
3460 3480 fp = fopen(archive_hash, "w");
3461 3481 if (fp == NULL) {
3462 3482 free(archive_hash);
3463 3483 free(hash);
3464 3484 return (BAM_ERROR);
3465 3485 }
3466 3486
3467 3487 (void) fprintf(fp, "%s\n", hash);
3468 3488 (void) fclose(fp);
3469 3489 free(hash);
3470 3490 free(archive_hash);
3471 3491 return (BAM_SUCCESS);
3472 3492 }
3473 3493
3474 3494 /*
3475 3495 * Extends the current boot archive without recreating it from scratch
3476 3496 */
3477 3497 static int
3478 3498 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3479 3499 {
3480 3500 int fd = -1, newfd = -1, ret, i;
3481 3501 int next_session = 0, new_size = 0;
3482 3502 char cmdline[3 * PATH_MAX + 64];
3483 3503 const char *func = "extend_iso_archive()";
3484 3504 filelist_t flist = {0};
3485 3505 struct iso_pdesc saved_desc[MAX_IVDs];
3486 3506
3487 3507 fd = open(archive, O_RDWR);
3488 3508 if (fd == -1) {
3489 3509 if (bam_verbose)
3490 3510 bam_error(_("failed to open file: %s: %s\n"),
3491 3511 archive, strerror(errno));
3492 3512 goto out_err;
3493 3513 }
3494 3514
3495 3515 /*
3496 3516 * A partial read is likely due to a corrupted file
3497 3517 */
3498 3518 ret = pread64(fd, saved_desc, sizeof (saved_desc),
3499 3519 VOLDESC_OFF * CD_BLOCK);
3500 3520 if (ret != sizeof (saved_desc)) {
3501 3521 if (bam_verbose)
3502 3522 bam_error(_("read failed for file: %s: %s\n"),
3503 3523 archive, strerror(errno));
3504 3524 goto out_err;
3505 3525 }
3506 3526
3507 3527 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3508 3528 if (bam_verbose)
3509 3529 bam_error(_("iso descriptor signature for %s is "
3510 3530 "invalid\n"), archive);
3511 3531 goto out_err;
3512 3532 }
3513 3533
3514 3534 /*
3515 3535 * Read primary descriptor and locate next_session offset (it should
3516 3536 * point to the end of the archive)
3517 3537 */
3518 3538 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3519 3539
3520 3540 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3521 3541 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3522 3542 MKISO_PARAMS, tempname, update_dir);
3523 3543
3524 3544 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3525 3545
3526 3546 ret = exec_cmd(cmdline, &flist);
3527 3547 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3528 3548 if (bam_verbose) {
3529 3549 bam_error(_("Command '%s' failed while generating "
3530 3550 "multisession archive\n"), cmdline);
3531 3551 dump_errormsg(flist);
3532 3552 }
3533 3553 goto out_flist_err;
3534 3554 }
3535 3555 filelist_free(&flist);
3536 3556
3537 3557 newfd = open(tempname, O_RDONLY);
3538 3558 if (newfd == -1) {
3539 3559 if (bam_verbose)
3540 3560 bam_error(_("failed to open file: %s: %s\n"),
3541 3561 archive, strerror(errno));
3542 3562 goto out_err;
3543 3563 }
3544 3564
3545 3565 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3546 3566 VOLDESC_OFF * CD_BLOCK);
3547 3567 if (ret != sizeof (saved_desc)) {
3548 3568 if (bam_verbose)
3549 3569 bam_error(_("read failed for file: %s: %s\n"),
3550 3570 archive, strerror(errno));
3551 3571 goto out_err;
3552 3572 }
3553 3573
3554 3574 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3555 3575 if (bam_verbose)
3556 3576 bam_error(_("iso descriptor signature for %s is "
3557 3577 "invalid\n"), archive);
3558 3578 goto out_err;
3559 3579 }
3560 3580
3561 3581 new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3562 3582 to_733(saved_desc[0].volume_space_size, new_size);
3563 3583
3564 3584 for (i = 1; i < MAX_IVDs; i++) {
3565 3585 if (saved_desc[i].type[0] == (unsigned char)255)
3566 3586 break;
3567 3587 if (memcmp(saved_desc[i].id, "CD001", 5))
3568 3588 break;
3569 3589
3570 3590 if (bam_verbose)
3571 3591 bam_print("%s: Updating descriptor entry [%d]\n", func,
3572 3592 i);
3573 3593
3574 3594 to_733(saved_desc[i].volume_space_size, new_size);
3575 3595 }
3576 3596
3577 3597 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3578 3598 if (ret != DVD_BLOCK) {
3579 3599 if (bam_verbose)
3580 3600 bam_error(_("write to file failed: %s: %s\n"),
3581 3601 archive, strerror(errno));
3582 3602 goto out_err;
3583 3603 }
3584 3604 (void) close(newfd);
3585 3605 newfd = -1;
3586 3606
3587 3607 ret = fsync(fd);
3588 3608 if (ret != 0)
3589 3609 sync();
3590 3610
3591 3611 ret = close(fd);
3592 3612 if (ret != 0) {
3593 3613 if (bam_verbose)
3594 3614 bam_error(_("failed to close file: %s: %s\n"),
3595 3615 archive, strerror(errno));
3596 3616 return (BAM_ERROR);
3597 3617 }
3598 3618 fd = -1;
3599 3619
3600 3620 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3601 3621 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3602 3622 (next_session/16));
3603 3623
3604 3624 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3605 3625
3606 3626 ret = exec_cmd(cmdline, &flist);
3607 3627 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3608 3628 if (bam_verbose)
3609 3629 bam_error(_("Command '%s' failed while generating "
3610 3630 "multisession archive\n"), cmdline);
3611 3631 goto out_flist_err;
3612 3632 }
3613 3633 filelist_free(&flist);
3614 3634
3615 3635 (void) unlink(tempname);
3616 3636
3617 3637 if (digest_archive(archive) == BAM_ERROR && bam_verbose)
3618 3638 bam_print("boot archive hashing failed\n");
3619 3639
3620 3640 if (flushfs(bam_root) != 0)
3621 3641 sync();
3622 3642
3623 3643 if (bam_verbose)
3624 3644 bam_print("boot archive updated successfully\n");
3625 3645
3626 3646 return (BAM_SUCCESS);
3627 3647
3628 3648 out_flist_err:
3629 3649 filelist_free(&flist);
3630 3650 out_err:
3631 3651 if (fd != -1)
3632 3652 (void) close(fd);
3633 3653 if (newfd != -1)
3634 3654 (void) close(newfd);
3635 3655 return (BAM_ERROR);
3636 3656 }
3637 3657
3638 3658 static int
3639 3659 create_x86_archive(char *archive, char *tempname, char *update_dir)
3640 3660 {
3641 3661 int ret;
3642 3662 char cmdline[3 * PATH_MAX + 64];
3643 3663 filelist_t flist = {0};
3644 3664 const char *func = "create_x86_archive()";
3645 3665
3646 3666 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3647 3667 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3648 3668
3649 3669 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3650 3670
3651 3671 ret = exec_cmd(cmdline, &flist);
3652 3672 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3653 3673 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3654 3674 cmdline);
3655 3675 dump_errormsg(flist);
3656 3676 filelist_free(&flist);
3657 3677 (void) unlink(tempname);
3658 3678 return (BAM_ERROR);
3659 3679 }
3660 3680
3661 3681 filelist_free(&flist);
3662 3682
3663 3683 if (check_archive(tempname) == BAM_ERROR)
3664 3684 return (BAM_ERROR);
3665 3685
3666 3686 return (do_archive_copy(tempname, archive));
3667 3687 }
3668 3688
3669 3689 static int
3670 3690 mkisofs_archive(char *root, int what)
3671 3691 {
3672 3692 int ret;
3673 3693 char temp[PATH_MAX];
3674 3694 char bootblk[PATH_MAX];
3675 3695 char boot_archive[PATH_MAX];
3676 3696
3677 3697 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3678 3698 ret = snprintf(temp, sizeof (temp),
3679 3699 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3680 3700 get_machine(), getpid());
3681 3701 else
3682 3702 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3683 3703 root, ARCHIVE_PREFIX, get_machine(), getpid());
3684 3704
3685 3705 if (ret >= sizeof (temp))
3686 3706 goto out_path_err;
3687 3707
3688 3708 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3689 3709 ret = snprintf(boot_archive, sizeof (boot_archive),
3690 3710 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3691 3711 ARCHIVE_SUFFIX);
3692 3712 else
3693 3713 ret = snprintf(boot_archive, sizeof (boot_archive),
3694 3714 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3695 3715 ARCHIVE_SUFFIX);
3696 3716
3697 3717 if (ret >= sizeof (boot_archive))
3698 3718 goto out_path_err;
3699 3719
3700 3720 bam_print("updating %s\n", boot_archive);
3701 3721
3702 3722 if (is_flag_on(IS_SPARC_TARGET)) {
3703 3723 ret = snprintf(bootblk, sizeof (bootblk),
3704 3724 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3705 3725 if (ret >= sizeof (bootblk))
3706 3726 goto out_path_err;
3707 3727
3708 3728 ret = create_sparc_archive(boot_archive, temp, bootblk,
3709 3729 get_cachedir(what));
3710 3730 } else {
3711 3731 if (!is_dir_flag_on(what, NO_MULTI)) {
3712 3732 if (bam_verbose)
3713 3733 bam_print("Attempting to extend x86 archive: "
3714 3734 "%s\n", boot_archive);
3715 3735
3716 3736 ret = extend_iso_archive(boot_archive, temp,
3717 3737 get_updatedir(what));
3718 3738 if (ret == BAM_SUCCESS) {
3719 3739 if (bam_verbose)
3720 3740 bam_print("Successfully extended %s\n",
3721 3741 boot_archive);
3722 3742
3723 3743 (void) rmdir_r(get_updatedir(what));
3724 3744 return (BAM_SUCCESS);
3725 3745 }
3726 3746 }
3727 3747 /*
3728 3748 * The boot archive will be recreated from scratch. We get here
3729 3749 * if at least one of these conditions is true:
3730 3750 * - bootadm was called without the -e switch
3731 3751 * - the archive (or the archive cache) doesn't exist
3732 3752 * - archive size is bigger than BA_SIZE_MAX
3733 3753 * - more than COUNT_MAX files need to be updated
3734 3754 * - an error occourred either populating the /updates directory
3735 3755 * or extend_iso_archive() failed
3736 3756 */
3737 3757 if (bam_verbose)
3738 3758 bam_print("Unable to extend %s... rebuilding archive\n",
3739 3759 boot_archive);
3740 3760
3741 3761 if (get_updatedir(what)[0] != '\0')
3742 3762 (void) rmdir_r(get_updatedir(what));
3743 3763
3744 3764
3745 3765 ret = create_x86_archive(boot_archive, temp,
3746 3766 get_cachedir(what));
3747 3767 }
3748 3768
3749 3769 if (digest_archive(boot_archive) == BAM_ERROR && bam_verbose)
3750 3770 bam_print("boot archive hashing failed\n");
3751 3771
3752 3772 if (ret == BAM_SUCCESS && bam_verbose)
|
↓ open down ↓ |
1309 lines elided |
↑ open up ↑ |
3753 3773 bam_print("Successfully created %s\n", boot_archive);
3754 3774
3755 3775 return (ret);
3756 3776
3757 3777 out_path_err:
3758 3778 bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3759 3779 root);
3760 3780 return (BAM_ERROR);
3761 3781 }
3762 3782
3783 +static int
3784 +assemble_systemfile(char *infilename, char *outfilename)
3785 +{
3786 + char buf[BUFSIZ];
3787 + FILE *infile, *outfile;
3788 + size_t n;
3789 +
3790 + if ((infile = fopen(infilename, "r")) == NULL) {
3791 + bam_error(_("failed to open file: %s: %s\n"), infilename,
3792 + strerror(errno));
3793 + return (BAM_ERROR);
3794 + }
3795 +
3796 + if ((outfile = fopen(outfilename, "a")) == NULL) {
3797 + bam_error(_("failed to open file: %s: %s\n"), outfilename,
3798 + strerror(errno));
3799 + (void) fclose(infile);
3800 + return (BAM_ERROR);
3801 + }
3802 +
3803 + while ((n = fread(buf, 1, sizeof (buf), infile)) > 0) {
3804 + if (fwrite(buf, 1, n, outfile) != n) {
3805 + bam_error(_("failed to write file: %s: %s\n"),
3806 + outfilename, strerror(errno));
3807 + (void) fclose(infile);
3808 + (void) fclose(outfile);
3809 + return (BAM_ERROR);
3810 + }
3811 + }
3812 +
3813 + (void) fclose(infile);
3814 + (void) fclose(outfile);
3815 +
3816 + return (BAM_SUCCESS);
3817 +}
3818 +
3819 +/*
3820 + * Concatenate all files (except those starting with a dot)
3821 + * from /etc/system.d directory into a single /etc/system.d/.self-assembly
3822 + * file. The kernel reads it before /etc/system file.
3823 + */
3763 3824 static error_t
3825 +build_etc_system_dir(char *root)
3826 +{
3827 + struct dirent **filelist;
3828 + char path[PATH_MAX], tmpfile[PATH_MAX];
3829 + int i, files, sysfiles = 0;
3830 + int ret = BAM_SUCCESS;
3831 + struct stat st;
3832 + timespec_t times[2];
3833 +
3834 + (void) snprintf(path, sizeof (path), "%s/%s", root, ETC_SYSTEM_DIR);
3835 + (void) snprintf(self_assembly, sizeof (self_assembly),
3836 + "%s%s", root, SELF_ASSEMBLY);
3837 + (void) snprintf(tmpfile, sizeof (tmpfile), "%s.%ld",
3838 + self_assembly, (long)getpid());
3839 +
3840 + if (stat(self_assembly, &st) >= 0 && (st.st_mode & S_IFMT) == S_IFREG) {
3841 + times[0] = times[1] = st.st_mtim;
3842 + } else {
3843 + times[1].tv_nsec = 0;
3844 + }
3845 +
3846 + if ((files = scandir(path, &filelist, NULL, alphasort)) < 0) {
3847 + /* Don't fail the update if <ROOT>/etc/system.d doesn't exist */
3848 + if (errno == ENOENT)
3849 + return (BAM_SUCCESS);
3850 + bam_error(_("can't read %s: %s\n"), path, strerror(errno));
3851 + return (BAM_ERROR);
3852 + }
3853 +
3854 + for (i = 0; i < files; i++) {
3855 + char filepath[PATH_MAX];
3856 + char *fname;
3857 +
3858 + fname = filelist[i]->d_name;
3859 +
3860 + /* skip anything that starts with a dot */
3861 + if (strncmp(fname, ".", 1) == 0) {
3862 + free(filelist[i]);
3863 + continue;
3864 + }
3865 +
3866 + if (bam_verbose)
3867 + bam_print(_("/etc/system.d adding %s/%s\n"),
3868 + path, fname);
3869 +
3870 + (void) snprintf(filepath, sizeof (filepath), "%s/%s",
3871 + path, fname);
3872 +
3873 + if ((assemble_systemfile(filepath, tmpfile)) < 0) {
3874 + bam_error(_("failed to append file: %s: %s\n"),
3875 + filepath, strerror(errno));
3876 + ret = BAM_ERROR;
3877 + break;
3878 + }
3879 + sysfiles++;
3880 + }
3881 +
3882 + if (sysfiles > 0) {
3883 + if (rename(tmpfile, self_assembly) < 0) {
3884 + bam_error(_("failed to rename file: %s: %s\n"), tmpfile,
3885 + strerror(errno));
3886 + return (BAM_ERROR);
3887 + }
3888 +
3889 + /*
3890 + * Use previous attribute times to avoid
3891 + * boot archive recreation.
3892 + */
3893 + if (times[1].tv_nsec != 0 &&
3894 + utimensat(AT_FDCWD, self_assembly, times, 0) != 0) {
3895 + bam_error(_("failed to change times: %s\n"),
3896 + strerror(errno));
3897 + return (BAM_ERROR);
3898 + }
3899 + } else {
3900 + (void) unlink(tmpfile);
3901 + (void) unlink(self_assembly);
3902 + }
3903 + return (ret);
3904 +}
3905 +
3906 +static error_t
3764 3907 create_ramdisk(char *root)
3765 3908 {
3766 3909 char *cmdline, path[PATH_MAX];
3767 3910 size_t len;
3768 3911 struct stat sb;
3769 3912 int ret, what, status = BAM_SUCCESS;
3770 3913
3771 3914 /* If there is mkisofs, use it to create the required archives */
3772 3915 if (is_mkisofs()) {
3773 3916 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3774 3917 if (has_cachedir(what) && is_dir_flag_on(what,
3775 3918 NEED_UPDATE)) {
3776 3919 ret = mkisofs_archive(root, what);
3777 3920 if (ret != 0)
3778 3921 status = BAM_ERROR;
3779 3922 }
3780 3923 }
3781 3924 return (status);
3782 3925 }
3783 3926
3784 3927 /*
3785 3928 * Else setup command args for create_ramdisk.ksh for the UFS archives
3786 3929 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3787 3930 */
3788 3931 if (bam_verbose)
3789 3932 bam_print("mkisofs not found, creating UFS archive\n");
3790 3933
3791 3934 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3792 3935 if (stat(path, &sb) != 0) {
3793 3936 bam_error(_("archive creation file not found: %s: %s\n"),
3794 3937 path, strerror(errno));
3795 3938 return (BAM_ERROR);
3796 3939 }
3797 3940
3798 3941 if (is_safe_exec(path) == BAM_ERROR)
3799 3942 return (BAM_ERROR);
3800 3943
3801 3944 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3802 3945 if (bam_alt_platform)
3803 3946 len += strlen(bam_platform) + strlen("-p ");
3804 3947 cmdline = s_calloc(1, len);
3805 3948
3806 3949 if (bam_alt_platform) {
3807 3950 assert(strlen(root) > 1);
3808 3951 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3809 3952 path, bam_platform, root);
3810 3953 /* chop off / at the end */
3811 3954 cmdline[strlen(cmdline) - 1] = '\0';
3812 3955 } else if (strlen(root) > 1) {
3813 3956 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3814 3957 /* chop off / at the end */
3815 3958 cmdline[strlen(cmdline) - 1] = '\0';
3816 3959 } else
3817 3960 (void) snprintf(cmdline, len, "%s", path);
3818 3961
3819 3962 if (exec_cmd(cmdline, NULL) != 0) {
3820 3963 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3821 3964 cmdline);
3822 3965 free(cmdline);
3823 3966 return (BAM_ERROR);
3824 3967 }
3825 3968 free(cmdline);
3826 3969 /*
3827 3970 * The existence of the expected archives used to be
3828 3971 * verified here. This check is done in create_ramdisk as
3829 3972 * it needs to be in sync with the altroot operated upon.
3830 3973 */
3831 3974 return (BAM_SUCCESS);
3832 3975 }
3833 3976
3834 3977 /*
3835 3978 * Checks if target filesystem is on a ramdisk
3836 3979 * 1 - is miniroot
3837 3980 * 0 - is not
3838 3981 * When in doubt assume it is not a ramdisk.
3839 3982 */
3840 3983 static int
3841 3984 is_ramdisk(char *root)
3842 3985 {
3843 3986 struct extmnttab mnt;
3844 3987 FILE *fp;
3845 3988 int found;
3846 3989 char mntpt[PATH_MAX];
3847 3990 char *cp;
3848 3991
3849 3992 /*
3850 3993 * There are 3 situations where creating archive is
3851 3994 * of dubious value:
3852 3995 * - create boot_archive on a lofi-mounted boot_archive
3853 3996 * - create it on a ramdisk which is the root filesystem
3854 3997 * - create it on a ramdisk mounted somewhere else
3855 3998 * The first is not easy to detect and checking for it is not
3856 3999 * worth it.
3857 4000 * The other two conditions are handled here
3858 4001 */
3859 4002 fp = fopen(MNTTAB, "r");
3860 4003 if (fp == NULL) {
3861 4004 bam_error(_("failed to open file: %s: %s\n"),
3862 4005 MNTTAB, strerror(errno));
3863 4006 return (0);
3864 4007 }
3865 4008
3866 4009 resetmnttab(fp);
3867 4010
3868 4011 /*
3869 4012 * Remove any trailing / from the mount point
3870 4013 */
3871 4014 (void) strlcpy(mntpt, root, sizeof (mntpt));
3872 4015 if (strcmp(root, "/") != 0) {
3873 4016 cp = mntpt + strlen(mntpt) - 1;
3874 4017 if (*cp == '/')
3875 4018 *cp = '\0';
3876 4019 }
3877 4020 found = 0;
3878 4021 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3879 4022 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3880 4023 found = 1;
3881 4024 break;
3882 4025 }
3883 4026 }
3884 4027
3885 4028 if (!found) {
3886 4029 if (bam_verbose)
3887 4030 bam_error(_("alternate root %s not in mnttab\n"),
3888 4031 mntpt);
3889 4032 (void) fclose(fp);
3890 4033 return (0);
3891 4034 }
3892 4035
3893 4036 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3894 4037 strlen(RAMDISK_SPECIAL)) == 0) {
3895 4038 if (bam_verbose)
3896 4039 bam_error(_("%s is on a ramdisk device\n"), bam_root);
3897 4040 (void) fclose(fp);
3898 4041 return (1);
3899 4042 }
3900 4043
3901 4044 (void) fclose(fp);
3902 4045
3903 4046 return (0);
3904 4047 }
3905 4048
3906 4049 static int
3907 4050 is_boot_archive(char *root)
3908 4051 {
3909 4052 char path[PATH_MAX];
3910 4053 struct stat sb;
3911 4054 int error;
3912 4055 const char *fcn = "is_boot_archive()";
3913 4056
3914 4057 /*
3915 4058 * We can't create an archive without the create_ramdisk script
3916 4059 */
3917 4060 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3918 4061 error = stat(path, &sb);
3919 4062 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3920 4063 if (error == -1) {
3921 4064 if (bam_verbose)
3922 4065 bam_print(_("file not found: %s\n"), path);
3923 4066 BAM_DPRINTF(("%s: not a boot archive based Solaris "
3924 4067 "instance: %s\n", fcn, root));
3925 4068 return (0);
3926 4069 }
3927 4070
3928 4071 BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
3929 4072 fcn, root));
3930 4073 return (1);
3931 4074 }
3932 4075
3933 4076 /*
3934 4077 * Need to call this for anything that operates on the GRUB menu
3935 4078 * In the x86 live upgrade case the directory /boot/grub may be present
3936 4079 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3937 4080 * is to check for the presence of the stage2 binary which is present
|
↓ open down ↓ |
164 lines elided |
↑ open up ↑ |
3938 4081 * only on GRUB targets (even on x86 boot partitions). Checking for the
3939 4082 * presence of the multiboot binary is not correct as it is not present
3940 4083 * on x86 boot partitions.
3941 4084 */
3942 4085 int
3943 4086 is_grub(const char *root)
3944 4087 {
3945 4088 char path[PATH_MAX];
3946 4089 struct stat sb;
3947 4090 void *defp;
3948 - boolean_t grub = B_FALSE;
4091 + boolean_t grub = B_TRUE;
3949 4092 const char *res = NULL;
3950 4093 const char *fcn = "is_grub()";
3951 4094
3952 - /* grub is disabled by default */
3953 - if ((defp = defopen_r(BE_DEFAULTS)) == NULL) {
3954 - return (0);
3955 - } else {
4095 + /* grub is enabled by default */
4096 + if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
3956 4097 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
3957 4098 if (res != NULL && res[0] != '\0') {
3958 - if (strcasecmp(res, "true") == 0)
3959 - grub = B_TRUE;
4099 + if (strcasecmp(res, "false") == 0)
4100 + grub = B_FALSE;
3960 4101 }
3961 4102 defclose_r(defp);
3962 4103 }
3963 4104
3964 4105 if (grub == B_TRUE) {
3965 4106 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3966 4107 if (stat(path, &sb) == -1) {
3967 4108 BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
3968 4109 fcn, path));
3969 4110 return (0);
3970 - } else
4111 + } else {
3971 4112 return (1);
4113 + }
3972 4114 }
3973 4115
3974 4116 return (0);
3975 4117 }
3976 4118
3977 4119 int
3978 4120 is_zfs(char *root)
3979 4121 {
3980 4122 struct statvfs vfs;
3981 4123 int ret;
3982 4124 const char *fcn = "is_zfs()";
3983 4125
3984 4126 ret = statvfs(root, &vfs);
3985 4127 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3986 4128 if (ret != 0) {
3987 4129 bam_error(_("statvfs failed for %s: %s\n"), root,
3988 4130 strerror(errno));
3989 4131 return (0);
3990 4132 }
3991 4133
3992 4134 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3993 4135 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
3994 4136 return (1);
3995 4137 } else {
3996 4138 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
3997 4139 return (0);
3998 4140 }
3999 4141 }
4000 4142
4001 4143 int
4002 4144 is_pcfs(char *root)
4003 4145 {
4004 4146 struct statvfs vfs;
4005 4147 int ret;
4006 4148 const char *fcn = "is_pcfs()";
4007 4149
4008 4150 ret = statvfs(root, &vfs);
4009 4151 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
4010 4152 if (ret != 0) {
4011 4153 bam_error(_("statvfs failed for %s: %s\n"), root,
4012 4154 strerror(errno));
4013 4155 return (0);
4014 4156 }
4015 4157
4016 4158 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
4017 4159 BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn, root));
4018 4160 return (1);
4019 4161 } else {
4020 4162 BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4021 4163 fcn, root));
4022 4164 return (0);
4023 4165 }
4024 4166 }
4025 4167
4026 4168 static int
4027 4169 is_readonly(char *root)
4028 4170 {
4029 4171 int fd;
4030 4172 int error;
4031 4173 char testfile[PATH_MAX];
4032 4174 const char *fcn = "is_readonly()";
4033 4175
4034 4176 /*
4035 4177 * Using statvfs() to check for a read-only filesystem is not
4036 4178 * reliable. The only way to reliably test is to attempt to
4037 4179 * create a file
4038 4180 */
4039 4181 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
4040 4182 root, BOOTADM_RDONLY_TEST, getpid());
4041 4183
4042 4184 (void) unlink(testfile);
4043 4185
4044 4186 errno = 0;
4045 4187 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
4046 4188 error = errno;
4047 4189 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
4048 4190 if (fd == -1 && error == EROFS) {
4049 4191 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
4050 4192 return (1);
4051 4193 } else if (fd == -1) {
4052 4194 bam_error(_("error during read-only test on %s: %s\n"),
4053 4195 root, strerror(error));
4054 4196 }
4055 4197
4056 4198 (void) close(fd);
4057 4199 (void) unlink(testfile);
4058 4200
4059 4201 BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
4060 4202 return (0);
4061 4203 }
4062 4204
4063 4205 static error_t
4064 4206 update_archive(char *root, char *opt)
4065 4207 {
4066 4208 error_t ret;
4067 4209
4068 4210 assert(root);
4069 4211 assert(opt == NULL);
4070 4212
4071 4213 init_walk_args();
4072 4214 (void) umask(022);
4073 4215
4074 4216 /*
4075 4217 * Never update non-BE root in update_all
4076 4218 */
4077 4219 if (!is_be(root) && bam_update_all)
4078 4220 return (BAM_SUCCESS);
4079 4221 /*
4080 4222 * root must belong to a boot archive based OS,
4081 4223 */
4082 4224 if (!is_boot_archive(root)) {
4083 4225 /*
4084 4226 * Emit message only if not in context of update_all.
4085 4227 * If in update_all, emit only if verbose flag is set.
4086 4228 */
4087 4229 if (!bam_update_all || bam_verbose)
4088 4230 bam_print(_("%s: not a boot archive based Solaris "
4089 4231 "instance\n"), root);
4090 4232 return (BAM_ERROR);
4091 4233 }
4092 4234
4093 4235 /*
4094 4236 * If smf check is requested when / is writable (can happen
4095 4237 * on first reboot following an upgrade because service
4096 4238 * dependency is messed up), skip the check.
4097 4239 */
4098 4240 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
4099 4241 return (BAM_SUCCESS);
4100 4242
4101 4243 /*
4102 4244 * Don't generate archive on ramdisk.
4103 4245 */
4104 4246 if (is_ramdisk(root))
4105 4247 return (BAM_SUCCESS);
4106 4248
4107 4249 /*
|
↓ open down ↓ |
126 lines elided |
↑ open up ↑ |
4108 4250 * root must be writable. This check applies to alternate
4109 4251 * root (-R option); bam_root_readonly applies to '/' only.
4110 4252 * The behaviour translates into being the one of a 'check'.
4111 4253 */
4112 4254 if (!bam_smf_check && !bam_check && is_readonly(root)) {
4113 4255 set_flag(RDONLY_FSCHK);
4114 4256 bam_check = 1;
4115 4257 }
4116 4258
4117 4259 /*
4260 + * Process the /etc/system.d/self-assembly file.
4261 + */
4262 + if (build_etc_system_dir(bam_root) == BAM_ERROR)
4263 + return (BAM_ERROR);
4264 +
4265 + /*
4118 4266 * Now check if an update is really needed.
4119 4267 */
4120 4268 ret = update_required(root);
4121 4269
4122 4270 /*
4123 4271 * The check command (-n) is *not* a dry run.
4124 4272 * It only checks if the archive is in sync.
4125 4273 * A readonly filesystem has to be considered an error only if an update
4126 4274 * is required.
4127 4275 */
4128 4276 if (bam_nowrite()) {
4129 4277 if (is_flag_on(RDONLY_FSCHK)) {
4130 4278 bam_check = bam_saved_check;
4131 4279 if (ret > 0)
4132 4280 bam_error(_("%s filesystem is read-only, "
4133 4281 "skipping archives update\n"), root);
4134 4282 if (bam_update_all)
4135 4283 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4136 4284 }
4137 4285
4138 4286 bam_exit((ret != 0) ? 1 : 0);
4139 4287 }
4140 4288
4141 4289 if (ret == 1) {
4142 4290 /* create the ramdisk */
4143 4291 ret = create_ramdisk(root);
4144 4292 }
4145 4293
4146 4294 /*
4147 4295 * if the archive is updated, save the new stat data and update the
4148 4296 * timestamp file
4149 4297 */
4150 4298 if (ret == 0 && walk_arg.new_nvlp != NULL) {
4151 4299 savenew(root);
4152 4300 update_timestamp(root);
4153 4301 }
4154 4302
4155 4303 clear_walk_args();
4156 4304
4157 4305 return (ret);
4158 4306 }
4159 4307
4160 4308 static char *
4161 4309 find_root_pool()
4162 4310 {
4163 4311 char *special = get_special("/");
4164 4312 char *p;
4165 4313
4166 4314 if (special == NULL)
4167 4315 return (NULL);
4168 4316
4169 4317 if (*special == '/') {
4170 4318 free(special);
4171 4319 return (NULL);
4172 4320 }
4173 4321
4174 4322 if ((p = strchr(special, '/')) != NULL)
4175 4323 *p = '\0';
4176 4324
4177 4325 return (special);
4178 4326 }
4179 4327
4180 4328 static error_t
4181 4329 synchronize_BE_menu(void)
4182 4330 {
4183 4331 struct stat sb;
4184 4332 char cmdline[PATH_MAX];
4185 4333 char cksum_line[PATH_MAX];
4186 4334 filelist_t flist = {0};
4187 4335 char *old_cksum_str;
4188 4336 char *old_size_str;
4189 4337 char *old_file;
4190 4338 char *curr_cksum_str;
4191 4339 char *curr_size_str;
4192 4340 char *curr_file;
4193 4341 char *pool = NULL;
4194 4342 char *mntpt = NULL;
4195 4343 zfs_mnted_t mnted;
4196 4344 FILE *cfp;
4197 4345 int found;
4198 4346 int ret;
4199 4347 const char *fcn = "synchronize_BE_menu()";
4200 4348
4201 4349 BAM_DPRINTF(("%s: entered. No args\n", fcn));
4202 4350
4203 4351 /* Check if findroot enabled LU BE */
4204 4352 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4205 4353 BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn));
4206 4354 return (BAM_SUCCESS);
4207 4355 }
4208 4356
4209 4357 if (stat(LU_MENU_CKSUM, &sb) != 0) {
4210 4358 BAM_DPRINTF(("%s: checksum file absent: %s\n",
4211 4359 fcn, LU_MENU_CKSUM));
4212 4360 goto menu_sync;
4213 4361 }
4214 4362
4215 4363 cfp = fopen(LU_MENU_CKSUM, "r");
4216 4364 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4217 4365 if (cfp == NULL) {
4218 4366 bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4219 4367 LU_MENU_CKSUM);
4220 4368 goto menu_sync;
4221 4369 }
4222 4370 BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn, LU_MENU_CKSUM));
4223 4371
4224 4372 found = 0;
4225 4373 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4226 4374 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4227 4375 if (found) {
4228 4376 bam_error(_("multiple checksums for GRUB menu in "
4229 4377 "checksum file: %s\n"), LU_MENU_CKSUM);
4230 4378 (void) fclose(cfp);
4231 4379 goto menu_sync;
4232 4380 }
4233 4381 found = 1;
4234 4382 }
4235 4383 BAM_DPRINTF(("%s: read checksum file: %s\n", fcn, LU_MENU_CKSUM));
4236 4384
4237 4385
4238 4386 old_cksum_str = strtok(cksum_line, " \t");
4239 4387 old_size_str = strtok(NULL, " \t");
4240 4388 old_file = strtok(NULL, " \t");
4241 4389
4242 4390 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4243 4391 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4244 4392 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4245 4393 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4246 4394 bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4247 4395 LU_MENU_CKSUM);
4248 4396 goto menu_sync;
4249 4397 }
4250 4398 BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn, LU_MENU_CKSUM));
4251 4399
4252 4400 /* Get checksum of current menu */
4253 4401 pool = find_root_pool();
4254 4402 if (pool) {
4255 4403 mntpt = mount_top_dataset(pool, &mnted);
4256 4404 if (mntpt == NULL) {
4257 4405 bam_error(_("failed to mount top dataset for %s\n"),
4258 4406 pool);
4259 4407 free(pool);
4260 4408 return (BAM_ERROR);
4261 4409 }
4262 4410 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4263 4411 CKSUM, mntpt, GRUB_MENU);
4264 4412 } else {
4265 4413 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4266 4414 CKSUM, GRUB_MENU);
4267 4415 }
4268 4416 ret = exec_cmd(cmdline, &flist);
4269 4417 if (pool) {
4270 4418 (void) umount_top_dataset(pool, mnted, mntpt);
4271 4419 free(pool);
4272 4420 }
4273 4421 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4274 4422 if (ret != 0) {
4275 4423 bam_error(_("error generating checksum of GRUB menu\n"));
4276 4424 return (BAM_ERROR);
4277 4425 }
4278 4426 BAM_DPRINTF(("%s: successfully generated checksum\n", fcn));
4279 4427
4280 4428 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4281 4429 if ((flist.head == NULL) || (flist.head != flist.tail)) {
4282 4430 bam_error(_("bad checksum generated for GRUB menu\n"));
4283 4431 filelist_free(&flist);
4284 4432 return (BAM_ERROR);
4285 4433 }
4286 4434 BAM_DPRINTF(("%s: generated checksum output valid\n", fcn));
4287 4435
4288 4436 curr_cksum_str = strtok(flist.head->line, " \t");
4289 4437 curr_size_str = strtok(NULL, " \t");
4290 4438 curr_file = strtok(NULL, " \t");
4291 4439
4292 4440 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4293 4441 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4294 4442 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4295 4443 if (curr_cksum_str == NULL || curr_size_str == NULL ||
4296 4444 curr_file == NULL) {
4297 4445 bam_error(_("error parsing checksum generated "
4298 4446 "for GRUB menu\n"));
4299 4447 filelist_free(&flist);
4300 4448 return (BAM_ERROR);
4301 4449 }
4302 4450 BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn));
4303 4451
4304 4452 if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4305 4453 strcmp(old_size_str, curr_size_str) == 0 &&
4306 4454 strcmp(old_file, curr_file) == 0) {
4307 4455 filelist_free(&flist);
4308 4456 BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn));
4309 4457 return (BAM_SUCCESS);
4310 4458 }
4311 4459
4312 4460 filelist_free(&flist);
4313 4461
4314 4462 /* cksum doesn't match - the menu has changed */
4315 4463 BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn));
4316 4464
4317 4465 menu_sync:
4318 4466 bam_print(_("propagating updated GRUB menu\n"));
4319 4467
4320 4468 (void) snprintf(cmdline, sizeof (cmdline),
4321 4469 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4322 4470 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4323 4471 ret = exec_cmd(cmdline, NULL);
4324 4472 INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4325 4473 if (ret != 0) {
4326 4474 bam_error(_("error propagating updated GRUB menu\n"));
4327 4475 return (BAM_ERROR);
4328 4476 }
4329 4477 BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn));
4330 4478
4331 4479 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4332 4480 GRUB_MENU, GRUB_BACKUP_MENU);
4333 4481 ret = exec_cmd(cmdline, NULL);
4334 4482 INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4335 4483 if (ret != 0) {
4336 4484 bam_error(_("failed to create backup for GRUB menu: %s\n"),
4337 4485 GRUB_BACKUP_MENU);
4338 4486 return (BAM_ERROR);
4339 4487 }
4340 4488 BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4341 4489 fcn, GRUB_BACKUP_MENU));
4342 4490
4343 4491 (void) snprintf(cmdline, sizeof (cmdline),
4344 4492 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4345 4493 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4346 4494 ret = exec_cmd(cmdline, NULL);
4347 4495 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4348 4496 if (ret != 0) {
4349 4497 bam_error(_("error propagating backup GRUB menu: %s\n"),
4350 4498 GRUB_BACKUP_MENU);
4351 4499 return (BAM_ERROR);
4352 4500 }
4353 4501 BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4354 4502 fcn, GRUB_BACKUP_MENU));
4355 4503
4356 4504 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4357 4505 CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4358 4506 ret = exec_cmd(cmdline, NULL);
4359 4507 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4360 4508 if (ret != 0) {
4361 4509 bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4362 4510 LU_MENU_CKSUM);
4363 4511 return (BAM_ERROR);
4364 4512 }
4365 4513 BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4366 4514 fcn, LU_MENU_CKSUM));
4367 4515
4368 4516 (void) snprintf(cmdline, sizeof (cmdline),
4369 4517 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4370 4518 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4371 4519 ret = exec_cmd(cmdline, NULL);
4372 4520 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4373 4521 if (ret != 0) {
4374 4522 bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4375 4523 LU_MENU_CKSUM);
4376 4524 return (BAM_ERROR);
4377 4525 }
4378 4526 BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4379 4527 fcn, LU_MENU_CKSUM));
4380 4528
4381 4529 return (BAM_SUCCESS);
4382 4530 }
4383 4531
4384 4532 static error_t
4385 4533 update_all(char *root, char *opt)
4386 4534 {
4387 4535 struct extmnttab mnt;
4388 4536 struct stat sb;
4389 4537 FILE *fp;
4390 4538 char multibt[PATH_MAX];
4391 4539 char creatram[PATH_MAX];
4392 4540 error_t ret = BAM_SUCCESS;
4393 4541
4394 4542 assert(root);
4395 4543 assert(opt == NULL);
4396 4544
4397 4545 if (bam_rootlen != 1 || *root != '/') {
4398 4546 elide_trailing_slash(root, multibt, sizeof (multibt));
4399 4547 bam_error(_("an alternate root (%s) cannot be used with this "
4400 4548 "sub-command\n"), multibt);
4401 4549 return (BAM_ERROR);
4402 4550 }
4403 4551
4404 4552 /*
4405 4553 * First update archive for current root
4406 4554 */
4407 4555 if (update_archive(root, opt) != BAM_SUCCESS)
4408 4556 ret = BAM_ERROR;
4409 4557
4410 4558 if (ret == BAM_ERROR)
4411 4559 goto out;
4412 4560
4413 4561 /*
4414 4562 * Now walk the mount table, performing archive update
4415 4563 * for all mounted Newboot root filesystems
4416 4564 */
4417 4565 fp = fopen(MNTTAB, "r");
4418 4566 if (fp == NULL) {
4419 4567 bam_error(_("failed to open file: %s: %s\n"),
4420 4568 MNTTAB, strerror(errno));
4421 4569 ret = BAM_ERROR;
4422 4570 goto out;
4423 4571 }
4424 4572
4425 4573 resetmnttab(fp);
4426 4574
4427 4575 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4428 4576 if (mnt.mnt_special == NULL)
4429 4577 continue;
4430 4578 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4431 4579 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4432 4580 continue;
4433 4581 if (strcmp(mnt.mnt_mountp, "/") == 0)
4434 4582 continue;
4435 4583
4436 4584 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4437 4585 mnt.mnt_mountp, CREATE_RAMDISK);
4438 4586
4439 4587 if (stat(creatram, &sb) == -1)
4440 4588 continue;
4441 4589
4442 4590 /*
4443 4591 * We put a trailing slash to be consistent with root = "/"
4444 4592 * case, such that we don't have to print // in some cases.
4445 4593 */
4446 4594 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4447 4595 mnt.mnt_mountp);
4448 4596 bam_rootlen = strlen(rootbuf);
4449 4597
4450 4598 /*
4451 4599 * It's possible that other mounts may be an alternate boot
4452 4600 * architecture, so check it again.
4453 4601 */
4454 4602 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4455 4603 (update_archive(rootbuf, opt) != BAM_SUCCESS))
4456 4604 ret = BAM_ERROR;
4457 4605 }
4458 4606
4459 4607 (void) fclose(fp);
4460 4608
4461 4609 out:
4462 4610 /*
4463 4611 * We no longer use biosdev for Live Upgrade. Hence
4464 4612 * there is no need to defer (to shutdown time) any fdisk
4465 4613 * updates
4466 4614 */
4467 4615 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4468 4616 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4469 4617 "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
4470 4618 }
4471 4619
4472 4620 /*
4473 4621 * If user has updated menu in current BE, propagate the
4474 4622 * updates to all BEs.
4475 4623 */
4476 4624 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4477 4625 ret = BAM_ERROR;
4478 4626
4479 4627 return (ret);
4480 4628 }
4481 4629
4482 4630 static void
4483 4631 append_line(menu_t *mp, line_t *lp)
4484 4632 {
4485 4633 if (mp->start == NULL) {
4486 4634 mp->start = lp;
4487 4635 } else {
4488 4636 mp->end->next = lp;
4489 4637 lp->prev = mp->end;
4490 4638 }
4491 4639 mp->end = lp;
4492 4640 }
4493 4641
4494 4642 void
4495 4643 unlink_line(menu_t *mp, line_t *lp)
4496 4644 {
4497 4645 /* unlink from list */
4498 4646 if (lp->prev)
4499 4647 lp->prev->next = lp->next;
4500 4648 else
4501 4649 mp->start = lp->next;
4502 4650 if (lp->next)
4503 4651 lp->next->prev = lp->prev;
4504 4652 else
4505 4653 mp->end = lp->prev;
4506 4654 }
4507 4655
4508 4656 static entry_t *
4509 4657 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4510 4658 {
4511 4659 entry_t *ent, *prev;
4512 4660 const char *fcn = "boot_entry_new()";
4513 4661
4514 4662 assert(mp);
4515 4663 assert(start);
4516 4664 assert(end);
4517 4665
4518 4666 ent = s_calloc(1, sizeof (entry_t));
4519 4667 BAM_DPRINTF(("%s: new boot entry alloced\n", fcn));
4520 4668 ent->start = start;
4521 4669 ent->end = end;
4522 4670
4523 4671 if (mp->entries == NULL) {
4524 4672 mp->entries = ent;
4525 4673 BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn));
4526 4674 return (ent);
4527 4675 }
4528 4676
4529 4677 prev = mp->entries;
4530 4678 while (prev->next)
4531 4679 prev = prev->next;
4532 4680 prev->next = ent;
4533 4681 ent->prev = prev;
4534 4682 BAM_DPRINTF(("%s: new boot entry linked in\n", fcn));
4535 4683 return (ent);
4536 4684 }
4537 4685
4538 4686 static void
4539 4687 boot_entry_addline(entry_t *ent, line_t *lp)
4540 4688 {
4541 4689 if (ent)
4542 4690 ent->end = lp;
4543 4691 }
4544 4692
4545 4693 /*
4546 4694 * Check whether cmd matches the one indexed by which, and whether arg matches
4547 4695 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4548 4696 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4549 4697 * strstr(), so it can be a partial match.
4550 4698 */
4551 4699 static int
4552 4700 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4553 4701 {
4554 4702 int ret;
4555 4703 const char *fcn = "check_cmd()";
4556 4704
4557 4705 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, arg, str));
4558 4706
4559 4707 if (cmd != NULL) {
4560 4708 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4561 4709 (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4562 4710 BAM_DPRINTF(("%s: command %s does not match %s\n",
4563 4711 fcn, cmd, menu_cmds[which]));
4564 4712 return (0);
4565 4713 }
4566 4714 ret = (strstr(arg, str) != NULL);
4567 4715 } else
4568 4716 ret = 0;
4569 4717
4570 4718 if (ret) {
4571 4719 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
4572 4720 } else {
4573 4721 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
4574 4722 }
4575 4723
4576 4724 return (ret);
4577 4725 }
4578 4726
4579 4727 static error_t
4580 4728 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4581 4729 {
4582 4730 const char *fcn = "kernel_parser()";
4583 4731
4584 4732 assert(entry);
4585 4733 assert(cmd);
4586 4734 assert(arg);
4587 4735
4588 4736 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4589 4737 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4590 4738 BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn, cmd));
4591 4739 return (BAM_ERROR);
4592 4740 }
4593 4741
4594 4742 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4595 4743 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4596 4744 fcn, arg));
4597 4745 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4598 4746 } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4599 4747 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4600 4748 BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn, arg));
4601 4749 entry->flags |= BAM_ENTRY_DBOOT;
4602 4750 } else if (strncmp(arg, DIRECT_BOOT_64,
4603 4751 sizeof (DIRECT_BOOT_64) - 1) == 0) {
4604 4752 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4605 4753 fcn, arg));
4606 4754 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4607 4755 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4608 4756 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4609 4757 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4610 4758 fcn, arg));
4611 4759 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4612 4760 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4613 4761 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4614 4762 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4615 4763 "flag: %s\n", fcn, arg));
4616 4764 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4617 4765 | BAM_ENTRY_32BIT;
4618 4766 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4619 4767 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4620 4768 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4621 4769 "flag: %s\n", fcn, arg));
4622 4770 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4623 4771 | BAM_ENTRY_64BIT;
4624 4772 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4625 4773 BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn, arg));
4626 4774 entry->flags |= BAM_ENTRY_MULTIBOOT;
4627 4775 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4628 4776 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4629 4777 BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4630 4778 "flag: %s\n", fcn, arg));
4631 4779 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4632 4780 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4633 4781 BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn, arg));
4634 4782 entry->flags |= BAM_ENTRY_HV;
4635 4783 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4636 4784 BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn, arg));
4637 4785 return (BAM_ERROR);
4638 4786 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4639 4787 strstr(arg, UNIX_SPACE)) {
4640 4788 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4641 4789 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4642 4790 strstr(arg, AMD_UNIX_SPACE)) {
4643 4791 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4644 4792 } else {
4645 4793 BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn, arg));
4646 4794 bam_error(_("kernel command on line %d not recognized.\n"),
4647 4795 linenum);
4648 4796 return (BAM_ERROR);
4649 4797 }
4650 4798
4651 4799 return (BAM_SUCCESS);
4652 4800 }
4653 4801
4654 4802 static error_t
4655 4803 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4656 4804 {
4657 4805 const char *fcn = "module_parser()";
4658 4806
4659 4807 assert(entry);
4660 4808 assert(cmd);
4661 4809 assert(arg);
4662 4810
4663 4811 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4664 4812 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4665 4813 BAM_DPRINTF(("%s: not module cmd: %s\n", fcn, cmd));
4666 4814 return (BAM_ERROR);
4667 4815 }
4668 4816
4669 4817 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4670 4818 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4671 4819 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4672 4820 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4673 4821 strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4674 4822 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4675 4823 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4676 4824 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4677 4825 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4678 4826 BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn, arg));
4679 4827 return (BAM_SUCCESS);
4680 4828 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4681 4829 !(entry->flags & BAM_ENTRY_LU)) {
4682 4830 /* don't emit warning for hand entries */
4683 4831 BAM_DPRINTF(("%s: is HAND module: %s\n", fcn, arg));
4684 4832 return (BAM_ERROR);
4685 4833 } else {
4686 4834 BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn, arg));
4687 4835 bam_error(_("module command on line %d not recognized.\n"),
4688 4836 linenum);
4689 4837 return (BAM_ERROR);
4690 4838 }
4691 4839 }
4692 4840
4693 4841 /*
4694 4842 * A line in menu.lst looks like
4695 4843 * [ ]*<cmd>[ \t=]*<arg>*
4696 4844 */
4697 4845 static void
4698 4846 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4699 4847 {
4700 4848 /*
4701 4849 * save state across calls. This is so that
4702 4850 * header gets the right entry# after title has
4703 4851 * been processed
4704 4852 */
4705 4853 static line_t *prev = NULL;
4706 4854 static entry_t *curr_ent = NULL;
4707 4855 static int in_liveupgrade = 0;
4708 4856 static int is_libbe_ent = 0;
4709 4857
4710 4858 line_t *lp;
4711 4859 char *cmd, *sep, *arg;
4712 4860 char save, *cp, *line;
4713 4861 menu_flag_t flag = BAM_INVALID;
4714 4862 const char *fcn = "line_parser()";
4715 4863
4716 4864 cmd = NULL;
4717 4865 if (str == NULL) {
4718 4866 return;
4719 4867 }
4720 4868
4721 4869 /*
4722 4870 * First save a copy of the entire line.
4723 4871 * We use this later to set the line field.
4724 4872 */
4725 4873 line = s_strdup(str);
4726 4874
4727 4875 /* Eat up leading whitespace */
4728 4876 while (*str == ' ' || *str == '\t')
4729 4877 str++;
4730 4878
4731 4879 if (*str == '#') { /* comment */
4732 4880 cmd = s_strdup("#");
4733 4881 sep = NULL;
4734 4882 arg = s_strdup(str + 1);
4735 4883 flag = BAM_COMMENT;
4736 4884 if (strstr(arg, BAM_LU_HDR) != NULL) {
4737 4885 in_liveupgrade = 1;
4738 4886 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4739 4887 in_liveupgrade = 0;
4740 4888 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4741 4889 is_libbe_ent = 1;
4742 4890 }
4743 4891 } else if (*str == '\0') { /* blank line */
4744 4892 cmd = sep = arg = NULL;
4745 4893 flag = BAM_EMPTY;
4746 4894 } else {
4747 4895 /*
4748 4896 * '=' is not a documented separator in grub syntax.
4749 4897 * However various development bits use '=' as a
4750 4898 * separator. In addition, external users also
4751 4899 * use = as a separator. So we will allow that usage.
4752 4900 */
4753 4901 cp = str;
4754 4902 while (*str != ' ' && *str != '\t' && *str != '=') {
4755 4903 if (*str == '\0') {
4756 4904 cmd = s_strdup(cp);
4757 4905 sep = arg = NULL;
4758 4906 break;
4759 4907 }
4760 4908 str++;
4761 4909 }
4762 4910
4763 4911 if (*str != '\0') {
4764 4912 save = *str;
4765 4913 *str = '\0';
4766 4914 cmd = s_strdup(cp);
4767 4915 *str = save;
4768 4916
4769 4917 str++;
4770 4918 save = *str;
4771 4919 *str = '\0';
4772 4920 sep = s_strdup(str - 1);
4773 4921 *str = save;
4774 4922
4775 4923 while (*str == ' ' || *str == '\t')
4776 4924 str++;
4777 4925 if (*str == '\0')
4778 4926 arg = NULL;
4779 4927 else
4780 4928 arg = s_strdup(str);
4781 4929 }
4782 4930 }
4783 4931
4784 4932 lp = s_calloc(1, sizeof (line_t));
4785 4933
4786 4934 lp->cmd = cmd;
4787 4935 lp->sep = sep;
4788 4936 lp->arg = arg;
4789 4937 lp->line = line;
4790 4938 lp->lineNum = ++(*lineNum);
4791 4939 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4792 4940 lp->entryNum = ++(*entryNum);
4793 4941 lp->flags = BAM_TITLE;
4794 4942 if (prev && prev->flags == BAM_COMMENT &&
4795 4943 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4796 4944 prev->entryNum = lp->entryNum;
4797 4945 curr_ent = boot_entry_new(mp, prev, lp);
4798 4946 curr_ent->flags |= BAM_ENTRY_BOOTADM;
4799 4947 BAM_DPRINTF(("%s: is bootadm(1M) entry: %s\n",
4800 4948 fcn, arg));
4801 4949 } else {
4802 4950 curr_ent = boot_entry_new(mp, lp, lp);
4803 4951 if (in_liveupgrade) {
4804 4952 curr_ent->flags |= BAM_ENTRY_LU;
4805 4953 BAM_DPRINTF(("%s: is LU entry: %s\n",
4806 4954 fcn, arg));
4807 4955 }
4808 4956 }
4809 4957 curr_ent->entryNum = *entryNum;
4810 4958 } else if (flag != BAM_INVALID) {
4811 4959 /*
4812 4960 * For header comments, the entry# is "fixed up"
4813 4961 * by the subsequent title
4814 4962 */
4815 4963 lp->entryNum = *entryNum;
4816 4964 lp->flags = flag;
4817 4965 } else {
4818 4966 lp->entryNum = *entryNum;
4819 4967
4820 4968 if (*entryNum == ENTRY_INIT) {
4821 4969 lp->flags = BAM_GLOBAL;
4822 4970 } else {
4823 4971 lp->flags = BAM_ENTRY;
4824 4972
4825 4973 if (cmd && arg) {
4826 4974 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4827 4975 BAM_DPRINTF(("%s: setting ROOT: %s\n",
4828 4976 fcn, arg));
4829 4977 curr_ent->flags |= BAM_ENTRY_ROOT;
4830 4978 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4831 4979 == 0) {
4832 4980 BAM_DPRINTF(("%s: setting "
4833 4981 "FINDROOT: %s\n", fcn, arg));
4834 4982 curr_ent->flags |= BAM_ENTRY_FINDROOT;
4835 4983 } else if (strcmp(cmd,
4836 4984 menu_cmds[CHAINLOADER_CMD]) == 0) {
4837 4985 BAM_DPRINTF(("%s: setting "
4838 4986 "CHAINLOADER: %s\n", fcn, arg));
4839 4987 curr_ent->flags |=
4840 4988 BAM_ENTRY_CHAINLOADER;
4841 4989 } else if (kernel_parser(curr_ent, cmd, arg,
4842 4990 lp->lineNum) != BAM_SUCCESS) {
4843 4991 (void) module_parser(curr_ent, cmd,
4844 4992 arg, lp->lineNum);
4845 4993 }
4846 4994 }
4847 4995 }
4848 4996 }
4849 4997
4850 4998 /* record default, old default, and entry line ranges */
4851 4999 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4852 5000 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4853 5001 mp->curdefault = lp;
4854 5002 } else if (lp->flags == BAM_COMMENT &&
4855 5003 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4856 5004 mp->olddefault = lp;
4857 5005 } else if (lp->flags == BAM_COMMENT &&
4858 5006 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4859 5007 mp->old_rc_default = lp;
4860 5008 } else if (lp->flags == BAM_ENTRY ||
4861 5009 (lp->flags == BAM_COMMENT &&
4862 5010 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4863 5011 if (is_libbe_ent) {
4864 5012 curr_ent->flags |= BAM_ENTRY_LIBBE;
4865 5013 is_libbe_ent = 0;
4866 5014 }
4867 5015
4868 5016 boot_entry_addline(curr_ent, lp);
4869 5017 }
4870 5018 append_line(mp, lp);
4871 5019
4872 5020 prev = lp;
4873 5021 }
4874 5022
4875 5023 void
4876 5024 update_numbering(menu_t *mp)
4877 5025 {
4878 5026 int lineNum;
4879 5027 int entryNum;
4880 5028 int old_default_value;
4881 5029 line_t *lp, *prev, *default_lp, *default_entry;
4882 5030 char buf[PATH_MAX];
4883 5031
4884 5032 if (mp->start == NULL) {
4885 5033 return;
4886 5034 }
4887 5035
4888 5036 lineNum = LINE_INIT;
4889 5037 entryNum = ENTRY_INIT;
4890 5038 old_default_value = ENTRY_INIT;
4891 5039 lp = default_lp = default_entry = NULL;
4892 5040
4893 5041 prev = NULL;
4894 5042 for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4895 5043 lp->lineNum = ++lineNum;
4896 5044
4897 5045 /*
4898 5046 * Get the value of the default command
4899 5047 */
4900 5048 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4901 5049 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4902 5050 lp->arg) {
4903 5051 old_default_value = atoi(lp->arg);
4904 5052 default_lp = lp;
4905 5053 }
4906 5054
4907 5055 /*
4908 5056 * If not a booting entry, nothing else to fix for this
4909 5057 * entry
4910 5058 */
4911 5059 if (lp->entryNum == ENTRY_INIT)
4912 5060 continue;
4913 5061
4914 5062 /*
4915 5063 * Record the position of the default entry.
4916 5064 * The following works because global
4917 5065 * commands like default and timeout should precede
4918 5066 * actual boot entries, so old_default_value
4919 5067 * is already known (or default cmd is missing).
4920 5068 */
4921 5069 if (default_entry == NULL &&
4922 5070 old_default_value != ENTRY_INIT &&
4923 5071 lp->entryNum == old_default_value) {
4924 5072 default_entry = lp;
4925 5073 }
4926 5074
4927 5075 /*
4928 5076 * Now fixup the entry number
4929 5077 */
4930 5078 if (lp->cmd != NULL &&
4931 5079 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4932 5080 lp->entryNum = ++entryNum;
4933 5081 /* fixup the bootadm header */
4934 5082 if (prev && prev->flags == BAM_COMMENT &&
4935 5083 prev->arg &&
4936 5084 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4937 5085 prev->entryNum = lp->entryNum;
4938 5086 }
4939 5087 } else {
4940 5088 lp->entryNum = entryNum;
4941 5089 }
4942 5090 }
4943 5091
4944 5092 /*
4945 5093 * No default command in menu, simply return
4946 5094 */
4947 5095 if (default_lp == NULL) {
4948 5096 return;
4949 5097 }
4950 5098
4951 5099 free(default_lp->arg);
4952 5100 free(default_lp->line);
4953 5101
4954 5102 if (default_entry == NULL) {
4955 5103 default_lp->arg = s_strdup("0");
4956 5104 } else {
4957 5105 (void) snprintf(buf, sizeof (buf), "%d",
4958 5106 default_entry->entryNum);
4959 5107 default_lp->arg = s_strdup(buf);
4960 5108 }
4961 5109
4962 5110 /*
4963 5111 * The following is required since only the line field gets
4964 5112 * written back to menu.lst
4965 5113 */
4966 5114 (void) snprintf(buf, sizeof (buf), "%s%s%s",
4967 5115 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4968 5116 default_lp->line = s_strdup(buf);
4969 5117 }
4970 5118
4971 5119
4972 5120 static menu_t *
4973 5121 menu_read(char *menu_path)
4974 5122 {
4975 5123 FILE *fp;
4976 5124 char buf[BAM_MAXLINE], *cp;
4977 5125 menu_t *mp;
4978 5126 int line, entry, len, n;
4979 5127
4980 5128 mp = s_calloc(1, sizeof (menu_t));
4981 5129
4982 5130 fp = fopen(menu_path, "r");
4983 5131 if (fp == NULL) { /* Let the caller handle this error */
4984 5132 free(mp);
4985 5133 return (NULL);
4986 5134 }
4987 5135
4988 5136 /* Note: GRUB boot entry number starts with 0 */
4989 5137 line = LINE_INIT;
4990 5138 entry = ENTRY_INIT;
4991 5139 cp = buf;
4992 5140 len = sizeof (buf);
4993 5141 while (s_fgets(cp, len, fp) != NULL) {
4994 5142 n = strlen(cp);
4995 5143 if (cp[n - 1] == '\\') {
4996 5144 len -= n - 1;
4997 5145 assert(len >= 2);
4998 5146 cp += n - 1;
4999 5147 continue;
5000 5148 }
5001 5149 line_parser(mp, buf, &line, &entry);
5002 5150 cp = buf;
5003 5151 len = sizeof (buf);
5004 5152 }
5005 5153
5006 5154 if (fclose(fp) == EOF) {
5007 5155 bam_error(_("failed to close file: %s: %s\n"), menu_path,
5008 5156 strerror(errno));
5009 5157 }
5010 5158
5011 5159 return (mp);
5012 5160 }
5013 5161
5014 5162 static error_t
5015 5163 selector(menu_t *mp, char *opt, int *entry, char **title)
5016 5164 {
5017 5165 char *eq;
5018 5166 char *opt_dup;
5019 5167 int entryNum;
5020 5168
5021 5169 assert(mp);
5022 5170 assert(mp->start);
5023 5171 assert(opt);
5024 5172
5025 5173 opt_dup = s_strdup(opt);
5026 5174
5027 5175 if (entry)
5028 5176 *entry = ENTRY_INIT;
5029 5177 if (title)
5030 5178 *title = NULL;
5031 5179
5032 5180 eq = strchr(opt_dup, '=');
5033 5181 if (eq == NULL) {
5034 5182 bam_error(_("invalid option: %s\n"), opt);
5035 5183 free(opt_dup);
5036 5184 return (BAM_ERROR);
5037 5185 }
5038 5186
5039 5187 *eq = '\0';
5040 5188 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
5041 5189 assert(mp->end);
5042 5190 entryNum = s_strtol(eq + 1);
5043 5191 if (entryNum < 0 || entryNum > mp->end->entryNum) {
5044 5192 bam_error(_("invalid boot entry number: %s\n"), eq + 1);
5045 5193 free(opt_dup);
5046 5194 return (BAM_ERROR);
5047 5195 }
5048 5196 *entry = entryNum;
5049 5197 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
5050 5198 *title = opt + (eq - opt_dup) + 1;
5051 5199 } else {
5052 5200 bam_error(_("invalid option: %s\n"), opt);
5053 5201 free(opt_dup);
5054 5202 return (BAM_ERROR);
5055 5203 }
5056 5204
5057 5205 free(opt_dup);
5058 5206 return (BAM_SUCCESS);
5059 5207 }
5060 5208
5061 5209 /*
5062 5210 * If invoked with no titles/entries (opt == NULL)
5063 5211 * only title lines in file are printed.
5064 5212 *
5065 5213 * If invoked with a title or entry #, all
5066 5214 * lines in *every* matching entry are listed
5067 5215 */
5068 5216 static error_t
5069 5217 list_entry(menu_t *mp, char *menu_path, char *opt)
5070 5218 {
5071 5219 line_t *lp;
5072 5220 int entry = ENTRY_INIT;
5073 5221 int found;
5074 5222 char *title = NULL;
5075 5223
5076 5224 assert(mp);
5077 5225 assert(menu_path);
5078 5226
5079 5227 /* opt is optional */
5080 5228 BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path,
5081 5229 opt ? opt : "<NULL>"));
5082 5230
5083 5231 if (mp->start == NULL) {
5084 5232 bam_error(_("menu file not found: %s\n"), menu_path);
5085 5233 return (BAM_ERROR);
5086 5234 }
5087 5235
5088 5236 if (opt != NULL) {
5089 5237 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
5090 5238 return (BAM_ERROR);
5091 5239 }
5092 5240 assert((entry != ENTRY_INIT) ^ (title != NULL));
5093 5241 } else {
5094 5242 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
5095 5243 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
5096 5244 }
5097 5245
5098 5246 found = 0;
5099 5247 for (lp = mp->start; lp; lp = lp->next) {
5100 5248 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
5101 5249 continue;
5102 5250 if (opt == NULL && lp->flags == BAM_TITLE) {
5103 5251 bam_print(_("%d %s\n"), lp->entryNum,
5104 5252 lp->arg);
5105 5253 found = 1;
5106 5254 continue;
5107 5255 }
5108 5256 if (entry != ENTRY_INIT && lp->entryNum == entry) {
5109 5257 bam_print(_("%s\n"), lp->line);
5110 5258 found = 1;
5111 5259 continue;
5112 5260 }
5113 5261
5114 5262 /*
5115 5263 * We set the entry value here so that all lines
5116 5264 * in entry get printed. If we subsequently match
5117 5265 * title in other entries, all lines in those
5118 5266 * entries get printed as well.
5119 5267 */
5120 5268 if (title && lp->flags == BAM_TITLE && lp->arg &&
5121 5269 strncmp(title, lp->arg, strlen(title)) == 0) {
5122 5270 bam_print(_("%s\n"), lp->line);
5123 5271 entry = lp->entryNum;
5124 5272 found = 1;
5125 5273 continue;
5126 5274 }
5127 5275 }
5128 5276
5129 5277 if (!found) {
5130 5278 bam_error(_("no matching entry found\n"));
5131 5279 return (BAM_ERROR);
5132 5280 }
5133 5281
5134 5282 return (BAM_SUCCESS);
5135 5283 }
5136 5284
5137 5285 int
5138 5286 add_boot_entry(menu_t *mp,
5139 5287 char *title,
5140 5288 char *findroot,
5141 5289 char *kernel,
5142 5290 char *mod_kernel,
5143 5291 char *module,
5144 5292 char *bootfs)
5145 5293 {
5146 5294 int lineNum;
5147 5295 int entryNum;
5148 5296 char linebuf[BAM_MAXLINE];
5149 5297 menu_cmd_t k_cmd;
5150 5298 menu_cmd_t m_cmd;
5151 5299 const char *fcn = "add_boot_entry()";
5152 5300
5153 5301 assert(mp);
5154 5302
5155 5303 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
5156 5304 if (findroot == NULL) {
5157 5305 bam_error(_("can't find argument for findroot command\n"));
5158 5306 return (BAM_ERROR);
5159 5307 }
5160 5308
5161 5309 if (title == NULL) {
5162 5310 title = "Solaris"; /* default to Solaris */
5163 5311 }
5164 5312 if (kernel == NULL) {
5165 5313 bam_error(_("missing suboption: %s\n"), menu_cmds[KERNEL_CMD]);
5166 5314 return (BAM_ERROR);
5167 5315 }
5168 5316 if (module == NULL) {
5169 5317 if (bam_direct != BAM_DIRECT_DBOOT) {
5170 5318 bam_error(_("missing suboption: %s\n"),
5171 5319 menu_cmds[MODULE_CMD]);
5172 5320 return (BAM_ERROR);
5173 5321 }
5174 5322
5175 5323 /* Figure the commands out from the kernel line */
5176 5324 if (strstr(kernel, "$ISADIR") != NULL) {
5177 5325 module = DIRECT_BOOT_ARCHIVE;
5178 5326 } else if (strstr(kernel, "amd64") != NULL) {
5179 5327 module = DIRECT_BOOT_ARCHIVE_64;
5180 5328 } else {
5181 5329 module = DIRECT_BOOT_ARCHIVE_32;
5182 5330 }
5183 5331 }
5184 5332
5185 5333 k_cmd = KERNEL_DOLLAR_CMD;
5186 5334 m_cmd = MODULE_DOLLAR_CMD;
5187 5335
5188 5336 if (mp->start) {
5189 5337 lineNum = mp->end->lineNum;
5190 5338 entryNum = mp->end->entryNum;
5191 5339 } else {
5192 5340 lineNum = LINE_INIT;
5193 5341 entryNum = ENTRY_INIT;
5194 5342 }
5195 5343
5196 5344 /*
5197 5345 * No separator for comment (HDR/FTR) commands
5198 5346 * The syntax for comments is #<comment>
5199 5347 */
5200 5348 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5201 5349 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5202 5350 line_parser(mp, linebuf, &lineNum, &entryNum);
5203 5351
5204 5352 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5205 5353 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5206 5354 line_parser(mp, linebuf, &lineNum, &entryNum);
5207 5355
5208 5356 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5209 5357 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5210 5358 line_parser(mp, linebuf, &lineNum, &entryNum);
5211 5359 BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5212 5360 fcn, lineNum, entryNum));
5213 5361
5214 5362 if (bootfs != NULL) {
5215 5363 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5216 5364 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5217 5365 line_parser(mp, linebuf, &lineNum, &entryNum);
5218 5366 }
5219 5367
5220 5368 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5221 5369 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5222 5370 line_parser(mp, linebuf, &lineNum, &entryNum);
5223 5371
5224 5372 if (mod_kernel != NULL) {
5225 5373 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5226 5374 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5227 5375 line_parser(mp, linebuf, &lineNum, &entryNum);
5228 5376 }
5229 5377
5230 5378 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5231 5379 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5232 5380 line_parser(mp, linebuf, &lineNum, &entryNum);
5233 5381
5234 5382 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5235 5383 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5236 5384 line_parser(mp, linebuf, &lineNum, &entryNum);
5237 5385
5238 5386 return (entryNum);
5239 5387 }
5240 5388
5241 5389 error_t
5242 5390 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5243 5391 {
5244 5392 line_t *lp;
5245 5393 line_t *freed;
5246 5394 entry_t *ent;
5247 5395 entry_t *tmp;
5248 5396 int deleted = 0;
5249 5397 const char *fcn = "delete_boot_entry()";
5250 5398
5251 5399 assert(entryNum != ENTRY_INIT);
5252 5400
5253 5401 tmp = NULL;
5254 5402
5255 5403 ent = mp->entries;
5256 5404 while (ent) {
5257 5405 lp = ent->start;
5258 5406
5259 5407 /*
5260 5408 * Check entry number and make sure it's a modifiable entry.
5261 5409 *
5262 5410 * Guidelines:
5263 5411 * + We can modify a bootadm-created entry
5264 5412 * + We can modify a libbe-created entry
5265 5413 */
5266 5414 if ((lp->flags != BAM_COMMENT &&
5267 5415 (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5268 5416 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5269 5417 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5270 5418 ent = ent->next;
5271 5419 continue;
5272 5420 }
5273 5421
5274 5422 /* free the entry content */
5275 5423 do {
5276 5424 freed = lp;
5277 5425 lp = lp->next; /* prev stays the same */
5278 5426 BAM_DPRINTF(("%s: freeing line: %d\n",
5279 5427 fcn, freed->lineNum));
5280 5428 unlink_line(mp, freed);
5281 5429 line_free(freed);
5282 5430 } while (freed != ent->end);
5283 5431
5284 5432 /* free the entry_t structure */
5285 5433 assert(tmp == NULL);
5286 5434 tmp = ent;
5287 5435 ent = ent->next;
5288 5436 if (tmp->prev)
5289 5437 tmp->prev->next = ent;
5290 5438 else
5291 5439 mp->entries = ent;
5292 5440 if (ent)
5293 5441 ent->prev = tmp->prev;
5294 5442 BAM_DPRINTF(("%s: freeing entry: %d\n", fcn, tmp->entryNum));
5295 5443 free(tmp);
5296 5444 tmp = NULL;
5297 5445 deleted = 1;
5298 5446 }
5299 5447
5300 5448 assert(tmp == NULL);
5301 5449
5302 5450 if (!deleted && entryNum != ALL_ENTRIES) {
5303 5451 if (quiet == DBE_PRINTERR)
5304 5452 bam_error(_("no matching bootadm entry found\n"));
5305 5453 return (BAM_ERROR);
5306 5454 }
5307 5455
5308 5456 /*
5309 5457 * Now that we have deleted an entry, update
5310 5458 * the entry numbering and the default cmd.
5311 5459 */
5312 5460 update_numbering(mp);
5313 5461
5314 5462 return (BAM_SUCCESS);
5315 5463 }
5316 5464
5317 5465 static error_t
5318 5466 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5319 5467 {
5320 5468 assert(mp);
5321 5469 assert(dummy == NULL);
5322 5470 assert(opt == NULL);
5323 5471
5324 5472 BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5325 5473
5326 5474 if (mp->start == NULL) {
5327 5475 bam_print(_("the GRUB menu is empty\n"));
5328 5476 return (BAM_SUCCESS);
5329 5477 }
5330 5478
5331 5479 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5332 5480 return (BAM_ERROR);
5333 5481 }
5334 5482
5335 5483 return (BAM_WRITE);
5336 5484 }
5337 5485
5338 5486 static FILE *
5339 5487 create_diskmap(char *osroot)
5340 5488 {
5341 5489 FILE *fp;
5342 5490 char cmd[PATH_MAX + 16];
5343 5491 char path[PATH_MAX];
5344 5492 const char *fcn = "create_diskmap()";
5345 5493
5346 5494 /* make sure we have a map file */
5347 5495 fp = fopen(GRUBDISK_MAP, "r");
5348 5496 if (fp == NULL) {
5349 5497 int ret;
5350 5498
5351 5499 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5352 5500 CREATE_DISKMAP);
5353 5501 if (ret >= sizeof (path)) {
5354 5502 bam_error(_("unable to create path on mountpoint %s, "
5355 5503 "path too long\n"), osroot);
5356 5504 return (NULL);
5357 5505 }
5358 5506 if (is_safe_exec(path) == BAM_ERROR)
5359 5507 return (NULL);
5360 5508
5361 5509 (void) snprintf(cmd, sizeof (cmd),
5362 5510 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5363 5511 if (exec_cmd(cmd, NULL) != 0)
5364 5512 return (NULL);
5365 5513 fp = fopen(GRUBDISK_MAP, "r");
5366 5514 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5367 5515 if (fp) {
5368 5516 BAM_DPRINTF(("%s: created diskmap file: %s\n",
5369 5517 fcn, GRUBDISK_MAP));
5370 5518 } else {
5371 5519 BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5372 5520 fcn, GRUBDISK_MAP));
5373 5521 }
5374 5522 }
5375 5523 return (fp);
5376 5524 }
5377 5525
5378 5526 #define SECTOR_SIZE 512
5379 5527
5380 5528 static int
5381 5529 get_partition(char *device)
5382 5530 {
5383 5531 int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5384 5532 struct mboot *mboot;
5385 5533 char boot_sect[SECTOR_SIZE];
5386 5534 char *wholedisk, *slice;
5387 5535 #ifdef i386
5388 5536 ext_part_t *epp;
5389 5537 uint32_t secnum, numsec;
5390 5538 int rval, pno, ext_partno = PARTNO_NOTFOUND;
5391 5539 #endif
5392 5540
5393 5541 /* form whole disk (p0) */
5394 5542 slice = device + strlen(device) - 2;
5395 5543 is_pcfs = (*slice != 's');
5396 5544 if (!is_pcfs)
5397 5545 *slice = '\0';
5398 5546 wholedisk = s_calloc(1, strlen(device) + 3);
5399 5547 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5400 5548 if (!is_pcfs)
5401 5549 *slice = 's';
5402 5550
5403 5551 /* read boot sector */
5404 5552 fd = open(wholedisk, O_RDONLY);
5405 5553 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5406 5554 return (partno);
5407 5555 }
5408 5556 (void) close(fd);
5409 5557
5410 5558 #ifdef i386
5411 5559 /* Read/Initialize extended partition information */
5412 5560 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5413 5561 != FDISK_SUCCESS) {
5414 5562 switch (rval) {
5415 5563 /*
5416 5564 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5417 5565 * be considered as soft errors and hence
5418 5566 * we do not return
5419 5567 */
5420 5568 case FDISK_EBADLOGDRIVE:
5421 5569 break;
5422 5570 case FDISK_ENOLOGDRIVE:
5423 5571 break;
5424 5572 case FDISK_EBADMAGIC:
5425 5573 /*FALLTHROUGH*/
5426 5574 default:
5427 5575 free(wholedisk);
5428 5576 libfdisk_fini(&epp);
5429 5577 return (partno);
5430 5578 }
5431 5579 }
5432 5580 #endif
5433 5581 free(wholedisk);
5434 5582
5435 5583 /* parse fdisk table */
5436 5584 mboot = (struct mboot *)((void *)boot_sect);
5437 5585 for (i = 0; i < FD_NUMPART; i++) {
5438 5586 struct ipart *part =
5439 5587 (struct ipart *)(uintptr_t)mboot->parts + i;
5440 5588 if (is_pcfs) { /* looking for solaris boot part */
5441 5589 if (part->systid == 0xbe) {
5442 5590 partno = i;
5443 5591 break;
5444 5592 }
5445 5593 } else { /* look for solaris partition, old and new */
5446 5594 if (part->systid == EFI_PMBR) {
5447 5595 partno = PARTNO_EFI;
5448 5596 break;
5449 5597 }
5450 5598
5451 5599 #ifdef i386
5452 5600 if ((part->systid == SUNIXOS &&
5453 5601 (fdisk_is_linux_swap(epp, part->relsect,
5454 5602 NULL) != 0)) || part->systid == SUNIXOS2) {
5455 5603 #else
5456 5604 if (part->systid == SUNIXOS ||
5457 5605 part->systid == SUNIXOS2) {
5458 5606 #endif
5459 5607 partno = i;
5460 5608 break;
5461 5609 }
5462 5610
5463 5611 #ifdef i386
5464 5612 if (fdisk_is_dos_extended(part->systid))
5465 5613 ext_partno = i;
5466 5614 #endif
5467 5615 }
5468 5616 }
5469 5617 #ifdef i386
5470 5618 /* If no primary solaris partition, check extended partition */
5471 5619 if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5472 5620 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5473 5621 if (rval == FDISK_SUCCESS) {
5474 5622 partno = pno - 1;
5475 5623 }
5476 5624 }
5477 5625 libfdisk_fini(&epp);
5478 5626 #endif
5479 5627 return (partno);
5480 5628 }
5481 5629
5482 5630 char *
5483 5631 get_grubroot(char *osroot, char *osdev, char *menu_root)
5484 5632 {
5485 5633 char *grubroot; /* (hd#,#,#) */
5486 5634 char *slice;
5487 5635 char *grubhd = NULL;
5488 5636 int fdiskpart;
5489 5637 int found = 0;
5490 5638 char *devname;
5491 5639 char *ctdname = strstr(osdev, "dsk/");
5492 5640 char linebuf[PATH_MAX];
5493 5641 FILE *fp;
5494 5642
5495 5643 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5496 5644 if (ctdname == NULL) {
5497 5645 bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev);
5498 5646 return (NULL);
5499 5647 }
5500 5648
5501 5649 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5502 5650 /* menu bears no resemblance to our reality */
5503 5651 bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5504 5652 "bootdisk: %s\n"), osdev);
5505 5653 return (NULL);
5506 5654 }
5507 5655
5508 5656 ctdname += strlen("dsk/");
5509 5657 slice = strrchr(ctdname, 's');
5510 5658 if (slice)
5511 5659 *slice = '\0';
5512 5660
5513 5661 fp = create_diskmap(osroot);
5514 5662 if (fp == NULL) {
5515 5663 bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5516 5664 osroot);
5517 5665 return (NULL);
5518 5666 }
5519 5667
5520 5668 rewind(fp);
5521 5669 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5522 5670 grubhd = strtok(linebuf, " \t\n");
5523 5671 if (grubhd)
5524 5672 devname = strtok(NULL, " \t\n");
5525 5673 else
5526 5674 devname = NULL;
5527 5675 if (devname && strcmp(devname, ctdname) == 0) {
5528 5676 found = 1;
5529 5677 break;
5530 5678 }
5531 5679 }
5532 5680
5533 5681 if (slice)
5534 5682 *slice = 's';
5535 5683
5536 5684 (void) fclose(fp);
5537 5685 fp = NULL;
5538 5686
5539 5687 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5540 5688 if (found == 0) {
5541 5689 bam_error(_("not using biosdev command for disk: %s.\n"),
5542 5690 osdev);
5543 5691 return (NULL);
5544 5692 }
5545 5693
5546 5694 fdiskpart = get_partition(osdev);
5547 5695 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5548 5696 if (fdiskpart == PARTNO_NOTFOUND) {
5549 5697 bam_error(_("failed to determine fdisk partition: %s\n"),
5550 5698 osdev);
5551 5699 return (NULL);
5552 5700 }
5553 5701
5554 5702 grubroot = s_calloc(1, 10);
5555 5703 if (fdiskpart == PARTNO_EFI) {
5556 5704 fdiskpart = atoi(&slice[1]);
5557 5705 slice = NULL;
5558 5706 }
5559 5707
5560 5708 if (slice) {
5561 5709 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5562 5710 grubhd, fdiskpart, slice[1] + 'a' - '0');
5563 5711 } else
5564 5712 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5565 5713 grubhd, fdiskpart);
5566 5714
5567 5715 assert(fp == NULL);
5568 5716 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5569 5717 return (grubroot);
5570 5718 }
5571 5719
5572 5720 static char *
5573 5721 find_primary_common(char *mntpt, char *fstype)
5574 5722 {
5575 5723 char signdir[PATH_MAX];
5576 5724 char tmpsign[MAXNAMELEN + 1];
5577 5725 char *lu;
5578 5726 char *ufs;
5579 5727 char *zfs;
5580 5728 DIR *dirp = NULL;
5581 5729 struct dirent *entp;
5582 5730 struct stat sb;
5583 5731 const char *fcn = "find_primary_common()";
5584 5732
5585 5733 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5586 5734 mntpt, GRUBSIGN_DIR);
5587 5735
5588 5736 if (stat(signdir, &sb) == -1) {
5589 5737 BAM_DPRINTF(("%s: no sign dir: %s\n", fcn, signdir));
5590 5738 return (NULL);
5591 5739 }
5592 5740
5593 5741 dirp = opendir(signdir);
5594 5742 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5595 5743 if (dirp == NULL) {
5596 5744 bam_error(_("opendir of %s failed: %s\n"), signdir,
5597 5745 strerror(errno));
5598 5746 return (NULL);
5599 5747 }
5600 5748
5601 5749 ufs = zfs = lu = NULL;
5602 5750
5603 5751 while ((entp = readdir(dirp)) != NULL) {
5604 5752 if (strcmp(entp->d_name, ".") == 0 ||
5605 5753 strcmp(entp->d_name, "..") == 0)
5606 5754 continue;
5607 5755
5608 5756 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5609 5757
5610 5758 if (lu == NULL &&
5611 5759 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5612 5760 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5613 5761 lu = s_strdup(tmpsign);
5614 5762 }
5615 5763
5616 5764 if (ufs == NULL &&
5617 5765 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5618 5766 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5619 5767 ufs = s_strdup(tmpsign);
5620 5768 }
5621 5769
5622 5770 if (zfs == NULL &&
5623 5771 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5624 5772 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5625 5773 zfs = s_strdup(tmpsign);
5626 5774 }
5627 5775 }
5628 5776
5629 5777 BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn,
5630 5778 zfs ? zfs : "NULL",
5631 5779 ufs ? ufs : "NULL",
5632 5780 lu ? lu : "NULL"));
5633 5781
5634 5782 if (dirp) {
5635 5783 (void) closedir(dirp);
5636 5784 dirp = NULL;
5637 5785 }
5638 5786
5639 5787 if (strcmp(fstype, "ufs") == 0 && zfs) {
5640 5788 bam_error(_("found mismatched boot signature %s for "
5641 5789 "filesystem type: %s.\n"), zfs, "ufs");
5642 5790 free(zfs);
5643 5791 zfs = NULL;
5644 5792 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5645 5793 bam_error(_("found mismatched boot signature %s for "
5646 5794 "filesystem type: %s.\n"), ufs, "zfs");
5647 5795 free(ufs);
5648 5796 ufs = NULL;
5649 5797 }
5650 5798
5651 5799 assert(dirp == NULL);
5652 5800
5653 5801 /* For now, we let Live Upgrade take care of its signature itself */
5654 5802 if (lu) {
5655 5803 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5656 5804 free(lu);
5657 5805 lu = NULL;
5658 5806 }
5659 5807
5660 5808 return (zfs ? zfs : ufs);
5661 5809 }
5662 5810
5663 5811 static char *
5664 5812 find_backup_common(char *mntpt, char *fstype)
5665 5813 {
5666 5814 FILE *bfp = NULL;
5667 5815 char tmpsign[MAXNAMELEN + 1];
5668 5816 char backup[PATH_MAX];
5669 5817 char *ufs;
5670 5818 char *zfs;
5671 5819 char *lu;
5672 5820 int error;
5673 5821 const char *fcn = "find_backup_common()";
5674 5822
5675 5823 /*
5676 5824 * We didn't find it in the primary directory.
5677 5825 * Look at the backup
5678 5826 */
5679 5827 (void) snprintf(backup, sizeof (backup), "%s%s",
5680 5828 mntpt, GRUBSIGN_BACKUP);
5681 5829
5682 5830 bfp = fopen(backup, "r");
5683 5831 if (bfp == NULL) {
5684 5832 error = errno;
5685 5833 if (bam_verbose) {
5686 5834 bam_error(_("failed to open file: %s: %s\n"),
5687 5835 backup, strerror(error));
5688 5836 }
5689 5837 BAM_DPRINTF(("%s: failed to open %s: %s\n",
5690 5838 fcn, backup, strerror(error)));
5691 5839 return (NULL);
5692 5840 }
5693 5841
5694 5842 ufs = zfs = lu = NULL;
5695 5843
5696 5844 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5697 5845
5698 5846 if (lu == NULL &&
5699 5847 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5700 5848 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5701 5849 lu = s_strdup(tmpsign);
5702 5850 }
5703 5851
5704 5852 if (ufs == NULL &&
5705 5853 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5706 5854 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5707 5855 ufs = s_strdup(tmpsign);
5708 5856 }
5709 5857
5710 5858 if (zfs == NULL &&
5711 5859 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5712 5860 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5713 5861 zfs = s_strdup(tmpsign);
5714 5862 }
5715 5863 }
5716 5864
5717 5865 BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn,
5718 5866 zfs ? zfs : "NULL",
5719 5867 ufs ? ufs : "NULL",
5720 5868 lu ? lu : "NULL"));
5721 5869
5722 5870 if (bfp) {
5723 5871 (void) fclose(bfp);
5724 5872 bfp = NULL;
5725 5873 }
5726 5874
5727 5875 if (strcmp(fstype, "ufs") == 0 && zfs) {
5728 5876 bam_error(_("found mismatched boot signature %s for "
5729 5877 "filesystem type: %s.\n"), zfs, "ufs");
5730 5878 free(zfs);
5731 5879 zfs = NULL;
5732 5880 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5733 5881 bam_error(_("found mismatched boot signature %s for "
5734 5882 "filesystem type: %s.\n"), ufs, "zfs");
5735 5883 free(ufs);
5736 5884 ufs = NULL;
5737 5885 }
5738 5886
5739 5887 assert(bfp == NULL);
5740 5888
5741 5889 /* For now, we let Live Upgrade take care of its signature itself */
5742 5890 if (lu) {
5743 5891 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5744 5892 free(lu);
5745 5893 lu = NULL;
5746 5894 }
5747 5895
5748 5896 return (zfs ? zfs : ufs);
5749 5897 }
5750 5898
5751 5899 static char *
5752 5900 find_ufs_existing(char *osroot)
5753 5901 {
5754 5902 char *sign;
5755 5903 const char *fcn = "find_ufs_existing()";
5756 5904
5757 5905 sign = find_primary_common(osroot, "ufs");
5758 5906 if (sign == NULL) {
5759 5907 sign = find_backup_common(osroot, "ufs");
5760 5908 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
5761 5909 sign ? sign : "NULL"));
5762 5910 } else {
5763 5911 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
5764 5912 }
5765 5913
5766 5914 return (sign);
5767 5915 }
5768 5916
5769 5917 char *
5770 5918 get_mountpoint(char *special, char *fstype)
5771 5919 {
5772 5920 FILE *mntfp;
5773 5921 struct mnttab mp = {0};
5774 5922 struct mnttab mpref = {0};
5775 5923 int error;
5776 5924 int ret;
5777 5925 const char *fcn = "get_mountpoint()";
5778 5926
5779 5927 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
5780 5928
5781 5929 mntfp = fopen(MNTTAB, "r");
5782 5930 error = errno;
5783 5931 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5784 5932 if (mntfp == NULL) {
5785 5933 bam_error(_("failed to open file: %s: %s\n"),
5786 5934 MNTTAB, strerror(error));
5787 5935 return (NULL);
5788 5936 }
5789 5937
5790 5938 mpref.mnt_special = special;
5791 5939 mpref.mnt_fstype = fstype;
5792 5940
5793 5941 ret = getmntany(mntfp, &mp, &mpref);
5794 5942 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5795 5943 if (ret != 0) {
5796 5944 (void) fclose(mntfp);
5797 5945 BAM_DPRINTF(("%s: no mount-point for special=%s and "
5798 5946 "fstype=%s\n", fcn, special, fstype));
5799 5947 return (NULL);
5800 5948 }
5801 5949 (void) fclose(mntfp);
5802 5950
5803 5951 assert(mp.mnt_mountp);
5804 5952
5805 5953 BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5806 5954 fcn, special, mp.mnt_mountp));
5807 5955
5808 5956 return (s_strdup(mp.mnt_mountp));
5809 5957 }
5810 5958
5811 5959 /*
5812 5960 * Mounts a "legacy" top dataset (if needed)
5813 5961 * Returns: The mountpoint of the legacy top dataset or NULL on error
5814 5962 * mnted returns one of the above values defined for zfs_mnted_t
5815 5963 */
5816 5964 static char *
5817 5965 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5818 5966 {
5819 5967 char cmd[PATH_MAX];
5820 5968 char tmpmnt[PATH_MAX];
5821 5969 filelist_t flist = {0};
5822 5970 char *is_mounted;
5823 5971 struct stat sb;
5824 5972 int ret;
5825 5973 const char *fcn = "mount_legacy_dataset()";
5826 5974
5827 5975 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5828 5976
5829 5977 *mnted = ZFS_MNT_ERROR;
5830 5978
5831 5979 (void) snprintf(cmd, sizeof (cmd),
5832 5980 "/sbin/zfs get -Ho value mounted %s",
5833 5981 pool);
5834 5982
5835 5983 ret = exec_cmd(cmd, &flist);
5836 5984 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5837 5985 if (ret != 0) {
5838 5986 bam_error(_("failed to determine mount status of ZFS "
5839 5987 "pool %s\n"), pool);
5840 5988 return (NULL);
5841 5989 }
5842 5990
5843 5991 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5844 5992 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5845 5993 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
5846 5994 filelist_free(&flist);
5847 5995 return (NULL);
5848 5996 }
5849 5997
5850 5998 is_mounted = strtok(flist.head->line, " \t\n");
5851 5999 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5852 6000 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5853 6001 if (strcmp(is_mounted, "no") != 0) {
5854 6002 filelist_free(&flist);
5855 6003 *mnted = LEGACY_ALREADY;
5856 6004 /* get_mountpoint returns a strdup'ed string */
5857 6005 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
5858 6006 fcn, pool));
5859 6007 return (get_mountpoint(pool, "zfs"));
5860 6008 }
5861 6009
5862 6010 filelist_free(&flist);
5863 6011
5864 6012 /*
5865 6013 * legacy top dataset is not mounted. Mount it now
5866 6014 * First create a mountpoint.
5867 6015 */
5868 6016 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5869 6017 ZFS_LEGACY_MNTPT, getpid());
5870 6018
5871 6019 ret = stat(tmpmnt, &sb);
5872 6020 if (ret == -1) {
5873 6021 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
5874 6022 fcn, pool, tmpmnt));
5875 6023 ret = mkdirp(tmpmnt, DIR_PERMS);
5876 6024 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5877 6025 if (ret == -1) {
5878 6026 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
5879 6027 strerror(errno));
5880 6028 return (NULL);
5881 6029 }
5882 6030 } else {
5883 6031 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
5884 6032 "present\n", fcn, pool, tmpmnt));
5885 6033 }
5886 6034
5887 6035 (void) snprintf(cmd, sizeof (cmd),
5888 6036 "/sbin/mount -F zfs %s %s",
5889 6037 pool, tmpmnt);
5890 6038
5891 6039 ret = exec_cmd(cmd, NULL);
5892 6040 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5893 6041 if (ret != 0) {
5894 6042 bam_error(_("mount of ZFS pool %s failed\n"), pool);
5895 6043 (void) rmdir(tmpmnt);
5896 6044 return (NULL);
5897 6045 }
5898 6046
5899 6047 *mnted = LEGACY_MOUNTED;
5900 6048 BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
5901 6049 fcn, pool, tmpmnt));
5902 6050 return (s_strdup(tmpmnt));
5903 6051 }
5904 6052
5905 6053 /*
5906 6054 * Mounts the top dataset (if needed)
5907 6055 * Returns: The mountpoint of the top dataset or NULL on error
5908 6056 * mnted returns one of the above values defined for zfs_mnted_t
5909 6057 */
5910 6058 char *
5911 6059 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5912 6060 {
5913 6061 char cmd[PATH_MAX];
5914 6062 filelist_t flist = {0};
5915 6063 char *is_mounted;
5916 6064 char *mntpt;
5917 6065 char *zmntpt;
5918 6066 int ret;
5919 6067 const char *fcn = "mount_top_dataset()";
5920 6068
5921 6069 *mnted = ZFS_MNT_ERROR;
5922 6070
5923 6071 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5924 6072
5925 6073 /*
5926 6074 * First check if the top dataset is a "legacy" dataset
5927 6075 */
5928 6076 (void) snprintf(cmd, sizeof (cmd),
5929 6077 "/sbin/zfs get -Ho value mountpoint %s",
5930 6078 pool);
5931 6079 ret = exec_cmd(cmd, &flist);
5932 6080 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5933 6081 if (ret != 0) {
5934 6082 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
5935 6083 pool);
5936 6084 return (NULL);
5937 6085 }
5938 6086
5939 6087 if (flist.head && (flist.head == flist.tail)) {
5940 6088 char *legacy = strtok(flist.head->line, " \t\n");
5941 6089 if (legacy && strcmp(legacy, "legacy") == 0) {
5942 6090 filelist_free(&flist);
5943 6091 BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
5944 6092 return (mount_legacy_dataset(pool, mnted));
5945 6093 }
5946 6094 }
5947 6095
5948 6096 filelist_free(&flist);
5949 6097
5950 6098 BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
5951 6099
5952 6100 (void) snprintf(cmd, sizeof (cmd),
5953 6101 "/sbin/zfs get -Ho value mounted %s",
5954 6102 pool);
5955 6103
5956 6104 ret = exec_cmd(cmd, &flist);
5957 6105 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5958 6106 if (ret != 0) {
5959 6107 bam_error(_("failed to determine mount status of ZFS "
5960 6108 "pool %s\n"), pool);
5961 6109 return (NULL);
5962 6110 }
5963 6111
5964 6112 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5965 6113 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5966 6114 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
5967 6115 filelist_free(&flist);
5968 6116 return (NULL);
5969 6117 }
5970 6118
5971 6119 is_mounted = strtok(flist.head->line, " \t\n");
5972 6120 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5973 6121 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5974 6122 if (strcmp(is_mounted, "no") != 0) {
5975 6123 filelist_free(&flist);
5976 6124 *mnted = ZFS_ALREADY;
5977 6125 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
5978 6126 fcn, pool));
5979 6127 goto mounted;
5980 6128 }
5981 6129
5982 6130 filelist_free(&flist);
5983 6131 BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
5984 6132 fcn, pool));
5985 6133
5986 6134 /* top dataset is not mounted. Mount it now */
5987 6135 (void) snprintf(cmd, sizeof (cmd),
5988 6136 "/sbin/zfs mount %s", pool);
5989 6137 ret = exec_cmd(cmd, NULL);
5990 6138 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5991 6139 if (ret != 0) {
5992 6140 bam_error(_("mount of ZFS pool %s failed\n"), pool);
5993 6141 return (NULL);
5994 6142 }
5995 6143 *mnted = ZFS_MOUNTED;
5996 6144 BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
5997 6145 /*FALLTHRU*/
5998 6146 mounted:
5999 6147 /*
6000 6148 * Now get the mountpoint
6001 6149 */
6002 6150 (void) snprintf(cmd, sizeof (cmd),
6003 6151 "/sbin/zfs get -Ho value mountpoint %s",
6004 6152 pool);
6005 6153
6006 6154 ret = exec_cmd(cmd, &flist);
6007 6155 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
6008 6156 if (ret != 0) {
6009 6157 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6010 6158 pool);
6011 6159 goto error;
6012 6160 }
6013 6161
6014 6162 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
6015 6163 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6016 6164 bam_error(_("ZFS pool %s has no mount-point\n"), pool);
6017 6165 goto error;
6018 6166 }
6019 6167
6020 6168 mntpt = strtok(flist.head->line, " \t\n");
6021 6169 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
6022 6170 if (*mntpt != '/') {
6023 6171 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6024 6172 pool, mntpt);
6025 6173 goto error;
6026 6174 }
6027 6175 zmntpt = s_strdup(mntpt);
6028 6176
6029 6177 filelist_free(&flist);
6030 6178
6031 6179 BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6032 6180 fcn, pool, zmntpt));
6033 6181
6034 6182 return (zmntpt);
6035 6183
6036 6184 error:
6037 6185 filelist_free(&flist);
6038 6186 (void) umount_top_dataset(pool, *mnted, NULL);
6039 6187 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6040 6188 return (NULL);
6041 6189 }
6042 6190
6043 6191 int
6044 6192 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
6045 6193 {
6046 6194 char cmd[PATH_MAX];
6047 6195 int ret;
6048 6196 const char *fcn = "umount_top_dataset()";
6049 6197
6050 6198 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
6051 6199 switch (mnted) {
6052 6200 case LEGACY_ALREADY:
6053 6201 case ZFS_ALREADY:
6054 6202 /* nothing to do */
6055 6203 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6056 6204 "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
6057 6205 free(mntpt);
6058 6206 return (BAM_SUCCESS);
6059 6207 case LEGACY_MOUNTED:
6060 6208 (void) snprintf(cmd, sizeof (cmd),
6061 6209 "/sbin/umount %s", pool);
6062 6210 ret = exec_cmd(cmd, NULL);
6063 6211 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
6064 6212 if (ret != 0) {
6065 6213 bam_error(_("umount of %s failed\n"), pool);
6066 6214 free(mntpt);
6067 6215 return (BAM_ERROR);
6068 6216 }
6069 6217 if (mntpt)
6070 6218 (void) rmdir(mntpt);
6071 6219 free(mntpt);
6072 6220 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6073 6221 "successfully unmounted\n", fcn, pool));
6074 6222 return (BAM_SUCCESS);
6075 6223 case ZFS_MOUNTED:
6076 6224 free(mntpt);
6077 6225 (void) snprintf(cmd, sizeof (cmd),
6078 6226 "/sbin/zfs unmount %s", pool);
6079 6227 ret = exec_cmd(cmd, NULL);
6080 6228 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
6081 6229 if (ret != 0) {
6082 6230 bam_error(_("umount of %s failed\n"), pool);
6083 6231 return (BAM_ERROR);
6084 6232 }
6085 6233 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6086 6234 "successfully unmounted\n", fcn, pool));
6087 6235 return (BAM_SUCCESS);
6088 6236 default:
6089 6237 bam_error(_("Internal error: bad saved mount state for "
6090 6238 "pool %s\n"), pool);
6091 6239 return (BAM_ERROR);
6092 6240 }
6093 6241 /*NOTREACHED*/
6094 6242 }
6095 6243
6096 6244 /*
6097 6245 * For ZFS, osdev can be one of two forms
6098 6246 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6099 6247 * It can be a /dev/[r]dsk special file. We handle both instances
6100 6248 */
6101 6249 static char *
6102 6250 get_pool(char *osdev)
6103 6251 {
6104 6252 char cmd[PATH_MAX];
6105 6253 char buf[PATH_MAX];
6106 6254 filelist_t flist = {0};
6107 6255 char *pool;
6108 6256 char *cp;
6109 6257 char *slash;
6110 6258 int ret;
6111 6259 const char *fcn = "get_pool()";
6112 6260
6113 6261 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
6114 6262 if (osdev == NULL) {
6115 6263 bam_error(_("NULL device: cannot determine pool name\n"));
6116 6264 return (NULL);
6117 6265 }
6118 6266
6119 6267 BAM_DPRINTF(("%s: osdev arg = %s\n", fcn, osdev));
6120 6268
6121 6269 if (osdev[0] != '/') {
6122 6270 (void) strlcpy(buf, osdev, sizeof (buf));
6123 6271 slash = strchr(buf, '/');
6124 6272 if (slash)
6125 6273 *slash = '\0';
6126 6274 pool = s_strdup(buf);
6127 6275 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6128 6276 return (pool);
6129 6277 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6130 6278 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6131 6279 bam_error(_("invalid device %s: cannot determine pool name\n"),
6132 6280 osdev);
6133 6281 return (NULL);
6134 6282 }
6135 6283
6136 6284 /*
6137 6285 * Call the zfs fstyp directly since this is a zpool. This avoids
6138 6286 * potential pcfs conflicts if the first block wasn't cleared.
6139 6287 */
6140 6288 (void) snprintf(cmd, sizeof (cmd),
6141 6289 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6142 6290 osdev);
6143 6291
6144 6292 ret = exec_cmd(cmd, &flist);
6145 6293 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
6146 6294 if (ret != 0) {
6147 6295 bam_error(_("fstyp -a on device %s failed\n"), osdev);
6148 6296 return (NULL);
6149 6297 }
6150 6298
6151 6299 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
6152 6300 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6153 6301 bam_error(_("NULL fstyp -a output for device %s\n"), osdev);
6154 6302 filelist_free(&flist);
6155 6303 return (NULL);
6156 6304 }
6157 6305
6158 6306 (void) strtok(flist.head->line, "'");
6159 6307 cp = strtok(NULL, "'");
6160 6308 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6161 6309 if (cp == NULL) {
6162 6310 bam_error(_("bad fstyp -a output for device %s\n"), osdev);
6163 6311 filelist_free(&flist);
6164 6312 return (NULL);
6165 6313 }
6166 6314
6167 6315 pool = s_strdup(cp);
6168 6316
6169 6317 filelist_free(&flist);
6170 6318
6171 6319 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6172 6320
6173 6321 return (pool);
6174 6322 }
6175 6323
6176 6324 static char *
6177 6325 find_zfs_existing(char *osdev)
6178 6326 {
6179 6327 char *pool;
6180 6328 zfs_mnted_t mnted;
6181 6329 char *mntpt;
6182 6330 char *sign;
6183 6331 const char *fcn = "find_zfs_existing()";
6184 6332
6185 6333 pool = get_pool(osdev);
6186 6334 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6187 6335 if (pool == NULL) {
6188 6336 bam_error(_("failed to get pool for device: %s\n"), osdev);
6189 6337 return (NULL);
6190 6338 }
6191 6339
6192 6340 mntpt = mount_top_dataset(pool, &mnted);
6193 6341 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
6194 6342 if (mntpt == NULL) {
6195 6343 bam_error(_("failed to mount top dataset for pool: %s\n"),
6196 6344 pool);
6197 6345 free(pool);
6198 6346 return (NULL);
6199 6347 }
6200 6348
6201 6349 sign = find_primary_common(mntpt, "zfs");
6202 6350 if (sign == NULL) {
6203 6351 sign = find_backup_common(mntpt, "zfs");
6204 6352 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
6205 6353 sign ? sign : "NULL"));
6206 6354 } else {
6207 6355 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
6208 6356 }
6209 6357
6210 6358 (void) umount_top_dataset(pool, mnted, mntpt);
6211 6359
6212 6360 free(pool);
6213 6361
6214 6362 return (sign);
6215 6363 }
6216 6364
6217 6365 static char *
6218 6366 find_existing_sign(char *osroot, char *osdev, char *fstype)
6219 6367 {
6220 6368 const char *fcn = "find_existing_sign()";
6221 6369
6222 6370 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6223 6371 if (strcmp(fstype, "ufs") == 0) {
6224 6372 BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn));
6225 6373 return (find_ufs_existing(osroot));
6226 6374 } else if (strcmp(fstype, "zfs") == 0) {
6227 6375 BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn));
6228 6376 return (find_zfs_existing(osdev));
6229 6377 } else {
6230 6378 bam_error(_("boot signature not supported for fstype: %s\n"),
6231 6379 fstype);
6232 6380 return (NULL);
6233 6381 }
6234 6382 }
6235 6383
6236 6384 #define MH_HASH_SZ 16
6237 6385
6238 6386 typedef enum {
6239 6387 MH_ERROR = -1,
6240 6388 MH_NOMATCH,
6241 6389 MH_MATCH
6242 6390 } mh_search_t;
6243 6391
6244 6392 typedef struct mcache {
6245 6393 char *mc_special;
6246 6394 char *mc_mntpt;
6247 6395 char *mc_fstype;
6248 6396 struct mcache *mc_next;
6249 6397 } mcache_t;
6250 6398
6251 6399 typedef struct mhash {
6252 6400 mcache_t *mh_hash[MH_HASH_SZ];
6253 6401 } mhash_t;
6254 6402
6255 6403 static int
6256 6404 mhash_fcn(char *key)
6257 6405 {
6258 6406 int i;
6259 6407 uint64_t sum = 0;
6260 6408
6261 6409 for (i = 0; key[i] != '\0'; i++) {
6262 6410 sum += (uchar_t)key[i];
6263 6411 }
6264 6412
6265 6413 sum %= MH_HASH_SZ;
6266 6414
6267 6415 assert(sum < MH_HASH_SZ);
6268 6416
6269 6417 return (sum);
6270 6418 }
6271 6419
6272 6420 static mhash_t *
6273 6421 cache_mnttab(void)
6274 6422 {
6275 6423 FILE *mfp;
6276 6424 struct extmnttab mnt;
6277 6425 mcache_t *mcp;
6278 6426 mhash_t *mhp;
6279 6427 char *ctds;
6280 6428 int idx;
6281 6429 int error;
6282 6430 char *special_dup;
6283 6431 const char *fcn = "cache_mnttab()";
6284 6432
6285 6433 mfp = fopen(MNTTAB, "r");
6286 6434 error = errno;
6287 6435 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6288 6436 if (mfp == NULL) {
6289 6437 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
6290 6438 strerror(error));
6291 6439 return (NULL);
6292 6440 }
6293 6441
6294 6442 mhp = s_calloc(1, sizeof (mhash_t));
6295 6443
6296 6444 resetmnttab(mfp);
6297 6445
6298 6446 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6299 6447 /* only cache ufs */
6300 6448 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6301 6449 continue;
6302 6450
6303 6451 /* basename() modifies its arg, so dup it */
6304 6452 special_dup = s_strdup(mnt.mnt_special);
6305 6453 ctds = basename(special_dup);
6306 6454
6307 6455 mcp = s_calloc(1, sizeof (mcache_t));
6308 6456 mcp->mc_special = s_strdup(ctds);
6309 6457 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6310 6458 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6311 6459 BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6312 6460 "fstype=%s\n", fcn, ctds, mnt.mnt_mountp, mnt.mnt_fstype));
6313 6461 idx = mhash_fcn(ctds);
6314 6462 mcp->mc_next = mhp->mh_hash[idx];
6315 6463 mhp->mh_hash[idx] = mcp;
6316 6464 free(special_dup);
6317 6465 }
6318 6466
6319 6467 (void) fclose(mfp);
6320 6468
6321 6469 return (mhp);
6322 6470 }
6323 6471
6324 6472 static void
6325 6473 free_mnttab(mhash_t *mhp)
6326 6474 {
6327 6475 mcache_t *mcp;
6328 6476 int i;
6329 6477
6330 6478 for (i = 0; i < MH_HASH_SZ; i++) {
6331 6479 while ((mcp = mhp->mh_hash[i]) != NULL) {
6332 6480 mhp->mh_hash[i] = mcp->mc_next;
6333 6481 free(mcp->mc_special);
6334 6482 free(mcp->mc_mntpt);
6335 6483 free(mcp->mc_fstype);
6336 6484 free(mcp);
6337 6485 }
6338 6486 }
6339 6487
6340 6488 for (i = 0; i < MH_HASH_SZ; i++) {
6341 6489 assert(mhp->mh_hash[i] == NULL);
6342 6490 }
6343 6491 free(mhp);
6344 6492 }
6345 6493
6346 6494 static mh_search_t
6347 6495 search_hash(mhash_t *mhp, char *special, char **mntpt)
6348 6496 {
6349 6497 int idx;
6350 6498 mcache_t *mcp;
6351 6499 const char *fcn = "search_hash()";
6352 6500
6353 6501 assert(mntpt);
6354 6502
6355 6503 *mntpt = NULL;
6356 6504
6357 6505 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6358 6506 if (strchr(special, '/')) {
6359 6507 bam_error(_("invalid key for mnttab hash: %s\n"), special);
6360 6508 return (MH_ERROR);
6361 6509 }
6362 6510
6363 6511 idx = mhash_fcn(special);
6364 6512
6365 6513 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6366 6514 if (strcmp(mcp->mc_special, special) == 0)
6367 6515 break;
6368 6516 }
6369 6517
6370 6518 if (mcp == NULL) {
6371 6519 BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn, special));
6372 6520 return (MH_NOMATCH);
6373 6521 }
6374 6522
6375 6523 assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6376 6524 *mntpt = mcp->mc_mntpt;
6377 6525 BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn, special));
6378 6526 return (MH_MATCH);
6379 6527 }
6380 6528
6381 6529 static int
6382 6530 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6383 6531 {
6384 6532 char *sign;
6385 6533 char *signline;
6386 6534 char signbuf[MAXNAMELEN];
6387 6535 int len;
6388 6536 int error;
6389 6537 const char *fcn = "check_add_ufs_sign_to_list()";
6390 6538
6391 6539 /* safe to specify NULL as "osdev" arg for UFS */
6392 6540 sign = find_existing_sign(mntpt, NULL, "ufs");
6393 6541 if (sign == NULL) {
6394 6542 /* No existing signature, nothing to add to list */
6395 6543 BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6396 6544 fcn, mntpt));
6397 6545 return (0);
6398 6546 }
6399 6547
6400 6548 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6401 6549 signline = signbuf;
6402 6550
6403 6551 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6404 6552 if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6405 6553 strlen(GRUBSIGN_UFS_PREFIX))) {
6406 6554 bam_error(_("invalid UFS boot signature %s\n"), sign);
6407 6555 free(sign);
6408 6556 /* ignore invalid signatures */
6409 6557 return (0);
6410 6558 }
6411 6559
6412 6560 len = fputs(signline, tfp);
6413 6561 error = errno;
6414 6562 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6415 6563 if (len != strlen(signline)) {
6416 6564 bam_error(_("failed to write signature %s to signature "
6417 6565 "list: %s\n"), sign, strerror(error));
6418 6566 free(sign);
6419 6567 return (-1);
6420 6568 }
6421 6569
6422 6570 free(sign);
6423 6571
6424 6572 BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6425 6573 fcn, mntpt));
6426 6574 return (0);
6427 6575 }
6428 6576
6429 6577 /*
6430 6578 * slice is a basename not a full pathname
6431 6579 */
6432 6580 static int
6433 6581 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6434 6582 {
6435 6583 int ret;
6436 6584 char cmd[PATH_MAX];
6437 6585 char path[PATH_MAX];
6438 6586 struct stat sbuf;
6439 6587 char *mntpt;
6440 6588 filelist_t flist = {0};
6441 6589 char *fstype;
6442 6590 char blkslice[PATH_MAX];
6443 6591 const char *fcn = "process_slice_common()";
6444 6592
6445 6593
6446 6594 ret = search_hash(mhp, slice, &mntpt);
6447 6595 switch (ret) {
6448 6596 case MH_MATCH:
6449 6597 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6450 6598 return (-1);
6451 6599 else
6452 6600 return (0);
6453 6601 case MH_NOMATCH:
6454 6602 break;
6455 6603 case MH_ERROR:
6456 6604 default:
6457 6605 return (-1);
6458 6606 }
6459 6607
6460 6608 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6461 6609 if (stat(path, &sbuf) == -1) {
6462 6610 BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn, path));
6463 6611 return (0);
6464 6612 }
6465 6613
6466 6614 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6467 6615 (void) snprintf(cmd, sizeof (cmd),
6468 6616 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6469 6617 slice);
6470 6618
6471 6619 if (exec_cmd(cmd, &flist) != 0) {
6472 6620 if (bam_verbose)
6473 6621 bam_print(_("fstyp failed for slice: %s\n"), slice);
6474 6622 return (0);
6475 6623 }
6476 6624
6477 6625 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6478 6626 if (bam_verbose)
6479 6627 bam_print(_("bad output from fstyp for slice: %s\n"),
6480 6628 slice);
6481 6629 filelist_free(&flist);
6482 6630 return (0);
6483 6631 }
6484 6632
6485 6633 fstype = strtok(flist.head->line, " \t\n");
6486 6634 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6487 6635 if (bam_verbose)
6488 6636 bam_print(_("%s is not a ufs slice: %s\n"),
6489 6637 slice, fstype);
6490 6638 filelist_free(&flist);
6491 6639 return (0);
6492 6640 }
6493 6641
6494 6642 filelist_free(&flist);
6495 6643
6496 6644 /*
6497 6645 * Since we are mounting the filesystem read-only, the
6498 6646 * the last mount field of the superblock is unchanged
6499 6647 * and does not need to be fixed up post-mount;
6500 6648 */
6501 6649
6502 6650 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6503 6651 slice);
6504 6652
6505 6653 (void) snprintf(cmd, sizeof (cmd),
6506 6654 "/usr/sbin/mount -F ufs -o ro %s %s "
6507 6655 "> /dev/null 2>&1", blkslice, tmpmnt);
6508 6656
6509 6657 if (exec_cmd(cmd, NULL) != 0) {
6510 6658 if (bam_verbose)
6511 6659 bam_print(_("mount of %s (fstype %s) failed\n"),
6512 6660 blkslice, "ufs");
6513 6661 return (0);
6514 6662 }
6515 6663
6516 6664 ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6517 6665
6518 6666 (void) snprintf(cmd, sizeof (cmd),
6519 6667 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6520 6668 tmpmnt);
6521 6669
6522 6670 if (exec_cmd(cmd, NULL) != 0) {
6523 6671 bam_print(_("umount of %s failed\n"), slice);
6524 6672 return (0);
6525 6673 }
6526 6674
6527 6675 return (ret);
6528 6676 }
6529 6677
6530 6678 static int
6531 6679 process_vtoc_slices(
6532 6680 char *s0,
6533 6681 struct vtoc *vtoc,
6534 6682 FILE *tfp,
6535 6683 mhash_t *mhp,
6536 6684 char *tmpmnt)
6537 6685 {
6538 6686 int idx;
6539 6687 char slice[PATH_MAX];
6540 6688 size_t len;
6541 6689 char *cp;
6542 6690 const char *fcn = "process_vtoc_slices()";
6543 6691
6544 6692 len = strlen(s0);
6545 6693
6546 6694 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6547 6695
6548 6696 s0[len - 1] = '\0';
6549 6697
6550 6698 (void) strlcpy(slice, s0, sizeof (slice));
6551 6699
6552 6700 s0[len - 1] = '0';
6553 6701
6554 6702 cp = slice + len - 1;
6555 6703
6556 6704 for (idx = 0; idx < vtoc->v_nparts; idx++) {
6557 6705
6558 6706 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6559 6707
6560 6708 if (vtoc->v_part[idx].p_size == 0) {
6561 6709 BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6562 6710 fcn, slice));
6563 6711 continue;
6564 6712 }
6565 6713
6566 6714 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6567 6715 switch (vtoc->v_part[idx].p_tag) {
6568 6716 case V_SWAP:
6569 6717 case V_USR:
6570 6718 case V_BACKUP:
6571 6719 case V_VAR:
6572 6720 case V_HOME:
6573 6721 case V_ALTSCTR:
6574 6722 BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6575 6723 "skipping: %s\n", fcn, slice));
6576 6724 continue;
6577 6725 default:
6578 6726 BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6579 6727 fcn, slice));
6580 6728 break;
6581 6729 }
6582 6730
6583 6731 /* skip unmountable and readonly slices */
6584 6732 switch (vtoc->v_part[idx].p_flag) {
6585 6733 case V_UNMNT:
6586 6734 case V_RONLY:
6587 6735 BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6588 6736 fcn, slice));
6589 6737 continue;
6590 6738 default:
6591 6739 BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6592 6740 fcn, slice));
6593 6741 break;
6594 6742 }
6595 6743
6596 6744 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6597 6745 return (-1);
6598 6746 }
6599 6747 }
6600 6748
6601 6749 return (0);
6602 6750 }
6603 6751
6604 6752 static int
6605 6753 process_efi_slices(
6606 6754 char *s0,
6607 6755 struct dk_gpt *efi,
6608 6756 FILE *tfp,
6609 6757 mhash_t *mhp,
6610 6758 char *tmpmnt)
6611 6759 {
6612 6760 int idx;
6613 6761 char slice[PATH_MAX];
6614 6762 size_t len;
6615 6763 char *cp;
6616 6764 const char *fcn = "process_efi_slices()";
6617 6765
6618 6766 len = strlen(s0);
6619 6767
6620 6768 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6621 6769
6622 6770 s0[len - 1] = '\0';
6623 6771
6624 6772 (void) strlcpy(slice, s0, sizeof (slice));
6625 6773
6626 6774 s0[len - 1] = '0';
6627 6775
6628 6776 cp = slice + len - 1;
6629 6777
6630 6778 for (idx = 0; idx < efi->efi_nparts; idx++) {
6631 6779
6632 6780 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6633 6781
6634 6782 if (efi->efi_parts[idx].p_size == 0) {
6635 6783 BAM_DPRINTF(("%s: EFI: skipping 0-length slice: %s\n",
6636 6784 fcn, slice));
6637 6785 continue;
6638 6786 }
6639 6787
6640 6788 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6641 6789 switch (efi->efi_parts[idx].p_tag) {
6642 6790 case V_SWAP:
6643 6791 case V_USR:
6644 6792 case V_BACKUP:
6645 6793 case V_VAR:
6646 6794 case V_HOME:
6647 6795 case V_ALTSCTR:
6648 6796 BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6649 6797 fcn, slice));
6650 6798 continue;
6651 6799 default:
6652 6800 BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6653 6801 fcn, slice));
6654 6802 break;
6655 6803 }
6656 6804
6657 6805 /* skip unmountable and readonly slices */
6658 6806 switch (efi->efi_parts[idx].p_flag) {
6659 6807 case V_UNMNT:
6660 6808 case V_RONLY:
6661 6809 BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6662 6810 fcn, slice));
6663 6811 continue;
6664 6812 default:
6665 6813 BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6666 6814 fcn, slice));
6667 6815 break;
6668 6816 }
6669 6817
6670 6818 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6671 6819 return (-1);
6672 6820 }
6673 6821 }
6674 6822
6675 6823 return (0);
6676 6824 }
6677 6825
6678 6826 /*
6679 6827 * s0 is a basename not a full path
6680 6828 */
6681 6829 static int
6682 6830 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6683 6831 {
6684 6832 struct vtoc vtoc;
6685 6833 struct dk_gpt *efi;
6686 6834 char s0path[PATH_MAX];
6687 6835 struct stat sbuf;
6688 6836 int e_flag;
6689 6837 int v_flag;
6690 6838 int retval;
6691 6839 int err;
6692 6840 int fd;
6693 6841 const char *fcn = "process_slice0()";
6694 6842
6695 6843 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6696 6844
6697 6845 if (stat(s0path, &sbuf) == -1) {
6698 6846 BAM_DPRINTF(("%s: slice 0 does not exist: %s\n", fcn, s0path));
6699 6847 return (0);
6700 6848 }
6701 6849
6702 6850 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6703 6851 if (fd == -1) {
6704 6852 bam_error(_("failed to open file: %s: %s\n"), s0path,
6705 6853 strerror(errno));
6706 6854 return (0);
6707 6855 }
6708 6856
6709 6857 e_flag = v_flag = 0;
6710 6858 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6711 6859 switch (retval) {
6712 6860 case VT_EIO:
6713 6861 BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6714 6862 fcn, s0path));
6715 6863 break;
6716 6864 case VT_EINVAL:
6717 6865 BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6718 6866 fcn, s0path));
6719 6867 break;
6720 6868 case VT_ERROR:
6721 6869 BAM_DPRINTF(("%s: VTOC: unknown error while "
6722 6870 "reading: %s\n", fcn, s0path));
6723 6871 break;
6724 6872 case VT_ENOTSUP:
6725 6873 e_flag = 1;
6726 6874 BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6727 6875 fcn, s0path));
6728 6876 break;
6729 6877 case 0:
6730 6878 v_flag = 1;
6731 6879 BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6732 6880 fcn, s0path));
6733 6881 break;
6734 6882 default:
6735 6883 BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6736 6884 "code: %s\n", fcn, s0path));
6737 6885 break;
6738 6886 }
6739 6887
6740 6888
6741 6889 if (e_flag) {
6742 6890 e_flag = 0;
6743 6891 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6744 6892 switch (retval) {
6745 6893 case VT_EIO:
6746 6894 BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6747 6895 fcn, s0path));
6748 6896 break;
6749 6897 case VT_EINVAL:
6750 6898 BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn, s0path));
6751 6899 break;
6752 6900 case VT_ERROR:
6753 6901 BAM_DPRINTF(("%s: EFI: unknown error while "
6754 6902 "reading: %s\n", fcn, s0path));
6755 6903 break;
6756 6904 case VT_ENOTSUP:
6757 6905 BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6758 6906 fcn, s0path));
6759 6907 break;
6760 6908 case 0:
6761 6909 e_flag = 1;
6762 6910 BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6763 6911 fcn, s0path));
6764 6912 break;
6765 6913 default:
6766 6914 BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6767 6915 fcn, s0path));
6768 6916 break;
6769 6917 }
6770 6918 }
6771 6919
6772 6920 (void) close(fd);
6773 6921
6774 6922 if (v_flag) {
6775 6923 retval = process_vtoc_slices(s0,
6776 6924 &vtoc, tfp, mhp, tmpmnt);
6777 6925 } else if (e_flag) {
6778 6926 retval = process_efi_slices(s0,
6779 6927 efi, tfp, mhp, tmpmnt);
6780 6928 } else {
6781 6929 BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6782 6930 fcn, s0path));
6783 6931 return (0);
6784 6932 }
6785 6933
6786 6934 return (retval);
6787 6935 }
6788 6936
6789 6937 /*
6790 6938 * Find and create a list of all existing UFS boot signatures
6791 6939 */
6792 6940 static int
6793 6941 FindAllUfsSignatures(void)
6794 6942 {
6795 6943 mhash_t *mnttab_hash;
6796 6944 DIR *dirp = NULL;
6797 6945 struct dirent *dp;
6798 6946 char tmpmnt[PATH_MAX];
6799 6947 char cmd[PATH_MAX];
6800 6948 struct stat sb;
6801 6949 int fd;
6802 6950 FILE *tfp;
6803 6951 size_t len;
6804 6952 int ret;
6805 6953 int error;
6806 6954 const char *fcn = "FindAllUfsSignatures()";
6807 6955
6808 6956 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) {
6809 6957 bam_print(_(" - signature list %s exists\n"),
6810 6958 UFS_SIGNATURE_LIST);
6811 6959 return (0);
6812 6960 }
6813 6961
6814 6962 fd = open(UFS_SIGNATURE_LIST".tmp",
6815 6963 O_RDWR|O_CREAT|O_TRUNC, 0644);
6816 6964 error = errno;
6817 6965 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6818 6966 if (fd == -1) {
6819 6967 bam_error(_("failed to open file: %s: %s\n"),
6820 6968 UFS_SIGNATURE_LIST".tmp", strerror(error));
6821 6969 return (-1);
6822 6970 }
6823 6971
6824 6972 ret = close(fd);
6825 6973 error = errno;
6826 6974 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6827 6975 if (ret == -1) {
6828 6976 bam_error(_("failed to close file: %s: %s\n"),
6829 6977 UFS_SIGNATURE_LIST".tmp", strerror(error));
6830 6978 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6831 6979 return (-1);
6832 6980 }
6833 6981
6834 6982 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6835 6983 error = errno;
6836 6984 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6837 6985 if (tfp == NULL) {
6838 6986 bam_error(_("failed to open file: %s: %s\n"),
6839 6987 UFS_SIGNATURE_LIST".tmp", strerror(error));
6840 6988 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6841 6989 return (-1);
6842 6990 }
6843 6991
6844 6992 mnttab_hash = cache_mnttab();
6845 6993 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6846 6994 if (mnttab_hash == NULL) {
6847 6995 (void) fclose(tfp);
6848 6996 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6849 6997 bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn);
6850 6998 return (-1);
6851 6999 }
6852 7000
6853 7001 (void) snprintf(tmpmnt, sizeof (tmpmnt),
6854 7002 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6855 7003 (void) unlink(tmpmnt);
6856 7004
6857 7005 ret = mkdirp(tmpmnt, DIR_PERMS);
6858 7006 error = errno;
6859 7007 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6860 7008 if (ret == -1) {
6861 7009 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
6862 7010 strerror(error));
6863 7011 free_mnttab(mnttab_hash);
6864 7012 (void) fclose(tfp);
6865 7013 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6866 7014 return (-1);
6867 7015 }
6868 7016
6869 7017 dirp = opendir("/dev/rdsk");
6870 7018 error = errno;
6871 7019 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6872 7020 if (dirp == NULL) {
6873 7021 bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
6874 7022 strerror(error));
6875 7023 goto fail;
6876 7024 }
6877 7025
6878 7026 while ((dp = readdir(dirp)) != NULL) {
6879 7027 if (strcmp(dp->d_name, ".") == 0 ||
6880 7028 strcmp(dp->d_name, "..") == 0)
6881 7029 continue;
6882 7030
6883 7031 /*
6884 7032 * we only look for the s0 slice. This is guranteed to
6885 7033 * have 's' at len - 2.
6886 7034 */
6887 7035 len = strlen(dp->d_name);
6888 7036 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6889 7037 BAM_DPRINTF(("%s: skipping non-s0 slice: %s\n",
6890 7038 fcn, dp->d_name));
6891 7039 continue;
6892 7040 }
6893 7041
6894 7042 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6895 7043 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6896 7044 if (ret == -1)
6897 7045 goto fail;
6898 7046 }
6899 7047
6900 7048 (void) closedir(dirp);
6901 7049 free_mnttab(mnttab_hash);
6902 7050 (void) rmdir(tmpmnt);
6903 7051
6904 7052 ret = fclose(tfp);
6905 7053 error = errno;
6906 7054 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6907 7055 if (ret == EOF) {
6908 7056 bam_error(_("failed to close file: %s: %s\n"),
6909 7057 UFS_SIGNATURE_LIST".tmp", strerror(error));
6910 7058 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6911 7059 return (-1);
6912 7060 }
6913 7061
6914 7062 /* We have a list of existing GRUB signatures. Sort it first */
6915 7063 (void) snprintf(cmd, sizeof (cmd),
6916 7064 "/usr/bin/sort -u %s.tmp > %s.sorted",
6917 7065 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6918 7066
6919 7067 ret = exec_cmd(cmd, NULL);
6920 7068 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6921 7069 if (ret != 0) {
6922 7070 bam_error(_("error sorting GRUB UFS boot signatures\n"));
6923 7071 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6924 7072 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6925 7073 return (-1);
6926 7074 }
6927 7075
6928 7076 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6929 7077
6930 7078 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6931 7079 error = errno;
6932 7080 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6933 7081 if (ret == -1) {
6934 7082 bam_error(_("rename to file failed: %s: %s\n"),
6935 7083 UFS_SIGNATURE_LIST, strerror(error));
6936 7084 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6937 7085 return (-1);
6938 7086 }
6939 7087
6940 7088 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6941 7089 BAM_DPRINTF(("%s: generated zero length signlist: %s.\n",
6942 7090 fcn, UFS_SIGNATURE_LIST));
6943 7091 }
6944 7092
6945 7093 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
6946 7094 return (0);
6947 7095
6948 7096 fail:
6949 7097 if (dirp)
6950 7098 (void) closedir(dirp);
6951 7099 free_mnttab(mnttab_hash);
6952 7100 (void) rmdir(tmpmnt);
6953 7101 (void) fclose(tfp);
6954 7102 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6955 7103 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6956 7104 return (-1);
6957 7105 }
6958 7106
6959 7107 static char *
6960 7108 create_ufs_sign(void)
6961 7109 {
6962 7110 struct stat sb;
6963 7111 int signnum = -1;
6964 7112 char tmpsign[MAXNAMELEN + 1];
6965 7113 char *numstr;
6966 7114 int i;
6967 7115 FILE *tfp;
6968 7116 int ret;
6969 7117 int error;
6970 7118 const char *fcn = "create_ufs_sign()";
6971 7119
6972 7120 bam_print(_(" - searching for UFS boot signatures\n"));
6973 7121
6974 7122 ret = FindAllUfsSignatures();
6975 7123 INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6976 7124 if (ret == -1) {
6977 7125 bam_error(_("search for UFS boot signatures failed\n"));
6978 7126 return (NULL);
6979 7127 }
6980 7128
6981 7129 /* Make sure the list exists and is owned by root */
6982 7130 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6983 7131 (void) unlink(UFS_SIGNATURE_LIST));
6984 7132 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6985 7133 (void) unlink(UFS_SIGNATURE_LIST);
6986 7134 bam_error(_("missing UFS signature list file: %s\n"),
6987 7135 UFS_SIGNATURE_LIST);
6988 7136 return (NULL);
6989 7137 }
6990 7138
6991 7139 if (sb.st_size == 0) {
6992 7140 bam_print(_(" - no existing UFS boot signatures\n"));
6993 7141 i = 0;
6994 7142 goto found;
6995 7143 }
6996 7144
6997 7145 /* The signature list was sorted when it was created */
6998 7146 tfp = fopen(UFS_SIGNATURE_LIST, "r");
6999 7147 error = errno;
7000 7148 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
7001 7149 if (tfp == NULL) {
7002 7150 bam_error(_("error opening UFS boot signature list "
7003 7151 "file %s: %s\n"), UFS_SIGNATURE_LIST, strerror(error));
7004 7152 (void) unlink(UFS_SIGNATURE_LIST);
7005 7153 return (NULL);
7006 7154 }
7007 7155
7008 7156 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
7009 7157
7010 7158 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
7011 7159 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7012 7160 (void) fclose(tfp);
7013 7161 (void) unlink(UFS_SIGNATURE_LIST);
7014 7162 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7015 7163 return (NULL);
7016 7164 }
7017 7165 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
7018 7166
7019 7167 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
7020 7168 (void) fclose(tfp);
7021 7169 (void) unlink(UFS_SIGNATURE_LIST);
7022 7170 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7023 7171 return (NULL);
7024 7172 }
7025 7173
7026 7174 signnum = atoi(numstr);
7027 7175 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
7028 7176 if (signnum < 0) {
7029 7177 (void) fclose(tfp);
7030 7178 (void) unlink(UFS_SIGNATURE_LIST);
7031 7179 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7032 7180 return (NULL);
7033 7181 }
7034 7182
7035 7183 if (i != signnum) {
7036 7184 BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7037 7185 fcn, i));
7038 7186 break;
7039 7187 }
7040 7188 }
7041 7189
7042 7190 (void) fclose(tfp);
7043 7191
7044 7192 found:
7045 7193 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
7046 7194
7047 7195 /* add the ufs signature to the /var/run list of signatures */
7048 7196 ret = ufs_add_to_sign_list(tmpsign);
7049 7197 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
7050 7198 if (ret == -1) {
7051 7199 (void) unlink(UFS_SIGNATURE_LIST);
7052 7200 bam_error(_("failed to add sign %s to signlist.\n"), tmpsign);
7053 7201 return (NULL);
7054 7202 }
7055 7203
7056 7204 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7057 7205
7058 7206 return (s_strdup(tmpsign));
7059 7207 }
7060 7208
7061 7209 static char *
7062 7210 get_fstype(char *osroot)
7063 7211 {
7064 7212 FILE *mntfp;
7065 7213 struct mnttab mp = {0};
7066 7214 struct mnttab mpref = {0};
7067 7215 int error;
7068 7216 int ret;
7069 7217 const char *fcn = "get_fstype()";
7070 7218
7071 7219 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
7072 7220 if (osroot == NULL) {
7073 7221 bam_error(_("no OS mountpoint. Cannot determine fstype\n"));
7074 7222 return (NULL);
7075 7223 }
7076 7224
7077 7225 mntfp = fopen(MNTTAB, "r");
7078 7226 error = errno;
7079 7227 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
7080 7228 if (mntfp == NULL) {
7081 7229 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7082 7230 strerror(error));
7083 7231 return (NULL);
7084 7232 }
7085 7233
7086 7234 if (*osroot == '\0')
7087 7235 mpref.mnt_mountp = "/";
7088 7236 else
7089 7237 mpref.mnt_mountp = osroot;
7090 7238
7091 7239 ret = getmntany(mntfp, &mp, &mpref);
7092 7240 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
7093 7241 if (ret != 0) {
7094 7242 bam_error(_("failed to find OS mountpoint %s in %s\n"),
7095 7243 osroot, MNTTAB);
7096 7244 (void) fclose(mntfp);
7097 7245 return (NULL);
7098 7246 }
7099 7247 (void) fclose(mntfp);
7100 7248
7101 7249 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
7102 7250 if (mp.mnt_fstype == NULL) {
7103 7251 bam_error(_("NULL fstype found for OS root %s\n"), osroot);
7104 7252 return (NULL);
7105 7253 }
7106 7254
7107 7255 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7108 7256
7109 7257 return (s_strdup(mp.mnt_fstype));
7110 7258 }
7111 7259
7112 7260 static char *
7113 7261 create_zfs_sign(char *osdev)
7114 7262 {
7115 7263 char tmpsign[PATH_MAX];
7116 7264 char *pool;
7117 7265 const char *fcn = "create_zfs_sign()";
7118 7266
7119 7267 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, osdev));
7120 7268
7121 7269 /*
7122 7270 * First find the pool name
7123 7271 */
7124 7272 pool = get_pool(osdev);
7125 7273 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
7126 7274 if (pool == NULL) {
7127 7275 bam_error(_("failed to get pool name from %s\n"), osdev);
7128 7276 return (NULL);
7129 7277 }
7130 7278
7131 7279 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
7132 7280
7133 7281 BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn, tmpsign));
7134 7282
7135 7283 free(pool);
7136 7284
7137 7285 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7138 7286
7139 7287 return (s_strdup(tmpsign));
7140 7288 }
7141 7289
7142 7290 static char *
7143 7291 create_new_sign(char *osdev, char *fstype)
7144 7292 {
7145 7293 char *sign;
7146 7294 const char *fcn = "create_new_sign()";
7147 7295
7148 7296 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
7149 7297
7150 7298 if (strcmp(fstype, "zfs") == 0) {
7151 7299 BAM_DPRINTF(("%s: created new ZFS sign\n", fcn));
7152 7300 sign = create_zfs_sign(osdev);
7153 7301 } else if (strcmp(fstype, "ufs") == 0) {
7154 7302 BAM_DPRINTF(("%s: created new UFS sign\n", fcn));
7155 7303 sign = create_ufs_sign();
7156 7304 } else {
7157 7305 bam_error(_("boot signature not supported for fstype: %s\n"),
7158 7306 fstype);
7159 7307 sign = NULL;
7160 7308 }
7161 7309
7162 7310 BAM_DPRINTF(("%s: created new sign: %s\n", fcn,
7163 7311 sign ? sign : "<NULL>"));
7164 7312 return (sign);
7165 7313 }
7166 7314
7167 7315 static int
7168 7316 set_backup_common(char *mntpt, char *sign)
7169 7317 {
7170 7318 FILE *bfp;
7171 7319 char backup[PATH_MAX];
7172 7320 char tmpsign[PATH_MAX];
7173 7321 int error;
7174 7322 char *bdir;
7175 7323 char *backup_dup;
7176 7324 struct stat sb;
7177 7325 int ret;
7178 7326 const char *fcn = "set_backup_common()";
7179 7327
7180 7328 (void) snprintf(backup, sizeof (backup), "%s%s",
7181 7329 mntpt, GRUBSIGN_BACKUP);
7182 7330
7183 7331 /* First read the backup */
7184 7332 bfp = fopen(backup, "r");
7185 7333 if (bfp != NULL) {
7186 7334 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
7187 7335 if (strcmp(tmpsign, sign) == 0) {
7188 7336 BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7189 7337 fcn, sign));
7190 7338 (void) fclose(bfp);
7191 7339 return (0);
7192 7340 }
7193 7341 }
7194 7342 (void) fclose(bfp);
7195 7343 BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7196 7344 fcn, sign));
7197 7345 } else {
7198 7346 BAM_DPRINTF(("%s: no backup file (%s) found.\n", fcn, backup));
7199 7347 }
7200 7348
7201 7349 /*
7202 7350 * Didn't find the correct signature. First create
7203 7351 * the directory if necessary.
7204 7352 */
7205 7353
7206 7354 /* dirname() modifies its argument so dup it */
7207 7355 backup_dup = s_strdup(backup);
7208 7356 bdir = dirname(backup_dup);
7209 7357 assert(bdir);
7210 7358
7211 7359 ret = stat(bdir, &sb);
7212 7360 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
7213 7361 if (ret == -1) {
7214 7362 BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7215 7363 fcn, bdir));
7216 7364 ret = mkdirp(bdir, DIR_PERMS);
7217 7365 error = errno;
7218 7366 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
7219 7367 if (ret == -1) {
7220 7368 bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7221 7369 GRUBSIGN_BACKUP, strerror(error));
7222 7370 free(backup_dup);
7223 7371 return (-1);
7224 7372 }
7225 7373 }
7226 7374 free(backup_dup);
7227 7375
7228 7376 /*
7229 7377 * Open the backup in append mode to add the correct
7230 7378 * signature;
7231 7379 */
7232 7380 bfp = fopen(backup, "a");
7233 7381 error = errno;
7234 7382 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
7235 7383 if (bfp == NULL) {
7236 7384 bam_error(_("error opening boot signature backup "
7237 7385 "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7238 7386 return (-1);
7239 7387 }
7240 7388
7241 7389 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
7242 7390
7243 7391 ret = fputs(tmpsign, bfp);
7244 7392 error = errno;
7245 7393 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
7246 7394 if (ret != strlen(tmpsign)) {
7247 7395 bam_error(_("error writing boot signature backup "
7248 7396 "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7249 7397 (void) fclose(bfp);
7250 7398 return (-1);
7251 7399 }
7252 7400
7253 7401 (void) fclose(bfp);
7254 7402
7255 7403 if (bam_verbose)
7256 7404 bam_print(_("updated boot signature backup file %s\n"),
7257 7405 GRUBSIGN_BACKUP);
7258 7406
7259 7407 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7260 7408
7261 7409 return (0);
7262 7410 }
7263 7411
7264 7412 static int
7265 7413 set_backup_ufs(char *osroot, char *sign)
7266 7414 {
7267 7415 const char *fcn = "set_backup_ufs()";
7268 7416
7269 7417 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7270 7418 return (set_backup_common(osroot, sign));
7271 7419 }
7272 7420
7273 7421 static int
7274 7422 set_backup_zfs(char *osdev, char *sign)
7275 7423 {
7276 7424 char *pool;
7277 7425 char *mntpt;
7278 7426 zfs_mnted_t mnted;
7279 7427 int ret;
7280 7428 const char *fcn = "set_backup_zfs()";
7281 7429
7282 7430 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7283 7431
7284 7432 pool = get_pool(osdev);
7285 7433 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7286 7434 if (pool == NULL) {
7287 7435 bam_error(_("failed to get pool name from %s\n"), osdev);
7288 7436 return (-1);
7289 7437 }
7290 7438
7291 7439 mntpt = mount_top_dataset(pool, &mnted);
7292 7440 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7293 7441 if (mntpt == NULL) {
7294 7442 bam_error(_("failed to mount top dataset for %s\n"), pool);
7295 7443 free(pool);
7296 7444 return (-1);
7297 7445 }
7298 7446
7299 7447 ret = set_backup_common(mntpt, sign);
7300 7448
7301 7449 (void) umount_top_dataset(pool, mnted, mntpt);
7302 7450
7303 7451 free(pool);
7304 7452
7305 7453 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7306 7454 if (ret == 0) {
7307 7455 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7308 7456 } else {
7309 7457 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7310 7458 }
7311 7459
7312 7460 return (ret);
7313 7461 }
7314 7462
7315 7463 static int
7316 7464 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7317 7465 {
7318 7466 const char *fcn = "set_backup()";
7319 7467 int ret;
7320 7468
7321 7469 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7322 7470
7323 7471 if (strcmp(fstype, "ufs") == 0) {
7324 7472 BAM_DPRINTF(("%s: setting UFS backup sign\n", fcn));
7325 7473 ret = set_backup_ufs(osroot, sign);
7326 7474 } else if (strcmp(fstype, "zfs") == 0) {
7327 7475 BAM_DPRINTF(("%s: setting ZFS backup sign\n", fcn));
7328 7476 ret = set_backup_zfs(osdev, sign);
7329 7477 } else {
7330 7478 bam_error(_("boot signature not supported for fstype: %s\n"),
7331 7479 fstype);
7332 7480 ret = -1;
7333 7481 }
7334 7482
7335 7483 if (ret == 0) {
7336 7484 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7337 7485 } else {
7338 7486 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7339 7487 }
7340 7488
7341 7489 return (ret);
7342 7490 }
7343 7491
7344 7492 static int
7345 7493 set_primary_common(char *mntpt, char *sign)
7346 7494 {
7347 7495 char signfile[PATH_MAX];
7348 7496 char signdir[PATH_MAX];
7349 7497 struct stat sb;
7350 7498 int fd;
7351 7499 int error;
7352 7500 int ret;
7353 7501 const char *fcn = "set_primary_common()";
7354 7502
7355 7503 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7356 7504 mntpt, GRUBSIGN_DIR, sign);
7357 7505
7358 7506 if (stat(signfile, &sb) != -1) {
7359 7507 if (bam_verbose)
7360 7508 bam_print(_("primary sign %s exists\n"), sign);
7361 7509 return (0);
7362 7510 } else {
7363 7511 BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7364 7512 fcn, signfile));
7365 7513 }
7366 7514
7367 7515 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
7368 7516 mntpt, GRUBSIGN_DIR);
7369 7517
7370 7518 if (stat(signdir, &sb) == -1) {
7371 7519 BAM_DPRINTF(("%s: primary signdir (%s) does not exist\n",
7372 7520 fcn, signdir));
7373 7521 ret = mkdirp(signdir, DIR_PERMS);
7374 7522 error = errno;
7375 7523 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7376 7524 if (ret == -1) {
7377 7525 bam_error(_("error creating boot signature "
7378 7526 "directory %s: %s\n"), signdir, strerror(errno));
7379 7527 return (-1);
7380 7528 }
7381 7529 }
7382 7530
7383 7531 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7384 7532 error = errno;
7385 7533 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7386 7534 if (fd == -1) {
7387 7535 bam_error(_("error creating primary boot signature %s: %s\n"),
7388 7536 signfile, strerror(error));
7389 7537 return (-1);
7390 7538 }
7391 7539
7392 7540 ret = fsync(fd);
7393 7541 error = errno;
7394 7542 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7395 7543 if (ret != 0) {
7396 7544 bam_error(_("error syncing primary boot signature %s: %s\n"),
7397 7545 signfile, strerror(error));
7398 7546 }
7399 7547
7400 7548 (void) close(fd);
7401 7549
7402 7550 if (bam_verbose)
7403 7551 bam_print(_("created primary GRUB boot signature: %s\n"),
7404 7552 signfile);
7405 7553
7406 7554 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7407 7555
7408 7556 return (0);
7409 7557 }
7410 7558
7411 7559 static int
7412 7560 set_primary_ufs(char *osroot, char *sign)
7413 7561 {
7414 7562 const char *fcn = "set_primary_ufs()";
7415 7563
7416 7564 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7417 7565 return (set_primary_common(osroot, sign));
7418 7566 }
7419 7567
7420 7568 static int
7421 7569 set_primary_zfs(char *osdev, char *sign)
7422 7570 {
7423 7571 char *pool;
7424 7572 char *mntpt;
7425 7573 zfs_mnted_t mnted;
7426 7574 int ret;
7427 7575 const char *fcn = "set_primary_zfs()";
7428 7576
7429 7577 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7430 7578
7431 7579 pool = get_pool(osdev);
7432 7580 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7433 7581 if (pool == NULL) {
7434 7582 bam_error(_("failed to get pool name from %s\n"), osdev);
7435 7583 return (-1);
7436 7584 }
7437 7585
7438 7586 /* Pool name must exist in the sign */
7439 7587 ret = (strstr(sign, pool) != NULL);
7440 7588 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7441 7589 if (ret == 0) {
7442 7590 bam_error(_("pool name %s not present in signature %s\n"),
7443 7591 pool, sign);
7444 7592 free(pool);
7445 7593 return (-1);
7446 7594 }
7447 7595
7448 7596 mntpt = mount_top_dataset(pool, &mnted);
7449 7597 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7450 7598 if (mntpt == NULL) {
7451 7599 bam_error(_("failed to mount top dataset for %s\n"), pool);
7452 7600 free(pool);
7453 7601 return (-1);
7454 7602 }
7455 7603
7456 7604 ret = set_primary_common(mntpt, sign);
7457 7605
7458 7606 (void) umount_top_dataset(pool, mnted, mntpt);
7459 7607
7460 7608 free(pool);
7461 7609
7462 7610 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7463 7611 if (ret == 0) {
7464 7612 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7465 7613 } else {
7466 7614 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7467 7615 }
7468 7616
7469 7617 return (ret);
7470 7618 }
7471 7619
7472 7620 static int
7473 7621 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7474 7622 {
7475 7623 const char *fcn = "set_primary()";
7476 7624 int ret;
7477 7625
7478 7626 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7479 7627 if (strcmp(fstype, "ufs") == 0) {
7480 7628 BAM_DPRINTF(("%s: setting UFS primary sign\n", fcn));
7481 7629 ret = set_primary_ufs(osroot, sign);
7482 7630 } else if (strcmp(fstype, "zfs") == 0) {
7483 7631 BAM_DPRINTF(("%s: setting ZFS primary sign\n", fcn));
7484 7632 ret = set_primary_zfs(osdev, sign);
7485 7633 } else {
7486 7634 bam_error(_("boot signature not supported for fstype: %s\n"),
7487 7635 fstype);
7488 7636 ret = -1;
7489 7637 }
7490 7638
7491 7639 if (ret == 0) {
7492 7640 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7493 7641 } else {
7494 7642 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7495 7643 }
7496 7644
7497 7645 return (ret);
7498 7646 }
7499 7647
7500 7648 static int
7501 7649 ufs_add_to_sign_list(char *sign)
7502 7650 {
7503 7651 FILE *tfp;
7504 7652 char signline[MAXNAMELEN];
7505 7653 char cmd[PATH_MAX];
7506 7654 int ret;
7507 7655 int error;
7508 7656 const char *fcn = "ufs_add_to_sign_list()";
7509 7657
7510 7658 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7511 7659 if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7512 7660 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7513 7661 bam_error(_("invalid UFS boot signature %s\n"), sign);
7514 7662 (void) unlink(UFS_SIGNATURE_LIST);
7515 7663 return (-1);
7516 7664 }
7517 7665
7518 7666 /*
7519 7667 * most failures in this routine are not a fatal error
7520 7668 * We simply unlink the /var/run file and continue
7521 7669 */
7522 7670
7523 7671 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7524 7672 error = errno;
7525 7673 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7526 7674 if (ret == -1) {
7527 7675 bam_error(_("rename to file failed: %s: %s\n"),
7528 7676 UFS_SIGNATURE_LIST".tmp", strerror(error));
7529 7677 (void) unlink(UFS_SIGNATURE_LIST);
7530 7678 return (0);
7531 7679 }
7532 7680
7533 7681 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7534 7682 error = errno;
7535 7683 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7536 7684 if (tfp == NULL) {
7537 7685 bam_error(_("failed to open file: %s: %s\n"),
7538 7686 UFS_SIGNATURE_LIST".tmp", strerror(error));
7539 7687 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7540 7688 return (0);
7541 7689 }
7542 7690
7543 7691 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7544 7692
7545 7693 ret = fputs(signline, tfp);
7546 7694 error = errno;
7547 7695 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7548 7696 if (ret != strlen(signline)) {
7549 7697 bam_error(_("failed to write signature %s to signature "
7550 7698 "list: %s\n"), sign, strerror(error));
7551 7699 (void) fclose(tfp);
7552 7700 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7553 7701 return (0);
7554 7702 }
7555 7703
7556 7704 ret = fclose(tfp);
7557 7705 error = errno;
7558 7706 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7559 7707 if (ret == EOF) {
7560 7708 bam_error(_("failed to close file: %s: %s\n"),
7561 7709 UFS_SIGNATURE_LIST".tmp", strerror(error));
7562 7710 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7563 7711 return (0);
7564 7712 }
7565 7713
7566 7714 /* Sort the list again */
7567 7715 (void) snprintf(cmd, sizeof (cmd),
7568 7716 "/usr/bin/sort -u %s.tmp > %s.sorted",
7569 7717 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7570 7718
7571 7719 ret = exec_cmd(cmd, NULL);
7572 7720 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7573 7721 if (ret != 0) {
7574 7722 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7575 7723 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7576 7724 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7577 7725 return (0);
7578 7726 }
7579 7727
7580 7728 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7581 7729
7582 7730 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7583 7731 error = errno;
7584 7732 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7585 7733 if (ret == -1) {
7586 7734 bam_error(_("rename to file failed: %s: %s\n"),
7587 7735 UFS_SIGNATURE_LIST, strerror(error));
7588 7736 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7589 7737 return (0);
7590 7738 }
7591 7739
7592 7740 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7593 7741
7594 7742 return (0);
7595 7743 }
7596 7744
7597 7745 static int
7598 7746 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7599 7747 {
7600 7748 int ret;
7601 7749 const char *fcn = "set_signature()";
7602 7750
7603 7751 BAM_DPRINTF(("%s: entered. args: %s %s %s %s\n", fcn,
7604 7752 osroot, osdev, sign, fstype));
7605 7753
7606 7754 ret = set_backup(osroot, osdev, sign, fstype);
7607 7755 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7608 7756 if (ret == -1) {
7609 7757 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7610 7758 bam_error(_("failed to set backup sign (%s) for %s: %s\n"),
7611 7759 sign, osroot, osdev);
7612 7760 return (-1);
7613 7761 }
7614 7762
7615 7763 ret = set_primary(osroot, osdev, sign, fstype);
7616 7764 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7617 7765
7618 7766 if (ret == 0) {
7619 7767 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7620 7768 } else {
7621 7769 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7622 7770 bam_error(_("failed to set primary sign (%s) for %s: %s\n"),
7623 7771 sign, osroot, osdev);
7624 7772
7625 7773 }
7626 7774 return (ret);
7627 7775 }
7628 7776
7629 7777 char *
7630 7778 get_grubsign(char *osroot, char *osdev)
7631 7779 {
7632 7780 char *grubsign; /* (<sign>,#,#) */
7633 7781 char *slice;
7634 7782 int fdiskpart;
7635 7783 char *sign;
7636 7784 char *fstype;
7637 7785 int ret;
7638 7786 const char *fcn = "get_grubsign()";
7639 7787
7640 7788 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, osdev));
7641 7789 fstype = get_fstype(osroot);
7642 7790 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7643 7791 if (fstype == NULL) {
7644 7792 bam_error(_("failed to get fstype for %s\n"), osroot);
7645 7793 return (NULL);
7646 7794 }
7647 7795
7648 7796 sign = find_existing_sign(osroot, osdev, fstype);
7649 7797 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7650 7798 if (sign == NULL) {
7651 7799 BAM_DPRINTF(("%s: no existing grubsign for %s: %s\n",
7652 7800 fcn, osroot, osdev));
7653 7801 sign = create_new_sign(osdev, fstype);
7654 7802 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7655 7803 if (sign == NULL) {
7656 7804 bam_error(_("failed to create GRUB boot signature for "
7657 7805 "device: %s\n"), osdev);
7658 7806 free(fstype);
7659 7807 return (NULL);
7660 7808 }
7661 7809 }
7662 7810
7663 7811 ret = set_signature(osroot, osdev, sign, fstype);
7664 7812 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7665 7813 if (ret == -1) {
7666 7814 bam_error(_("failed to write GRUB boot signature for "
7667 7815 "device: %s\n"), osdev);
7668 7816 free(sign);
7669 7817 free(fstype);
7670 7818 (void) unlink(UFS_SIGNATURE_LIST);
7671 7819 return (NULL);
7672 7820 }
7673 7821
7674 7822 free(fstype);
7675 7823
7676 7824 if (bam_verbose)
7677 7825 bam_print(_("found or created GRUB signature %s for %s\n"),
7678 7826 sign, osdev);
7679 7827
7680 7828 fdiskpart = get_partition(osdev);
7681 7829 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7682 7830 if (fdiskpart == PARTNO_NOTFOUND) {
7683 7831 bam_error(_("failed to determine fdisk partition: %s\n"),
7684 7832 osdev);
7685 7833 free(sign);
7686 7834 return (NULL);
7687 7835 }
7688 7836
7689 7837 slice = strrchr(osdev, 's');
7690 7838
7691 7839 if (fdiskpart == PARTNO_EFI) {
7692 7840 fdiskpart = atoi(&slice[1]);
7693 7841 slice = NULL;
7694 7842 }
7695 7843
7696 7844 grubsign = s_calloc(1, MAXNAMELEN + 10);
7697 7845 if (slice) {
7698 7846 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7699 7847 sign, fdiskpart, slice[1] + 'a' - '0');
7700 7848 } else
7701 7849 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7702 7850 sign, fdiskpart);
7703 7851
7704 7852 free(sign);
7705 7853
7706 7854 BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn, grubsign));
7707 7855
7708 7856 return (grubsign);
7709 7857 }
7710 7858
7711 7859 static char *
7712 7860 get_title(char *rootdir)
7713 7861 {
7714 7862 static char title[80];
7715 7863 char *cp = NULL;
7716 7864 char release[PATH_MAX];
7717 7865 FILE *fp;
7718 7866 const char *fcn = "get_title()";
7719 7867
7720 7868 /* open the /etc/release file */
7721 7869 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7722 7870
7723 7871 fp = fopen(release, "r");
7724 7872 if (fp == NULL) {
7725 7873 bam_error(_("failed to open file: %s: %s\n"), release,
7726 7874 strerror(errno));
7727 7875 cp = NULL;
7728 7876 goto out;
7729 7877 }
7730 7878
7731 7879 /* grab first line of /etc/release */
7732 7880 cp = s_fgets(title, sizeof (title), fp);
7733 7881 if (cp) {
7734 7882 while (isspace(*cp)) /* remove leading spaces */
7735 7883 cp++;
7736 7884 }
7737 7885
7738 7886 (void) fclose(fp);
7739 7887
7740 7888 out:
7741 7889 cp = cp ? cp : "Oracle Solaris";
7742 7890
7743 7891 BAM_DPRINTF(("%s: got title: %s\n", fcn, cp));
7744 7892
7745 7893 return (cp);
7746 7894 }
7747 7895
7748 7896 char *
7749 7897 get_special(char *mountp)
7750 7898 {
7751 7899 FILE *mntfp;
7752 7900 struct mnttab mp = {0};
7753 7901 struct mnttab mpref = {0};
7754 7902 int error;
7755 7903 int ret;
7756 7904 const char *fcn = "get_special()";
7757 7905
7758 7906 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7759 7907 if (mountp == NULL) {
7760 7908 bam_error(_("cannot get special file: NULL mount-point\n"));
7761 7909 return (NULL);
7762 7910 }
7763 7911
7764 7912 mntfp = fopen(MNTTAB, "r");
7765 7913 error = errno;
7766 7914 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7767 7915 if (mntfp == NULL) {
7768 7916 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7769 7917 strerror(error));
7770 7918 return (NULL);
7771 7919 }
7772 7920
7773 7921 if (*mountp == '\0')
7774 7922 mpref.mnt_mountp = "/";
7775 7923 else
7776 7924 mpref.mnt_mountp = mountp;
7777 7925
7778 7926 ret = getmntany(mntfp, &mp, &mpref);
7779 7927 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7780 7928 if (ret != 0) {
7781 7929 (void) fclose(mntfp);
7782 7930 BAM_DPRINTF(("%s: Cannot get special file: mount-point %s "
7783 7931 "not in mnttab\n", fcn, mountp));
7784 7932 return (NULL);
7785 7933 }
7786 7934 (void) fclose(mntfp);
7787 7935
7788 7936 BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
7789 7937
7790 7938 return (s_strdup(mp.mnt_special));
7791 7939 }
7792 7940
7793 7941 static void
7794 7942 free_physarray(char **physarray, int n)
7795 7943 {
7796 7944 int i;
7797 7945 const char *fcn = "free_physarray()";
7798 7946
7799 7947 assert(physarray);
7800 7948 assert(n);
7801 7949
7802 7950 BAM_DPRINTF(("%s: entering args: %d\n", fcn, n));
7803 7951
7804 7952 for (i = 0; i < n; i++) {
7805 7953 free(physarray[i]);
7806 7954 }
7807 7955 free(physarray);
7808 7956
7809 7957 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7810 7958 }
7811 7959
7812 7960 static int
7813 7961 zfs_get_physical(char *special, char ***physarray, int *n)
7814 7962 {
7815 7963 char sdup[PATH_MAX];
7816 7964 char cmd[PATH_MAX];
7817 7965 char dsk[PATH_MAX];
7818 7966 char *pool;
7819 7967 filelist_t flist = {0};
7820 7968 line_t *lp;
7821 7969 line_t *startlp;
7822 7970 char *comp1;
7823 7971 int i;
7824 7972 int ret;
7825 7973 const char *fcn = "zfs_get_physical()";
7826 7974
7827 7975 assert(special);
7828 7976
7829 7977 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, special));
7830 7978
7831 7979 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7832 7980 if (special[0] == '/') {
7833 7981 bam_error(_("invalid device for ZFS filesystem: %s\n"),
7834 7982 special);
7835 7983 return (-1);
7836 7984 }
7837 7985
7838 7986 (void) strlcpy(sdup, special, sizeof (sdup));
7839 7987
7840 7988 pool = strtok(sdup, "/");
7841 7989 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7842 7990 if (pool == NULL) {
7843 7991 bam_error(_("cannot derive ZFS pool from special: %s\n"),
7844 7992 special);
7845 7993 return (-1);
7846 7994 }
7847 7995
7848 7996 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7849 7997
7850 7998 ret = exec_cmd(cmd, &flist);
7851 7999 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7852 8000 if (ret != 0) {
7853 8001 bam_error(_("cannot get zpool status for pool: %s\n"), pool);
7854 8002 return (-1);
7855 8003 }
7856 8004
7857 8005 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7858 8006 if (flist.head == NULL) {
7859 8007 bam_error(_("bad zpool status for pool=%s\n"), pool);
7860 8008 filelist_free(&flist);
7861 8009 return (-1);
7862 8010 }
7863 8011
7864 8012 for (lp = flist.head; lp; lp = lp->next) {
7865 8013 BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
7866 8014 fcn, lp->line));
7867 8015 comp1 = strtok(lp->line, " \t");
7868 8016 if (comp1 == NULL) {
7869 8017 free(lp->line);
7870 8018 lp->line = NULL;
7871 8019 } else {
7872 8020 comp1 = s_strdup(comp1);
7873 8021 free(lp->line);
7874 8022 lp->line = comp1;
7875 8023 }
7876 8024 }
7877 8025
7878 8026 for (lp = flist.head; lp; lp = lp->next) {
7879 8027 if (lp->line == NULL)
7880 8028 continue;
7881 8029 if (strcmp(lp->line, pool) == 0) {
7882 8030 BAM_DPRINTF(("%s: found pool name: %s in zpool "
7883 8031 "status\n", fcn, pool));
7884 8032 break;
7885 8033 }
7886 8034 }
7887 8035
7888 8036 if (lp == NULL) {
7889 8037 bam_error(_("no pool name %s in zpool status\n"), pool);
7890 8038 filelist_free(&flist);
7891 8039 return (-1);
7892 8040 }
7893 8041
7894 8042 startlp = lp->next;
7895 8043 for (i = 0, lp = startlp; lp; lp = lp->next) {
7896 8044 if (lp->line == NULL)
7897 8045 continue;
7898 8046 if (strcmp(lp->line, "mirror") == 0)
7899 8047 continue;
7900 8048 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7901 8049 break;
7902 8050 i++;
7903 8051 BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
7904 8052 fcn, i));
7905 8053 }
7906 8054
7907 8055 if (i == 0) {
7908 8056 bam_error(_("no physical device in zpool status for pool=%s\n"),
7909 8057 pool);
7910 8058 filelist_free(&flist);
7911 8059 return (-1);
7912 8060 }
7913 8061
7914 8062 *n = i;
7915 8063 *physarray = s_calloc(*n, sizeof (char *));
7916 8064 for (i = 0, lp = startlp; lp; lp = lp->next) {
7917 8065 if (lp->line == NULL)
7918 8066 continue;
7919 8067 if (strcmp(lp->line, "mirror") == 0)
7920 8068 continue;
7921 8069 if (strcmp(lp->line, "errors:") == 0)
7922 8070 break;
7923 8071 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7924 8072 strncmp(lp->line, "/dev/rdsk/",
7925 8073 strlen("/dev/rdsk/")) != 0) {
7926 8074 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7927 8075 lp->line);
7928 8076 } else {
7929 8077 (void) strlcpy(dsk, lp->line, sizeof (dsk));
7930 8078 }
7931 8079 BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
7932 8080 fcn, dsk, pool));
7933 8081 (*physarray)[i++] = s_strdup(dsk);
7934 8082 }
7935 8083
7936 8084 assert(i == *n);
7937 8085
7938 8086 filelist_free(&flist);
7939 8087
7940 8088 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7941 8089 return (0);
7942 8090 }
7943 8091
7944 8092 static int
7945 8093 get_physical(char *menu_root, char ***physarray, int *n)
7946 8094 {
7947 8095 char *special;
7948 8096 int ret;
7949 8097 const char *fcn = "get_physical()";
7950 8098
7951 8099 assert(menu_root);
7952 8100 assert(physarray);
7953 8101 assert(n);
7954 8102
7955 8103 *physarray = NULL;
7956 8104 *n = 0;
7957 8105
7958 8106 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_root));
7959 8107
7960 8108 /* First get the device special file from /etc/mnttab */
7961 8109 special = get_special(menu_root);
7962 8110 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7963 8111 if (special == NULL) {
7964 8112 bam_error(_("cannot get special file for mount-point: %s\n"),
7965 8113 menu_root);
7966 8114 return (-1);
7967 8115 }
7968 8116
7969 8117 /* If already a physical device nothing to do */
7970 8118 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7971 8119 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7972 8120 BAM_DPRINTF(("%s: got physical device already directly for "
7973 8121 "menu_root=%s special=%s\n", fcn, menu_root, special));
7974 8122 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7975 8123 *physarray = s_calloc(1, sizeof (char *));
7976 8124 (*physarray)[0] = special;
7977 8125 *n = 1;
7978 8126 return (0);
7979 8127 }
7980 8128
7981 8129 if (is_zfs(menu_root)) {
7982 8130 ret = zfs_get_physical(special, physarray, n);
7983 8131 } else {
7984 8132 bam_error(_("cannot derive physical device for %s (%s), "
7985 8133 "unsupported filesystem\n"), menu_root, special);
7986 8134 ret = -1;
7987 8135 }
7988 8136
7989 8137 free(special);
7990 8138
7991 8139 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7992 8140 if (ret == -1) {
7993 8141 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7994 8142 } else {
7995 8143 int i;
7996 8144 assert (*n > 0);
7997 8145 for (i = 0; i < *n; i++) {
7998 8146 BAM_DPRINTF(("%s: returning physical=%s\n",
7999 8147 fcn, (*physarray)[i]));
8000 8148 }
8001 8149 }
8002 8150
8003 8151 return (ret);
8004 8152 }
8005 8153
8006 8154 static int
8007 8155 is_bootdisk(char *osroot, char *physical)
8008 8156 {
8009 8157 int ret;
8010 8158 char *grubroot;
8011 8159 char *bootp;
8012 8160 const char *fcn = "is_bootdisk()";
8013 8161
8014 8162 assert(osroot);
8015 8163 assert(physical);
8016 8164
8017 8165 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, physical));
8018 8166
8019 8167 bootp = strstr(physical, "p0:boot");
8020 8168 if (bootp)
8021 8169 *bootp = '\0';
8022 8170 /*
8023 8171 * We just want the BIOS mapping for menu disk.
8024 8172 * Don't pass menu_root to get_grubroot() as the
8025 8173 * check that it is used for is not relevant here.
8026 8174 * The osroot is immaterial as well - it is only used to
8027 8175 * to find create_diskmap script. Everything hinges on
8028 8176 * "physical"
8029 8177 */
8030 8178 grubroot = get_grubroot(osroot, physical, NULL);
8031 8179
8032 8180 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
8033 8181 if (grubroot == NULL) {
8034 8182 if (bam_verbose)
8035 8183 bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8036 8184 "disk: %s\n"), physical);
8037 8185 return (0);
8038 8186 }
8039 8187 ret = grubroot[3] == '0';
8040 8188 free(grubroot);
8041 8189
8042 8190 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
8043 8191
8044 8192 return (ret);
8045 8193 }
8046 8194
8047 8195 /*
8048 8196 * Check if menu is on the boot device
8049 8197 * Return 0 (false) on error
8050 8198 */
8051 8199 static int
8052 8200 menu_on_bootdisk(char *osroot, char *menu_root)
8053 8201 {
8054 8202 char **physarray;
8055 8203 int ret;
8056 8204 int n;
8057 8205 int i;
8058 8206 int on_bootdisk;
8059 8207 const char *fcn = "menu_on_bootdisk()";
8060 8208
8061 8209 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8062 8210
8063 8211 ret = get_physical(menu_root, &physarray, &n);
8064 8212 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
8065 8213 if (ret != 0) {
8066 8214 bam_error(_("cannot get physical device special file for menu "
8067 8215 "root: %s\n"), menu_root);
8068 8216 return (0);
8069 8217 }
8070 8218
8071 8219 assert(physarray);
8072 8220 assert(n > 0);
8073 8221
8074 8222 on_bootdisk = 0;
8075 8223 for (i = 0; i < n; i++) {
8076 8224 assert(strncmp(physarray[i], "/dev/dsk/",
8077 8225 strlen("/dev/dsk/")) == 0 ||
8078 8226 strncmp(physarray[i], "/dev/rdsk/",
8079 8227 strlen("/dev/rdsk/")) == 0);
8080 8228
8081 8229 BAM_DPRINTF(("%s: checking if phys-device=%s is on bootdisk\n",
8082 8230 fcn, physarray[i]));
8083 8231 if (is_bootdisk(osroot, physarray[i])) {
8084 8232 on_bootdisk = 1;
8085 8233 BAM_DPRINTF(("%s: phys-device=%s *IS* on bootdisk\n",
8086 8234 fcn, physarray[i]));
8087 8235 }
8088 8236 }
8089 8237
8090 8238 free_physarray(physarray, n);
8091 8239
8092 8240 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8093 8241 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8094 8242 if (on_bootdisk) {
8095 8243 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8096 8244 } else {
8097 8245 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8098 8246 }
8099 8247
8100 8248 return (on_bootdisk);
8101 8249 }
8102 8250
8103 8251 void
8104 8252 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8105 8253 {
8106 8254 const char *fcn = "bam_add_line()";
8107 8255
8108 8256 assert(mp);
8109 8257 assert(entry);
8110 8258 assert(prev);
8111 8259 assert(lp);
8112 8260
8113 8261 lp->next = prev->next;
8114 8262 if (prev->next) {
8115 8263 BAM_DPRINTF(("%s: previous next exists\n", fcn));
8116 8264 prev->next->prev = lp;
8117 8265 } else {
8118 8266 BAM_DPRINTF(("%s: previous next does not exist\n", fcn));
8119 8267 }
8120 8268 prev->next = lp;
8121 8269 lp->prev = prev;
8122 8270
8123 8271 if (entry->end == prev) {
8124 8272 BAM_DPRINTF(("%s: last line in entry\n", fcn));
8125 8273 entry->end = lp;
8126 8274 }
8127 8275 if (mp->end == prev) {
8128 8276 assert(lp->next == NULL);
8129 8277 mp->end = lp;
8130 8278 BAM_DPRINTF(("%s: last line in menu\n", fcn));
8131 8279 }
8132 8280 }
8133 8281
8134 8282 /*
8135 8283 * look for matching bootadm entry with specified parameters
8136 8284 * Here are the rules (based on existing usage):
8137 8285 * - If title is specified, match on title only
8138 8286 * - Else, match on root/findroot, kernel, and module.
8139 8287 * Note that, if root_opt is non-zero, the absence of
8140 8288 * root line is considered a match.
8141 8289 */
8142 8290 static entry_t *
8143 8291 find_boot_entry(
8144 8292 menu_t *mp,
8145 8293 char *title,
8146 8294 char *kernel,
8147 8295 char *findroot,
8148 8296 char *root,
8149 8297 char *module,
8150 8298 int root_opt,
8151 8299 int *entry_num)
8152 8300 {
8153 8301 int i;
8154 8302 line_t *lp;
8155 8303 entry_t *ent;
8156 8304 const char *fcn = "find_boot_entry()";
8157 8305
8158 8306 if (entry_num)
8159 8307 *entry_num = BAM_ERROR;
8160 8308
8161 8309 /* find matching entry */
8162 8310 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8163 8311 lp = ent->start;
8164 8312
8165 8313 /* first line of entry must be bootadm comment */
8166 8314 lp = ent->start;
8167 8315 if (lp->flags != BAM_COMMENT ||
8168 8316 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8169 8317 continue;
8170 8318 }
8171 8319
8172 8320 /* advance to title line */
8173 8321 lp = lp->next;
8174 8322 if (title) {
8175 8323 if (lp->flags == BAM_TITLE && lp->arg &&
8176 8324 strcmp(lp->arg, title) == 0) {
8177 8325 BAM_DPRINTF(("%s: matched title: %s\n",
8178 8326 fcn, title));
8179 8327 break;
8180 8328 }
8181 8329 BAM_DPRINTF(("%s: no match title: %s, %s\n",
8182 8330 fcn, title, lp->arg));
8183 8331 continue; /* check title only */
8184 8332 }
8185 8333
8186 8334 lp = lp->next; /* advance to root line */
8187 8335 if (lp == NULL) {
8188 8336 continue;
8189 8337 } else if (lp->cmd != NULL &&
8190 8338 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8191 8339 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8192 8340 findroot = NULL);
8193 8341 if (findroot == NULL) {
8194 8342 BAM_DPRINTF(("%s: no match line has findroot, "
8195 8343 "we don't: %s\n", fcn, lp->arg));
8196 8344 continue;
8197 8345 }
8198 8346 /* findroot command found, try match */
8199 8347 if (strcmp(lp->arg, findroot) != 0) {
8200 8348 BAM_DPRINTF(("%s: no match findroot: %s, %s\n",
8201 8349 fcn, findroot, lp->arg));
8202 8350 continue;
8203 8351 }
8204 8352 BAM_DPRINTF(("%s: matched findroot: %s\n",
8205 8353 fcn, findroot));
8206 8354 lp = lp->next; /* advance to kernel line */
8207 8355 } else if (lp->cmd != NULL &&
8208 8356 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8209 8357 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8210 8358 if (root == NULL) {
8211 8359 BAM_DPRINTF(("%s: no match, line has root, we "
8212 8360 "don't: %s\n", fcn, lp->arg));
8213 8361 continue;
8214 8362 }
8215 8363 /* root cmd found, try match */
8216 8364 if (strcmp(lp->arg, root) != 0) {
8217 8365 BAM_DPRINTF(("%s: no match root: %s, %s\n",
8218 8366 fcn, root, lp->arg));
8219 8367 continue;
8220 8368 }
8221 8369 BAM_DPRINTF(("%s: matched root: %s\n", fcn, root));
8222 8370 lp = lp->next; /* advance to kernel line */
8223 8371 } else {
8224 8372 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8225 8373 root_opt = 0);
8226 8374 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8227 8375 root_opt = 1);
8228 8376 /* no root command, see if root is optional */
8229 8377 if (root_opt == 0) {
8230 8378 BAM_DPRINTF(("%s: root NOT optional\n", fcn));
8231 8379 continue;
8232 8380 }
8233 8381 BAM_DPRINTF(("%s: root IS optional\n", fcn));
8234 8382 }
8235 8383
8236 8384 if (lp == NULL || lp->next == NULL) {
8237 8385 continue;
8238 8386 }
8239 8387
8240 8388 if (kernel &&
8241 8389 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8242 8390 if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8243 8391 !(ent->flags & BAM_ENTRY_DBOOT) ||
8244 8392 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8245 8393 continue;
8246 8394
8247 8395 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8248 8396
8249 8397 }
8250 8398 BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn,
8251 8399 kernel, lp->arg));
8252 8400
8253 8401 /*
8254 8402 * Check for matching module entry (failsafe or normal).
8255 8403 * If it fails to match, we go around the loop again.
8256 8404 * For xpv entries, there are two module lines, so we
8257 8405 * do the check twice.
8258 8406 */
8259 8407 lp = lp->next; /* advance to module line */
8260 8408 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8261 8409 (((lp = lp->next) != NULL) &&
8262 8410 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8263 8411 /* match found */
8264 8412 BAM_DPRINTF(("%s: module match: %s, %s\n", fcn,
8265 8413 module, lp->arg));
8266 8414 break;
8267 8415 }
8268 8416
8269 8417 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8270 8418 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8271 8419 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8272 8420 ent->flags |= BAM_ENTRY_UPGFSMODULE;
8273 8421 break;
8274 8422 }
8275 8423
8276 8424 }
8277 8425
8278 8426 if (ent && entry_num) {
8279 8427 *entry_num = i;
8280 8428 }
8281 8429
8282 8430 if (ent) {
8283 8431 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8284 8432 } else {
8285 8433 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_ERROR));
8286 8434 }
8287 8435 return (ent);
8288 8436 }
8289 8437
8290 8438 static int
8291 8439 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8292 8440 char *kernel, char *mod_kernel, char *module, int root_opt)
8293 8441 {
8294 8442 int i;
8295 8443 int change_kernel = 0;
8296 8444 entry_t *ent;
8297 8445 line_t *lp;
8298 8446 line_t *tlp;
8299 8447 char linebuf[BAM_MAXLINE];
8300 8448 const char *fcn = "update_boot_entry()";
8301 8449
8302 8450 /* note: don't match on title, it's updated on upgrade */
8303 8451 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8304 8452 root_opt, &i);
8305 8453 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8306 8454 /*
8307 8455 * We may be upgrading a kernel from multiboot to
8308 8456 * directboot. Look for a multiboot entry. A multiboot
8309 8457 * entry will not have a findroot line.
8310 8458 */
8311 8459 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8312 8460 MULTIBOOT_ARCHIVE, root_opt, &i);
8313 8461 if (ent != NULL) {
8314 8462 BAM_DPRINTF(("%s: upgrading entry from dboot to "
8315 8463 "multiboot: root = %s\n", fcn, root));
8316 8464 change_kernel = 1;
8317 8465 }
8318 8466 } else if (ent) {
8319 8467 BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8320 8468 fcn, findroot));
8321 8469 }
8322 8470
8323 8471 if (ent == NULL) {
8324 8472 BAM_DPRINTF(("%s: boot entry not found in menu. Creating "
8325 8473 "new entry, findroot = %s\n", fcn, findroot));
8326 8474 return (add_boot_entry(mp, title, findroot,
8327 8475 kernel, mod_kernel, module, NULL));
8328 8476 }
8329 8477
8330 8478 /* replace title of existing entry and update findroot line */
8331 8479 lp = ent->start;
8332 8480 lp = lp->next; /* title line */
8333 8481 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8334 8482 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8335 8483 free(lp->arg);
8336 8484 free(lp->line);
8337 8485 lp->arg = s_strdup(title);
8338 8486 lp->line = s_strdup(linebuf);
8339 8487 BAM_DPRINTF(("%s: changing title to: %s\n", fcn, title));
8340 8488
8341 8489 tlp = lp; /* title line */
8342 8490 lp = lp->next; /* root line */
8343 8491
8344 8492 /* if no root or findroot command, create a new line_t */
8345 8493 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8346 8494 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8347 8495 lp = s_calloc(1, sizeof (line_t));
8348 8496 bam_add_line(mp, ent, tlp, lp);
8349 8497 } else {
8350 8498 if (lp->cmd != NULL)
8351 8499 free(lp->cmd);
8352 8500
8353 8501 free(lp->sep);
8354 8502 free(lp->arg);
8355 8503 free(lp->line);
8356 8504 }
8357 8505
8358 8506 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8359 8507 lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8360 8508 lp->arg = s_strdup(findroot);
8361 8509 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8362 8510 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8363 8511 lp->line = s_strdup(linebuf);
8364 8512 BAM_DPRINTF(("%s: adding findroot line: %s\n", fcn, findroot));
8365 8513
8366 8514 /* kernel line */
8367 8515 lp = lp->next;
8368 8516
8369 8517 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8370 8518 char *params = NULL;
8371 8519
8372 8520 params = strstr(lp->line, "-s");
8373 8521 if (params != NULL)
8374 8522 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8375 8523 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8376 8524 kernel, params+2);
8377 8525 else
8378 8526 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8379 8527 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8380 8528 kernel);
8381 8529
8382 8530 if (lp->cmd != NULL)
8383 8531 free(lp->cmd);
8384 8532
8385 8533 free(lp->arg);
8386 8534 free(lp->line);
8387 8535 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8388 8536 lp->arg = s_strdup(strstr(linebuf, "/"));
8389 8537 lp->line = s_strdup(linebuf);
8390 8538 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8391 8539 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8392 8540 fcn, lp->prev->cmd));
8393 8541 }
8394 8542
8395 8543 if (change_kernel) {
8396 8544 /*
8397 8545 * We're upgrading from multiboot to directboot.
8398 8546 */
8399 8547 if (lp->cmd != NULL &&
8400 8548 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8401 8549 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8402 8550 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8403 8551 kernel);
8404 8552 free(lp->cmd);
8405 8553 free(lp->arg);
8406 8554 free(lp->line);
8407 8555 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8408 8556 lp->arg = s_strdup(kernel);
8409 8557 lp->line = s_strdup(linebuf);
8410 8558 lp = lp->next;
8411 8559 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8412 8560 fcn, kernel));
8413 8561 }
8414 8562 if (lp->cmd != NULL &&
8415 8563 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8416 8564 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8417 8565 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8418 8566 module);
8419 8567 free(lp->cmd);
8420 8568 free(lp->arg);
8421 8569 free(lp->line);
8422 8570 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8423 8571 lp->arg = s_strdup(module);
8424 8572 lp->line = s_strdup(linebuf);
8425 8573 lp = lp->next;
8426 8574 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8427 8575 fcn, module));
8428 8576 }
8429 8577 }
8430 8578
8431 8579 /* module line */
8432 8580 lp = lp->next;
8433 8581
8434 8582 if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8435 8583 if (lp->cmd != NULL &&
8436 8584 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8437 8585 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8438 8586 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8439 8587 module);
8440 8588 free(lp->cmd);
8441 8589 free(lp->arg);
8442 8590 free(lp->line);
8443 8591 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8444 8592 lp->arg = s_strdup(module);
8445 8593 lp->line = s_strdup(linebuf);
8446 8594 lp = lp->next;
8447 8595 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8448 8596 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8449 8597 fcn, module));
8450 8598 }
8451 8599 }
8452 8600
8453 8601 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8454 8602 return (i);
8455 8603 }
8456 8604
8457 8605 int
8458 8606 root_optional(char *osroot, char *menu_root)
8459 8607 {
8460 8608 char *ospecial;
8461 8609 char *mspecial;
8462 8610 char *slash;
8463 8611 int root_opt;
8464 8612 int ret1;
8465 8613 int ret2;
8466 8614 const char *fcn = "root_optional()";
8467 8615
8468 8616 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8469 8617
8470 8618 /*
8471 8619 * For all filesystems except ZFS, a straight compare of osroot
8472 8620 * and menu_root will tell us if root is optional.
8473 8621 * For ZFS, the situation is complicated by the fact that
8474 8622 * menu_root and osroot are always different
8475 8623 */
8476 8624 ret1 = is_zfs(osroot);
8477 8625 ret2 = is_zfs(menu_root);
8478 8626 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8479 8627 if (!ret1 || !ret2) {
8480 8628 BAM_DPRINTF(("%s: one or more non-ZFS filesystems (%s, %s)\n",
8481 8629 fcn, osroot, menu_root));
8482 8630 root_opt = (strcmp(osroot, menu_root) == 0);
8483 8631 goto out;
8484 8632 }
8485 8633
8486 8634 ospecial = get_special(osroot);
8487 8635 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8488 8636 if (ospecial == NULL) {
8489 8637 bam_error(_("failed to get special file for osroot: %s\n"),
8490 8638 osroot);
8491 8639 return (0);
8492 8640 }
8493 8641 BAM_DPRINTF(("%s: ospecial=%s for osroot=%s\n", fcn, ospecial, osroot));
8494 8642
8495 8643 mspecial = get_special(menu_root);
8496 8644 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8497 8645 if (mspecial == NULL) {
8498 8646 bam_error(_("failed to get special file for menu_root: %s\n"),
8499 8647 menu_root);
8500 8648 free(ospecial);
8501 8649 return (0);
8502 8650 }
8503 8651 BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8504 8652 fcn, mspecial, menu_root));
8505 8653
8506 8654 slash = strchr(ospecial, '/');
8507 8655 if (slash)
8508 8656 *slash = '\0';
8509 8657 BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8510 8658 fcn, ospecial, osroot));
8511 8659
8512 8660 root_opt = (strcmp(ospecial, mspecial) == 0);
8513 8661
8514 8662 free(ospecial);
8515 8663 free(mspecial);
8516 8664
8517 8665 out:
8518 8666 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8519 8667 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8520 8668 if (root_opt) {
8521 8669 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8522 8670 } else {
8523 8671 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8524 8672 }
8525 8673
8526 8674 return (root_opt);
8527 8675 }
8528 8676
8529 8677 /*ARGSUSED*/
8530 8678 static error_t
8531 8679 update_entry(menu_t *mp, char *menu_root, char *osdev)
8532 8680 {
8533 8681 int entry;
8534 8682 char *grubsign;
8535 8683 char *grubroot;
8536 8684 char *title;
8537 8685 char osroot[PATH_MAX];
8538 8686 char *failsafe_kernel = NULL;
8539 8687 struct stat sbuf;
8540 8688 char failsafe[256];
8541 8689 char failsafe_64[256];
8542 8690 int ret;
8543 8691 const char *fcn = "update_entry()";
8544 8692
8545 8693 assert(mp);
8546 8694 assert(menu_root);
8547 8695 assert(osdev);
8548 8696 assert(bam_root);
8549 8697
8550 8698 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, menu_root, osdev,
8551 8699 bam_root));
8552 8700
8553 8701 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8554 8702
8555 8703 title = get_title(osroot);
8556 8704 assert(title);
8557 8705
8558 8706 grubsign = get_grubsign(osroot, osdev);
8559 8707 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8560 8708 if (grubsign == NULL) {
8561 8709 bam_error(_("failed to get grubsign for root: %s, device %s\n"),
8562 8710 osroot, osdev);
8563 8711 return (BAM_ERROR);
8564 8712 }
8565 8713
8566 8714 /*
8567 8715 * It is not a fatal error if get_grubroot() fails
8568 8716 * We no longer rely on biosdev to populate the
8569 8717 * menu
8570 8718 */
8571 8719 grubroot = get_grubroot(osroot, osdev, menu_root);
8572 8720 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8573 8721 if (grubroot) {
8574 8722 BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8575 8723 "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8576 8724 } else {
8577 8725 BAM_DPRINTF(("%s: get_grubroot failed. osroot=%s, osdev=%s, "
8578 8726 "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8579 8727 }
8580 8728
8581 8729 /* add the entry for normal Solaris */
8582 8730 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8583 8731 bam_direct = BAM_DIRECT_MULTIBOOT);
8584 8732 if (bam_direct == BAM_DIRECT_DBOOT) {
8585 8733 entry = update_boot_entry(mp, title, grubsign, grubroot,
8586 8734 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8587 8735 NULL, DIRECT_BOOT_ARCHIVE,
8588 8736 root_optional(osroot, menu_root));
8589 8737 BAM_DPRINTF(("%s: updated boot entry bam_zfs=%d, "
8590 8738 "grubsign = %s\n", fcn, bam_zfs, grubsign));
8591 8739 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8592 8740 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8593 8741 grubroot, XEN_MENU, bam_zfs ?
8594 8742 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8595 8743 DIRECT_BOOT_ARCHIVE,
8596 8744 root_optional(osroot, menu_root));
8597 8745 BAM_DPRINTF(("%s: updated HV entry bam_zfs=%d, "
8598 8746 "grubsign = %s\n", fcn, bam_zfs, grubsign));
8599 8747 }
8600 8748 } else {
8601 8749 entry = update_boot_entry(mp, title, grubsign, grubroot,
8602 8750 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8603 8751 root_optional(osroot, menu_root));
8604 8752
8605 8753 BAM_DPRINTF(("%s: updated MULTIBOOT entry grubsign = %s\n",
8606 8754 fcn, grubsign));
8607 8755 }
8608 8756
8609 8757 /*
8610 8758 * Add the entry for failsafe archive. On a bfu'd system, the
8611 8759 * failsafe may be different than the installed kernel.
8612 8760 */
8613 8761 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8614 8762 osroot, FAILSAFE_ARCHIVE_32);
8615 8763 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8616 8764 osroot, FAILSAFE_ARCHIVE_64);
8617 8765
8618 8766 /*
8619 8767 * Check if at least one of the two archives exists
8620 8768 * Using $ISADIR as the default line, we have an entry which works
8621 8769 * for both the cases.
8622 8770 */
8623 8771
8624 8772 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8625 8773
8626 8774 /* Figure out where the kernel line should point */
8627 8775 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8628 8776 DIRECT_BOOT_FAILSAFE_32);
8629 8777 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8630 8778 osroot, DIRECT_BOOT_FAILSAFE_64);
8631 8779 if (stat(failsafe, &sbuf) == 0 ||
8632 8780 stat(failsafe_64, &sbuf) == 0) {
8633 8781 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8634 8782 } else {
8635 8783 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8636 8784 osroot, MULTI_BOOT_FAILSAFE);
8637 8785 if (stat(failsafe, &sbuf) == 0) {
8638 8786 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8639 8787 }
8640 8788 }
8641 8789 if (failsafe_kernel != NULL) {
8642 8790 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8643 8791 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8644 8792 root_optional(osroot, menu_root));
8645 8793 BAM_DPRINTF(("%s: updated FAILSAFE entry "
8646 8794 "failsafe_kernel = %s\n", fcn, failsafe_kernel));
8647 8795 }
8648 8796 }
8649 8797 free(grubroot);
8650 8798
8651 8799 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8652 8800 if (entry == BAM_ERROR) {
8653 8801 bam_error(_("failed to add boot entry with title=%s, grub "
8654 8802 "signature=%s\n"), title, grubsign);
8655 8803 free(grubsign);
8656 8804 return (BAM_ERROR);
8657 8805 }
8658 8806 free(grubsign);
8659 8807
8660 8808 update_numbering(mp);
8661 8809 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8662 8810 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8663 8811 if (ret == BAM_ERROR) {
8664 8812 bam_error(_("failed to set GRUB menu default to %d\n"), entry);
8665 8813 }
8666 8814 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8667 8815 return (BAM_WRITE);
8668 8816 }
8669 8817
8670 8818 static void
8671 8819 save_default_entry(menu_t *mp, const char *which)
8672 8820 {
8673 8821 int lineNum;
8674 8822 int entryNum;
8675 8823 int entry = 0; /* default is 0 */
8676 8824 char linebuf[BAM_MAXLINE];
8677 8825 line_t *lp = mp->curdefault;
8678 8826 const char *fcn = "save_default_entry()";
8679 8827
8680 8828 if (mp->start) {
8681 8829 lineNum = mp->end->lineNum;
8682 8830 entryNum = mp->end->entryNum;
8683 8831 } else {
8684 8832 lineNum = LINE_INIT;
8685 8833 entryNum = ENTRY_INIT;
8686 8834 }
8687 8835
8688 8836 if (lp)
8689 8837 entry = s_strtol(lp->arg);
8690 8838
8691 8839 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8692 8840 BAM_DPRINTF(("%s: saving default to: %s\n", fcn, linebuf));
8693 8841 line_parser(mp, linebuf, &lineNum, &entryNum);
8694 8842 BAM_DPRINTF(("%s: saved default to lineNum=%d, entryNum=%d\n", fcn,
8695 8843 lineNum, entryNum));
8696 8844 }
8697 8845
8698 8846 static void
8699 8847 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8700 8848 {
8701 8849 int entry;
8702 8850 char *str;
8703 8851 const char *fcn = "restore_default_entry()";
8704 8852
8705 8853 if (lp == NULL) {
8706 8854 BAM_DPRINTF(("%s: NULL saved default\n", fcn));
8707 8855 return; /* nothing to restore */
8708 8856 }
8709 8857
8710 8858 BAM_DPRINTF(("%s: saved default string: %s\n", fcn, which));
8711 8859
8712 8860 str = lp->arg + strlen(which);
8713 8861 entry = s_strtol(str);
8714 8862 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8715 8863
8716 8864 BAM_DPRINTF(("%s: restored default to entryNum: %d\n", fcn, entry));
8717 8865
8718 8866 /* delete saved old default line */
8719 8867 unlink_line(mp, lp);
8720 8868 line_free(lp);
8721 8869 }
8722 8870
8723 8871 /*
8724 8872 * This function is for supporting reboot with args.
8725 8873 * The opt value can be:
8726 8874 * NULL delete temp entry, if present
8727 8875 * entry=<n> switches default entry to <n>
8728 8876 * else treated as boot-args and setup a temperary menu entry
8729 8877 * and make it the default
8730 8878 * Note that we are always rebooting the current OS instance
8731 8879 * so osroot == / always.
8732 8880 */
8733 8881 #define REBOOT_TITLE "Solaris_reboot_transient"
8734 8882
8735 8883 /*ARGSUSED*/
8736 8884 static error_t
8737 8885 update_temp(menu_t *mp, char *dummy, char *opt)
8738 8886 {
8739 8887 int entry;
8740 8888 char *osdev;
8741 8889 char *fstype;
8742 8890 char *sign;
8743 8891 char *opt_ptr;
8744 8892 char *path;
8745 8893 char kernbuf[BUFSIZ];
8746 8894 char args_buf[BUFSIZ];
8747 8895 char signbuf[PATH_MAX];
8748 8896 int ret;
8749 8897 const char *fcn = "update_temp()";
8750 8898
8751 8899 assert(mp);
8752 8900 assert(dummy == NULL);
8753 8901
8754 8902 /* opt can be NULL */
8755 8903 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt ? opt : "<NULL>"));
8756 8904 BAM_DPRINTF(("%s: bam_alt_root: %d, bam_root: %s\n", fcn,
8757 8905 bam_alt_root, bam_root));
8758 8906
8759 8907 if (bam_alt_root || bam_rootlen != 1 ||
8760 8908 strcmp(bam_root, "/") != 0 ||
8761 8909 strcmp(rootbuf, "/") != 0) {
8762 8910 bam_error(_("an alternate root (%s) cannot be used with this "
8763 8911 "sub-command\n"), bam_root);
8764 8912 return (BAM_ERROR);
8765 8913 }
8766 8914
8767 8915 /* If no option, delete exiting reboot menu entry */
8768 8916 if (opt == NULL) {
8769 8917 entry_t *ent;
8770 8918 BAM_DPRINTF(("%s: opt is NULL\n", fcn));
8771 8919 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8772 8920 NULL, NULL, 0, &entry);
8773 8921 if (ent == NULL) { /* not found is ok */
8774 8922 BAM_DPRINTF(("%s: transient entry not found\n", fcn));
8775 8923 return (BAM_SUCCESS);
8776 8924 }
8777 8925 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8778 8926 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8779 8927 mp->olddefault = NULL;
8780 8928 BAM_DPRINTF(("%s: restored old default\n", fcn));
8781 8929 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8782 8930 return (BAM_WRITE);
8783 8931 }
8784 8932
8785 8933 /* if entry= is specified, set the default entry */
8786 8934 if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8787 8935 int entryNum = s_strtol(opt + strlen("entry="));
8788 8936 BAM_DPRINTF(("%s: opt has entry=: %s\n", fcn, opt));
8789 8937 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8790 8938 /* this is entry=# option */
8791 8939 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8792 8940 BAM_DPRINTF(("%s: default set to %d, "
8793 8941 "set_default ret=%d\n", fcn, entry, ret));
8794 8942 return (ret);
8795 8943 } else {
8796 8944 bam_error(_("failed to set GRUB menu default to %d\n"),
8797 8945 entryNum);
8798 8946 return (BAM_ERROR);
8799 8947 }
8800 8948 }
8801 8949
8802 8950 /*
8803 8951 * add a new menu entry based on opt and make it the default
8804 8952 */
8805 8953
8806 8954 fstype = get_fstype("/");
8807 8955 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8808 8956 if (fstype == NULL) {
8809 8957 bam_error(_("failed to determine filesystem type for \"/\". "
8810 8958 "Reboot with \narguments failed.\n"));
8811 8959 return (BAM_ERROR);
8812 8960 }
8813 8961
8814 8962 osdev = get_special("/");
8815 8963 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8816 8964 if (osdev == NULL) {
8817 8965 free(fstype);
8818 8966 bam_error(_("failed to find device special file for \"/\". "
8819 8967 "Reboot with \narguments failed.\n"));
8820 8968 return (BAM_ERROR);
8821 8969 }
8822 8970
8823 8971 sign = find_existing_sign("/", osdev, fstype);
8824 8972 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8825 8973 if (sign == NULL) {
8826 8974 free(fstype);
8827 8975 free(osdev);
8828 8976 bam_error(_("failed to find boot signature. Reboot with "
8829 8977 "arguments failed.\n"));
8830 8978 return (BAM_ERROR);
8831 8979 }
8832 8980
8833 8981 free(osdev);
8834 8982 (void) strlcpy(signbuf, sign, sizeof (signbuf));
8835 8983 free(sign);
8836 8984
8837 8985 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8838 8986 strchr(signbuf, ')') == NULL);
8839 8987
8840 8988 /*
8841 8989 * There is no alternate root while doing reboot with args
8842 8990 * This version of bootadm is only delivered with a DBOOT
8843 8991 * version of Solaris.
8844 8992 */
8845 8993 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8846 8994 if (bam_direct != BAM_DIRECT_DBOOT) {
8847 8995 free(fstype);
8848 8996 bam_error(_("the root filesystem is not a dboot Solaris "
8849 8997 "instance. \nThis version of bootadm is not supported "
8850 8998 "on this version of Solaris.\n"));
8851 8999 return (BAM_ERROR);
8852 9000 }
8853 9001
8854 9002 /* add an entry for Solaris reboot */
8855 9003 if (opt[0] == '-') {
8856 9004 /* It's an option - first see if boot-file is set */
8857 9005 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8858 9006 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8859 9007 if (ret != BAM_SUCCESS) {
8860 9008 free(fstype);
8861 9009 bam_error(_("reboot with arguments: error querying "
8862 9010 "current boot-file settings\n"));
8863 9011 return (BAM_ERROR);
8864 9012 }
8865 9013 if (kernbuf[0] == '\0')
8866 9014 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8867 9015 sizeof (kernbuf));
8868 9016 /*
8869 9017 * If this is a zfs file system and kernbuf does not
8870 9018 * have "-B $ZFS-BOOTFS" string yet, add it.
8871 9019 */
8872 9020 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8873 9021 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8874 9022 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8875 9023 }
8876 9024 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8877 9025 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8878 9026 BAM_DPRINTF(("%s: reboot with args, option specified: "
8879 9027 "kern=%s\n", fcn, kernbuf));
8880 9028 } else if (opt[0] == '/') {
8881 9029 /* It's a full path, so write it out. */
8882 9030 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8883 9031
8884 9032 /*
8885 9033 * If someone runs:
8886 9034 *
8887 9035 * # eeprom boot-args='-kd'
8888 9036 * # reboot /platform/i86pc/kernel/unix
8889 9037 *
8890 9038 * we want to use the boot-args as part of the boot
8891 9039 * line. On the other hand, if someone runs:
8892 9040 *
8893 9041 * # reboot "/platform/i86pc/kernel/unix -kd"
8894 9042 *
8895 9043 * we don't need to mess with boot-args. If there's
8896 9044 * no space in the options string, assume we're in the
8897 9045 * first case.
8898 9046 */
8899 9047 if (strchr(opt, ' ') == NULL) {
8900 9048 ret = get_kernel(mp, ARGS_CMD, args_buf,
8901 9049 sizeof (args_buf));
8902 9050 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8903 9051 if (ret != BAM_SUCCESS) {
8904 9052 free(fstype);
8905 9053 bam_error(_("reboot with arguments: error "
8906 9054 "querying current boot-args settings\n"));
8907 9055 return (BAM_ERROR);
8908 9056 }
8909 9057
8910 9058 if (args_buf[0] != '\0') {
8911 9059 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8912 9060 (void) strlcat(kernbuf, args_buf,
8913 9061 sizeof (kernbuf));
8914 9062 }
8915 9063 }
8916 9064 BAM_DPRINTF(("%s: reboot with args, abspath specified: "
8917 9065 "kern=%s\n", fcn, kernbuf));
8918 9066 } else {
8919 9067 /*
8920 9068 * It may be a partial path, or it may be a partial
8921 9069 * path followed by options. Assume that only options
8922 9070 * follow a space. If someone sends us a kernel path
8923 9071 * that includes a space, they deserve to be broken.
8924 9072 */
8925 9073 opt_ptr = strchr(opt, ' ');
8926 9074 if (opt_ptr != NULL) {
8927 9075 *opt_ptr = '\0';
8928 9076 }
8929 9077
8930 9078 path = expand_path(opt);
8931 9079 if (path != NULL) {
8932 9080 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8933 9081 free(path);
8934 9082
8935 9083 /*
8936 9084 * If there were options given, use those.
8937 9085 * Otherwise, copy over the default options.
8938 9086 */
8939 9087 if (opt_ptr != NULL) {
8940 9088 /* Restore the space in opt string */
8941 9089 *opt_ptr = ' ';
8942 9090 (void) strlcat(kernbuf, opt_ptr,
8943 9091 sizeof (kernbuf));
8944 9092 } else {
8945 9093 ret = get_kernel(mp, ARGS_CMD, args_buf,
8946 9094 sizeof (args_buf));
8947 9095 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8948 9096 ret = BAM_ERROR);
8949 9097 if (ret != BAM_SUCCESS) {
8950 9098 free(fstype);
8951 9099 bam_error(_("reboot with arguments: "
8952 9100 "error querying current boot-args "
8953 9101 "settings\n"));
8954 9102 return (BAM_ERROR);
8955 9103 }
8956 9104
8957 9105 if (args_buf[0] != '\0') {
8958 9106 (void) strlcat(kernbuf, " ",
8959 9107 sizeof (kernbuf));
8960 9108 (void) strlcat(kernbuf,
8961 9109 args_buf, sizeof (kernbuf));
8962 9110 }
8963 9111 }
8964 9112 BAM_DPRINTF(("%s: resolved partial path: %s\n",
8965 9113 fcn, kernbuf));
8966 9114 } else {
8967 9115 free(fstype);
8968 9116 bam_error(_("unable to expand %s to a full file"
8969 9117 " path.\n"), opt);
8970 9118 bam_print_stderr(_("Rebooting with default kernel "
8971 9119 "and options.\n"));
8972 9120 return (BAM_ERROR);
8973 9121 }
8974 9122 }
8975 9123 free(fstype);
8976 9124 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8977 9125 NULL, NULL, NULL);
8978 9126 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8979 9127 if (entry == BAM_ERROR) {
8980 9128 bam_error(_("Cannot update menu. Cannot reboot with "
8981 9129 "requested arguments\n"));
8982 9130 return (BAM_ERROR);
8983 9131 }
8984 9132
8985 9133 save_default_entry(mp, BAM_OLDDEF);
8986 9134 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8987 9135 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8988 9136 if (ret == BAM_ERROR) {
8989 9137 bam_error(_("reboot with arguments: setting GRUB menu default "
8990 9138 "to %d failed\n"), entry);
8991 9139 }
8992 9140 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8993 9141 return (BAM_WRITE);
8994 9142 }
8995 9143
8996 9144 error_t
8997 9145 set_global(menu_t *mp, char *globalcmd, int val)
8998 9146 {
8999 9147 line_t *lp;
9000 9148 line_t *found;
9001 9149 line_t *last;
9002 9150 char *cp;
9003 9151 char *str;
9004 9152 char prefix[BAM_MAXLINE];
9005 9153 size_t len;
9006 9154 const char *fcn = "set_global()";
9007 9155
9008 9156 assert(mp);
9009 9157 assert(globalcmd);
9010 9158
9011 9159 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
9012 9160 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
9013 9161 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
9014 9162 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
9015 9163 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
9016 9164 (void) snprintf(prefix, sizeof (prefix), "%d", val);
9017 9165 bam_error(_("invalid boot entry number: %s\n"), prefix);
9018 9166 return (BAM_ERROR);
9019 9167 }
9020 9168 }
9021 9169
9022 9170 found = last = NULL;
9023 9171 for (lp = mp->start; lp; lp = lp->next) {
9024 9172 if (lp->flags != BAM_GLOBAL)
9025 9173 continue;
9026 9174
9027 9175 last = lp; /* track the last global found */
9028 9176
9029 9177 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
9030 9178 if (lp->cmd == NULL) {
9031 9179 bam_error(_("no command at line %d\n"), lp->lineNum);
9032 9180 continue;
9033 9181 }
9034 9182 if (strcmp(globalcmd, lp->cmd) != 0)
9035 9183 continue;
9036 9184
9037 9185 BAM_DPRINTF(("%s: found matching global command: %s\n",
9038 9186 fcn, globalcmd));
9039 9187
9040 9188 if (found) {
9041 9189 bam_error(_("duplicate command %s at line %d of "
9042 9190 "%sboot/grub/menu.lst\n"), globalcmd,
9043 9191 lp->lineNum, bam_root);
9044 9192 }
9045 9193 found = lp;
9046 9194 }
9047 9195
9048 9196 if (found == NULL) {
9049 9197 lp = s_calloc(1, sizeof (line_t));
9050 9198 if (last == NULL) {
9051 9199 lp->next = mp->start;
9052 9200 mp->start = lp;
9053 9201 mp->end = (mp->end) ? mp->end : lp;
9054 9202 } else {
9055 9203 lp->next = last->next;
9056 9204 last->next = lp;
9057 9205 if (lp->next == NULL)
9058 9206 mp->end = lp;
9059 9207 }
9060 9208 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
9061 9209 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
9062 9210 len += 10; /* val < 10 digits */
9063 9211 lp->line = s_calloc(1, len);
9064 9212 (void) snprintf(lp->line, len, "%s%s%d",
9065 9213 globalcmd, menu_cmds[SEP_CMD], val);
9066 9214 BAM_DPRINTF(("%s: wrote new global line: %s\n", fcn, lp->line));
9067 9215 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9068 9216 return (BAM_WRITE);
9069 9217 }
9070 9218
9071 9219 /*
9072 9220 * We are changing an existing entry. Retain any prefix whitespace,
9073 9221 * but overwrite everything else. This preserves tabs added for
9074 9222 * readability.
9075 9223 */
9076 9224 str = found->line;
9077 9225 cp = prefix;
9078 9226 while (*str == ' ' || *str == '\t')
9079 9227 *(cp++) = *(str++);
9080 9228 *cp = '\0'; /* Terminate prefix */
9081 9229 len = strlen(prefix) + strlen(globalcmd);
9082 9230 len += strlen(menu_cmds[SEP_CMD]) + 10;
9083 9231
9084 9232 free(found->line);
9085 9233 found->line = s_calloc(1, len);
9086 9234 (void) snprintf(found->line, len,
9087 9235 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
9088 9236
9089 9237 BAM_DPRINTF(("%s: replaced global line with: %s\n", fcn, found->line));
9090 9238 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9091 9239 return (BAM_WRITE); /* need a write to menu */
9092 9240 }
9093 9241
9094 9242 /*
9095 9243 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
9096 9244 * expand it to a full unix path. The calling function is expected to
9097 9245 * output a message if an error occurs and NULL is returned.
9098 9246 */
9099 9247 static char *
9100 9248 expand_path(const char *partial_path)
9101 9249 {
9102 9250 int new_path_len;
9103 9251 char *new_path;
9104 9252 char new_path2[PATH_MAX];
9105 9253 struct stat sb;
9106 9254 const char *fcn = "expand_path()";
9107 9255
9108 9256 new_path_len = strlen(partial_path) + 64;
9109 9257 new_path = s_calloc(1, new_path_len);
9110 9258
9111 9259 /* First, try the simplest case - something like "kernel/unix" */
9112 9260 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
9113 9261 partial_path);
9114 9262 if (stat(new_path, &sb) == 0) {
9115 9263 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9116 9264 return (new_path);
9117 9265 }
9118 9266
9119 9267 if (strcmp(partial_path, "kmdb") == 0) {
9120 9268 (void) snprintf(new_path, new_path_len, "%s -k",
9121 9269 DIRECT_BOOT_KERNEL);
9122 9270 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9123 9271 return (new_path);
9124 9272 }
9125 9273
9126 9274 /*
9127 9275 * We've quickly reached unsupported usage. Try once more to
9128 9276 * see if we were just given a glom name.
9129 9277 */
9130 9278 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9131 9279 partial_path);
9132 9280 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9133 9281 partial_path);
9134 9282 if (stat(new_path, &sb) == 0) {
9135 9283 if (stat(new_path2, &sb) == 0) {
9136 9284 /*
9137 9285 * We matched both, so we actually
9138 9286 * want to write the $ISADIR version.
9139 9287 */
9140 9288 (void) snprintf(new_path, new_path_len,
9141 9289 "/platform/i86pc/kernel/%s/$ISADIR/unix",
9142 9290 partial_path);
9143 9291 }
9144 9292 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9145 9293 return (new_path);
9146 9294 }
9147 9295
9148 9296 free(new_path);
9149 9297 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9150 9298 return (NULL);
9151 9299 }
9152 9300
9153 9301 /*
9154 9302 * The kernel cmd and arg have been changed, so
9155 9303 * check whether the archive line needs to change.
9156 9304 */
9157 9305 static void
9158 9306 set_archive_line(entry_t *entryp, line_t *kernelp)
9159 9307 {
9160 9308 line_t *lp = entryp->start;
9161 9309 char *new_archive;
9162 9310 menu_cmd_t m_cmd;
9163 9311 const char *fcn = "set_archive_line()";
9164 9312
9165 9313 for (; lp != NULL; lp = lp->next) {
9166 9314 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9167 9315 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9168 9316 break;
9169 9317 }
9170 9318
9171 9319 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9172 9320 if (lp == entryp->end) {
9173 9321 BAM_DPRINTF(("%s: no module/archive line for entry: "
9174 9322 "%d\n", fcn, entryp->entryNum));
9175 9323 return;
9176 9324 }
9177 9325 }
9178 9326 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9179 9327 if (lp == NULL) {
9180 9328 BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9181 9329 fcn, entryp->entryNum));
9182 9330 return;
9183 9331 }
9184 9332
9185 9333 if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9186 9334 new_archive = DIRECT_BOOT_ARCHIVE;
9187 9335 m_cmd = MODULE_DOLLAR_CMD;
9188 9336 } else if (strstr(kernelp->arg, "amd64") != NULL) {
9189 9337 new_archive = DIRECT_BOOT_ARCHIVE_64;
9190 9338 m_cmd = MODULE_CMD;
9191 9339 } else {
9192 9340 new_archive = DIRECT_BOOT_ARCHIVE_32;
9193 9341 m_cmd = MODULE_CMD;
9194 9342 }
9195 9343
9196 9344 if (strcmp(lp->arg, new_archive) == 0) {
9197 9345 BAM_DPRINTF(("%s: no change for line: %s\n", fcn, lp->arg));
9198 9346 return;
9199 9347 }
9200 9348
9201 9349 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9202 9350 free(lp->cmd);
9203 9351 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9204 9352 }
9205 9353
9206 9354 free(lp->arg);
9207 9355 lp->arg = s_strdup(new_archive);
9208 9356 update_line(lp);
9209 9357 BAM_DPRINTF(("%s: replaced for line: %s\n", fcn, lp->line));
9210 9358 }
9211 9359
9212 9360 /*
9213 9361 * Title for an entry to set properties that once went in bootenv.rc.
9214 9362 */
9215 9363 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
9216 9364
9217 9365 /*
9218 9366 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9219 9367 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
9220 9368 * string, reset the value to the default. If path is a non-zero-length
9221 9369 * string, set the kernel or arguments.
9222 9370 */
9223 9371 static error_t
9224 9372 get_set_kernel(
9225 9373 menu_t *mp,
9226 9374 menu_cmd_t optnum,
9227 9375 char *path,
9228 9376 char *buf,
9229 9377 size_t bufsize)
9230 9378 {
9231 9379 int entryNum;
9232 9380 int rv = BAM_SUCCESS;
9233 9381 int free_new_path = 0;
9234 9382 entry_t *entryp;
9235 9383 line_t *ptr;
9236 9384 line_t *kernelp;
9237 9385 char *new_arg;
9238 9386 char *old_args;
9239 9387 char *space;
9240 9388 char *new_path;
9241 9389 char old_space;
9242 9390 size_t old_kernel_len = 0;
9243 9391 size_t new_str_len;
9244 9392 char *fstype;
9245 9393 char *osdev;
9246 9394 char *sign;
9247 9395 char signbuf[PATH_MAX];
9248 9396 int ret;
9249 9397 const char *fcn = "get_set_kernel()";
9250 9398
9251 9399 assert(bufsize > 0);
9252 9400
9253 9401 ptr = kernelp = NULL;
9254 9402 new_arg = old_args = space = NULL;
9255 9403 new_path = NULL;
9256 9404 buf[0] = '\0';
9257 9405
9258 9406 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9259 9407 bam_direct = BAM_DIRECT_MULTIBOOT);
9260 9408 if (bam_direct != BAM_DIRECT_DBOOT) {
9261 9409 bam_error(_("bootadm set-menu %s may only be run on "
9262 9410 "directboot kernels.\n"),
9263 9411 optnum == KERNEL_CMD ? "kernel" : "args");
9264 9412 return (BAM_ERROR);
9265 9413 }
9266 9414
9267 9415 /*
9268 9416 * If a user changed the default entry to a non-bootadm controlled
9269 9417 * one, we don't want to mess with it. Just print an error and
9270 9418 * return.
9271 9419 */
9272 9420 if (mp->curdefault) {
9273 9421 entryNum = s_strtol(mp->curdefault->arg);
9274 9422 for (entryp = mp->entries; entryp; entryp = entryp->next) {
9275 9423 if (entryp->entryNum == entryNum)
9276 9424 break;
9277 9425 }
9278 9426 if ((entryp != NULL) &&
9279 9427 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9280 9428 bam_error(_("Default /boot/grub/menu.lst entry is not "
9281 9429 "controlled by bootadm. Exiting\n"));
9282 9430 return (BAM_ERROR);
9283 9431 }
9284 9432 }
9285 9433
9286 9434 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9287 9435 0, &entryNum);
9288 9436
9289 9437 if (entryp != NULL) {
9290 9438 for (ptr = entryp->start; ptr && ptr != entryp->end;
9291 9439 ptr = ptr->next) {
9292 9440 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9293 9441 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9294 9442 kernelp = ptr;
9295 9443 break;
9296 9444 }
9297 9445 }
9298 9446 if (kernelp == NULL) {
9299 9447 bam_error(_("no kernel line found in entry %d\n"),
9300 9448 entryNum);
9301 9449 return (BAM_ERROR);
9302 9450 }
9303 9451
9304 9452 old_kernel_len = strcspn(kernelp->arg, " \t");
9305 9453 space = old_args = kernelp->arg + old_kernel_len;
9306 9454 while ((*old_args == ' ') || (*old_args == '\t'))
9307 9455 old_args++;
9308 9456 }
9309 9457
9310 9458 if (path == NULL) {
9311 9459 if (entryp == NULL) {
9312 9460 BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9313 9461 fcn));
9314 9462 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9315 9463 return (BAM_SUCCESS);
9316 9464 }
9317 9465 assert(kernelp);
9318 9466 if (optnum == ARGS_CMD) {
9319 9467 if (old_args[0] != '\0') {
9320 9468 (void) strlcpy(buf, old_args, bufsize);
9321 9469 BAM_DPRINTF(("%s: read menu boot-args: %s\n",
9322 9470 fcn, buf));
9323 9471 }
9324 9472 } else {
9325 9473 /*
9326 9474 * We need to print the kernel, so we just turn the
9327 9475 * first space into a '\0' and print the beginning.
9328 9476 * We don't print anything if it's the default kernel.
9329 9477 */
9330 9478 old_space = *space;
9331 9479 *space = '\0';
9332 9480 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9333 9481 (void) strlcpy(buf, kernelp->arg, bufsize);
9334 9482 BAM_DPRINTF(("%s: read menu boot-file: %s\n",
9335 9483 fcn, buf));
9336 9484 }
9337 9485 *space = old_space;
9338 9486 }
9339 9487 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9340 9488 return (BAM_SUCCESS);
9341 9489 }
9342 9490
9343 9491 /*
9344 9492 * First, check if we're resetting an entry to the default.
9345 9493 */
9346 9494 if ((path[0] == '\0') ||
9347 9495 ((optnum == KERNEL_CMD) &&
9348 9496 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9349 9497 if ((entryp == NULL) || (kernelp == NULL)) {
9350 9498 /* No previous entry, it's already the default */
9351 9499 BAM_DPRINTF(("%s: no reset, already has default\n",
9352 9500 fcn));
9353 9501 return (BAM_SUCCESS);
9354 9502 }
9355 9503
9356 9504 /*
9357 9505 * Check if we can delete the entry. If we're resetting the
9358 9506 * kernel command, and the args is already empty, or if we're
9359 9507 * resetting the args command, and the kernel is already the
9360 9508 * default, we can restore the old default and delete the entry.
9361 9509 */
9362 9510 if (((optnum == KERNEL_CMD) &&
9363 9511 ((old_args == NULL) || (old_args[0] == '\0'))) ||
9364 9512 ((optnum == ARGS_CMD) &&
9365 9513 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9366 9514 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9367 9515 kernelp = NULL;
9368 9516 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9369 9517 restore_default_entry(mp, BAM_OLD_RC_DEF,
9370 9518 mp->old_rc_default);
9371 9519 mp->old_rc_default = NULL;
9372 9520 rv = BAM_WRITE;
9373 9521 BAM_DPRINTF(("%s: resetting to default\n", fcn));
9374 9522 goto done;
9375 9523 }
9376 9524
9377 9525 if (optnum == KERNEL_CMD) {
9378 9526 /*
9379 9527 * At this point, we've already checked that old_args
9380 9528 * and entryp are valid pointers. The "+ 2" is for
9381 9529 * a space a the string termination character.
9382 9530 */
9383 9531 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9384 9532 strlen(old_args) + 2;
9385 9533 new_arg = s_calloc(1, new_str_len);
9386 9534 (void) snprintf(new_arg, new_str_len, "%s %s",
9387 9535 DIRECT_BOOT_KERNEL, old_args);
9388 9536 free(kernelp->arg);
9389 9537 kernelp->arg = new_arg;
9390 9538
9391 9539 /*
9392 9540 * We have changed the kernel line, so we may need
9393 9541 * to update the archive line as well.
9394 9542 */
9395 9543 set_archive_line(entryp, kernelp);
9396 9544 BAM_DPRINTF(("%s: reset kernel to default, but "
9397 9545 "retained old args: %s\n", fcn, kernelp->arg));
9398 9546 } else {
9399 9547 /*
9400 9548 * We're resetting the boot args to nothing, so
9401 9549 * we only need to copy the kernel. We've already
9402 9550 * checked that the kernel is not the default.
9403 9551 */
9404 9552 new_arg = s_calloc(1, old_kernel_len + 1);
9405 9553 (void) snprintf(new_arg, old_kernel_len + 1, "%s",
9406 9554 kernelp->arg);
9407 9555 free(kernelp->arg);
9408 9556 kernelp->arg = new_arg;
9409 9557 BAM_DPRINTF(("%s: reset args to default, but retained "
9410 9558 "old kernel: %s\n", fcn, kernelp->arg));
9411 9559 }
9412 9560 rv = BAM_WRITE;
9413 9561 goto done;
9414 9562 }
9415 9563
9416 9564 /*
9417 9565 * Expand the kernel file to a full path, if necessary
9418 9566 */
9419 9567 if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9420 9568 new_path = expand_path(path);
9421 9569 if (new_path == NULL) {
9422 9570 bam_error(_("unable to expand %s to a full file "
9423 9571 "path.\n"), path);
9424 9572 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9425 9573 return (BAM_ERROR);
9426 9574 }
9427 9575 free_new_path = 1;
9428 9576 } else {
9429 9577 new_path = path;
9430 9578 free_new_path = 0;
9431 9579 }
9432 9580
9433 9581 /*
9434 9582 * At this point, we know we're setting a new value. First, take care
9435 9583 * of the case where there was no previous entry.
9436 9584 */
9437 9585 if (entryp == NULL) {
9438 9586
9439 9587 /* Similar to code in update_temp */
9440 9588 fstype = get_fstype("/");
9441 9589 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9442 9590 if (fstype == NULL) {
9443 9591 bam_error(_("cannot determine filesystem type for "
9444 9592 "\"/\".\nCannot generate GRUB menu entry with "
9445 9593 "EEPROM arguments.\n"));
9446 9594 rv = BAM_ERROR;
9447 9595 goto done;
9448 9596 }
9449 9597
9450 9598 osdev = get_special("/");
9451 9599 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9452 9600 if (osdev == NULL) {
9453 9601 free(fstype);
9454 9602 bam_error(_("cannot determine device special file for "
9455 9603 "\"/\".\nCannot generate GRUB menu entry with "
9456 9604 "EEPROM arguments.\n"));
9457 9605 rv = BAM_ERROR;
9458 9606 goto done;
9459 9607 }
9460 9608
9461 9609 sign = find_existing_sign("/", osdev, fstype);
9462 9610 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9463 9611 if (sign == NULL) {
9464 9612 free(fstype);
9465 9613 free(osdev);
9466 9614 bam_error(_("cannot determine boot signature for "
9467 9615 "\"/\".\nCannot generate GRUB menu entry with "
9468 9616 "EEPROM arguments.\n"));
9469 9617 rv = BAM_ERROR;
9470 9618 goto done;
9471 9619 }
9472 9620
9473 9621 free(osdev);
9474 9622 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9475 9623 free(sign);
9476 9624 assert(strchr(signbuf, '(') == NULL &&
9477 9625 strchr(signbuf, ',') == NULL &&
9478 9626 strchr(signbuf, ')') == NULL);
9479 9627
9480 9628 if (optnum == KERNEL_CMD) {
9481 9629 if (strcmp(fstype, "zfs") == 0) {
9482 9630 new_str_len = strlen(new_path) +
9483 9631 strlen(ZFS_BOOT) + 8;
9484 9632 new_arg = s_calloc(1, new_str_len);
9485 9633 (void) snprintf(new_arg, new_str_len, "%s %s",
9486 9634 new_path, ZFS_BOOT);
9487 9635 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9488 9636 new_arg));
9489 9637 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9490 9638 signbuf, new_arg, NULL, NULL, NULL);
9491 9639 free(new_arg);
9492 9640 } else {
9493 9641 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9494 9642 new_path));
9495 9643 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9496 9644 signbuf, new_path, NULL, NULL, NULL);
9497 9645 }
9498 9646 } else {
9499 9647 new_str_len = strlen(path) + 8;
9500 9648 if (strcmp(fstype, "zfs") == 0) {
9501 9649 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9502 9650 new_arg = s_calloc(1, new_str_len);
9503 9651 (void) snprintf(new_arg, new_str_len, "%s %s",
9504 9652 DIRECT_BOOT_KERNEL_ZFS, path);
9505 9653 } else {
9506 9654 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9507 9655 new_arg = s_calloc(1, new_str_len);
9508 9656 (void) snprintf(new_arg, new_str_len, "%s %s",
9509 9657 DIRECT_BOOT_KERNEL, path);
9510 9658 }
9511 9659
9512 9660 BAM_DPRINTF(("%s: new args=%s\n", fcn, new_arg));
9513 9661 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9514 9662 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9515 9663 free(new_arg);
9516 9664 }
9517 9665 free(fstype);
9518 9666 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9519 9667 entryNum = BAM_ERROR);
9520 9668 if (entryNum == BAM_ERROR) {
9521 9669 bam_error(_("failed to add boot entry: %s\n"),
9522 9670 BOOTENV_RC_TITLE);
9523 9671 rv = BAM_ERROR;
9524 9672 goto done;
9525 9673 }
9526 9674 save_default_entry(mp, BAM_OLD_RC_DEF);
9527 9675 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9528 9676 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9529 9677 if (ret == BAM_ERROR) {
9530 9678 bam_error(_("failed to set default to: %d\n"),
9531 9679 entryNum);
9532 9680 }
9533 9681 rv = BAM_WRITE;
9534 9682 goto done;
9535 9683 }
9536 9684
9537 9685 /*
9538 9686 * There was already an bootenv entry which we need to edit.
9539 9687 */
9540 9688 if (optnum == KERNEL_CMD) {
9541 9689 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9542 9690 new_arg = s_calloc(1, new_str_len);
9543 9691 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9544 9692 old_args);
9545 9693 free(kernelp->arg);
9546 9694 kernelp->arg = new_arg;
9547 9695
9548 9696 /*
9549 9697 * If we have changed the kernel line, we may need to update
9550 9698 * the archive line as well.
9551 9699 */
9552 9700 set_archive_line(entryp, kernelp);
9553 9701 BAM_DPRINTF(("%s: rc line exists, replaced kernel, same "
9554 9702 "args: %s\n", fcn, kernelp->arg));
9555 9703 } else {
9556 9704 new_str_len = old_kernel_len + strlen(path) + 8;
9557 9705 new_arg = s_calloc(1, new_str_len);
9558 9706 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9559 9707 (void) strlcat(new_arg, " ", new_str_len);
9560 9708 (void) strlcat(new_arg, path, new_str_len);
9561 9709 free(kernelp->arg);
9562 9710 kernelp->arg = new_arg;
9563 9711 BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9564 9712 "args: %s\n", fcn, kernelp->arg));
9565 9713 }
9566 9714 rv = BAM_WRITE;
9567 9715
9568 9716 done:
9569 9717 if ((rv == BAM_WRITE) && kernelp)
9570 9718 update_line(kernelp);
9571 9719 if (free_new_path)
9572 9720 free(new_path);
9573 9721 if (rv == BAM_WRITE) {
9574 9722 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9575 9723 } else {
9576 9724 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9577 9725 }
9578 9726 return (rv);
9579 9727 }
9580 9728
9581 9729 static error_t
9582 9730 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9583 9731 {
9584 9732 const char *fcn = "get_kernel()";
9585 9733 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_cmds[optnum]));
9586 9734 return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9587 9735 }
9588 9736
9589 9737 static error_t
9590 9738 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9591 9739 {
9592 9740 const char *fcn = "set_kernel()";
9593 9741 assert(path != NULL);
9594 9742 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn,
9595 9743 menu_cmds[optnum], path));
9596 9744 return (get_set_kernel(mp, optnum, path, buf, bufsize));
9597 9745 }
9598 9746
9599 9747 /*ARGSUSED*/
9600 9748 static error_t
9601 9749 set_option(menu_t *mp, char *dummy, char *opt)
9602 9750 {
9603 9751 int optnum;
9604 9752 int optval;
9605 9753 char *val;
9606 9754 char buf[BUFSIZ] = "";
9607 9755 error_t rv;
9608 9756 const char *fcn = "set_option()";
9609 9757
9610 9758 assert(mp);
9611 9759 assert(opt);
9612 9760 assert(dummy == NULL);
9613 9761
9614 9762 /* opt is set from bam_argv[0] and is always non-NULL */
9615 9763 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt));
9616 9764
9617 9765 val = strchr(opt, '=');
9618 9766 if (val != NULL) {
9619 9767 *val = '\0';
9620 9768 }
9621 9769
9622 9770 if (strcmp(opt, "default") == 0) {
9623 9771 optnum = DEFAULT_CMD;
9624 9772 } else if (strcmp(opt, "timeout") == 0) {
9625 9773 optnum = TIMEOUT_CMD;
9626 9774 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9627 9775 optnum = KERNEL_CMD;
9628 9776 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9629 9777 optnum = ARGS_CMD;
9630 9778 } else {
9631 9779 bam_error(_("invalid option: %s\n"), opt);
9632 9780 return (BAM_ERROR);
9633 9781 }
9634 9782
9635 9783 /*
9636 9784 * kernel and args are allowed without "=new_value" strings. All
9637 9785 * others cause errors
9638 9786 */
9639 9787 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9640 9788 bam_error(_("option has no argument: %s\n"), opt);
9641 9789 return (BAM_ERROR);
9642 9790 } else if (val != NULL) {
9643 9791 *val = '=';
9644 9792 }
9645 9793
9646 9794 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9647 9795 BAM_DPRINTF(("%s: setting %s option to %s\n",
9648 9796 fcn, menu_cmds[optnum], val ? val + 1 : "NULL"));
9649 9797
9650 9798 if (val)
9651 9799 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9652 9800 else
9653 9801 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9654 9802 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9655 9803 (void) printf("%s\n", buf);
9656 9804 } else {
9657 9805 optval = s_strtol(val + 1);
9658 9806 BAM_DPRINTF(("%s: setting %s option to %s\n", fcn,
9659 9807 menu_cmds[optnum], val + 1));
9660 9808 rv = set_global(mp, menu_cmds[optnum], optval);
9661 9809 }
9662 9810
9663 9811 if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9664 9812 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9665 9813 } else {
9666 9814 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9667 9815 }
9668 9816
9669 9817 return (rv);
9670 9818 }
9671 9819
9672 9820 /*
9673 9821 * The quiet argument suppresses messages. This is used
9674 9822 * when invoked in the context of other commands (e.g. list_entry)
9675 9823 */
9676 9824 static error_t
9677 9825 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9678 9826 {
9679 9827 line_t *lp;
9680 9828 char *arg;
9681 9829 int done, ret = BAM_SUCCESS;
9682 9830
9683 9831 assert(mp);
9684 9832 assert(menu_path);
9685 9833 assert(globalcmd);
9686 9834
9687 9835 if (mp->start == NULL) {
9688 9836 if (!quiet)
9689 9837 bam_error(_("menu file not found: %s\n"), menu_path);
9690 9838 return (BAM_ERROR);
9691 9839 }
9692 9840
9693 9841 done = 0;
9694 9842 for (lp = mp->start; lp; lp = lp->next) {
9695 9843 if (lp->flags != BAM_GLOBAL)
9696 9844 continue;
9697 9845
9698 9846 if (lp->cmd == NULL) {
9699 9847 if (!quiet)
9700 9848 bam_error(_("no command at line %d\n"),
9701 9849 lp->lineNum);
9702 9850 continue;
9703 9851 }
9704 9852
9705 9853 if (strcmp(globalcmd, lp->cmd) != 0)
9706 9854 continue;
9707 9855
9708 9856 /* Found global. Check for duplicates */
9709 9857 if (done && !quiet) {
9710 9858 bam_error(_("duplicate command %s at line %d of "
9711 9859 "%sboot/grub/menu.lst\n"), globalcmd,
9712 9860 lp->lineNum, bam_root);
9713 9861 ret = BAM_ERROR;
9714 9862 }
9715 9863
9716 9864 arg = lp->arg ? lp->arg : "";
9717 9865 bam_print(_("%s %s\n"), globalcmd, arg);
9718 9866 done = 1;
9719 9867 }
9720 9868
9721 9869 if (!done && bam_verbose)
9722 9870 bam_print(_("no %s entry found\n"), globalcmd);
9723 9871
9724 9872 return (ret);
9725 9873 }
9726 9874
9727 9875 static error_t
9728 9876 menu_write(char *root, menu_t *mp)
9729 9877 {
9730 9878 const char *fcn = "menu_write()";
9731 9879
9732 9880 BAM_DPRINTF(("%s: entered menu_write() for root: <%s>\n", fcn, root));
9733 9881 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9734 9882 }
9735 9883
9736 9884 void
9737 9885 line_free(line_t *lp)
9738 9886 {
9739 9887 if (lp == NULL)
9740 9888 return;
9741 9889
9742 9890 if (lp->cmd != NULL)
9743 9891 free(lp->cmd);
9744 9892 if (lp->sep)
9745 9893 free(lp->sep);
9746 9894 if (lp->arg)
9747 9895 free(lp->arg);
9748 9896 if (lp->line)
9749 9897 free(lp->line);
9750 9898 free(lp);
9751 9899 }
9752 9900
9753 9901 static void
9754 9902 linelist_free(line_t *start)
9755 9903 {
9756 9904 line_t *lp;
9757 9905
9758 9906 while (start) {
9759 9907 lp = start;
9760 9908 start = start->next;
9761 9909 line_free(lp);
9762 9910 }
9763 9911 }
9764 9912
9765 9913 static void
9766 9914 filelist_free(filelist_t *flistp)
9767 9915 {
9768 9916 linelist_free(flistp->head);
9769 9917 flistp->head = NULL;
9770 9918 flistp->tail = NULL;
9771 9919 }
9772 9920
9773 9921 static void
9774 9922 menu_free(menu_t *mp)
9775 9923 {
9776 9924 entry_t *ent, *tmp;
9777 9925 assert(mp);
9778 9926
9779 9927 if (mp->start)
9780 9928 linelist_free(mp->start);
9781 9929 ent = mp->entries;
9782 9930 while (ent) {
9783 9931 tmp = ent;
9784 9932 ent = tmp->next;
9785 9933 free(tmp);
9786 9934 }
9787 9935
9788 9936 free(mp);
9789 9937 }
9790 9938
9791 9939 /*
9792 9940 * Utility routines
9793 9941 */
9794 9942
9795 9943
9796 9944 /*
9797 9945 * Returns 0 on success
9798 9946 * Any other value indicates an error
9799 9947 */
9800 9948 static int
9801 9949 exec_cmd(char *cmdline, filelist_t *flistp)
9802 9950 {
9803 9951 char buf[BUFSIZ];
9804 9952 int ret;
9805 9953 FILE *ptr;
9806 9954 sigset_t set;
9807 9955 void (*disp)(int);
9808 9956
9809 9957 /*
9810 9958 * For security
9811 9959 * - only absolute paths are allowed
9812 9960 * - set IFS to space and tab
9813 9961 */
9814 9962 if (*cmdline != '/') {
9815 9963 bam_error(_("path is not absolute: %s\n"), cmdline);
9816 9964 return (-1);
9817 9965 }
9818 9966 (void) putenv("IFS= \t");
9819 9967
9820 9968 /*
9821 9969 * We may have been exec'ed with SIGCHLD blocked
9822 9970 * unblock it here
9823 9971 */
9824 9972 (void) sigemptyset(&set);
9825 9973 (void) sigaddset(&set, SIGCHLD);
9826 9974 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9827 9975 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno));
9828 9976 return (-1);
9829 9977 }
9830 9978
9831 9979 /*
9832 9980 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9833 9981 */
9834 9982 disp = sigset(SIGCHLD, SIG_DFL);
9835 9983 if (disp == SIG_ERR) {
9836 9984 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
9837 9985 strerror(errno));
9838 9986 return (-1);
9839 9987 }
9840 9988 if (disp == SIG_HOLD) {
9841 9989 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
9842 9990 cmdline);
9843 9991 return (-1);
9844 9992 }
9845 9993
9846 9994 ptr = popen(cmdline, "r");
9847 9995 if (ptr == NULL) {
9848 9996 bam_error(_("popen failed: %s: %s\n"), cmdline,
9849 9997 strerror(errno));
9850 9998 return (-1);
9851 9999 }
9852 10000
9853 10001 /*
9854 10002 * If we simply do a pclose() following a popen(), pclose()
9855 10003 * will close the reader end of the pipe immediately even
9856 10004 * if the child process has not started/exited. pclose()
9857 10005 * does wait for cmd to terminate before returning though.
9858 10006 * When the executed command writes its output to the pipe
9859 10007 * there is no reader process and the command dies with
9860 10008 * SIGPIPE. To avoid this we read repeatedly until read
9861 10009 * terminates with EOF. This indicates that the command
9862 10010 * (writer) has closed the pipe and we can safely do a
9863 10011 * pclose().
9864 10012 *
9865 10013 * Since pclose() does wait for the command to exit,
9866 10014 * we can safely reap the exit status of the command
9867 10015 * from the value returned by pclose()
9868 10016 */
9869 10017 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9870 10018 if (flistp == NULL) {
9871 10019 /* s_fgets strips newlines, so insert them at the end */
9872 10020 bam_print(_("%s\n"), buf);
9873 10021 } else {
9874 10022 append_to_flist(flistp, buf);
9875 10023 }
9876 10024 }
9877 10025
9878 10026 ret = pclose(ptr);
9879 10027 if (ret == -1) {
9880 10028 bam_error(_("pclose failed: %s: %s\n"), cmdline,
9881 10029 strerror(errno));
9882 10030 return (-1);
9883 10031 }
9884 10032
9885 10033 if (WIFEXITED(ret)) {
9886 10034 return (WEXITSTATUS(ret));
9887 10035 } else {
9888 10036 bam_error(_("command terminated abnormally: %s: %d\n"),
9889 10037 cmdline, ret);
9890 10038 return (-1);
9891 10039 }
9892 10040 }
9893 10041
9894 10042 /*
9895 10043 * Since this function returns -1 on error
9896 10044 * it cannot be used to convert -1. However,
9897 10045 * that is sufficient for what we need.
9898 10046 */
9899 10047 static long
9900 10048 s_strtol(char *str)
9901 10049 {
9902 10050 long l;
9903 10051 char *res = NULL;
9904 10052
9905 10053 if (str == NULL) {
9906 10054 return (-1);
9907 10055 }
9908 10056
9909 10057 errno = 0;
9910 10058 l = strtol(str, &res, 10);
9911 10059 if (errno || *res != '\0') {
9912 10060 return (-1);
9913 10061 }
9914 10062
9915 10063 return (l);
9916 10064 }
9917 10065
9918 10066 /*
9919 10067 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9920 10068 */
9921 10069 static int
9922 10070 s_fputs(char *str, FILE *fp)
9923 10071 {
9924 10072 char linebuf[BAM_MAXLINE];
9925 10073
9926 10074 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9927 10075 return (fputs(linebuf, fp));
9928 10076 }
9929 10077
9930 10078 /*
9931 10079 * Wrapper around fgets, that strips newlines returned by fgets
9932 10080 */
9933 10081 char *
9934 10082 s_fgets(char *buf, int buflen, FILE *fp)
9935 10083 {
9936 10084 int n;
9937 10085
9938 10086 buf = fgets(buf, buflen, fp);
9939 10087 if (buf) {
9940 10088 n = strlen(buf);
9941 10089 if (n == buflen - 1 && buf[n-1] != '\n')
9942 10090 bam_error(_("the following line is too long "
9943 10091 "(> %d chars)\n\t%s\n"), buflen - 1, buf);
9944 10092 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9945 10093 }
9946 10094
9947 10095 return (buf);
9948 10096 }
9949 10097
9950 10098 void *
9951 10099 s_calloc(size_t nelem, size_t sz)
9952 10100 {
9953 10101 void *ptr;
9954 10102
9955 10103 ptr = calloc(nelem, sz);
9956 10104 if (ptr == NULL) {
9957 10105 bam_error(_("could not allocate memory: size = %u\n"),
9958 10106 nelem*sz);
9959 10107 bam_exit(1);
9960 10108 }
9961 10109 return (ptr);
9962 10110 }
9963 10111
9964 10112 void *
9965 10113 s_realloc(void *ptr, size_t sz)
9966 10114 {
9967 10115 ptr = realloc(ptr, sz);
9968 10116 if (ptr == NULL) {
9969 10117 bam_error(_("could not allocate memory: size = %u\n"), sz);
9970 10118 bam_exit(1);
9971 10119 }
9972 10120 return (ptr);
9973 10121 }
9974 10122
9975 10123 char *
9976 10124 s_strdup(char *str)
9977 10125 {
9978 10126 char *ptr;
9979 10127
9980 10128 if (str == NULL)
9981 10129 return (NULL);
9982 10130
9983 10131 ptr = strdup(str);
9984 10132 if (ptr == NULL) {
9985 10133 bam_error(_("could not allocate memory: size = %u\n"),
9986 10134 strlen(str) + 1);
9987 10135 bam_exit(1);
9988 10136 }
9989 10137 return (ptr);
9990 10138 }
9991 10139
9992 10140 /*
9993 10141 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9994 10142 * Returns 0 otherwise
9995 10143 */
9996 10144 static int
9997 10145 is_amd64(void)
9998 10146 {
9999 10147 static int amd64 = -1;
10000 10148 char isabuf[257]; /* from sysinfo(2) manpage */
10001 10149
10002 10150 if (amd64 != -1)
10003 10151 return (amd64);
10004 10152
10005 10153 if (bam_alt_platform) {
10006 10154 if (strcmp(bam_platform, "i86pc") == 0) {
10007 10155 amd64 = 1; /* diskless server */
10008 10156 }
10009 10157 } else {
10010 10158 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
10011 10159 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
10012 10160 amd64 = 1;
10013 10161 } else if (strstr(isabuf, "i386") == NULL) {
10014 10162 amd64 = 1; /* diskless server */
10015 10163 }
10016 10164 }
10017 10165 if (amd64 == -1)
10018 10166 amd64 = 0;
10019 10167
10020 10168 return (amd64);
10021 10169 }
10022 10170
10023 10171 static char *
10024 10172 get_machine(void)
10025 10173 {
10026 10174 static int cached = -1;
10027 10175 static char mbuf[257]; /* from sysinfo(2) manpage */
10028 10176
10029 10177 if (cached == 0)
10030 10178 return (mbuf);
10031 10179
10032 10180 if (bam_alt_platform) {
10033 10181 return (bam_platform);
10034 10182 } else {
10035 10183 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
10036 10184 cached = 1;
10037 10185 }
10038 10186 }
10039 10187 if (cached == -1) {
10040 10188 mbuf[0] = '\0';
10041 10189 cached = 0;
10042 10190 }
10043 10191
10044 10192 return (mbuf);
10045 10193 }
10046 10194
10047 10195 int
10048 10196 is_sparc(void)
10049 10197 {
10050 10198 static int issparc = -1;
10051 10199 char mbuf[257]; /* from sysinfo(2) manpage */
10052 10200
10053 10201 if (issparc != -1)
10054 10202 return (issparc);
10055 10203
10056 10204 if (bam_alt_platform) {
10057 10205 if (strncmp(bam_platform, "sun4", 4) == 0) {
10058 10206 issparc = 1;
10059 10207 }
10060 10208 } else {
10061 10209 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
10062 10210 strcmp(mbuf, "sparc") == 0) {
10063 10211 issparc = 1;
10064 10212 }
10065 10213 }
10066 10214 if (issparc == -1)
10067 10215 issparc = 0;
10068 10216
10069 10217 return (issparc);
10070 10218 }
10071 10219
10072 10220 static void
10073 10221 append_to_flist(filelist_t *flistp, char *s)
10074 10222 {
10075 10223 line_t *lp;
10076 10224
10077 10225 lp = s_calloc(1, sizeof (line_t));
10078 10226 lp->line = s_strdup(s);
10079 10227 if (flistp->head == NULL)
10080 10228 flistp->head = lp;
10081 10229 else
10082 10230 flistp->tail->next = lp;
10083 10231 flistp->tail = lp;
10084 10232 }
10085 10233
10086 10234 #if !defined(_OBP)
10087 10235
10088 10236 UCODE_VENDORS;
10089 10237
10090 10238 /*ARGSUSED*/
10091 10239 static void
10092 10240 ucode_install(char *root)
10093 10241 {
10094 10242 int i;
10095 10243
10096 10244 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
10097 10245 int cmd_len = PATH_MAX + 256;
10098 10246 char cmd[PATH_MAX + 256];
10099 10247 char file[PATH_MAX];
10100 10248 char timestamp[PATH_MAX];
10101 10249 struct stat fstatus, tstatus;
10102 10250 struct utimbuf u_times;
10103 10251
10104 10252 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
10105 10253 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
10106 10254 ucode_vendors[i].extstr);
10107 10255
10108 10256 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
10109 10257 continue;
10110 10258
10111 10259 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
10112 10260
10113 10261 if (stat(timestamp, &tstatus) == 0 &&
10114 10262 fstatus.st_mtime <= tstatus.st_mtime)
10115 10263 continue;
10116 10264
10117 10265 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
10118 10266 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
10119 10267 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
10120 10268 if (system(cmd) != 0)
10121 10269 return;
10122 10270
10123 10271 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
10124 10272 return;
10125 10273
10126 10274 u_times.actime = fstatus.st_atime;
10127 10275 u_times.modtime = fstatus.st_mtime;
10128 10276 (void) utime(timestamp, &u_times);
10129 10277 }
10130 10278 }
10131 10279 #endif
|
↓ open down ↓ |
6004 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX