xref: /illumos-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision b610f78eb3ffbdbc0436ad4f8646ed839b30dfe9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * bootadm(1M) is a new utility for managing bootability of
31  * Solaris *Newboot* environments. It has two primary tasks:
32  * 	- Allow end users to manage bootability of Newboot Solaris instances
33  *	- Provide services to other subsystems in Solaris (primarily Install)
34  */
35 
36 /* Headers */
37 #include <stdio.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <stdarg.h>
45 #include <limits.h>
46 #include <signal.h>
47 #include <sys/wait.h>
48 #include <sys/mnttab.h>
49 #include <sys/statvfs.h>
50 #include <libnvpair.h>
51 #include <ftw.h>
52 #include <fcntl.h>
53 #include <strings.h>
54 #include <sys/systeminfo.h>
55 #include <sys/dktp/fdisk.h>
56 
57 #include <pwd.h>
58 #include <grp.h>
59 #include <device_info.h>
60 
61 #include <sys/mount.h>
62 
63 #include <libintl.h>
64 #include <locale.h>
65 
66 #include <assert.h>
67 
68 #include "message.h"
69 
70 #ifndef TEXT_DOMAIN
71 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
72 #endif	/* TEXT_DOMAIN */
73 
74 /* Type definitions */
75 
76 /* Primary subcmds */
77 typedef enum {
78 	BAM_MENU = 3,
79 	BAM_ARCHIVE
80 } subcmd_t;
81 
82 /* GRUB menu per-line classification */
83 typedef enum {
84 	BAM_INVALID = 0,
85 	BAM_EMPTY,
86 	BAM_COMMENT,
87 	BAM_GLOBAL,
88 	BAM_ENTRY,
89 	BAM_TITLE
90 } menu_flag_t;
91 
92 /* struct for menu.lst contents */
93 typedef struct line {
94 	int  lineNum;	/* Line number in menu.lst */
95 	int  entryNum;	/* menu boot entry #. ENTRY_INIT if not applicable */
96 	char *cmd;
97 	char *sep;
98 	char *arg;
99 	char *line;
100 	menu_flag_t flags;
101 	struct line *next;
102 } line_t;
103 
104 typedef struct {
105 	line_t *start;
106 	line_t *end;
107 } menu_t;
108 
109 typedef enum {
110     OPT_ABSENT = 0,	/* No option */
111     OPT_REQ,		/* option required */
112     OPT_OPTIONAL	/* option may or may not be present */
113 } option_t;
114 
115 typedef enum {
116     BAM_ERROR = -1,	/* Must be negative. add_boot_entry() depends on it */
117     BAM_SUCCESS = 0,
118     BAM_WRITE = 2
119 } error_t;
120 
121 typedef struct {
122 	char	*subcmd;
123 	option_t option;
124 	error_t (*handler)();
125 } subcmd_defn_t;
126 
127 
128 #define	BAM_MAXLINE	8192
129 
130 #define	LINE_INIT	0	/* lineNum initial value */
131 #define	ENTRY_INIT	-1	/* entryNum initial value */
132 #define	ALL_ENTRIES	-2	/* selects all boot entries */
133 
134 #define	GRUB_DIR		"/boot/grub"
135 #define	MULTI_BOOT		"/platform/i86pc/multiboot"
136 #define	BOOT_ARCHIVE		"/platform/i86pc/boot_archive"
137 #define	GRUB_MENU		"/boot/grub/menu.lst"
138 #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
139 #define	RAMDISK_SPECIAL		"/ramdisk"
140 
141 /* lock related */
142 #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
143 #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
144 
145 #define	CREATE_RAMDISK		"/boot/solaris/bin/create_ramdisk"
146 #define	CREATE_DISKMAP		"/boot/solaris/bin/create_diskmap"
147 #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
148 
149 #define	GRUB_slice		"/etc/lu/GRUB_slice"
150 #define	GRUB_root		"/etc/lu/GRUB_root"
151 #define	GRUB_backup_menu	"/etc/lu/GRUB_backup_menu"
152 #define	GRUB_slice_mntpt	"/tmp/GRUB_slice_mntpt"
153 #define	LU_ACTIVATE_FILE	"/etc/lu/DelayUpdate/activate.sh"
154 
155 #define	INSTALLGRUB		"/sbin/installgrub"
156 #define	STAGE1			"/boot/grub/stage1"
157 #define	STAGE2			"/boot/grub/stage2"
158 
159 #define	QUIET			1
160 
161 
162 /*
163  * Default file attributes
164  */
165 #define	DEFAULT_DEV_MODE	0644	/* default permissions */
166 #define	DEFAULT_DEV_UID		0	/* user root */
167 #define	DEFAULT_DEV_GID		3	/* group sys */
168 
169 /*
170  * Menu related
171  * menu_cmd_t and menu_cmds must be kept in sync
172  */
173 typedef enum {
174 	DEFAULT_CMD = 0,
175 	TIMEOUT_CMD,
176 	TITLE_CMD,
177 	ROOT_CMD,
178 	KERNEL_CMD,
179 	MODULE_CMD,
180 	SEP_CMD,
181 	COMMENT_CMD
182 } menu_cmd_t;
183 
184 static char *menu_cmds[] = {
185 	"default",	/* DEFAULT_CMD */
186 	"timeout",	/* TIMEOUT_CMD */
187 	"title",	/* TITLE_CMD */
188 	"root",		/* ROOT_CMD */
189 	"kernel",	/* KERNEL_CMD */
190 	"module",	/* MODULE_CMD */
191 	" ",		/* SEP_CMD */
192 	"#",		/* COMMENT_CMD */
193 	NULL
194 };
195 
196 #define	OPT_ENTRY_NUM	"entry"
197 
198 /*
199  * archive related
200  */
201 typedef struct {
202 	line_t *head;
203 	line_t *tail;
204 } filelist_t;
205 
206 #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
207 #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
208 
209 #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
210 #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
211 #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
212 #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
213 
214 #define	BAM_HDR		"---------- ADDED BY BOOTADM - DO NOT EDIT ----------"
215 #define	BAM_FTR		"---------------------END BOOTADM--------------------"
216 
217 
218 /* Globals */
219 static char *prog;
220 static subcmd_t bam_cmd;
221 static char *bam_root;
222 static int bam_rootlen;
223 static int bam_root_readonly;
224 static char *bam_subcmd;
225 static char *bam_opt;
226 static int bam_debug;
227 static char **bam_argv;
228 static int bam_argc;
229 static int bam_force;
230 static int bam_verbose;
231 static int bam_check;
232 static int bam_smf_check;
233 static int bam_lock_fd = -1;
234 static char rootbuf[PATH_MAX] = "/";
235 static int bam_update_all;
236 
237 /* function prototypes */
238 static void parse_args_internal(int argc, char *argv[]);
239 static void parse_args(int argc, char *argv[]);
240 static error_t bam_menu(char *subcmd, char *opt, int argc, char *argv[]);
241 static error_t bam_archive(char *subcmd, char *opt);
242 
243 static void bam_error(char *format, ...);
244 static void bam_print(char *format, ...);
245 static void bam_exit(int excode);
246 static void bam_lock(void);
247 static void bam_unlock(void);
248 
249 static int exec_cmd(char *cmdline, char *output, int64_t osize);
250 static error_t read_globals(menu_t *mp, char *menu_path,
251     char *globalcmd, int quiet);
252 
253 static menu_t *menu_read(char *menu_path);
254 static error_t menu_write(char *root, menu_t *mp);
255 static void linelist_free(line_t *start);
256 static void menu_free(menu_t *mp);
257 static void line_free(line_t *lp);
258 static void filelist_free(filelist_t *flistp);
259 static error_t list2file(char *root, char *tmp,
260     char *final, line_t *start);
261 static error_t list_entry(menu_t *mp, char *menu_path, char *opt);
262 static error_t delete_entry(menu_t *mp, char *menu_path, char *opt);
263 static error_t delete_all_entries(menu_t *mp, char *menu_path, char *opt);
264 static error_t update_entry(menu_t *mp, char *root, char *opt);
265 static error_t update_temp(menu_t *mp, char *root, char *opt);
266 
267 static error_t update_archive(char *root, char *opt);
268 static error_t list_archive(char *root, char *opt);
269 static error_t update_all(char *root, char *opt);
270 static error_t read_list(char *root, filelist_t  *flistp);
271 static error_t set_global(menu_t *mp, char *globalcmd, int val);
272 static error_t set_option(menu_t *mp, char *globalcmd, char *opt);
273 
274 static long s_strtol(char *str);
275 static char *s_fgets(char *buf, int n, FILE *fp);
276 static int s_fputs(char *str, FILE *fp);
277 
278 static void *s_calloc(size_t nelem, size_t sz);
279 static char *s_strdup(char *str);
280 static int is_readonly(char *);
281 static int is_amd64(void);
282 static void append_to_flist(filelist_t *, char *);
283 
284 #if defined(__sparc)
285 static void sparc_abort(void);
286 #endif
287 
288 /* Menu related sub commands */
289 static subcmd_defn_t menu_subcmds[] = {
290 	"set_option",		OPT_OPTIONAL,	set_option,	/* PUB */
291 	"list_entry",		OPT_OPTIONAL,	list_entry,	/* PUB */
292 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, /* PVT */
293 	"update_entry",		OPT_REQ,	update_entry,	/* menu */
294 	"update_temp",		OPT_OPTIONAL,	update_temp,	/* reboot */
295 	NULL,			0,		NULL	/* must be last */
296 };
297 
298 /* Archive related sub commands */
299 static subcmd_defn_t arch_subcmds[] = {
300 	"update",		OPT_ABSENT,	update_archive, /* PUB */
301 	"update_all",		OPT_ABSENT,	update_all,	/* PVT */
302 	"list",			OPT_OPTIONAL,	list_archive,	/* PUB */
303 	NULL,			0,		NULL	/* must be last */
304 };
305 
306 static struct {
307 	nvlist_t *new_nvlp;
308 	nvlist_t *old_nvlp;
309 	int need_update;
310 } walk_arg;
311 
312 static void
313 usage(void)
314 {
315 	(void) fprintf(stderr, "USAGE:\n");
316 
317 
318 	/* archive usage */
319 	(void) fprintf(stderr, "\t%s update-archive [-vn] [-R altroot]\n",
320 	    prog);
321 	(void) fprintf(stderr, "\t%s list-archive [-R altroot]\n", prog);
322 #ifndef __sparc
323 	/* x86 only */
324 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
325 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
326 #endif
327 }
328 
329 int
330 main(int argc, char *argv[])
331 {
332 	error_t ret;
333 
334 	(void) setlocale(LC_ALL, "");
335 	(void) textdomain(TEXT_DOMAIN);
336 
337 	if ((prog = strrchr(argv[0], '/')) == NULL) {
338 		prog = argv[0];
339 	} else {
340 		prog++;
341 	}
342 
343 	if (geteuid() != 0) {
344 		bam_error(MUST_BE_ROOT);
345 		bam_exit(1);
346 	}
347 
348 	bam_lock();
349 
350 	/*
351 	 * Don't depend on caller's umask
352 	 */
353 	(void) umask(0022);
354 
355 	parse_args(argc, argv);
356 
357 #if defined(__sparc)
358 	/*
359 	 * There are only two valid invocations of bootadm
360 	 * on SPARC:
361 	 *
362 	 *	- SPARC diskless server creating boot_archive for i386 clients
363 	 *	- archive creation call during reboot of a SPARC system
364 	 *
365 	 *	The latter should be a NOP
366 	 */
367 	if (bam_cmd != BAM_ARCHIVE) {
368 		sparc_abort();
369 	}
370 #endif
371 
372 	switch (bam_cmd) {
373 		case BAM_MENU:
374 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
375 			break;
376 		case BAM_ARCHIVE:
377 			ret = bam_archive(bam_subcmd, bam_opt);
378 			break;
379 		default:
380 			usage();
381 			bam_exit(1);
382 	}
383 
384 	if (ret != BAM_SUCCESS)
385 		bam_exit(1);
386 
387 	bam_unlock();
388 	return (0);
389 }
390 
391 #if defined(__sparc)
392 
393 static void
394 sparc_abort(void)
395 {
396 	bam_error(NOT_ON_SPARC);
397 	bam_exit(1);
398 }
399 
400 #endif
401 
402 /*
403  * Equivalence of public and internal commands:
404  *	update-archive  -- -a update
405  *	list-archive	-- -a list
406  *	set-menu	-- -m set_option
407  *	list-menu	-- -m list_entry
408  *	update-menu	-- -m update_entry
409  */
410 static struct cmd_map {
411 	char *bam_cmdname;
412 	int bam_cmd;
413 	char *bam_subcmd;
414 } cmd_map[] = {
415 	{ "update-archive",	BAM_ARCHIVE,	"update"},
416 	{ "list-archive",	BAM_ARCHIVE,	"list"},
417 	{ "set-menu",		BAM_MENU,	"set_option"},
418 	{ "list-menu",		BAM_MENU,	"list_entry"},
419 	{ "update-menu",	BAM_MENU,	"update_entry"},
420 	{ NULL,			0,		NULL}
421 };
422 
423 /*
424  * Commands syntax published in bootadm(1M) are parsed here
425  */
426 static void
427 parse_args(int argc, char *argv[])
428 {
429 	struct cmd_map *cmp = cmd_map;
430 
431 	/* command conforming to the final spec */
432 	if (argc > 1 && argv[1][0] != '-') {
433 		/*
434 		 * Map commands to internal table.
435 		 */
436 		while (cmp->bam_cmdname) {
437 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
438 				bam_cmd = cmp->bam_cmd;
439 				bam_subcmd = cmp->bam_subcmd;
440 				break;
441 			}
442 			cmp++;
443 		}
444 		if (cmp->bam_cmdname == NULL) {
445 			usage();
446 			bam_exit(1);
447 		}
448 		argc--;
449 		argv++;
450 	}
451 
452 	parse_args_internal(argc, argv);
453 }
454 
455 /*
456  * A combination of public and private commands are parsed here.
457  * The internal syntax and the corresponding functionality are:
458  *	-a update	-- update-archive
459  *	-a list		-- list-archive
460  *	-a update-all	-- (reboot to sync all mounted OS archive)
461  *	-m update_entry	-- update-menu
462  *	-m list_entry	-- list-menu
463  *	-m update_temp	-- (reboot -- [boot-args])
464  *	-m delete_all_entries -- (called from install)
465  */
466 static void
467 parse_args_internal(int argc, char *argv[])
468 {
469 	int c, error;
470 	extern char *optarg;
471 	extern int optind, opterr;
472 
473 	/* Suppress error message from getopt */
474 	opterr = 0;
475 
476 	error = 0;
477 	while ((c = getopt(argc, argv, "a:d:fm:no:vR:")) != -1) {
478 		switch (c) {
479 		case 'a':
480 			if (bam_cmd) {
481 				error = 1;
482 				bam_error(MULT_CMDS, c);
483 			}
484 			bam_cmd = BAM_ARCHIVE;
485 			bam_subcmd = optarg;
486 			break;
487 		case 'd':
488 			if (bam_debug) {
489 				error = 1;
490 				bam_error(DUP_OPT, c);
491 			}
492 			bam_debug = s_strtol(optarg);
493 			break;
494 		case 'f':
495 			if (bam_force) {
496 				error = 1;
497 				bam_error(DUP_OPT, c);
498 			}
499 			bam_force = 1;
500 			break;
501 		case 'm':
502 			if (bam_cmd) {
503 				error = 1;
504 				bam_error(MULT_CMDS, c);
505 			}
506 			bam_cmd = BAM_MENU;
507 			bam_subcmd = optarg;
508 			break;
509 		case 'n':
510 			if (bam_check) {
511 				error = 1;
512 				bam_error(DUP_OPT, c);
513 			}
514 			bam_check = 1;
515 			bam_smf_check = bam_root_readonly;
516 			break;
517 		case 'o':
518 			if (bam_opt) {
519 				error = 1;
520 				bam_error(DUP_OPT, c);
521 			}
522 			bam_opt = optarg;
523 			break;
524 		case 'v':
525 			if (bam_verbose) {
526 				error = 1;
527 				bam_error(DUP_OPT, c);
528 			}
529 			bam_verbose = 1;
530 			break;
531 		case 'R':
532 			if (bam_root) {
533 				error = 1;
534 				bam_error(DUP_OPT, c);
535 				break;
536 			} else if (realpath(optarg, rootbuf) == NULL) {
537 				error = 1;
538 				bam_error(CANT_RESOLVE, optarg,
539 				    strerror(errno));
540 				break;
541 			}
542 			bam_root = rootbuf;
543 			if (rootbuf[strlen(rootbuf) - 1] != '/')
544 				(void) strcat(rootbuf, "/");
545 			bam_rootlen = strlen(rootbuf);
546 			break;
547 		case '?':
548 			error = 1;
549 			bam_error(BAD_OPT, optopt);
550 			break;
551 		default :
552 			error = 1;
553 			bam_error(BAD_OPT, c);
554 			break;
555 		}
556 	}
557 
558 	/*
559 	 * A command option must be specfied
560 	 */
561 	if (!bam_cmd) {
562 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
563 			usage();
564 			bam_exit(0);
565 		}
566 		bam_error(NEED_CMD);
567 		error = 1;
568 	}
569 
570 	if (error) {
571 		usage();
572 		bam_exit(1);
573 	}
574 
575 	if (optind > argc) {
576 		bam_error(INT_ERROR, "parse_args");
577 		bam_exit(1);
578 	} else if (optind < argc) {
579 		bam_argv = &argv[optind];
580 		bam_argc = argc - optind;
581 	}
582 
583 	/*
584 	 * -n implies verbose mode
585 	 */
586 	if (bam_check)
587 		bam_verbose = 1;
588 }
589 
590 static error_t
591 check_subcmd_and_options(
592 	char *subcmd,
593 	char *opt,
594 	subcmd_defn_t *table,
595 	error_t (**fp)())
596 {
597 	int i;
598 
599 	if (subcmd == NULL) {
600 		bam_error(NEED_SUBCMD);
601 		return (BAM_ERROR);
602 	}
603 
604 	if (bam_root == NULL) {
605 		bam_root = rootbuf;
606 		bam_rootlen = 1;
607 	}
608 
609 	/* verify that subcmd is valid */
610 	for (i = 0; table[i].subcmd != NULL; i++) {
611 		if (strcmp(table[i].subcmd, subcmd) == 0)
612 			break;
613 	}
614 
615 	if (table[i].subcmd == NULL) {
616 		bam_error(INVALID_SUBCMD, subcmd);
617 		return (BAM_ERROR);
618 	}
619 
620 	/* subcmd verifies that opt is appropriate */
621 	if (table[i].option != OPT_OPTIONAL) {
622 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
623 			if (opt)
624 				bam_error(NO_OPT_REQ, subcmd);
625 			else
626 				bam_error(MISS_OPT, subcmd);
627 			return (BAM_ERROR);
628 		}
629 	}
630 
631 	*fp = table[i].handler;
632 
633 	return (BAM_SUCCESS);
634 }
635 
636 
637 static char *
638 mount_grub_slice(int *mnted, char **physlice, int mode)
639 {
640 	struct extmnttab mnt;
641 	struct stat sb;
642 	char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32];
643 	char *mntpt;
644 	int p, l, f;
645 	FILE *fp;
646 
647 	assert(mnted);
648 	*mnted = 0;
649 
650 	/*
651 	 * physlice arg may be NULL
652 	 */
653 	if (physlice)
654 		*physlice = NULL;
655 
656 	if (stat(GRUB_slice, &sb) != 0) {
657 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
658 		return (NULL);
659 	}
660 
661 	fp = fopen(GRUB_slice, "r");
662 	if (fp == NULL) {
663 		bam_error(OPEN_FAIL, GRUB_slice, strerror(errno));
664 		return (NULL);
665 	}
666 
667 	dev[0] = fstype[0] = phys[0] = '\0';
668 	p = sizeof ("PHYS_SLICE=") - 1;
669 	l = sizeof ("LOG_SLICE=") - 1;
670 	f = sizeof ("LOG_FSTYP=") - 1;
671 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
672 		if (strncmp(buf, "PHYS_SLICE=", p) == 0) {
673 			(void) strlcpy(phys, buf + p, sizeof (phys));
674 			continue;
675 		}
676 		if (strncmp(buf, "LOG_SLICE=", l) == 0) {
677 			(void) strlcpy(dev, buf + l, sizeof (dev));
678 			continue;
679 		}
680 		if (strncmp(buf, "LOG_FSTYP=", f) == 0) {
681 			(void) strlcpy(fstype, buf + f, sizeof (fstype));
682 			continue;
683 		}
684 	}
685 	(void) fclose(fp);
686 
687 	if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') {
688 		bam_error(BAD_SLICE_FILE, GRUB_slice);
689 		return (NULL);
690 	}
691 
692 	if (mode != QUIET)
693 		bam_print(USING_GRUB_SLICE, dev);
694 
695 	if (physlice) {
696 		*physlice = s_strdup(phys);
697 	}
698 
699 	/*
700 	 * Check if the slice is already mounted
701 	 */
702 	fp = fopen(MNTTAB, "r");
703 	if (fp == NULL) {
704 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
705 		if (physlice) {
706 			free(*physlice);
707 			*physlice = NULL;
708 		}
709 		return (NULL);
710 	}
711 
712 	resetmnttab(fp);
713 
714 	mntpt = NULL;
715 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
716 		if (strcmp(mnt.mnt_special, dev) == 0) {
717 			mntpt = s_strdup(mnt.mnt_mountp);
718 			break;
719 		}
720 	}
721 
722 	(void) fclose(fp);
723 
724 	if (mntpt) {
725 		return (mntpt);
726 	}
727 
728 
729 	/*
730 	 * GRUB slice is not mounted, we need to mount it now.
731 	 * First create the mountpoint
732 	 */
733 	mntpt = s_calloc(1, PATH_MAX);
734 	(void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid());
735 	if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) {
736 		bam_error(MKDIR_FAILED, mntpt, strerror(errno));
737 		free(mntpt);
738 		if (physlice) {
739 			free(*physlice);
740 			*physlice = NULL;
741 		}
742 		return (NULL);
743 	}
744 
745 	if (mount(dev, mntpt, 0, fstype, NULL, 0, NULL, 0) != 0) {
746 		bam_error(MOUNT_FAILED, dev, fstype, mntpt, strerror(errno));
747 		if (rmdir(mntpt) != 0) {
748 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
749 		}
750 		free(mntpt);
751 		if (physlice) {
752 			free(*physlice);
753 			*physlice = NULL;
754 		}
755 		return (NULL);
756 	}
757 
758 	*mnted = 1;
759 	return (mntpt);
760 }
761 
762 static void
763 umount_grub_slice(int mnted, char *mntpt, char *physlice)
764 {
765 	/*
766 	 * If we have not dealt with GRUB slice
767 	 * we have nothing to do - just return.
768 	 */
769 	if (mntpt == NULL)
770 		return;
771 
772 
773 	/*
774 	 * If we mounted the filesystem earlier in mount_grub_slice()
775 	 * unmount it now.
776 	 */
777 	if (mnted) {
778 		if (umount(mntpt) != 0) {
779 			bam_error(UMOUNT_FAILED, mntpt, strerror(errno));
780 		}
781 		if (rmdir(mntpt) != 0) {
782 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
783 		}
784 	}
785 	if (physlice)
786 		free(physlice);
787 	free(mntpt);
788 }
789 
790 static error_t
791 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
792 {
793 	error_t ret;
794 	char menu_path[PATH_MAX];
795 	menu_t *menu;
796 	char *mntpt, *menu_root;
797 	struct stat sb;
798 	int mnted;	/* set if we did a mount */
799 	int mode;
800 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
801 
802 	/*
803 	 * Check arguments
804 	 */
805 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
806 	if (ret == BAM_ERROR) {
807 		return (BAM_ERROR);
808 	}
809 
810 	if (strcmp(subcmd, "update_temp") == 0)
811 		mode = QUIET;
812 	else
813 		mode = 0;
814 
815 	mntpt = NULL;
816 	mnted = 0;
817 	if (stat(GRUB_slice, &sb) == 0) {
818 		mntpt = mount_grub_slice(&mnted, NULL, mode);
819 		if (mntpt == NULL) {
820 			return (BAM_ERROR);
821 		}
822 		menu_root = mntpt;
823 	} else {
824 		menu_root = bam_root;
825 	}
826 
827 	(void) snprintf(menu_path, sizeof (menu_path), "%s%s",
828 	    menu_root, GRUB_MENU);
829 
830 	menu = menu_read(menu_path);
831 	assert(menu);
832 
833 	/*
834 	 * Special handling for setting timeout and default
835 	 */
836 	if (strcmp(subcmd, "set_option") == 0) {
837 		if (largc != 1 || largv[0] == NULL) {
838 			usage();
839 			umount_grub_slice(mnted, mntpt, NULL);
840 			return (BAM_ERROR);
841 		}
842 		opt = largv[0];
843 	} else if (largc != 0) {
844 		usage();
845 		umount_grub_slice(mnted, mntpt, NULL);
846 		return (BAM_ERROR);
847 	}
848 
849 	/*
850 	 * Once the sub-cmd handler has run
851 	 * only the line field is guaranteed to have valid values
852 	 */
853 	if (strcmp(subcmd, "update_entry") == 0)
854 		ret = f(menu, bam_root, opt);
855 	else
856 		ret = f(menu, menu_path, opt);
857 	if (ret == BAM_WRITE) {
858 		ret = menu_write(menu_root, menu);
859 	}
860 
861 	menu_free(menu);
862 
863 	umount_grub_slice(mnted, mntpt, NULL);
864 
865 	return (ret);
866 }
867 
868 
869 static error_t
870 bam_archive(
871 	char *subcmd,
872 	char *opt)
873 {
874 	error_t ret;
875 	error_t (*f)(char *root, char *opt);
876 
877 	/*
878 	 * Check arguments
879 	 */
880 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
881 	if (ret != BAM_SUCCESS) {
882 		return (BAM_ERROR);
883 	}
884 
885 #if defined(__sparc)
886 	/*
887 	 * A NOP if called on SPARC during reboot
888 	 */
889 	if (strcmp(subcmd, "update_all") == 0)
890 		return (BAM_SUCCESS);
891 	else if (strcmp(subcmd, "update") != 0)
892 		sparc_abort();
893 #endif
894 
895 	/*
896 	 * Check archive not supported with update_all
897 	 * since it is awkward to display out-of-sync
898 	 * information for each BE.
899 	 */
900 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
901 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
902 		return (BAM_ERROR);
903 	}
904 
905 	if (strcmp(subcmd, "update_all") == 0)
906 		bam_update_all = 1;
907 
908 	ret = f(bam_root, opt);
909 
910 	bam_update_all = 0;
911 
912 	return (ret);
913 }
914 
915 /*PRINTFLIKE1*/
916 static void
917 bam_error(char *format, ...)
918 {
919 	va_list ap;
920 
921 	va_start(ap, format);
922 	(void) fprintf(stderr, "%s: ", prog);
923 	(void) vfprintf(stderr, format, ap);
924 	va_end(ap);
925 }
926 
927 /*PRINTFLIKE1*/
928 static void
929 bam_print(char *format, ...)
930 {
931 	va_list ap;
932 
933 	va_start(ap, format);
934 	(void) vfprintf(stdout, format, ap);
935 	va_end(ap);
936 }
937 
938 static void
939 bam_exit(int excode)
940 {
941 	bam_unlock();
942 	exit(excode);
943 }
944 
945 static void
946 bam_lock(void)
947 {
948 	struct flock lock;
949 	pid_t pid;
950 
951 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
952 	if (bam_lock_fd < 0) {
953 		/*
954 		 * We may be invoked early in boot for archive verification.
955 		 * In this case, root is readonly and /var/run may not exist.
956 		 * Proceed without the lock
957 		 */
958 		if (errno == EROFS || errno == ENOENT) {
959 			bam_root_readonly = 1;
960 			return;
961 		}
962 
963 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
964 		bam_exit(1);
965 	}
966 
967 	lock.l_type = F_WRLCK;
968 	lock.l_whence = SEEK_SET;
969 	lock.l_start = 0;
970 	lock.l_len = 0;
971 
972 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
973 		if (errno != EACCES && errno != EAGAIN) {
974 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
975 			(void) close(bam_lock_fd);
976 			bam_lock_fd = -1;
977 			bam_exit(1);
978 		}
979 		pid = 0;
980 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
981 		bam_print(FILE_LOCKED, pid);
982 
983 		lock.l_type = F_WRLCK;
984 		lock.l_whence = SEEK_SET;
985 		lock.l_start = 0;
986 		lock.l_len = 0;
987 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
988 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
989 			(void) close(bam_lock_fd);
990 			bam_lock_fd = -1;
991 			bam_exit(1);
992 		}
993 	}
994 
995 	/* We own the lock now */
996 	pid = getpid();
997 	(void) write(bam_lock_fd, &pid, sizeof (pid));
998 }
999 
1000 static void
1001 bam_unlock(void)
1002 {
1003 	struct flock unlock;
1004 
1005 	/*
1006 	 * NOP if we don't hold the lock
1007 	 */
1008 	if (bam_lock_fd < 0) {
1009 		return;
1010 	}
1011 
1012 	unlock.l_type = F_UNLCK;
1013 	unlock.l_whence = SEEK_SET;
1014 	unlock.l_start = 0;
1015 	unlock.l_len = 0;
1016 
1017 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1018 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1019 	}
1020 
1021 	if (close(bam_lock_fd) == -1) {
1022 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
1023 	}
1024 	bam_lock_fd = -1;
1025 }
1026 
1027 static error_t
1028 list_archive(char *root, char *opt)
1029 {
1030 	filelist_t flist;
1031 	filelist_t *flistp = &flist;
1032 	line_t *lp;
1033 
1034 	assert(root);
1035 	assert(opt == NULL);
1036 
1037 	flistp->head = flistp->tail = NULL;
1038 	if (read_list(root, flistp) != BAM_SUCCESS) {
1039 		return (BAM_ERROR);
1040 	}
1041 	assert(flistp->head && flistp->tail);
1042 
1043 	for (lp = flistp->head; lp; lp = lp->next) {
1044 		bam_print(PRINT, lp->line);
1045 	}
1046 
1047 	filelist_free(flistp);
1048 
1049 	return (BAM_SUCCESS);
1050 }
1051 
1052 /*
1053  * This routine writes a list of lines to a file.
1054  * The list is *not* freed
1055  */
1056 static error_t
1057 list2file(char *root, char *tmp, char *final, line_t *start)
1058 {
1059 	char tmpfile[PATH_MAX];
1060 	char path[PATH_MAX];
1061 	FILE *fp;
1062 	int ret;
1063 	struct stat sb;
1064 	mode_t mode;
1065 	uid_t root_uid;
1066 	gid_t sys_gid;
1067 	struct passwd *pw;
1068 	struct group *gp;
1069 
1070 
1071 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
1072 
1073 	if (start == NULL) {
1074 		if (stat(path, &sb) != -1) {
1075 			bam_print(UNLINK_EMPTY, path);
1076 			if (unlink(path) != 0) {
1077 				bam_error(UNLINK_FAIL, path, strerror(errno));
1078 				return (BAM_ERROR);
1079 			} else {
1080 				return (BAM_SUCCESS);
1081 			}
1082 		}
1083 	}
1084 
1085 	/*
1086 	 * Preserve attributes of existing file if possible,
1087 	 * otherwise ask the system for uid/gid of root/sys.
1088 	 * If all fails, fall back on hard-coded defaults.
1089 	 */
1090 	if (stat(path, &sb) != -1) {
1091 		mode = sb.st_mode;
1092 		root_uid = sb.st_uid;
1093 		sys_gid = sb.st_gid;
1094 	} else {
1095 		mode = DEFAULT_DEV_MODE;
1096 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1097 			root_uid = pw->pw_uid;
1098 		} else {
1099 			if (bam_verbose)
1100 				bam_error(CANT_FIND_USER,
1101 				    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1102 			root_uid = (uid_t)DEFAULT_DEV_UID;
1103 		}
1104 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1105 			sys_gid = gp->gr_gid;
1106 		} else {
1107 			if (bam_verbose)
1108 				bam_error(CANT_FIND_GROUP,
1109 				    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1110 			sys_gid = (gid_t)DEFAULT_DEV_GID;
1111 		}
1112 	}
1113 
1114 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1115 
1116 	/* Truncate tmpfile first */
1117 	fp = fopen(tmpfile, "w");
1118 	if (fp == NULL) {
1119 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1120 		return (BAM_ERROR);
1121 	}
1122 	ret = fclose(fp);
1123 	if (ret == EOF) {
1124 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1125 		return (BAM_ERROR);
1126 	}
1127 
1128 	/* Now open it in append mode */
1129 	fp = fopen(tmpfile, "a");
1130 	if (fp == NULL) {
1131 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1132 		return (BAM_ERROR);
1133 	}
1134 
1135 	for (; start; start = start->next) {
1136 		ret = s_fputs(start->line, fp);
1137 		if (ret == EOF) {
1138 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
1139 			(void) fclose(fp);
1140 			return (BAM_ERROR);
1141 		}
1142 	}
1143 
1144 	ret = fclose(fp);
1145 	if (ret == EOF) {
1146 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1147 		return (BAM_ERROR);
1148 	}
1149 
1150 	/*
1151 	 * Set up desired attributes.  Ignore failures on filesystems
1152 	 * not supporting these operations - pcfs reports unsupported
1153 	 * operations as EINVAL.
1154 	 */
1155 	ret = chmod(tmpfile, mode);
1156 	if (ret == -1 &&
1157 	    errno != EINVAL && errno != ENOTSUP) {
1158 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
1159 		return (BAM_ERROR);
1160 	}
1161 
1162 	ret = chown(tmpfile, root_uid, sys_gid);
1163 	if (ret == -1 &&
1164 	    errno != EINVAL && errno != ENOTSUP) {
1165 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
1166 		return (BAM_ERROR);
1167 	}
1168 
1169 
1170 	/*
1171 	 * Do an atomic rename
1172 	 */
1173 	ret = rename(tmpfile, path);
1174 	if (ret != 0) {
1175 		bam_error(RENAME_FAIL, path, strerror(errno));
1176 		return (BAM_ERROR);
1177 	}
1178 
1179 	return (BAM_SUCCESS);
1180 }
1181 
1182 /*
1183  * This function should always return 0 - since we want
1184  * to create stat data for *all* files in the list.
1185  */
1186 /*ARGSUSED*/
1187 static int
1188 cmpstat(
1189 	const char *file,
1190 	const struct stat *stat,
1191 	int flags,
1192 	struct FTW *ftw)
1193 {
1194 	uint_t sz;
1195 	uint64_t *value;
1196 	uint64_t filestat[2];
1197 	int error;
1198 
1199 	/*
1200 	 * We only want regular files
1201 	 */
1202 	if (!S_ISREG(stat->st_mode))
1203 		return (0);
1204 
1205 	/*
1206 	 * new_nvlp may be NULL if there were errors earlier
1207 	 * but this is not fatal to update determination.
1208 	 */
1209 	if (walk_arg.new_nvlp) {
1210 		filestat[0] = stat->st_size;
1211 		filestat[1] = stat->st_mtime;
1212 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
1213 		    file + bam_rootlen, filestat, 2);
1214 		if (error)
1215 			bam_error(NVADD_FAIL, file, strerror(error));
1216 	}
1217 
1218 	/*
1219 	 * The remaining steps are only required if we haven't made a
1220 	 * decision about update or if we are checking (-n)
1221 	 */
1222 	if (walk_arg.need_update && !bam_check)
1223 		return (0);
1224 
1225 	/*
1226 	 * If we are invoked as part of system/filesyste/boot-archive
1227 	 * SMF service, ignore amd64 modules unless we are booted amd64.
1228 	 */
1229 	if (bam_smf_check && !is_amd64() && strstr(file, "/amd64/") == 0)
1230 		return (0);
1231 
1232 	/*
1233 	 * We need an update if file doesn't exist in old archive
1234 	 */
1235 	if (walk_arg.old_nvlp == NULL ||
1236 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
1237 	    file + bam_rootlen, &value, &sz) != 0) {
1238 		if (bam_smf_check)	/* ignore new during smf check */
1239 			return (0);
1240 		walk_arg.need_update = 1;
1241 		if (bam_verbose)
1242 			bam_print(PARSEABLE_NEW_FILE, file);
1243 		return (0);
1244 	}
1245 
1246 	/*
1247 	 * File exists in old archive. Check if file has changed
1248 	 */
1249 	assert(sz == 2);
1250 	bcopy(value, filestat, sizeof (filestat));
1251 
1252 	if (filestat[0] != stat->st_size ||
1253 	    filestat[1] != stat->st_mtime) {
1254 		walk_arg.need_update = 1;
1255 		if (bam_verbose)
1256 			if (bam_smf_check)
1257 				bam_print("    %s\n", file);
1258 			else
1259 				bam_print(PARSEABLE_OUT_DATE, file);
1260 	}
1261 
1262 	return (0);
1263 }
1264 
1265 /*
1266  * Check flags and presence of required files.
1267  * The force flag and/or absence of files should
1268  * trigger an update.
1269  * Suppress stdout output if check (-n) option is set
1270  * (as -n should only produce parseable output.)
1271  */
1272 static void
1273 check_flags_and_files(char *root)
1274 {
1275 	char path[PATH_MAX];
1276 	struct stat sb;
1277 
1278 	/*
1279 	 * if force, create archive unconditionally
1280 	 */
1281 	if (bam_force) {
1282 		walk_arg.need_update = 1;
1283 		if (bam_verbose && !bam_check)
1284 			bam_print(UPDATE_FORCE);
1285 		return;
1286 	}
1287 
1288 	/*
1289 	 * If archive is missing, create archive
1290 	 */
1291 	(void) snprintf(path, sizeof (path), "%s%s", root, BOOT_ARCHIVE);
1292 	if (stat(path, &sb) != 0) {
1293 		if (bam_verbose && !bam_check)
1294 			bam_print(UPDATE_ARCH_MISS, path);
1295 		walk_arg.need_update = 1;
1296 		return;
1297 	}
1298 }
1299 
1300 static error_t
1301 read_one_list(char *root, filelist_t  *flistp, char *filelist)
1302 {
1303 	char path[PATH_MAX];
1304 	FILE *fp;
1305 	char buf[BAM_MAXLINE];
1306 
1307 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
1308 
1309 	fp = fopen(path, "r");
1310 	if (fp == NULL) {
1311 		if (bam_debug)
1312 			bam_error(FLIST_FAIL, path, strerror(errno));
1313 		return (BAM_ERROR);
1314 	}
1315 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1316 		/* skip blank lines */
1317 		if (strspn(buf, " \t") == strlen(buf))
1318 			continue;
1319 		append_to_flist(flistp, buf);
1320 	}
1321 	if (fclose(fp) != 0) {
1322 		bam_error(CLOSE_FAIL, path, strerror(errno));
1323 		return (BAM_ERROR);
1324 	}
1325 	return (BAM_SUCCESS);
1326 }
1327 
1328 static error_t
1329 read_list(char *root, filelist_t  *flistp)
1330 {
1331 	int rval;
1332 
1333 	flistp->head = flistp->tail = NULL;
1334 
1335 	/*
1336 	 * Read current lists of files - only the first is mandatory
1337 	 */
1338 	rval = read_one_list(root, flistp, BOOT_FILE_LIST);
1339 	if (rval != BAM_SUCCESS)
1340 		return (rval);
1341 	(void) read_one_list(root, flistp, ETC_FILE_LIST);
1342 
1343 	if (flistp->head == NULL) {
1344 		bam_error(NO_FLIST);
1345 		return (BAM_ERROR);
1346 	}
1347 
1348 	return (BAM_SUCCESS);
1349 }
1350 
1351 static void
1352 getoldstat(char *root)
1353 {
1354 	char path[PATH_MAX];
1355 	int fd, error;
1356 	struct stat sb;
1357 	char *ostat;
1358 
1359 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
1360 	fd = open(path, O_RDONLY);
1361 	if (fd == -1) {
1362 		if (bam_verbose)
1363 			bam_print(OPEN_FAIL, path, strerror(errno));
1364 		walk_arg.need_update = 1;
1365 		return;
1366 	}
1367 
1368 	if (fstat(fd, &sb) != 0) {
1369 		bam_error(STAT_FAIL, path, strerror(errno));
1370 		(void) close(fd);
1371 		walk_arg.need_update = 1;
1372 		return;
1373 	}
1374 
1375 	ostat = s_calloc(1, sb.st_size);
1376 
1377 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
1378 		bam_error(READ_FAIL, path, strerror(errno));
1379 		(void) close(fd);
1380 		free(ostat);
1381 		walk_arg.need_update = 1;
1382 		return;
1383 	}
1384 
1385 	(void) close(fd);
1386 
1387 	walk_arg.old_nvlp = NULL;
1388 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
1389 
1390 	free(ostat);
1391 
1392 	if (error) {
1393 		bam_error(UNPACK_FAIL, path, strerror(error));
1394 		walk_arg.old_nvlp = NULL;
1395 		walk_arg.need_update = 1;
1396 		return;
1397 	}
1398 }
1399 
1400 static void
1401 create_newstat(void)
1402 {
1403 	int error;
1404 
1405 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
1406 	if (error) {
1407 		/*
1408 		 * Not fatal - we can still create archive
1409 		 */
1410 		walk_arg.new_nvlp = NULL;
1411 		bam_error(NVALLOC_FAIL, strerror(error));
1412 	}
1413 }
1414 
1415 static void
1416 walk_list(char *root, filelist_t *flistp)
1417 {
1418 	char path[PATH_MAX];
1419 	line_t *lp;
1420 
1421 	for (lp = flistp->head; lp; lp = lp->next) {
1422 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
1423 		/* XXX shouldn't we use FTW_MOUNT ? */
1424 		if (nftw(path, cmpstat, 20, 0) == -1) {
1425 			/*
1426 			 * Some files may not exist.
1427 			 * For example: etc/rtc_config on a x86 diskless system
1428 			 * Emit verbose message only
1429 			 */
1430 			if (bam_verbose)
1431 				bam_print(NFTW_FAIL, path, strerror(errno));
1432 		}
1433 	}
1434 }
1435 
1436 static void
1437 savenew(char *root)
1438 {
1439 	char path[PATH_MAX];
1440 	char path2[PATH_MAX];
1441 	size_t sz;
1442 	char *nstat;
1443 	int fd, wrote, error;
1444 
1445 	nstat = NULL;
1446 	sz = 0;
1447 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
1448 	    NV_ENCODE_XDR, 0);
1449 	if (error) {
1450 		bam_error(PACK_FAIL, strerror(error));
1451 		return;
1452 	}
1453 
1454 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
1455 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
1456 	if (fd == -1) {
1457 		bam_error(OPEN_FAIL, path, strerror(errno));
1458 		free(nstat);
1459 		return;
1460 	}
1461 	wrote = write(fd, nstat, sz);
1462 	if (wrote != sz) {
1463 		bam_error(WRITE_FAIL, path, strerror(errno));
1464 		(void) close(fd);
1465 		free(nstat);
1466 		return;
1467 	}
1468 	(void) close(fd);
1469 	free(nstat);
1470 
1471 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
1472 	if (rename(path, path2) != 0) {
1473 		bam_error(RENAME_FAIL, path2, strerror(errno));
1474 	}
1475 }
1476 
1477 static void
1478 clear_walk_args(void)
1479 {
1480 	if (walk_arg.old_nvlp)
1481 		nvlist_free(walk_arg.old_nvlp);
1482 	if (walk_arg.new_nvlp)
1483 		nvlist_free(walk_arg.new_nvlp);
1484 	walk_arg.need_update = 0;
1485 	walk_arg.old_nvlp = NULL;
1486 	walk_arg.new_nvlp = NULL;
1487 }
1488 
1489 /*
1490  * Returns:
1491  *	0 - no update necessary
1492  *	1 - update required.
1493  *	BAM_ERROR (-1) - An error occurred
1494  *
1495  * Special handling for check (-n):
1496  * ================================
1497  * The check (-n) option produces parseable output.
1498  * To do this, we suppress all stdout messages unrelated
1499  * to out of sync files.
1500  * All stderr messages are still printed though.
1501  *
1502  */
1503 static int
1504 update_required(char *root)
1505 {
1506 	struct stat sb;
1507 	char path[PATH_MAX];
1508 	filelist_t flist;
1509 	filelist_t *flistp = &flist;
1510 	int need_update;
1511 
1512 	flistp->head = flistp->tail = NULL;
1513 
1514 	walk_arg.need_update = 0;
1515 
1516 	/*
1517 	 * Without consulting stat data, check if we need update
1518 	 */
1519 	check_flags_and_files(root);
1520 
1521 	/*
1522 	 * In certain deployment scenarios, filestat may not
1523 	 * exist. Ignore it during boot-archive SMF check.
1524 	 */
1525 	if (bam_smf_check) {
1526 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
1527 		if (stat(path, &sb) != 0)
1528 			return (0);
1529 	}
1530 
1531 	/*
1532 	 * consult stat data only if we haven't made a decision
1533 	 * about update. If checking (-n) however, we always
1534 	 * need stat data (since we want to compare old and new)
1535 	 */
1536 	if (!walk_arg.need_update || bam_check)
1537 		getoldstat(root);
1538 
1539 	/*
1540 	 * read list of files
1541 	 */
1542 	if (read_list(root, flistp) != BAM_SUCCESS) {
1543 		clear_walk_args();
1544 		return (BAM_ERROR);
1545 	}
1546 
1547 	assert(flistp->head && flistp->tail);
1548 
1549 	/*
1550 	 * At this point either the update is required
1551 	 * or the decision is pending. In either case
1552 	 * we need to create new stat nvlist
1553 	 */
1554 	create_newstat();
1555 
1556 	/*
1557 	 * This walk does 2 things:
1558 	 *  	- gets new stat data for every file
1559 	 *	- (optional) compare old and new stat data
1560 	 */
1561 	walk_list(root, &flist);
1562 
1563 	/* done with the file list */
1564 	filelist_free(flistp);
1565 
1566 	/*
1567 	 * if we didn't succeed in  creating new stat data above
1568 	 * just return result of update check so that archive is built.
1569 	 */
1570 	if (walk_arg.new_nvlp == NULL) {
1571 		bam_error(NO_NEW_STAT);
1572 		need_update = walk_arg.need_update;
1573 		clear_walk_args();
1574 		return (need_update ? 1 : 0);
1575 	}
1576 
1577 
1578 	/*
1579 	 * If no update required, discard newstat
1580 	 */
1581 	if (!walk_arg.need_update) {
1582 		clear_walk_args();
1583 		return (0);
1584 	}
1585 
1586 	/*
1587 	 * At this point we need an update - so save new stat data
1588 	 * However, if only checking (-n), don't save new stat data.
1589 	 */
1590 	if (!bam_check)
1591 		savenew(root);
1592 
1593 	clear_walk_args();
1594 
1595 	return (1);
1596 }
1597 
1598 static error_t
1599 create_ramdisk(char *root)
1600 {
1601 	char *cmdline, path[PATH_MAX];
1602 	size_t len;
1603 	struct stat sb;
1604 
1605 	/*
1606 	 * Setup command args for create_ramdisk.ksh
1607 	 */
1608 	(void) snprintf(path, sizeof (path), "%s%s", root, CREATE_RAMDISK);
1609 	if (stat(path, &sb) != 0) {
1610 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
1611 		return (BAM_ERROR);
1612 	}
1613 
1614 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
1615 	cmdline = s_calloc(1, len);
1616 
1617 	if (strlen(root) > 1) {
1618 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
1619 		/* chop off / at the end */
1620 		cmdline[strlen(cmdline) - 1] = '\0';
1621 	} else
1622 		(void) snprintf(cmdline, len, "%s", path);
1623 
1624 	if (exec_cmd(cmdline, NULL, 0) != 0) {
1625 		bam_error(ARCHIVE_FAIL, cmdline);
1626 		free(cmdline);
1627 		return (BAM_ERROR);
1628 	}
1629 	free(cmdline);
1630 
1631 	/*
1632 	 * Verify that the archive has been created
1633 	 */
1634 	(void) snprintf(path, sizeof (path), "%s%s", root, BOOT_ARCHIVE);
1635 	if (stat(path, &sb) != 0) {
1636 		bam_error(ARCHIVE_NOT_CREATED, path);
1637 		return (BAM_ERROR);
1638 	}
1639 
1640 	return (BAM_SUCCESS);
1641 }
1642 
1643 /*
1644  * Checks if target filesystem is on a ramdisk
1645  * 1 - is miniroot
1646  * 0 - is not
1647  * When in doubt assume it is not a ramdisk.
1648  */
1649 static int
1650 is_ramdisk(char *root)
1651 {
1652 	struct extmnttab mnt;
1653 	FILE *fp;
1654 	int found;
1655 	char mntpt[PATH_MAX];
1656 	char *cp;
1657 
1658 	/*
1659 	 * There are 3 situations where creating archive is
1660 	 * of dubious value:
1661 	 *	- create boot_archive on a lofi-mounted boot_archive
1662 	 *	- create it on a ramdisk which is the root filesystem
1663 	 *	- create it on a ramdisk mounted somewhere else
1664 	 * The first is not easy to detect and checking for it is not
1665 	 * worth it.
1666 	 * The other two conditions are handled here
1667 	 */
1668 
1669 	fp = fopen(MNTTAB, "r");
1670 	if (fp == NULL) {
1671 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1672 		return (0);
1673 	}
1674 
1675 	resetmnttab(fp);
1676 
1677 	/*
1678 	 * Remove any trailing / from the mount point
1679 	 */
1680 	(void) strlcpy(mntpt, root, sizeof (mntpt));
1681 	if (strcmp(root, "/") != 0) {
1682 		cp = mntpt + strlen(mntpt) - 1;
1683 		if (*cp == '/')
1684 			*cp = '\0';
1685 	}
1686 	found = 0;
1687 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1688 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
1689 			found = 1;
1690 			break;
1691 		}
1692 	}
1693 
1694 	if (!found) {
1695 		if (bam_verbose)
1696 			bam_error(NOT_IN_MNTTAB, mntpt);
1697 		(void) fclose(fp);
1698 		return (0);
1699 	}
1700 
1701 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
1702 		if (bam_verbose)
1703 			bam_error(IS_RAMDISK, bam_root);
1704 		(void) fclose(fp);
1705 		return (1);
1706 	}
1707 
1708 	(void) fclose(fp);
1709 
1710 	return (0);
1711 }
1712 
1713 static int
1714 is_newboot(char *root)
1715 {
1716 	char path[PATH_MAX];
1717 	struct stat sb;
1718 
1719 	/*
1720 	 * We can't boot without MULTI_BOOT
1721 	 */
1722 	(void) snprintf(path, sizeof (path), "%s%s", root, MULTI_BOOT);
1723 	if (stat(path, &sb) == -1) {
1724 		if (bam_verbose)
1725 			bam_print(FILE_MISS, path);
1726 		return (0);
1727 	}
1728 
1729 	/*
1730 	 * We can't generate archive without GRUB_DIR
1731 	 */
1732 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR);
1733 	if (stat(path, &sb) == -1) {
1734 		if (bam_verbose)
1735 			bam_print(DIR_MISS, path);
1736 		return (0);
1737 	}
1738 
1739 	return (1);
1740 }
1741 
1742 static int
1743 is_readonly(char *root)
1744 {
1745 	struct statvfs vfs;
1746 
1747 	/*
1748 	 * Check for RDONLY filesystem
1749 	 * When in doubt assume it is not readonly
1750 	 */
1751 	if (statvfs(root, &vfs) != 0) {
1752 		if (bam_verbose)
1753 			bam_error(STATVFS_FAIL, root, strerror(errno));
1754 		return (0);
1755 	}
1756 
1757 	if (vfs.f_flag & ST_RDONLY) {
1758 		return (1);
1759 	}
1760 
1761 	return (0);
1762 }
1763 
1764 static error_t
1765 update_archive(char *root, char *opt)
1766 {
1767 	error_t ret;
1768 
1769 	assert(root);
1770 	assert(opt == NULL);
1771 
1772 	/*
1773 	 * root must belong to a GRUB boot OS,
1774 	 * don't care on sparc except for diskless clients
1775 	 */
1776 	if (!is_newboot(root)) {
1777 		/*
1778 		 * Emit message only if not in context of update_all.
1779 		 * If in update_all, emit only if verbose flag is set.
1780 		 */
1781 		if (!bam_update_all || bam_verbose)
1782 			bam_print(NOT_GRUB_BOOT, root);
1783 		return (BAM_SUCCESS);
1784 	}
1785 
1786 	/*
1787 	 * root must be writable
1788 	 * Note: statvfs() does not always report the truth
1789 	 */
1790 	if (is_readonly(root)) {
1791 		if (!bam_smf_check && bam_verbose)
1792 			bam_print(RDONLY_FS, root);
1793 		return (BAM_SUCCESS);
1794 	}
1795 
1796 	/*
1797 	 * Don't generate archive on ramdisk
1798 	 */
1799 	if (is_ramdisk(root)) {
1800 		if (bam_verbose)
1801 			bam_print(SKIP_RAMDISK);
1802 		return (BAM_SUCCESS);
1803 	}
1804 
1805 	/*
1806 	 * Now check if updated is really needed
1807 	 */
1808 	ret = update_required(root);
1809 
1810 	/*
1811 	 * The check command (-n) is *not* a dry run
1812 	 * It only checks if the archive is in sync.
1813 	 */
1814 	if (bam_check) {
1815 		bam_exit((ret != 0) ? 1 : 0);
1816 	}
1817 
1818 	if (ret == 1) {
1819 		/* create the ramdisk */
1820 		ret = create_ramdisk(root);
1821 	}
1822 	return (ret);
1823 }
1824 
1825 static void
1826 restore_grub_slice(void)
1827 {
1828 	struct stat sb;
1829 	char *mntpt, *physlice;
1830 	int mnted;	/* set if we did a mount */
1831 	char menupath[PATH_MAX], cmd[PATH_MAX];
1832 
1833 	if (stat(GRUB_slice, &sb) != 0) {
1834 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
1835 		return;
1836 	}
1837 
1838 	/*
1839 	 * If we are doing an luactivate, don't attempt to restore GRUB or else
1840 	 * we may not be able to get to DCA boot environments. Let luactivate
1841 	 * handle GRUB/DCA installation
1842 	 */
1843 	if (stat(LU_ACTIVATE_FILE, &sb) == 0) {
1844 		return;
1845 	}
1846 
1847 	mnted = 0;
1848 	physlice = NULL;
1849 	mntpt = mount_grub_slice(&mnted, &physlice, QUIET);
1850 	if (mntpt == NULL) {
1851 		bam_error(CANNOT_RESTORE_GRUB_SLICE);
1852 		return;
1853 	}
1854 
1855 	(void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU);
1856 	if (stat(menupath, &sb) == 0) {
1857 		umount_grub_slice(mnted, mntpt, physlice);
1858 		return;
1859 	}
1860 
1861 	/*
1862 	 * The menu is missing - we need to do a restore
1863 	 */
1864 	bam_print(RESTORING_GRUB);
1865 
1866 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s %s",
1867 	    INSTALLGRUB, STAGE1, STAGE2, physlice);
1868 
1869 	if (exec_cmd(cmd, NULL, 0) != 0) {
1870 		bam_error(RESTORE_GRUB_FAILED);
1871 		umount_grub_slice(mnted, mntpt, physlice);
1872 		return;
1873 	}
1874 
1875 	if (stat(GRUB_backup_menu, &sb) != 0) {
1876 		bam_error(MISSING_BACKUP_MENU,
1877 		    GRUB_backup_menu, strerror(errno));
1878 		umount_grub_slice(mnted, mntpt, physlice);
1879 		return;
1880 	}
1881 
1882 	(void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s",
1883 	    GRUB_backup_menu, menupath);
1884 
1885 	if (exec_cmd(cmd, NULL, 0) != 0) {
1886 		bam_error(RESTORE_MENU_FAILED, menupath);
1887 		umount_grub_slice(mnted, mntpt, physlice);
1888 		return;
1889 	}
1890 
1891 	/* Success */
1892 	umount_grub_slice(mnted, mntpt, physlice);
1893 }
1894 
1895 static error_t
1896 update_all(char *root, char *opt)
1897 {
1898 	struct extmnttab mnt;
1899 	struct stat sb;
1900 	FILE *fp;
1901 	char multibt[PATH_MAX];
1902 	error_t ret = BAM_SUCCESS;
1903 
1904 	assert(bam_rootlen == 1 && root[0] == '/');
1905 	assert(opt == NULL);
1906 
1907 	/*
1908 	 * First update archive for current root
1909 	 */
1910 	if (update_archive(root, opt) != BAM_SUCCESS)
1911 		ret = BAM_ERROR;
1912 
1913 	/*
1914 	 * Now walk the mount table, performing archive update
1915 	 * for all mounted Newboot root filesystems
1916 	 */
1917 	fp = fopen(MNTTAB, "r");
1918 	if (fp == NULL) {
1919 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1920 		ret = BAM_ERROR;
1921 		goto out;
1922 	}
1923 
1924 	resetmnttab(fp);
1925 
1926 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1927 		if (mnt.mnt_special == NULL)
1928 			continue;
1929 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
1930 			continue;
1931 		if (strcmp(mnt.mnt_mountp, "/") == 0)
1932 			continue;
1933 
1934 		(void) snprintf(multibt, sizeof (multibt), "%s%s",
1935 		    mnt.mnt_mountp, MULTI_BOOT);
1936 
1937 		if (stat(multibt, &sb) == -1)
1938 			continue;
1939 
1940 		/*
1941 		 * We put a trailing slash to be consistent with root = "/"
1942 		 * case, such that we don't have to print // in some cases.
1943 		 */
1944 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
1945 		    mnt.mnt_mountp);
1946 		bam_rootlen = strlen(rootbuf);
1947 		if (update_archive(rootbuf, opt) != BAM_SUCCESS)
1948 			ret = BAM_ERROR;
1949 	}
1950 
1951 	(void) fclose(fp);
1952 
1953 out:
1954 	if (stat(GRUB_slice, &sb) == 0) {
1955 		restore_grub_slice();
1956 	}
1957 
1958 	return (ret);
1959 }
1960 
1961 static void
1962 append_line(menu_t *mp, line_t *lp)
1963 {
1964 	if (mp->start == NULL) {
1965 		mp->start = lp;
1966 	} else {
1967 		mp->end->next = lp;
1968 	}
1969 	mp->end = lp;
1970 }
1971 
1972 /*
1973  * A line in menu.lst looks like
1974  * [ ]*<cmd>[ \t=]*<arg>*
1975  */
1976 static void
1977 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
1978 {
1979 	/*
1980 	 * save state across calls. This is so that
1981 	 * header gets the right entry# after title has
1982 	 * been processed
1983 	 */
1984 	static line_t *prev;
1985 
1986 	line_t	*lp;
1987 	char *cmd, *sep, *arg;
1988 	char save, *cp, *line;
1989 	menu_flag_t flag = BAM_INVALID;
1990 
1991 	if (str == NULL) {
1992 		return;
1993 	}
1994 
1995 	/*
1996 	 * First save a copy of the entire line.
1997 	 * We use this later to set the line field.
1998 	 */
1999 	line = s_strdup(str);
2000 
2001 	/* Eat up leading whitespace */
2002 	while (*str == ' ' || *str == '\t')
2003 		str++;
2004 
2005 	if (*str == '#') {		/* comment */
2006 		cmd = s_strdup("#");
2007 		sep = NULL;
2008 		arg = s_strdup(str + 1);
2009 		flag = BAM_COMMENT;
2010 	} else if (*str == '\0') {	/* blank line */
2011 		cmd = sep = arg = NULL;
2012 		flag = BAM_EMPTY;
2013 	} else {
2014 		/*
2015 		 * '=' is not a documented separator in grub syntax.
2016 		 * However various development bits use '=' as a
2017 		 * separator. In addition, external users also
2018 		 * use = as a separator. So we will allow that usage.
2019 		 */
2020 		cp = str;
2021 		while (*str != ' ' && *str != '\t' && *str != '=') {
2022 			if (*str == '\0') {
2023 				cmd = s_strdup(cp);
2024 				sep = arg = NULL;
2025 				break;
2026 			}
2027 			str++;
2028 		}
2029 
2030 		if (*str != '\0') {
2031 			save = *str;
2032 			*str = '\0';
2033 			cmd = s_strdup(cp);
2034 			*str = save;
2035 
2036 			str++;
2037 			save = *str;
2038 			*str = '\0';
2039 			sep = s_strdup(str - 1);
2040 			*str = save;
2041 
2042 			while (*str == ' ' || *str == '\t')
2043 				str++;
2044 			if (*str == '\0')
2045 				arg = NULL;
2046 			else
2047 				arg = s_strdup(str);
2048 		}
2049 	}
2050 
2051 	lp = s_calloc(1, sizeof (line_t));
2052 
2053 	lp->cmd = cmd;
2054 	lp->sep = sep;
2055 	lp->arg = arg;
2056 	lp->line = line;
2057 	lp->lineNum = ++(*lineNum);
2058 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
2059 		lp->entryNum = ++(*entryNum);
2060 		lp->flags = BAM_TITLE;
2061 		if (prev && prev->flags == BAM_COMMENT &&
2062 		    prev->arg && strcmp(prev->arg, BAM_HDR) == 0)
2063 			prev->entryNum = lp->entryNum;
2064 	} else if (flag != BAM_INVALID) {
2065 		/*
2066 		 * For header comments, the entry# is "fixed up"
2067 		 * by the subsequent title
2068 		 */
2069 		lp->entryNum = *entryNum;
2070 		lp->flags = flag;
2071 	} else {
2072 		lp->entryNum = *entryNum;
2073 		lp->flags = (*entryNum == ENTRY_INIT) ? BAM_GLOBAL : BAM_ENTRY;
2074 	}
2075 
2076 	append_line(mp, lp);
2077 
2078 	prev = lp;
2079 }
2080 
2081 static menu_t *
2082 menu_read(char *menu_path)
2083 {
2084 	FILE *fp;
2085 	char buf[BAM_MAXLINE], *cp;
2086 	menu_t *mp;
2087 	int line, entry, len, n;
2088 
2089 	mp = s_calloc(1, sizeof (menu_t));
2090 
2091 	fp = fopen(menu_path, "r");
2092 	if (fp == NULL) { /* Let the caller handle this error */
2093 		return (mp);
2094 	}
2095 
2096 
2097 	/* Note: GRUB boot entry number starts with 0 */
2098 	line = LINE_INIT;
2099 	entry = ENTRY_INIT;
2100 	cp = buf;
2101 	len = sizeof (buf);
2102 	while (s_fgets(cp, len, fp) != NULL) {
2103 		n = strlen(cp);
2104 		if (cp[n - 1] == '\\') {
2105 			len -= n - 1;
2106 			assert(len >= 2);
2107 			cp += n - 1;
2108 			continue;
2109 		}
2110 		line_parser(mp, buf, &line, &entry);
2111 		cp = buf;
2112 		len = sizeof (buf);
2113 	}
2114 
2115 	if (fclose(fp) == EOF) {
2116 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
2117 	}
2118 
2119 	return (mp);
2120 }
2121 
2122 static error_t
2123 selector(menu_t *mp, char *opt, int *entry, char **title)
2124 {
2125 	char *eq;
2126 	char *opt_dup;
2127 	int entryNum;
2128 
2129 	assert(mp);
2130 	assert(mp->start);
2131 	assert(opt);
2132 
2133 	opt_dup = s_strdup(opt);
2134 
2135 	if (entry)
2136 		*entry = ENTRY_INIT;
2137 	if (title)
2138 		*title = NULL;
2139 
2140 	eq = strchr(opt_dup, '=');
2141 	if (eq == NULL) {
2142 		bam_error(INVALID_OPT, opt);
2143 		free(opt_dup);
2144 		return (BAM_ERROR);
2145 	}
2146 
2147 	*eq = '\0';
2148 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
2149 		assert(mp->end);
2150 		entryNum = s_strtol(eq + 1);
2151 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
2152 			bam_error(INVALID_ENTRY, eq + 1);
2153 			free(opt_dup);
2154 			return (BAM_ERROR);
2155 		}
2156 		*entry = entryNum;
2157 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
2158 		*title = opt + (eq - opt_dup) + 1;
2159 	} else {
2160 		bam_error(INVALID_OPT, opt);
2161 		free(opt_dup);
2162 		return (BAM_ERROR);
2163 	}
2164 
2165 	free(opt_dup);
2166 	return (BAM_SUCCESS);
2167 }
2168 
2169 /*
2170  * If invoked with no titles/entries (opt == NULL)
2171  * only title lines in file are printed.
2172  *
2173  * If invoked with a title or entry #, all
2174  * lines in *every* matching entry are listed
2175  */
2176 static error_t
2177 list_entry(menu_t *mp, char *menu_path, char *opt)
2178 {
2179 	line_t *lp;
2180 	int entry = ENTRY_INIT;
2181 	int found;
2182 	char *title = NULL;
2183 
2184 	assert(mp);
2185 	assert(menu_path);
2186 
2187 	if (mp->start == NULL) {
2188 		bam_error(NO_MENU, menu_path);
2189 		return (BAM_ERROR);
2190 	}
2191 
2192 	if (opt != NULL) {
2193 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
2194 			return (BAM_ERROR);
2195 		}
2196 		assert((entry != ENTRY_INIT) ^ (title != NULL));
2197 	} else {
2198 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
2199 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
2200 	}
2201 
2202 	found = 0;
2203 	for (lp = mp->start; lp; lp = lp->next) {
2204 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
2205 			continue;
2206 		if (opt == NULL && lp->flags == BAM_TITLE) {
2207 			bam_print(PRINT_TITLE, lp->entryNum,
2208 			    lp->arg);
2209 			found = 1;
2210 			continue;
2211 		}
2212 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
2213 			bam_print(PRINT, lp->line);
2214 			found = 1;
2215 			continue;
2216 		}
2217 
2218 		/*
2219 		 * We set the entry value here so that all lines
2220 		 * in entry get printed. If we subsequently match
2221 		 * title in other entries, all lines in those
2222 		 * entries get printed as well.
2223 		 */
2224 		if (title && lp->flags == BAM_TITLE && lp->arg &&
2225 		    strncmp(title, lp->arg, strlen(title)) == 0) {
2226 			bam_print(PRINT, lp->line);
2227 			entry = lp->entryNum;
2228 			found = 1;
2229 			continue;
2230 		}
2231 	}
2232 
2233 	if (!found) {
2234 		bam_error(NO_MATCH_ENTRY);
2235 		return (BAM_ERROR);
2236 	}
2237 
2238 	return (BAM_SUCCESS);
2239 }
2240 
2241 static int
2242 add_boot_entry(menu_t *mp,
2243 	char *title,
2244 	char *root,
2245 	char *kernel,
2246 	char *module)
2247 {
2248 	menu_t dummy;
2249 	int lineNum, entryNum;
2250 	char linebuf[BAM_MAXLINE];
2251 
2252 	assert(mp);
2253 
2254 	if (title == NULL) {
2255 		bam_error(SUBOPT_MISS, menu_cmds[TITLE_CMD]);
2256 		return (BAM_ERROR);
2257 	}
2258 	if (root == NULL) {
2259 		bam_error(SUBOPT_MISS, menu_cmds[ROOT_CMD]);
2260 		return (BAM_ERROR);
2261 	}
2262 	if (kernel == NULL) {
2263 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
2264 		return (BAM_ERROR);
2265 	}
2266 	if (module == NULL) {
2267 		bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
2268 		return (BAM_ERROR);
2269 	}
2270 
2271 	if (mp->start) {
2272 		lineNum = mp->end->lineNum;
2273 		entryNum = mp->end->entryNum;
2274 	} else {
2275 		lineNum = LINE_INIT;
2276 		entryNum = ENTRY_INIT;
2277 	}
2278 
2279 	/*
2280 	 * No separator for comment (HDR/FTR) commands
2281 	 * The syntax for comments is #<comment>
2282 	 */
2283 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
2284 	    menu_cmds[COMMENT_CMD], BAM_HDR);
2285 	dummy.start = dummy.end = NULL;
2286 	line_parser(&dummy, linebuf, &lineNum, &entryNum);
2287 	if (dummy.start == NULL || dummy.start->flags != BAM_COMMENT) {
2288 		line_free(dummy.start);
2289 		bam_error(INVALID_HDR, BAM_HDR);
2290 		return (BAM_ERROR);
2291 	}
2292 	assert(dummy.start == dummy.end);
2293 	append_line(mp, dummy.start);
2294 
2295 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2296 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
2297 	dummy.start = dummy.end = NULL;
2298 	line_parser(&dummy, linebuf, &lineNum, &entryNum);
2299 	if (dummy.start == NULL || dummy.start->flags != BAM_TITLE) {
2300 		line_free(dummy.start);
2301 		bam_error(INVALID_TITLE, title);
2302 		return (BAM_ERROR);
2303 	}
2304 	assert(dummy.start == dummy.end);
2305 	append_line(mp, dummy.start);
2306 
2307 
2308 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2309 	    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
2310 	dummy.start = dummy.end = NULL;
2311 	line_parser(&dummy, linebuf, &lineNum, &entryNum);
2312 	if (dummy.start == NULL || dummy.start->flags != BAM_ENTRY) {
2313 		line_free(dummy.start);
2314 		bam_error(INVALID_ROOT, root);
2315 		return (BAM_ERROR);
2316 	}
2317 	assert(dummy.start == dummy.end);
2318 	append_line(mp, dummy.start);
2319 
2320 
2321 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2322 	    menu_cmds[KERNEL_CMD], menu_cmds[SEP_CMD], kernel);
2323 	dummy.start = dummy.end = NULL;
2324 	line_parser(&dummy, linebuf, &lineNum, &entryNum);
2325 	if (dummy.start == NULL || dummy.start->flags != BAM_ENTRY) {
2326 		line_free(dummy.start);
2327 		bam_error(INVALID_KERNEL, kernel);
2328 		return (BAM_ERROR);
2329 	}
2330 	assert(dummy.start == dummy.end);
2331 	append_line(mp, dummy.start);
2332 
2333 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2334 	    menu_cmds[MODULE_CMD], menu_cmds[SEP_CMD], module);
2335 	dummy.start = dummy.end = NULL;
2336 	line_parser(&dummy, linebuf, &lineNum, &entryNum);
2337 	if (dummy.start == NULL || dummy.start->flags != BAM_ENTRY) {
2338 		line_free(dummy.start);
2339 		bam_error(INVALID_MODULE, module);
2340 		return (BAM_ERROR);
2341 	}
2342 	assert(dummy.start == dummy.end);
2343 	append_line(mp, dummy.start);
2344 
2345 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
2346 	    menu_cmds[COMMENT_CMD], BAM_FTR);
2347 	dummy.start = dummy.end = NULL;
2348 	line_parser(&dummy, linebuf, &lineNum, &entryNum);
2349 	if (dummy.start == NULL || dummy.start->flags != BAM_COMMENT) {
2350 		line_free(dummy.start);
2351 		bam_error(INVALID_FOOTER, BAM_FTR);
2352 		return (BAM_ERROR);
2353 	}
2354 	assert(dummy.start == dummy.end);
2355 	append_line(mp, dummy.start);
2356 
2357 	return (entryNum);
2358 }
2359 
2360 static error_t
2361 do_delete(menu_t *mp, int entryNum)
2362 {
2363 	int bootadm_entry = 0;
2364 	line_t *lp, *prev, *save;
2365 	int deleted;
2366 
2367 	assert(entryNum != ENTRY_INIT);
2368 
2369 	deleted = 0;
2370 	prev = NULL;
2371 	for (lp = mp->start; lp; ) {
2372 
2373 		if (lp->entryNum == ENTRY_INIT) {
2374 			prev = lp;
2375 			lp = lp->next;
2376 			continue;
2377 		}
2378 
2379 		if (entryNum != ALL_ENTRIES && lp->entryNum != entryNum) {
2380 			prev = lp;
2381 			lp = lp->next;
2382 			continue;
2383 		}
2384 
2385 		/*
2386 		 * can only delete bootadm entries
2387 		 */
2388 		if (lp->flags == BAM_COMMENT && strcmp(lp->arg, BAM_HDR) == 0) {
2389 			bootadm_entry = 1;
2390 		}
2391 
2392 		if (!bootadm_entry) {
2393 			prev = lp;
2394 			lp = lp->next;
2395 			continue;
2396 		}
2397 
2398 		if (lp->flags == BAM_COMMENT && strcmp(lp->arg, BAM_FTR) == 0)
2399 			bootadm_entry = 0;
2400 
2401 		if (prev == NULL)
2402 			mp->start = lp->next;
2403 		else
2404 			prev->next = lp->next;
2405 		if (mp->end == lp)
2406 			mp->end = prev;
2407 		save = lp->next;
2408 		line_free(lp);
2409 		lp = save;	/* prev stays the same */
2410 
2411 		deleted = 1;
2412 	}
2413 
2414 	if (!deleted && entryNum != ALL_ENTRIES) {
2415 		bam_error(NO_BOOTADM_MATCH);
2416 		return (BAM_ERROR);
2417 	}
2418 
2419 	return (BAM_SUCCESS);
2420 }
2421 
2422 static error_t
2423 delete_entry(menu_t *mp, char *menu_path, char *opt)
2424 {
2425 	int entry = ENTRY_INIT;
2426 	char *title = NULL;
2427 	line_t *lp;
2428 
2429 	assert(mp);
2430 	assert(opt);
2431 
2432 	/*
2433 	 * Do a quick check. If the file is empty
2434 	 * we have nothing to delete
2435 	 */
2436 	if (mp->start == NULL) {
2437 		bam_print(EMPTY_FILE, menu_path);
2438 		return (BAM_SUCCESS);
2439 	}
2440 
2441 	if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
2442 		return (BAM_ERROR);
2443 	}
2444 	assert((entry != ENTRY_INIT) ^ (title != NULL));
2445 
2446 	for (lp = mp->start; lp; lp = lp->next) {
2447 		if (entry != ENTRY_INIT)
2448 			break;
2449 		assert(title);
2450 		if (lp->flags == BAM_TITLE &&
2451 		    lp->arg && strcmp(lp->arg, title) == 0) {
2452 			entry = lp->entryNum;
2453 			break;
2454 		}
2455 	}
2456 
2457 	if (entry == ENTRY_INIT) {
2458 		bam_error(NO_MATCH, title);
2459 		return (BAM_ERROR);
2460 	}
2461 
2462 	if (do_delete(mp, entry) != BAM_SUCCESS) {
2463 		return (BAM_ERROR);
2464 	}
2465 
2466 	return (BAM_WRITE);
2467 }
2468 
2469 static error_t
2470 delete_all_entries(menu_t *mp, char *menu_path, char *opt)
2471 {
2472 	assert(mp);
2473 	assert(opt == NULL);
2474 
2475 	if (mp->start == NULL) {
2476 		bam_print(EMPTY_FILE, menu_path);
2477 		return (BAM_SUCCESS);
2478 	}
2479 
2480 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
2481 		return (BAM_ERROR);
2482 	}
2483 
2484 	return (BAM_WRITE);
2485 }
2486 
2487 static FILE *
2488 open_diskmap(void)
2489 {
2490 	FILE *fp;
2491 	char cmd[PATH_MAX];
2492 
2493 	/* make sure we have a map file */
2494 	fp = fopen(GRUBDISK_MAP, "r");
2495 	if (fp == NULL) {
2496 		(void) snprintf(cmd, sizeof (cmd),
2497 		    "%s > /dev/null", CREATE_DISKMAP);
2498 		(void) system(cmd);
2499 		fp = fopen(GRUBDISK_MAP, "r");
2500 	}
2501 	return (fp);
2502 }
2503 
2504 #define	SECTOR_SIZE	512
2505 
2506 static int
2507 get_partition(char *device)
2508 {
2509 	int i, fd, is_pcfs, partno = -1;
2510 	struct mboot *mboot;
2511 	char boot_sect[SECTOR_SIZE];
2512 	char *wholedisk, *slice;
2513 
2514 	/* form whole disk (p0) */
2515 	slice = device + strlen(device) - 2;
2516 	is_pcfs = (*slice != 's');
2517 	if (!is_pcfs)
2518 		*slice = '\0';
2519 	wholedisk = s_calloc(1, strlen(device) + 3);
2520 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
2521 	if (!is_pcfs)
2522 		*slice = 's';
2523 
2524 	/* read boot sector */
2525 	fd = open(wholedisk, O_RDONLY);
2526 	free(wholedisk);
2527 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
2528 		return (partno);
2529 	}
2530 	(void) close(fd);
2531 
2532 	/* parse fdisk table */
2533 	mboot = (struct mboot *)((void *)boot_sect);
2534 	for (i = 0; i < FD_NUMPART; i++) {
2535 		struct ipart *part =
2536 		    (struct ipart *)(uintptr_t)mboot->parts + i;
2537 		if (is_pcfs) {	/* looking for solaris boot part */
2538 			if (part->systid == 0xbe) {
2539 				partno = i;
2540 				break;
2541 			}
2542 		} else {	/* look for solaris partition, old and new */
2543 			if (part->systid == SUNIXOS ||
2544 			    part->systid == SUNIXOS2) {
2545 				partno = i;
2546 				break;
2547 			}
2548 		}
2549 	}
2550 	return (partno);
2551 }
2552 
2553 static char *
2554 get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
2555 {
2556 	char *grubdisk;	/* (hd#,#,#) */
2557 	char *slice;
2558 	char *grubhd;
2559 	int fdiskpart;
2560 	int found = 0;
2561 	char *devname, *ctdname = strstr(rootdev, "dsk/");
2562 	char linebuf[PATH_MAX];
2563 
2564 	if (ctdname == NULL)
2565 		return (NULL);
2566 
2567 	ctdname += strlen("dsk/");
2568 	slice = strrchr(ctdname, 's');
2569 	if (slice)
2570 		*slice = '\0';
2571 
2572 	rewind(fp);
2573 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
2574 		grubhd = strtok(linebuf, " \t\n");
2575 		if (grubhd)
2576 			devname = strtok(NULL, " \t\n");
2577 		else
2578 			devname = NULL;
2579 		if (devname && strcmp(devname, ctdname) == 0) {
2580 			found = 1;
2581 			break;
2582 		}
2583 	}
2584 
2585 	if (slice)
2586 		*slice = 's';
2587 
2588 	if (found == 0) {
2589 		if (bam_verbose)
2590 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
2591 		grubhd = "0";	/* assume disk 0 if can't match */
2592 	}
2593 
2594 	fdiskpart = get_partition(rootdev);
2595 	if (fdiskpart == -1)
2596 		return (NULL);
2597 
2598 	grubdisk = s_calloc(1, 10);
2599 	if (slice) {
2600 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
2601 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
2602 	} else
2603 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
2604 		    grubhd, fdiskpart);
2605 
2606 	/* if root not on bootdev, change GRUB disk to 0 */
2607 	if (!on_bootdev)
2608 		grubdisk[3] = '0';
2609 	return (grubdisk);
2610 }
2611 
2612 static char *get_title(char *rootdir)
2613 {
2614 	static char title[80];	/* from /etc/release */
2615 	char *cp, release[PATH_MAX];
2616 	FILE *fp;
2617 
2618 	/* open the /etc/release file */
2619 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
2620 
2621 	fp = fopen(release, "r");
2622 	if (fp == NULL)
2623 		return ("Solaris");	/* default to Solaris */
2624 
2625 	while (s_fgets(title, sizeof (title), fp) != NULL) {
2626 		cp = strstr(title, "Solaris");
2627 		if (cp)
2628 			break;
2629 	}
2630 	(void) fclose(fp);
2631 	return (cp);
2632 }
2633 
2634 static char *
2635 get_special(char *mountp)
2636 {
2637 	FILE *mntfp;
2638 	struct mnttab mp = {0}, mpref = {0};
2639 
2640 	mntfp = fopen(MNTTAB, "r");
2641 	if (mntfp == NULL) {
2642 		return (0);
2643 	}
2644 
2645 	if (*mountp == '\0')
2646 		mpref.mnt_mountp = "/";
2647 	else
2648 		mpref.mnt_mountp = mountp;
2649 	if (getmntany(mntfp, &mp, &mpref) != 0) {
2650 		(void) fclose(mntfp);
2651 		return (NULL);
2652 	}
2653 	(void) fclose(mntfp);
2654 
2655 	return (s_strdup(mp.mnt_special));
2656 }
2657 
2658 static char *
2659 os_to_grubdisk(char *osdisk, int on_bootdev)
2660 {
2661 	FILE *fp;
2662 	char *grubdisk;
2663 
2664 	/* translate /dev/dsk name to grub disk name */
2665 	fp = open_diskmap();
2666 	if (fp == NULL) {
2667 		bam_error(DISKMAP_FAIL, osdisk);
2668 		return (NULL);
2669 	}
2670 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
2671 	(void) fclose(fp);
2672 	return (grubdisk);
2673 }
2674 
2675 /*
2676  * Check if root is on the boot device
2677  * Return 0 (false) on error
2678  */
2679 static int
2680 menu_on_bootdev(char *menu_root, FILE *fp)
2681 {
2682 	int ret;
2683 	char *grubhd, *bootp, *special;
2684 
2685 	special = get_special(menu_root);
2686 	if (special == NULL)
2687 		return (0);
2688 	bootp = strstr(special, "p0:boot");
2689 	if (bootp)
2690 		*bootp = '\0';
2691 	grubhd = get_grubdisk(special, fp, 1);
2692 	free(special);
2693 
2694 	if (grubhd == NULL)
2695 		return (0);
2696 	ret = grubhd[3] == '0';
2697 	free(grubhd);
2698 	return (ret);
2699 }
2700 
2701 /*ARGSUSED*/
2702 static error_t
2703 update_entry(menu_t *mp, char *menu_root, char *opt)
2704 {
2705 	FILE *fp;
2706 	int entry;
2707 	line_t *lp;
2708 	char *grubdisk, *title, *osdev, *osroot;
2709 	int bootadm_entry, entry_to_delete;
2710 
2711 	assert(mp);
2712 	assert(opt);
2713 
2714 	osdev = strtok(opt, ",");
2715 	osroot = strtok(NULL, ",");
2716 	if (osroot == NULL)
2717 		osroot = menu_root;
2718 	title = get_title(osroot);
2719 
2720 	/* translate /dev/dsk name to grub disk name */
2721 	fp = open_diskmap();
2722 	if (fp == NULL) {
2723 		bam_error(DISKMAP_FAIL, osdev);
2724 		return (BAM_ERROR);
2725 	}
2726 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
2727 	(void) fclose(fp);
2728 	if (grubdisk == NULL) {
2729 		bam_error(DISKMAP_FAIL, osdev);
2730 		return (BAM_ERROR);
2731 	}
2732 
2733 	/* delete existing entries with matching grub hd name */
2734 	for (;;) {
2735 		entry_to_delete = -1;
2736 		bootadm_entry = 0;
2737 		for (lp = mp->start; lp; lp = lp->next) {
2738 			/*
2739 			 * can only delete bootadm entries
2740 			 */
2741 			if (lp->flags == BAM_COMMENT) {
2742 				if (strcmp(lp->arg, BAM_HDR) == 0)
2743 					bootadm_entry = 1;
2744 				else if (strcmp(lp->arg, BAM_FTR) == 0)
2745 					bootadm_entry = 0;
2746 			}
2747 
2748 			if (bootadm_entry && lp->flags == BAM_ENTRY &&
2749 			    strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0 &&
2750 			    strcmp(lp->arg, grubdisk) == 0) {
2751 				entry_to_delete = lp->entryNum;
2752 			}
2753 		}
2754 		if (entry_to_delete == -1)
2755 			break;
2756 		(void) do_delete(mp, entry_to_delete);
2757 	}
2758 
2759 	/* add the entry for normal Solaris */
2760 	entry = add_boot_entry(mp, title, grubdisk,
2761 	    "/platform/i86pc/multiboot",
2762 	    "/platform/i86pc/boot_archive");
2763 
2764 	/* add the entry for failsafe archive */
2765 	(void) add_boot_entry(mp, "Solaris failsafe", grubdisk,
2766 	    "/boot/multiboot kernel/unix -s",
2767 	    "/boot/x86.miniroot-safe");
2768 	free(grubdisk);
2769 
2770 	if (entry == BAM_ERROR) {
2771 		return (BAM_ERROR);
2772 	}
2773 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
2774 	return (BAM_WRITE);
2775 }
2776 
2777 static char *
2778 read_grub_root(void)
2779 {
2780 	FILE *fp;
2781 	struct stat sb;
2782 	char buf[BAM_MAXLINE];
2783 	char *rootstr;
2784 
2785 	if (stat(GRUB_slice, &sb) != 0) {
2786 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
2787 		return (NULL);
2788 	}
2789 
2790 	if (stat(GRUB_root, &sb) != 0) {
2791 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
2792 		return (NULL);
2793 	}
2794 
2795 	fp = fopen(GRUB_root, "r");
2796 	if (fp == NULL) {
2797 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
2798 		return (NULL);
2799 	}
2800 
2801 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
2802 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
2803 		(void) fclose(fp);
2804 		return (NULL);
2805 	}
2806 
2807 	/*
2808 	 * Copy buf here as check below may trash the buffer
2809 	 */
2810 	rootstr = s_strdup(buf);
2811 
2812 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
2813 		bam_error(BAD_ROOT_FILE, GRUB_root);
2814 		free(rootstr);
2815 		rootstr = NULL;
2816 	}
2817 
2818 	(void) fclose(fp);
2819 
2820 	return (rootstr);
2821 }
2822 
2823 /*
2824  * This function is for supporting reboot with args.
2825  * The opt value can be:
2826  * NULL		delete temp entry, if present
2827  * entry=#	switches default entry to 1
2828  * else		treated as boot-args and setup a temperary menu entry
2829  *		and make it the default
2830  */
2831 #define	REBOOT_TITLE	"Solaris_reboot_transient"
2832 
2833 static error_t
2834 update_temp(menu_t *mp, char *menupath, char *opt)
2835 {
2836 	int entry;
2837 	char *grubdisk, *rootdev;
2838 	char kernbuf[1024];
2839 	struct stat sb;
2840 
2841 	assert(mp);
2842 
2843 	if (opt != NULL &&
2844 	    strncmp(opt, "entry=", strlen("entry=")) == 0 &&
2845 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
2846 		/* this is entry=# option */
2847 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
2848 	}
2849 
2850 	/* If no option, delete exiting reboot menu entry */
2851 	if (opt == NULL)
2852 		return (delete_entry(mp, menupath, "title="REBOOT_TITLE));
2853 
2854 	/*
2855 	 * add a new menu entry base on opt and make it the default
2856 	 */
2857 	grubdisk = NULL;
2858 	if (stat(GRUB_slice, &sb) != 0) {
2859 		/*
2860 		 * 1. First get root disk name from mnttab
2861 		 * 2. Translate disk name to grub name
2862 		 * 3. Add the new menu entry
2863 		 */
2864 		rootdev = get_special("/");
2865 		if (rootdev) {
2866 			grubdisk = os_to_grubdisk(rootdev, 1);
2867 			free(rootdev);
2868 		}
2869 	} else {
2870 		/*
2871 		 * This is an LU BE. The GRUB_root file
2872 		 * contains entry for GRUB's "root" cmd.
2873 		 */
2874 		grubdisk = read_grub_root();
2875 	}
2876 	if (grubdisk == NULL) {
2877 		bam_error(REBOOT_WITH_ARGS_FAILED);
2878 		return (BAM_ERROR);
2879 	}
2880 
2881 	/* add an entry for Solaris reboot */
2882 	(void) snprintf(kernbuf, sizeof (kernbuf),
2883 	    "/platform/i86pc/multiboot %s", opt);
2884 	entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
2885 	    "/platform/i86pc/boot_archive");
2886 	free(grubdisk);
2887 
2888 	if (entry == BAM_ERROR) {
2889 		bam_error(REBOOT_WITH_ARGS_FAILED);
2890 		return (BAM_ERROR);
2891 	}
2892 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
2893 	return (BAM_WRITE);
2894 }
2895 
2896 static error_t
2897 set_global(menu_t *mp, char *globalcmd, int val)
2898 {
2899 	line_t *lp, *found, *last;
2900 	char *cp, *str;
2901 	char prefix[BAM_MAXLINE];
2902 	size_t len;
2903 
2904 	assert(mp);
2905 	assert(globalcmd);
2906 
2907 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
2908 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
2909 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
2910 			bam_error(INVALID_ENTRY, prefix);
2911 			return (BAM_ERROR);
2912 		}
2913 	}
2914 
2915 	found = last = NULL;
2916 	for (lp = mp->start; lp; lp = lp->next) {
2917 		if (lp->flags != BAM_GLOBAL)
2918 			continue;
2919 
2920 		last = lp; /* track the last global found */
2921 
2922 		if (lp->cmd == NULL) {
2923 			bam_error(NO_CMD, lp->lineNum);
2924 			continue;
2925 		}
2926 		if (strcmp(globalcmd, lp->cmd) != 0)
2927 			continue;
2928 
2929 		if (found) {
2930 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
2931 		}
2932 		found = lp;
2933 	}
2934 
2935 	if (found == NULL) {
2936 		lp = s_calloc(1, sizeof (line_t));
2937 		if (last == NULL) {
2938 			lp->next = mp->start;
2939 			mp->start = lp;
2940 			mp->end = (mp->end) ? mp->end : lp;
2941 		} else {
2942 			lp->next = last->next;
2943 			last->next = lp;
2944 			if (lp->next == NULL)
2945 				mp->end = lp;
2946 		}
2947 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
2948 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
2949 		len += 10;	/* val < 10 digits */
2950 		lp->line = s_calloc(1, len);
2951 		(void) snprintf(lp->line, len, "%s%s%d",
2952 		    globalcmd, menu_cmds[SEP_CMD], val);
2953 		return (BAM_WRITE);
2954 	}
2955 
2956 	/*
2957 	 * We are changing an existing entry. Retain any prefix whitespace,
2958 	 * but overwrite everything else. This preserves tabs added for
2959 	 * readability.
2960 	 */
2961 	str = found->line;
2962 	cp = prefix;
2963 	while (*str == ' ' || *str == '\t')
2964 		*(cp++) = *(str++);
2965 	*cp = '\0'; /* Terminate prefix */
2966 	len = strlen(prefix) + strlen(globalcmd);
2967 	len += strlen(menu_cmds[SEP_CMD]) + 10;
2968 
2969 	free(found->line);
2970 	found->line = s_calloc(1, len);
2971 	(void) snprintf(found->line, len,
2972 		"%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
2973 
2974 	return (BAM_WRITE); /* need a write to menu */
2975 }
2976 
2977 /*ARGSUSED*/
2978 static error_t
2979 set_option(menu_t *mp, char *menu_path, char *opt)
2980 {
2981 	int optnum, optval;
2982 	char *val;
2983 
2984 	assert(mp);
2985 	assert(opt);
2986 
2987 	val = strchr(opt, '=');
2988 	if (val == NULL) {
2989 		bam_error(INVALID_ENTRY, opt);
2990 		return (BAM_ERROR);
2991 	}
2992 
2993 	*val = '\0';
2994 	if (strcmp(opt, "default") == 0) {
2995 		optnum = DEFAULT_CMD;
2996 	} else if (strcmp(opt, "timeout") == 0) {
2997 		optnum = TIMEOUT_CMD;
2998 	} else {
2999 		bam_error(INVALID_ENTRY, opt);
3000 		return (BAM_ERROR);
3001 	}
3002 
3003 	optval = s_strtol(val + 1);
3004 	*val = '=';
3005 	return (set_global(mp, menu_cmds[optnum], optval));
3006 }
3007 
3008 /*
3009  * The quiet argument suppresses messages. This is used
3010  * when invoked in the context of other commands (e.g. list_entry)
3011  */
3012 static error_t
3013 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
3014 {
3015 	line_t *lp;
3016 	char *arg;
3017 	int done, ret = BAM_SUCCESS;
3018 
3019 	assert(mp);
3020 	assert(menu_path);
3021 	assert(globalcmd);
3022 
3023 	if (mp->start == NULL) {
3024 		if (!quiet)
3025 			bam_error(NO_MENU, menu_path);
3026 		return (BAM_ERROR);
3027 	}
3028 
3029 	done = 0;
3030 	for (lp = mp->start; lp; lp = lp->next) {
3031 		if (lp->flags != BAM_GLOBAL)
3032 			continue;
3033 
3034 		if (lp->cmd == NULL) {
3035 			if (!quiet)
3036 				bam_error(NO_CMD, lp->lineNum);
3037 			continue;
3038 		}
3039 
3040 		if (strcmp(globalcmd, lp->cmd) != 0)
3041 			continue;
3042 
3043 		/* Found global. Check for duplicates */
3044 		if (done && !quiet) {
3045 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
3046 			ret = BAM_ERROR;
3047 		}
3048 
3049 		arg = lp->arg ? lp->arg : "";
3050 		bam_print(GLOBAL_CMD, globalcmd, arg);
3051 		done = 1;
3052 	}
3053 
3054 	if (!done && bam_verbose)
3055 		bam_print(NO_ENTRY, globalcmd);
3056 
3057 	return (ret);
3058 }
3059 
3060 static error_t
3061 menu_write(char *root, menu_t *mp)
3062 {
3063 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
3064 }
3065 
3066 static void
3067 line_free(line_t *lp)
3068 {
3069 	if (lp == NULL)
3070 		return;
3071 
3072 	if (lp->cmd)
3073 		free(lp->cmd);
3074 	if (lp->sep)
3075 		free(lp->sep);
3076 	if (lp->arg)
3077 		free(lp->arg);
3078 	if (lp->line)
3079 		free(lp->line);
3080 	free(lp);
3081 }
3082 
3083 static void
3084 linelist_free(line_t *start)
3085 {
3086 	line_t *lp;
3087 
3088 	while (start) {
3089 		lp = start;
3090 		start = start->next;
3091 		line_free(lp);
3092 	}
3093 }
3094 
3095 static void
3096 filelist_free(filelist_t *flistp)
3097 {
3098 	linelist_free(flistp->head);
3099 	flistp->head = NULL;
3100 	flistp->tail = NULL;
3101 }
3102 
3103 static void
3104 menu_free(menu_t *mp)
3105 {
3106 	assert(mp);
3107 
3108 	if (mp->start)
3109 		linelist_free(mp->start);
3110 	free(mp);
3111 
3112 }
3113 
3114 /*
3115  * Utility routines
3116  */
3117 
3118 
3119 /*
3120  * Returns 0 on success
3121  * Any other value indicates an error
3122  */
3123 static int
3124 exec_cmd(char *cmdline, char *output, int64_t osize)
3125 {
3126 	char buf[BUFSIZ];
3127 	int ret;
3128 	FILE *ptr;
3129 	size_t len;
3130 	sigset_t set;
3131 	void (*disp)(int);
3132 
3133 	/*
3134 	 * For security
3135 	 * - only absolute paths are allowed
3136 	 * - set IFS to space and tab
3137 	 */
3138 	if (*cmdline != '/') {
3139 		bam_error(ABS_PATH_REQ, cmdline);
3140 		return (-1);
3141 	}
3142 	(void) putenv("IFS= \t");
3143 
3144 	/*
3145 	 * We may have been exec'ed with SIGCHLD blocked
3146 	 * unblock it here
3147 	 */
3148 	(void) sigemptyset(&set);
3149 	(void) sigaddset(&set, SIGCHLD);
3150 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
3151 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
3152 		return (-1);
3153 	}
3154 
3155 	/*
3156 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
3157 	 */
3158 	disp = sigset(SIGCHLD, SIG_DFL);
3159 	if (disp == SIG_ERR) {
3160 		bam_error(FAILED_SIG, strerror(errno));
3161 		return (-1);
3162 	}
3163 	if (disp == SIG_HOLD) {
3164 		bam_error(BLOCKED_SIG, cmdline);
3165 		return (-1);
3166 	}
3167 
3168 	ptr = popen(cmdline, "r");
3169 	if (ptr == NULL) {
3170 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
3171 		return (-1);
3172 	}
3173 
3174 	/*
3175 	 * If we simply do a pclose() following a popen(), pclose()
3176 	 * will close the reader end of the pipe immediately even
3177 	 * if the child process has not started/exited. pclose()
3178 	 * does wait for cmd to terminate before returning though.
3179 	 * When the executed command writes its output to the pipe
3180 	 * there is no reader process and the command dies with
3181 	 * SIGPIPE. To avoid this we read repeatedly until read
3182 	 * terminates with EOF. This indicates that the command
3183 	 * (writer) has closed the pipe and we can safely do a
3184 	 * pclose().
3185 	 *
3186 	 * Since pclose() does wait for the command to exit,
3187 	 * we can safely reap the exit status of the command
3188 	 * from the value returned by pclose()
3189 	 */
3190 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
3191 		/* if (bam_verbose)  XXX */
3192 			bam_print(PRINT_NO_NEWLINE, buf);
3193 		if (output && osize > 0) {
3194 			(void) snprintf(output, osize, "%s", buf);
3195 			len = strlen(buf);
3196 			output += len;
3197 			osize -= len;
3198 		}
3199 	}
3200 
3201 	ret = pclose(ptr);
3202 	if (ret == -1) {
3203 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
3204 		return (-1);
3205 	}
3206 
3207 	if (WIFEXITED(ret)) {
3208 		return (WEXITSTATUS(ret));
3209 	} else {
3210 		bam_error(EXEC_FAIL, cmdline, ret);
3211 		return (-1);
3212 	}
3213 }
3214 
3215 /*
3216  * Since this function returns -1 on error
3217  * it cannot be used to convert -1. However,
3218  * that is sufficient for what we need.
3219  */
3220 static long
3221 s_strtol(char *str)
3222 {
3223 	long l;
3224 	char *res = NULL;
3225 
3226 	if (str == NULL) {
3227 		return (-1);
3228 	}
3229 
3230 	errno = 0;
3231 	l = strtol(str, &res, 10);
3232 	if (errno || *res != '\0') {
3233 		return (-1);
3234 	}
3235 
3236 	return (l);
3237 }
3238 
3239 /*
3240  * Wrapper around fputs, that adds a newline (since fputs doesn't)
3241  */
3242 static int
3243 s_fputs(char *str, FILE *fp)
3244 {
3245 	char linebuf[BAM_MAXLINE];
3246 
3247 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
3248 	return (fputs(linebuf, fp));
3249 }
3250 
3251 /*
3252  * Wrapper around fgets, that strips newlines returned by fgets
3253  */
3254 static char *
3255 s_fgets(char *buf, int buflen, FILE *fp)
3256 {
3257 	int n;
3258 
3259 	buf = fgets(buf, buflen, fp);
3260 	if (buf) {
3261 		n = strlen(buf);
3262 		if (n == buflen - 1 && buf[n-1] != '\n')
3263 			bam_error(TOO_LONG, buflen - 1, buf);
3264 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
3265 	}
3266 
3267 	return (buf);
3268 }
3269 
3270 static void *
3271 s_calloc(size_t nelem, size_t sz)
3272 {
3273 	void *ptr;
3274 
3275 	ptr = calloc(nelem, sz);
3276 	if (ptr == NULL) {
3277 		bam_error(NO_MEM, nelem*sz);
3278 		bam_exit(1);
3279 	}
3280 	return (ptr);
3281 }
3282 
3283 static char *
3284 s_strdup(char *str)
3285 {
3286 	char *ptr;
3287 
3288 	if (str == NULL)
3289 		return (NULL);
3290 
3291 	ptr = strdup(str);
3292 	if (ptr == NULL) {
3293 		bam_error(NO_MEM, strlen(str) + 1);
3294 		bam_exit(1);
3295 	}
3296 	return (ptr);
3297 }
3298 
3299 /*
3300  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
3301  * Returns 0 otherwise
3302  */
3303 static int
3304 is_amd64(void)
3305 {
3306 	static int amd64 = -1;
3307 	char isabuf[257];	/* from sysinfo(2) manpage */
3308 
3309 	if (amd64 != -1)
3310 		return (amd64);
3311 
3312 	if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
3313 	    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0)
3314 		amd64 = 1;
3315 	else if (strstr(isabuf, "i386") == NULL)
3316 		amd64 = 1;		/* diskless server */
3317 	else
3318 		amd64 = 0;
3319 
3320 	return (amd64);
3321 }
3322 
3323 static void
3324 append_to_flist(filelist_t *flistp, char *s)
3325 {
3326 	line_t *lp;
3327 
3328 	lp = s_calloc(1, sizeof (line_t));
3329 	lp->line = s_strdup(s);
3330 	if (flistp->head == NULL)
3331 		flistp->head = lp;
3332 	else
3333 		flistp->tail->next = lp;
3334 	flistp->tail = lp;
3335 }
3336