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