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