xref: /titanic_44/usr/src/cmd/boot/bootadm/bootadm.c (revision c9b6d37c673213b7ad91d849a105790cb469f95b)
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  * Check whether cmd matches the one indexed by which, and whether arg matches
2406  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
2407  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
2408  * strstr(), so it can be a partial match.
2409  */
2410 static int
2411 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
2412 {
2413 	if ((strcmp(cmd, menu_cmds[which]) != 0) &&
2414 	    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
2415 		return (0);
2416 	}
2417 	return (strstr(arg, str) != NULL);
2418 }
2419 
2420 /*
2421  * A line in menu.lst looks like
2422  * [ ]*<cmd>[ \t=]*<arg>*
2423  */
2424 static void
2425 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
2426 {
2427 	/*
2428 	 * save state across calls. This is so that
2429 	 * header gets the right entry# after title has
2430 	 * been processed
2431 	 */
2432 	static line_t *prev = NULL;
2433 	static entry_t *curr_ent = NULL;
2434 	static int in_liveupgrade = 0;
2435 
2436 	line_t	*lp;
2437 	char *cmd, *sep, *arg;
2438 	char save, *cp, *line;
2439 	menu_flag_t flag = BAM_INVALID;
2440 
2441 	if (str == NULL) {
2442 		return;
2443 	}
2444 
2445 	/*
2446 	 * First save a copy of the entire line.
2447 	 * We use this later to set the line field.
2448 	 */
2449 	line = s_strdup(str);
2450 
2451 	/* Eat up leading whitespace */
2452 	while (*str == ' ' || *str == '\t')
2453 		str++;
2454 
2455 	if (*str == '#') {		/* comment */
2456 		cmd = s_strdup("#");
2457 		sep = NULL;
2458 		arg = s_strdup(str + 1);
2459 		flag = BAM_COMMENT;
2460 		if (strstr(arg, BAM_LU_HDR) != NULL) {
2461 			in_liveupgrade = 1;
2462 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
2463 			in_liveupgrade = 0;
2464 		}
2465 	} else if (*str == '\0') {	/* blank line */
2466 		cmd = sep = arg = NULL;
2467 		flag = BAM_EMPTY;
2468 	} else {
2469 		/*
2470 		 * '=' is not a documented separator in grub syntax.
2471 		 * However various development bits use '=' as a
2472 		 * separator. In addition, external users also
2473 		 * use = as a separator. So we will allow that usage.
2474 		 */
2475 		cp = str;
2476 		while (*str != ' ' && *str != '\t' && *str != '=') {
2477 			if (*str == '\0') {
2478 				cmd = s_strdup(cp);
2479 				sep = arg = NULL;
2480 				break;
2481 			}
2482 			str++;
2483 		}
2484 
2485 		if (*str != '\0') {
2486 			save = *str;
2487 			*str = '\0';
2488 			cmd = s_strdup(cp);
2489 			*str = save;
2490 
2491 			str++;
2492 			save = *str;
2493 			*str = '\0';
2494 			sep = s_strdup(str - 1);
2495 			*str = save;
2496 
2497 			while (*str == ' ' || *str == '\t')
2498 				str++;
2499 			if (*str == '\0')
2500 				arg = NULL;
2501 			else
2502 				arg = s_strdup(str);
2503 		}
2504 	}
2505 
2506 	lp = s_calloc(1, sizeof (line_t));
2507 
2508 	lp->cmd = cmd;
2509 	lp->sep = sep;
2510 	lp->arg = arg;
2511 	lp->line = line;
2512 	lp->lineNum = ++(*lineNum);
2513 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
2514 		lp->entryNum = ++(*entryNum);
2515 		lp->flags = BAM_TITLE;
2516 		if (prev && prev->flags == BAM_COMMENT &&
2517 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2518 			prev->entryNum = lp->entryNum;
2519 			curr_ent = boot_entry_new(mp, prev, lp);
2520 			curr_ent->flags = BAM_ENTRY_BOOTADM;
2521 		} else {
2522 			curr_ent = boot_entry_new(mp, lp, lp);
2523 			if (in_liveupgrade) {
2524 				curr_ent->flags = BAM_ENTRY_LU;
2525 			}
2526 		}
2527 		curr_ent->entryNum = *entryNum;
2528 	} else if (flag != BAM_INVALID) {
2529 		/*
2530 		 * For header comments, the entry# is "fixed up"
2531 		 * by the subsequent title
2532 		 */
2533 		lp->entryNum = *entryNum;
2534 		lp->flags = flag;
2535 	} else {
2536 		lp->entryNum = *entryNum;
2537 
2538 		if (*entryNum == ENTRY_INIT) {
2539 			lp->flags = BAM_GLOBAL;
2540 		} else {
2541 			lp->flags = BAM_ENTRY;
2542 
2543 			if (cmd && arg) {
2544 				/*
2545 				 * We only compare for the length of "module"
2546 				 * so that "module$" will also match.
2547 				 */
2548 				if (check_cmd(cmd, MODULE_CMD, arg, MINIROOT))
2549 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
2550 				else if (check_cmd(cmd, KERNEL_CMD, arg,
2551 				    "xen.gz"))
2552 					curr_ent->flags |= BAM_ENTRY_HV;
2553 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
2554 					curr_ent->flags |= BAM_ENTRY_ROOT;
2555 				else if (strcmp(cmd,
2556 				    menu_cmds[CHAINLOADER_CMD]) == 0)
2557 					curr_ent->flags |=
2558 					    BAM_ENTRY_CHAINLOADER;
2559 			}
2560 		}
2561 	}
2562 
2563 	/* record default, old default, and entry line ranges */
2564 	if (lp->flags == BAM_GLOBAL &&
2565 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
2566 		mp->curdefault = lp;
2567 	} else if (lp->flags == BAM_COMMENT &&
2568 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
2569 		mp->olddefault = lp;
2570 	} else if (lp->flags == BAM_COMMENT &&
2571 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
2572 		mp->old_rc_default = lp;
2573 	} else if (lp->flags == BAM_ENTRY ||
2574 	    (lp->flags == BAM_COMMENT &&
2575 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
2576 		boot_entry_addline(curr_ent, lp);
2577 	}
2578 	append_line(mp, lp);
2579 
2580 	prev = lp;
2581 }
2582 
2583 static void
2584 update_numbering(menu_t *mp)
2585 {
2586 	int lineNum;
2587 	int entryNum;
2588 	int old_default_value;
2589 	line_t *lp, *prev, *default_lp, *default_entry;
2590 	char buf[PATH_MAX];
2591 
2592 	if (mp->start == NULL) {
2593 		return;
2594 	}
2595 
2596 	lineNum = LINE_INIT;
2597 	entryNum = ENTRY_INIT;
2598 	old_default_value = ENTRY_INIT;
2599 	lp = default_lp = default_entry = NULL;
2600 
2601 	prev = NULL;
2602 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
2603 		lp->lineNum = ++lineNum;
2604 
2605 		/*
2606 		 * Get the value of the default command
2607 		 */
2608 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
2609 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
2610 		    lp->arg) {
2611 			old_default_value = atoi(lp->arg);
2612 			default_lp = lp;
2613 		}
2614 
2615 		/*
2616 		 * If not boot entry, nothing else to fix for this
2617 		 * entry
2618 		 */
2619 		if (lp->entryNum == ENTRY_INIT)
2620 			continue;
2621 
2622 		/*
2623 		 * Record the position of the default entry.
2624 		 * The following works because global
2625 		 * commands like default and timeout should precede
2626 		 * actual boot entries, so old_default_value
2627 		 * is already known (or default cmd is missing).
2628 		 */
2629 		if (default_entry == NULL &&
2630 		    old_default_value != ENTRY_INIT &&
2631 		    lp->entryNum == old_default_value) {
2632 			default_entry = lp;
2633 		}
2634 
2635 		/*
2636 		 * Now fixup the entry number
2637 		 */
2638 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
2639 			lp->entryNum = ++entryNum;
2640 			/* fixup the bootadm header */
2641 			if (prev && prev->flags == BAM_COMMENT &&
2642 			    prev->arg &&
2643 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2644 				prev->entryNum = lp->entryNum;
2645 			}
2646 		} else {
2647 			lp->entryNum = entryNum;
2648 		}
2649 	}
2650 
2651 	/*
2652 	 * No default command in menu, simply return
2653 	 */
2654 	if (default_lp == NULL) {
2655 		return;
2656 	}
2657 
2658 	free(default_lp->arg);
2659 	free(default_lp->line);
2660 
2661 	if (default_entry == NULL) {
2662 		default_lp->arg = s_strdup("0");
2663 	} else {
2664 		(void) snprintf(buf, sizeof (buf), "%d",
2665 		    default_entry->entryNum);
2666 		default_lp->arg = s_strdup(buf);
2667 	}
2668 
2669 	/*
2670 	 * The following is required since only the line field gets
2671 	 * written back to menu.lst
2672 	 */
2673 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
2674 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
2675 	default_lp->line = s_strdup(buf);
2676 }
2677 
2678 
2679 static menu_t *
2680 menu_read(char *menu_path)
2681 {
2682 	FILE *fp;
2683 	char buf[BAM_MAXLINE], *cp;
2684 	menu_t *mp;
2685 	int line, entry, len, n;
2686 
2687 	mp = s_calloc(1, sizeof (menu_t));
2688 
2689 	fp = fopen(menu_path, "r");
2690 	if (fp == NULL) { /* Let the caller handle this error */
2691 		return (mp);
2692 	}
2693 
2694 
2695 	/* Note: GRUB boot entry number starts with 0 */
2696 	line = LINE_INIT;
2697 	entry = ENTRY_INIT;
2698 	cp = buf;
2699 	len = sizeof (buf);
2700 	while (s_fgets(cp, len, fp) != NULL) {
2701 		n = strlen(cp);
2702 		if (cp[n - 1] == '\\') {
2703 			len -= n - 1;
2704 			assert(len >= 2);
2705 			cp += n - 1;
2706 			continue;
2707 		}
2708 		line_parser(mp, buf, &line, &entry);
2709 		cp = buf;
2710 		len = sizeof (buf);
2711 	}
2712 
2713 	if (fclose(fp) == EOF) {
2714 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
2715 	}
2716 
2717 	return (mp);
2718 }
2719 
2720 static error_t
2721 selector(menu_t *mp, char *opt, int *entry, char **title)
2722 {
2723 	char *eq;
2724 	char *opt_dup;
2725 	int entryNum;
2726 
2727 	assert(mp);
2728 	assert(mp->start);
2729 	assert(opt);
2730 
2731 	opt_dup = s_strdup(opt);
2732 
2733 	if (entry)
2734 		*entry = ENTRY_INIT;
2735 	if (title)
2736 		*title = NULL;
2737 
2738 	eq = strchr(opt_dup, '=');
2739 	if (eq == NULL) {
2740 		bam_error(INVALID_OPT, opt);
2741 		free(opt_dup);
2742 		return (BAM_ERROR);
2743 	}
2744 
2745 	*eq = '\0';
2746 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
2747 		assert(mp->end);
2748 		entryNum = s_strtol(eq + 1);
2749 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
2750 			bam_error(INVALID_ENTRY, eq + 1);
2751 			free(opt_dup);
2752 			return (BAM_ERROR);
2753 		}
2754 		*entry = entryNum;
2755 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
2756 		*title = opt + (eq - opt_dup) + 1;
2757 	} else {
2758 		bam_error(INVALID_OPT, opt);
2759 		free(opt_dup);
2760 		return (BAM_ERROR);
2761 	}
2762 
2763 	free(opt_dup);
2764 	return (BAM_SUCCESS);
2765 }
2766 
2767 /*
2768  * If invoked with no titles/entries (opt == NULL)
2769  * only title lines in file are printed.
2770  *
2771  * If invoked with a title or entry #, all
2772  * lines in *every* matching entry are listed
2773  */
2774 static error_t
2775 list_entry(menu_t *mp, char *menu_path, char *opt)
2776 {
2777 	line_t *lp;
2778 	int entry = ENTRY_INIT;
2779 	int found;
2780 	char *title = NULL;
2781 
2782 	assert(mp);
2783 	assert(menu_path);
2784 
2785 	if (mp->start == NULL) {
2786 		bam_error(NO_MENU, menu_path);
2787 		return (BAM_ERROR);
2788 	}
2789 
2790 	if (opt != NULL) {
2791 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
2792 			return (BAM_ERROR);
2793 		}
2794 		assert((entry != ENTRY_INIT) ^ (title != NULL));
2795 	} else {
2796 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
2797 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
2798 	}
2799 
2800 	found = 0;
2801 	for (lp = mp->start; lp; lp = lp->next) {
2802 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
2803 			continue;
2804 		if (opt == NULL && lp->flags == BAM_TITLE) {
2805 			bam_print(PRINT_TITLE, lp->entryNum,
2806 			    lp->arg);
2807 			found = 1;
2808 			continue;
2809 		}
2810 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
2811 			bam_print(PRINT, lp->line);
2812 			found = 1;
2813 			continue;
2814 		}
2815 
2816 		/*
2817 		 * We set the entry value here so that all lines
2818 		 * in entry get printed. If we subsequently match
2819 		 * title in other entries, all lines in those
2820 		 * entries get printed as well.
2821 		 */
2822 		if (title && lp->flags == BAM_TITLE && lp->arg &&
2823 		    strncmp(title, lp->arg, strlen(title)) == 0) {
2824 			bam_print(PRINT, lp->line);
2825 			entry = lp->entryNum;
2826 			found = 1;
2827 			continue;
2828 		}
2829 	}
2830 
2831 	if (!found) {
2832 		bam_error(NO_MATCH_ENTRY);
2833 		return (BAM_ERROR);
2834 	}
2835 
2836 	return (BAM_SUCCESS);
2837 }
2838 
2839 int
2840 add_boot_entry(menu_t *mp,
2841 	char *title,
2842 	char *root,
2843 	char *kernel,
2844 	char *mod_kernel,
2845 	char *module)
2846 {
2847 	int lineNum, entryNum;
2848 	char linebuf[BAM_MAXLINE];
2849 	menu_cmd_t k_cmd, m_cmd;
2850 
2851 	assert(mp);
2852 
2853 	if (title == NULL) {
2854 		title = "Solaris";	/* default to Solaris */
2855 	}
2856 	if (kernel == NULL) {
2857 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
2858 		return (BAM_ERROR);
2859 	}
2860 	if (module == NULL) {
2861 		if (bam_direct != BAM_DIRECT_DBOOT) {
2862 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
2863 			return (BAM_ERROR);
2864 		}
2865 
2866 		/* Figure the commands out from the kernel line */
2867 		if (strstr(kernel, "$ISADIR") != NULL) {
2868 			module = DIRECT_BOOT_ARCHIVE;
2869 			k_cmd = KERNEL_DOLLAR_CMD;
2870 			m_cmd = MODULE_DOLLAR_CMD;
2871 		} else if (strstr(kernel, "amd64") != NULL) {
2872 			module = DIRECT_BOOT_ARCHIVE_64;
2873 			k_cmd = KERNEL_CMD;
2874 			m_cmd = MODULE_CMD;
2875 		} else {
2876 			module = DIRECT_BOOT_ARCHIVE_32;
2877 			k_cmd = KERNEL_CMD;
2878 			m_cmd = MODULE_CMD;
2879 		}
2880 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
2881 	    (strstr(kernel, "$ISADIR") != NULL)) {
2882 		/*
2883 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
2884 		 * command.  Otherwise, use "kernel".
2885 		 */
2886 		k_cmd = KERNEL_DOLLAR_CMD;
2887 		m_cmd = MODULE_DOLLAR_CMD;
2888 	} else {
2889 		k_cmd = KERNEL_CMD;
2890 		m_cmd = MODULE_CMD;
2891 	}
2892 
2893 	if (mp->start) {
2894 		lineNum = mp->end->lineNum;
2895 		entryNum = mp->end->entryNum;
2896 	} else {
2897 		lineNum = LINE_INIT;
2898 		entryNum = ENTRY_INIT;
2899 	}
2900 
2901 	/*
2902 	 * No separator for comment (HDR/FTR) commands
2903 	 * The syntax for comments is #<comment>
2904 	 */
2905 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
2906 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
2907 	line_parser(mp, linebuf, &lineNum, &entryNum);
2908 
2909 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2910 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
2911 	line_parser(mp, linebuf, &lineNum, &entryNum);
2912 
2913 	if (root) {
2914 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2915 		    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
2916 		line_parser(mp, linebuf, &lineNum, &entryNum);
2917 	}
2918 
2919 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2920 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
2921 	line_parser(mp, linebuf, &lineNum, &entryNum);
2922 
2923 	if (mod_kernel != NULL) {
2924 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2925 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
2926 		line_parser(mp, linebuf, &lineNum, &entryNum);
2927 	}
2928 
2929 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2930 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
2931 	line_parser(mp, linebuf, &lineNum, &entryNum);
2932 
2933 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
2934 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
2935 	line_parser(mp, linebuf, &lineNum, &entryNum);
2936 
2937 	return (entryNum);
2938 }
2939 
2940 static error_t
2941 do_delete(menu_t *mp, int entryNum)
2942 {
2943 	line_t *lp, *freed;
2944 	entry_t *ent, *tmp;
2945 	int deleted;
2946 
2947 	assert(entryNum != ENTRY_INIT);
2948 
2949 	ent = mp->entries;
2950 	while (ent) {
2951 		lp = ent->start;
2952 		/* check entry number and make sure it's a bootadm entry */
2953 		if (lp->flags != BAM_COMMENT ||
2954 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
2955 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
2956 			ent = ent->next;
2957 			continue;
2958 		}
2959 
2960 		/* free the entry content */
2961 		do {
2962 			freed = lp;
2963 			lp = lp->next;	/* prev stays the same */
2964 			unlink_line(mp, freed);
2965 			line_free(freed);
2966 		} while (freed != ent->end);
2967 
2968 		/* free the entry_t structure */
2969 		tmp = ent;
2970 		ent = ent->next;
2971 		if (tmp->prev)
2972 			tmp->prev->next = ent;
2973 		else
2974 			mp->entries = ent;
2975 		if (ent)
2976 			ent->prev = tmp->prev;
2977 		deleted = 1;
2978 	}
2979 
2980 	if (!deleted && entryNum != ALL_ENTRIES) {
2981 		bam_error(NO_BOOTADM_MATCH);
2982 		return (BAM_ERROR);
2983 	}
2984 
2985 	/*
2986 	 * Now that we have deleted an entry, update
2987 	 * the entry numbering and the default cmd.
2988 	 */
2989 	update_numbering(mp);
2990 
2991 	return (BAM_SUCCESS);
2992 }
2993 
2994 static error_t
2995 delete_all_entries(menu_t *mp, char *menu_path, char *opt)
2996 {
2997 	assert(mp);
2998 	assert(opt == NULL);
2999 
3000 	if (mp->start == NULL) {
3001 		bam_print(EMPTY_FILE, menu_path);
3002 		return (BAM_SUCCESS);
3003 	}
3004 
3005 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
3006 		return (BAM_ERROR);
3007 	}
3008 
3009 	return (BAM_WRITE);
3010 }
3011 
3012 static FILE *
3013 open_diskmap(char *root)
3014 {
3015 	FILE *fp;
3016 	char cmd[PATH_MAX];
3017 
3018 	/* make sure we have a map file */
3019 	fp = fopen(GRUBDISK_MAP, "r");
3020 	if (fp == NULL) {
3021 		(void) snprintf(cmd, sizeof (cmd),
3022 		    "%s%s > /dev/null", root, CREATE_DISKMAP);
3023 		(void) system(cmd);
3024 		fp = fopen(GRUBDISK_MAP, "r");
3025 	}
3026 	return (fp);
3027 }
3028 
3029 #define	SECTOR_SIZE	512
3030 
3031 static int
3032 get_partition(char *device)
3033 {
3034 	int i, fd, is_pcfs, partno = -1;
3035 	struct mboot *mboot;
3036 	char boot_sect[SECTOR_SIZE];
3037 	char *wholedisk, *slice;
3038 
3039 	/* form whole disk (p0) */
3040 	slice = device + strlen(device) - 2;
3041 	is_pcfs = (*slice != 's');
3042 	if (!is_pcfs)
3043 		*slice = '\0';
3044 	wholedisk = s_calloc(1, strlen(device) + 3);
3045 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
3046 	if (!is_pcfs)
3047 		*slice = 's';
3048 
3049 	/* read boot sector */
3050 	fd = open(wholedisk, O_RDONLY);
3051 	free(wholedisk);
3052 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
3053 		return (partno);
3054 	}
3055 	(void) close(fd);
3056 
3057 	/* parse fdisk table */
3058 	mboot = (struct mboot *)((void *)boot_sect);
3059 	for (i = 0; i < FD_NUMPART; i++) {
3060 		struct ipart *part =
3061 		    (struct ipart *)(uintptr_t)mboot->parts + i;
3062 		if (is_pcfs) {	/* looking for solaris boot part */
3063 			if (part->systid == 0xbe) {
3064 				partno = i;
3065 				break;
3066 			}
3067 		} else {	/* look for solaris partition, old and new */
3068 			if (part->systid == SUNIXOS ||
3069 			    part->systid == SUNIXOS2) {
3070 				partno = i;
3071 				break;
3072 			}
3073 		}
3074 	}
3075 	return (partno);
3076 }
3077 
3078 static char *
3079 get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
3080 {
3081 	char *grubdisk;	/* (hd#,#,#) */
3082 	char *slice;
3083 	char *grubhd;
3084 	int fdiskpart;
3085 	int found = 0;
3086 	char *devname, *ctdname = strstr(rootdev, "dsk/");
3087 	char linebuf[PATH_MAX];
3088 
3089 	if (ctdname == NULL)
3090 		return (NULL);
3091 
3092 	ctdname += strlen("dsk/");
3093 	slice = strrchr(ctdname, 's');
3094 	if (slice)
3095 		*slice = '\0';
3096 
3097 	rewind(fp);
3098 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
3099 		grubhd = strtok(linebuf, " \t\n");
3100 		if (grubhd)
3101 			devname = strtok(NULL, " \t\n");
3102 		else
3103 			devname = NULL;
3104 		if (devname && strcmp(devname, ctdname) == 0) {
3105 			found = 1;
3106 			break;
3107 		}
3108 	}
3109 
3110 	if (slice)
3111 		*slice = 's';
3112 
3113 	if (found == 0) {
3114 		if (bam_verbose)
3115 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
3116 		grubhd = "0";	/* assume disk 0 if can't match */
3117 	}
3118 
3119 	fdiskpart = get_partition(rootdev);
3120 	if (fdiskpart == -1)
3121 		return (NULL);
3122 
3123 	grubdisk = s_calloc(1, 10);
3124 	if (slice) {
3125 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
3126 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
3127 	} else
3128 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
3129 		    grubhd, fdiskpart);
3130 
3131 	/* if root not on bootdev, change GRUB disk to 0 */
3132 	if (!on_bootdev)
3133 		grubdisk[3] = '0';
3134 	return (grubdisk);
3135 }
3136 
3137 static char *
3138 get_title(char *rootdir)
3139 {
3140 	static char title[80];	/* from /etc/release */
3141 	char *cp = NULL, release[PATH_MAX];
3142 	FILE *fp;
3143 
3144 	/* open the /etc/release file */
3145 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
3146 
3147 	fp = fopen(release, "r");
3148 	if (fp == NULL)
3149 		return (NULL);
3150 
3151 	while (s_fgets(title, sizeof (title), fp) != NULL) {
3152 		cp = strstr(title, "Solaris");
3153 		if (cp)
3154 			break;
3155 	}
3156 	(void) fclose(fp);
3157 	return (cp == NULL ? "Solaris" : cp);
3158 }
3159 
3160 char *
3161 get_special(char *mountp)
3162 {
3163 	FILE *mntfp;
3164 	struct mnttab mp = {0}, mpref = {0};
3165 
3166 	mntfp = fopen(MNTTAB, "r");
3167 	if (mntfp == NULL) {
3168 		return (0);
3169 	}
3170 
3171 	if (*mountp == '\0')
3172 		mpref.mnt_mountp = "/";
3173 	else
3174 		mpref.mnt_mountp = mountp;
3175 	if (getmntany(mntfp, &mp, &mpref) != 0) {
3176 		(void) fclose(mntfp);
3177 		return (NULL);
3178 	}
3179 	(void) fclose(mntfp);
3180 
3181 	return (s_strdup(mp.mnt_special));
3182 }
3183 
3184 char *
3185 os_to_grubdisk(char *osdisk, int on_bootdev)
3186 {
3187 	FILE *fp;
3188 	char *grubdisk;
3189 
3190 	/* translate /dev/dsk name to grub disk name */
3191 	fp = open_diskmap("");
3192 	if (fp == NULL) {
3193 		bam_error(DISKMAP_FAIL, osdisk);
3194 		return (NULL);
3195 	}
3196 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
3197 	(void) fclose(fp);
3198 	return (grubdisk);
3199 }
3200 
3201 /*
3202  * Check if root is on the boot device
3203  * Return 0 (false) on error
3204  */
3205 static int
3206 menu_on_bootdev(char *menu_root, FILE *fp)
3207 {
3208 	int ret;
3209 	char *grubhd, *bootp, *special;
3210 
3211 	special = get_special(menu_root);
3212 	if (special == NULL)
3213 		return (0);
3214 	bootp = strstr(special, "p0:boot");
3215 	if (bootp)
3216 		*bootp = '\0';
3217 	grubhd = get_grubdisk(special, fp, 1);
3218 	free(special);
3219 
3220 	if (grubhd == NULL)
3221 		return (0);
3222 	ret = grubhd[3] == '0';
3223 	free(grubhd);
3224 	return (ret);
3225 }
3226 
3227 /*
3228  * look for matching bootadm entry with specified parameters
3229  * Here are the rules (based on existing usage):
3230  * - If title is specified, match on title only
3231  * - Else, match on kernel, grubdisk and module.  Note that, if root_opt is
3232  *   non-zero, the absence of root line is considered a match.
3233  */
3234 static entry_t *
3235 find_boot_entry(menu_t *mp, char *title, char *kernel, char *root,
3236     char *module, int root_opt, int *entry_num)
3237 {
3238 	int i;
3239 	line_t *lp;
3240 	entry_t *ent;
3241 
3242 	/* find matching entry */
3243 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
3244 		lp = ent->start;
3245 
3246 		/* first line of entry must be bootadm comment */
3247 		lp = ent->start;
3248 		if (lp->flags != BAM_COMMENT ||
3249 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
3250 			continue;
3251 		}
3252 
3253 		/* advance to title line */
3254 		lp = lp->next;
3255 		if (title) {
3256 			if (lp->flags == BAM_TITLE && lp->arg &&
3257 			    strcmp(lp->arg, title) == 0)
3258 				break;
3259 			continue;	/* check title only */
3260 		}
3261 
3262 		lp = lp->next;	/* advance to root line */
3263 		if (lp == NULL) {
3264 			continue;
3265 		} else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3266 			/* root command found, match grub disk */
3267 			if (strcmp(lp->arg, root) != 0) {
3268 				continue;
3269 			}
3270 			lp = lp->next;	/* advance to kernel line */
3271 		} else {
3272 			/* no root command, see if root is optional */
3273 			if (root_opt == 0) {
3274 				continue;
3275 			}
3276 		}
3277 
3278 		if (lp == NULL || lp->next == NULL) {
3279 			continue;
3280 		}
3281 
3282 		if (kernel &&
3283 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
3284 			continue;
3285 		}
3286 
3287 		/*
3288 		 * Check for matching module entry (failsafe or normal).
3289 		 * If it fails to match, we go around the loop again.
3290 		 * For xpv entries, there are two module lines, so we
3291 		 * do the check twice.
3292 		 */
3293 		lp = lp->next;	/* advance to module line */
3294 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
3295 		    (((lp = lp->next) != NULL) &&
3296 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
3297 			/* match found */
3298 			break;
3299 		}
3300 	}
3301 
3302 	if (entry_num && ent) {
3303 		*entry_num = i;
3304 	}
3305 	return (ent);
3306 }
3307 
3308 static int
3309 update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
3310     char *mod_kernel, char *module, int root_opt)
3311 {
3312 	int i, change_kernel = 0;
3313 	entry_t *ent;
3314 	line_t *lp;
3315 	char linebuf[BAM_MAXLINE];
3316 
3317 	/* note: don't match on title, it's updated on upgrade */
3318 	ent = find_boot_entry(mp, NULL, kernel, root, module, root_opt, &i);
3319 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
3320 		/*
3321 		 * We may be upgrading a kernel from multiboot to
3322 		 * directboot.  Look for a multiboot entry.
3323 		 */
3324 		ent = find_boot_entry(mp, NULL, "multiboot", root,
3325 		    MULTI_BOOT_ARCHIVE, root_opt, NULL);
3326 		if (ent != NULL) {
3327 			change_kernel = 1;
3328 		}
3329 	}
3330 	if (ent == NULL)
3331 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
3332 		    kernel, mod_kernel, module));
3333 
3334 	/* replace title of exiting entry and delete root line */
3335 	lp = ent->start;
3336 	lp = lp->next;	/* title line */
3337 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3338 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3339 	free(lp->arg);
3340 	free(lp->line);
3341 	lp->arg = s_strdup(title);
3342 	lp->line = s_strdup(linebuf);
3343 
3344 	lp = lp->next;	/* root line */
3345 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3346 		if (root_opt) {		/* root line not needed */
3347 			line_t *tmp = lp;
3348 			lp = lp->next;
3349 			unlink_line(mp, tmp);
3350 			line_free(tmp);
3351 		} else
3352 			lp = lp->next;
3353 	}
3354 
3355 	if (change_kernel) {
3356 		/*
3357 		 * We're upgrading from multiboot to directboot.
3358 		 */
3359 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
3360 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3361 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
3362 			    kernel);
3363 			free(lp->arg);
3364 			free(lp->line);
3365 			lp->arg = s_strdup(kernel);
3366 			lp->line = s_strdup(linebuf);
3367 			lp = lp->next;
3368 		}
3369 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
3370 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3371 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
3372 			    module);
3373 			free(lp->arg);
3374 			free(lp->line);
3375 			lp->arg = s_strdup(module);
3376 			lp->line = s_strdup(linebuf);
3377 			lp = lp->next;
3378 		}
3379 	}
3380 	return (i);
3381 }
3382 
3383 /*ARGSUSED*/
3384 static error_t
3385 update_entry(menu_t *mp, char *menu_root, char *opt)
3386 {
3387 	FILE *fp;
3388 	int entry;
3389 	char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL;
3390 	struct stat sbuf;
3391 	char failsafe[256];
3392 
3393 	assert(mp);
3394 	assert(opt);
3395 
3396 	osdev = strtok(opt, ",");
3397 	osroot = strtok(NULL, ",");
3398 	if (osroot == NULL)
3399 		osroot = menu_root;
3400 	title = get_title(osroot);
3401 
3402 	/* translate /dev/dsk name to grub disk name */
3403 	fp = open_diskmap(osroot);
3404 	if (fp == NULL) {
3405 		bam_error(DISKMAP_FAIL, osdev);
3406 		return (BAM_ERROR);
3407 	}
3408 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
3409 	(void) fclose(fp);
3410 	if (grubdisk == NULL) {
3411 		bam_error(DISKMAP_FAIL, osdev);
3412 		return (BAM_ERROR);
3413 	}
3414 
3415 	/* add the entry for normal Solaris */
3416 	if (bam_direct == BAM_DIRECT_DBOOT) {
3417 		entry = update_boot_entry(mp, title, grubdisk,
3418 		    DIRECT_BOOT_KERNEL, NULL, DIRECT_BOOT_ARCHIVE,
3419 		    osroot == menu_root);
3420 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
3421 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubdisk,
3422 			    XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE,
3423 			    osroot == menu_root);
3424 		}
3425 	} else {
3426 		entry = update_boot_entry(mp, title, grubdisk, MULTI_BOOT,
3427 		    NULL, MULTI_BOOT_ARCHIVE, osroot == menu_root);
3428 	}
3429 
3430 	/*
3431 	 * Add the entry for failsafe archive.  On a bfu'd system, the
3432 	 * failsafe may be different than the installed kernel.
3433 	 */
3434 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
3435 	if (stat(failsafe, &sbuf) == 0) {
3436 
3437 		/* Figure out where the kernel line should point */
3438 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
3439 		    DIRECT_BOOT_FAILSAFE_KERNEL);
3440 		if (stat(failsafe, &sbuf) == 0) {
3441 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
3442 		} else {
3443 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
3444 			    osroot, MULTI_BOOT_FAILSAFE);
3445 			if (stat(failsafe, &sbuf) == 0) {
3446 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
3447 			}
3448 		}
3449 		if (failsafe_kernel != NULL) {
3450 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
3451 			    failsafe_kernel, NULL, MINIROOT,
3452 			    osroot == menu_root);
3453 		}
3454 	}
3455 	free(grubdisk);
3456 
3457 	if (entry == BAM_ERROR) {
3458 		return (BAM_ERROR);
3459 	}
3460 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3461 	return (BAM_WRITE);
3462 }
3463 
3464 static char *
3465 read_grub_root(void)
3466 {
3467 	FILE *fp;
3468 	struct stat sb;
3469 	char buf[BAM_MAXLINE];
3470 	char *rootstr;
3471 
3472 	if (stat(GRUB_slice, &sb) != 0) {
3473 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3474 		return (NULL);
3475 	}
3476 
3477 	if (stat(GRUB_root, &sb) != 0) {
3478 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3479 		return (NULL);
3480 	}
3481 
3482 	fp = fopen(GRUB_root, "r");
3483 	if (fp == NULL) {
3484 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3485 		return (NULL);
3486 	}
3487 
3488 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3489 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3490 		(void) fclose(fp);
3491 		return (NULL);
3492 	}
3493 
3494 	/*
3495 	 * Copy buf here as check below may trash the buffer
3496 	 */
3497 	rootstr = s_strdup(buf);
3498 
3499 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3500 		bam_error(BAD_ROOT_FILE, GRUB_root);
3501 		free(rootstr);
3502 		rootstr = NULL;
3503 	}
3504 
3505 	(void) fclose(fp);
3506 
3507 	return (rootstr);
3508 }
3509 
3510 static void
3511 save_default_entry(menu_t *mp, const char *which)
3512 {
3513 	int lineNum, entryNum;
3514 	int entry = 0;	/* default is 0 */
3515 	char linebuf[BAM_MAXLINE];
3516 	line_t *lp = mp->curdefault;
3517 
3518 	if (mp->start) {
3519 		lineNum = mp->end->lineNum;
3520 		entryNum = mp->end->entryNum;
3521 	} else {
3522 		lineNum = LINE_INIT;
3523 		entryNum = ENTRY_INIT;
3524 	}
3525 
3526 	if (lp)
3527 		entry = s_strtol(lp->arg);
3528 
3529 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
3530 	line_parser(mp, linebuf, &lineNum, &entryNum);
3531 }
3532 
3533 static void
3534 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
3535 {
3536 	int entry;
3537 	char *str;
3538 
3539 	if (lp == NULL)
3540 		return;		/* nothing to restore */
3541 
3542 	str = lp->arg + strlen(which);
3543 	entry = s_strtol(str);
3544 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3545 
3546 	/* delete saved old default line */
3547 	unlink_line(mp, lp);
3548 	line_free(lp);
3549 }
3550 
3551 /*
3552  * This function is for supporting reboot with args.
3553  * The opt value can be:
3554  * NULL		delete temp entry, if present
3555  * entry=#	switches default entry to 1
3556  * else		treated as boot-args and setup a temperary menu entry
3557  *		and make it the default
3558  */
3559 #define	REBOOT_TITLE	"Solaris_reboot_transient"
3560 
3561 /*ARGSUSED*/
3562 static error_t
3563 update_temp(menu_t *mp, char *menupath, char *opt)
3564 {
3565 	int entry;
3566 	char *grubdisk, *rootdev, *path, *opt_ptr;
3567 	char kernbuf[BUFSIZ];
3568 	char args_buf[BUFSIZ];
3569 	struct stat sb;
3570 
3571 	assert(mp);
3572 
3573 	/* If no option, delete exiting reboot menu entry */
3574 	if (opt == NULL) {
3575 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
3576 		    NULL, 0, &entry);
3577 		if (ent == NULL)	/* not found is ok */
3578 			return (BAM_SUCCESS);
3579 		(void) do_delete(mp, entry);
3580 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
3581 		mp->olddefault = NULL;
3582 		return (BAM_WRITE);
3583 	}
3584 
3585 	/* if entry= is specified, set the default entry */
3586 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
3587 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
3588 		/* this is entry=# option */
3589 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
3590 	}
3591 
3592 	/*
3593 	 * add a new menu entry base on opt and make it the default
3594 	 */
3595 	grubdisk = NULL;
3596 	if (stat(GRUB_slice, &sb) != 0) {
3597 		/*
3598 		 * 1. First get root disk name from mnttab
3599 		 * 2. Translate disk name to grub name
3600 		 * 3. Add the new menu entry
3601 		 */
3602 		rootdev = get_special("/");
3603 		if (rootdev) {
3604 			grubdisk = os_to_grubdisk(rootdev, 1);
3605 			free(rootdev);
3606 		}
3607 	} else {
3608 		/*
3609 		 * This is an LU BE. The GRUB_root file
3610 		 * contains entry for GRUB's "root" cmd.
3611 		 */
3612 		grubdisk = read_grub_root();
3613 	}
3614 	if (grubdisk == NULL) {
3615 		bam_error(REBOOT_WITH_ARGS_FAILED);
3616 		return (BAM_ERROR);
3617 	}
3618 
3619 	/* add an entry for Solaris reboot */
3620 	if (bam_direct == BAM_DIRECT_DBOOT) {
3621 		if (opt[0] == '-') {
3622 			/* It's an option - first see if boot-file is set */
3623 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
3624 			    != BAM_SUCCESS)
3625 				return (BAM_ERROR);
3626 			if (kernbuf[0] == '\0')
3627 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
3628 				    BUFSIZ);
3629 			(void) strlcat(kernbuf, " ", BUFSIZ);
3630 			(void) strlcat(kernbuf, opt, BUFSIZ);
3631 		} else if (opt[0] == '/') {
3632 			/* It's a full path, so write it out. */
3633 			(void) strlcpy(kernbuf, opt, BUFSIZ);
3634 
3635 			/*
3636 			 * If someone runs:
3637 			 *
3638 			 *	# eeprom boot-args='-kd'
3639 			 *	# reboot /platform/i86pc/kernel/unix
3640 			 *
3641 			 * we want to use the boot-args as part of the boot
3642 			 * line.  On the other hand, if someone runs:
3643 			 *
3644 			 *	# reboot "/platform/i86pc/kernel/unix -kd"
3645 			 *
3646 			 * we don't need to mess with boot-args.  If there's
3647 			 * no space in the options string, assume we're in the
3648 			 * first case.
3649 			 */
3650 			if (strchr(opt, ' ') == NULL) {
3651 				if (set_kernel(mp, ARGS_CMD, NULL, args_buf,
3652 				    BUFSIZ) != BAM_SUCCESS)
3653 					return (BAM_ERROR);
3654 
3655 				if (args_buf[0] != '\0') {
3656 					(void) strlcat(kernbuf, " ", BUFSIZ);
3657 					(void) strlcat(kernbuf, args_buf,
3658 					    BUFSIZ);
3659 				}
3660 			}
3661 		} else {
3662 			/*
3663 			 * It may be a partial path, or it may be a partial
3664 			 * path followed by options.  Assume that only options
3665 			 * follow a space.  If someone sends us a kernel path
3666 			 * that includes a space, they deserve to be broken.
3667 			 */
3668 			opt_ptr = strchr(opt, ' ');
3669 			if (opt_ptr != NULL) {
3670 				*opt_ptr = '\0';
3671 			}
3672 
3673 			path = expand_path(opt);
3674 			if (path != NULL) {
3675 				(void) strlcpy(kernbuf, path, BUFSIZ);
3676 				free(path);
3677 
3678 				/*
3679 				 * If there were options given, use those.
3680 				 * Otherwise, copy over the default options.
3681 				 */
3682 				if (opt_ptr != NULL) {
3683 					/* Restore the space in opt string */
3684 					*opt_ptr = ' ';
3685 					(void) strlcat(kernbuf, opt_ptr,
3686 					    BUFSIZ);
3687 				} else {
3688 					if (set_kernel(mp, ARGS_CMD, NULL,
3689 					    args_buf, BUFSIZ) != BAM_SUCCESS)
3690 						return (BAM_ERROR);
3691 
3692 					if (args_buf[0] != '\0') {
3693 						(void) strlcat(kernbuf, " ",
3694 						    BUFSIZ);
3695 						(void) strlcat(kernbuf,
3696 						    args_buf, BUFSIZ);
3697 					}
3698 				}
3699 			} else {
3700 				bam_error(UNKNOWN_KERNEL, opt);
3701 				bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
3702 				return (BAM_ERROR);
3703 			}
3704 		}
3705 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3706 		    NULL, NULL);
3707 	} else {
3708 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
3709 		    MULTI_BOOT, opt);
3710 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3711 		    NULL, MULTI_BOOT_ARCHIVE);
3712 	}
3713 	free(grubdisk);
3714 
3715 	if (entry == BAM_ERROR) {
3716 		bam_error(REBOOT_WITH_ARGS_FAILED);
3717 		return (BAM_ERROR);
3718 	}
3719 
3720 	save_default_entry(mp, BAM_OLDDEF);
3721 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3722 	return (BAM_WRITE);
3723 }
3724 
3725 static error_t
3726 set_global(menu_t *mp, char *globalcmd, int val)
3727 {
3728 	line_t *lp, *found, *last;
3729 	char *cp, *str;
3730 	char prefix[BAM_MAXLINE];
3731 	size_t len;
3732 
3733 	assert(mp);
3734 	assert(globalcmd);
3735 
3736 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3737 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3738 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3739 			bam_error(INVALID_ENTRY, prefix);
3740 			return (BAM_ERROR);
3741 		}
3742 	}
3743 
3744 	found = last = NULL;
3745 	for (lp = mp->start; lp; lp = lp->next) {
3746 		if (lp->flags != BAM_GLOBAL)
3747 			continue;
3748 
3749 		last = lp; /* track the last global found */
3750 
3751 		if (lp->cmd == NULL) {
3752 			bam_error(NO_CMD, lp->lineNum);
3753 			continue;
3754 		}
3755 		if (strcmp(globalcmd, lp->cmd) != 0)
3756 			continue;
3757 
3758 		if (found) {
3759 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
3760 		}
3761 		found = lp;
3762 	}
3763 
3764 	if (found == NULL) {
3765 		lp = s_calloc(1, sizeof (line_t));
3766 		if (last == NULL) {
3767 			lp->next = mp->start;
3768 			mp->start = lp;
3769 			mp->end = (mp->end) ? mp->end : lp;
3770 		} else {
3771 			lp->next = last->next;
3772 			last->next = lp;
3773 			if (lp->next == NULL)
3774 				mp->end = lp;
3775 		}
3776 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
3777 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
3778 		len += 10;	/* val < 10 digits */
3779 		lp->line = s_calloc(1, len);
3780 		(void) snprintf(lp->line, len, "%s%s%d",
3781 		    globalcmd, menu_cmds[SEP_CMD], val);
3782 		return (BAM_WRITE);
3783 	}
3784 
3785 	/*
3786 	 * We are changing an existing entry. Retain any prefix whitespace,
3787 	 * but overwrite everything else. This preserves tabs added for
3788 	 * readability.
3789 	 */
3790 	str = found->line;
3791 	cp = prefix;
3792 	while (*str == ' ' || *str == '\t')
3793 		*(cp++) = *(str++);
3794 	*cp = '\0'; /* Terminate prefix */
3795 	len = strlen(prefix) + strlen(globalcmd);
3796 	len += strlen(menu_cmds[SEP_CMD]) + 10;
3797 
3798 	free(found->line);
3799 	found->line = s_calloc(1, len);
3800 	(void) snprintf(found->line, len,
3801 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
3802 
3803 	return (BAM_WRITE); /* need a write to menu */
3804 }
3805 
3806 /*
3807  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
3808  * expand it to a full unix path.  The calling function is expected to
3809  * output a message if an error occurs and NULL is returned.
3810  */
3811 static char *
3812 expand_path(const char *partial_path)
3813 {
3814 	int new_path_len;
3815 	char *new_path, new_path2[PATH_MAX];
3816 	struct stat sb;
3817 
3818 	new_path_len = strlen(partial_path) + 64;
3819 	new_path = s_calloc(1, new_path_len);
3820 
3821 	/* First, try the simplest case - something like "kernel/unix" */
3822 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
3823 	    partial_path);
3824 	if (stat(new_path, &sb) == 0) {
3825 		return (new_path);
3826 	}
3827 
3828 	if (strcmp(partial_path, "kmdb") == 0) {
3829 		(void) snprintf(new_path, new_path_len, "%s -k",
3830 		    DIRECT_BOOT_KERNEL);
3831 		return (new_path);
3832 	}
3833 
3834 	/*
3835 	 * We've quickly reached unsupported usage.  Try once more to
3836 	 * see if we were just given a glom name.
3837 	 */
3838 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
3839 	    partial_path);
3840 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
3841 	    partial_path);
3842 	if (stat(new_path, &sb) == 0) {
3843 		if (stat(new_path2, &sb) == 0) {
3844 			/*
3845 			 * We matched both, so we actually
3846 			 * want to write the $ISADIR version.
3847 			 */
3848 			(void) snprintf(new_path, new_path_len,
3849 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
3850 			    partial_path);
3851 		}
3852 		return (new_path);
3853 	}
3854 
3855 	free(new_path);
3856 	return (NULL);
3857 }
3858 
3859 /*
3860  * The kernel cmd and arg have been changed, so
3861  * check whether the archive line needs to change.
3862  */
3863 static void
3864 set_archive_line(entry_t *entryp, line_t *kernelp)
3865 {
3866 	line_t *lp = entryp->start;
3867 	char *new_archive;
3868 	menu_cmd_t m_cmd;
3869 
3870 	for (; lp != NULL; lp = lp->next) {
3871 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
3872 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
3873 			break;
3874 		}
3875 		if (lp == entryp->end)
3876 			return;
3877 	}
3878 	if (lp == NULL)
3879 		return;
3880 
3881 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
3882 		new_archive = DIRECT_BOOT_ARCHIVE;
3883 		m_cmd = MODULE_DOLLAR_CMD;
3884 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
3885 		new_archive = DIRECT_BOOT_ARCHIVE_64;
3886 		m_cmd = MODULE_CMD;
3887 	} else {
3888 		new_archive = DIRECT_BOOT_ARCHIVE_32;
3889 		m_cmd = MODULE_CMD;
3890 	}
3891 
3892 	if (strcmp(lp->arg, new_archive) == 0)
3893 		return;
3894 
3895 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
3896 		free(lp->cmd);
3897 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
3898 	}
3899 
3900 	free(lp->arg);
3901 	lp->arg = s_strdup(new_archive);
3902 	update_line(lp);
3903 }
3904 
3905 /*
3906  * Title for an entry to set properties that once went in bootenv.rc.
3907  */
3908 #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
3909 
3910 /*
3911  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
3912  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
3913  * string, reset the value to the default.  If path is a non-zero-length
3914  * string, set the kernel or arguments.
3915  */
3916 static error_t
3917 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
3918 {
3919 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
3920 	entry_t *entryp;
3921 	line_t *ptr, *kernelp;
3922 	char *new_arg, *old_args, *space;
3923 	char *grubdisk, *rootdev, *new_path;
3924 	char old_space;
3925 	size_t old_kernel_len, new_str_len;
3926 	struct stat sb;
3927 
3928 	assert(bufsize > 0);
3929 
3930 	ptr = kernelp = NULL;
3931 	new_arg = old_args = space = NULL;
3932 	grubdisk = rootdev = new_path = NULL;
3933 	buf[0] = '\0';
3934 
3935 	if (bam_direct != BAM_DIRECT_DBOOT) {
3936 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
3937 		return (BAM_ERROR);
3938 	}
3939 
3940 	/*
3941 	 * If a user changed the default entry to a non-bootadm controlled
3942 	 * one, we don't want to mess with it.  Just print an error and
3943 	 * return.
3944 	 */
3945 	if (mp->curdefault) {
3946 		entryNum = s_strtol(mp->curdefault->arg);
3947 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
3948 			if (entryp->entryNum == entryNum)
3949 				break;
3950 		}
3951 		if ((entryp != NULL) &&
3952 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
3953 			bam_error(DEFAULT_NOT_BAM);
3954 			return (BAM_ERROR);
3955 		}
3956 	}
3957 
3958 	entryNum = -1;
3959 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, 0,
3960 	    &entryNum);
3961 
3962 	if (entryp != NULL) {
3963 		for (ptr = entryp->start; ptr && ptr != entryp->end;
3964 		    ptr = ptr->next) {
3965 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
3966 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
3967 				kernelp = ptr;
3968 				break;
3969 			}
3970 		}
3971 		if (kernelp == NULL) {
3972 			bam_error(NO_KERNEL, entryNum);
3973 			return (BAM_ERROR);
3974 		}
3975 
3976 		old_kernel_len = strcspn(kernelp->arg, " \t");
3977 		space = old_args = kernelp->arg + old_kernel_len;
3978 		while ((*old_args == ' ') || (*old_args == '\t'))
3979 			old_args++;
3980 	}
3981 
3982 	if (path == NULL) {
3983 		/* Simply report what was found */
3984 		if (kernelp == NULL)
3985 			return (BAM_SUCCESS);
3986 
3987 		if (optnum == ARGS_CMD) {
3988 			if (old_args[0] != '\0')
3989 				(void) strlcpy(buf, old_args, bufsize);
3990 		} else {
3991 			/*
3992 			 * We need to print the kernel, so we just turn the
3993 			 * first space into a '\0' and print the beginning.
3994 			 * We don't print anything if it's the default kernel.
3995 			 */
3996 			old_space = *space;
3997 			*space = '\0';
3998 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
3999 				(void) strlcpy(buf, kernelp->arg, bufsize);
4000 			*space = old_space;
4001 		}
4002 		return (BAM_SUCCESS);
4003 	}
4004 
4005 	/*
4006 	 * First, check if we're resetting an entry to the default.
4007 	 */
4008 	if ((path[0] == '\0') ||
4009 	    ((optnum == KERNEL_CMD) &&
4010 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
4011 		if ((entryp == NULL) || (kernelp == NULL)) {
4012 			/* No previous entry, it's already the default */
4013 			return (BAM_SUCCESS);
4014 		}
4015 
4016 		/*
4017 		 * Check if we can delete the entry.  If we're resetting the
4018 		 * kernel command, and the args is already empty, or if we're
4019 		 * resetting the args command, and the kernel is already the
4020 		 * default, we can restore the old default and delete the entry.
4021 		 */
4022 		if (((optnum == KERNEL_CMD) &&
4023 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
4024 		    ((optnum == ARGS_CMD) &&
4025 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
4026 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
4027 			kernelp = NULL;
4028 			(void) do_delete(mp, entryNum);
4029 			restore_default_entry(mp, BAM_OLD_RC_DEF,
4030 			    mp->old_rc_default);
4031 			mp->old_rc_default = NULL;
4032 			rv = BAM_WRITE;
4033 			goto done;
4034 		}
4035 
4036 		if (optnum == KERNEL_CMD) {
4037 			/*
4038 			 * At this point, we've already checked that old_args
4039 			 * and entryp are valid pointers.  The "+ 2" is for
4040 			 * a space a the string termination character.
4041 			 */
4042 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
4043 			    strlen(old_args) + 2;
4044 			new_arg = s_calloc(1, new_str_len);
4045 			(void) snprintf(new_arg, new_str_len, "%s %s",
4046 			    DIRECT_BOOT_KERNEL, old_args);
4047 			free(kernelp->arg);
4048 			kernelp->arg = new_arg;
4049 
4050 			/*
4051 			 * We have changed the kernel line, so we may need
4052 			 * to update the archive line as well.
4053 			 */
4054 			set_archive_line(entryp, kernelp);
4055 		} else {
4056 			/*
4057 			 * We're resetting the boot args to nothing, so
4058 			 * we only need to copy the kernel.  We've already
4059 			 * checked that the kernel is not the default.
4060 			 */
4061 			new_arg = s_calloc(1, old_kernel_len + 1);
4062 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
4063 			    kernelp->arg);
4064 			free(kernelp->arg);
4065 			kernelp->arg = new_arg;
4066 		}
4067 		rv = BAM_WRITE;
4068 		goto done;
4069 	}
4070 
4071 	/*
4072 	 * Expand the kernel file to a full path, if necessary
4073 	 */
4074 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
4075 		new_path = expand_path(path);
4076 		if (new_path == NULL) {
4077 			bam_error(UNKNOWN_KERNEL, path);
4078 			return (BAM_ERROR);
4079 		}
4080 		free_new_path = 1;
4081 	} else {
4082 		new_path = path;
4083 		free_new_path = 0;
4084 	}
4085 
4086 	/*
4087 	 * At this point, we know we're setting a new value.  First, take care
4088 	 * of the case where there was no previous entry.
4089 	 */
4090 	if (entryp == NULL) {
4091 		/* Similar to code in update_temp */
4092 		if (stat(GRUB_slice, &sb) != 0) {
4093 			/*
4094 			 * 1. First get root disk name from mnttab
4095 			 * 2. Translate disk name to grub name
4096 			 * 3. Add the new menu entry
4097 			 */
4098 			rootdev = get_special("/");
4099 			if (rootdev) {
4100 				grubdisk = os_to_grubdisk(rootdev, 1);
4101 				free(rootdev);
4102 			}
4103 		} else {
4104 			/*
4105 			 * This is an LU BE. The GRUB_root file
4106 			 * contains entry for GRUB's "root" cmd.
4107 			 */
4108 			grubdisk = read_grub_root();
4109 		}
4110 		if (grubdisk == NULL) {
4111 			bam_error(REBOOT_WITH_ARGS_FAILED);
4112 			rv = BAM_ERROR;
4113 			goto done;
4114 		}
4115 		if (optnum == KERNEL_CMD) {
4116 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
4117 			    grubdisk, new_path, NULL, NULL);
4118 		} else {
4119 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
4120 			    strlen(path) + 8;
4121 			new_arg = s_calloc(1, new_str_len);
4122 
4123 			(void) snprintf(new_arg, new_str_len, "%s %s",
4124 			    DIRECT_BOOT_KERNEL, path);
4125 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
4126 			    grubdisk, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
4127 		}
4128 		save_default_entry(mp, BAM_OLD_RC_DEF);
4129 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
4130 		rv = BAM_WRITE;
4131 		goto done;
4132 	}
4133 
4134 	/*
4135 	 * There was already an bootenv entry which we need to edit.
4136 	 */
4137 	if (optnum == KERNEL_CMD) {
4138 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
4139 		new_arg = s_calloc(1, new_str_len);
4140 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
4141 		    old_args);
4142 		free(kernelp->arg);
4143 		kernelp->arg = new_arg;
4144 
4145 		/*
4146 		 * If we have changed the kernel line, we may need to update
4147 		 * the archive line as well.
4148 		 */
4149 		set_archive_line(entryp, kernelp);
4150 	} else {
4151 		new_str_len = old_kernel_len + strlen(path) + 8;
4152 		new_arg = s_calloc(1, new_str_len);
4153 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
4154 		(void) strlcat(new_arg, " ", new_str_len);
4155 		(void) strlcat(new_arg, path, new_str_len);
4156 		free(kernelp->arg);
4157 		kernelp->arg = new_arg;
4158 	}
4159 	rv = BAM_WRITE;
4160 
4161 done:
4162 	if ((rv == BAM_WRITE) && kernelp)
4163 		update_line(kernelp);
4164 	if (free_new_path)
4165 		free(new_path);
4166 	return (rv);
4167 }
4168 
4169 /*ARGSUSED*/
4170 static error_t
4171 set_option(menu_t *mp, char *menu_path, char *opt)
4172 {
4173 	int optnum, optval;
4174 	char *val;
4175 	char buf[BUFSIZ] = "";
4176 	error_t rv;
4177 
4178 	assert(mp);
4179 	assert(opt);
4180 
4181 	val = strchr(opt, '=');
4182 	if (val != NULL) {
4183 		*val = '\0';
4184 	}
4185 
4186 	if (strcmp(opt, "default") == 0) {
4187 		optnum = DEFAULT_CMD;
4188 	} else if (strcmp(opt, "timeout") == 0) {
4189 		optnum = TIMEOUT_CMD;
4190 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
4191 		optnum = KERNEL_CMD;
4192 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
4193 		optnum = ARGS_CMD;
4194 	} else {
4195 		bam_error(INVALID_ENTRY, opt);
4196 		return (BAM_ERROR);
4197 	}
4198 
4199 	/*
4200 	 * kernel and args are allowed without "=new_value" strings.  All
4201 	 * others cause errors
4202 	 */
4203 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
4204 		bam_error(INVALID_ENTRY, opt);
4205 		return (BAM_ERROR);
4206 	} else if (val != NULL) {
4207 		*val = '=';
4208 	}
4209 
4210 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
4211 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
4212 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
4213 			(void) printf("%s\n", buf);
4214 		return (rv);
4215 	} else {
4216 		optval = s_strtol(val + 1);
4217 		return (set_global(mp, menu_cmds[optnum], optval));
4218 	}
4219 }
4220 
4221 /*
4222  * The quiet argument suppresses messages. This is used
4223  * when invoked in the context of other commands (e.g. list_entry)
4224  */
4225 static error_t
4226 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
4227 {
4228 	line_t *lp;
4229 	char *arg;
4230 	int done, ret = BAM_SUCCESS;
4231 
4232 	assert(mp);
4233 	assert(menu_path);
4234 	assert(globalcmd);
4235 
4236 	if (mp->start == NULL) {
4237 		if (!quiet)
4238 			bam_error(NO_MENU, menu_path);
4239 		return (BAM_ERROR);
4240 	}
4241 
4242 	done = 0;
4243 	for (lp = mp->start; lp; lp = lp->next) {
4244 		if (lp->flags != BAM_GLOBAL)
4245 			continue;
4246 
4247 		if (lp->cmd == NULL) {
4248 			if (!quiet)
4249 				bam_error(NO_CMD, lp->lineNum);
4250 			continue;
4251 		}
4252 
4253 		if (strcmp(globalcmd, lp->cmd) != 0)
4254 			continue;
4255 
4256 		/* Found global. Check for duplicates */
4257 		if (done && !quiet) {
4258 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
4259 			ret = BAM_ERROR;
4260 		}
4261 
4262 		arg = lp->arg ? lp->arg : "";
4263 		bam_print(GLOBAL_CMD, globalcmd, arg);
4264 		done = 1;
4265 	}
4266 
4267 	if (!done && bam_verbose)
4268 		bam_print(NO_ENTRY, globalcmd);
4269 
4270 	return (ret);
4271 }
4272 
4273 static error_t
4274 menu_write(char *root, menu_t *mp)
4275 {
4276 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
4277 }
4278 
4279 static void
4280 line_free(line_t *lp)
4281 {
4282 	if (lp == NULL)
4283 		return;
4284 
4285 	if (lp->cmd)
4286 		free(lp->cmd);
4287 	if (lp->sep)
4288 		free(lp->sep);
4289 	if (lp->arg)
4290 		free(lp->arg);
4291 	if (lp->line)
4292 		free(lp->line);
4293 	free(lp);
4294 }
4295 
4296 static void
4297 linelist_free(line_t *start)
4298 {
4299 	line_t *lp;
4300 
4301 	while (start) {
4302 		lp = start;
4303 		start = start->next;
4304 		line_free(lp);
4305 	}
4306 }
4307 
4308 static void
4309 filelist_free(filelist_t *flistp)
4310 {
4311 	linelist_free(flistp->head);
4312 	flistp->head = NULL;
4313 	flistp->tail = NULL;
4314 }
4315 
4316 static void
4317 menu_free(menu_t *mp)
4318 {
4319 	entry_t *ent, *tmp;
4320 	assert(mp);
4321 
4322 	if (mp->start)
4323 		linelist_free(mp->start);
4324 	ent = mp->entries;
4325 	while (ent) {
4326 		tmp = ent;
4327 		ent = tmp->next;
4328 		free(tmp);
4329 	}
4330 
4331 	free(mp);
4332 }
4333 
4334 /*
4335  * Utility routines
4336  */
4337 
4338 
4339 /*
4340  * Returns 0 on success
4341  * Any other value indicates an error
4342  */
4343 static int
4344 exec_cmd(char *cmdline, char *output, int64_t osize)
4345 {
4346 	char buf[BUFSIZ];
4347 	int ret;
4348 	FILE *ptr;
4349 	size_t len;
4350 	sigset_t set;
4351 	void (*disp)(int);
4352 
4353 	/*
4354 	 * For security
4355 	 * - only absolute paths are allowed
4356 	 * - set IFS to space and tab
4357 	 */
4358 	if (*cmdline != '/') {
4359 		bam_error(ABS_PATH_REQ, cmdline);
4360 		return (-1);
4361 	}
4362 	(void) putenv("IFS= \t");
4363 
4364 	/*
4365 	 * We may have been exec'ed with SIGCHLD blocked
4366 	 * unblock it here
4367 	 */
4368 	(void) sigemptyset(&set);
4369 	(void) sigaddset(&set, SIGCHLD);
4370 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
4371 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
4372 		return (-1);
4373 	}
4374 
4375 	/*
4376 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
4377 	 */
4378 	disp = sigset(SIGCHLD, SIG_DFL);
4379 	if (disp == SIG_ERR) {
4380 		bam_error(FAILED_SIG, strerror(errno));
4381 		return (-1);
4382 	}
4383 	if (disp == SIG_HOLD) {
4384 		bam_error(BLOCKED_SIG, cmdline);
4385 		return (-1);
4386 	}
4387 
4388 	ptr = popen(cmdline, "r");
4389 	if (ptr == NULL) {
4390 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
4391 		return (-1);
4392 	}
4393 
4394 	/*
4395 	 * If we simply do a pclose() following a popen(), pclose()
4396 	 * will close the reader end of the pipe immediately even
4397 	 * if the child process has not started/exited. pclose()
4398 	 * does wait for cmd to terminate before returning though.
4399 	 * When the executed command writes its output to the pipe
4400 	 * there is no reader process and the command dies with
4401 	 * SIGPIPE. To avoid this we read repeatedly until read
4402 	 * terminates with EOF. This indicates that the command
4403 	 * (writer) has closed the pipe and we can safely do a
4404 	 * pclose().
4405 	 *
4406 	 * Since pclose() does wait for the command to exit,
4407 	 * we can safely reap the exit status of the command
4408 	 * from the value returned by pclose()
4409 	 */
4410 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
4411 		/* if (bam_verbose)  XXX */
4412 			bam_print(PRINT_NO_NEWLINE, buf);
4413 		if (output && osize > 0) {
4414 			(void) snprintf(output, osize, "%s", buf);
4415 			len = strlen(buf);
4416 			output += len;
4417 			osize -= len;
4418 		}
4419 	}
4420 
4421 	ret = pclose(ptr);
4422 	if (ret == -1) {
4423 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
4424 		return (-1);
4425 	}
4426 
4427 	if (WIFEXITED(ret)) {
4428 		return (WEXITSTATUS(ret));
4429 	} else {
4430 		bam_error(EXEC_FAIL, cmdline, ret);
4431 		return (-1);
4432 	}
4433 }
4434 
4435 /*
4436  * Since this function returns -1 on error
4437  * it cannot be used to convert -1. However,
4438  * that is sufficient for what we need.
4439  */
4440 static long
4441 s_strtol(char *str)
4442 {
4443 	long l;
4444 	char *res = NULL;
4445 
4446 	if (str == NULL) {
4447 		return (-1);
4448 	}
4449 
4450 	errno = 0;
4451 	l = strtol(str, &res, 10);
4452 	if (errno || *res != '\0') {
4453 		return (-1);
4454 	}
4455 
4456 	return (l);
4457 }
4458 
4459 /*
4460  * Wrapper around fputs, that adds a newline (since fputs doesn't)
4461  */
4462 static int
4463 s_fputs(char *str, FILE *fp)
4464 {
4465 	char linebuf[BAM_MAXLINE];
4466 
4467 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
4468 	return (fputs(linebuf, fp));
4469 }
4470 
4471 /*
4472  * Wrapper around fgets, that strips newlines returned by fgets
4473  */
4474 char *
4475 s_fgets(char *buf, int buflen, FILE *fp)
4476 {
4477 	int n;
4478 
4479 	buf = fgets(buf, buflen, fp);
4480 	if (buf) {
4481 		n = strlen(buf);
4482 		if (n == buflen - 1 && buf[n-1] != '\n')
4483 			bam_error(TOO_LONG, buflen - 1, buf);
4484 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
4485 	}
4486 
4487 	return (buf);
4488 }
4489 
4490 void *
4491 s_calloc(size_t nelem, size_t sz)
4492 {
4493 	void *ptr;
4494 
4495 	ptr = calloc(nelem, sz);
4496 	if (ptr == NULL) {
4497 		bam_error(NO_MEM, nelem*sz);
4498 		bam_exit(1);
4499 	}
4500 	return (ptr);
4501 }
4502 
4503 void *
4504 s_realloc(void *ptr, size_t sz)
4505 {
4506 	ptr = realloc(ptr, sz);
4507 	if (ptr == NULL) {
4508 		bam_error(NO_MEM, sz);
4509 		bam_exit(1);
4510 	}
4511 	return (ptr);
4512 }
4513 
4514 static char *
4515 s_strdup(char *str)
4516 {
4517 	char *ptr;
4518 
4519 	if (str == NULL)
4520 		return (NULL);
4521 
4522 	ptr = strdup(str);
4523 	if (ptr == NULL) {
4524 		bam_error(NO_MEM, strlen(str) + 1);
4525 		bam_exit(1);
4526 	}
4527 	return (ptr);
4528 }
4529 
4530 /*
4531  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
4532  * Returns 0 otherwise
4533  */
4534 static int
4535 is_amd64(void)
4536 {
4537 	static int amd64 = -1;
4538 	char isabuf[257];	/* from sysinfo(2) manpage */
4539 
4540 	if (amd64 != -1)
4541 		return (amd64);
4542 
4543 	if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
4544 	    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0)
4545 		amd64 = 1;
4546 	else if (strstr(isabuf, "i386") == NULL)
4547 		amd64 = 1;		/* diskless server */
4548 	else
4549 		amd64 = 0;
4550 
4551 	return (amd64);
4552 }
4553 
4554 static void
4555 append_to_flist(filelist_t *flistp, char *s)
4556 {
4557 	line_t *lp;
4558 
4559 	lp = s_calloc(1, sizeof (line_t));
4560 	lp->line = s_strdup(s);
4561 	if (flistp->head == NULL)
4562 		flistp->head = lp;
4563 	else
4564 		flistp->tail->next = lp;
4565 	flistp->tail = lp;
4566 }
4567 
4568 #if defined(__i386)
4569 
4570 UCODE_VENDORS;
4571 
4572 /*ARGSUSED*/
4573 static void
4574 ucode_install(char *root)
4575 {
4576 	int i;
4577 
4578 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
4579 		int cmd_len = PATH_MAX + 256;
4580 		char cmd[PATH_MAX + 256];
4581 		char file[PATH_MAX];
4582 		char timestamp[PATH_MAX];
4583 		struct stat fstatus, tstatus;
4584 		struct utimbuf u_times;
4585 
4586 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt",
4587 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr);
4588 
4589 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
4590 			continue;
4591 
4592 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
4593 
4594 		if (stat(timestamp, &tstatus) == 0 &&
4595 		    fstatus.st_mtime <= tstatus.st_mtime)
4596 			continue;
4597 
4598 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
4599 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
4600 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
4601 		if (system(cmd) != 0)
4602 			return;
4603 
4604 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
4605 			return;
4606 
4607 		u_times.actime = fstatus.st_atime;
4608 		u_times.modtime = fstatus.st_mtime;
4609 		(void) utime(timestamp, &u_times);
4610 	}
4611 }
4612 #endif
4613