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