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