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