xref: /titanic_41/usr/src/cmd/boot/bootadm/bootadm.c (revision 54925bf60766fbb4f1f2d7c843721406a7b7a3fb)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * bootadm(1M) is a new utility for managing bootability of
30  * Solaris *Newboot* environments. It has two primary tasks:
31  * 	- Allow end users to manage bootability of Newboot Solaris instances
32  *	- Provide services to other subsystems in Solaris (primarily Install)
33  */
34 
35 /* Headers */
36 #include <stdio.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <stdarg.h>
44 #include <limits.h>
45 #include <signal.h>
46 #include <sys/wait.h>
47 #include <sys/mnttab.h>
48 #include <sys/statvfs.h>
49 #include <libnvpair.h>
50 #include <ftw.h>
51 #include <fcntl.h>
52 #include <strings.h>
53 #include <utime.h>
54 #include <sys/systeminfo.h>
55 #include <sys/dktp/fdisk.h>
56 #include <sys/param.h>
57 #if defined(__i386)
58 #include <sys/ucode.h>
59 #endif
60 
61 #include <pwd.h>
62 #include <grp.h>
63 #include <device_info.h>
64 
65 #include <locale.h>
66 
67 #include <assert.h>
68 
69 #include "message.h"
70 #include "bootadm.h"
71 
72 #ifndef TEXT_DOMAIN
73 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
74 #endif	/* TEXT_DOMAIN */
75 
76 /* Type definitions */
77 
78 /* Primary subcmds */
79 typedef enum {
80 	BAM_MENU = 3,
81 	BAM_ARCHIVE
82 } subcmd_t;
83 
84 typedef enum {
85     OPT_ABSENT = 0,	/* No option */
86     OPT_REQ,		/* option required */
87     OPT_OPTIONAL	/* option may or may not be present */
88 } option_t;
89 
90 typedef struct {
91 	char	*subcmd;
92 	option_t option;
93 	error_t (*handler)();
94 	int	unpriv;			/* is this an unprivileged command */
95 } subcmd_defn_t;
96 
97 #define	LINE_INIT	0	/* lineNum initial value */
98 #define	ENTRY_INIT	-1	/* entryNum initial value */
99 #define	ALL_ENTRIES	-2	/* selects all boot entries */
100 
101 #define	GRUB_DIR		"/boot/grub"
102 #define	GRUB_MENU		"/boot/grub/menu.lst"
103 #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
104 #define	RAMDISK_SPECIAL		"/ramdisk"
105 #define	STUBBOOT		"/stubboot"
106 
107 /* lock related */
108 #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
109 #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
110 
111 #define	CREATE_RAMDISK		"/boot/solaris/bin/create_ramdisk"
112 #define	CREATE_DISKMAP		"/boot/solaris/bin/create_diskmap"
113 #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
114 
115 #define	GRUB_slice		"/etc/lu/GRUB_slice"
116 #define	GRUB_root		"/etc/lu/GRUB_root"
117 #define	GRUB_backup_menu	"/etc/lu/GRUB_backup_menu"
118 #define	GRUB_slice_mntpt	"/tmp/GRUB_slice_mntpt"
119 #define	LU_ACTIVATE_FILE	"/etc/lu/DelayUpdate/activate.sh"
120 #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
121 #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
122 
123 #define	INSTALLGRUB		"/sbin/installgrub"
124 #define	STAGE1			"/boot/grub/stage1"
125 #define	STAGE2			"/boot/grub/stage2"
126 
127 /*
128  * The following two defines are used to detect and create the correct
129  * boot archive  when safemode patching is underway.  LOFS_PATCH_FILE is a
130  * contracted private interface between bootadm and the install
131  * consolidation.  It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE
132  * is applied.
133  */
134 
135 #define	LOFS_PATCH_FILE		"/var/run/.patch_loopback_mode"
136 #define	LOFS_PATCH_MNT		"/var/run/.patch_root_loopbackmnt"
137 
138 /*
139  * Default file attributes
140  */
141 #define	DEFAULT_DEV_MODE	0644	/* default permissions */
142 #define	DEFAULT_DEV_UID		0	/* user root */
143 #define	DEFAULT_DEV_GID		3	/* group sys */
144 
145 /*
146  * Menu related
147  * menu_cmd_t and menu_cmds must be kept in sync
148  */
149 char *menu_cmds[] = {
150 	"default",	/* DEFAULT_CMD */
151 	"timeout",	/* TIMEOUT_CMD */
152 	"title",	/* TITLE_CMD */
153 	"root",		/* ROOT_CMD */
154 	"kernel",	/* KERNEL_CMD */
155 	"kernel$",	/* KERNEL_DOLLAR_CMD */
156 	"module",	/* MODULE_CMD */
157 	"module$",	/* MODULE_DOLLAR_CMD */
158 	" ",		/* SEP_CMD */
159 	"#",		/* COMMENT_CMD */
160 	"chainloader",	/* CHAINLOADER_CMD */
161 	"args",		/* ARGS_CMD */
162 	NULL
163 };
164 
165 #define	OPT_ENTRY_NUM	"entry"
166 
167 /*
168  * archive related
169  */
170 typedef struct {
171 	line_t *head;
172 	line_t *tail;
173 } filelist_t;
174 
175 #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
176 #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
177 
178 #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
179 #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
180 #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
181 #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
182 
183 /* Globals */
184 int bam_verbose;
185 int bam_force;
186 static char *prog;
187 static subcmd_t bam_cmd;
188 static char *bam_root;
189 static int bam_rootlen;
190 static int bam_root_readonly;
191 static int bam_alt_root;
192 static char *bam_subcmd;
193 static char *bam_opt;
194 static int bam_debug;
195 static char **bam_argv;
196 static int bam_argc;
197 static int bam_check;
198 static int bam_smf_check;
199 static int bam_lock_fd = -1;
200 static char rootbuf[PATH_MAX] = "/";
201 static int bam_update_all;
202 
203 /* function prototypes */
204 static void parse_args_internal(int argc, char *argv[]);
205 static void parse_args(int argc, char *argv[]);
206 static error_t bam_menu(char *subcmd, char *opt, int argc, char *argv[]);
207 static error_t bam_archive(char *subcmd, char *opt);
208 
209 static void bam_print(char *format, ...);
210 static void bam_exit(int excode);
211 static void bam_lock(void);
212 static void bam_unlock(void);
213 
214 static int exec_cmd(char *cmdline, char *output, int64_t osize);
215 static error_t read_globals(menu_t *mp, char *menu_path,
216     char *globalcmd, int quiet);
217 
218 static menu_t *menu_read(char *menu_path);
219 static error_t menu_write(char *root, menu_t *mp);
220 static void linelist_free(line_t *start);
221 static void menu_free(menu_t *mp);
222 static void line_free(line_t *lp);
223 static void filelist_free(filelist_t *flistp);
224 static error_t list2file(char *root, char *tmp,
225     char *final, line_t *start);
226 static error_t list_entry(menu_t *mp, char *menu_path, char *opt);
227 static error_t delete_all_entries(menu_t *mp, char *menu_path, char *opt);
228 static error_t update_entry(menu_t *mp, char *root, char *opt);
229 static error_t update_temp(menu_t *mp, char *root, char *opt);
230 
231 static error_t update_archive(char *root, char *opt);
232 static error_t list_archive(char *root, char *opt);
233 static error_t update_all(char *root, char *opt);
234 static error_t read_list(char *root, filelist_t  *flistp);
235 static error_t set_global(menu_t *mp, char *globalcmd, int val);
236 static error_t set_option(menu_t *mp, char *globalcmd, char *opt);
237 static error_t set_kernel(menu_t *mp, menu_cmd_t optnum, char *path,
238     char *buf, size_t bufsize);
239 static char *expand_path(const char *partial_path);
240 
241 static long s_strtol(char *str);
242 static int s_fputs(char *str, FILE *fp);
243 
244 static char *s_strdup(char *str);
245 static int is_readonly(char *);
246 static int is_amd64(void);
247 static void append_to_flist(filelist_t *, char *);
248 
249 #if defined(__sparc)
250 static void sparc_abort(void);
251 #endif
252 
253 #if defined(__i386)
254 static void ucode_install();
255 #endif
256 
257 /* Menu related sub commands */
258 static subcmd_defn_t menu_subcmds[] = {
259 	"set_option",		OPT_OPTIONAL,	set_option, 0,	/* PUB */
260 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
261 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
262 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
263 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
264 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
265 	NULL,			0,		NULL, 0	/* must be last */
266 };
267 
268 /* Archive related sub commands */
269 static subcmd_defn_t arch_subcmds[] = {
270 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
271 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
272 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
273 	NULL,			0,		NULL, 0	/* must be last */
274 };
275 
276 static struct {
277 	nvlist_t *new_nvlp;
278 	nvlist_t *old_nvlp;
279 	int need_update;
280 } walk_arg;
281 
282 
283 struct safefile {
284 	char *name;
285 	struct safefile *next;
286 };
287 
288 static struct safefile *safefiles = NULL;
289 #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
290 
291 static void
292 usage(void)
293 {
294 	(void) fprintf(stderr, "USAGE:\n");
295 
296 
297 	/* archive usage */
298 	(void) fprintf(stderr, "\t%s update-archive [-vn] [-R altroot]\n",
299 	    prog);
300 	(void) fprintf(stderr, "\t%s list-archive [-R altroot]\n", prog);
301 #ifndef __sparc
302 	/* x86 only */
303 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
304 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
305 #endif
306 }
307 
308 int
309 main(int argc, char *argv[])
310 {
311 	error_t ret;
312 
313 	(void) setlocale(LC_ALL, "");
314 	(void) textdomain(TEXT_DOMAIN);
315 
316 	if ((prog = strrchr(argv[0], '/')) == NULL) {
317 		prog = argv[0];
318 	} else {
319 		prog++;
320 	}
321 
322 
323 	/*
324 	 * Don't depend on caller's umask
325 	 */
326 	(void) umask(0022);
327 
328 	parse_args(argc, argv);
329 
330 #if defined(__sparc)
331 	/*
332 	 * There are only two valid invocations of bootadm
333 	 * on SPARC:
334 	 *
335 	 *	- SPARC diskless server creating boot_archive for i386 clients
336 	 *	- archive creation call during reboot of a SPARC system
337 	 *
338 	 *	The latter should be a NOP
339 	 */
340 	if (bam_cmd != BAM_ARCHIVE) {
341 		sparc_abort();
342 	}
343 #endif
344 
345 	switch (bam_cmd) {
346 		case BAM_MENU:
347 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
348 			break;
349 		case BAM_ARCHIVE:
350 			ret = bam_archive(bam_subcmd, bam_opt);
351 			break;
352 		default:
353 			usage();
354 			bam_exit(1);
355 	}
356 
357 	if (ret != BAM_SUCCESS)
358 		bam_exit(1);
359 
360 	bam_unlock();
361 	return (0);
362 }
363 
364 #if defined(__sparc)
365 
366 static void
367 sparc_abort(void)
368 {
369 	bam_error(NOT_ON_SPARC);
370 	bam_exit(1);
371 }
372 
373 #endif
374 
375 /*
376  * Equivalence of public and internal commands:
377  *	update-archive  -- -a update
378  *	list-archive	-- -a list
379  *	set-menu	-- -m set_option
380  *	list-menu	-- -m list_entry
381  *	update-menu	-- -m update_entry
382  */
383 static struct cmd_map {
384 	char *bam_cmdname;
385 	int bam_cmd;
386 	char *bam_subcmd;
387 } cmd_map[] = {
388 	{ "update-archive",	BAM_ARCHIVE,	"update"},
389 	{ "list-archive",	BAM_ARCHIVE,	"list"},
390 	{ "set-menu",		BAM_MENU,	"set_option"},
391 	{ "list-menu",		BAM_MENU,	"list_entry"},
392 	{ "update-menu",	BAM_MENU,	"update_entry"},
393 	{ NULL,			0,		NULL}
394 };
395 
396 /*
397  * Commands syntax published in bootadm(1M) are parsed here
398  */
399 static void
400 parse_args(int argc, char *argv[])
401 {
402 	struct cmd_map *cmp = cmd_map;
403 
404 	/* command conforming to the final spec */
405 	if (argc > 1 && argv[1][0] != '-') {
406 		/*
407 		 * Map commands to internal table.
408 		 */
409 		while (cmp->bam_cmdname) {
410 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
411 				bam_cmd = cmp->bam_cmd;
412 				bam_subcmd = cmp->bam_subcmd;
413 				break;
414 			}
415 			cmp++;
416 		}
417 		if (cmp->bam_cmdname == NULL) {
418 			usage();
419 			bam_exit(1);
420 		}
421 		argc--;
422 		argv++;
423 	}
424 
425 	parse_args_internal(argc, argv);
426 }
427 
428 /*
429  * A combination of public and private commands are parsed here.
430  * The internal syntax and the corresponding functionality are:
431  *	-a update	-- update-archive
432  *	-a list		-- list-archive
433  *	-a update-all	-- (reboot to sync all mounted OS archive)
434  *	-m update_entry	-- update-menu
435  *	-m list_entry	-- list-menu
436  *	-m update_temp	-- (reboot -- [boot-args])
437  *	-m delete_all_entries -- (called from install)
438  */
439 static void
440 parse_args_internal(int argc, char *argv[])
441 {
442 	int c, error;
443 	extern char *optarg;
444 	extern int optind, opterr;
445 
446 	/* Suppress error message from getopt */
447 	opterr = 0;
448 
449 	error = 0;
450 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:")) != -1) {
451 		switch (c) {
452 		case 'a':
453 			if (bam_cmd) {
454 				error = 1;
455 				bam_error(MULT_CMDS, c);
456 			}
457 			bam_cmd = BAM_ARCHIVE;
458 			bam_subcmd = optarg;
459 			break;
460 		case 'd':
461 			if (bam_debug) {
462 				error = 1;
463 				bam_error(DUP_OPT, c);
464 			}
465 			bam_debug = s_strtol(optarg);
466 			break;
467 		case 'f':
468 			if (bam_force) {
469 				error = 1;
470 				bam_error(DUP_OPT, c);
471 			}
472 			bam_force = 1;
473 			break;
474 		case 'm':
475 			if (bam_cmd) {
476 				error = 1;
477 				bam_error(MULT_CMDS, c);
478 			}
479 			bam_cmd = BAM_MENU;
480 			bam_subcmd = optarg;
481 			break;
482 		case 'n':
483 			if (bam_check) {
484 				error = 1;
485 				bam_error(DUP_OPT, c);
486 			}
487 			bam_check = 1;
488 			break;
489 		case 'o':
490 			if (bam_opt) {
491 				error = 1;
492 				bam_error(DUP_OPT, c);
493 			}
494 			bam_opt = optarg;
495 			break;
496 		case 'v':
497 			if (bam_verbose) {
498 				error = 1;
499 				bam_error(DUP_OPT, c);
500 			}
501 			bam_verbose = 1;
502 			break;
503 		case 'C':
504 			bam_smf_check = 1;
505 			break;
506 		case 'R':
507 			if (bam_root) {
508 				error = 1;
509 				bam_error(DUP_OPT, c);
510 				break;
511 			} else if (realpath(optarg, rootbuf) == NULL) {
512 				error = 1;
513 				bam_error(CANT_RESOLVE, optarg,
514 				    strerror(errno));
515 				break;
516 			}
517 			bam_alt_root = 1;
518 			bam_root = rootbuf;
519 			bam_rootlen = strlen(rootbuf);
520 			break;
521 		case '?':
522 			error = 1;
523 			bam_error(BAD_OPT, optopt);
524 			break;
525 		default :
526 			error = 1;
527 			bam_error(BAD_OPT, c);
528 			break;
529 		}
530 	}
531 
532 	/*
533 	 * A command option must be specfied
534 	 */
535 	if (!bam_cmd) {
536 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
537 			usage();
538 			bam_exit(0);
539 		}
540 		bam_error(NEED_CMD);
541 		error = 1;
542 	}
543 
544 	if (error) {
545 		usage();
546 		bam_exit(1);
547 	}
548 
549 	if (optind > argc) {
550 		bam_error(INT_ERROR, "parse_args");
551 		bam_exit(1);
552 	} else if (optind < argc) {
553 		bam_argv = &argv[optind];
554 		bam_argc = argc - optind;
555 	}
556 
557 	/*
558 	 * -n implies verbose mode
559 	 */
560 	if (bam_check)
561 		bam_verbose = 1;
562 }
563 
564 static error_t
565 check_subcmd_and_options(
566 	char *subcmd,
567 	char *opt,
568 	subcmd_defn_t *table,
569 	error_t (**fp)())
570 {
571 	int i;
572 
573 	if (subcmd == NULL) {
574 		bam_error(NEED_SUBCMD);
575 		return (BAM_ERROR);
576 	}
577 
578 	if (bam_argc != 0 || bam_argv) {
579 		if (strcmp(subcmd, "set_option") != 0 || bam_argc != 1) {
580 			bam_error(TRAILING_ARGS);
581 			usage();
582 			return (BAM_ERROR);
583 		}
584 	}
585 
586 	if (bam_root == NULL) {
587 		bam_root = rootbuf;
588 		bam_rootlen = 1;
589 	}
590 
591 	/* verify that subcmd is valid */
592 	for (i = 0; table[i].subcmd != NULL; i++) {
593 		if (strcmp(table[i].subcmd, subcmd) == 0)
594 			break;
595 	}
596 
597 	if (table[i].subcmd == NULL) {
598 		bam_error(INVALID_SUBCMD, subcmd);
599 		return (BAM_ERROR);
600 	}
601 
602 	if (table[i].unpriv == 0 && geteuid() != 0) {
603 		bam_error(MUST_BE_ROOT);
604 		return (BAM_ERROR);
605 	}
606 
607 	/*
608 	 * Currently only privileged commands need a lock
609 	 */
610 	if (table[i].unpriv == 0)
611 		bam_lock();
612 
613 	/* subcmd verifies that opt is appropriate */
614 	if (table[i].option != OPT_OPTIONAL) {
615 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
616 			if (opt)
617 				bam_error(NO_OPT_REQ, subcmd);
618 			else
619 				bam_error(MISS_OPT, subcmd);
620 			return (BAM_ERROR);
621 		}
622 	}
623 
624 	*fp = table[i].handler;
625 
626 	return (BAM_SUCCESS);
627 }
628 
629 
630 static char *
631 mount_grub_slice(int *mnted, char **physlice, char **logslice, char **fs_type)
632 {
633 	struct extmnttab mnt;
634 	struct stat sb;
635 	char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32];
636 	char cmd[PATH_MAX];
637 	char *mntpt;
638 	int p, l, f;
639 	FILE *fp;
640 
641 	assert(mnted);
642 	*mnted = 0;
643 
644 	/*
645 	 * physlice, logslice, fs_type  args may be NULL
646 	 */
647 	if (physlice)
648 		*physlice = NULL;
649 	if (logslice)
650 		*logslice = NULL;
651 	if (fs_type)
652 		*fs_type = NULL;
653 
654 	if (stat(GRUB_slice, &sb) != 0) {
655 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
656 		return (NULL);
657 	}
658 
659 	fp = fopen(GRUB_slice, "r");
660 	if (fp == NULL) {
661 		bam_error(OPEN_FAIL, GRUB_slice, strerror(errno));
662 		return (NULL);
663 	}
664 
665 	dev[0] = fstype[0] = phys[0] = '\0';
666 	p = sizeof ("PHYS_SLICE=") - 1;
667 	l = sizeof ("LOG_SLICE=") - 1;
668 	f = sizeof ("LOG_FSTYP=") - 1;
669 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
670 		if (strncmp(buf, "PHYS_SLICE=", p) == 0) {
671 			(void) strlcpy(phys, buf + p, sizeof (phys));
672 			continue;
673 		}
674 		if (strncmp(buf, "LOG_SLICE=", l) == 0) {
675 			(void) strlcpy(dev, buf + l, sizeof (dev));
676 			continue;
677 		}
678 		if (strncmp(buf, "LOG_FSTYP=", f) == 0) {
679 			(void) strlcpy(fstype, buf + f, sizeof (fstype));
680 			continue;
681 		}
682 	}
683 	(void) fclose(fp);
684 
685 	if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') {
686 		bam_error(BAD_SLICE_FILE, GRUB_slice);
687 		return (NULL);
688 	}
689 
690 	if (physlice) {
691 		*physlice = s_strdup(phys);
692 	}
693 	if (logslice) {
694 		*logslice = s_strdup(dev);
695 	}
696 	if (fs_type) {
697 		*fs_type = s_strdup(fstype);
698 	}
699 
700 	/*
701 	 * Check if the slice is already mounted
702 	 */
703 	fp = fopen(MNTTAB, "r");
704 	if (fp == NULL) {
705 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
706 		goto error;
707 	}
708 
709 	resetmnttab(fp);
710 
711 	mntpt = NULL;
712 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
713 		if (strcmp(mnt.mnt_special, dev) == 0) {
714 			mntpt = s_strdup(mnt.mnt_mountp);
715 			break;
716 		}
717 	}
718 
719 	(void) fclose(fp);
720 
721 	if (mntpt) {
722 		return (mntpt);
723 	}
724 
725 
726 	/*
727 	 * GRUB slice is not mounted, we need to mount it now.
728 	 * First create the mountpoint
729 	 */
730 	mntpt = s_calloc(1, PATH_MAX);
731 	(void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid());
732 	if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) {
733 		bam_error(MKDIR_FAILED, mntpt, strerror(errno));
734 		free(mntpt);
735 		goto error;
736 	}
737 
738 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount -F %s %s %s",
739 	    fstype, dev, mntpt);
740 
741 	if (exec_cmd(cmd, NULL, 0) != 0) {
742 		bam_error(MOUNT_FAILED, dev, fstype);
743 		if (rmdir(mntpt) != 0) {
744 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
745 		}
746 		free(mntpt);
747 		goto error;
748 	}
749 
750 	*mnted = 1;
751 	return (mntpt);
752 
753 error:
754 	if (physlice) {
755 		free(*physlice);
756 		*physlice = NULL;
757 	}
758 	if (logslice) {
759 		free(*logslice);
760 		*logslice = NULL;
761 	}
762 	if (fs_type) {
763 		free(*fs_type);
764 		*fs_type = NULL;
765 	}
766 	return (NULL);
767 }
768 
769 static void
770 umount_grub_slice(
771 	int mnted,
772 	char *mntpt,
773 	char *physlice,
774 	char *logslice,
775 	char *fs_type)
776 {
777 	char cmd[PATH_MAX];
778 
779 	/*
780 	 * If we have not dealt with GRUB slice
781 	 * we have nothing to do - just return.
782 	 */
783 	if (mntpt == NULL)
784 		return;
785 
786 
787 	/*
788 	 * If we mounted the filesystem earlier in mount_grub_slice()
789 	 * unmount it now.
790 	 */
791 	if (mnted) {
792 		(void) snprintf(cmd, sizeof (cmd), "/sbin/umount %s",
793 		    mntpt);
794 		if (exec_cmd(cmd, NULL, 0) != 0) {
795 			bam_error(UMOUNT_FAILED, mntpt);
796 		}
797 		if (rmdir(mntpt) != 0) {
798 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
799 		}
800 	}
801 
802 	if (physlice)
803 		free(physlice);
804 	if (logslice)
805 		free(logslice);
806 	if (fs_type)
807 		free(fs_type);
808 
809 	free(mntpt);
810 }
811 
812 static char *
813 use_stubboot(void)
814 {
815 	int mnted;
816 	struct stat sb;
817 	struct extmnttab mnt;
818 	FILE *fp;
819 	char cmd[PATH_MAX];
820 
821 	if (stat(STUBBOOT, &sb) != 0) {
822 		bam_error(STUBBOOT_DIR_NOT_FOUND);
823 		return (NULL);
824 	}
825 
826 	/*
827 	 * Check if stubboot is mounted. If not, mount it
828 	 */
829 	fp = fopen(MNTTAB, "r");
830 	if (fp == NULL) {
831 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
832 		return (NULL);
833 	}
834 
835 	resetmnttab(fp);
836 
837 	mnted = 0;
838 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
839 		if (strcmp(mnt.mnt_mountp, STUBBOOT) == 0) {
840 			mnted = 1;
841 			break;
842 		}
843 	}
844 
845 	(void) fclose(fp);
846 
847 	if (mnted)
848 		return (STUBBOOT);
849 
850 	/*
851 	 * Stubboot is not mounted, mount it now.
852 	 * It should exist in /etc/vfstab
853 	 */
854 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount %s",
855 	    STUBBOOT);
856 	if (exec_cmd(cmd, NULL, 0) != 0) {
857 		bam_error(MOUNT_MNTPT_FAILED, STUBBOOT);
858 		return (NULL);
859 	}
860 
861 	return (STUBBOOT);
862 }
863 
864 static void
865 disp_active_menu_locn(char *menu_path, char *logslice, char *fstype, int mnted)
866 {
867 	/*
868 	 * Check if we did a temp mount of an unmounted device.
869 	 * If yes, print the block device and fstype for that device
870 	 * else it is already mounted, so we print the path to the GRUB menu.
871 	 */
872 	if (mnted) {
873 		bam_print(GRUB_MENU_DEVICE, logslice);
874 		bam_print(GRUB_MENU_FSTYPE, fstype);
875 	} else {
876 		bam_print(GRUB_MENU_PATH, menu_path);
877 	}
878 }
879 
880 /*
881  * NOTE: A single "/" is also considered a trailing slash and will
882  * be deleted.
883  */
884 static void
885 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
886 {
887 	size_t dstlen;
888 
889 	assert(src);
890 	assert(dst);
891 
892 	(void) strlcpy(dst, src, dstsize);
893 
894 	dstlen = strlen(dst);
895 	if (dst[dstlen - 1] == '/') {
896 		dst[dstlen - 1] = '\0';
897 	}
898 }
899 
900 static error_t
901 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
902 {
903 	error_t ret;
904 	char menu_path[PATH_MAX];
905 	char path[PATH_MAX];
906 	menu_t *menu;
907 	char *mntpt, *menu_root, *logslice, *fstype;
908 	struct stat sb;
909 	int mnted;	/* set if we did a mount */
910 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
911 
912 	/*
913 	 * Check arguments
914 	 */
915 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
916 	if (ret == BAM_ERROR) {
917 		return (BAM_ERROR);
918 	}
919 
920 	mntpt = NULL;
921 	mnted = 0;
922 	logslice = fstype = NULL;
923 
924 	/*
925 	 * Check for the menu.list file:
926 	 *
927 	 * 1. Check for a GRUB_slice file, be it on / or
928 	 *    on the user-provided alternate root.
929 	 * 2. Use the alternate root, if given.
930 	 * 3. Check /stubboot
931 	 * 4. Use /
932 	 */
933 	if (bam_alt_root) {
934 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
935 		    GRUB_slice);
936 	} else {
937 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
938 	}
939 
940 	if (stat(path, &sb) == 0) {
941 		mntpt = mount_grub_slice(&mnted, NULL, &logslice, &fstype);
942 		menu_root = mntpt;
943 	} else if (bam_alt_root) {
944 		menu_root = bam_root;
945 	} else if (stat(STUBBOOT, &sb) == 0) {
946 		menu_root = use_stubboot();
947 	} else {
948 		menu_root = bam_root;
949 	}
950 
951 	if (menu_root == NULL) {
952 		bam_error(CANNOT_LOCATE_GRUB_MENU);
953 		return (BAM_ERROR);
954 	}
955 
956 	elide_trailing_slash(menu_root, menu_path, sizeof (menu_path));
957 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
958 
959 	/*
960 	 * If listing the menu, display the active menu
961 	 * location
962 	 */
963 	if (strcmp(subcmd, "list_entry") == 0) {
964 		disp_active_menu_locn(menu_path, logslice, fstype, mnted);
965 	}
966 
967 	menu = menu_read(menu_path);
968 	assert(menu);
969 
970 	/*
971 	 * Special handling for setting timeout and default
972 	 */
973 	if (strcmp(subcmd, "set_option") == 0) {
974 		if (largc != 1 || largv[0] == NULL) {
975 			usage();
976 			menu_free(menu);
977 			umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
978 			return (BAM_ERROR);
979 		}
980 		opt = largv[0];
981 	} else if (largc != 0) {
982 		usage();
983 		menu_free(menu);
984 		umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
985 		return (BAM_ERROR);
986 	}
987 
988 	ret = dboot_or_multiboot(bam_root);
989 	if (ret != BAM_SUCCESS)
990 		return (ret);
991 
992 	/*
993 	 * Once the sub-cmd handler has run
994 	 * only the line field is guaranteed to have valid values
995 	 */
996 	if ((strcmp(subcmd, "update_entry") == 0) ||
997 	    (strcmp(subcmd, "upgrade") == 0))
998 		ret = f(menu, bam_root, opt);
999 	else
1000 		ret = f(menu, menu_path, opt);
1001 	if (ret == BAM_WRITE) {
1002 		ret = menu_write(menu_root, menu);
1003 	}
1004 
1005 	menu_free(menu);
1006 
1007 	umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
1008 
1009 	return (ret);
1010 }
1011 
1012 
1013 static error_t
1014 bam_archive(
1015 	char *subcmd,
1016 	char *opt)
1017 {
1018 	error_t ret;
1019 	error_t (*f)(char *root, char *opt);
1020 
1021 	/*
1022 	 * Add trailing / for archive subcommands
1023 	 */
1024 	if (rootbuf[strlen(rootbuf) - 1] != '/')
1025 		(void) strcat(rootbuf, "/");
1026 	bam_rootlen = strlen(rootbuf);
1027 
1028 	/*
1029 	 * Check arguments
1030 	 */
1031 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1032 	if (ret != BAM_SUCCESS) {
1033 		return (BAM_ERROR);
1034 	}
1035 
1036 #if defined(__sparc)
1037 	/*
1038 	 * A NOP if called on SPARC during reboot
1039 	 */
1040 	if (strcmp(subcmd, "update_all") == 0)
1041 		return (BAM_SUCCESS);
1042 	else if (strcmp(subcmd, "update") != 0)
1043 		sparc_abort();
1044 #endif
1045 
1046 	ret = dboot_or_multiboot(rootbuf);
1047 	if (ret != BAM_SUCCESS)
1048 		return (ret);
1049 
1050 	/*
1051 	 * Check archive not supported with update_all
1052 	 * since it is awkward to display out-of-sync
1053 	 * information for each BE.
1054 	 */
1055 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
1056 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
1057 		return (BAM_ERROR);
1058 	}
1059 
1060 	if (strcmp(subcmd, "update_all") == 0)
1061 		bam_update_all = 1;
1062 
1063 #if defined(__i386)
1064 	ucode_install(bam_root);
1065 #endif
1066 
1067 	ret = f(bam_root, opt);
1068 
1069 	bam_update_all = 0;
1070 
1071 	return (ret);
1072 }
1073 
1074 /*PRINTFLIKE1*/
1075 void
1076 bam_error(char *format, ...)
1077 {
1078 	va_list ap;
1079 
1080 	va_start(ap, format);
1081 	(void) fprintf(stderr, "%s: ", prog);
1082 	(void) vfprintf(stderr, format, ap);
1083 	va_end(ap);
1084 }
1085 
1086 /*PRINTFLIKE1*/
1087 static void
1088 bam_print(char *format, ...)
1089 {
1090 	va_list ap;
1091 
1092 	va_start(ap, format);
1093 	(void) vfprintf(stdout, format, ap);
1094 	va_end(ap);
1095 }
1096 
1097 /*PRINTFLIKE1*/
1098 void
1099 bam_print_stderr(char *format, ...)
1100 {
1101 	va_list ap;
1102 
1103 	va_start(ap, format);
1104 	(void) vfprintf(stderr, format, ap);
1105 	va_end(ap);
1106 }
1107 
1108 static void
1109 bam_exit(int excode)
1110 {
1111 	bam_unlock();
1112 	exit(excode);
1113 }
1114 
1115 static void
1116 bam_lock(void)
1117 {
1118 	struct flock lock;
1119 	pid_t pid;
1120 
1121 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1122 	if (bam_lock_fd < 0) {
1123 		/*
1124 		 * We may be invoked early in boot for archive verification.
1125 		 * In this case, root is readonly and /var/run may not exist.
1126 		 * Proceed without the lock
1127 		 */
1128 		if (errno == EROFS || errno == ENOENT) {
1129 			bam_root_readonly = 1;
1130 			return;
1131 		}
1132 
1133 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
1134 		bam_exit(1);
1135 	}
1136 
1137 	lock.l_type = F_WRLCK;
1138 	lock.l_whence = SEEK_SET;
1139 	lock.l_start = 0;
1140 	lock.l_len = 0;
1141 
1142 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1143 		if (errno != EACCES && errno != EAGAIN) {
1144 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1145 			(void) close(bam_lock_fd);
1146 			bam_lock_fd = -1;
1147 			bam_exit(1);
1148 		}
1149 		pid = 0;
1150 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1151 		bam_print(FILE_LOCKED, pid);
1152 
1153 		lock.l_type = F_WRLCK;
1154 		lock.l_whence = SEEK_SET;
1155 		lock.l_start = 0;
1156 		lock.l_len = 0;
1157 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1158 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1159 			(void) close(bam_lock_fd);
1160 			bam_lock_fd = -1;
1161 			bam_exit(1);
1162 		}
1163 	}
1164 
1165 	/* We own the lock now */
1166 	pid = getpid();
1167 	(void) write(bam_lock_fd, &pid, sizeof (pid));
1168 }
1169 
1170 static void
1171 bam_unlock(void)
1172 {
1173 	struct flock unlock;
1174 
1175 	/*
1176 	 * NOP if we don't hold the lock
1177 	 */
1178 	if (bam_lock_fd < 0) {
1179 		return;
1180 	}
1181 
1182 	unlock.l_type = F_UNLCK;
1183 	unlock.l_whence = SEEK_SET;
1184 	unlock.l_start = 0;
1185 	unlock.l_len = 0;
1186 
1187 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1188 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1189 	}
1190 
1191 	if (close(bam_lock_fd) == -1) {
1192 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
1193 	}
1194 	bam_lock_fd = -1;
1195 }
1196 
1197 static error_t
1198 list_archive(char *root, char *opt)
1199 {
1200 	filelist_t flist;
1201 	filelist_t *flistp = &flist;
1202 	line_t *lp;
1203 
1204 	assert(root);
1205 	assert(opt == NULL);
1206 
1207 	flistp->head = flistp->tail = NULL;
1208 	if (read_list(root, flistp) != BAM_SUCCESS) {
1209 		return (BAM_ERROR);
1210 	}
1211 	assert(flistp->head && flistp->tail);
1212 
1213 	for (lp = flistp->head; lp; lp = lp->next) {
1214 		bam_print(PRINT, lp->line);
1215 	}
1216 
1217 	filelist_free(flistp);
1218 
1219 	return (BAM_SUCCESS);
1220 }
1221 
1222 /*
1223  * This routine writes a list of lines to a file.
1224  * The list is *not* freed
1225  */
1226 static error_t
1227 list2file(char *root, char *tmp, char *final, line_t *start)
1228 {
1229 	char tmpfile[PATH_MAX];
1230 	char path[PATH_MAX];
1231 	FILE *fp;
1232 	int ret;
1233 	struct stat sb;
1234 	mode_t mode;
1235 	uid_t root_uid;
1236 	gid_t sys_gid;
1237 	struct passwd *pw;
1238 	struct group *gp;
1239 
1240 
1241 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
1242 
1243 	if (start == NULL) {
1244 		if (stat(path, &sb) != -1) {
1245 			bam_print(UNLINK_EMPTY, path);
1246 			if (unlink(path) != 0) {
1247 				bam_error(UNLINK_FAIL, path, strerror(errno));
1248 				return (BAM_ERROR);
1249 			} else {
1250 				return (BAM_SUCCESS);
1251 			}
1252 		}
1253 	}
1254 
1255 	/*
1256 	 * Preserve attributes of existing file if possible,
1257 	 * otherwise ask the system for uid/gid of root/sys.
1258 	 * If all fails, fall back on hard-coded defaults.
1259 	 */
1260 	if (stat(path, &sb) != -1) {
1261 		mode = sb.st_mode;
1262 		root_uid = sb.st_uid;
1263 		sys_gid = sb.st_gid;
1264 	} else {
1265 		mode = DEFAULT_DEV_MODE;
1266 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1267 			root_uid = pw->pw_uid;
1268 		} else {
1269 			if (bam_verbose)
1270 				bam_error(CANT_FIND_USER,
1271 				    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1272 			root_uid = (uid_t)DEFAULT_DEV_UID;
1273 		}
1274 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1275 			sys_gid = gp->gr_gid;
1276 		} else {
1277 			if (bam_verbose)
1278 				bam_error(CANT_FIND_GROUP,
1279 				    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1280 			sys_gid = (gid_t)DEFAULT_DEV_GID;
1281 		}
1282 	}
1283 
1284 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1285 
1286 	/* Truncate tmpfile first */
1287 	fp = fopen(tmpfile, "w");
1288 	if (fp == NULL) {
1289 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1290 		return (BAM_ERROR);
1291 	}
1292 	ret = fclose(fp);
1293 	if (ret == EOF) {
1294 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1295 		return (BAM_ERROR);
1296 	}
1297 
1298 	/* Now open it in append mode */
1299 	fp = fopen(tmpfile, "a");
1300 	if (fp == NULL) {
1301 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1302 		return (BAM_ERROR);
1303 	}
1304 
1305 	for (; start; start = start->next) {
1306 		ret = s_fputs(start->line, fp);
1307 		if (ret == EOF) {
1308 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
1309 			(void) fclose(fp);
1310 			return (BAM_ERROR);
1311 		}
1312 	}
1313 
1314 	ret = fclose(fp);
1315 	if (ret == EOF) {
1316 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1317 		return (BAM_ERROR);
1318 	}
1319 
1320 	/*
1321 	 * Set up desired attributes.  Ignore failures on filesystems
1322 	 * not supporting these operations - pcfs reports unsupported
1323 	 * operations as EINVAL.
1324 	 */
1325 	ret = chmod(tmpfile, mode);
1326 	if (ret == -1 &&
1327 	    errno != EINVAL && errno != ENOTSUP) {
1328 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
1329 		return (BAM_ERROR);
1330 	}
1331 
1332 	ret = chown(tmpfile, root_uid, sys_gid);
1333 	if (ret == -1 &&
1334 	    errno != EINVAL && errno != ENOTSUP) {
1335 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
1336 		return (BAM_ERROR);
1337 	}
1338 
1339 
1340 	/*
1341 	 * Do an atomic rename
1342 	 */
1343 	ret = rename(tmpfile, path);
1344 	if (ret != 0) {
1345 		bam_error(RENAME_FAIL, path, strerror(errno));
1346 		return (BAM_ERROR);
1347 	}
1348 
1349 	return (BAM_SUCCESS);
1350 }
1351 
1352 /*
1353  * This function should always return 0 - since we want
1354  * to create stat data for *all* files in the list.
1355  */
1356 /*ARGSUSED*/
1357 static int
1358 cmpstat(
1359 	const char *file,
1360 	const struct stat *stat,
1361 	int flags,
1362 	struct FTW *ftw)
1363 {
1364 	uint_t sz;
1365 	uint64_t *value;
1366 	uint64_t filestat[2];
1367 	int error;
1368 
1369 	struct safefile *safefilep;
1370 	FILE *fp;
1371 
1372 	/*
1373 	 * We only want regular files
1374 	 */
1375 	if (!S_ISREG(stat->st_mode))
1376 		return (0);
1377 
1378 	/*
1379 	 * new_nvlp may be NULL if there were errors earlier
1380 	 * but this is not fatal to update determination.
1381 	 */
1382 	if (walk_arg.new_nvlp) {
1383 		filestat[0] = stat->st_size;
1384 		filestat[1] = stat->st_mtime;
1385 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
1386 		    file + bam_rootlen, filestat, 2);
1387 		if (error)
1388 			bam_error(NVADD_FAIL, file, strerror(error));
1389 	}
1390 
1391 	/*
1392 	 * The remaining steps are only required if we haven't made a
1393 	 * decision about update or if we are checking (-n)
1394 	 */
1395 	if (walk_arg.need_update && !bam_check)
1396 		return (0);
1397 
1398 	/*
1399 	 * If we are invoked as part of system/filesyste/boot-archive, then
1400 	 * there are a number of things we should not worry about
1401 	 */
1402 	if (bam_smf_check) {
1403 		/* ignore amd64 modules unless we are booted amd64. */
1404 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
1405 			return (0);
1406 
1407 		/* read in list of safe files */
1408 		if (safefiles == NULL)
1409 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
1410 				safefiles = s_calloc(1,
1411 				    sizeof (struct safefile));
1412 				safefilep = safefiles;
1413 				safefilep->name = s_calloc(1, MAXPATHLEN +
1414 				    MAXNAMELEN);
1415 				safefilep->next = NULL;
1416 				while (s_fgets(safefilep->name, MAXPATHLEN +
1417 				    MAXNAMELEN, fp) != NULL) {
1418 					safefilep->next = s_calloc(1,
1419 					    sizeof (struct safefile));
1420 					safefilep = safefilep->next;
1421 					safefilep->name = s_calloc(1,
1422 					    MAXPATHLEN + MAXNAMELEN);
1423 					safefilep->next = NULL;
1424 				}
1425 				(void) fclose(fp);
1426 			}
1427 	}
1428 
1429 	/*
1430 	 * We need an update if file doesn't exist in old archive
1431 	 */
1432 	if (walk_arg.old_nvlp == NULL ||
1433 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
1434 	    file + bam_rootlen, &value, &sz) != 0) {
1435 		if (bam_smf_check)	/* ignore new during smf check */
1436 			return (0);
1437 		walk_arg.need_update = 1;
1438 		if (bam_verbose)
1439 			bam_print(PARSEABLE_NEW_FILE, file);
1440 		return (0);
1441 	}
1442 
1443 	/*
1444 	 * File exists in old archive. Check if file has changed
1445 	 */
1446 	assert(sz == 2);
1447 	bcopy(value, filestat, sizeof (filestat));
1448 
1449 	if (filestat[0] != stat->st_size ||
1450 	    filestat[1] != stat->st_mtime) {
1451 		if (bam_smf_check) {
1452 			safefilep = safefiles;
1453 			while (safefilep != NULL) {
1454 				if (strcmp(file + bam_rootlen,
1455 				    safefilep->name) == 0) {
1456 					(void) creat(NEED_UPDATE_FILE, 0644);
1457 					return (0);
1458 				}
1459 				safefilep = safefilep->next;
1460 			}
1461 		}
1462 		walk_arg.need_update = 1;
1463 		if (bam_verbose)
1464 			if (bam_smf_check)
1465 				bam_print("    %s\n", file);
1466 			else
1467 				bam_print(PARSEABLE_OUT_DATE, file);
1468 	}
1469 
1470 	return (0);
1471 }
1472 
1473 /*
1474  * Check flags and presence of required files.
1475  * The force flag and/or absence of files should
1476  * trigger an update.
1477  * Suppress stdout output if check (-n) option is set
1478  * (as -n should only produce parseable output.)
1479  */
1480 static void
1481 check_flags_and_files(char *root)
1482 {
1483 	char path[PATH_MAX];
1484 	struct stat sb;
1485 
1486 	/*
1487 	 * if force, create archive unconditionally
1488 	 */
1489 	if (bam_force) {
1490 		walk_arg.need_update = 1;
1491 		if (bam_verbose && !bam_check)
1492 			bam_print(UPDATE_FORCE);
1493 		return;
1494 	}
1495 
1496 	/*
1497 	 * If archive is missing, create archive
1498 	 */
1499 	(void) snprintf(path, sizeof (path), "%s%s", root,
1500 	    DIRECT_BOOT_ARCHIVE_32);
1501 	if (stat(path, &sb) != 0) {
1502 		if (bam_verbose && !bam_check)
1503 			bam_print(UPDATE_ARCH_MISS, path);
1504 		walk_arg.need_update = 1;
1505 		return;
1506 	}
1507 	if (bam_direct == BAM_DIRECT_DBOOT) {
1508 		(void) snprintf(path, sizeof (path), "%s%s", root,
1509 		    DIRECT_BOOT_ARCHIVE_64);
1510 		if (stat(path, &sb) != 0) {
1511 			if (bam_verbose && !bam_check)
1512 				bam_print(UPDATE_ARCH_MISS, path);
1513 			walk_arg.need_update = 1;
1514 			return;
1515 		}
1516 	}
1517 }
1518 
1519 static error_t
1520 read_one_list(char *root, filelist_t  *flistp, char *filelist)
1521 {
1522 	char path[PATH_MAX];
1523 	FILE *fp;
1524 	char buf[BAM_MAXLINE];
1525 
1526 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
1527 
1528 	fp = fopen(path, "r");
1529 	if (fp == NULL) {
1530 		if (bam_debug)
1531 			bam_error(FLIST_FAIL, path, strerror(errno));
1532 		return (BAM_ERROR);
1533 	}
1534 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1535 		/* skip blank lines */
1536 		if (strspn(buf, " \t") == strlen(buf))
1537 			continue;
1538 		append_to_flist(flistp, buf);
1539 	}
1540 	if (fclose(fp) != 0) {
1541 		bam_error(CLOSE_FAIL, path, strerror(errno));
1542 		return (BAM_ERROR);
1543 	}
1544 	return (BAM_SUCCESS);
1545 }
1546 
1547 static error_t
1548 read_list(char *root, filelist_t  *flistp)
1549 {
1550 	int rval;
1551 
1552 	flistp->head = flistp->tail = NULL;
1553 
1554 	/*
1555 	 * Read current lists of files - only the first is mandatory
1556 	 */
1557 	rval = read_one_list(root, flistp, BOOT_FILE_LIST);
1558 	if (rval != BAM_SUCCESS)
1559 		return (rval);
1560 	(void) read_one_list(root, flistp, ETC_FILE_LIST);
1561 
1562 	if (flistp->head == NULL) {
1563 		bam_error(NO_FLIST);
1564 		return (BAM_ERROR);
1565 	}
1566 
1567 	return (BAM_SUCCESS);
1568 }
1569 
1570 static void
1571 getoldstat(char *root)
1572 {
1573 	char path[PATH_MAX];
1574 	int fd, error;
1575 	struct stat sb;
1576 	char *ostat;
1577 
1578 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
1579 	fd = open(path, O_RDONLY);
1580 	if (fd == -1) {
1581 		if (bam_verbose)
1582 			bam_print(OPEN_FAIL, path, strerror(errno));
1583 		walk_arg.need_update = 1;
1584 		return;
1585 	}
1586 
1587 	if (fstat(fd, &sb) != 0) {
1588 		bam_error(STAT_FAIL, path, strerror(errno));
1589 		(void) close(fd);
1590 		walk_arg.need_update = 1;
1591 		return;
1592 	}
1593 
1594 	ostat = s_calloc(1, sb.st_size);
1595 
1596 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
1597 		bam_error(READ_FAIL, path, strerror(errno));
1598 		(void) close(fd);
1599 		free(ostat);
1600 		walk_arg.need_update = 1;
1601 		return;
1602 	}
1603 
1604 	(void) close(fd);
1605 
1606 	walk_arg.old_nvlp = NULL;
1607 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
1608 
1609 	free(ostat);
1610 
1611 	if (error) {
1612 		bam_error(UNPACK_FAIL, path, strerror(error));
1613 		walk_arg.old_nvlp = NULL;
1614 		walk_arg.need_update = 1;
1615 		return;
1616 	}
1617 }
1618 
1619 /*
1620  * Checks if a file in the current (old) archive has
1621  * been deleted from the root filesystem. This is needed for
1622  * software like Trusted Extensions (TX) that switch early
1623  * in boot based on presence/absence of a kernel module.
1624  */
1625 static void
1626 check4stale(char *root)
1627 {
1628 	nvpair_t	*nvp;
1629 	nvlist_t	*nvlp;
1630 	char 		*file;
1631 	char		path[PATH_MAX];
1632 	struct stat	sb;
1633 
1634 	/*
1635 	 * Skip stale file check during smf check
1636 	 */
1637 	if (bam_smf_check)
1638 		return;
1639 
1640 	/* Nothing to do if no old stats */
1641 	if ((nvlp = walk_arg.old_nvlp) == NULL)
1642 		return;
1643 
1644 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
1645 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
1646 		file = nvpair_name(nvp);
1647 		if (file == NULL)
1648 			continue;
1649 		(void) snprintf(path, sizeof (path), "%s/%s",
1650 		    root, file);
1651 		if (stat(path, &sb) == -1) {
1652 			walk_arg.need_update = 1;
1653 			if (bam_verbose)
1654 				bam_print(PARSEABLE_STALE_FILE, path);
1655 		}
1656 	}
1657 }
1658 
1659 static void
1660 create_newstat(void)
1661 {
1662 	int error;
1663 
1664 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
1665 	if (error) {
1666 		/*
1667 		 * Not fatal - we can still create archive
1668 		 */
1669 		walk_arg.new_nvlp = NULL;
1670 		bam_error(NVALLOC_FAIL, strerror(error));
1671 	}
1672 }
1673 
1674 static void
1675 walk_list(char *root, filelist_t *flistp)
1676 {
1677 	char path[PATH_MAX];
1678 	line_t *lp;
1679 
1680 	for (lp = flistp->head; lp; lp = lp->next) {
1681 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
1682 		/* XXX shouldn't we use FTW_MOUNT ? */
1683 		if (nftw(path, cmpstat, 20, 0) == -1) {
1684 			/*
1685 			 * Some files may not exist.
1686 			 * For example: etc/rtc_config on a x86 diskless system
1687 			 * Emit verbose message only
1688 			 */
1689 			if (bam_verbose)
1690 				bam_print(NFTW_FAIL, path, strerror(errno));
1691 		}
1692 	}
1693 }
1694 
1695 static void
1696 savenew(char *root)
1697 {
1698 	char path[PATH_MAX];
1699 	char path2[PATH_MAX];
1700 	size_t sz;
1701 	char *nstat;
1702 	int fd, wrote, error;
1703 
1704 	nstat = NULL;
1705 	sz = 0;
1706 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
1707 	    NV_ENCODE_XDR, 0);
1708 	if (error) {
1709 		bam_error(PACK_FAIL, strerror(error));
1710 		return;
1711 	}
1712 
1713 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
1714 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
1715 	if (fd == -1) {
1716 		bam_error(OPEN_FAIL, path, strerror(errno));
1717 		free(nstat);
1718 		return;
1719 	}
1720 	wrote = write(fd, nstat, sz);
1721 	if (wrote != sz) {
1722 		bam_error(WRITE_FAIL, path, strerror(errno));
1723 		(void) close(fd);
1724 		free(nstat);
1725 		return;
1726 	}
1727 	(void) close(fd);
1728 	free(nstat);
1729 
1730 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
1731 	if (rename(path, path2) != 0) {
1732 		bam_error(RENAME_FAIL, path2, strerror(errno));
1733 	}
1734 }
1735 
1736 static void
1737 clear_walk_args(void)
1738 {
1739 	if (walk_arg.old_nvlp)
1740 		nvlist_free(walk_arg.old_nvlp);
1741 	if (walk_arg.new_nvlp)
1742 		nvlist_free(walk_arg.new_nvlp);
1743 	walk_arg.need_update = 0;
1744 	walk_arg.old_nvlp = NULL;
1745 	walk_arg.new_nvlp = NULL;
1746 }
1747 
1748 /*
1749  * Returns:
1750  *	0 - no update necessary
1751  *	1 - update required.
1752  *	BAM_ERROR (-1) - An error occurred
1753  *
1754  * Special handling for check (-n):
1755  * ================================
1756  * The check (-n) option produces parseable output.
1757  * To do this, we suppress all stdout messages unrelated
1758  * to out of sync files.
1759  * All stderr messages are still printed though.
1760  *
1761  */
1762 static int
1763 update_required(char *root)
1764 {
1765 	struct stat sb;
1766 	char path[PATH_MAX];
1767 	filelist_t flist;
1768 	filelist_t *flistp = &flist;
1769 	int need_update;
1770 
1771 	flistp->head = flistp->tail = NULL;
1772 
1773 	walk_arg.need_update = 0;
1774 
1775 	/*
1776 	 * Without consulting stat data, check if we need update
1777 	 */
1778 	check_flags_and_files(root);
1779 
1780 	/*
1781 	 * In certain deployment scenarios, filestat may not
1782 	 * exist. Ignore it during boot-archive SMF check.
1783 	 */
1784 	if (bam_smf_check) {
1785 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
1786 		if (stat(path, &sb) != 0)
1787 			return (0);
1788 	}
1789 
1790 	/*
1791 	 * consult stat data only if we haven't made a decision
1792 	 * about update. If checking (-n) however, we always
1793 	 * need stat data (since we want to compare old and new)
1794 	 */
1795 	if (!walk_arg.need_update || bam_check)
1796 		getoldstat(root);
1797 
1798 	/*
1799 	 * Check if the archive contains files that are no longer
1800 	 * present on the root filesystem.
1801 	 */
1802 	if (!walk_arg.need_update || bam_check)
1803 		check4stale(root);
1804 
1805 	/*
1806 	 * read list of files
1807 	 */
1808 	if (read_list(root, flistp) != BAM_SUCCESS) {
1809 		clear_walk_args();
1810 		return (BAM_ERROR);
1811 	}
1812 
1813 	assert(flistp->head && flistp->tail);
1814 
1815 	/*
1816 	 * At this point either the update is required
1817 	 * or the decision is pending. In either case
1818 	 * we need to create new stat nvlist
1819 	 */
1820 	create_newstat();
1821 
1822 	/*
1823 	 * This walk does 2 things:
1824 	 *  	- gets new stat data for every file
1825 	 *	- (optional) compare old and new stat data
1826 	 */
1827 	walk_list(root, &flist);
1828 
1829 	/* done with the file list */
1830 	filelist_free(flistp);
1831 
1832 	/*
1833 	 * if we didn't succeed in  creating new stat data above
1834 	 * just return result of update check so that archive is built.
1835 	 */
1836 	if (walk_arg.new_nvlp == NULL) {
1837 		bam_error(NO_NEW_STAT);
1838 		need_update = walk_arg.need_update;
1839 		clear_walk_args();
1840 		return (need_update ? 1 : 0);
1841 	}
1842 
1843 
1844 	/*
1845 	 * If no update required, discard newstat
1846 	 */
1847 	if (!walk_arg.need_update) {
1848 		clear_walk_args();
1849 		return (0);
1850 	}
1851 
1852 	/*
1853 	 * At this point we need an update - so save new stat data
1854 	 * However, if only checking (-n), don't save new stat data.
1855 	 */
1856 	if (!bam_check)
1857 		savenew(root);
1858 
1859 	clear_walk_args();
1860 
1861 	return (1);
1862 }
1863 
1864 static error_t
1865 create_ramdisk(char *root)
1866 {
1867 	char *cmdline, path[PATH_MAX];
1868 	size_t len;
1869 	struct stat sb;
1870 
1871 	/*
1872 	 * Setup command args for create_ramdisk.ksh
1873 	 */
1874 	(void) snprintf(path, sizeof (path), "%s%s", root, CREATE_RAMDISK);
1875 	if (stat(path, &sb) != 0) {
1876 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
1877 		return (BAM_ERROR);
1878 	}
1879 
1880 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
1881 	cmdline = s_calloc(1, len);
1882 
1883 	if (strlen(root) > 1) {
1884 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
1885 		/* chop off / at the end */
1886 		cmdline[strlen(cmdline) - 1] = '\0';
1887 	} else
1888 		(void) snprintf(cmdline, len, "%s", path);
1889 
1890 	if (exec_cmd(cmdline, NULL, 0) != 0) {
1891 		bam_error(ARCHIVE_FAIL, cmdline);
1892 		free(cmdline);
1893 		return (BAM_ERROR);
1894 	}
1895 	free(cmdline);
1896 
1897 	/*
1898 	 * Verify that the archive has been created
1899 	 */
1900 	(void) snprintf(path, sizeof (path), "%s%s", root,
1901 	    DIRECT_BOOT_ARCHIVE_32);
1902 	if (stat(path, &sb) != 0) {
1903 		bam_error(ARCHIVE_NOT_CREATED, path);
1904 		return (BAM_ERROR);
1905 	}
1906 	if (bam_direct == BAM_DIRECT_DBOOT) {
1907 		(void) snprintf(path, sizeof (path), "%s%s", root,
1908 		    DIRECT_BOOT_ARCHIVE_64);
1909 		if (stat(path, &sb) != 0) {
1910 			bam_error(ARCHIVE_NOT_CREATED, path);
1911 			return (BAM_ERROR);
1912 		}
1913 	}
1914 
1915 	return (BAM_SUCCESS);
1916 }
1917 
1918 /*
1919  * Checks if target filesystem is on a ramdisk
1920  * 1 - is miniroot
1921  * 0 - is not
1922  * When in doubt assume it is not a ramdisk.
1923  */
1924 static int
1925 is_ramdisk(char *root)
1926 {
1927 	struct extmnttab mnt;
1928 	FILE *fp;
1929 	int found;
1930 	char mntpt[PATH_MAX];
1931 	char *cp;
1932 
1933 	/*
1934 	 * There are 3 situations where creating archive is
1935 	 * of dubious value:
1936 	 *	- create boot_archive on a lofi-mounted boot_archive
1937 	 *	- create it on a ramdisk which is the root filesystem
1938 	 *	- create it on a ramdisk mounted somewhere else
1939 	 * The first is not easy to detect and checking for it is not
1940 	 * worth it.
1941 	 * The other two conditions are handled here
1942 	 */
1943 
1944 	fp = fopen(MNTTAB, "r");
1945 	if (fp == NULL) {
1946 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1947 		return (0);
1948 	}
1949 
1950 	resetmnttab(fp);
1951 
1952 	/*
1953 	 * Remove any trailing / from the mount point
1954 	 */
1955 	(void) strlcpy(mntpt, root, sizeof (mntpt));
1956 	if (strcmp(root, "/") != 0) {
1957 		cp = mntpt + strlen(mntpt) - 1;
1958 		if (*cp == '/')
1959 			*cp = '\0';
1960 	}
1961 	found = 0;
1962 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1963 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
1964 			found = 1;
1965 			break;
1966 		}
1967 	}
1968 
1969 	if (!found) {
1970 		if (bam_verbose)
1971 			bam_error(NOT_IN_MNTTAB, mntpt);
1972 		(void) fclose(fp);
1973 		return (0);
1974 	}
1975 
1976 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
1977 		if (bam_verbose)
1978 			bam_error(IS_RAMDISK, bam_root);
1979 		(void) fclose(fp);
1980 		return (1);
1981 	}
1982 
1983 	(void) fclose(fp);
1984 
1985 	return (0);
1986 }
1987 
1988 static int
1989 is_newboot(char *root)
1990 {
1991 	char path[PATH_MAX];
1992 	struct stat sb;
1993 
1994 	/*
1995 	 * We can't boot without MULTI_BOOT
1996 	 */
1997 	(void) snprintf(path, sizeof (path), "%s%s", root, MULTI_BOOT);
1998 	if (stat(path, &sb) == -1) {
1999 		if (bam_verbose)
2000 			bam_print(FILE_MISS, path);
2001 		return (0);
2002 	}
2003 
2004 	/*
2005 	 * We can't generate archive without GRUB_DIR
2006 	 */
2007 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR);
2008 	if (stat(path, &sb) == -1) {
2009 		if (bam_verbose)
2010 			bam_print(DIR_MISS, path);
2011 		return (0);
2012 	}
2013 
2014 	return (1);
2015 }
2016 
2017 static int
2018 is_readonly(char *root)
2019 {
2020 	struct statvfs vfs;
2021 
2022 	/*
2023 	 * Check for RDONLY filesystem
2024 	 * When in doubt assume it is not readonly
2025 	 */
2026 	if (statvfs(root, &vfs) != 0) {
2027 		if (bam_verbose)
2028 			bam_error(STATVFS_FAIL, root, strerror(errno));
2029 		return (0);
2030 	}
2031 
2032 	if (vfs.f_flag & ST_RDONLY) {
2033 		return (1);
2034 	}
2035 
2036 	return (0);
2037 }
2038 
2039 static error_t
2040 update_archive(char *root, char *opt)
2041 {
2042 	error_t ret;
2043 
2044 	assert(root);
2045 	assert(opt == NULL);
2046 
2047 	/*
2048 	 * root must belong to a GRUB boot OS,
2049 	 * don't care on sparc except for diskless clients
2050 	 */
2051 	if (!is_newboot(root)) {
2052 		/*
2053 		 * Emit message only if not in context of update_all.
2054 		 * If in update_all, emit only if verbose flag is set.
2055 		 */
2056 		if (!bam_update_all || bam_verbose)
2057 			bam_print(NOT_GRUB_BOOT, root);
2058 		return (BAM_SUCCESS);
2059 	}
2060 
2061 	/*
2062 	 * If smf check is requested when / is writable (can happen
2063 	 * on first reboot following an upgrade because service
2064 	 * dependency is messed up), skip the check.
2065 	 */
2066 	if (bam_smf_check && !bam_root_readonly)
2067 		return (BAM_SUCCESS);
2068 
2069 	/*
2070 	 * root must be writable. This check applies to alternate
2071 	 * root (-R option); bam_root_readonly applies to '/' only.
2072 	 * Note: statvfs() does not always report the truth
2073 	 */
2074 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
2075 		if (bam_verbose)
2076 			bam_print(RDONLY_FS, root);
2077 		return (BAM_SUCCESS);
2078 	}
2079 
2080 	/*
2081 	 * Don't generate archive on ramdisk
2082 	 */
2083 	if (is_ramdisk(root)) {
2084 		if (bam_verbose)
2085 			bam_print(SKIP_RAMDISK);
2086 		return (BAM_SUCCESS);
2087 	}
2088 
2089 	/*
2090 	 * Now check if updated is really needed
2091 	 */
2092 	ret = update_required(root);
2093 
2094 	/*
2095 	 * The check command (-n) is *not* a dry run
2096 	 * It only checks if the archive is in sync.
2097 	 */
2098 	if (bam_check) {
2099 		bam_exit((ret != 0) ? 1 : 0);
2100 	}
2101 
2102 	if (ret == 1) {
2103 		/* create the ramdisk */
2104 		ret = create_ramdisk(root);
2105 	}
2106 	return (ret);
2107 }
2108 
2109 static void
2110 update_fdisk(void)
2111 {
2112 	struct stat sb;
2113 	char cmd[PATH_MAX];
2114 	int ret1, ret2;
2115 
2116 	assert(stat(GRUB_fdisk, &sb) == 0);
2117 	assert(stat(GRUB_fdisk_target, &sb) == 0);
2118 
2119 	(void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`",
2120 	    GRUB_fdisk, GRUB_fdisk_target);
2121 
2122 	bam_print(UPDATING_FDISK);
2123 	if (exec_cmd(cmd, NULL, 0) != 0) {
2124 		bam_error(FDISK_UPDATE_FAILED);
2125 	}
2126 
2127 	/*
2128 	 * We are done, remove the files.
2129 	 */
2130 	ret1 = unlink(GRUB_fdisk);
2131 	ret2 = unlink(GRUB_fdisk_target);
2132 	if (ret1 != 0 || ret2 != 0) {
2133 		bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target);
2134 	}
2135 }
2136 
2137 static void
2138 restore_grub_slice(void)
2139 {
2140 	struct stat sb;
2141 	char *mntpt, *physlice;
2142 	int mnted;	/* set if we did a mount */
2143 	char menupath[PATH_MAX], cmd[PATH_MAX];
2144 
2145 	if (stat(GRUB_slice, &sb) != 0) {
2146 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
2147 		return;
2148 	}
2149 
2150 	/*
2151 	 * If we are doing an luactivate, don't attempt to restore GRUB or else
2152 	 * we may not be able to get to DCA boot environments. Let luactivate
2153 	 * handle GRUB/DCA installation
2154 	 */
2155 	if (stat(LU_ACTIVATE_FILE, &sb) == 0) {
2156 		return;
2157 	}
2158 
2159 	mnted = 0;
2160 	physlice = NULL;
2161 	mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL);
2162 	if (mntpt == NULL) {
2163 		bam_error(CANNOT_RESTORE_GRUB_SLICE);
2164 		return;
2165 	}
2166 
2167 	(void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU);
2168 	if (stat(menupath, &sb) == 0) {
2169 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2170 		return;
2171 	}
2172 
2173 	/*
2174 	 * The menu is missing - we need to do a restore
2175 	 */
2176 	bam_print(RESTORING_GRUB);
2177 
2178 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s %s",
2179 	    INSTALLGRUB, STAGE1, STAGE2, physlice);
2180 
2181 	if (exec_cmd(cmd, NULL, 0) != 0) {
2182 		bam_error(RESTORE_GRUB_FAILED);
2183 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2184 		return;
2185 	}
2186 
2187 	if (stat(GRUB_backup_menu, &sb) != 0) {
2188 		bam_error(MISSING_BACKUP_MENU,
2189 		    GRUB_backup_menu, strerror(errno));
2190 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2191 		return;
2192 	}
2193 
2194 	(void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s",
2195 	    GRUB_backup_menu, menupath);
2196 
2197 	if (exec_cmd(cmd, NULL, 0) != 0) {
2198 		bam_error(RESTORE_MENU_FAILED, menupath);
2199 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2200 		return;
2201 	}
2202 
2203 	/* Success */
2204 	umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2205 }
2206 
2207 static error_t
2208 update_all(char *root, char *opt)
2209 {
2210 	struct extmnttab mnt;
2211 	struct stat sb;
2212 	FILE *fp;
2213 	char multibt[PATH_MAX];
2214 	error_t ret = BAM_SUCCESS;
2215 	int ret1, ret2;
2216 
2217 	assert(root);
2218 	assert(opt == NULL);
2219 
2220 	if (bam_rootlen != 1 || *root != '/') {
2221 		elide_trailing_slash(root, multibt, sizeof (multibt));
2222 		bam_error(ALT_ROOT_INVALID, multibt);
2223 		return (BAM_ERROR);
2224 	}
2225 
2226 	/*
2227 	 * Check to see if we are in the midst of safemode patching
2228 	 * If so skip building the archive for /. Instead build it
2229 	 * against the latest bits obtained by creating a fresh lofs
2230 	 * mount of root.
2231 	 */
2232 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
2233 		if (mkdir(LOFS_PATCH_MNT, 0755) == -1 &&
2234 		    errno != EEXIST) {
2235 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
2236 			    strerror(errno));
2237 			ret = BAM_ERROR;
2238 			goto out;
2239 		}
2240 		(void) snprintf(multibt, sizeof (multibt),
2241 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
2242 		if (exec_cmd(multibt, NULL, 0) != 0) {
2243 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
2244 			ret = BAM_ERROR;
2245 		}
2246 		if (ret != BAM_ERROR) {
2247 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
2248 			    LOFS_PATCH_MNT);
2249 			bam_rootlen = strlen(rootbuf);
2250 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
2251 				ret = BAM_ERROR;
2252 			/*
2253 			 * unmount the lofs mount since there could be
2254 			 * multiple invocations of bootadm -a update_all
2255 			 */
2256 			(void) snprintf(multibt, sizeof (multibt),
2257 			    "/sbin/umount %s", LOFS_PATCH_MNT);
2258 			if (exec_cmd(multibt, NULL, 0) != 0) {
2259 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
2260 				ret = BAM_ERROR;
2261 			}
2262 		}
2263 	} else {
2264 		/*
2265 		 * First update archive for current root
2266 		 */
2267 		if (update_archive(root, opt) != BAM_SUCCESS)
2268 			ret = BAM_ERROR;
2269 	}
2270 
2271 	if (ret == BAM_ERROR)
2272 		goto out;
2273 
2274 	/*
2275 	 * Now walk the mount table, performing archive update
2276 	 * for all mounted Newboot root filesystems
2277 	 */
2278 	fp = fopen(MNTTAB, "r");
2279 	if (fp == NULL) {
2280 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2281 		ret = BAM_ERROR;
2282 		goto out;
2283 	}
2284 
2285 	resetmnttab(fp);
2286 
2287 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
2288 		if (mnt.mnt_special == NULL)
2289 			continue;
2290 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
2291 			continue;
2292 		if (strcmp(mnt.mnt_mountp, "/") == 0)
2293 			continue;
2294 
2295 		(void) snprintf(multibt, sizeof (multibt), "%s%s",
2296 		    mnt.mnt_mountp, MULTI_BOOT);
2297 
2298 		if (stat(multibt, &sb) == -1)
2299 			continue;
2300 
2301 		/*
2302 		 * We put a trailing slash to be consistent with root = "/"
2303 		 * case, such that we don't have to print // in some cases.
2304 		 */
2305 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
2306 		    mnt.mnt_mountp);
2307 		bam_rootlen = strlen(rootbuf);
2308 
2309 		/*
2310 		 * It's possible that other mounts may be an alternate boot
2311 		 * architecture, so check it again.
2312 		 */
2313 		if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) ||
2314 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
2315 			ret = BAM_ERROR;
2316 	}
2317 
2318 	(void) fclose(fp);
2319 
2320 out:
2321 	if (stat(GRUB_slice, &sb) == 0) {
2322 		restore_grub_slice();
2323 	}
2324 
2325 	/*
2326 	 * Update fdisk table as we go down. Updating it when
2327 	 * the system is running will confuse biosdev.
2328 	 */
2329 	ret1 = stat(GRUB_fdisk, &sb);
2330 	ret2 = stat(GRUB_fdisk_target, &sb);
2331 	if ((ret1 == 0) && (ret2 == 0)) {
2332 		update_fdisk();
2333 	} else if ((ret1 == 0) ^ (ret2 == 0)) {
2334 		/*
2335 		 * It is an error for one file to be
2336 		 * present and the other absent.
2337 		 * It is normal for both files to be
2338 		 * absent - it indicates that no fdisk
2339 		 * update is required.
2340 		 */
2341 		bam_error(MISSING_FDISK_FILE,
2342 		    ret1 ? GRUB_fdisk : GRUB_fdisk_target);
2343 		ret = BAM_ERROR;
2344 	}
2345 
2346 	return (ret);
2347 }
2348 
2349 static void
2350 append_line(menu_t *mp, line_t *lp)
2351 {
2352 	if (mp->start == NULL) {
2353 		mp->start = lp;
2354 	} else {
2355 		mp->end->next = lp;
2356 		lp->prev = mp->end;
2357 	}
2358 	mp->end = lp;
2359 }
2360 
2361 static void
2362 unlink_line(menu_t *mp, line_t *lp)
2363 {
2364 	/* unlink from list */
2365 	if (lp->prev)
2366 		lp->prev->next = lp->next;
2367 	else
2368 		mp->start = lp->next;
2369 	if (lp->next)
2370 		lp->next->prev = lp->prev;
2371 	else
2372 		mp->end = lp->prev;
2373 }
2374 
2375 static entry_t *
2376 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
2377 {
2378 	entry_t *ent, *prev;
2379 
2380 	ent = s_calloc(1, sizeof (entry_t));
2381 	ent->start = start;
2382 	ent->end = end;
2383 
2384 	if (mp->entries == NULL) {
2385 		mp->entries = ent;
2386 		return (ent);
2387 	}
2388 
2389 	prev = mp->entries;
2390 	while (prev->next)
2391 		prev = prev-> next;
2392 	prev->next = ent;
2393 	ent->prev = prev;
2394 	return (ent);
2395 }
2396 
2397 static void
2398 boot_entry_addline(entry_t *ent, line_t *lp)
2399 {
2400 	if (ent)
2401 		ent->end = lp;
2402 }
2403 
2404 /*
2405  * A line in menu.lst looks like
2406  * [ ]*<cmd>[ \t=]*<arg>*
2407  */
2408 static void
2409 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
2410 {
2411 	/*
2412 	 * save state across calls. This is so that
2413 	 * header gets the right entry# after title has
2414 	 * been processed
2415 	 */
2416 	static line_t *prev = NULL;
2417 	static entry_t *curr_ent = NULL;
2418 	static int in_liveupgrade = 0;
2419 
2420 	line_t	*lp;
2421 	char *cmd, *sep, *arg;
2422 	char save, *cp, *line;
2423 	menu_flag_t flag = BAM_INVALID;
2424 
2425 	if (str == NULL) {
2426 		return;
2427 	}
2428 
2429 	/*
2430 	 * First save a copy of the entire line.
2431 	 * We use this later to set the line field.
2432 	 */
2433 	line = s_strdup(str);
2434 
2435 	/* Eat up leading whitespace */
2436 	while (*str == ' ' || *str == '\t')
2437 		str++;
2438 
2439 	if (*str == '#') {		/* comment */
2440 		cmd = s_strdup("#");
2441 		sep = NULL;
2442 		arg = s_strdup(str + 1);
2443 		flag = BAM_COMMENT;
2444 		if (strstr(arg, BAM_LU_HDR) != NULL) {
2445 			in_liveupgrade = 1;
2446 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
2447 			in_liveupgrade = 0;
2448 		}
2449 	} else if (*str == '\0') {	/* blank line */
2450 		cmd = sep = arg = NULL;
2451 		flag = BAM_EMPTY;
2452 	} else {
2453 		/*
2454 		 * '=' is not a documented separator in grub syntax.
2455 		 * However various development bits use '=' as a
2456 		 * separator. In addition, external users also
2457 		 * use = as a separator. So we will allow that usage.
2458 		 */
2459 		cp = str;
2460 		while (*str != ' ' && *str != '\t' && *str != '=') {
2461 			if (*str == '\0') {
2462 				cmd = s_strdup(cp);
2463 				sep = arg = NULL;
2464 				break;
2465 			}
2466 			str++;
2467 		}
2468 
2469 		if (*str != '\0') {
2470 			save = *str;
2471 			*str = '\0';
2472 			cmd = s_strdup(cp);
2473 			*str = save;
2474 
2475 			str++;
2476 			save = *str;
2477 			*str = '\0';
2478 			sep = s_strdup(str - 1);
2479 			*str = save;
2480 
2481 			while (*str == ' ' || *str == '\t')
2482 				str++;
2483 			if (*str == '\0')
2484 				arg = NULL;
2485 			else
2486 				arg = s_strdup(str);
2487 		}
2488 	}
2489 
2490 	lp = s_calloc(1, sizeof (line_t));
2491 
2492 	lp->cmd = cmd;
2493 	lp->sep = sep;
2494 	lp->arg = arg;
2495 	lp->line = line;
2496 	lp->lineNum = ++(*lineNum);
2497 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
2498 		lp->entryNum = ++(*entryNum);
2499 		lp->flags = BAM_TITLE;
2500 		if (prev && prev->flags == BAM_COMMENT &&
2501 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2502 			prev->entryNum = lp->entryNum;
2503 			curr_ent = boot_entry_new(mp, prev, lp);
2504 			curr_ent->flags = BAM_ENTRY_BOOTADM;
2505 		} else {
2506 			curr_ent = boot_entry_new(mp, lp, lp);
2507 			if (in_liveupgrade) {
2508 				curr_ent->flags = BAM_ENTRY_LU;
2509 			}
2510 		}
2511 		curr_ent->entryNum = *entryNum;
2512 	} else if (flag != BAM_INVALID) {
2513 		/*
2514 		 * For header comments, the entry# is "fixed up"
2515 		 * by the subsequent title
2516 		 */
2517 		lp->entryNum = *entryNum;
2518 		lp->flags = flag;
2519 	} else {
2520 		lp->entryNum = *entryNum;
2521 
2522 		if (*entryNum == ENTRY_INIT) {
2523 			lp->flags = BAM_GLOBAL;
2524 		} else {
2525 			lp->flags = BAM_ENTRY;
2526 
2527 			if (cmd && arg) {
2528 				/*
2529 				 * We only compare for the length of "module"
2530 				 * so that "module$" will also match.
2531 				 */
2532 				if ((strncmp(cmd, menu_cmds[MODULE_CMD],
2533 				    strlen(menu_cmds[MODULE_CMD])) == 0) &&
2534 				    (strcmp(arg, MINIROOT) == 0))
2535 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
2536 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
2537 					curr_ent->flags |= BAM_ENTRY_ROOT;
2538 				else if (strcmp(cmd,
2539 				    menu_cmds[CHAINLOADER_CMD]) == 0)
2540 					curr_ent->flags |=
2541 					    BAM_ENTRY_CHAINLOADER;
2542 			}
2543 		}
2544 	}
2545 
2546 	/* record default, old default, and entry line ranges */
2547 	if (lp->flags == BAM_GLOBAL &&
2548 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
2549 		mp->curdefault = lp;
2550 	} else if (lp->flags == BAM_COMMENT &&
2551 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
2552 		mp->olddefault = lp;
2553 	} else if (lp->flags == BAM_COMMENT &&
2554 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
2555 		mp->old_rc_default = lp;
2556 	} else if (lp->flags == BAM_ENTRY ||
2557 	    (lp->flags == BAM_COMMENT &&
2558 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
2559 		boot_entry_addline(curr_ent, lp);
2560 	}
2561 	append_line(mp, lp);
2562 
2563 	prev = lp;
2564 }
2565 
2566 static void
2567 update_numbering(menu_t *mp)
2568 {
2569 	int lineNum;
2570 	int entryNum;
2571 	int old_default_value;
2572 	line_t *lp, *prev, *default_lp, *default_entry;
2573 	char buf[PATH_MAX];
2574 
2575 	if (mp->start == NULL) {
2576 		return;
2577 	}
2578 
2579 	lineNum = LINE_INIT;
2580 	entryNum = ENTRY_INIT;
2581 	old_default_value = ENTRY_INIT;
2582 	lp = default_lp = default_entry = NULL;
2583 
2584 	prev = NULL;
2585 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
2586 		lp->lineNum = ++lineNum;
2587 
2588 		/*
2589 		 * Get the value of the default command
2590 		 */
2591 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
2592 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
2593 		    lp->arg) {
2594 			old_default_value = atoi(lp->arg);
2595 			default_lp = lp;
2596 		}
2597 
2598 		/*
2599 		 * If not boot entry, nothing else to fix for this
2600 		 * entry
2601 		 */
2602 		if (lp->entryNum == ENTRY_INIT)
2603 			continue;
2604 
2605 		/*
2606 		 * Record the position of the default entry.
2607 		 * The following works because global
2608 		 * commands like default and timeout should precede
2609 		 * actual boot entries, so old_default_value
2610 		 * is already known (or default cmd is missing).
2611 		 */
2612 		if (default_entry == NULL &&
2613 		    old_default_value != ENTRY_INIT &&
2614 		    lp->entryNum == old_default_value) {
2615 			default_entry = lp;
2616 		}
2617 
2618 		/*
2619 		 * Now fixup the entry number
2620 		 */
2621 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
2622 			lp->entryNum = ++entryNum;
2623 			/* fixup the bootadm header */
2624 			if (prev && prev->flags == BAM_COMMENT &&
2625 			    prev->arg &&
2626 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2627 				prev->entryNum = lp->entryNum;
2628 			}
2629 		} else {
2630 			lp->entryNum = entryNum;
2631 		}
2632 	}
2633 
2634 	/*
2635 	 * No default command in menu, simply return
2636 	 */
2637 	if (default_lp == NULL) {
2638 		return;
2639 	}
2640 
2641 	free(default_lp->arg);
2642 	free(default_lp->line);
2643 
2644 	if (default_entry == NULL) {
2645 		default_lp->arg = s_strdup("0");
2646 	} else {
2647 		(void) snprintf(buf, sizeof (buf), "%d",
2648 		    default_entry->entryNum);
2649 		default_lp->arg = s_strdup(buf);
2650 	}
2651 
2652 	/*
2653 	 * The following is required since only the line field gets
2654 	 * written back to menu.lst
2655 	 */
2656 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
2657 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
2658 	default_lp->line = s_strdup(buf);
2659 }
2660 
2661 
2662 static menu_t *
2663 menu_read(char *menu_path)
2664 {
2665 	FILE *fp;
2666 	char buf[BAM_MAXLINE], *cp;
2667 	menu_t *mp;
2668 	int line, entry, len, n;
2669 
2670 	mp = s_calloc(1, sizeof (menu_t));
2671 
2672 	fp = fopen(menu_path, "r");
2673 	if (fp == NULL) { /* Let the caller handle this error */
2674 		return (mp);
2675 	}
2676 
2677 
2678 	/* Note: GRUB boot entry number starts with 0 */
2679 	line = LINE_INIT;
2680 	entry = ENTRY_INIT;
2681 	cp = buf;
2682 	len = sizeof (buf);
2683 	while (s_fgets(cp, len, fp) != NULL) {
2684 		n = strlen(cp);
2685 		if (cp[n - 1] == '\\') {
2686 			len -= n - 1;
2687 			assert(len >= 2);
2688 			cp += n - 1;
2689 			continue;
2690 		}
2691 		line_parser(mp, buf, &line, &entry);
2692 		cp = buf;
2693 		len = sizeof (buf);
2694 	}
2695 
2696 	if (fclose(fp) == EOF) {
2697 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
2698 	}
2699 
2700 	return (mp);
2701 }
2702 
2703 static error_t
2704 selector(menu_t *mp, char *opt, int *entry, char **title)
2705 {
2706 	char *eq;
2707 	char *opt_dup;
2708 	int entryNum;
2709 
2710 	assert(mp);
2711 	assert(mp->start);
2712 	assert(opt);
2713 
2714 	opt_dup = s_strdup(opt);
2715 
2716 	if (entry)
2717 		*entry = ENTRY_INIT;
2718 	if (title)
2719 		*title = NULL;
2720 
2721 	eq = strchr(opt_dup, '=');
2722 	if (eq == NULL) {
2723 		bam_error(INVALID_OPT, opt);
2724 		free(opt_dup);
2725 		return (BAM_ERROR);
2726 	}
2727 
2728 	*eq = '\0';
2729 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
2730 		assert(mp->end);
2731 		entryNum = s_strtol(eq + 1);
2732 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
2733 			bam_error(INVALID_ENTRY, eq + 1);
2734 			free(opt_dup);
2735 			return (BAM_ERROR);
2736 		}
2737 		*entry = entryNum;
2738 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
2739 		*title = opt + (eq - opt_dup) + 1;
2740 	} else {
2741 		bam_error(INVALID_OPT, opt);
2742 		free(opt_dup);
2743 		return (BAM_ERROR);
2744 	}
2745 
2746 	free(opt_dup);
2747 	return (BAM_SUCCESS);
2748 }
2749 
2750 /*
2751  * If invoked with no titles/entries (opt == NULL)
2752  * only title lines in file are printed.
2753  *
2754  * If invoked with a title or entry #, all
2755  * lines in *every* matching entry are listed
2756  */
2757 static error_t
2758 list_entry(menu_t *mp, char *menu_path, char *opt)
2759 {
2760 	line_t *lp;
2761 	int entry = ENTRY_INIT;
2762 	int found;
2763 	char *title = NULL;
2764 
2765 	assert(mp);
2766 	assert(menu_path);
2767 
2768 	if (mp->start == NULL) {
2769 		bam_error(NO_MENU, menu_path);
2770 		return (BAM_ERROR);
2771 	}
2772 
2773 	if (opt != NULL) {
2774 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
2775 			return (BAM_ERROR);
2776 		}
2777 		assert((entry != ENTRY_INIT) ^ (title != NULL));
2778 	} else {
2779 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
2780 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
2781 	}
2782 
2783 	found = 0;
2784 	for (lp = mp->start; lp; lp = lp->next) {
2785 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
2786 			continue;
2787 		if (opt == NULL && lp->flags == BAM_TITLE) {
2788 			bam_print(PRINT_TITLE, lp->entryNum,
2789 			    lp->arg);
2790 			found = 1;
2791 			continue;
2792 		}
2793 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
2794 			bam_print(PRINT, lp->line);
2795 			found = 1;
2796 			continue;
2797 		}
2798 
2799 		/*
2800 		 * We set the entry value here so that all lines
2801 		 * in entry get printed. If we subsequently match
2802 		 * title in other entries, all lines in those
2803 		 * entries get printed as well.
2804 		 */
2805 		if (title && lp->flags == BAM_TITLE && lp->arg &&
2806 		    strncmp(title, lp->arg, strlen(title)) == 0) {
2807 			bam_print(PRINT, lp->line);
2808 			entry = lp->entryNum;
2809 			found = 1;
2810 			continue;
2811 		}
2812 	}
2813 
2814 	if (!found) {
2815 		bam_error(NO_MATCH_ENTRY);
2816 		return (BAM_ERROR);
2817 	}
2818 
2819 	return (BAM_SUCCESS);
2820 }
2821 
2822 static int
2823 add_boot_entry(menu_t *mp,
2824 	char *title,
2825 	char *root,
2826 	char *kernel,
2827 	char *module)
2828 {
2829 	int lineNum, entryNum;
2830 	char linebuf[BAM_MAXLINE];
2831 	menu_cmd_t k_cmd, m_cmd;
2832 
2833 	assert(mp);
2834 
2835 	if (title == NULL) {
2836 		title = "Solaris";	/* default to Solaris */
2837 	}
2838 	if (kernel == NULL) {
2839 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
2840 		return (BAM_ERROR);
2841 	}
2842 	if (module == NULL) {
2843 		if (bam_direct != BAM_DIRECT_DBOOT) {
2844 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
2845 			return (BAM_ERROR);
2846 		}
2847 
2848 		/* Figure the commands out from the kernel line */
2849 		if (strstr(kernel, "$ISADIR") != NULL) {
2850 			module = DIRECT_BOOT_ARCHIVE;
2851 			k_cmd = KERNEL_DOLLAR_CMD;
2852 			m_cmd = MODULE_DOLLAR_CMD;
2853 		} else if (strstr(kernel, "amd64") != NULL) {
2854 			module = DIRECT_BOOT_ARCHIVE_64;
2855 			k_cmd = KERNEL_CMD;
2856 			m_cmd = MODULE_CMD;
2857 		} else {
2858 			module = DIRECT_BOOT_ARCHIVE_32;
2859 			k_cmd = KERNEL_CMD;
2860 			m_cmd = MODULE_CMD;
2861 		}
2862 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
2863 	    (strstr(kernel, "$ISADIR") != NULL)) {
2864 		/*
2865 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
2866 		 * command.  Otherwise, use "kernel".
2867 		 */
2868 		k_cmd = KERNEL_DOLLAR_CMD;
2869 		m_cmd = MODULE_DOLLAR_CMD;
2870 	} else {
2871 		k_cmd = KERNEL_CMD;
2872 		m_cmd = MODULE_CMD;
2873 	}
2874 
2875 	if (mp->start) {
2876 		lineNum = mp->end->lineNum;
2877 		entryNum = mp->end->entryNum;
2878 	} else {
2879 		lineNum = LINE_INIT;
2880 		entryNum = ENTRY_INIT;
2881 	}
2882 
2883 	/*
2884 	 * No separator for comment (HDR/FTR) commands
2885 	 * The syntax for comments is #<comment>
2886 	 */
2887 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
2888 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
2889 	line_parser(mp, linebuf, &lineNum, &entryNum);
2890 
2891 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2892 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
2893 	line_parser(mp, linebuf, &lineNum, &entryNum);
2894 
2895 	if (root) {
2896 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2897 		    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
2898 		line_parser(mp, linebuf, &lineNum, &entryNum);
2899 	}
2900 
2901 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2902 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
2903 	line_parser(mp, linebuf, &lineNum, &entryNum);
2904 
2905 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2906 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
2907 	line_parser(mp, linebuf, &lineNum, &entryNum);
2908 
2909 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
2910 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
2911 	line_parser(mp, linebuf, &lineNum, &entryNum);
2912 
2913 	return (entryNum);
2914 }
2915 
2916 static error_t
2917 do_delete(menu_t *mp, int entryNum)
2918 {
2919 	line_t *lp, *freed;
2920 	entry_t *ent, *tmp;
2921 	int deleted;
2922 
2923 	assert(entryNum != ENTRY_INIT);
2924 
2925 	ent = mp->entries;
2926 	while (ent) {
2927 		lp = ent->start;
2928 		/* check entry number and make sure it's a bootadm entry */
2929 		if (lp->flags != BAM_COMMENT ||
2930 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
2931 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
2932 			ent = ent->next;
2933 			continue;
2934 		}
2935 
2936 		/* free the entry content */
2937 		do {
2938 			freed = lp;
2939 			lp = lp->next;	/* prev stays the same */
2940 			unlink_line(mp, freed);
2941 			line_free(freed);
2942 		} while (freed != ent->end);
2943 
2944 		/* free the entry_t structure */
2945 		tmp = ent;
2946 		ent = ent->next;
2947 		if (tmp->prev)
2948 			tmp->prev->next = ent;
2949 		else
2950 			mp->entries = ent;
2951 		if (ent)
2952 			ent->prev = tmp->prev;
2953 		deleted = 1;
2954 	}
2955 
2956 	if (!deleted && entryNum != ALL_ENTRIES) {
2957 		bam_error(NO_BOOTADM_MATCH);
2958 		return (BAM_ERROR);
2959 	}
2960 
2961 	/*
2962 	 * Now that we have deleted an entry, update
2963 	 * the entry numbering and the default cmd.
2964 	 */
2965 	update_numbering(mp);
2966 
2967 	return (BAM_SUCCESS);
2968 }
2969 
2970 static error_t
2971 delete_all_entries(menu_t *mp, char *menu_path, char *opt)
2972 {
2973 	assert(mp);
2974 	assert(opt == NULL);
2975 
2976 	if (mp->start == NULL) {
2977 		bam_print(EMPTY_FILE, menu_path);
2978 		return (BAM_SUCCESS);
2979 	}
2980 
2981 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
2982 		return (BAM_ERROR);
2983 	}
2984 
2985 	return (BAM_WRITE);
2986 }
2987 
2988 static FILE *
2989 open_diskmap(char *root)
2990 {
2991 	FILE *fp;
2992 	char cmd[PATH_MAX];
2993 
2994 	/* make sure we have a map file */
2995 	fp = fopen(GRUBDISK_MAP, "r");
2996 	if (fp == NULL) {
2997 		(void) snprintf(cmd, sizeof (cmd),
2998 		    "%s%s > /dev/null", root, CREATE_DISKMAP);
2999 		(void) system(cmd);
3000 		fp = fopen(GRUBDISK_MAP, "r");
3001 	}
3002 	return (fp);
3003 }
3004 
3005 #define	SECTOR_SIZE	512
3006 
3007 static int
3008 get_partition(char *device)
3009 {
3010 	int i, fd, is_pcfs, partno = -1;
3011 	struct mboot *mboot;
3012 	char boot_sect[SECTOR_SIZE];
3013 	char *wholedisk, *slice;
3014 
3015 	/* form whole disk (p0) */
3016 	slice = device + strlen(device) - 2;
3017 	is_pcfs = (*slice != 's');
3018 	if (!is_pcfs)
3019 		*slice = '\0';
3020 	wholedisk = s_calloc(1, strlen(device) + 3);
3021 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
3022 	if (!is_pcfs)
3023 		*slice = 's';
3024 
3025 	/* read boot sector */
3026 	fd = open(wholedisk, O_RDONLY);
3027 	free(wholedisk);
3028 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
3029 		return (partno);
3030 	}
3031 	(void) close(fd);
3032 
3033 	/* parse fdisk table */
3034 	mboot = (struct mboot *)((void *)boot_sect);
3035 	for (i = 0; i < FD_NUMPART; i++) {
3036 		struct ipart *part =
3037 		    (struct ipart *)(uintptr_t)mboot->parts + i;
3038 		if (is_pcfs) {	/* looking for solaris boot part */
3039 			if (part->systid == 0xbe) {
3040 				partno = i;
3041 				break;
3042 			}
3043 		} else {	/* look for solaris partition, old and new */
3044 			if (part->systid == SUNIXOS ||
3045 			    part->systid == SUNIXOS2) {
3046 				partno = i;
3047 				break;
3048 			}
3049 		}
3050 	}
3051 	return (partno);
3052 }
3053 
3054 static char *
3055 get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
3056 {
3057 	char *grubdisk;	/* (hd#,#,#) */
3058 	char *slice;
3059 	char *grubhd;
3060 	int fdiskpart;
3061 	int found = 0;
3062 	char *devname, *ctdname = strstr(rootdev, "dsk/");
3063 	char linebuf[PATH_MAX];
3064 
3065 	if (ctdname == NULL)
3066 		return (NULL);
3067 
3068 	ctdname += strlen("dsk/");
3069 	slice = strrchr(ctdname, 's');
3070 	if (slice)
3071 		*slice = '\0';
3072 
3073 	rewind(fp);
3074 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
3075 		grubhd = strtok(linebuf, " \t\n");
3076 		if (grubhd)
3077 			devname = strtok(NULL, " \t\n");
3078 		else
3079 			devname = NULL;
3080 		if (devname && strcmp(devname, ctdname) == 0) {
3081 			found = 1;
3082 			break;
3083 		}
3084 	}
3085 
3086 	if (slice)
3087 		*slice = 's';
3088 
3089 	if (found == 0) {
3090 		if (bam_verbose)
3091 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
3092 		grubhd = "0";	/* assume disk 0 if can't match */
3093 	}
3094 
3095 	fdiskpart = get_partition(rootdev);
3096 	if (fdiskpart == -1)
3097 		return (NULL);
3098 
3099 	grubdisk = s_calloc(1, 10);
3100 	if (slice) {
3101 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
3102 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
3103 	} else
3104 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
3105 		    grubhd, fdiskpart);
3106 
3107 	/* if root not on bootdev, change GRUB disk to 0 */
3108 	if (!on_bootdev)
3109 		grubdisk[3] = '0';
3110 	return (grubdisk);
3111 }
3112 
3113 static char *
3114 get_title(char *rootdir)
3115 {
3116 	static char title[80];	/* from /etc/release */
3117 	char *cp = NULL, release[PATH_MAX];
3118 	FILE *fp;
3119 
3120 	/* open the /etc/release file */
3121 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
3122 
3123 	fp = fopen(release, "r");
3124 	if (fp == NULL)
3125 		return (NULL);
3126 
3127 	while (s_fgets(title, sizeof (title), fp) != NULL) {
3128 		cp = strstr(title, "Solaris");
3129 		if (cp)
3130 			break;
3131 	}
3132 	(void) fclose(fp);
3133 	return (cp == NULL ? "Solaris" : cp);
3134 }
3135 
3136 char *
3137 get_special(char *mountp)
3138 {
3139 	FILE *mntfp;
3140 	struct mnttab mp = {0}, mpref = {0};
3141 
3142 	mntfp = fopen(MNTTAB, "r");
3143 	if (mntfp == NULL) {
3144 		return (0);
3145 	}
3146 
3147 	if (*mountp == '\0')
3148 		mpref.mnt_mountp = "/";
3149 	else
3150 		mpref.mnt_mountp = mountp;
3151 	if (getmntany(mntfp, &mp, &mpref) != 0) {
3152 		(void) fclose(mntfp);
3153 		return (NULL);
3154 	}
3155 	(void) fclose(mntfp);
3156 
3157 	return (s_strdup(mp.mnt_special));
3158 }
3159 
3160 char *
3161 os_to_grubdisk(char *osdisk, int on_bootdev)
3162 {
3163 	FILE *fp;
3164 	char *grubdisk;
3165 
3166 	/* translate /dev/dsk name to grub disk name */
3167 	fp = open_diskmap("");
3168 	if (fp == NULL) {
3169 		bam_error(DISKMAP_FAIL, osdisk);
3170 		return (NULL);
3171 	}
3172 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
3173 	(void) fclose(fp);
3174 	return (grubdisk);
3175 }
3176 
3177 /*
3178  * Check if root is on the boot device
3179  * Return 0 (false) on error
3180  */
3181 static int
3182 menu_on_bootdev(char *menu_root, FILE *fp)
3183 {
3184 	int ret;
3185 	char *grubhd, *bootp, *special;
3186 
3187 	special = get_special(menu_root);
3188 	if (special == NULL)
3189 		return (0);
3190 	bootp = strstr(special, "p0:boot");
3191 	if (bootp)
3192 		*bootp = '\0';
3193 	grubhd = get_grubdisk(special, fp, 1);
3194 	free(special);
3195 
3196 	if (grubhd == NULL)
3197 		return (0);
3198 	ret = grubhd[3] == '0';
3199 	free(grubhd);
3200 	return (ret);
3201 }
3202 
3203 /*
3204  * look for matching bootadm entry with specified parameters
3205  * Here are the rules (based on existing usage):
3206  * - If title is specified, match on title only
3207  * - Else, match on grubdisk and module (don't care about kernel line).
3208  *   note that, if root_opt is non-zero, the absence of root line is
3209  *   considered a match.
3210  */
3211 static entry_t *
3212 find_boot_entry(menu_t *mp, char *title, char *root, char *module,
3213     int root_opt, int *entry_num)
3214 {
3215 	int i;
3216 	line_t *lp;
3217 	entry_t *ent;
3218 
3219 	/* find matching entry */
3220 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
3221 		lp = ent->start;
3222 
3223 		/* first line of entry must be bootadm comment */
3224 		lp = ent->start;
3225 		if (lp->flags != BAM_COMMENT ||
3226 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
3227 			continue;
3228 		}
3229 
3230 		/* advance to title line */
3231 		lp = lp->next;
3232 		if (title) {
3233 			if (lp->flags == BAM_TITLE && lp->arg &&
3234 			    strcmp(lp->arg, title) == 0)
3235 				break;
3236 			continue;	/* check title only */
3237 		}
3238 
3239 		lp = lp->next;	/* advance to root line */
3240 		if (lp == NULL || strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3241 			/* root command found, match grub disk */
3242 			if (strcmp(lp->arg, root) != 0) {
3243 				continue;
3244 			}
3245 			lp = lp->next;	/* advance to kernel line */
3246 		} else {
3247 			/* no root command, see if root is optional */
3248 			if (root_opt == 0) {
3249 				continue;
3250 			}
3251 		}
3252 
3253 		if (lp == NULL || lp->next == NULL) {
3254 			continue;
3255 		}
3256 
3257 		/*
3258 		 * Check for matching module entry (failsafe or normal).  We
3259 		 * use a strncmp to match "module" or "module$", since we
3260 		 * don't know which one it should be.  If it fails to match,
3261 		 * we go around the loop again.
3262 		 */
3263 		lp = lp->next;	/* advance to module line */
3264 		if ((strncmp(lp->cmd, menu_cmds[MODULE_CMD],
3265 		    strlen(menu_cmds[MODULE_CMD])) != 0) ||
3266 		    (strcmp(lp->arg, module) != 0)) {
3267 			continue;
3268 		}
3269 		break;	/* match found */
3270 	}
3271 
3272 	*entry_num = i;
3273 	return (ent);
3274 }
3275 
3276 static int
3277 update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
3278     char *module, int root_opt)
3279 {
3280 	int i, change_kernel = 0;
3281 	entry_t *ent;
3282 	line_t *lp;
3283 	char linebuf[BAM_MAXLINE];
3284 
3285 	/* note: don't match on title, it's updated on upgrade */
3286 	ent = find_boot_entry(mp, NULL, root, module, root_opt, &i);
3287 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
3288 		/*
3289 		 * We may be upgrading a kernel from multiboot to
3290 		 * directboot.  Look for a multiboot entry.
3291 		 */
3292 		ent = find_boot_entry(mp, NULL, root, MULTI_BOOT_ARCHIVE,
3293 		    root_opt, &i);
3294 		if (ent != NULL) {
3295 			change_kernel = 1;
3296 		}
3297 	}
3298 	if (ent == NULL)
3299 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
3300 		    kernel, module));
3301 
3302 	/* replace title of exiting entry and delete root line */
3303 	lp = ent->start;
3304 	lp = lp->next;	/* title line */
3305 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3306 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3307 	free(lp->arg);
3308 	free(lp->line);
3309 	lp->arg = s_strdup(title);
3310 	lp->line = s_strdup(linebuf);
3311 
3312 	lp = lp->next;	/* root line */
3313 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3314 		if (root_opt) {		/* root line not needed */
3315 			line_t *tmp = lp;
3316 			lp = lp->next;
3317 			unlink_line(mp, tmp);
3318 			line_free(tmp);
3319 		} else
3320 			lp = lp->next;
3321 	}
3322 
3323 	if (change_kernel) {
3324 		/*
3325 		 * We're upgrading from multiboot to directboot.
3326 		 */
3327 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
3328 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3329 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
3330 			    kernel);
3331 			free(lp->arg);
3332 			free(lp->line);
3333 			lp->arg = s_strdup(kernel);
3334 			lp->line = s_strdup(linebuf);
3335 			lp = lp->next;
3336 		}
3337 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
3338 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3339 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
3340 			    module);
3341 			free(lp->arg);
3342 			free(lp->line);
3343 			lp->arg = s_strdup(module);
3344 			lp->line = s_strdup(linebuf);
3345 			lp = lp->next;
3346 		}
3347 	}
3348 	return (i);
3349 }
3350 
3351 /*ARGSUSED*/
3352 static error_t
3353 update_entry(menu_t *mp, char *menu_root, char *opt)
3354 {
3355 	FILE *fp;
3356 	int entry;
3357 	char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL;
3358 	struct stat sbuf;
3359 	char failsafe[256];
3360 
3361 	assert(mp);
3362 	assert(opt);
3363 
3364 	osdev = strtok(opt, ",");
3365 	osroot = strtok(NULL, ",");
3366 	if (osroot == NULL)
3367 		osroot = menu_root;
3368 	title = get_title(osroot);
3369 
3370 	/* translate /dev/dsk name to grub disk name */
3371 	fp = open_diskmap(osroot);
3372 	if (fp == NULL) {
3373 		bam_error(DISKMAP_FAIL, osdev);
3374 		return (BAM_ERROR);
3375 	}
3376 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
3377 	(void) fclose(fp);
3378 	if (grubdisk == NULL) {
3379 		bam_error(DISKMAP_FAIL, osdev);
3380 		return (BAM_ERROR);
3381 	}
3382 
3383 	/* add the entry for normal Solaris */
3384 	if (bam_direct == BAM_DIRECT_DBOOT) {
3385 		entry = update_boot_entry(mp, title, grubdisk,
3386 		    DIRECT_BOOT_KERNEL, DIRECT_BOOT_ARCHIVE,
3387 		    osroot == menu_root);
3388 	} else {
3389 		entry = update_boot_entry(mp, title, grubdisk,
3390 		    MULTI_BOOT, MULTI_BOOT_ARCHIVE,
3391 		    osroot == menu_root);
3392 	}
3393 
3394 	/* add the entry for failsafe archive */
3395 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
3396 	if (stat(failsafe, &sbuf) == 0) {
3397 
3398 		/* Figure out where the kernel line should point */
3399 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
3400 		    DIRECT_BOOT_FAILSAFE_KERNEL);
3401 		if (stat(failsafe, &sbuf) == 0) {
3402 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
3403 		} else {
3404 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
3405 			    osroot, MULTI_BOOT_FAILSAFE);
3406 			if (stat(failsafe, &sbuf) == 0) {
3407 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
3408 			}
3409 		}
3410 		if (failsafe_kernel != NULL) {
3411 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
3412 			    failsafe_kernel, MINIROOT, osroot == menu_root);
3413 		} else {
3414 			bam_error(NO_FAILSAFE_KERNEL);
3415 		}
3416 	}
3417 	free(grubdisk);
3418 
3419 	if (entry == BAM_ERROR) {
3420 		return (BAM_ERROR);
3421 	}
3422 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3423 	return (BAM_WRITE);
3424 }
3425 
3426 static char *
3427 read_grub_root(void)
3428 {
3429 	FILE *fp;
3430 	struct stat sb;
3431 	char buf[BAM_MAXLINE];
3432 	char *rootstr;
3433 
3434 	if (stat(GRUB_slice, &sb) != 0) {
3435 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3436 		return (NULL);
3437 	}
3438 
3439 	if (stat(GRUB_root, &sb) != 0) {
3440 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3441 		return (NULL);
3442 	}
3443 
3444 	fp = fopen(GRUB_root, "r");
3445 	if (fp == NULL) {
3446 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3447 		return (NULL);
3448 	}
3449 
3450 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3451 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3452 		(void) fclose(fp);
3453 		return (NULL);
3454 	}
3455 
3456 	/*
3457 	 * Copy buf here as check below may trash the buffer
3458 	 */
3459 	rootstr = s_strdup(buf);
3460 
3461 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3462 		bam_error(BAD_ROOT_FILE, GRUB_root);
3463 		free(rootstr);
3464 		rootstr = NULL;
3465 	}
3466 
3467 	(void) fclose(fp);
3468 
3469 	return (rootstr);
3470 }
3471 
3472 static void
3473 save_default_entry(menu_t *mp, const char *which)
3474 {
3475 	int lineNum, entryNum;
3476 	int entry = 0;	/* default is 0 */
3477 	char linebuf[BAM_MAXLINE];
3478 	line_t *lp = mp->curdefault;
3479 
3480 	if (mp->start) {
3481 		lineNum = mp->end->lineNum;
3482 		entryNum = mp->end->entryNum;
3483 	} else {
3484 		lineNum = LINE_INIT;
3485 		entryNum = ENTRY_INIT;
3486 	}
3487 
3488 	if (lp)
3489 		entry = s_strtol(lp->arg);
3490 
3491 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
3492 	line_parser(mp, linebuf, &lineNum, &entryNum);
3493 }
3494 
3495 static void
3496 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
3497 {
3498 	int entry;
3499 	char *str;
3500 
3501 	if (lp == NULL)
3502 		return;		/* nothing to restore */
3503 
3504 	str = lp->arg + strlen(which);
3505 	entry = s_strtol(str);
3506 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3507 
3508 	/* delete saved old default line */
3509 	unlink_line(mp, lp);
3510 	line_free(lp);
3511 }
3512 
3513 /*
3514  * This function is for supporting reboot with args.
3515  * The opt value can be:
3516  * NULL		delete temp entry, if present
3517  * entry=#	switches default entry to 1
3518  * else		treated as boot-args and setup a temperary menu entry
3519  *		and make it the default
3520  */
3521 #define	REBOOT_TITLE	"Solaris_reboot_transient"
3522 
3523 /*ARGSUSED*/
3524 static error_t
3525 update_temp(menu_t *mp, char *menupath, char *opt)
3526 {
3527 	int entry;
3528 	char *grubdisk, *rootdev, *path, *opt_ptr;
3529 	char kernbuf[BUFSIZ];
3530 	char args_buf[BUFSIZ];
3531 	struct stat sb;
3532 
3533 	assert(mp);
3534 
3535 	/* If no option, delete exiting reboot menu entry */
3536 	if (opt == NULL) {
3537 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
3538 		    0, &entry);
3539 		if (ent == NULL)	/* not found is ok */
3540 			return (BAM_SUCCESS);
3541 		(void) do_delete(mp, entry);
3542 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
3543 		mp->olddefault = NULL;
3544 		return (BAM_WRITE);
3545 	}
3546 
3547 	/* if entry= is specified, set the default entry */
3548 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
3549 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
3550 		/* this is entry=# option */
3551 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
3552 	}
3553 
3554 	/*
3555 	 * add a new menu entry base on opt and make it the default
3556 	 */
3557 	grubdisk = NULL;
3558 	if (stat(GRUB_slice, &sb) != 0) {
3559 		/*
3560 		 * 1. First get root disk name from mnttab
3561 		 * 2. Translate disk name to grub name
3562 		 * 3. Add the new menu entry
3563 		 */
3564 		rootdev = get_special("/");
3565 		if (rootdev) {
3566 			grubdisk = os_to_grubdisk(rootdev, 1);
3567 			free(rootdev);
3568 		}
3569 	} else {
3570 		/*
3571 		 * This is an LU BE. The GRUB_root file
3572 		 * contains entry for GRUB's "root" cmd.
3573 		 */
3574 		grubdisk = read_grub_root();
3575 	}
3576 	if (grubdisk == NULL) {
3577 		bam_error(REBOOT_WITH_ARGS_FAILED);
3578 		return (BAM_ERROR);
3579 	}
3580 
3581 	/* add an entry for Solaris reboot */
3582 	if (bam_direct == BAM_DIRECT_DBOOT) {
3583 		if (opt[0] == '-') {
3584 			/* It's an option - first see if boot-file is set */
3585 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
3586 			    != BAM_SUCCESS)
3587 				return (BAM_ERROR);
3588 			if (kernbuf[0] == '\0')
3589 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
3590 				    BUFSIZ);
3591 			(void) strlcat(kernbuf, " ", BUFSIZ);
3592 			(void) strlcat(kernbuf, opt, BUFSIZ);
3593 		} else if (opt[0] == '/') {
3594 			/* It's a full path, so write it out. */
3595 			(void) strlcpy(kernbuf, opt, BUFSIZ);
3596 
3597 			/*
3598 			 * If someone runs:
3599 			 *
3600 			 *	# eeprom boot-args='-kd'
3601 			 *	# reboot /platform/i86pc/kernel/unix
3602 			 *
3603 			 * we want to use the boot-args as part of the boot
3604 			 * line.  On the other hand, if someone runs:
3605 			 *
3606 			 *	# reboot "/platform/i86pc/kernel/unix -kd"
3607 			 *
3608 			 * we don't need to mess with boot-args.  If there's
3609 			 * no space in the options string, assume we're in the
3610 			 * first case.
3611 			 */
3612 			if (strchr(opt, ' ') == NULL) {
3613 				if (set_kernel(mp, ARGS_CMD, NULL, args_buf,
3614 				    BUFSIZ) != BAM_SUCCESS)
3615 					return (BAM_ERROR);
3616 
3617 				if (args_buf[0] != '\0') {
3618 					(void) strlcat(kernbuf, " ", BUFSIZ);
3619 					(void) strlcat(kernbuf, args_buf,
3620 					    BUFSIZ);
3621 				}
3622 			}
3623 		} else {
3624 			/*
3625 			 * It may be a partial path, or it may be a partial
3626 			 * path followed by options.  Assume that only options
3627 			 * follow a space.  If someone sends us a kernel path
3628 			 * that includes a space, they deserve to be broken.
3629 			 */
3630 			opt_ptr = strchr(opt, ' ');
3631 			if (opt_ptr != NULL) {
3632 				*opt_ptr = '\0';
3633 			}
3634 
3635 			path = expand_path(opt);
3636 			if (path != NULL) {
3637 				(void) strlcpy(kernbuf, path, BUFSIZ);
3638 				free(path);
3639 
3640 				/*
3641 				 * If there were options given, use those.
3642 				 * Otherwise, copy over the default options.
3643 				 */
3644 				if (opt_ptr != NULL) {
3645 					/* Restore the space in opt string */
3646 					*opt_ptr = ' ';
3647 					(void) strlcat(kernbuf, opt_ptr,
3648 					    BUFSIZ);
3649 				} else {
3650 					if (set_kernel(mp, ARGS_CMD, NULL,
3651 					    args_buf, BUFSIZ) != BAM_SUCCESS)
3652 						return (BAM_ERROR);
3653 
3654 					if (args_buf[0] != '\0') {
3655 						(void) strlcat(kernbuf, " ",
3656 						    BUFSIZ);
3657 						(void) strlcat(kernbuf,
3658 						    args_buf, BUFSIZ);
3659 					}
3660 				}
3661 			} else {
3662 				bam_error(UNKNOWN_KERNEL, opt);
3663 				bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
3664 				return (BAM_ERROR);
3665 			}
3666 		}
3667 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3668 		    NULL);
3669 	} else {
3670 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
3671 		    MULTI_BOOT, opt);
3672 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3673 		    MULTI_BOOT_ARCHIVE);
3674 	}
3675 	free(grubdisk);
3676 
3677 	if (entry == BAM_ERROR) {
3678 		bam_error(REBOOT_WITH_ARGS_FAILED);
3679 		return (BAM_ERROR);
3680 	}
3681 
3682 	save_default_entry(mp, BAM_OLDDEF);
3683 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3684 	return (BAM_WRITE);
3685 }
3686 
3687 static error_t
3688 set_global(menu_t *mp, char *globalcmd, int val)
3689 {
3690 	line_t *lp, *found, *last;
3691 	char *cp, *str;
3692 	char prefix[BAM_MAXLINE];
3693 	size_t len;
3694 
3695 	assert(mp);
3696 	assert(globalcmd);
3697 
3698 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3699 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3700 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3701 			bam_error(INVALID_ENTRY, prefix);
3702 			return (BAM_ERROR);
3703 		}
3704 	}
3705 
3706 	found = last = NULL;
3707 	for (lp = mp->start; lp; lp = lp->next) {
3708 		if (lp->flags != BAM_GLOBAL)
3709 			continue;
3710 
3711 		last = lp; /* track the last global found */
3712 
3713 		if (lp->cmd == NULL) {
3714 			bam_error(NO_CMD, lp->lineNum);
3715 			continue;
3716 		}
3717 		if (strcmp(globalcmd, lp->cmd) != 0)
3718 			continue;
3719 
3720 		if (found) {
3721 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
3722 		}
3723 		found = lp;
3724 	}
3725 
3726 	if (found == NULL) {
3727 		lp = s_calloc(1, sizeof (line_t));
3728 		if (last == NULL) {
3729 			lp->next = mp->start;
3730 			mp->start = lp;
3731 			mp->end = (mp->end) ? mp->end : lp;
3732 		} else {
3733 			lp->next = last->next;
3734 			last->next = lp;
3735 			if (lp->next == NULL)
3736 				mp->end = lp;
3737 		}
3738 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
3739 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
3740 		len += 10;	/* val < 10 digits */
3741 		lp->line = s_calloc(1, len);
3742 		(void) snprintf(lp->line, len, "%s%s%d",
3743 		    globalcmd, menu_cmds[SEP_CMD], val);
3744 		return (BAM_WRITE);
3745 	}
3746 
3747 	/*
3748 	 * We are changing an existing entry. Retain any prefix whitespace,
3749 	 * but overwrite everything else. This preserves tabs added for
3750 	 * readability.
3751 	 */
3752 	str = found->line;
3753 	cp = prefix;
3754 	while (*str == ' ' || *str == '\t')
3755 		*(cp++) = *(str++);
3756 	*cp = '\0'; /* Terminate prefix */
3757 	len = strlen(prefix) + strlen(globalcmd);
3758 	len += strlen(menu_cmds[SEP_CMD]) + 10;
3759 
3760 	free(found->line);
3761 	found->line = s_calloc(1, len);
3762 	(void) snprintf(found->line, len,
3763 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
3764 
3765 	return (BAM_WRITE); /* need a write to menu */
3766 }
3767 
3768 /*
3769  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
3770  * expand it to a full unix path.  The calling function is expected to
3771  * output a message if an error occurs and NULL is returned.
3772  */
3773 static char *
3774 expand_path(const char *partial_path)
3775 {
3776 	int new_path_len;
3777 	char *new_path, new_path2[PATH_MAX];
3778 	struct stat sb;
3779 
3780 	new_path_len = strlen(partial_path) + 64;
3781 	new_path = s_calloc(1, new_path_len);
3782 
3783 	/* First, try the simplest case - something like "kernel/unix" */
3784 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
3785 	    partial_path);
3786 	if (stat(new_path, &sb) == 0) {
3787 		return (new_path);
3788 	}
3789 
3790 	if (strcmp(partial_path, "kmdb") == 0) {
3791 		(void) snprintf(new_path, new_path_len, "%s -k",
3792 		    DIRECT_BOOT_KERNEL);
3793 		return (new_path);
3794 	}
3795 
3796 	/*
3797 	 * We've quickly reached unsupported usage.  Try once more to
3798 	 * see if we were just given a glom name.
3799 	 */
3800 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
3801 	    partial_path);
3802 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
3803 	    partial_path);
3804 	if (stat(new_path, &sb) == 0) {
3805 		if (stat(new_path2, &sb) == 0) {
3806 			/*
3807 			 * We matched both, so we actually
3808 			 * want to write the $ISADIR version.
3809 			 */
3810 			(void) snprintf(new_path, new_path_len,
3811 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
3812 			    partial_path);
3813 		}
3814 		return (new_path);
3815 	}
3816 
3817 	free(new_path);
3818 	return (NULL);
3819 }
3820 
3821 /*
3822  * The kernel cmd and arg have been changed, so
3823  * check whether the archive line needs to change.
3824  */
3825 static void
3826 set_archive_line(entry_t *entryp, line_t *kernelp)
3827 {
3828 	line_t *lp = entryp->start;
3829 	char *new_archive;
3830 	menu_cmd_t m_cmd;
3831 
3832 	for (; lp != NULL; lp = lp->next) {
3833 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
3834 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
3835 			break;
3836 		}
3837 		if (lp == entryp->end)
3838 			return;
3839 	}
3840 	if (lp == NULL)
3841 		return;
3842 
3843 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
3844 		new_archive = DIRECT_BOOT_ARCHIVE;
3845 		m_cmd = MODULE_DOLLAR_CMD;
3846 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
3847 		new_archive = DIRECT_BOOT_ARCHIVE_64;
3848 		m_cmd = MODULE_CMD;
3849 	} else {
3850 		new_archive = DIRECT_BOOT_ARCHIVE_32;
3851 		m_cmd = MODULE_CMD;
3852 	}
3853 
3854 	if (strcmp(lp->arg, new_archive) == 0)
3855 		return;
3856 
3857 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
3858 		free(lp->cmd);
3859 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
3860 	}
3861 
3862 	free(lp->arg);
3863 	lp->arg = s_strdup(new_archive);
3864 	update_line(lp);
3865 }
3866 
3867 /*
3868  * Title for an entry to set properties that once went in bootenv.rc.
3869  */
3870 #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
3871 
3872 /*
3873  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
3874  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
3875  * string, reset the value to the default.  If path is a non-zero-length
3876  * string, set the kernel or arguments.
3877  */
3878 static error_t
3879 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
3880 {
3881 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
3882 	entry_t *entryp;
3883 	line_t *ptr, *kernelp;
3884 	char *new_arg, *old_args, *space;
3885 	char *grubdisk, *rootdev, *new_path;
3886 	char old_space;
3887 	size_t old_kernel_len, new_str_len;
3888 	struct stat sb;
3889 
3890 	assert(bufsize > 0);
3891 
3892 	ptr = kernelp = NULL;
3893 	new_arg = old_args = space = NULL;
3894 	grubdisk = rootdev = new_path = NULL;
3895 	buf[0] = '\0';
3896 
3897 	if (bam_direct != BAM_DIRECT_DBOOT) {
3898 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
3899 		return (BAM_ERROR);
3900 	}
3901 
3902 	/*
3903 	 * If a user changed the default entry to a non-bootadm controlled
3904 	 * one, we don't want to mess with it.  Just print an error and
3905 	 * return.
3906 	 */
3907 	if (mp->curdefault) {
3908 		entryNum = s_strtol(mp->curdefault->arg);
3909 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
3910 			if (entryp->entryNum == entryNum)
3911 				break;
3912 		}
3913 		if ((entryp != NULL) &&
3914 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
3915 			bam_error(DEFAULT_NOT_BAM);
3916 			return (BAM_ERROR);
3917 		}
3918 	}
3919 
3920 	entryNum = -1;
3921 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, 0,
3922 	    &entryNum);
3923 
3924 	if (entryp != NULL) {
3925 		for (ptr = entryp->start; ptr && ptr != entryp->end;
3926 		    ptr = ptr->next) {
3927 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
3928 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
3929 				kernelp = ptr;
3930 				break;
3931 			}
3932 		}
3933 		if (kernelp == NULL) {
3934 			bam_error(NO_KERNEL, entryNum);
3935 			return (BAM_ERROR);
3936 		}
3937 
3938 		old_kernel_len = strcspn(kernelp->arg, " \t");
3939 		space = old_args = kernelp->arg + old_kernel_len;
3940 		while ((*old_args == ' ') || (*old_args == '\t'))
3941 			old_args++;
3942 	}
3943 
3944 	if (path == NULL) {
3945 		/* Simply report what was found */
3946 		if (kernelp == NULL)
3947 			return (BAM_SUCCESS);
3948 
3949 		if (optnum == ARGS_CMD) {
3950 			if (old_args[0] != '\0')
3951 				(void) strlcpy(buf, old_args, bufsize);
3952 		} else {
3953 			/*
3954 			 * We need to print the kernel, so we just turn the
3955 			 * first space into a '\0' and print the beginning.
3956 			 * We don't print anything if it's the default kernel.
3957 			 */
3958 			old_space = *space;
3959 			*space = '\0';
3960 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
3961 				(void) strlcpy(buf, kernelp->arg, bufsize);
3962 			*space = old_space;
3963 		}
3964 		return (BAM_SUCCESS);
3965 	}
3966 
3967 	/*
3968 	 * First, check if we're resetting an entry to the default.
3969 	 */
3970 	if ((path[0] == '\0') ||
3971 	    ((optnum == KERNEL_CMD) &&
3972 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
3973 		if ((entryp == NULL) || (kernelp == NULL)) {
3974 			/* No previous entry, it's already the default */
3975 			return (BAM_SUCCESS);
3976 		}
3977 
3978 		/*
3979 		 * Check if we can delete the entry.  If we're resetting the
3980 		 * kernel command, and the args is already empty, or if we're
3981 		 * resetting the args command, and the kernel is already the
3982 		 * default, we can restore the old default and delete the entry.
3983 		 */
3984 		if (((optnum == KERNEL_CMD) &&
3985 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
3986 		    ((optnum == ARGS_CMD) &&
3987 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
3988 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
3989 			kernelp = NULL;
3990 			(void) do_delete(mp, entryNum);
3991 			restore_default_entry(mp, BAM_OLD_RC_DEF,
3992 			    mp->old_rc_default);
3993 			mp->old_rc_default = NULL;
3994 			rv = BAM_WRITE;
3995 			goto done;
3996 		}
3997 
3998 		if (optnum == KERNEL_CMD) {
3999 			/*
4000 			 * At this point, we've already checked that old_args
4001 			 * and entryp are valid pointers.  The "+ 2" is for
4002 			 * a space a the string termination character.
4003 			 */
4004 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
4005 			    strlen(old_args) + 2;
4006 			new_arg = s_calloc(1, new_str_len);
4007 			(void) snprintf(new_arg, new_str_len, "%s %s",
4008 			    DIRECT_BOOT_KERNEL, old_args);
4009 			free(kernelp->arg);
4010 			kernelp->arg = new_arg;
4011 
4012 			/*
4013 			 * We have changed the kernel line, so we may need
4014 			 * to update the archive line as well.
4015 			 */
4016 			set_archive_line(entryp, kernelp);
4017 		} else {
4018 			/*
4019 			 * We're resetting the boot args to nothing, so
4020 			 * we only need to copy the kernel.  We've already
4021 			 * checked that the kernel is not the default.
4022 			 */
4023 			new_arg = s_calloc(1, old_kernel_len + 1);
4024 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
4025 			    kernelp->arg);
4026 			free(kernelp->arg);
4027 			kernelp->arg = new_arg;
4028 		}
4029 		rv = BAM_WRITE;
4030 		goto done;
4031 	}
4032 
4033 	/*
4034 	 * Expand the kernel file to a full path, if necessary
4035 	 */
4036 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
4037 		new_path = expand_path(path);
4038 		if (new_path == NULL) {
4039 			bam_error(UNKNOWN_KERNEL, path);
4040 			return (BAM_ERROR);
4041 		}
4042 		free_new_path = 1;
4043 	} else {
4044 		new_path = path;
4045 		free_new_path = 0;
4046 	}
4047 
4048 	/*
4049 	 * At this point, we know we're setting a new value.  First, take care
4050 	 * of the case where there was no previous entry.
4051 	 */
4052 	if (entryp == NULL) {
4053 		/* Similar to code in update_temp */
4054 		if (stat(GRUB_slice, &sb) != 0) {
4055 			/*
4056 			 * 1. First get root disk name from mnttab
4057 			 * 2. Translate disk name to grub name
4058 			 * 3. Add the new menu entry
4059 			 */
4060 			rootdev = get_special("/");
4061 			if (rootdev) {
4062 				grubdisk = os_to_grubdisk(rootdev, 1);
4063 				free(rootdev);
4064 			}
4065 		} else {
4066 			/*
4067 			 * This is an LU BE. The GRUB_root file
4068 			 * contains entry for GRUB's "root" cmd.
4069 			 */
4070 			grubdisk = read_grub_root();
4071 		}
4072 		if (grubdisk == NULL) {
4073 			bam_error(REBOOT_WITH_ARGS_FAILED);
4074 			rv = BAM_ERROR;
4075 			goto done;
4076 		}
4077 		if (optnum == KERNEL_CMD) {
4078 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
4079 			    grubdisk, new_path, NULL);
4080 		} else {
4081 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
4082 			    strlen(path) + 8;
4083 			new_arg = s_calloc(1, new_str_len);
4084 
4085 			(void) snprintf(new_arg, new_str_len, "%s %s",
4086 			    DIRECT_BOOT_KERNEL, path);
4087 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
4088 			    grubdisk, new_arg, DIRECT_BOOT_ARCHIVE);
4089 		}
4090 		save_default_entry(mp, BAM_OLD_RC_DEF);
4091 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
4092 		rv = BAM_WRITE;
4093 		goto done;
4094 	}
4095 
4096 	/*
4097 	 * There was already an bootenv entry which we need to edit.
4098 	 */
4099 	if (optnum == KERNEL_CMD) {
4100 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
4101 		new_arg = s_calloc(1, new_str_len);
4102 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
4103 		    old_args);
4104 		free(kernelp->arg);
4105 		kernelp->arg = new_arg;
4106 
4107 		/*
4108 		 * If we have changed the kernel line, we may need to update
4109 		 * the archive line as well.
4110 		 */
4111 		set_archive_line(entryp, kernelp);
4112 	} else {
4113 		new_str_len = old_kernel_len + strlen(path) + 8;
4114 		new_arg = s_calloc(1, new_str_len);
4115 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
4116 		(void) strlcat(new_arg, " ", new_str_len);
4117 		(void) strlcat(new_arg, path, new_str_len);
4118 		free(kernelp->arg);
4119 		kernelp->arg = new_arg;
4120 	}
4121 	rv = BAM_WRITE;
4122 
4123 done:
4124 	if ((rv == BAM_WRITE) && kernelp)
4125 		update_line(kernelp);
4126 	if (free_new_path)
4127 		free(new_path);
4128 	return (rv);
4129 }
4130 
4131 /*ARGSUSED*/
4132 static error_t
4133 set_option(menu_t *mp, char *menu_path, char *opt)
4134 {
4135 	int optnum, optval;
4136 	char *val;
4137 	char buf[BUFSIZ] = "";
4138 	error_t rv;
4139 
4140 	assert(mp);
4141 	assert(opt);
4142 
4143 	val = strchr(opt, '=');
4144 	if (val != NULL) {
4145 		*val = '\0';
4146 	}
4147 
4148 	if (strcmp(opt, "default") == 0) {
4149 		optnum = DEFAULT_CMD;
4150 	} else if (strcmp(opt, "timeout") == 0) {
4151 		optnum = TIMEOUT_CMD;
4152 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
4153 		optnum = KERNEL_CMD;
4154 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
4155 		optnum = ARGS_CMD;
4156 	} else {
4157 		bam_error(INVALID_ENTRY, opt);
4158 		return (BAM_ERROR);
4159 	}
4160 
4161 	/*
4162 	 * kernel and args are allowed without "=new_value" strings.  All
4163 	 * others cause errors
4164 	 */
4165 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
4166 		bam_error(INVALID_ENTRY, opt);
4167 		return (BAM_ERROR);
4168 	} else if (val != NULL) {
4169 		*val = '=';
4170 	}
4171 
4172 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
4173 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
4174 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
4175 			(void) printf("%s\n", buf);
4176 		return (rv);
4177 	} else {
4178 		optval = s_strtol(val + 1);
4179 		return (set_global(mp, menu_cmds[optnum], optval));
4180 	}
4181 }
4182 
4183 /*
4184  * The quiet argument suppresses messages. This is used
4185  * when invoked in the context of other commands (e.g. list_entry)
4186  */
4187 static error_t
4188 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
4189 {
4190 	line_t *lp;
4191 	char *arg;
4192 	int done, ret = BAM_SUCCESS;
4193 
4194 	assert(mp);
4195 	assert(menu_path);
4196 	assert(globalcmd);
4197 
4198 	if (mp->start == NULL) {
4199 		if (!quiet)
4200 			bam_error(NO_MENU, menu_path);
4201 		return (BAM_ERROR);
4202 	}
4203 
4204 	done = 0;
4205 	for (lp = mp->start; lp; lp = lp->next) {
4206 		if (lp->flags != BAM_GLOBAL)
4207 			continue;
4208 
4209 		if (lp->cmd == NULL) {
4210 			if (!quiet)
4211 				bam_error(NO_CMD, lp->lineNum);
4212 			continue;
4213 		}
4214 
4215 		if (strcmp(globalcmd, lp->cmd) != 0)
4216 			continue;
4217 
4218 		/* Found global. Check for duplicates */
4219 		if (done && !quiet) {
4220 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
4221 			ret = BAM_ERROR;
4222 		}
4223 
4224 		arg = lp->arg ? lp->arg : "";
4225 		bam_print(GLOBAL_CMD, globalcmd, arg);
4226 		done = 1;
4227 	}
4228 
4229 	if (!done && bam_verbose)
4230 		bam_print(NO_ENTRY, globalcmd);
4231 
4232 	return (ret);
4233 }
4234 
4235 static error_t
4236 menu_write(char *root, menu_t *mp)
4237 {
4238 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
4239 }
4240 
4241 static void
4242 line_free(line_t *lp)
4243 {
4244 	if (lp == NULL)
4245 		return;
4246 
4247 	if (lp->cmd)
4248 		free(lp->cmd);
4249 	if (lp->sep)
4250 		free(lp->sep);
4251 	if (lp->arg)
4252 		free(lp->arg);
4253 	if (lp->line)
4254 		free(lp->line);
4255 	free(lp);
4256 }
4257 
4258 static void
4259 linelist_free(line_t *start)
4260 {
4261 	line_t *lp;
4262 
4263 	while (start) {
4264 		lp = start;
4265 		start = start->next;
4266 		line_free(lp);
4267 	}
4268 }
4269 
4270 static void
4271 filelist_free(filelist_t *flistp)
4272 {
4273 	linelist_free(flistp->head);
4274 	flistp->head = NULL;
4275 	flistp->tail = NULL;
4276 }
4277 
4278 static void
4279 menu_free(menu_t *mp)
4280 {
4281 	entry_t *ent, *tmp;
4282 	assert(mp);
4283 
4284 	if (mp->start)
4285 		linelist_free(mp->start);
4286 	ent = mp->entries;
4287 	while (ent) {
4288 		tmp = ent;
4289 		ent = tmp->next;
4290 		free(tmp);
4291 	}
4292 
4293 	free(mp);
4294 }
4295 
4296 /*
4297  * Utility routines
4298  */
4299 
4300 
4301 /*
4302  * Returns 0 on success
4303  * Any other value indicates an error
4304  */
4305 static int
4306 exec_cmd(char *cmdline, char *output, int64_t osize)
4307 {
4308 	char buf[BUFSIZ];
4309 	int ret;
4310 	FILE *ptr;
4311 	size_t len;
4312 	sigset_t set;
4313 	void (*disp)(int);
4314 
4315 	/*
4316 	 * For security
4317 	 * - only absolute paths are allowed
4318 	 * - set IFS to space and tab
4319 	 */
4320 	if (*cmdline != '/') {
4321 		bam_error(ABS_PATH_REQ, cmdline);
4322 		return (-1);
4323 	}
4324 	(void) putenv("IFS= \t");
4325 
4326 	/*
4327 	 * We may have been exec'ed with SIGCHLD blocked
4328 	 * unblock it here
4329 	 */
4330 	(void) sigemptyset(&set);
4331 	(void) sigaddset(&set, SIGCHLD);
4332 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
4333 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
4334 		return (-1);
4335 	}
4336 
4337 	/*
4338 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
4339 	 */
4340 	disp = sigset(SIGCHLD, SIG_DFL);
4341 	if (disp == SIG_ERR) {
4342 		bam_error(FAILED_SIG, strerror(errno));
4343 		return (-1);
4344 	}
4345 	if (disp == SIG_HOLD) {
4346 		bam_error(BLOCKED_SIG, cmdline);
4347 		return (-1);
4348 	}
4349 
4350 	ptr = popen(cmdline, "r");
4351 	if (ptr == NULL) {
4352 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
4353 		return (-1);
4354 	}
4355 
4356 	/*
4357 	 * If we simply do a pclose() following a popen(), pclose()
4358 	 * will close the reader end of the pipe immediately even
4359 	 * if the child process has not started/exited. pclose()
4360 	 * does wait for cmd to terminate before returning though.
4361 	 * When the executed command writes its output to the pipe
4362 	 * there is no reader process and the command dies with
4363 	 * SIGPIPE. To avoid this we read repeatedly until read
4364 	 * terminates with EOF. This indicates that the command
4365 	 * (writer) has closed the pipe and we can safely do a
4366 	 * pclose().
4367 	 *
4368 	 * Since pclose() does wait for the command to exit,
4369 	 * we can safely reap the exit status of the command
4370 	 * from the value returned by pclose()
4371 	 */
4372 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
4373 		/* if (bam_verbose)  XXX */
4374 			bam_print(PRINT_NO_NEWLINE, buf);
4375 		if (output && osize > 0) {
4376 			(void) snprintf(output, osize, "%s", buf);
4377 			len = strlen(buf);
4378 			output += len;
4379 			osize -= len;
4380 		}
4381 	}
4382 
4383 	ret = pclose(ptr);
4384 	if (ret == -1) {
4385 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
4386 		return (-1);
4387 	}
4388 
4389 	if (WIFEXITED(ret)) {
4390 		return (WEXITSTATUS(ret));
4391 	} else {
4392 		bam_error(EXEC_FAIL, cmdline, ret);
4393 		return (-1);
4394 	}
4395 }
4396 
4397 /*
4398  * Since this function returns -1 on error
4399  * it cannot be used to convert -1. However,
4400  * that is sufficient for what we need.
4401  */
4402 static long
4403 s_strtol(char *str)
4404 {
4405 	long l;
4406 	char *res = NULL;
4407 
4408 	if (str == NULL) {
4409 		return (-1);
4410 	}
4411 
4412 	errno = 0;
4413 	l = strtol(str, &res, 10);
4414 	if (errno || *res != '\0') {
4415 		return (-1);
4416 	}
4417 
4418 	return (l);
4419 }
4420 
4421 /*
4422  * Wrapper around fputs, that adds a newline (since fputs doesn't)
4423  */
4424 static int
4425 s_fputs(char *str, FILE *fp)
4426 {
4427 	char linebuf[BAM_MAXLINE];
4428 
4429 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
4430 	return (fputs(linebuf, fp));
4431 }
4432 
4433 /*
4434  * Wrapper around fgets, that strips newlines returned by fgets
4435  */
4436 char *
4437 s_fgets(char *buf, int buflen, FILE *fp)
4438 {
4439 	int n;
4440 
4441 	buf = fgets(buf, buflen, fp);
4442 	if (buf) {
4443 		n = strlen(buf);
4444 		if (n == buflen - 1 && buf[n-1] != '\n')
4445 			bam_error(TOO_LONG, buflen - 1, buf);
4446 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
4447 	}
4448 
4449 	return (buf);
4450 }
4451 
4452 void *
4453 s_calloc(size_t nelem, size_t sz)
4454 {
4455 	void *ptr;
4456 
4457 	ptr = calloc(nelem, sz);
4458 	if (ptr == NULL) {
4459 		bam_error(NO_MEM, nelem*sz);
4460 		bam_exit(1);
4461 	}
4462 	return (ptr);
4463 }
4464 
4465 void *
4466 s_realloc(void *ptr, size_t sz)
4467 {
4468 	ptr = realloc(ptr, sz);
4469 	if (ptr == NULL) {
4470 		bam_error(NO_MEM, sz);
4471 		bam_exit(1);
4472 	}
4473 	return (ptr);
4474 }
4475 
4476 static char *
4477 s_strdup(char *str)
4478 {
4479 	char *ptr;
4480 
4481 	if (str == NULL)
4482 		return (NULL);
4483 
4484 	ptr = strdup(str);
4485 	if (ptr == NULL) {
4486 		bam_error(NO_MEM, strlen(str) + 1);
4487 		bam_exit(1);
4488 	}
4489 	return (ptr);
4490 }
4491 
4492 /*
4493  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
4494  * Returns 0 otherwise
4495  */
4496 static int
4497 is_amd64(void)
4498 {
4499 	static int amd64 = -1;
4500 	char isabuf[257];	/* from sysinfo(2) manpage */
4501 
4502 	if (amd64 != -1)
4503 		return (amd64);
4504 
4505 	if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
4506 	    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0)
4507 		amd64 = 1;
4508 	else if (strstr(isabuf, "i386") == NULL)
4509 		amd64 = 1;		/* diskless server */
4510 	else
4511 		amd64 = 0;
4512 
4513 	return (amd64);
4514 }
4515 
4516 static void
4517 append_to_flist(filelist_t *flistp, char *s)
4518 {
4519 	line_t *lp;
4520 
4521 	lp = s_calloc(1, sizeof (line_t));
4522 	lp->line = s_strdup(s);
4523 	if (flistp->head == NULL)
4524 		flistp->head = lp;
4525 	else
4526 		flistp->tail->next = lp;
4527 	flistp->tail = lp;
4528 }
4529 
4530 #if defined(__i386)
4531 
4532 UCODE_VENDORS;
4533 
4534 /*ARGSUSED*/
4535 static void
4536 ucode_install(char *root)
4537 {
4538 	int i;
4539 
4540 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
4541 		int cmd_len = PATH_MAX + 256;
4542 		char cmd[PATH_MAX + 256];
4543 		char file[PATH_MAX];
4544 		char timestamp[PATH_MAX];
4545 		struct stat fstatus, tstatus;
4546 		struct utimbuf u_times;
4547 
4548 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt",
4549 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr);
4550 
4551 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
4552 			continue;
4553 
4554 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
4555 
4556 		if (stat(timestamp, &tstatus) == 0 &&
4557 		    fstatus.st_mtime <= tstatus.st_mtime)
4558 			continue;
4559 
4560 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
4561 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
4562 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
4563 		if (system(cmd) != 0)
4564 			return;
4565 
4566 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
4567 			return;
4568 
4569 		u_times.actime = fstatus.st_atime;
4570 		u_times.modtime = fstatus.st_mtime;
4571 		(void) utime(timestamp, &u_times);
4572 	}
4573 }
4574 #endif
4575