xref: /illumos-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 75383e32)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2012 Milan Jurik. All rights reserved.
25  * Copyright (c) 2015 by Delphix. All rights reserved.
26  * Copyright 2016 Toomas Soome <tsoome@me.com>
27  * Copyright 2016 Nexenta Systems, Inc.
28  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
29  */
30 
31 /*
32  * bootadm(1M) is a new utility for managing bootability of
33  * Solaris *Newboot* environments. It has two primary tasks:
34  *	- Allow end users to manage bootability of Newboot Solaris instances
35  *	- Provide services to other subsystems in Solaris (primarily Install)
36  */
37 
38 /* Headers */
39 #include <stdio.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <alloca.h>
47 #include <stdarg.h>
48 #include <limits.h>
49 #include <signal.h>
50 #include <sys/wait.h>
51 #include <sys/mnttab.h>
52 #include <sys/mntent.h>
53 #include <sys/statvfs.h>
54 #include <libnvpair.h>
55 #include <ftw.h>
56 #include <fcntl.h>
57 #include <strings.h>
58 #include <utime.h>
59 #include <sys/systeminfo.h>
60 #include <sys/dktp/fdisk.h>
61 #include <sys/param.h>
62 #include <dirent.h>
63 #include <ctype.h>
64 #include <libgen.h>
65 #include <sys/sysmacros.h>
66 #include <sys/elf.h>
67 #include <libscf.h>
68 #include <zlib.h>
69 #include <sys/lockfs.h>
70 #include <sys/filio.h>
71 #include <libbe.h>
72 #include <deflt.h>
73 #ifdef i386
74 #include <libfdisk.h>
75 #endif
76 
77 #if !defined(_OBP)
78 #include <sys/ucode.h>
79 #endif
80 
81 #include <pwd.h>
82 #include <grp.h>
83 #include <device_info.h>
84 #include <sys/vtoc.h>
85 #include <sys/efi_partition.h>
86 #include <regex.h>
87 #include <locale.h>
88 #include <sys/mkdev.h>
89 
90 #include "bootadm.h"
91 
92 #ifndef TEXT_DOMAIN
93 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
94 #endif	/* TEXT_DOMAIN */
95 
96 /* Type definitions */
97 
98 /* Primary subcmds */
99 typedef enum {
100 	BAM_MENU = 3,
101 	BAM_ARCHIVE,
102 	BAM_INSTALL
103 } subcmd_t;
104 
105 #define	LINE_INIT	0	/* lineNum initial value */
106 #define	ENTRY_INIT	-1	/* entryNum initial value */
107 #define	ALL_ENTRIES	-2	/* selects all boot entries */
108 
109 #define	PARTNO_NOTFOUND -1	/* Solaris partition not found */
110 #define	PARTNO_EFI	-2	/* EFI partition table found */
111 
112 #define	GRUB_DIR		"/boot/grub"
113 #define	GRUB_STAGE2		GRUB_DIR "/stage2"
114 #define	GRUB_MENU		"/boot/grub/menu.lst"
115 #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
116 #define	GRUB_BACKUP_MENU	"/etc/lu/GRUB_backup_menu"
117 #define	RAMDISK_SPECIAL		"/devices/ramdisk"
118 #define	STUBBOOT		"/stubboot"
119 #define	MULTIBOOT		"/platform/i86pc/multiboot"
120 #define	GRUBSIGN_DIR		"/boot/grub/bootsign"
121 #define	GRUBSIGN_BACKUP		"/etc/bootsign"
122 #define	GRUBSIGN_UFS_PREFIX	"rootfs"
123 #define	GRUBSIGN_ZFS_PREFIX	"pool_"
124 #define	GRUBSIGN_LU_PREFIX	"BE_"
125 #define	UFS_SIGNATURE_LIST	"/var/run/grub_ufs_signatures"
126 #define	ZFS_LEGACY_MNTPT	"/tmp/bootadm_mnt_zfs_legacy"
127 
128 /* SMF */
129 #define	BOOT_ARCHIVE_FMRI	"system/boot-archive:default"
130 #define	SCF_PG_CONFIG		"config"
131 #define	SCF_PROPERTY_FORMAT	"format"
132 
133 /* BE defaults */
134 #define	BE_DEFAULTS		"/etc/default/be"
135 #define	BE_DFLT_BE_HAS_GRUB	"BE_HAS_GRUB="
136 
137 #define	BOOTADM_RDONLY_TEST	"BOOTADM_RDONLY_TEST"
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	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
146 #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
147 
148 #define	GRUB_slice		"/etc/lu/GRUB_slice"
149 #define	GRUB_root		"/etc/lu/GRUB_root"
150 #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
151 #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
152 #define	FINDROOT_INSTALLGRUB	"/etc/lu/installgrub.findroot"
153 #define	LULIB			"/usr/lib/lu/lulib"
154 #define	LULIB_PROPAGATE_FILE	"lulib_propagate_file"
155 #define	CKSUM			"/usr/bin/cksum"
156 #define	LU_MENU_CKSUM		"/etc/lu/menu.cksum"
157 #define	BOOTADM			"/sbin/bootadm"
158 
159 #define	INSTALLGRUB		"/sbin/installgrub"
160 #define	STAGE1			"/boot/grub/stage1"
161 #define	STAGE2			"/boot/grub/stage2"
162 
163 /*
164  * Default file attributes
165  */
166 #define	DEFAULT_DEV_MODE	0644	/* default permissions */
167 #define	DEFAULT_DEV_UID		0	/* user root */
168 #define	DEFAULT_DEV_GID		3	/* group sys */
169 
170 /*
171  * Menu related
172  * menu_cmd_t and menu_cmds must be kept in sync
173  */
174 char *menu_cmds[] = {
175 	"default",	/* DEFAULT_CMD */
176 	"timeout",	/* TIMEOUT_CMD */
177 	"title",	/* TITLE_CMD */
178 	"root",		/* ROOT_CMD */
179 	"kernel",	/* KERNEL_CMD */
180 	"kernel$",	/* KERNEL_DOLLAR_CMD */
181 	"module",	/* MODULE_CMD */
182 	"module$",	/* MODULE_DOLLAR_CMD */
183 	" ",		/* SEP_CMD */
184 	"#",		/* COMMENT_CMD */
185 	"chainloader",	/* CHAINLOADER_CMD */
186 	"args",		/* ARGS_CMD */
187 	"findroot",	/* FINDROOT_CMD */
188 	"bootfs",	/* BOOTFS_CMD */
189 	NULL
190 };
191 
192 char *bam_formats[] = {
193 	"hsfs",
194 	"ufs",
195 	"cpio",
196 	"ufs-nocompress",
197 	NULL
198 };
199 #define	BAM_FORMAT_UNSET -1
200 #define	BAM_FORMAT_HSFS 0
201 short bam_format = BAM_FORMAT_UNSET;
202 
203 #define	OPT_ENTRY_NUM	"entry"
204 
205 /*
206  * exec_cmd related
207  */
208 typedef struct {
209 	line_t *head;
210 	line_t *tail;
211 } filelist_t;
212 
213 #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
214 #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
215 
216 #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
217 #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
218 #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
219 #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
220 
221 #define	FILE_STAT_TIMESTAMP	"boot/solaris/timestamp.cache"
222 
223 /* Globals */
224 int bam_verbose;
225 int bam_force;
226 int bam_debug;
227 int bam_skip_lock;
228 static char *prog;
229 static subcmd_t bam_cmd;
230 char *bam_root;
231 int bam_rootlen;
232 static int bam_root_readonly;
233 int bam_alt_root;
234 static int bam_extend = 0;
235 static int bam_purge = 0;
236 static char *bam_subcmd;
237 static char *bam_opt;
238 static char **bam_argv;
239 static char *bam_pool;
240 static int bam_argc;
241 static int bam_check;
242 static int bam_saved_check;
243 static int bam_smf_check;
244 static int bam_lock_fd = -1;
245 static int bam_zfs;
246 static int bam_mbr;
247 char rootbuf[PATH_MAX] = "/";
248 static int bam_update_all;
249 static int bam_alt_platform;
250 static char *bam_platform;
251 static char *bam_home_env = NULL;
252 
253 /* function prototypes */
254 static void parse_args_internal(int, char *[]);
255 static void parse_args(int, char *argv[]);
256 static error_t bam_menu(char *, char *, int, char *[]);
257 static error_t bam_install(char *, char *);
258 static error_t bam_archive(char *, char *);
259 
260 static void bam_lock(void);
261 static void bam_unlock(void);
262 
263 static int exec_cmd(char *, filelist_t *);
264 static error_t read_globals(menu_t *, char *, char *, int);
265 static int menu_on_bootdisk(char *os_root, char *menu_root);
266 static menu_t *menu_read(char *);
267 static error_t menu_write(char *, menu_t *);
268 static void linelist_free(line_t *);
269 static void menu_free(menu_t *);
270 static void filelist_free(filelist_t *);
271 static error_t list2file(char *, char *, char *, line_t *);
272 static error_t list_entry(menu_t *, char *, char *);
273 static error_t list_setting(menu_t *, char *, char *);
274 static error_t delete_all_entries(menu_t *, char *, char *);
275 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
276 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
277 
278 static error_t install_bootloader(void);
279 static error_t update_archive(char *, char *);
280 static error_t list_archive(char *, char *);
281 static error_t update_all(char *, char *);
282 static error_t read_list(char *, filelist_t *);
283 static error_t set_option(menu_t *, char *, char *);
284 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
285 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
286 static char *expand_path(const char *);
287 
288 static long s_strtol(char *);
289 static int s_fputs(char *, FILE *);
290 
291 static int is_amd64(void);
292 static char *get_machine(void);
293 static void append_to_flist(filelist_t *, char *);
294 static int ufs_add_to_sign_list(char *sign);
295 static error_t synchronize_BE_menu(void);
296 
297 #if !defined(_OBP)
298 static void ucode_install();
299 #endif
300 
301 /* Menu related sub commands */
302 static subcmd_defn_t menu_subcmds[] = {
303 	"set_option",		OPT_ABSENT,	set_option, 0,	/* PUB */
304 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
305 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
306 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
307 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
308 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
309 	"list_setting",		OPT_OPTIONAL,	list_setting, 1, /* menu */
310 	"disable_hypervisor",	OPT_ABSENT,	cvt_to_metal, 0, /* menu */
311 	"enable_hypervisor",	OPT_ABSENT,	cvt_to_hyper, 0, /* menu */
312 	NULL,			0,		NULL, 0	/* must be last */
313 };
314 
315 /* Archive related sub commands */
316 static subcmd_defn_t arch_subcmds[] = {
317 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
318 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
319 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
320 	NULL,			0,		NULL, 0	/* must be last */
321 };
322 
323 /* Install related sub commands */
324 static subcmd_defn_t inst_subcmds[] = {
325 	"install_bootloader",	OPT_ABSENT,	install_bootloader, 0, /* PUB */
326 	NULL,			0,		NULL, 0	/* must be last */
327 };
328 
329 #define	build_path(buf, len, root, prefix, suffix) \
330     snprintf((buf), (len), "%s%s%s%s%s", (root), (prefix), get_machine(), \
331     is_flag_on(IS_SPARC_TARGET) ? "" : "/amd64", (suffix))
332 
333 /*
334  * Directory specific flags:
335  * NEED_UPDATE : the specified archive needs to be updated
336  * NO_EXTEND   : don't extend the specified archive, but recreate it
337  */
338 #define	NEED_UPDATE		0x00000001
339 #define	NO_EXTEND		0x00000002
340 
341 #define	set_dir_flag(f)		(walk_arg.dirinfo.flags |= (f))
342 #define	unset_dir_flag(f)	(walk_arg.dirinfo.flags &= ~(f))
343 #define	is_dir_flag_on(f)	(walk_arg.dirinfo.flags & (f) ? 1 : 0)
344 
345 #define	get_cachedir()		(walk_arg.dirinfo.cdir_path)
346 #define	get_updatedir()		(walk_arg.dirinfo.update_path)
347 #define	get_count()		(walk_arg.dirinfo.count)
348 #define	has_cachedir()		(walk_arg.dirinfo.has_dir)
349 #define	set_dir_present()	(walk_arg.dirinfo.has_dir = 1)
350 
351 /*
352  * dirinfo_t (specific cache directory information):
353  * cdir_path:   path to the archive cache directory
354  * update_path: path to the update directory (contains the files that will be
355  *              used to extend the archive)
356  * has_dir:	the specified cache directory is active
357  * count:	the number of files to update
358  * flags:	directory specific flags
359  */
360 typedef struct _dirinfo {
361 	char	cdir_path[PATH_MAX];
362 	char	update_path[PATH_MAX];
363 	int	has_dir;
364 	int	count;
365 	int	flags;
366 } dirinfo_t;
367 
368 /*
369  * Update flags:
370  * NEED_CACHE_DIR : cache directory is missing and needs to be created
371  * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
372  * UPDATE_ERROR : an error occourred while traversing the list of files
373  * RDONLY_FSCHK : the target filesystem is read-only
374  * RAMDSK_FSCHK : the target filesystem is on a ramdisk
375  */
376 #define	NEED_CACHE_DIR		0x00000001
377 #define	IS_SPARC_TARGET		0x00000002
378 #define	UPDATE_ERROR		0x00000004
379 #define	RDONLY_FSCHK		0x00000008
380 #define	INVALIDATE_CACHE	0x00000010
381 
382 #define	is_flag_on(flag)	(walk_arg.update_flags & flag ? 1 : 0)
383 #define	set_flag(flag)		(walk_arg.update_flags |= flag)
384 #define	unset_flag(flag)	(walk_arg.update_flags &= ~flag)
385 
386 /*
387  * struct walk_arg :
388  * update_flags: flags related to the current updating process
389  * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
390  * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
391  */
392 static struct {
393 	int		update_flags;
394 	nvlist_t	*new_nvlp;
395 	nvlist_t	*old_nvlp;
396 	FILE		*sparcfile;
397 	dirinfo_t	dirinfo;
398 } walk_arg;
399 
400 struct safefile {
401 	char *name;
402 	struct safefile *next;
403 };
404 
405 static struct safefile *safefiles = NULL;
406 
407 /*
408  * svc:/system/filesystem/usr:default service checks for this file and
409  * does a boot archive update and then reboot the system.
410  */
411 #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
412 
413 /*
414  * svc:/system/boot-archive-update:default checks for this file and
415  * updates the boot archive.
416  */
417 #define	NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
418 
419 /* Thanks growisofs */
420 #define	CD_BLOCK	((off64_t)2048)
421 #define	VOLDESC_OFF	16
422 #define	DVD_BLOCK	(32*1024)
423 #define	MAX_IVDs	16
424 
425 struct iso_pdesc {
426     unsigned char type	[1];
427     unsigned char id	[5];
428     unsigned char void1	[80-5-1];
429     unsigned char volume_space_size [8];
430     unsigned char void2	[2048-80-8];
431 };
432 
433 /*
434  * COUNT_MAX:	maximum number of changed files to justify a multisession update
435  * BA_SIZE_MAX:	maximum size of the boot_archive to justify a multisession
436  *		update
437  */
438 #define	COUNT_MAX		50
439 #define	BA_SIZE_MAX		(50 * 1024 * 1024)
440 
441 #define	bam_nowrite()		(bam_check || bam_smf_check)
442 
443 static int sync_menu = 1;	/* whether we need to sync the BE menus */
444 
445 static void
446 usage(void)
447 {
448 	(void) fprintf(stderr, "USAGE:\n");
449 
450 	/* archive usage */
451 	(void) fprintf(stderr,
452 	    "\t%s update-archive [-vnf] [-R altroot [-p platform]] "
453 	    "[-F format]\n", prog);
454 	(void) fprintf(stderr,
455 	    "\t%s list-archive [-R altroot [-p platform]]\n", prog);
456 #if defined(_OBP)
457 	(void) fprintf(stderr,
458 	    "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
459 #else
460 	(void) fprintf(stderr,
461 	    "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
462 #endif
463 #if !defined(_OBP)
464 	/* x86 only */
465 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
466 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
467 #endif
468 }
469 
470 /*
471  * Best effort attempt to restore the $HOME value.
472  */
473 static void
474 restore_env()
475 {
476 	char	home_env[PATH_MAX];
477 
478 	if (bam_home_env) {
479 		(void) snprintf(home_env, sizeof (home_env), "HOME=%s",
480 		    bam_home_env);
481 		(void) putenv(home_env);
482 	}
483 }
484 
485 
486 #define		SLEEP_TIME	5
487 #define		MAX_TRIES	4
488 
489 /*
490  * Sanitize the environment in which bootadm will execute its sub-processes
491  * (ex. mkisofs). This is done to prevent those processes from attempting
492  * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
493  * or, potentially, insecure.
494  */
495 static void
496 sanitize_env()
497 {
498 	int	stry = 0;
499 
500 	/* don't depend on caller umask */
501 	(void) umask(0022);
502 
503 	/* move away from a potential unsafe current working directory */
504 	while (chdir("/") == -1) {
505 		if (errno != EINTR) {
506 			bam_print("WARNING: unable to chdir to /");
507 			break;
508 		}
509 	}
510 
511 	bam_home_env = getenv("HOME");
512 	while (bam_home_env != NULL && putenv("HOME=/") == -1) {
513 		if (errno == ENOMEM) {
514 			/* retry no more than MAX_TRIES times */
515 			if (++stry > MAX_TRIES) {
516 				bam_print("WARNING: unable to recover from "
517 				    "system memory pressure... aborting \n");
518 				bam_exit(EXIT_FAILURE);
519 			}
520 			/* memory is tight, try to sleep */
521 			bam_print("Attempting to recover from memory pressure: "
522 			    "sleeping for %d seconds\n", SLEEP_TIME * stry);
523 			(void) sleep(SLEEP_TIME * stry);
524 		} else {
525 			bam_print("WARNING: unable to sanitize HOME\n");
526 		}
527 	}
528 }
529 
530 int
531 main(int argc, char *argv[])
532 {
533 	error_t ret = BAM_SUCCESS;
534 
535 	(void) setlocale(LC_ALL, "");
536 	(void) textdomain(TEXT_DOMAIN);
537 
538 	if ((prog = strrchr(argv[0], '/')) == NULL) {
539 		prog = argv[0];
540 	} else {
541 		prog++;
542 	}
543 
544 	INJECT_ERROR1("ASSERT_ON", assert(0))
545 
546 	sanitize_env();
547 
548 	parse_args(argc, argv);
549 
550 	switch (bam_cmd) {
551 		case BAM_MENU:
552 			if (is_grub(bam_alt_root ? bam_root : "/")) {
553 				ret = bam_menu(bam_subcmd, bam_opt,
554 				    bam_argc, bam_argv);
555 			} else {
556 				ret = bam_loader_menu(bam_subcmd, bam_opt,
557 				    bam_argc, bam_argv);
558 			}
559 			break;
560 		case BAM_ARCHIVE:
561 			ret = bam_archive(bam_subcmd, bam_opt);
562 			break;
563 		case BAM_INSTALL:
564 			ret = bam_install(bam_subcmd, bam_opt);
565 			break;
566 		default:
567 			usage();
568 			bam_exit(1);
569 	}
570 
571 	if (ret != BAM_SUCCESS)
572 		bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
573 
574 	bam_unlock();
575 	return (0);
576 }
577 
578 /*
579  * Equivalence of public and internal commands:
580  *	update-archive  -- -a update
581  *	list-archive	-- -a list
582  *	set-menu	-- -m set_option
583  *	list-menu	-- -m list_entry
584  *	update-menu	-- -m update_entry
585  *	install-bootloader	-- -i install_bootloader
586  */
587 static struct cmd_map {
588 	char *bam_cmdname;
589 	int bam_cmd;
590 	char *bam_subcmd;
591 } cmd_map[] = {
592 	{ "update-archive",	BAM_ARCHIVE,	"update"},
593 	{ "list-archive",	BAM_ARCHIVE,	"list"},
594 	{ "set-menu",		BAM_MENU,	"set_option"},
595 	{ "list-menu",		BAM_MENU,	"list_entry"},
596 	{ "update-menu",	BAM_MENU,	"update_entry"},
597 	{ "install-bootloader",	BAM_INSTALL,	"install_bootloader"},
598 	{ NULL,			0,		NULL}
599 };
600 
601 /*
602  * Commands syntax published in bootadm(1M) are parsed here
603  */
604 static void
605 parse_args(int argc, char *argv[])
606 {
607 	struct cmd_map *cmp = cmd_map;
608 
609 	/* command conforming to the final spec */
610 	if (argc > 1 && argv[1][0] != '-') {
611 		/*
612 		 * Map commands to internal table.
613 		 */
614 		while (cmp->bam_cmdname) {
615 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
616 				bam_cmd = cmp->bam_cmd;
617 				bam_subcmd = cmp->bam_subcmd;
618 				break;
619 			}
620 			cmp++;
621 		}
622 		if (cmp->bam_cmdname == NULL) {
623 			usage();
624 			bam_exit(1);
625 		}
626 		argc--;
627 		argv++;
628 	}
629 
630 	parse_args_internal(argc, argv);
631 }
632 
633 /*
634  * A combination of public and private commands are parsed here.
635  * The internal syntax and the corresponding functionality are:
636  *	-a update			-- update-archive
637  *	-a list				-- list-archive
638  *	-a update_all			-- (reboot to sync all mnted OS archive)
639  *	-i install_bootloader		-- install-bootloader
640  *	-m update_entry			-- update-menu
641  *	-m list_entry			-- list-menu
642  *	-m update_temp			-- (reboot -- [boot-args])
643  *	-m delete_all_entries		-- (called from install)
644  *	-m enable_hypervisor [args]	-- cvt_to_hyper
645  *	-m disable_hypervisor		-- cvt_to_metal
646  *	-m list_setting [entry] [value]	-- list_setting
647  *
648  * A set of private flags is there too:
649  *	-Q		-- purge the cache directories and rebuild them
650  *	-e		-- use the (faster) archive update approach (used by
651  *			   reboot)
652  *	-L		-- skip locking
653  */
654 static void
655 parse_args_internal(int argc, char *argv[])
656 {
657 	int c, i, error;
658 	extern char *optarg;
659 	extern int optind, opterr;
660 #if defined(_OBP)
661 	const char *optstring = "a:d:fF:i:m:no:veQCLR:p:P:XZ";
662 #else
663 	const char *optstring = "a:d:fF:i:m:no:veQCMLR:p:P:XZ";
664 #endif
665 
666 	/* Suppress error message from getopt */
667 	opterr = 0;
668 
669 	error = 0;
670 	while ((c = getopt(argc, argv, optstring)) != -1) {
671 		switch (c) {
672 		case 'a':
673 			if (bam_cmd) {
674 				error = 1;
675 				bam_error(
676 				    _("multiple commands specified: -%c\n"), c);
677 			}
678 			bam_cmd = BAM_ARCHIVE;
679 			bam_subcmd = optarg;
680 			break;
681 		case 'd':
682 			if (bam_debug) {
683 				error = 1;
684 				bam_error(
685 				    _("duplicate options specified: -%c\n"), c);
686 			}
687 			bam_debug = s_strtol(optarg);
688 			break;
689 		case 'f':
690 			bam_force = 1;
691 			break;
692 		case 'F':
693 			if (bam_format != BAM_FORMAT_UNSET) {
694 				error = 1;
695 				bam_error(
696 				    _("multiple formats specified: -%c\n"), c);
697 			}
698 			for (i = 0; bam_formats[i] != NULL; i++) {
699 				if (strcmp(bam_formats[i], optarg) == 0) {
700 					bam_format = i;
701 					break;
702 				}
703 			}
704 			if (bam_format == BAM_FORMAT_UNSET) {
705 				error = 1;
706 				bam_error(
707 				    _("unknown format specified: -%c %s\n"),
708 				    c, optarg);
709 			}
710 			break;
711 		case 'Q':
712 			bam_purge = 1;
713 			break;
714 		case 'L':
715 			bam_skip_lock = 1;
716 			break;
717 		case 'i':
718 			if (bam_cmd) {
719 				error = 1;
720 				bam_error(
721 				    _("multiple commands specified: -%c\n"), c);
722 			}
723 			bam_cmd = BAM_INSTALL;
724 			bam_subcmd = optarg;
725 			break;
726 		case 'm':
727 			if (bam_cmd) {
728 				error = 1;
729 				bam_error(
730 				    _("multiple commands specified: -%c\n"), c);
731 			}
732 			bam_cmd = BAM_MENU;
733 			bam_subcmd = optarg;
734 			break;
735 #if !defined(_OBP)
736 		case 'M':
737 			bam_mbr = 1;
738 			break;
739 #endif
740 		case 'n':
741 			bam_check = 1;
742 			/*
743 			 * We save the original value of bam_check. The new
744 			 * approach in case of a read-only filesystem is to
745 			 * behave as a check, so we need a way to restore the
746 			 * original value after the evaluation of the read-only
747 			 * filesystem has been done.
748 			 * Even if we don't allow at the moment a check with
749 			 * update_all, this approach is more robust than
750 			 * simply resetting bam_check to zero.
751 			 */
752 			bam_saved_check = 1;
753 			break;
754 		case 'o':
755 			if (bam_opt) {
756 				error = 1;
757 				bam_error(
758 				    _("duplicate options specified: -%c\n"), c);
759 			}
760 			bam_opt = optarg;
761 			break;
762 		case 'v':
763 			bam_verbose = 1;
764 			break;
765 		case 'C':
766 			bam_smf_check = 1;
767 			break;
768 		case 'P':
769 			if (bam_pool != NULL) {
770 				error = 1;
771 				bam_error(
772 				    _("duplicate options specified: -%c\n"), c);
773 			}
774 			bam_pool = optarg;
775 			break;
776 		case 'R':
777 			if (bam_root) {
778 				error = 1;
779 				bam_error(
780 				    _("duplicate options specified: -%c\n"), c);
781 				break;
782 			} else if (realpath(optarg, rootbuf) == NULL) {
783 				error = 1;
784 				bam_error(_("cannot resolve path %s: %s\n"),
785 				    optarg, strerror(errno));
786 				break;
787 			}
788 			bam_alt_root = 1;
789 			bam_root = rootbuf;
790 			bam_rootlen = strlen(rootbuf);
791 			break;
792 		case 'p':
793 			bam_alt_platform = 1;
794 			bam_platform = optarg;
795 			if ((strcmp(bam_platform, "i86pc") != 0) &&
796 			    (strcmp(bam_platform, "sun4u") != 0) &&
797 			    (strcmp(bam_platform, "sun4v") != 0)) {
798 				error = 1;
799 				bam_error(_("invalid platform %s - must be "
800 				    "one of sun4u, sun4v or i86pc\n"),
801 				    bam_platform);
802 			}
803 			break;
804 		case 'X':
805 			bam_is_hv = BAM_HV_PRESENT;
806 			break;
807 		case 'Z':
808 			bam_zfs = 1;
809 			break;
810 		case 'e':
811 			bam_extend = 1;
812 			break;
813 		case '?':
814 			error = 1;
815 			bam_error(_("invalid option or missing option "
816 			    "argument: -%c\n"), optopt);
817 			break;
818 		default :
819 			error = 1;
820 			bam_error(_("invalid option or missing option "
821 			    "argument: -%c\n"), c);
822 			break;
823 		}
824 	}
825 
826 	/*
827 	 * An alternate platform requires an alternate root
828 	 */
829 	if (bam_alt_platform && bam_alt_root == 0) {
830 		usage();
831 		bam_exit(0);
832 	}
833 
834 	/*
835 	 * A command option must be specfied
836 	 */
837 	if (!bam_cmd) {
838 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
839 			usage();
840 			bam_exit(0);
841 		}
842 		bam_error(_("a command option must be specified\n"));
843 		error = 1;
844 	}
845 
846 	if (error) {
847 		usage();
848 		bam_exit(1);
849 	}
850 
851 	if (optind > argc) {
852 		bam_error(_("Internal error: %s\n"), "parse_args");
853 		bam_exit(1);
854 	} else if (optind < argc) {
855 		bam_argv = &argv[optind];
856 		bam_argc = argc - optind;
857 	}
858 
859 	/*
860 	 * mbr and pool are options for install_bootloader
861 	 */
862 	if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
863 		usage();
864 		bam_exit(0);
865 	}
866 
867 	/*
868 	 * -n implies verbose mode
869 	 */
870 	if (bam_check)
871 		bam_verbose = 1;
872 }
873 
874 error_t
875 check_subcmd_and_options(
876 	char *subcmd,
877 	char *opt,
878 	subcmd_defn_t *table,
879 	error_t (**fp)())
880 {
881 	int i;
882 
883 	if (subcmd == NULL) {
884 		bam_error(_("this command requires a sub-command\n"));
885 		return (BAM_ERROR);
886 	}
887 
888 	if (strcmp(subcmd, "set_option") == 0) {
889 		if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
890 			bam_error(_("missing argument for sub-command\n"));
891 			usage();
892 			return (BAM_ERROR);
893 		} else if (bam_argc > 1 || bam_argv[1] != NULL) {
894 			bam_error(_("invalid trailing arguments\n"));
895 			usage();
896 			return (BAM_ERROR);
897 		}
898 	} else if (strcmp(subcmd, "update_all") == 0) {
899 		/*
900 		 * The only option we accept for the "update_all"
901 		 * subcmd is "fastboot".
902 		 */
903 		if (bam_argc > 1 || (bam_argc == 1 &&
904 		    strcmp(bam_argv[0], "fastboot") != 0)) {
905 			bam_error(_("invalid trailing arguments\n"));
906 			usage();
907 			return (BAM_ERROR);
908 		}
909 		if (bam_argc == 1)
910 			sync_menu = 0;
911 	} else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
912 	    (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
913 		/*
914 		 * Of the remaining subcommands, only "enable_hypervisor" and
915 		 * "list_setting" take trailing arguments.
916 		 */
917 		bam_error(_("invalid trailing arguments\n"));
918 		usage();
919 		return (BAM_ERROR);
920 	}
921 
922 	if (bam_root == NULL) {
923 		bam_root = rootbuf;
924 		bam_rootlen = 1;
925 	}
926 
927 	/* verify that subcmd is valid */
928 	for (i = 0; table[i].subcmd != NULL; i++) {
929 		if (strcmp(table[i].subcmd, subcmd) == 0)
930 			break;
931 	}
932 
933 	if (table[i].subcmd == NULL) {
934 		bam_error(_("invalid sub-command specified: %s\n"), subcmd);
935 		return (BAM_ERROR);
936 	}
937 
938 	if (table[i].unpriv == 0 && geteuid() != 0) {
939 		bam_error(_("you must be root to run this command\n"));
940 		return (BAM_ERROR);
941 	}
942 
943 	/*
944 	 * Currently only privileged commands need a lock
945 	 */
946 	if (table[i].unpriv == 0)
947 		bam_lock();
948 
949 	/* subcmd verifies that opt is appropriate */
950 	if (table[i].option != OPT_OPTIONAL) {
951 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
952 			if (opt)
953 				bam_error(_("this sub-command (%s) does not "
954 				    "take options\n"), subcmd);
955 			else
956 				bam_error(_("an option is required for this "
957 				    "sub-command: %s\n"), subcmd);
958 			return (BAM_ERROR);
959 		}
960 	}
961 
962 	*fp = table[i].handler;
963 
964 	return (BAM_SUCCESS);
965 }
966 
967 /*
968  * NOTE: A single "/" is also considered a trailing slash and will
969  * be deleted.
970  */
971 void
972 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
973 {
974 	size_t dstlen;
975 
976 	assert(src);
977 	assert(dst);
978 
979 	(void) strlcpy(dst, src, dstsize);
980 
981 	dstlen = strlen(dst);
982 	if (dst[dstlen - 1] == '/') {
983 		dst[dstlen - 1] = '\0';
984 	}
985 }
986 
987 static int
988 is_safe_exec(char *path)
989 {
990 	struct stat	sb;
991 
992 	if (lstat(path, &sb) != 0) {
993 		bam_error(_("stat of file failed: %s: %s\n"), path,
994 		    strerror(errno));
995 		return (BAM_ERROR);
996 	}
997 
998 	if (!S_ISREG(sb.st_mode)) {
999 		bam_error(_("%s is not a regular file, skipping\n"), path);
1000 		return (BAM_ERROR);
1001 	}
1002 
1003 	if (sb.st_uid != getuid()) {
1004 		bam_error(_("%s is not owned by %d, skipping\n"),
1005 		    path, getuid());
1006 		return (BAM_ERROR);
1007 	}
1008 
1009 	if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
1010 		bam_error(_("%s is others or group writable, skipping\n"),
1011 		    path);
1012 		return (BAM_ERROR);
1013 	}
1014 
1015 	return (BAM_SUCCESS);
1016 }
1017 
1018 static error_t
1019 list_setting(menu_t *mp, char *which, char *setting)
1020 {
1021 	line_t	*lp;
1022 	entry_t	*ent;
1023 
1024 	char	*p = which;
1025 	int	entry;
1026 
1027 	int	found;
1028 
1029 	assert(which);
1030 	assert(setting);
1031 
1032 	if (*which != NULL) {
1033 		/*
1034 		 * If "which" is not a number, assume it's a setting we want
1035 		 * to look for and so set up the routine to look for "which"
1036 		 * in the default entry.
1037 		 */
1038 		while (*p != NULL)
1039 			if (!(isdigit((int)*p++))) {
1040 				setting = which;
1041 				which = mp->curdefault->arg;
1042 				break;
1043 			}
1044 	} else {
1045 		which = mp->curdefault->arg;
1046 	}
1047 
1048 	entry = atoi(which);
1049 
1050 	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1051 	    ent = ent->next)
1052 		;
1053 
1054 	if (!ent) {
1055 		bam_error(_("no matching entry found\n"));
1056 		return (BAM_ERROR);
1057 	}
1058 
1059 	found = (*setting == NULL);
1060 
1061 	for (lp = ent->start; lp != NULL; lp = lp->next) {
1062 		if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
1063 			bam_print("%s\n", lp->line);
1064 		else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1065 			bam_print("%s\n", lp->arg);
1066 			found = 1;
1067 		}
1068 
1069 		if (lp == ent->end)
1070 			break;
1071 	}
1072 
1073 	if (!found) {
1074 		bam_error(_("no matching entry found\n"));
1075 		return (BAM_ERROR);
1076 	}
1077 
1078 	return (BAM_SUCCESS);
1079 }
1080 
1081 static error_t
1082 install_bootloader(void)
1083 {
1084 	nvlist_t	*nvl;
1085 	uint16_t	flags = 0;
1086 	int		found = 0;
1087 	struct extmnttab mnt;
1088 	struct stat	statbuf = {0};
1089 	be_node_list_t	*be_nodes, *node;
1090 	FILE		*fp;
1091 	char		*root_ds = NULL;
1092 	int		ret = BAM_ERROR;
1093 
1094 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1095 		bam_error(_("out of memory\n"));
1096 		return (ret);
1097 	}
1098 
1099 	/*
1100 	 * if bam_alt_root is set, the stage files are used from alt root.
1101 	 * if pool is set, the target devices are pool devices, stage files
1102 	 * are read from pool bootfs unless alt root is set.
1103 	 *
1104 	 * use arguments as targets, stage files are from alt or current root
1105 	 * if no arguments and no pool, install on current boot pool.
1106 	 */
1107 
1108 	if (bam_alt_root) {
1109 		if (stat(bam_root, &statbuf) != 0) {
1110 			bam_error(_("stat of file failed: %s: %s\n"), bam_root,
1111 			    strerror(errno));
1112 			goto done;
1113 		}
1114 		if ((fp = fopen(MNTTAB, "r")) == NULL) {
1115 			bam_error(_("failed to open file: %s: %s\n"),
1116 			    MNTTAB, strerror(errno));
1117 			goto done;
1118 		}
1119 		resetmnttab(fp);
1120 		while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1121 			if (mnt.mnt_major == major(statbuf.st_dev) &&
1122 			    mnt.mnt_minor == minor(statbuf.st_dev)) {
1123 				found = 1;
1124 				root_ds = strdup(mnt.mnt_special);
1125 				break;
1126 			}
1127 		}
1128 		(void) fclose(fp);
1129 
1130 		if (found == 0) {
1131 			bam_error(_("alternate root %s not in mnttab\n"),
1132 			    bam_root);
1133 			goto done;
1134 		}
1135 		if (root_ds == NULL) {
1136 			bam_error(_("out of memory\n"));
1137 			goto done;
1138 		}
1139 
1140 		if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1141 			bam_error(_("No BE's found\n"));
1142 			goto done;
1143 		}
1144 		for (node = be_nodes; node != NULL; node = node->be_next_node)
1145 			if (strcmp(root_ds, node->be_root_ds) == 0)
1146 				break;
1147 
1148 		if (node == NULL)
1149 			bam_error(_("BE (%s) does not exist\n"), root_ds);
1150 
1151 		free(root_ds);
1152 		root_ds = NULL;
1153 		if (node == NULL) {
1154 			be_free_list(be_nodes);
1155 			goto done;
1156 		}
1157 		ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1158 		    node->be_node_name);
1159 		ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1160 		    node->be_root_ds);
1161 		be_free_list(be_nodes);
1162 		if (ret != 0) {
1163 			ret = BAM_ERROR;
1164 			goto done;
1165 		}
1166 	}
1167 
1168 	if (bam_force)
1169 		flags |= BE_INSTALLBOOT_FLAG_FORCE;
1170 	if (bam_mbr)
1171 		flags |= BE_INSTALLBOOT_FLAG_MBR;
1172 	if (bam_verbose)
1173 		flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1174 
1175 	if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1176 		bam_error(_("out of memory\n"));
1177 		ret = BAM_ERROR;
1178 		goto done;
1179 	}
1180 
1181 	/*
1182 	 * if altroot was set, we got be name and be root, only need
1183 	 * to set pool name as target.
1184 	 * if no altroot, need to find be name and root from pool.
1185 	 */
1186 	if (bam_pool != NULL) {
1187 		ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1188 		if (ret != 0) {
1189 			ret = BAM_ERROR;
1190 			goto done;
1191 		}
1192 		if (found) {
1193 			ret = be_installboot(nvl);
1194 			if (ret != 0)
1195 				ret = BAM_ERROR;
1196 			goto done;
1197 		}
1198 	}
1199 
1200 	if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1201 		bam_error(_("No BE's found\n"));
1202 		ret = BAM_ERROR;
1203 		goto done;
1204 	}
1205 
1206 	if (bam_pool != NULL) {
1207 		/*
1208 		 * find active be_node in bam_pool
1209 		 */
1210 		for (node = be_nodes; node != NULL; node = node->be_next_node) {
1211 			if (strcmp(bam_pool, node->be_rpool) != 0)
1212 				continue;
1213 			if (node->be_active_on_boot)
1214 				break;
1215 		}
1216 		if (node == NULL) {
1217 			bam_error(_("No active BE in %s\n"), bam_pool);
1218 			be_free_list(be_nodes);
1219 			ret = BAM_ERROR;
1220 			goto done;
1221 		}
1222 		ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1223 		    node->be_node_name);
1224 		ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1225 		    node->be_root_ds);
1226 		be_free_list(be_nodes);
1227 		if (ret != 0) {
1228 			ret = BAM_ERROR;
1229 			goto done;
1230 		}
1231 		ret = be_installboot(nvl);
1232 		if (ret != 0)
1233 			ret = BAM_ERROR;
1234 		goto done;
1235 	}
1236 
1237 	/*
1238 	 * get dataset for "/" and fill up the args.
1239 	 */
1240 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1241 		bam_error(_("failed to open file: %s: %s\n"),
1242 		    MNTTAB, strerror(errno));
1243 		ret = BAM_ERROR;
1244 		be_free_list(be_nodes);
1245 		goto done;
1246 	}
1247 	resetmnttab(fp);
1248 	found = 0;
1249 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1250 		if (strcmp(mnt.mnt_mountp, "/") == 0) {
1251 			found = 1;
1252 			root_ds = strdup(mnt.mnt_special);
1253 			break;
1254 		}
1255 	}
1256 	(void) fclose(fp);
1257 
1258 	if (found == 0) {
1259 		bam_error(_("alternate root %s not in mnttab\n"), "/");
1260 		ret = BAM_ERROR;
1261 		be_free_list(be_nodes);
1262 		goto done;
1263 	}
1264 	if (root_ds == NULL) {
1265 		bam_error(_("out of memory\n"));
1266 		ret = BAM_ERROR;
1267 		be_free_list(be_nodes);
1268 		goto done;
1269 	}
1270 
1271 	for (node = be_nodes; node != NULL; node = node->be_next_node) {
1272 		if (strcmp(root_ds, node->be_root_ds) == 0)
1273 			break;
1274 	}
1275 
1276 	if (node == NULL) {
1277 		bam_error(_("No such BE: %s\n"), root_ds);
1278 		free(root_ds);
1279 		be_free_list(be_nodes);
1280 		ret = BAM_ERROR;
1281 		goto done;
1282 	}
1283 	free(root_ds);
1284 
1285 	ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1286 	ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1287 	ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1288 	be_free_list(be_nodes);
1289 
1290 	if (ret != 0)
1291 		ret = BAM_ERROR;
1292 	else
1293 		ret = be_installboot(nvl) ? BAM_ERROR : 0;
1294 done:
1295 	nvlist_free(nvl);
1296 
1297 	return (ret);
1298 }
1299 
1300 static error_t
1301 bam_install(char *subcmd, char *opt)
1302 {
1303 	error_t (*f)(void);
1304 
1305 	/*
1306 	 * Check arguments
1307 	 */
1308 	if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1309 	    BAM_ERROR)
1310 		return (BAM_ERROR);
1311 
1312 	return (f());
1313 }
1314 
1315 static error_t
1316 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1317 {
1318 	error_t			ret;
1319 	char			menu_path[PATH_MAX];
1320 	char			clean_menu_root[PATH_MAX];
1321 	char			path[PATH_MAX];
1322 	menu_t			*menu;
1323 	char			menu_root[PATH_MAX];
1324 	struct stat		sb;
1325 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1326 	char			*special = NULL;
1327 	char			*pool = NULL;
1328 	zfs_mnted_t		zmnted;
1329 	char			*zmntpt = NULL;
1330 	char			*osdev;
1331 	char			*osroot;
1332 	const char		*fcn = "bam_menu()";
1333 
1334 	/*
1335 	 * Menu sub-command only applies to GRUB (i.e. x86)
1336 	 */
1337 	if (!is_grub(bam_alt_root ? bam_root : "/")) {
1338 		bam_error(_("not a GRUB 0.97 based Illumos instance. "
1339 		    "Operation not supported\n"));
1340 		return (BAM_ERROR);
1341 	}
1342 
1343 	/*
1344 	 * Check arguments
1345 	 */
1346 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1347 	if (ret == BAM_ERROR) {
1348 		return (BAM_ERROR);
1349 	}
1350 
1351 	assert(bam_root);
1352 
1353 	(void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1354 	osdev = osroot = NULL;
1355 
1356 	if (strcmp(subcmd, "update_entry") == 0) {
1357 		assert(opt);
1358 
1359 		osdev = strtok(opt, ",");
1360 		assert(osdev);
1361 		osroot = strtok(NULL, ",");
1362 		if (osroot) {
1363 			/* fixup bam_root so that it points at osroot */
1364 			if (realpath(osroot, rootbuf) == NULL) {
1365 				bam_error(_("cannot resolve path %s: %s\n"),
1366 				    osroot, strerror(errno));
1367 				return (BAM_ERROR);
1368 			}
1369 			bam_alt_root = 1;
1370 			bam_root  = rootbuf;
1371 			bam_rootlen = strlen(rootbuf);
1372 		}
1373 	}
1374 
1375 	/*
1376 	 * We support menu on PCFS (under certain conditions), but
1377 	 * not the OS root
1378 	 */
1379 	if (is_pcfs(bam_root)) {
1380 		bam_error(_("root <%s> on PCFS is not supported\n"), bam_root);
1381 		return (BAM_ERROR);
1382 	}
1383 
1384 	if (stat(menu_root, &sb) == -1) {
1385 		bam_error(_("cannot find GRUB menu\n"));
1386 		return (BAM_ERROR);
1387 	}
1388 
1389 	BAM_DPRINTF(("%s: menu root is %s\n", fcn, menu_root));
1390 
1391 	/*
1392 	 * We no longer use the GRUB slice file. If it exists, then
1393 	 * the user is doing something that is unsupported (such as
1394 	 * standard upgrading an old Live Upgrade BE). If that
1395 	 * happens, mimic existing behavior i.e. pretend that it is
1396 	 * not a BE. Emit a warning though.
1397 	 */
1398 	if (bam_alt_root) {
1399 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
1400 		    GRUB_slice);
1401 	} else {
1402 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1403 	}
1404 
1405 	if (bam_verbose && stat(path, &sb) == 0)
1406 		bam_error(_("unsupported GRUB slice file (%s) exists - "
1407 		    "ignoring.\n"), path);
1408 
1409 	if (is_zfs(menu_root)) {
1410 		assert(strcmp(menu_root, bam_root) == 0);
1411 		special = get_special(menu_root);
1412 		INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1413 		if (special == NULL) {
1414 			bam_error(_("cant find special file for "
1415 			    "mount-point %s\n"), menu_root);
1416 			return (BAM_ERROR);
1417 		}
1418 		pool = strtok(special, "/");
1419 		INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1420 		if (pool == NULL) {
1421 			free(special);
1422 			bam_error(_("cant find pool for mount-point %s\n"),
1423 			    menu_root);
1424 			return (BAM_ERROR);
1425 		}
1426 		BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
1427 
1428 		zmntpt = mount_top_dataset(pool, &zmnted);
1429 		INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1430 		if (zmntpt == NULL) {
1431 			bam_error(_("cannot mount pool dataset for pool: %s\n"),
1432 			    pool);
1433 			free(special);
1434 			return (BAM_ERROR);
1435 		}
1436 		BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
1437 
1438 		(void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1439 		BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
1440 	}
1441 
1442 	elide_trailing_slash(menu_root, clean_menu_root,
1443 	    sizeof (clean_menu_root));
1444 
1445 	BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
1446 
1447 	(void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1448 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1449 
1450 	BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
1451 
1452 	/*
1453 	 * If listing the menu, display the menu location
1454 	 */
1455 	if (strcmp(subcmd, "list_entry") == 0)
1456 		bam_print(_("the location for the active GRUB menu is: %s\n"),
1457 		    menu_path);
1458 
1459 	if ((menu = menu_read(menu_path)) == NULL) {
1460 		bam_error(_("cannot find GRUB menu file: %s\n"), menu_path);
1461 		free(special);
1462 
1463 		return (BAM_ERROR);
1464 	}
1465 
1466 	/*
1467 	 * We already checked the following case in
1468 	 * check_subcmd_and_suboptions() above. Complete the
1469 	 * final step now.
1470 	 */
1471 	if (strcmp(subcmd, "set_option") == 0) {
1472 		assert(largc == 1 && largv[0] && largv[1] == NULL);
1473 		opt = largv[0];
1474 	} else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1475 	    (strcmp(subcmd, "list_setting") != 0)) {
1476 		assert(largc == 0 && largv == NULL);
1477 	}
1478 
1479 	ret = get_boot_cap(bam_root);
1480 	if (ret != BAM_SUCCESS) {
1481 		BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1482 		goto out;
1483 	}
1484 
1485 	/*
1486 	 * Once the sub-cmd handler has run
1487 	 * only the line field is guaranteed to have valid values
1488 	 */
1489 	if (strcmp(subcmd, "update_entry") == 0) {
1490 		ret = f(menu, menu_root, osdev);
1491 	} else if (strcmp(subcmd, "upgrade") == 0) {
1492 		ret = f(menu, bam_root, menu_root);
1493 	} else if (strcmp(subcmd, "list_entry") == 0) {
1494 		ret = f(menu, menu_path, opt);
1495 	} else if (strcmp(subcmd, "list_setting") == 0) {
1496 		ret = f(menu, ((largc > 0) ? largv[0] : ""),
1497 		    ((largc > 1) ? largv[1] : ""));
1498 	} else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1499 		if (is_sparc()) {
1500 			bam_error(_("%s operation unsupported on SPARC "
1501 			    "machines\n"), subcmd);
1502 			ret = BAM_ERROR;
1503 		} else {
1504 			ret = f(menu, bam_root, NULL);
1505 		}
1506 	} else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1507 		if (is_sparc()) {
1508 			bam_error(_("%s operation unsupported on SPARC "
1509 			    "machines\n"), subcmd);
1510 			ret = BAM_ERROR;
1511 		} else {
1512 			char *extra_args = NULL;
1513 
1514 			/*
1515 			 * Compress all arguments passed in the largv[] array
1516 			 * into one string that can then be appended to the
1517 			 * end of the kernel$ string the routine to enable the
1518 			 * hypervisor will build.
1519 			 *
1520 			 * This allows the caller to supply arbitrary unparsed
1521 			 * arguments, such as dom0 memory settings or APIC
1522 			 * options.
1523 			 *
1524 			 * This concatenation will be done without ANY syntax
1525 			 * checking whatsoever, so it's the responsibility of
1526 			 * the caller to make sure the arguments are valid and
1527 			 * do not duplicate arguments the conversion routines
1528 			 * may create.
1529 			 */
1530 			if (largc > 0) {
1531 				int extra_len, i;
1532 
1533 				for (extra_len = 0, i = 0; i < largc; i++)
1534 					extra_len += strlen(largv[i]);
1535 
1536 				/*
1537 				 * Allocate space for argument strings,
1538 				 * intervening spaces and terminating NULL.
1539 				 */
1540 				extra_args = alloca(extra_len + largc);
1541 
1542 				(void) strcpy(extra_args, largv[0]);
1543 
1544 				for (i = 1; i < largc; i++) {
1545 					(void) strcat(extra_args, " ");
1546 					(void) strcat(extra_args, largv[i]);
1547 				}
1548 			}
1549 
1550 			ret = f(menu, bam_root, extra_args);
1551 		}
1552 	} else
1553 		ret = f(menu, NULL, opt);
1554 
1555 	if (ret == BAM_WRITE) {
1556 		BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
1557 		    fcn, clean_menu_root));
1558 		ret = menu_write(clean_menu_root, menu);
1559 	}
1560 
1561 out:
1562 	INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1563 	assert((is_zfs(menu_root)) ^ (pool == NULL));
1564 	if (pool) {
1565 		(void) umount_top_dataset(pool, zmnted, zmntpt);
1566 		free(special);
1567 	}
1568 	menu_free(menu);
1569 	return (ret);
1570 }
1571 
1572 
1573 static error_t
1574 bam_archive(
1575 	char *subcmd,
1576 	char *opt)
1577 {
1578 	error_t			ret;
1579 	error_t			(*f)(char *root, char *opt);
1580 	const char		*fcn = "bam_archive()";
1581 
1582 	/*
1583 	 * Add trailing / for archive subcommands
1584 	 */
1585 	if (rootbuf[strlen(rootbuf) - 1] != '/')
1586 		(void) strcat(rootbuf, "/");
1587 	bam_rootlen = strlen(rootbuf);
1588 
1589 	/*
1590 	 * Check arguments
1591 	 */
1592 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1593 	if (ret != BAM_SUCCESS) {
1594 		return (BAM_ERROR);
1595 	}
1596 
1597 	ret = get_boot_cap(rootbuf);
1598 	if (ret != BAM_SUCCESS) {
1599 		BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1600 		return (ret);
1601 	}
1602 
1603 	/*
1604 	 * Check archive not supported with update_all
1605 	 * since it is awkward to display out-of-sync
1606 	 * information for each BE.
1607 	 */
1608 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
1609 		bam_error(_("the check option is not supported with "
1610 		    "subcmd: %s\n"), subcmd);
1611 		return (BAM_ERROR);
1612 	}
1613 
1614 	if (strcmp(subcmd, "update_all") == 0)
1615 		bam_update_all = 1;
1616 
1617 #if !defined(_OBP)
1618 	ucode_install(bam_root);
1619 #endif
1620 
1621 	ret = f(bam_root, opt);
1622 
1623 	bam_update_all = 0;
1624 
1625 	return (ret);
1626 }
1627 
1628 /*PRINTFLIKE1*/
1629 void
1630 bam_error(char *format, ...)
1631 {
1632 	va_list ap;
1633 
1634 	va_start(ap, format);
1635 	(void) fprintf(stderr, "%s: ", prog);
1636 	(void) vfprintf(stderr, format, ap);
1637 	va_end(ap);
1638 }
1639 
1640 /*PRINTFLIKE1*/
1641 void
1642 bam_derror(char *format, ...)
1643 {
1644 	va_list ap;
1645 
1646 	assert(bam_debug);
1647 
1648 	va_start(ap, format);
1649 	(void) fprintf(stderr, "DEBUG: ");
1650 	(void) vfprintf(stderr, format, ap);
1651 	va_end(ap);
1652 }
1653 
1654 /*PRINTFLIKE1*/
1655 void
1656 bam_print(char *format, ...)
1657 {
1658 	va_list ap;
1659 
1660 	va_start(ap, format);
1661 	(void) vfprintf(stdout, format, ap);
1662 	va_end(ap);
1663 }
1664 
1665 /*PRINTFLIKE1*/
1666 void
1667 bam_print_stderr(char *format, ...)
1668 {
1669 	va_list ap;
1670 
1671 	va_start(ap, format);
1672 	(void) vfprintf(stderr, format, ap);
1673 	va_end(ap);
1674 }
1675 
1676 void
1677 bam_exit(int excode)
1678 {
1679 	restore_env();
1680 	bam_unlock();
1681 	exit(excode);
1682 }
1683 
1684 static void
1685 bam_lock(void)
1686 {
1687 	struct flock lock;
1688 	pid_t pid;
1689 
1690 	if (bam_skip_lock)
1691 		return;
1692 
1693 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1694 	if (bam_lock_fd < 0) {
1695 		/*
1696 		 * We may be invoked early in boot for archive verification.
1697 		 * In this case, root is readonly and /var/run may not exist.
1698 		 * Proceed without the lock
1699 		 */
1700 		if (errno == EROFS || errno == ENOENT) {
1701 			bam_root_readonly = 1;
1702 			return;
1703 		}
1704 
1705 		bam_error(_("failed to open file: %s: %s\n"),
1706 		    BAM_LOCK_FILE, strerror(errno));
1707 		bam_exit(1);
1708 	}
1709 
1710 	lock.l_type = F_WRLCK;
1711 	lock.l_whence = SEEK_SET;
1712 	lock.l_start = 0;
1713 	lock.l_len = 0;
1714 
1715 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1716 		if (errno != EACCES && errno != EAGAIN) {
1717 			bam_error(_("failed to lock file: %s: %s\n"),
1718 			    BAM_LOCK_FILE, strerror(errno));
1719 			(void) close(bam_lock_fd);
1720 			bam_lock_fd = -1;
1721 			bam_exit(1);
1722 		}
1723 		pid = 0;
1724 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1725 		bam_print(
1726 		    _("another instance of bootadm (pid %lu) is running\n"),
1727 		    pid);
1728 
1729 		lock.l_type = F_WRLCK;
1730 		lock.l_whence = SEEK_SET;
1731 		lock.l_start = 0;
1732 		lock.l_len = 0;
1733 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1734 			bam_error(_("failed to lock file: %s: %s\n"),
1735 			    BAM_LOCK_FILE, strerror(errno));
1736 			(void) close(bam_lock_fd);
1737 			bam_lock_fd = -1;
1738 			bam_exit(1);
1739 		}
1740 	}
1741 
1742 	/* We own the lock now */
1743 	pid = getpid();
1744 	(void) write(bam_lock_fd, &pid, sizeof (pid));
1745 }
1746 
1747 static void
1748 bam_unlock(void)
1749 {
1750 	struct flock unlock;
1751 
1752 	if (bam_skip_lock)
1753 		return;
1754 
1755 	/*
1756 	 * NOP if we don't hold the lock
1757 	 */
1758 	if (bam_lock_fd < 0) {
1759 		return;
1760 	}
1761 
1762 	unlock.l_type = F_UNLCK;
1763 	unlock.l_whence = SEEK_SET;
1764 	unlock.l_start = 0;
1765 	unlock.l_len = 0;
1766 
1767 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1768 		bam_error(_("failed to unlock file: %s: %s\n"),
1769 		    BAM_LOCK_FILE, strerror(errno));
1770 	}
1771 
1772 	if (close(bam_lock_fd) == -1) {
1773 		bam_error(_("failed to close file: %s: %s\n"),
1774 		    BAM_LOCK_FILE, strerror(errno));
1775 	}
1776 	bam_lock_fd = -1;
1777 }
1778 
1779 static error_t
1780 list_archive(char *root, char *opt)
1781 {
1782 	filelist_t flist;
1783 	filelist_t *flistp = &flist;
1784 	line_t *lp;
1785 
1786 	assert(root);
1787 	assert(opt == NULL);
1788 
1789 	flistp->head = flistp->tail = NULL;
1790 	if (read_list(root, flistp) != BAM_SUCCESS) {
1791 		return (BAM_ERROR);
1792 	}
1793 	assert(flistp->head && flistp->tail);
1794 
1795 	for (lp = flistp->head; lp; lp = lp->next) {
1796 		bam_print(_("%s\n"), lp->line);
1797 	}
1798 
1799 	filelist_free(flistp);
1800 
1801 	return (BAM_SUCCESS);
1802 }
1803 
1804 /*
1805  * This routine writes a list of lines to a file.
1806  * The list is *not* freed
1807  */
1808 static error_t
1809 list2file(char *root, char *tmp, char *final, line_t *start)
1810 {
1811 	char		tmpfile[PATH_MAX];
1812 	char		path[PATH_MAX];
1813 	FILE		*fp;
1814 	int		ret;
1815 	struct stat	sb;
1816 	mode_t		mode;
1817 	uid_t		root_uid;
1818 	gid_t		sys_gid;
1819 	struct passwd	*pw;
1820 	struct group	*gp;
1821 	const char	*fcn = "list2file()";
1822 
1823 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
1824 
1825 	if (start == NULL) {
1826 		/* Empty GRUB menu */
1827 		if (stat(path, &sb) != -1) {
1828 			bam_print(_("file is empty, deleting file: %s\n"),
1829 			    path);
1830 			if (unlink(path) != 0) {
1831 				bam_error(_("failed to unlink file: %s: %s\n"),
1832 				    path, strerror(errno));
1833 				return (BAM_ERROR);
1834 			} else {
1835 				return (BAM_SUCCESS);
1836 			}
1837 		}
1838 		return (BAM_SUCCESS);
1839 	}
1840 
1841 	/*
1842 	 * Preserve attributes of existing file if possible,
1843 	 * otherwise ask the system for uid/gid of root/sys.
1844 	 * If all fails, fall back on hard-coded defaults.
1845 	 */
1846 	if (stat(path, &sb) != -1) {
1847 		mode = sb.st_mode;
1848 		root_uid = sb.st_uid;
1849 		sys_gid = sb.st_gid;
1850 	} else {
1851 		mode = DEFAULT_DEV_MODE;
1852 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1853 			root_uid = pw->pw_uid;
1854 		} else {
1855 			bam_error(_("getpwnam: uid for %s failed, "
1856 			    "defaulting to %d\n"),
1857 			    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1858 			root_uid = (uid_t)DEFAULT_DEV_UID;
1859 		}
1860 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1861 			sys_gid = gp->gr_gid;
1862 		} else {
1863 			bam_error(_("getgrnam: gid for %s failed, "
1864 			    "defaulting to %d\n"),
1865 			    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1866 			sys_gid = (gid_t)DEFAULT_DEV_GID;
1867 		}
1868 	}
1869 
1870 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1871 
1872 	/* Truncate tmpfile first */
1873 	fp = fopen(tmpfile, "w");
1874 	if (fp == NULL) {
1875 		bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1876 		    strerror(errno));
1877 		return (BAM_ERROR);
1878 	}
1879 	ret = fclose(fp);
1880 	INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1881 	if (ret == EOF) {
1882 		bam_error(_("failed to close file: %s: %s\n"),
1883 		    tmpfile, strerror(errno));
1884 		return (BAM_ERROR);
1885 	}
1886 
1887 	/* Now open it in append mode */
1888 	fp = fopen(tmpfile, "a");
1889 	if (fp == NULL) {
1890 		bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1891 		    strerror(errno));
1892 		return (BAM_ERROR);
1893 	}
1894 
1895 	for (; start; start = start->next) {
1896 		ret = s_fputs(start->line, fp);
1897 		INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1898 		if (ret == EOF) {
1899 			bam_error(_("write to file failed: %s: %s\n"),
1900 			    tmpfile, strerror(errno));
1901 			(void) fclose(fp);
1902 			return (BAM_ERROR);
1903 		}
1904 	}
1905 
1906 	ret = fclose(fp);
1907 	INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1908 	if (ret == EOF) {
1909 		bam_error(_("failed to close file: %s: %s\n"),
1910 		    tmpfile, strerror(errno));
1911 		return (BAM_ERROR);
1912 	}
1913 
1914 	/*
1915 	 * Set up desired attributes.  Ignore failures on filesystems
1916 	 * not supporting these operations - pcfs reports unsupported
1917 	 * operations as EINVAL.
1918 	 */
1919 	ret = chmod(tmpfile, mode);
1920 	if (ret == -1 &&
1921 	    errno != EINVAL && errno != ENOTSUP) {
1922 		bam_error(_("chmod operation on %s failed - %s\n"),
1923 		    tmpfile, strerror(errno));
1924 		return (BAM_ERROR);
1925 	}
1926 
1927 	ret = chown(tmpfile, root_uid, sys_gid);
1928 	if (ret == -1 &&
1929 	    errno != EINVAL && errno != ENOTSUP) {
1930 		bam_error(_("chgrp operation on %s failed - %s\n"),
1931 		    tmpfile, strerror(errno));
1932 		return (BAM_ERROR);
1933 	}
1934 
1935 	/*
1936 	 * Do an atomic rename
1937 	 */
1938 	ret = rename(tmpfile, path);
1939 	INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1940 	if (ret != 0) {
1941 		bam_error(_("rename to file failed: %s: %s\n"), path,
1942 		    strerror(errno));
1943 		return (BAM_ERROR);
1944 	}
1945 
1946 	BAM_DPRINTF(("%s: wrote file successfully: %s\n", fcn, path));
1947 	return (BAM_SUCCESS);
1948 }
1949 
1950 /*
1951  * Checks if the path specified (without the file name at the end) exists
1952  * and creates it if not. If the path exists and is not a directory, an attempt
1953  * to unlink is made.
1954  */
1955 static int
1956 setup_path(char *path)
1957 {
1958 	char		*p;
1959 	int		ret;
1960 	struct stat	sb;
1961 
1962 	p = strrchr(path, '/');
1963 	if (p != NULL) {
1964 		*p = '\0';
1965 		if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1966 			/* best effort attempt, mkdirp will catch the error */
1967 			(void) unlink(path);
1968 			if (bam_verbose)
1969 				bam_print(_("need to create directory "
1970 				    "path for %s\n"), path);
1971 			ret = mkdirp(path, DIR_PERMS);
1972 			if (ret == -1) {
1973 				bam_error(_("mkdir of %s failed: %s\n"),
1974 				    path, strerror(errno));
1975 				*p = '/';
1976 				return (BAM_ERROR);
1977 			}
1978 		}
1979 		*p = '/';
1980 		return (BAM_SUCCESS);
1981 	}
1982 	return (BAM_SUCCESS);
1983 }
1984 
1985 typedef union {
1986 	gzFile	gzfile;
1987 	int	fdfile;
1988 } outfile;
1989 
1990 typedef struct {
1991 	char		path[PATH_MAX];
1992 	outfile		out;
1993 } cachefile;
1994 
1995 static int
1996 setup_file(char *base, const char *path, cachefile *cf)
1997 {
1998 	int	ret;
1999 	char	*strip;
2000 
2001 	/* init gzfile or fdfile in case we fail before opening */
2002 	if (bam_direct == BAM_DIRECT_DBOOT)
2003 		cf->out.gzfile = NULL;
2004 	else
2005 		cf->out.fdfile = -1;
2006 
2007 	/* strip the trailing altroot path */
2008 	strip = (char *)path + strlen(rootbuf);
2009 
2010 	ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
2011 	if (ret >= sizeof (cf->path)) {
2012 		bam_error(_("unable to create path on mountpoint %s, "
2013 		    "path too long\n"), rootbuf);
2014 		return (BAM_ERROR);
2015 	}
2016 
2017 	/* Check if path is present in the archive cache directory */
2018 	if (setup_path(cf->path) == BAM_ERROR)
2019 		return (BAM_ERROR);
2020 
2021 	if (bam_direct == BAM_DIRECT_DBOOT) {
2022 		if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
2023 			bam_error(_("failed to open file: %s: %s\n"),
2024 			    cf->path, strerror(errno));
2025 			return (BAM_ERROR);
2026 		}
2027 		(void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
2028 		    Z_DEFAULT_STRATEGY);
2029 	} else {
2030 		if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
2031 		    == -1) {
2032 			bam_error(_("failed to open file: %s: %s\n"),
2033 			    cf->path, strerror(errno));
2034 			return (BAM_ERROR);
2035 		}
2036 	}
2037 
2038 	return (BAM_SUCCESS);
2039 }
2040 
2041 static int
2042 cache_write(cachefile cf, char *buf, int size)
2043 {
2044 	int	err;
2045 
2046 	if (bam_direct == BAM_DIRECT_DBOOT) {
2047 		if (gzwrite(cf.out.gzfile, buf, size) < 1) {
2048 			bam_error(_("failed to write to %s\n"),
2049 			    gzerror(cf.out.gzfile, &err));
2050 			if (err == Z_ERRNO && bam_verbose) {
2051 				bam_error(_("write to file failed: %s: %s\n"),
2052 				    cf.path, strerror(errno));
2053 			}
2054 			return (BAM_ERROR);
2055 		}
2056 	} else {
2057 		if (write(cf.out.fdfile, buf, size) < 1) {
2058 			bam_error(_("write to file failed: %s: %s\n"),
2059 			    cf.path, strerror(errno));
2060 			return (BAM_ERROR);
2061 		}
2062 	}
2063 	return (BAM_SUCCESS);
2064 }
2065 
2066 static int
2067 cache_close(cachefile cf)
2068 {
2069 	int	ret;
2070 
2071 	if (bam_direct == BAM_DIRECT_DBOOT) {
2072 		if (cf.out.gzfile) {
2073 			ret = gzclose(cf.out.gzfile);
2074 			if (ret != Z_OK) {
2075 				bam_error(_("failed to close file: %s: %s\n"),
2076 				    cf.path, strerror(errno));
2077 				return (BAM_ERROR);
2078 			}
2079 		}
2080 	} else {
2081 		if (cf.out.fdfile != -1) {
2082 			ret = close(cf.out.fdfile);
2083 			if (ret != 0) {
2084 				bam_error(_("failed to close file: %s: %s\n"),
2085 				    cf.path, strerror(errno));
2086 				return (BAM_ERROR);
2087 			}
2088 		}
2089 	}
2090 
2091 	return (BAM_SUCCESS);
2092 }
2093 
2094 static int
2095 dircache_updatefile(const char *path)
2096 {
2097 	int		ret, exitcode;
2098 	char		buf[4096 * 4];
2099 	FILE		*infile;
2100 	cachefile	outfile, outupdt;
2101 
2102 	if (bam_nowrite()) {
2103 		set_dir_flag(NEED_UPDATE);
2104 		return (BAM_SUCCESS);
2105 	}
2106 
2107 	if (!has_cachedir())
2108 		return (BAM_SUCCESS);
2109 
2110 	if ((infile = fopen(path, "rb")) == NULL) {
2111 		bam_error(_("failed to open file: %s: %s\n"), path,
2112 		    strerror(errno));
2113 		return (BAM_ERROR);
2114 	}
2115 
2116 	ret = setup_file(get_cachedir(), path, &outfile);
2117 	if (ret == BAM_ERROR) {
2118 		exitcode = BAM_ERROR;
2119 		goto out;
2120 	}
2121 	if (!is_dir_flag_on(NO_EXTEND)) {
2122 		ret = setup_file(get_updatedir(), path, &outupdt);
2123 		if (ret == BAM_ERROR)
2124 			set_dir_flag(NO_EXTEND);
2125 	}
2126 
2127 	while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2128 		if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2129 			exitcode = BAM_ERROR;
2130 			goto out;
2131 		}
2132 		if (!is_dir_flag_on(NO_EXTEND))
2133 			if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2134 				set_dir_flag(NO_EXTEND);
2135 	}
2136 
2137 	set_dir_flag(NEED_UPDATE);
2138 	get_count()++;
2139 	if (get_count() > COUNT_MAX)
2140 		set_dir_flag(NO_EXTEND);
2141 	exitcode = BAM_SUCCESS;
2142 out:
2143 	(void) fclose(infile);
2144 	if (cache_close(outfile) == BAM_ERROR)
2145 		exitcode = BAM_ERROR;
2146 	if (!is_dir_flag_on(NO_EXTEND) &&
2147 	    cache_close(outupdt) == BAM_ERROR)
2148 		exitcode = BAM_ERROR;
2149 	if (exitcode == BAM_ERROR)
2150 		set_flag(UPDATE_ERROR);
2151 	return (exitcode);
2152 }
2153 
2154 static int
2155 dircache_updatedir(const char *path, int updt)
2156 {
2157 	int		ret;
2158 	char		dpath[PATH_MAX];
2159 	char		*strip;
2160 	struct stat	sb;
2161 
2162 	strip = (char *)path + strlen(rootbuf);
2163 
2164 	ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2165 	    get_updatedir() : get_cachedir(), strip);
2166 
2167 	if (ret >= sizeof (dpath)) {
2168 		bam_error(_("unable to create path on mountpoint %s, "
2169 		    "path too long\n"), rootbuf);
2170 		set_flag(UPDATE_ERROR);
2171 		return (BAM_ERROR);
2172 	}
2173 
2174 	if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2175 		return (BAM_SUCCESS);
2176 
2177 	if (updt) {
2178 		if (!is_dir_flag_on(NO_EXTEND))
2179 			if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2180 				set_dir_flag(NO_EXTEND);
2181 	} else {
2182 		if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2183 			set_flag(UPDATE_ERROR);
2184 			return (BAM_ERROR);
2185 		}
2186 	}
2187 
2188 	set_dir_flag(NEED_UPDATE);
2189 	return (BAM_SUCCESS);
2190 }
2191 
2192 #define	DO_CACHE_DIR	0
2193 #define	DO_UPDATE_DIR	1
2194 
2195 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2196 typedef		Elf64_Ehdr	_elfhdr;
2197 #else
2198 typedef		Elf32_Ehdr	_elfhdr;
2199 #endif
2200 
2201 /*
2202  * This routine updates the contents of the cache directory
2203  */
2204 static int
2205 update_dircache(const char *path, int flags)
2206 {
2207 	int rc = BAM_SUCCESS;
2208 
2209 	switch (flags) {
2210 	case FTW_F:
2211 		{
2212 		int	fd;
2213 		_elfhdr	elf;
2214 
2215 		if ((fd = open(path, O_RDONLY)) < 0) {
2216 			bam_error(_("failed to open file: %s: %s\n"),
2217 			    path, strerror(errno));
2218 			set_flag(UPDATE_ERROR);
2219 			rc = BAM_ERROR;
2220 			break;
2221 		}
2222 
2223 		/*
2224 		 * libelf and gelf would be a cleaner and easier way to handle
2225 		 * this, but libelf fails compilation if _ILP32 is defined &&
2226 		 * _FILE_OFFSET_BITS is != 32 ...
2227 		 */
2228 		if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2229 			bam_error(_("read failed for file: %s: %s\n"),
2230 			    path, strerror(errno));
2231 			set_flag(UPDATE_ERROR);
2232 			(void) close(fd);
2233 			rc = BAM_ERROR;
2234 			break;
2235 		}
2236 		(void) close(fd);
2237 
2238 		if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2239 			/* Not an ELF file, include in archive */
2240 			rc = dircache_updatefile(path);
2241 		} else {
2242 			/* Include 64-bit ELF files only */
2243 			switch (elf.e_ident[EI_CLASS]) {
2244 			case ELFCLASS32:
2245 				bam_print(_("WARNING: ELF file %s is 32-bit "
2246 				    "and will be excluded\n"), path);
2247 				break;
2248 			case ELFCLASS64:
2249 				rc = dircache_updatefile(path);
2250 				break;
2251 			default:
2252 				bam_print(_("WARNING: ELF file %s is neither "
2253 				    "32-bit nor 64-bit\n"), path);
2254 				break;
2255 			}
2256 		}
2257 		break;
2258 		}
2259 	case FTW_D:
2260 		if (strstr(path, "/amd64") != NULL) {
2261 			if (has_cachedir()) {
2262 				rc = dircache_updatedir(path, DO_UPDATE_DIR);
2263 				if (rc == BAM_SUCCESS)
2264 					rc = dircache_updatedir(path,
2265 					    DO_CACHE_DIR);
2266 			}
2267 		}
2268 		break;
2269 	default:
2270 		rc = BAM_ERROR;
2271 		break;
2272 	}
2273 
2274 	return (rc);
2275 }
2276 
2277 /*ARGSUSED*/
2278 static int
2279 cmpstat(
2280 	const char *file,
2281 	const struct stat *st,
2282 	int flags,
2283 	struct FTW *ftw)
2284 {
2285 	uint_t		sz;
2286 	uint64_t	*value;
2287 	uint64_t	filestat[2];
2288 	int		error, ret, status;
2289 
2290 	struct safefile *safefilep;
2291 	FILE		*fp;
2292 	struct stat	sb;
2293 	regex_t re;
2294 
2295 	/*
2296 	 * On SPARC we create/update links too.
2297 	 */
2298 	if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2299 	    !is_flag_on(IS_SPARC_TARGET)))
2300 		return (0);
2301 
2302 	/*
2303 	 * Ignore broken links
2304 	 */
2305 	if (flags == FTW_SL && stat(file, &sb) < 0)
2306 		return (0);
2307 
2308 	/*
2309 	 * new_nvlp may be NULL if there were errors earlier
2310 	 * but this is not fatal to update determination.
2311 	 */
2312 	if (walk_arg.new_nvlp) {
2313 		filestat[0] = st->st_size;
2314 		filestat[1] = st->st_mtime;
2315 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2316 		    file + bam_rootlen, filestat, 2);
2317 		if (error)
2318 			bam_error(_("failed to update stat data for: %s: %s\n"),
2319 			    file, strerror(error));
2320 	}
2321 
2322 	/*
2323 	 * If we are invoked as part of system/filesystem/boot-archive, then
2324 	 * there are a number of things we should not worry about
2325 	 */
2326 	if (bam_smf_check) {
2327 		/* ignore amd64 modules unless we are booted amd64. */
2328 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
2329 			return (0);
2330 
2331 		/* read in list of safe files */
2332 		if (safefiles == NULL) {
2333 			fp = fopen("/boot/solaris/filelist.safe", "r");
2334 			if (fp != NULL) {
2335 				safefiles = s_calloc(1,
2336 				    sizeof (struct safefile));
2337 				safefilep = safefiles;
2338 				safefilep->name = s_calloc(1, MAXPATHLEN +
2339 				    MAXNAMELEN);
2340 				safefilep->next = NULL;
2341 				while (s_fgets(safefilep->name, MAXPATHLEN +
2342 				    MAXNAMELEN, fp) != NULL) {
2343 					safefilep->next = s_calloc(1,
2344 					    sizeof (struct safefile));
2345 					safefilep = safefilep->next;
2346 					safefilep->name = s_calloc(1,
2347 					    MAXPATHLEN + MAXNAMELEN);
2348 					safefilep->next = NULL;
2349 				}
2350 				(void) fclose(fp);
2351 			}
2352 		}
2353 	}
2354 
2355 	/*
2356 	 * On SPARC we create a -path-list file for mkisofs
2357 	 */
2358 	if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2359 		if (flags != FTW_D) {
2360 			char	*strip;
2361 
2362 			strip = (char *)file + strlen(rootbuf);
2363 			(void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2364 			    file);
2365 		}
2366 	}
2367 
2368 	/*
2369 	 * We are transitioning from the old model to the dircache or the cache
2370 	 * directory was removed: create the entry without further checkings.
2371 	 */
2372 	if (is_flag_on(NEED_CACHE_DIR)) {
2373 		if (bam_verbose)
2374 			bam_print(_("    new     %s\n"), file);
2375 
2376 		if (is_flag_on(IS_SPARC_TARGET)) {
2377 			set_dir_flag(NEED_UPDATE);
2378 			return (0);
2379 		}
2380 
2381 		ret = update_dircache(file, flags);
2382 		if (ret == BAM_ERROR) {
2383 			bam_error(_("directory cache update failed for %s\n"),
2384 			    file);
2385 			return (-1);
2386 		}
2387 
2388 		return (0);
2389 	}
2390 
2391 	/*
2392 	 * We need an update if file doesn't exist in old archive
2393 	 */
2394 	if (walk_arg.old_nvlp == NULL ||
2395 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2396 	    file + bam_rootlen, &value, &sz) != 0) {
2397 		if (bam_smf_check)	/* ignore new during smf check */
2398 			return (0);
2399 
2400 		if (is_flag_on(IS_SPARC_TARGET)) {
2401 			set_dir_flag(NEED_UPDATE);
2402 		} else {
2403 			ret = update_dircache(file, flags);
2404 			if (ret == BAM_ERROR) {
2405 				bam_error(_("directory cache update "
2406 				    "failed for %s\n"), file);
2407 				return (-1);
2408 			}
2409 		}
2410 
2411 		if (bam_verbose)
2412 			bam_print(_("    new     %s\n"), file);
2413 		return (0);
2414 	}
2415 
2416 	/*
2417 	 * If we got there, the file is already listed as to be included in the
2418 	 * iso image. We just need to know if we are going to rebuild it or not
2419 	 */
2420 	if (is_flag_on(IS_SPARC_TARGET) &&
2421 	    is_dir_flag_on(NEED_UPDATE) && !bam_nowrite())
2422 		return (0);
2423 	/*
2424 	 * File exists in old archive. Check if file has changed
2425 	 */
2426 	assert(sz == 2);
2427 	bcopy(value, filestat, sizeof (filestat));
2428 
2429 	if (flags != FTW_D && (filestat[0] != st->st_size ||
2430 	    filestat[1] != st->st_mtime)) {
2431 		if (bam_smf_check) {
2432 			safefilep = safefiles;
2433 			while (safefilep != NULL &&
2434 			    safefilep->name[0] != '\0') {
2435 				if (regcomp(&re, safefilep->name,
2436 				    REG_EXTENDED|REG_NOSUB) == 0) {
2437 					status = regexec(&re,
2438 					    file + bam_rootlen, 0, NULL, 0);
2439 					regfree(&re);
2440 					if (status == 0) {
2441 						(void) creat(
2442 						    NEED_UPDATE_SAFE_FILE,
2443 						    0644);
2444 						return (0);
2445 					}
2446 				}
2447 				safefilep = safefilep->next;
2448 			}
2449 		}
2450 
2451 		if (is_flag_on(IS_SPARC_TARGET)) {
2452 			set_dir_flag(NEED_UPDATE);
2453 		} else {
2454 			ret = update_dircache(file, flags);
2455 			if (ret == BAM_ERROR) {
2456 				bam_error(_("directory cache update failed "
2457 				    "for %s\n"), file);
2458 				return (-1);
2459 			}
2460 		}
2461 
2462 		if (bam_verbose) {
2463 			if (bam_smf_check)
2464 				bam_print("    %s\n", file);
2465 			else
2466 				bam_print(_("    changed %s\n"), file);
2467 		}
2468 	}
2469 
2470 	return (0);
2471 }
2472 
2473 /*
2474  * Remove a directory path recursively
2475  */
2476 static int
2477 rmdir_r(char *path)
2478 {
2479 	struct dirent	*d = NULL;
2480 	DIR		*dir = NULL;
2481 	char		tpath[PATH_MAX];
2482 	struct stat	sb;
2483 
2484 	if ((dir = opendir(path)) == NULL)
2485 		return (-1);
2486 
2487 	while ((d = readdir(dir)) != NULL) {
2488 		if ((strcmp(d->d_name, ".") != 0) &&
2489 		    (strcmp(d->d_name, "..") != 0)) {
2490 			(void) snprintf(tpath, sizeof (tpath), "%s/%s",
2491 			    path, d->d_name);
2492 			if (stat(tpath, &sb) == 0) {
2493 				if (sb.st_mode & S_IFDIR)
2494 					(void) rmdir_r(tpath);
2495 				else
2496 					(void) remove(tpath);
2497 			}
2498 		}
2499 	}
2500 	return (remove(path));
2501 }
2502 
2503 /*
2504  * Check if cache directory exists and, if not, create it and update flags
2505  * accordingly. If the path exists, but it's not a directory, a best effort
2506  * attempt to remove and recreate it is made.
2507  * If the user requested a 'purge', always recreate the directory from scratch.
2508  */
2509 static int
2510 set_cache_dir(char *root)
2511 {
2512 	struct stat	sb;
2513 	int		ret = 0;
2514 
2515 	ret = build_path(get_cachedir(), sizeof (get_cachedir()),
2516 	    root, ARCHIVE_PREFIX, CACHEDIR_SUFFIX);
2517 
2518 	if (ret >= sizeof (get_cachedir())) {
2519 		bam_error(_("unable to create path on mountpoint %s, "
2520 		    "path too long\n"), rootbuf);
2521 		return (BAM_ERROR);
2522 	}
2523 
2524 	if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2525 		(void) rmdir_r(get_cachedir());
2526 
2527 	if (stat(get_cachedir(), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2528 		/* best effort unlink attempt, mkdir will catch errors */
2529 		(void) unlink(get_cachedir());
2530 
2531 		if (bam_verbose)
2532 			bam_print(_("archive cache directory not found: %s\n"),
2533 			    get_cachedir());
2534 		ret = mkdir(get_cachedir(), DIR_PERMS);
2535 		if (ret < 0) {
2536 			bam_error(_("mkdir of %s failed: %s\n"),
2537 			    get_cachedir(), strerror(errno));
2538 			get_cachedir()[0] = '\0';
2539 			return (ret);
2540 		}
2541 		set_flag(NEED_CACHE_DIR);
2542 		set_dir_flag(NO_EXTEND);
2543 	}
2544 
2545 	return (BAM_SUCCESS);
2546 }
2547 
2548 static int
2549 set_update_dir(char *root)
2550 {
2551 	struct stat	sb;
2552 	int		ret;
2553 
2554 	if (is_dir_flag_on(NO_EXTEND))
2555 		return (BAM_SUCCESS);
2556 
2557 	if (!bam_extend) {
2558 		set_dir_flag(NO_EXTEND);
2559 		return (BAM_SUCCESS);
2560 	}
2561 
2562 	ret = build_path(get_updatedir(), sizeof (get_updatedir()),
2563 	    root, ARCHIVE_PREFIX, UPDATEDIR_SUFFIX);
2564 
2565 	if (ret >= sizeof (get_updatedir())) {
2566 		bam_error(_("unable to create path on mountpoint %s, "
2567 		    "path too long\n"), rootbuf);
2568 		return (BAM_ERROR);
2569 	}
2570 
2571 	if (stat(get_updatedir(), &sb) == 0) {
2572 		if (S_ISDIR(sb.st_mode))
2573 			ret = rmdir_r(get_updatedir());
2574 		else
2575 			ret = unlink(get_updatedir());
2576 
2577 		if (ret != 0)
2578 			set_dir_flag(NO_EXTEND);
2579 	}
2580 
2581 	if (mkdir(get_updatedir(), DIR_PERMS) < 0)
2582 		set_dir_flag(NO_EXTEND);
2583 
2584 	return (BAM_SUCCESS);
2585 }
2586 
2587 static int
2588 is_valid_archive(char *root)
2589 {
2590 	char		archive_path[PATH_MAX];
2591 	char		timestamp_path[PATH_MAX];
2592 	struct stat	sb, timestamp;
2593 	int		ret;
2594 
2595 	ret = build_path(archive_path, sizeof (archive_path),
2596 	    root, ARCHIVE_PREFIX, ARCHIVE_SUFFIX);
2597 
2598 	if (ret >= sizeof (archive_path)) {
2599 		bam_error(_("unable to create path on mountpoint %s, "
2600 		    "path too long\n"), rootbuf);
2601 		return (BAM_ERROR);
2602 	}
2603 
2604 	if (stat(archive_path, &sb) != 0) {
2605 		if (bam_verbose && !bam_check)
2606 			bam_print(_("archive not found: %s\n"), archive_path);
2607 		set_dir_flag(NEED_UPDATE | NO_EXTEND);
2608 		return (BAM_SUCCESS);
2609 	}
2610 
2611 	/*
2612 	 * The timestamp file is used to prevent stale files in the archive
2613 	 * cache.
2614 	 * Stale files can happen if the system is booted back and forth across
2615 	 * the transition from bootadm-before-the-cache to
2616 	 * bootadm-after-the-cache, since older versions of bootadm don't know
2617 	 * about the existence of the archive cache.
2618 	 *
2619 	 * Since only bootadm-after-the-cache versions know about about this
2620 	 * file, we require that the boot archive be older than this file.
2621 	 */
2622 	ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2623 	    FILE_STAT_TIMESTAMP);
2624 
2625 	if (ret >= sizeof (timestamp_path)) {
2626 		bam_error(_("unable to create path on mountpoint %s, "
2627 		    "path too long\n"), rootbuf);
2628 		return (BAM_ERROR);
2629 	}
2630 
2631 	if (stat(timestamp_path, &timestamp) != 0 ||
2632 	    sb.st_mtime > timestamp.st_mtime) {
2633 		if (bam_verbose && !bam_check)
2634 			bam_print(
2635 			    _("archive cache is out of sync. Rebuilding.\n"));
2636 		/*
2637 		 * Don't generate a false positive for the boot-archive service
2638 		 * but trigger an update of the archive cache in
2639 		 * boot-archive-update.
2640 		 */
2641 		if (bam_smf_check) {
2642 			(void) creat(NEED_UPDATE_FILE, 0644);
2643 			return (BAM_SUCCESS);
2644 		}
2645 
2646 		set_flag(INVALIDATE_CACHE);
2647 		set_dir_flag(NEED_UPDATE | NO_EXTEND);
2648 		return (BAM_SUCCESS);
2649 	}
2650 
2651 	if (is_flag_on(IS_SPARC_TARGET))
2652 		return (BAM_SUCCESS);
2653 
2654 	if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2655 		if (bam_verbose && !bam_check)
2656 			bam_print(_("archive %s is bigger than %d bytes and "
2657 			    "will be rebuilt\n"), archive_path, BA_SIZE_MAX);
2658 		set_dir_flag(NO_EXTEND);
2659 	}
2660 
2661 	return (BAM_SUCCESS);
2662 }
2663 
2664 /*
2665  * Check flags and presence of required files and directories.
2666  * The force flag and/or absence of files should
2667  * trigger an update.
2668  * Suppress stdout output if check (-n) option is set
2669  * (as -n should only produce parseable output.)
2670  */
2671 static int
2672 check_flags_and_files(char *root)
2673 {
2674 
2675 	struct stat	sb;
2676 	int		ret;
2677 
2678 	/*
2679 	 * If archive is missing, create archive
2680 	 */
2681 	ret = is_valid_archive(root);
2682 	if (ret == BAM_ERROR)
2683 		return (BAM_ERROR);
2684 
2685 	if (bam_nowrite())
2686 		return (BAM_SUCCESS);
2687 
2688 	/*
2689 	 * check if cache directories exist on x86.
2690 	 * check (and always open) the cache file on SPARC.
2691 	 */
2692 	if (is_sparc()) {
2693 		ret = snprintf(get_cachedir(),
2694 		    sizeof (get_cachedir()), "%s%s%s/%s", root,
2695 		    ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2696 
2697 		if (ret >= sizeof (get_cachedir())) {
2698 			bam_error(_("unable to create path on mountpoint %s, "
2699 			    "path too long\n"), rootbuf);
2700 			return (BAM_ERROR);
2701 		}
2702 
2703 		if (stat(get_cachedir(), &sb) != 0) {
2704 			set_flag(NEED_CACHE_DIR);
2705 			set_dir_flag(NEED_UPDATE);
2706 		}
2707 
2708 		walk_arg.sparcfile = fopen(get_cachedir(), "w");
2709 		if (walk_arg.sparcfile == NULL) {
2710 			bam_error(_("failed to open file: %s: %s\n"),
2711 			    get_cachedir(), strerror(errno));
2712 			return (BAM_ERROR);
2713 		}
2714 
2715 		set_dir_present();
2716 	} else {
2717 		if (set_cache_dir(root) != 0)
2718 			return (BAM_ERROR);
2719 
2720 		set_dir_present();
2721 
2722 		if (set_update_dir(root) != 0)
2723 			return (BAM_ERROR);
2724 	}
2725 
2726 	/*
2727 	 * if force, create archive unconditionally
2728 	 */
2729 	if (bam_force) {
2730 		set_dir_flag(NEED_UPDATE);
2731 		if (bam_verbose)
2732 			bam_print(_("forced update of archive requested\n"));
2733 		return (BAM_SUCCESS);
2734 	}
2735 
2736 	return (BAM_SUCCESS);
2737 }
2738 
2739 static error_t
2740 read_one_list(char *root, filelist_t  *flistp, char *filelist)
2741 {
2742 	char		path[PATH_MAX];
2743 	FILE		*fp;
2744 	char		buf[BAM_MAXLINE];
2745 	const char	*fcn = "read_one_list()";
2746 
2747 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2748 
2749 	fp = fopen(path, "r");
2750 	if (fp == NULL) {
2751 		BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2752 		    fcn, path, strerror(errno)));
2753 		return (BAM_ERROR);
2754 	}
2755 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2756 		/* skip blank lines */
2757 		if (strspn(buf, " \t") == strlen(buf))
2758 			continue;
2759 		append_to_flist(flistp, buf);
2760 	}
2761 	if (fclose(fp) != 0) {
2762 		bam_error(_("failed to close file: %s: %s\n"),
2763 		    path, strerror(errno));
2764 		return (BAM_ERROR);
2765 	}
2766 	return (BAM_SUCCESS);
2767 }
2768 
2769 static error_t
2770 read_list(char *root, filelist_t  *flistp)
2771 {
2772 	char		path[PATH_MAX];
2773 	char		cmd[PATH_MAX];
2774 	struct stat	sb;
2775 	int		n, rval;
2776 	const char	*fcn = "read_list()";
2777 
2778 	flistp->head = flistp->tail = NULL;
2779 
2780 	/*
2781 	 * build and check path to extract_boot_filelist.ksh
2782 	 */
2783 	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2784 	if (n >= sizeof (path)) {
2785 		bam_error(_("archive filelist is empty\n"));
2786 		return (BAM_ERROR);
2787 	}
2788 
2789 	if (is_safe_exec(path) == BAM_ERROR)
2790 		return (BAM_ERROR);
2791 
2792 	/*
2793 	 * If extract_boot_filelist is present, exec it, otherwise read
2794 	 * the filelists directly, for compatibility with older images.
2795 	 */
2796 	if (stat(path, &sb) == 0) {
2797 		/*
2798 		 * build arguments to exec extract_boot_filelist.ksh
2799 		 */
2800 		char *rootarg, *platarg;
2801 		int platarglen = 1, rootarglen = 1;
2802 		if (strlen(root) > 1)
2803 			rootarglen += strlen(root) + strlen("-R ");
2804 		if (bam_alt_platform)
2805 			platarglen += strlen(bam_platform) + strlen("-p ");
2806 		platarg = s_calloc(1, platarglen);
2807 		rootarg = s_calloc(1, rootarglen);
2808 		*platarg = 0;
2809 		*rootarg = 0;
2810 
2811 		if (strlen(root) > 1) {
2812 			(void) snprintf(rootarg, rootarglen,
2813 			    "-R %s", root);
2814 		}
2815 		if (bam_alt_platform) {
2816 			(void) snprintf(platarg, platarglen,
2817 			    "-p %s", bam_platform);
2818 		}
2819 		n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2820 		    path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2821 		free(platarg);
2822 		free(rootarg);
2823 		if (n >= sizeof (cmd)) {
2824 			bam_error(_("archive filelist is empty\n"));
2825 			return (BAM_ERROR);
2826 		}
2827 		if (exec_cmd(cmd, flistp) != 0) {
2828 			BAM_DPRINTF(("%s: failed to open archive "
2829 			    "filelist: %s: %s\n", fcn, path, strerror(errno)));
2830 			return (BAM_ERROR);
2831 		}
2832 	} else {
2833 		/*
2834 		 * Read current lists of files - only the first is mandatory
2835 		 */
2836 		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2837 		if (rval != BAM_SUCCESS)
2838 			return (rval);
2839 		(void) read_one_list(root, flistp, ETC_FILE_LIST);
2840 	}
2841 
2842 	if (flistp->head == NULL) {
2843 		bam_error(_("archive filelist is empty\n"));
2844 		return (BAM_ERROR);
2845 	}
2846 
2847 	return (BAM_SUCCESS);
2848 }
2849 
2850 static void
2851 getoldstat(char *root)
2852 {
2853 	char		path[PATH_MAX];
2854 	int		fd, error;
2855 	struct stat	sb;
2856 	char		*ostat;
2857 
2858 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2859 	fd = open(path, O_RDONLY);
2860 	if (fd == -1) {
2861 		if (bam_verbose)
2862 			bam_print(_("failed to open file: %s: %s\n"),
2863 			    path, strerror(errno));
2864 		goto out_err;
2865 	}
2866 
2867 	if (fstat(fd, &sb) != 0) {
2868 		bam_error(_("stat of file failed: %s: %s\n"), path,
2869 		    strerror(errno));
2870 		goto out_err;
2871 	}
2872 
2873 	ostat = s_calloc(1, sb.st_size);
2874 
2875 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
2876 		bam_error(_("read failed for file: %s: %s\n"), path,
2877 		    strerror(errno));
2878 		free(ostat);
2879 		goto out_err;
2880 	}
2881 
2882 	(void) close(fd);
2883 	fd = -1;
2884 
2885 	walk_arg.old_nvlp = NULL;
2886 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2887 
2888 	free(ostat);
2889 
2890 	if (error) {
2891 		bam_error(_("failed to unpack stat data: %s: %s\n"),
2892 		    path, strerror(error));
2893 		walk_arg.old_nvlp = NULL;
2894 		goto out_err;
2895 	} else {
2896 		return;
2897 	}
2898 
2899 out_err:
2900 	if (fd != -1)
2901 		(void) close(fd);
2902 	set_dir_flag(NEED_UPDATE);
2903 }
2904 
2905 /* Best effort stale entry removal */
2906 static void
2907 delete_stale(char *file)
2908 {
2909 	char		path[PATH_MAX];
2910 	struct stat	sb;
2911 
2912 	(void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(), file);
2913 	if (!bam_check && stat(path, &sb) == 0) {
2914 		if (sb.st_mode & S_IFDIR)
2915 			(void) rmdir_r(path);
2916 		else
2917 			(void) unlink(path);
2918 
2919 		set_dir_flag(NEED_UPDATE | NO_EXTEND);
2920 	}
2921 }
2922 
2923 /*
2924  * Checks if a file in the current (old) archive has
2925  * been deleted from the root filesystem. This is needed for
2926  * software like Trusted Extensions (TX) that switch early
2927  * in boot based on presence/absence of a kernel module.
2928  */
2929 static void
2930 check4stale(char *root)
2931 {
2932 	nvpair_t	*nvp;
2933 	nvlist_t	*nvlp;
2934 	char		*file;
2935 	char		path[PATH_MAX];
2936 
2937 	/*
2938 	 * Skip stale file check during smf check
2939 	 */
2940 	if (bam_smf_check)
2941 		return;
2942 
2943 	/*
2944 	 * If we need to (re)create the cache, there's no need to check for
2945 	 * stale files
2946 	 */
2947 	if (is_flag_on(NEED_CACHE_DIR))
2948 		return;
2949 
2950 	/* Nothing to do if no old stats */
2951 	if ((nvlp = walk_arg.old_nvlp) == NULL)
2952 		return;
2953 
2954 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2955 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
2956 		file = nvpair_name(nvp);
2957 		if (file == NULL)
2958 			continue;
2959 		(void) snprintf(path, sizeof (path), "%s/%s",
2960 		    root, file);
2961 		if (access(path, F_OK) < 0) {
2962 			if (bam_verbose)
2963 				bam_print(_("    stale %s\n"), path);
2964 
2965 			if (is_flag_on(IS_SPARC_TARGET)) {
2966 				set_dir_flag(NEED_UPDATE);
2967 			} else {
2968 				if (has_cachedir())
2969 					delete_stale(file);
2970 			}
2971 		}
2972 	}
2973 }
2974 
2975 static void
2976 create_newstat(void)
2977 {
2978 	int error;
2979 
2980 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2981 	if (error) {
2982 		/*
2983 		 * Not fatal - we can still create archive
2984 		 */
2985 		walk_arg.new_nvlp = NULL;
2986 		bam_error(_("failed to create stat data: %s\n"),
2987 		    strerror(error));
2988 	}
2989 }
2990 
2991 static int
2992 walk_list(char *root, filelist_t *flistp)
2993 {
2994 	char path[PATH_MAX];
2995 	line_t *lp;
2996 
2997 	for (lp = flistp->head; lp; lp = lp->next) {
2998 		/*
2999 		 * Don't follow symlinks.  A symlink must refer to
3000 		 * a file that would appear in the archive through
3001 		 * a direct reference.  This matches the archive
3002 		 * construction behavior.
3003 		 */
3004 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
3005 		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
3006 			if (is_flag_on(UPDATE_ERROR))
3007 				return (BAM_ERROR);
3008 			/*
3009 			 * Some files may not exist.
3010 			 * For example: etc/rtc_config on a x86 diskless system
3011 			 * Emit verbose message only
3012 			 */
3013 			if (bam_verbose)
3014 				bam_print(_("cannot find: %s: %s\n"),
3015 				    path, strerror(errno));
3016 		}
3017 	}
3018 
3019 	return (BAM_SUCCESS);
3020 }
3021 
3022 /*
3023  * Update the timestamp file.
3024  */
3025 static void
3026 update_timestamp(char *root)
3027 {
3028 	char	timestamp_path[PATH_MAX];
3029 
3030 	/* this path length has already been checked in check_flags_and_files */
3031 	(void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
3032 	    FILE_STAT_TIMESTAMP);
3033 
3034 	/*
3035 	 * recreate the timestamp file. Since an outdated or absent timestamp
3036 	 * file translates in a complete rebuild of the archive cache, notify
3037 	 * the user of the performance issue.
3038 	 */
3039 	if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
3040 		bam_error(_("failed to open file: %s: %s\n"), timestamp_path,
3041 		    strerror(errno));
3042 		bam_error(_("failed to update the timestamp file, next"
3043 		    " archive update may experience reduced performance\n"));
3044 	}
3045 }
3046 
3047 
3048 static void
3049 savenew(char *root)
3050 {
3051 	char	path[PATH_MAX];
3052 	char	path2[PATH_MAX];
3053 	size_t	sz;
3054 	char	*nstat;
3055 	int	fd, wrote, error;
3056 
3057 	nstat = NULL;
3058 	sz = 0;
3059 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3060 	    NV_ENCODE_XDR, 0);
3061 	if (error) {
3062 		bam_error(_("failed to pack stat data: %s\n"),
3063 		    strerror(error));
3064 		return;
3065 	}
3066 
3067 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3068 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3069 	if (fd == -1) {
3070 		bam_error(_("failed to open file: %s: %s\n"), path,
3071 		    strerror(errno));
3072 		free(nstat);
3073 		return;
3074 	}
3075 	wrote = write(fd, nstat, sz);
3076 	if (wrote != sz) {
3077 		bam_error(_("write to file failed: %s: %s\n"), path,
3078 		    strerror(errno));
3079 		(void) close(fd);
3080 		free(nstat);
3081 		return;
3082 	}
3083 	(void) close(fd);
3084 	free(nstat);
3085 
3086 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3087 	if (rename(path, path2) != 0) {
3088 		bam_error(_("rename to file failed: %s: %s\n"), path2,
3089 		    strerror(errno));
3090 	}
3091 }
3092 
3093 #define	init_walk_args()	bzero(&walk_arg, sizeof (walk_arg))
3094 
3095 static void
3096 clear_walk_args(void)
3097 {
3098 	nvlist_free(walk_arg.old_nvlp);
3099 	nvlist_free(walk_arg.new_nvlp);
3100 	if (walk_arg.sparcfile)
3101 		(void) fclose(walk_arg.sparcfile);
3102 	walk_arg.old_nvlp = NULL;
3103 	walk_arg.new_nvlp = NULL;
3104 	walk_arg.sparcfile = NULL;
3105 }
3106 
3107 /*
3108  * Returns:
3109  *	0 - no update necessary
3110  *	1 - update required.
3111  *	BAM_ERROR (-1) - An error occurred
3112  *
3113  * Special handling for check (-n):
3114  * ================================
3115  * The check (-n) option produces parseable output.
3116  * To do this, we suppress all stdout messages unrelated
3117  * to out of sync files.
3118  * All stderr messages are still printed though.
3119  *
3120  */
3121 static int
3122 update_required(char *root)
3123 {
3124 	struct stat	sb;
3125 	char		path[PATH_MAX];
3126 	filelist_t	flist;
3127 	filelist_t	*flistp = &flist;
3128 	int		ret;
3129 
3130 	flistp->head = flistp->tail = NULL;
3131 
3132 	if (is_sparc())
3133 		set_flag(IS_SPARC_TARGET);
3134 
3135 	/*
3136 	 * Check if cache directories and archives are present
3137 	 */
3138 
3139 	ret = check_flags_and_files(root);
3140 	if (ret < 0)
3141 		return (BAM_ERROR);
3142 
3143 	/*
3144 	 * In certain deployment scenarios, filestat may not
3145 	 * exist. Do not stop the boot process, but trigger an update
3146 	 * of the archives (which will recreate filestat.ramdisk).
3147 	 */
3148 	if (bam_smf_check) {
3149 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3150 		if (stat(path, &sb) != 0) {
3151 			(void) creat(NEED_UPDATE_FILE, 0644);
3152 			return (0);
3153 		}
3154 	}
3155 
3156 	getoldstat(root);
3157 
3158 	/*
3159 	 * Check if the archive contains files that are no longer
3160 	 * present on the root filesystem.
3161 	 */
3162 	check4stale(root);
3163 
3164 	/*
3165 	 * read list of files
3166 	 */
3167 	if (read_list(root, flistp) != BAM_SUCCESS) {
3168 		clear_walk_args();
3169 		return (BAM_ERROR);
3170 	}
3171 
3172 	assert(flistp->head && flistp->tail);
3173 
3174 	/*
3175 	 * At this point either the update is required
3176 	 * or the decision is pending. In either case
3177 	 * we need to create new stat nvlist
3178 	 */
3179 	create_newstat();
3180 	/*
3181 	 * This walk does 2 things:
3182 	 *	- gets new stat data for every file
3183 	 *	- (optional) compare old and new stat data
3184 	 */
3185 	ret = walk_list(root, &flist);
3186 
3187 	/* done with the file list */
3188 	filelist_free(flistp);
3189 
3190 	/* something went wrong */
3191 
3192 	if (ret == BAM_ERROR) {
3193 		bam_error(_("Failed to gather cache files, archives "
3194 		    "generation aborted\n"));
3195 		return (BAM_ERROR);
3196 	}
3197 
3198 	if (walk_arg.new_nvlp == NULL) {
3199 		if (walk_arg.sparcfile != NULL)
3200 			(void) fclose(walk_arg.sparcfile);
3201 		bam_error(_("cannot create new stat data\n"));
3202 	}
3203 
3204 	/* If nothing was updated, discard newstat. */
3205 
3206 	if (!is_dir_flag_on(NEED_UPDATE)) {
3207 		clear_walk_args();
3208 		return (0);
3209 	}
3210 
3211 	if (walk_arg.sparcfile != NULL)
3212 		(void) fclose(walk_arg.sparcfile);
3213 
3214 	return (1);
3215 }
3216 
3217 static int
3218 flushfs(char *root)
3219 {
3220 	char	cmd[PATH_MAX + 30];
3221 
3222 	(void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3223 	    LOCKFS_PATH, root);
3224 
3225 	return (exec_cmd(cmd, NULL));
3226 }
3227 
3228 static int
3229 do_archive_copy(char *source, char *dest)
3230 {
3231 
3232 	sync();
3233 
3234 	/* the equivalent of mv archive-new-$pid boot_archive */
3235 	if (rename(source, dest) != 0) {
3236 		(void) unlink(source);
3237 		return (BAM_ERROR);
3238 	}
3239 
3240 	if (flushfs(bam_root) != 0)
3241 		sync();
3242 
3243 	return (BAM_SUCCESS);
3244 }
3245 
3246 static int
3247 check_cmdline(filelist_t flist)
3248 {
3249 	line_t	*lp;
3250 
3251 	for (lp = flist.head; lp; lp = lp->next) {
3252 		if (strstr(lp->line, "Error:") != NULL ||
3253 		    strstr(lp->line, "Inode number overflow") != NULL) {
3254 			(void) fprintf(stderr, "%s\n", lp->line);
3255 			return (BAM_ERROR);
3256 		}
3257 	}
3258 
3259 	return (BAM_SUCCESS);
3260 }
3261 
3262 static void
3263 dump_errormsg(filelist_t flist)
3264 {
3265 	line_t	*lp;
3266 
3267 	for (lp = flist.head; lp; lp = lp->next)
3268 		(void) fprintf(stderr, "%s\n", lp->line);
3269 }
3270 
3271 static int
3272 check_archive(char *dest)
3273 {
3274 	struct stat	sb;
3275 
3276 	if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3277 	    sb.st_size < 10000) {
3278 		bam_error(_("archive file %s not generated correctly\n"), dest);
3279 		(void) unlink(dest);
3280 		return (BAM_ERROR);
3281 	}
3282 
3283 	return (BAM_SUCCESS);
3284 }
3285 
3286 static boolean_t
3287 is_be(char *root)
3288 {
3289 	zfs_handle_t	*zhp;
3290 	libzfs_handle_t	*hdl;
3291 	be_node_list_t	*be_nodes = NULL;
3292 	be_node_list_t	*cur_be;
3293 	boolean_t	be_exist = B_FALSE;
3294 	char		ds_path[ZFS_MAX_DATASET_NAME_LEN];
3295 
3296 	if (!is_zfs(root))
3297 		return (B_FALSE);
3298 	/*
3299 	 * Get dataset for mountpoint
3300 	 */
3301 	if ((hdl = libzfs_init()) == NULL)
3302 		return (B_FALSE);
3303 
3304 	if ((zhp = zfs_path_to_zhandle(hdl, root,
3305 	    ZFS_TYPE_FILESYSTEM)) == NULL) {
3306 		libzfs_fini(hdl);
3307 		return (B_FALSE);
3308 	}
3309 
3310 	(void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3311 
3312 	/*
3313 	 * Check if the current dataset is BE
3314 	 */
3315 	if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
3316 		for (cur_be = be_nodes; cur_be != NULL;
3317 		    cur_be = cur_be->be_next_node) {
3318 
3319 			/*
3320 			 * Because we guarantee that cur_be->be_root_ds
3321 			 * is null-terminated by internal data structure,
3322 			 * we can safely use strcmp()
3323 			 */
3324 			if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3325 				be_exist = B_TRUE;
3326 				break;
3327 			}
3328 		}
3329 		be_free_list(be_nodes);
3330 	}
3331 	zfs_close(zhp);
3332 	libzfs_fini(hdl);
3333 
3334 	return (be_exist);
3335 }
3336 
3337 /*
3338  * Returns B_TRUE if mkiso is in the expected PATH and should be used,
3339  * B_FALSE otherwise
3340  */
3341 static boolean_t
3342 use_mkisofs()
3343 {
3344 	scf_simple_prop_t *prop;
3345 	char *format = NULL;
3346 	boolean_t ret;
3347 
3348 	/* Check whether the mkisofs binary is in the expected location */
3349 	if (access(MKISOFS_PATH, X_OK) != 0) {
3350 		if (bam_verbose)
3351 			bam_print("mkisofs not found\n");
3352 		return (B_FALSE);
3353 	}
3354 
3355 	if (bam_format == BAM_FORMAT_HSFS) {
3356 		if (bam_verbose)
3357 			bam_print("-F specified HSFS");
3358 		return (B_TRUE);
3359 	}
3360 
3361 	/* If working on an alt-root, do not use HSFS unless asked via -F */
3362 	if (bam_alt_root)
3363 		return (B_FALSE);
3364 
3365 	/*
3366 	 * Then check that the system/boot-archive config/format property
3367 	 * is "hsfs" or empty.
3368 	 */
3369 	if ((prop = scf_simple_prop_get(NULL, BOOT_ARCHIVE_FMRI, SCF_PG_CONFIG,
3370 	    SCF_PROPERTY_FORMAT)) == NULL) {
3371 		/* Could not find property, use mkisofs */
3372 		if (bam_verbose) {
3373 			bam_print(
3374 			    "%s does not have %s/%s property, using mkisofs\n",
3375 			    BOOT_ARCHIVE_FMRI, SCF_PG_CONFIG,
3376 			    SCF_PROPERTY_FORMAT);
3377 		}
3378 		return (B_TRUE);
3379 	}
3380 	if (scf_simple_prop_numvalues(prop) < 0 ||
3381 	    (format = scf_simple_prop_next_astring(prop)) == NULL)
3382 		ret = B_TRUE;
3383 	else
3384 		ret = strcmp(format, "hsfs") == 0 ? B_TRUE : B_FALSE;
3385 	if (bam_verbose) {
3386 		if (ret)
3387 			bam_print("Creating hsfs boot archive\n");
3388 		else
3389 			bam_print("Creating %s boot archive\n", format);
3390 	}
3391 	scf_simple_prop_free(prop);
3392 	return (ret);
3393 }
3394 
3395 #define	MKISO_PARAMS	" -quiet -graft-points -dlrDJN -relaxed-filenames "
3396 
3397 static int
3398 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3399 {
3400 	int		ret;
3401 	char		cmdline[3 * PATH_MAX + 64];
3402 	filelist_t	flist = {0};
3403 	const char	*func = "create_sparc_archive()";
3404 
3405 	if (access(bootblk, R_OK) == 1) {
3406 		bam_error(_("unable to access bootblk file : %s\n"), bootblk);
3407 		return (BAM_ERROR);
3408 	}
3409 
3410 	/*
3411 	 * Prepare mkisofs command line and execute it
3412 	 */
3413 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3414 	    "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3415 	    tempname, list);
3416 
3417 	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3418 
3419 	ret = exec_cmd(cmdline, &flist);
3420 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3421 		dump_errormsg(flist);
3422 		goto out_err;
3423 	}
3424 
3425 	filelist_free(&flist);
3426 
3427 	/*
3428 	 * Prepare dd command line to copy the bootblk on the new archive and
3429 	 * execute it
3430 	 */
3431 	(void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3432 	    " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3433 	    bootblk, tempname);
3434 
3435 	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3436 
3437 	ret = exec_cmd(cmdline, &flist);
3438 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3439 		goto out_err;
3440 
3441 	filelist_free(&flist);
3442 
3443 	/* Did we get a valid archive ? */
3444 	if (check_archive(tempname) == BAM_ERROR)
3445 		return (BAM_ERROR);
3446 
3447 	return (do_archive_copy(tempname, archive));
3448 
3449 out_err:
3450 	filelist_free(&flist);
3451 	bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline);
3452 	(void) unlink(tempname);
3453 	return (BAM_ERROR);
3454 }
3455 
3456 static unsigned int
3457 from_733(unsigned char *s)
3458 {
3459 	int		i;
3460 	unsigned int	ret = 0;
3461 
3462 	for (i = 0; i < 4; i++)
3463 		ret |= s[i] << (8 * i);
3464 
3465 	return (ret);
3466 }
3467 
3468 static void
3469 to_733(unsigned char *s, unsigned int val)
3470 {
3471 	int	i;
3472 
3473 	for (i = 0; i < 4; i++)
3474 		s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3475 }
3476 
3477 /*
3478  * creates sha1 hash of archive
3479  */
3480 static int
3481 digest_archive(const char *archive)
3482 {
3483 	char *archive_hash;
3484 	char *hash;
3485 	int ret;
3486 	FILE *fp;
3487 
3488 	(void) asprintf(&archive_hash, "%s.hash", archive);
3489 	if (archive_hash == NULL)
3490 		return (BAM_ERROR);
3491 
3492 	if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
3493 		free(archive_hash);
3494 		return (ret);
3495 	}
3496 
3497 	fp = fopen(archive_hash, "w");
3498 	if (fp == NULL) {
3499 		free(archive_hash);
3500 		free(hash);
3501 		return (BAM_ERROR);
3502 	}
3503 
3504 	(void) fprintf(fp, "%s\n", hash);
3505 	(void) fclose(fp);
3506 	free(hash);
3507 	free(archive_hash);
3508 	return (BAM_SUCCESS);
3509 }
3510 
3511 /*
3512  * Extends the current boot archive without recreating it from scratch
3513  */
3514 static int
3515 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3516 {
3517 	int			fd = -1, newfd = -1, ret, i;
3518 	int			next_session = 0, new_size = 0;
3519 	char			cmdline[3 * PATH_MAX + 64];
3520 	const char		*func = "extend_iso_archive()";
3521 	filelist_t		flist = {0};
3522 	struct iso_pdesc	saved_desc[MAX_IVDs];
3523 
3524 	fd = open(archive, O_RDWR);
3525 	if (fd == -1) {
3526 		if (bam_verbose)
3527 			bam_error(_("failed to open file: %s: %s\n"),
3528 			    archive, strerror(errno));
3529 		goto out_err;
3530 	}
3531 
3532 	/*
3533 	 * A partial read is likely due to a corrupted file
3534 	 */
3535 	ret = pread64(fd, saved_desc, sizeof (saved_desc),
3536 	    VOLDESC_OFF * CD_BLOCK);
3537 	if (ret != sizeof (saved_desc)) {
3538 		if (bam_verbose)
3539 			bam_error(_("read failed for file: %s: %s\n"),
3540 			    archive, strerror(errno));
3541 		goto out_err;
3542 	}
3543 
3544 	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3545 		if (bam_verbose)
3546 			bam_error(_("iso descriptor signature for %s is "
3547 			    "invalid\n"), archive);
3548 		goto out_err;
3549 	}
3550 
3551 	/*
3552 	 * Read primary descriptor and locate next_session offset (it should
3553 	 * point to the end of the archive)
3554 	 */
3555 	next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3556 
3557 	(void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3558 	    "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3559 	    MKISO_PARAMS, tempname, update_dir);
3560 
3561 	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3562 
3563 	ret = exec_cmd(cmdline, &flist);
3564 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3565 		if (bam_verbose) {
3566 			bam_error(_("Command '%s' failed while generating "
3567 			    "multisession archive\n"), cmdline);
3568 			dump_errormsg(flist);
3569 		}
3570 		goto out_flist_err;
3571 	}
3572 	filelist_free(&flist);
3573 
3574 	newfd = open(tempname, O_RDONLY);
3575 	if (newfd == -1) {
3576 		if (bam_verbose)
3577 			bam_error(_("failed to open file: %s: %s\n"),
3578 			    archive, strerror(errno));
3579 		goto out_err;
3580 	}
3581 
3582 	ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3583 	    VOLDESC_OFF * CD_BLOCK);
3584 	if (ret != sizeof (saved_desc)) {
3585 		if (bam_verbose)
3586 			bam_error(_("read failed for file: %s: %s\n"),
3587 			    archive, strerror(errno));
3588 		goto out_err;
3589 	}
3590 
3591 	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3592 		if (bam_verbose)
3593 			bam_error(_("iso descriptor signature for %s is "
3594 			    "invalid\n"), archive);
3595 		goto out_err;
3596 	}
3597 
3598 	new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3599 	to_733(saved_desc[0].volume_space_size, new_size);
3600 
3601 	for (i = 1; i < MAX_IVDs; i++) {
3602 		if (saved_desc[i].type[0] == (unsigned char)255)
3603 			break;
3604 		if (memcmp(saved_desc[i].id, "CD001", 5))
3605 			break;
3606 
3607 		if (bam_verbose)
3608 			bam_print("%s: Updating descriptor entry [%d]\n", func,
3609 			    i);
3610 
3611 		to_733(saved_desc[i].volume_space_size, new_size);
3612 	}
3613 
3614 	ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3615 	if (ret != DVD_BLOCK) {
3616 		if (bam_verbose)
3617 			bam_error(_("write to file failed: %s: %s\n"),
3618 			    archive, strerror(errno));
3619 		goto out_err;
3620 	}
3621 	(void) close(newfd);
3622 	newfd = -1;
3623 
3624 	ret = fsync(fd);
3625 	if (ret != 0)
3626 		sync();
3627 
3628 	ret = close(fd);
3629 	if (ret != 0) {
3630 		if (bam_verbose)
3631 			bam_error(_("failed to close file: %s: %s\n"),
3632 			    archive, strerror(errno));
3633 		return (BAM_ERROR);
3634 	}
3635 	fd = -1;
3636 
3637 	(void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3638 	    "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3639 	    (next_session/16));
3640 
3641 	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3642 
3643 	ret = exec_cmd(cmdline, &flist);
3644 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3645 		if (bam_verbose)
3646 			bam_error(_("Command '%s' failed while generating "
3647 			    "multisession archive\n"), cmdline);
3648 		goto out_flist_err;
3649 	}
3650 	filelist_free(&flist);
3651 
3652 	(void) unlink(tempname);
3653 
3654 	if (digest_archive(archive) == BAM_ERROR && bam_verbose)
3655 		bam_print("boot archive hashing failed\n");
3656 
3657 	if (flushfs(bam_root) != 0)
3658 		sync();
3659 
3660 	if (bam_verbose)
3661 		bam_print("boot archive updated successfully\n");
3662 
3663 	return (BAM_SUCCESS);
3664 
3665 out_flist_err:
3666 	filelist_free(&flist);
3667 out_err:
3668 	if (fd != -1)
3669 		(void) close(fd);
3670 	if (newfd != -1)
3671 		(void) close(newfd);
3672 	return (BAM_ERROR);
3673 }
3674 
3675 static int
3676 create_x86_archive(char *archive, char *tempname, char *update_dir)
3677 {
3678 	int		ret;
3679 	char		cmdline[3 * PATH_MAX + 64];
3680 	filelist_t	flist = {0};
3681 	const char	*func = "create_x86_archive()";
3682 
3683 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3684 	    "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3685 
3686 	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3687 
3688 	ret = exec_cmd(cmdline, &flist);
3689 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3690 		bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3691 		    cmdline);
3692 		dump_errormsg(flist);
3693 		filelist_free(&flist);
3694 		(void) unlink(tempname);
3695 		return (BAM_ERROR);
3696 	}
3697 
3698 	filelist_free(&flist);
3699 
3700 	if (check_archive(tempname) == BAM_ERROR)
3701 		return (BAM_ERROR);
3702 
3703 	return (do_archive_copy(tempname, archive));
3704 }
3705 
3706 static int
3707 mkisofs_archive(char *root)
3708 {
3709 	int		ret;
3710 	char		suffix[20];
3711 	char		temp[PATH_MAX];
3712 	char		bootblk[PATH_MAX];
3713 	char		boot_archive[PATH_MAX];
3714 
3715 	ret = snprintf(suffix, sizeof (suffix), "/archive-new-%d", getpid());
3716 	if (ret >= sizeof (suffix))
3717 		goto out_path_err;
3718 
3719 	ret = build_path(temp, sizeof (temp), root, ARCHIVE_PREFIX, suffix);
3720 
3721 	if (ret >= sizeof (temp))
3722 		goto out_path_err;
3723 
3724 	ret = build_path(boot_archive, sizeof (boot_archive), root,
3725 	    ARCHIVE_PREFIX, ARCHIVE_SUFFIX);
3726 
3727 	if (ret >= sizeof (boot_archive))
3728 		goto out_path_err;
3729 
3730 	bam_print("updating %s (HSFS)\n",
3731 	    boot_archive[1] == '/' ? boot_archive + 1 : boot_archive);
3732 
3733 	if (is_flag_on(IS_SPARC_TARGET)) {
3734 		ret = snprintf(bootblk, sizeof (bootblk),
3735 		    "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3736 		if (ret >= sizeof (bootblk))
3737 			goto out_path_err;
3738 
3739 		ret = create_sparc_archive(boot_archive, temp, bootblk,
3740 		    get_cachedir());
3741 	} else {
3742 		if (!is_dir_flag_on(NO_EXTEND)) {
3743 			if (bam_verbose)
3744 				bam_print("Attempting to extend x86 archive: "
3745 				    "%s\n", boot_archive);
3746 
3747 			ret = extend_iso_archive(boot_archive, temp,
3748 			    get_updatedir());
3749 			if (ret == BAM_SUCCESS) {
3750 				if (bam_verbose)
3751 					bam_print("Successfully extended %s\n",
3752 					    boot_archive);
3753 
3754 				(void) rmdir_r(get_updatedir());
3755 				return (BAM_SUCCESS);
3756 			}
3757 		}
3758 		/*
3759 		 * The boot archive will be recreated from scratch. We get here
3760 		 * if at least one of these conditions is true:
3761 		 * - bootadm was called without the -e switch
3762 		 * - the archive (or the archive cache) doesn't exist
3763 		 * - archive size is bigger than BA_SIZE_MAX
3764 		 * - more than COUNT_MAX files need to be updated
3765 		 * - an error occourred either populating the /updates directory
3766 		 *   or extend_iso_archive() failed
3767 		 */
3768 		if (bam_verbose)
3769 			bam_print("Unable to extend %s... rebuilding archive\n",
3770 			    boot_archive);
3771 
3772 		if (get_updatedir()[0] != '\0')
3773 			(void) rmdir_r(get_updatedir());
3774 
3775 
3776 		ret = create_x86_archive(boot_archive, temp,
3777 		    get_cachedir());
3778 	}
3779 
3780 	if (digest_archive(boot_archive) == BAM_ERROR && bam_verbose)
3781 		bam_print("boot archive hashing failed\n");
3782 
3783 	if (ret == BAM_SUCCESS && bam_verbose)
3784 		bam_print("Successfully created %s\n", boot_archive);
3785 
3786 	return (ret);
3787 
3788 out_path_err:
3789 	bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3790 	    root);
3791 	return (BAM_ERROR);
3792 }
3793 
3794 static error_t
3795 create_ramdisk(char *root)
3796 {
3797 	char *cmdline, path[PATH_MAX];
3798 	size_t len;
3799 	struct stat sb;
3800 	int ret, status = BAM_SUCCESS;
3801 
3802 	/* If mkisofs should be used, use it to create the required archives */
3803 	if (use_mkisofs()) {
3804 		if (has_cachedir() && is_dir_flag_on(NEED_UPDATE)) {
3805 			ret = mkisofs_archive(root);
3806 			if (ret != 0)
3807 				status = BAM_ERROR;
3808 		}
3809 		return (status);
3810 	} else if (bam_format == BAM_FORMAT_HSFS) {
3811 		bam_error(_("cannot create hsfs archive\n"));
3812 		return (BAM_ERROR);
3813 	}
3814 
3815 	/*
3816 	 * Else setup command args for create_ramdisk.ksh for the archive
3817 	 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3818 	 */
3819 
3820 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3821 	if (stat(path, &sb) != 0) {
3822 		bam_error(_("archive creation file not found: %s: %s\n"),
3823 		    path, strerror(errno));
3824 		return (BAM_ERROR);
3825 	}
3826 
3827 	if (is_safe_exec(path) == BAM_ERROR)
3828 		return (BAM_ERROR);
3829 
3830 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
3831 	if (bam_alt_platform)
3832 		len += strlen(bam_platform) + strlen(" -p ");
3833 	if (bam_format != BAM_FORMAT_UNSET)
3834 		len += strlen(bam_formats[bam_format]) + strlen(" -f ");
3835 	cmdline = s_calloc(1, len);
3836 
3837 	if (bam_alt_platform) {
3838 		assert(strlen(root) > 1);
3839 		(void) snprintf(cmdline, len, "%s -p %s -R %s",
3840 		    path, bam_platform, root);
3841 		/* chop off / at the end */
3842 		cmdline[strlen(cmdline) - 1] = '\0';
3843 	} else if (strlen(root) > 1) {
3844 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
3845 		/* chop off / at the end */
3846 		cmdline[strlen(cmdline) - 1] = '\0';
3847 	} else
3848 		(void) snprintf(cmdline, len, "%s", path);
3849 
3850 	if (bam_format != BAM_FORMAT_UNSET) {
3851 		if (strlcat(cmdline, " -f ", len) >= len ||
3852 		    strlcat(cmdline, bam_formats[bam_format], len) >= len) {
3853 			bam_error(_("boot-archive command line too long\n"));
3854 			free(cmdline);
3855 			return (BAM_ERROR);
3856 		}
3857 	}
3858 
3859 	if (exec_cmd(cmdline, NULL) != 0) {
3860 		bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3861 		    cmdline);
3862 		free(cmdline);
3863 		return (BAM_ERROR);
3864 	}
3865 	free(cmdline);
3866 	/*
3867 	 * The existence of the expected archives used to be
3868 	 * verified here. This check is done in create_ramdisk as
3869 	 * it needs to be in sync with the altroot operated upon.
3870 	 */
3871 	return (BAM_SUCCESS);
3872 }
3873 
3874 /*
3875  * Checks if target filesystem is on a ramdisk
3876  * 1 - is miniroot
3877  * 0 - is not
3878  * When in doubt assume it is not a ramdisk.
3879  */
3880 static int
3881 is_ramdisk(char *root)
3882 {
3883 	struct extmnttab mnt;
3884 	FILE *fp;
3885 	int found;
3886 	char mntpt[PATH_MAX];
3887 	char *cp;
3888 
3889 	/*
3890 	 * There are 3 situations where creating archive is
3891 	 * of dubious value:
3892 	 *	- create boot_archive on a lofi-mounted boot_archive
3893 	 *	- create it on a ramdisk which is the root filesystem
3894 	 *	- create it on a ramdisk mounted somewhere else
3895 	 * The first is not easy to detect and checking for it is not
3896 	 * worth it.
3897 	 * The other two conditions are handled here
3898 	 */
3899 	fp = fopen(MNTTAB, "r");
3900 	if (fp == NULL) {
3901 		bam_error(_("failed to open file: %s: %s\n"),
3902 		    MNTTAB, strerror(errno));
3903 		return (0);
3904 	}
3905 
3906 	resetmnttab(fp);
3907 
3908 	/*
3909 	 * Remove any trailing / from the mount point
3910 	 */
3911 	(void) strlcpy(mntpt, root, sizeof (mntpt));
3912 	if (strcmp(root, "/") != 0) {
3913 		cp = mntpt + strlen(mntpt) - 1;
3914 		if (*cp == '/')
3915 			*cp = '\0';
3916 	}
3917 	found = 0;
3918 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3919 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3920 			found = 1;
3921 			break;
3922 		}
3923 	}
3924 
3925 	if (!found) {
3926 		if (bam_verbose)
3927 			bam_error(_("alternate root %s not in mnttab\n"),
3928 			    mntpt);
3929 		(void) fclose(fp);
3930 		return (0);
3931 	}
3932 
3933 	if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3934 	    strlen(RAMDISK_SPECIAL)) == 0) {
3935 		if (bam_verbose)
3936 			bam_error(_("%s is on a ramdisk device\n"), bam_root);
3937 		(void) fclose(fp);
3938 		return (1);
3939 	}
3940 
3941 	(void) fclose(fp);
3942 
3943 	return (0);
3944 }
3945 
3946 static int
3947 is_boot_archive(char *root)
3948 {
3949 	char		path[PATH_MAX];
3950 	struct stat	sb;
3951 	int		error;
3952 	const char	*fcn = "is_boot_archive()";
3953 
3954 	/*
3955 	 * We can't create an archive without the create_ramdisk script
3956 	 */
3957 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3958 	error = stat(path, &sb);
3959 	INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3960 	if (error == -1) {
3961 		if (bam_verbose)
3962 			bam_print(_("file not found: %s\n"), path);
3963 		BAM_DPRINTF(("%s: not a boot archive based Solaris "
3964 		    "instance: %s\n", fcn, root));
3965 		return (0);
3966 	}
3967 
3968 	BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
3969 	    fcn, root));
3970 	return (1);
3971 }
3972 
3973 /*
3974  * Need to call this for anything that operates on the GRUB menu
3975  * In the x86 live upgrade case the directory /boot/grub may be present
3976  * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3977  * is to check for the presence of the stage2 binary which is present
3978  * only on GRUB targets (even on x86 boot partitions). Checking for the
3979  * presence of the multiboot binary is not correct as it is not present
3980  * on x86 boot partitions.
3981  */
3982 int
3983 is_grub(const char *root)
3984 {
3985 	char path[PATH_MAX];
3986 	struct stat sb;
3987 	void *defp;
3988 	boolean_t grub = B_FALSE;
3989 	const char *res = NULL;
3990 	const char *fcn = "is_grub()";
3991 
3992 	/* grub is disabled by default */
3993 	if ((defp = defopen_r(BE_DEFAULTS)) == NULL) {
3994 		return (0);
3995 	} else {
3996 		res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
3997 		if (res != NULL && res[0] != '\0') {
3998 			if (strcasecmp(res, "true") == 0)
3999 				grub = B_TRUE;
4000 		}
4001 		defclose_r(defp);
4002 	}
4003 
4004 	if (grub == B_TRUE) {
4005 		(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
4006 		if (stat(path, &sb) == -1) {
4007 			BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
4008 			    fcn, path));
4009 			return (0);
4010 		} else
4011 			return (1);
4012 	}
4013 
4014 	return (0);
4015 }
4016 
4017 int
4018 is_zfs(char *root)
4019 {
4020 	struct statvfs		vfs;
4021 	int			ret;
4022 	const char		*fcn = "is_zfs()";
4023 
4024 	ret = statvfs(root, &vfs);
4025 	INJECT_ERROR1("STATVFS_ZFS", ret = 1);
4026 	if (ret != 0) {
4027 		bam_error(_("statvfs failed for %s: %s\n"), root,
4028 		    strerror(errno));
4029 		return (0);
4030 	}
4031 
4032 	if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
4033 		BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
4034 		return (1);
4035 	} else {
4036 		BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
4037 		return (0);
4038 	}
4039 }
4040 
4041 int
4042 is_pcfs(char *root)
4043 {
4044 	struct statvfs		vfs;
4045 	int			ret;
4046 	const char		*fcn = "is_pcfs()";
4047 
4048 	ret = statvfs(root, &vfs);
4049 	INJECT_ERROR1("STATVFS_PCFS", ret = 1);
4050 	if (ret != 0) {
4051 		bam_error(_("statvfs failed for %s: %s\n"), root,
4052 		    strerror(errno));
4053 		return (0);
4054 	}
4055 
4056 	if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
4057 		BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn, root));
4058 		return (1);
4059 	} else {
4060 		BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4061 		    fcn, root));
4062 		return (0);
4063 	}
4064 }
4065 
4066 static int
4067 is_readonly(char *root)
4068 {
4069 	int		fd;
4070 	int		error;
4071 	char		testfile[PATH_MAX];
4072 	const char	*fcn = "is_readonly()";
4073 
4074 	/*
4075 	 * Using statvfs() to check for a read-only filesystem is not
4076 	 * reliable. The only way to reliably test is to attempt to
4077 	 * create a file
4078 	 */
4079 	(void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
4080 	    root, BOOTADM_RDONLY_TEST, getpid());
4081 
4082 	(void) unlink(testfile);
4083 
4084 	errno = 0;
4085 	fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
4086 	error = errno;
4087 	INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
4088 	if (fd == -1 && error == EROFS) {
4089 		BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
4090 		return (1);
4091 	} else if (fd == -1) {
4092 		bam_error(_("error during read-only test on %s: %s\n"),
4093 		    root, strerror(error));
4094 	}
4095 
4096 	(void) close(fd);
4097 	(void) unlink(testfile);
4098 
4099 	BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
4100 	return (0);
4101 }
4102 
4103 static error_t
4104 update_archive(char *root, char *opt)
4105 {
4106 	error_t ret;
4107 
4108 	assert(root);
4109 	assert(opt == NULL);
4110 
4111 	init_walk_args();
4112 	(void) umask(022);
4113 
4114 	/*
4115 	 * Never update non-BE root in update_all
4116 	 */
4117 	if (bam_update_all && !is_be(root))
4118 		return (BAM_SUCCESS);
4119 	/*
4120 	 * root must belong to a boot archive based OS,
4121 	 */
4122 	if (!is_boot_archive(root)) {
4123 		/*
4124 		 * Emit message only if not in context of update_all.
4125 		 * If in update_all, emit only if verbose flag is set.
4126 		 */
4127 		if (!bam_update_all || bam_verbose)
4128 			bam_print(_("%s: not a boot archive based Solaris "
4129 			    "instance\n"), root);
4130 		return (BAM_ERROR);
4131 	}
4132 
4133 	/*
4134 	 * If smf check is requested when / is writable (can happen
4135 	 * on first reboot following an upgrade because service
4136 	 * dependency is messed up), skip the check.
4137 	 */
4138 	if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
4139 		return (BAM_SUCCESS);
4140 
4141 	/*
4142 	 * Don't generate archive on ramdisk.
4143 	 */
4144 	if (is_ramdisk(root))
4145 		return (BAM_SUCCESS);
4146 
4147 	/*
4148 	 * root must be writable. This check applies to alternate
4149 	 * root (-R option); bam_root_readonly applies to '/' only.
4150 	 * The behaviour translates into being the one of a 'check'.
4151 	 */
4152 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
4153 		set_flag(RDONLY_FSCHK);
4154 		bam_check = 1;
4155 	}
4156 
4157 	/*
4158 	 * Now check if an update is really needed.
4159 	 */
4160 	ret = update_required(root);
4161 
4162 	/*
4163 	 * The check command (-n) is *not* a dry run.
4164 	 * It only checks if the archive is in sync.
4165 	 * A readonly filesystem has to be considered an error only if an update
4166 	 * is required.
4167 	 */
4168 	if (bam_nowrite()) {
4169 		if (is_flag_on(RDONLY_FSCHK)) {
4170 			bam_check = bam_saved_check;
4171 			if (ret > 0)
4172 				bam_error(_("%s filesystem is read-only, "
4173 				    "skipping archives update\n"), root);
4174 			if (bam_update_all)
4175 				return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4176 		}
4177 
4178 		bam_exit((ret != 0) ? 1 : 0);
4179 	}
4180 
4181 	if (ret == 1) {
4182 		/* create the ramdisk */
4183 		ret = create_ramdisk(root);
4184 	}
4185 
4186 	/*
4187 	 * if the archive is updated, save the new stat data and update the
4188 	 * timestamp file
4189 	 */
4190 	if (ret == 0 && walk_arg.new_nvlp != NULL) {
4191 		savenew(root);
4192 		update_timestamp(root);
4193 	}
4194 
4195 	clear_walk_args();
4196 
4197 	return (ret);
4198 }
4199 
4200 static char *
4201 find_root_pool()
4202 {
4203 	char *special = get_special("/");
4204 	char *p;
4205 
4206 	if (special == NULL)
4207 		return (NULL);
4208 
4209 	if (*special == '/') {
4210 		free(special);
4211 		return (NULL);
4212 	}
4213 
4214 	if ((p = strchr(special, '/')) != NULL)
4215 		*p = '\0';
4216 
4217 	return (special);
4218 }
4219 
4220 static error_t
4221 synchronize_BE_menu(void)
4222 {
4223 	struct stat	sb;
4224 	char		cmdline[PATH_MAX];
4225 	char		cksum_line[PATH_MAX];
4226 	filelist_t	flist = {0};
4227 	char		*old_cksum_str;
4228 	char		*old_size_str;
4229 	char		*old_file;
4230 	char		*curr_cksum_str;
4231 	char		*curr_size_str;
4232 	char		*curr_file;
4233 	char		*pool = NULL;
4234 	char		*mntpt = NULL;
4235 	zfs_mnted_t	mnted;
4236 	FILE		*cfp;
4237 	int		found;
4238 	int		ret;
4239 	const char	*fcn = "synchronize_BE_menu()";
4240 
4241 	BAM_DPRINTF(("%s: entered. No args\n", fcn));
4242 
4243 	/* Check if findroot enabled LU BE */
4244 	if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4245 		BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn));
4246 		return (BAM_SUCCESS);
4247 	}
4248 
4249 	if (stat(LU_MENU_CKSUM, &sb) != 0) {
4250 		BAM_DPRINTF(("%s: checksum file absent: %s\n",
4251 		    fcn, LU_MENU_CKSUM));
4252 		goto menu_sync;
4253 	}
4254 
4255 	cfp = fopen(LU_MENU_CKSUM, "r");
4256 	INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4257 	if (cfp == NULL) {
4258 		bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4259 		    LU_MENU_CKSUM);
4260 		goto menu_sync;
4261 	}
4262 	BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn, LU_MENU_CKSUM));
4263 
4264 	found = 0;
4265 	while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4266 		INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4267 		if (found) {
4268 			bam_error(_("multiple checksums for GRUB menu in "
4269 			    "checksum file: %s\n"), LU_MENU_CKSUM);
4270 			(void) fclose(cfp);
4271 			goto menu_sync;
4272 		}
4273 		found = 1;
4274 	}
4275 	BAM_DPRINTF(("%s: read checksum file: %s\n", fcn, LU_MENU_CKSUM));
4276 
4277 
4278 	old_cksum_str = strtok(cksum_line, " \t");
4279 	old_size_str = strtok(NULL, " \t");
4280 	old_file = strtok(NULL, " \t");
4281 
4282 	INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4283 	INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4284 	INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4285 	if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4286 		bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4287 		    LU_MENU_CKSUM);
4288 		goto menu_sync;
4289 	}
4290 	BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn, LU_MENU_CKSUM));
4291 
4292 	/* Get checksum of current menu */
4293 	pool = find_root_pool();
4294 	if (pool) {
4295 		mntpt = mount_top_dataset(pool, &mnted);
4296 		if (mntpt == NULL) {
4297 			bam_error(_("failed to mount top dataset for %s\n"),
4298 			    pool);
4299 			free(pool);
4300 			return (BAM_ERROR);
4301 		}
4302 		(void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4303 		    CKSUM, mntpt, GRUB_MENU);
4304 	} else {
4305 		(void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4306 		    CKSUM, GRUB_MENU);
4307 	}
4308 	ret = exec_cmd(cmdline, &flist);
4309 	if (pool) {
4310 		(void) umount_top_dataset(pool, mnted, mntpt);
4311 		free(pool);
4312 	}
4313 	INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4314 	if (ret != 0) {
4315 		bam_error(_("error generating checksum of GRUB menu\n"));
4316 		return (BAM_ERROR);
4317 	}
4318 	BAM_DPRINTF(("%s: successfully generated checksum\n", fcn));
4319 
4320 	INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4321 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
4322 		bam_error(_("bad checksum generated for GRUB menu\n"));
4323 		filelist_free(&flist);
4324 		return (BAM_ERROR);
4325 	}
4326 	BAM_DPRINTF(("%s: generated checksum output valid\n", fcn));
4327 
4328 	curr_cksum_str = strtok(flist.head->line, " \t");
4329 	curr_size_str = strtok(NULL, " \t");
4330 	curr_file = strtok(NULL, " \t");
4331 
4332 	INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4333 	INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4334 	INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4335 	if (curr_cksum_str == NULL || curr_size_str == NULL ||
4336 	    curr_file == NULL) {
4337 		bam_error(_("error parsing checksum generated "
4338 		    "for GRUB menu\n"));
4339 		filelist_free(&flist);
4340 		return (BAM_ERROR);
4341 	}
4342 	BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn));
4343 
4344 	if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4345 	    strcmp(old_size_str, curr_size_str) == 0 &&
4346 	    strcmp(old_file, curr_file) == 0) {
4347 		filelist_free(&flist);
4348 		BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn));
4349 		return (BAM_SUCCESS);
4350 	}
4351 
4352 	filelist_free(&flist);
4353 
4354 	/* cksum doesn't match - the menu has changed */
4355 	BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn));
4356 
4357 menu_sync:
4358 	bam_print(_("propagating updated GRUB menu\n"));
4359 
4360 	(void) snprintf(cmdline, sizeof (cmdline),
4361 	    "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4362 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4363 	ret = exec_cmd(cmdline, NULL);
4364 	INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4365 	if (ret != 0) {
4366 		bam_error(_("error propagating updated GRUB menu\n"));
4367 		return (BAM_ERROR);
4368 	}
4369 	BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn));
4370 
4371 	(void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4372 	    GRUB_MENU, GRUB_BACKUP_MENU);
4373 	ret = exec_cmd(cmdline, NULL);
4374 	INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4375 	if (ret != 0) {
4376 		bam_error(_("failed to create backup for GRUB menu: %s\n"),
4377 		    GRUB_BACKUP_MENU);
4378 		return (BAM_ERROR);
4379 	}
4380 	BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4381 	    fcn, GRUB_BACKUP_MENU));
4382 
4383 	(void) snprintf(cmdline, sizeof (cmdline),
4384 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4385 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4386 	ret = exec_cmd(cmdline, NULL);
4387 	INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4388 	if (ret != 0) {
4389 		bam_error(_("error propagating backup GRUB menu: %s\n"),
4390 		    GRUB_BACKUP_MENU);
4391 		return (BAM_ERROR);
4392 	}
4393 	BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4394 	    fcn, GRUB_BACKUP_MENU));
4395 
4396 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4397 	    CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4398 	ret = exec_cmd(cmdline, NULL);
4399 	INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4400 	if (ret != 0) {
4401 		bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4402 		    LU_MENU_CKSUM);
4403 		return (BAM_ERROR);
4404 	}
4405 	BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4406 	    fcn, LU_MENU_CKSUM));
4407 
4408 	(void) snprintf(cmdline, sizeof (cmdline),
4409 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4410 	    LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4411 	ret = exec_cmd(cmdline, NULL);
4412 	INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4413 	if (ret != 0) {
4414 		bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4415 		    LU_MENU_CKSUM);
4416 		return (BAM_ERROR);
4417 	}
4418 	BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4419 	    fcn, LU_MENU_CKSUM));
4420 
4421 	return (BAM_SUCCESS);
4422 }
4423 
4424 static error_t
4425 update_all(char *root, char *opt)
4426 {
4427 	struct extmnttab mnt;
4428 	struct stat sb;
4429 	FILE *fp;
4430 	char multibt[PATH_MAX];
4431 	char creatram[PATH_MAX];
4432 	error_t ret = BAM_SUCCESS;
4433 
4434 	assert(root);
4435 	assert(opt == NULL);
4436 
4437 	if (bam_rootlen != 1 || *root != '/') {
4438 		elide_trailing_slash(root, multibt, sizeof (multibt));
4439 		bam_error(_("an alternate root (%s) cannot be used with this "
4440 		    "sub-command\n"), multibt);
4441 		return (BAM_ERROR);
4442 	}
4443 
4444 	/*
4445 	 * First update archive for current root
4446 	 */
4447 	if (update_archive(root, opt) != BAM_SUCCESS)
4448 		ret = BAM_ERROR;
4449 
4450 	if (ret == BAM_ERROR)
4451 		goto out;
4452 
4453 	/*
4454 	 * Now walk the mount table, performing archive update
4455 	 * for all mounted Newboot root filesystems
4456 	 */
4457 	fp = fopen(MNTTAB, "r");
4458 	if (fp == NULL) {
4459 		bam_error(_("failed to open file: %s: %s\n"),
4460 		    MNTTAB, strerror(errno));
4461 		ret = BAM_ERROR;
4462 		goto out;
4463 	}
4464 
4465 	resetmnttab(fp);
4466 
4467 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4468 		if (mnt.mnt_special == NULL)
4469 			continue;
4470 		if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4471 		    (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4472 			continue;
4473 		if (strcmp(mnt.mnt_mountp, "/") == 0)
4474 			continue;
4475 
4476 		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
4477 		    mnt.mnt_mountp, CREATE_RAMDISK);
4478 
4479 		if (stat(creatram, &sb) == -1)
4480 			continue;
4481 
4482 		/*
4483 		 * We put a trailing slash to be consistent with root = "/"
4484 		 * case, such that we don't have to print // in some cases.
4485 		 */
4486 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4487 		    mnt.mnt_mountp);
4488 		bam_rootlen = strlen(rootbuf);
4489 
4490 		/*
4491 		 * It's possible that other mounts may be an alternate boot
4492 		 * architecture, so check it again.
4493 		 */
4494 		if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4495 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
4496 			ret = BAM_ERROR;
4497 	}
4498 
4499 	(void) fclose(fp);
4500 
4501 out:
4502 	/*
4503 	 * We no longer use biosdev for Live Upgrade. Hence
4504 	 * there is no need to defer (to shutdown time) any fdisk
4505 	 * updates
4506 	 */
4507 	if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4508 		bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4509 		    "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
4510 	}
4511 
4512 	/*
4513 	 * If user has updated menu in current BE, propagate the
4514 	 * updates to all BEs.
4515 	 */
4516 	if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4517 		ret = BAM_ERROR;
4518 
4519 	return (ret);
4520 }
4521 
4522 static void
4523 append_line(menu_t *mp, line_t *lp)
4524 {
4525 	if (mp->start == NULL) {
4526 		mp->start = lp;
4527 	} else {
4528 		mp->end->next = lp;
4529 		lp->prev = mp->end;
4530 	}
4531 	mp->end = lp;
4532 }
4533 
4534 void
4535 unlink_line(menu_t *mp, line_t *lp)
4536 {
4537 	/* unlink from list */
4538 	if (lp->prev)
4539 		lp->prev->next = lp->next;
4540 	else
4541 		mp->start = lp->next;
4542 	if (lp->next)
4543 		lp->next->prev = lp->prev;
4544 	else
4545 		mp->end = lp->prev;
4546 }
4547 
4548 static entry_t *
4549 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4550 {
4551 	entry_t *ent, *prev;
4552 	const char *fcn = "boot_entry_new()";
4553 
4554 	assert(mp);
4555 	assert(start);
4556 	assert(end);
4557 
4558 	ent = s_calloc(1, sizeof (entry_t));
4559 	BAM_DPRINTF(("%s: new boot entry alloced\n", fcn));
4560 	ent->start = start;
4561 	ent->end = end;
4562 
4563 	if (mp->entries == NULL) {
4564 		mp->entries = ent;
4565 		BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn));
4566 		return (ent);
4567 	}
4568 
4569 	prev = mp->entries;
4570 	while (prev->next)
4571 		prev = prev->next;
4572 	prev->next = ent;
4573 	ent->prev = prev;
4574 	BAM_DPRINTF(("%s: new boot entry linked in\n", fcn));
4575 	return (ent);
4576 }
4577 
4578 static void
4579 boot_entry_addline(entry_t *ent, line_t *lp)
4580 {
4581 	if (ent)
4582 		ent->end = lp;
4583 }
4584 
4585 /*
4586  * Check whether cmd matches the one indexed by which, and whether arg matches
4587  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4588  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
4589  * strstr(), so it can be a partial match.
4590  */
4591 static int
4592 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4593 {
4594 	int			ret;
4595 	const char		*fcn = "check_cmd()";
4596 
4597 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, arg, str));
4598 
4599 	if (cmd != NULL) {
4600 		if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4601 		    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4602 			BAM_DPRINTF(("%s: command %s does not match %s\n",
4603 			    fcn, cmd, menu_cmds[which]));
4604 			return (0);
4605 		}
4606 		ret = (strstr(arg, str) != NULL);
4607 	} else
4608 		ret = 0;
4609 
4610 	if (ret) {
4611 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
4612 	} else {
4613 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
4614 	}
4615 
4616 	return (ret);
4617 }
4618 
4619 static error_t
4620 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4621 {
4622 	const char		*fcn  = "kernel_parser()";
4623 
4624 	assert(entry);
4625 	assert(cmd);
4626 	assert(arg);
4627 
4628 	if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4629 	    strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4630 		BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn, cmd));
4631 		return (BAM_ERROR);
4632 	}
4633 
4634 	if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4635 		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4636 		    fcn, arg));
4637 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4638 	} else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4639 	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4640 		BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn, arg));
4641 		entry->flags |= BAM_ENTRY_DBOOT;
4642 	} else if (strncmp(arg, DIRECT_BOOT_64,
4643 	    sizeof (DIRECT_BOOT_64) - 1) == 0) {
4644 		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4645 		    fcn, arg));
4646 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4647 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4648 	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4649 		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4650 		    fcn, arg));
4651 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4652 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4653 	    sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4654 		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4655 		    "flag: %s\n", fcn, arg));
4656 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4657 		    | BAM_ENTRY_32BIT;
4658 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4659 	    sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4660 		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4661 		    "flag: %s\n", fcn, arg));
4662 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4663 		    | BAM_ENTRY_64BIT;
4664 	} else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4665 		BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn, arg));
4666 		entry->flags |= BAM_ENTRY_MULTIBOOT;
4667 	} else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4668 	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4669 		BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4670 		    "flag: %s\n", fcn, arg));
4671 		entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4672 	} else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4673 		BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn, arg));
4674 		entry->flags |= BAM_ENTRY_HV;
4675 	} else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4676 		BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn, arg));
4677 		return (BAM_ERROR);
4678 	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4679 	    strstr(arg, UNIX_SPACE)) {
4680 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4681 	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4682 	    strstr(arg, AMD_UNIX_SPACE)) {
4683 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4684 	} else {
4685 		BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn, arg));
4686 		bam_error(_("kernel command on line %d not recognized.\n"),
4687 		    linenum);
4688 		return (BAM_ERROR);
4689 	}
4690 
4691 	return (BAM_SUCCESS);
4692 }
4693 
4694 static error_t
4695 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4696 {
4697 	const char		*fcn = "module_parser()";
4698 
4699 	assert(entry);
4700 	assert(cmd);
4701 	assert(arg);
4702 
4703 	if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4704 	    strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4705 		BAM_DPRINTF(("%s: not module cmd: %s\n", fcn, cmd));
4706 		return (BAM_ERROR);
4707 	}
4708 
4709 	if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4710 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4711 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4712 	    strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4713 	    strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4714 	    strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4715 	    strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4716 	    strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4717 	    strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4718 		BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn, arg));
4719 		return (BAM_SUCCESS);
4720 	} else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4721 	    !(entry->flags & BAM_ENTRY_LU)) {
4722 		/* don't emit warning for hand entries */
4723 		BAM_DPRINTF(("%s: is HAND module: %s\n", fcn, arg));
4724 		return (BAM_ERROR);
4725 	} else {
4726 		BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn, arg));
4727 		bam_error(_("module command on line %d not recognized.\n"),
4728 		    linenum);
4729 		return (BAM_ERROR);
4730 	}
4731 }
4732 
4733 /*
4734  * A line in menu.lst looks like
4735  * [ ]*<cmd>[ \t=]*<arg>*
4736  */
4737 static void
4738 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4739 {
4740 	/*
4741 	 * save state across calls. This is so that
4742 	 * header gets the right entry# after title has
4743 	 * been processed
4744 	 */
4745 	static line_t *prev = NULL;
4746 	static entry_t *curr_ent = NULL;
4747 	static int in_liveupgrade = 0;
4748 	static int is_libbe_ent = 0;
4749 
4750 	line_t	*lp;
4751 	char *cmd, *sep, *arg;
4752 	char save, *cp, *line;
4753 	menu_flag_t flag = BAM_INVALID;
4754 	const char *fcn = "line_parser()";
4755 
4756 	cmd = NULL;
4757 	if (str == NULL) {
4758 		return;
4759 	}
4760 
4761 	/*
4762 	 * First save a copy of the entire line.
4763 	 * We use this later to set the line field.
4764 	 */
4765 	line = s_strdup(str);
4766 
4767 	/* Eat up leading whitespace */
4768 	while (*str == ' ' || *str == '\t')
4769 		str++;
4770 
4771 	if (*str == '#') {		/* comment */
4772 		cmd = s_strdup("#");
4773 		sep = NULL;
4774 		arg = s_strdup(str + 1);
4775 		flag = BAM_COMMENT;
4776 		if (strstr(arg, BAM_LU_HDR) != NULL) {
4777 			in_liveupgrade = 1;
4778 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
4779 			in_liveupgrade = 0;
4780 		} else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4781 			is_libbe_ent = 1;
4782 		}
4783 	} else if (*str == '\0') {	/* blank line */
4784 		cmd = sep = arg = NULL;
4785 		flag = BAM_EMPTY;
4786 	} else {
4787 		/*
4788 		 * '=' is not a documented separator in grub syntax.
4789 		 * However various development bits use '=' as a
4790 		 * separator. In addition, external users also
4791 		 * use = as a separator. So we will allow that usage.
4792 		 */
4793 		cp = str;
4794 		while (*str != ' ' && *str != '\t' && *str != '=') {
4795 			if (*str == '\0') {
4796 				cmd = s_strdup(cp);
4797 				sep = arg = NULL;
4798 				break;
4799 			}
4800 			str++;
4801 		}
4802 
4803 		if (*str != '\0') {
4804 			save = *str;
4805 			*str = '\0';
4806 			cmd = s_strdup(cp);
4807 			*str = save;
4808 
4809 			str++;
4810 			save = *str;
4811 			*str = '\0';
4812 			sep = s_strdup(str - 1);
4813 			*str = save;
4814 
4815 			while (*str == ' ' || *str == '\t')
4816 				str++;
4817 			if (*str == '\0')
4818 				arg = NULL;
4819 			else
4820 				arg = s_strdup(str);
4821 		}
4822 	}
4823 
4824 	lp = s_calloc(1, sizeof (line_t));
4825 
4826 	lp->cmd = cmd;
4827 	lp->sep = sep;
4828 	lp->arg = arg;
4829 	lp->line = line;
4830 	lp->lineNum = ++(*lineNum);
4831 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4832 		lp->entryNum = ++(*entryNum);
4833 		lp->flags = BAM_TITLE;
4834 		if (prev && prev->flags == BAM_COMMENT &&
4835 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4836 			prev->entryNum = lp->entryNum;
4837 			curr_ent = boot_entry_new(mp, prev, lp);
4838 			curr_ent->flags |= BAM_ENTRY_BOOTADM;
4839 			BAM_DPRINTF(("%s: is bootadm(1M) entry: %s\n",
4840 			    fcn, arg));
4841 		} else {
4842 			curr_ent = boot_entry_new(mp, lp, lp);
4843 			if (in_liveupgrade) {
4844 				curr_ent->flags |= BAM_ENTRY_LU;
4845 				BAM_DPRINTF(("%s: is LU entry: %s\n",
4846 				    fcn, arg));
4847 			}
4848 		}
4849 		curr_ent->entryNum = *entryNum;
4850 	} else if (flag != BAM_INVALID) {
4851 		/*
4852 		 * For header comments, the entry# is "fixed up"
4853 		 * by the subsequent title
4854 		 */
4855 		lp->entryNum = *entryNum;
4856 		lp->flags = flag;
4857 	} else {
4858 		lp->entryNum = *entryNum;
4859 
4860 		if (*entryNum == ENTRY_INIT) {
4861 			lp->flags = BAM_GLOBAL;
4862 		} else {
4863 			lp->flags = BAM_ENTRY;
4864 
4865 			if (cmd && arg) {
4866 				if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4867 					BAM_DPRINTF(("%s: setting ROOT: %s\n",
4868 					    fcn, arg));
4869 					curr_ent->flags |= BAM_ENTRY_ROOT;
4870 				} else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4871 				    == 0) {
4872 					BAM_DPRINTF(("%s: setting "
4873 					    "FINDROOT: %s\n", fcn, arg));
4874 					curr_ent->flags |= BAM_ENTRY_FINDROOT;
4875 				} else if (strcmp(cmd,
4876 				    menu_cmds[CHAINLOADER_CMD]) == 0) {
4877 					BAM_DPRINTF(("%s: setting "
4878 					    "CHAINLOADER: %s\n", fcn, arg));
4879 					curr_ent->flags |=
4880 					    BAM_ENTRY_CHAINLOADER;
4881 				} else if (kernel_parser(curr_ent, cmd, arg,
4882 				    lp->lineNum) != BAM_SUCCESS) {
4883 					(void) module_parser(curr_ent, cmd,
4884 					    arg, lp->lineNum);
4885 				}
4886 			}
4887 		}
4888 	}
4889 
4890 	/* record default, old default, and entry line ranges */
4891 	if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4892 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4893 		mp->curdefault = lp;
4894 	} else if (lp->flags == BAM_COMMENT &&
4895 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4896 		mp->olddefault = lp;
4897 	} else if (lp->flags == BAM_COMMENT &&
4898 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4899 		mp->old_rc_default = lp;
4900 	} else if (lp->flags == BAM_ENTRY ||
4901 	    (lp->flags == BAM_COMMENT &&
4902 	    ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4903 		if (is_libbe_ent) {
4904 			curr_ent->flags |= BAM_ENTRY_LIBBE;
4905 			is_libbe_ent = 0;
4906 		}
4907 
4908 		boot_entry_addline(curr_ent, lp);
4909 	}
4910 	append_line(mp, lp);
4911 
4912 	prev = lp;
4913 }
4914 
4915 void
4916 update_numbering(menu_t *mp)
4917 {
4918 	int lineNum;
4919 	int entryNum;
4920 	int old_default_value;
4921 	line_t *lp, *prev, *default_lp, *default_entry;
4922 	char buf[PATH_MAX];
4923 
4924 	if (mp->start == NULL) {
4925 		return;
4926 	}
4927 
4928 	lineNum = LINE_INIT;
4929 	entryNum = ENTRY_INIT;
4930 	old_default_value = ENTRY_INIT;
4931 	lp = default_lp = default_entry = NULL;
4932 
4933 	prev = NULL;
4934 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4935 		lp->lineNum = ++lineNum;
4936 
4937 		/*
4938 		 * Get the value of the default command
4939 		 */
4940 		if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4941 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4942 		    lp->arg) {
4943 			old_default_value = atoi(lp->arg);
4944 			default_lp = lp;
4945 		}
4946 
4947 		/*
4948 		 * If not a booting entry, nothing else to fix for this
4949 		 * entry
4950 		 */
4951 		if (lp->entryNum == ENTRY_INIT)
4952 			continue;
4953 
4954 		/*
4955 		 * Record the position of the default entry.
4956 		 * The following works because global
4957 		 * commands like default and timeout should precede
4958 		 * actual boot entries, so old_default_value
4959 		 * is already known (or default cmd is missing).
4960 		 */
4961 		if (default_entry == NULL &&
4962 		    old_default_value != ENTRY_INIT &&
4963 		    lp->entryNum == old_default_value) {
4964 			default_entry = lp;
4965 		}
4966 
4967 		/*
4968 		 * Now fixup the entry number
4969 		 */
4970 		if (lp->cmd != NULL &&
4971 		    strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4972 			lp->entryNum = ++entryNum;
4973 			/* fixup the bootadm header */
4974 			if (prev && prev->flags == BAM_COMMENT &&
4975 			    prev->arg &&
4976 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4977 				prev->entryNum = lp->entryNum;
4978 			}
4979 		} else {
4980 			lp->entryNum = entryNum;
4981 		}
4982 	}
4983 
4984 	/*
4985 	 * No default command in menu, simply return
4986 	 */
4987 	if (default_lp == NULL) {
4988 		return;
4989 	}
4990 
4991 	free(default_lp->arg);
4992 	free(default_lp->line);
4993 
4994 	if (default_entry == NULL) {
4995 		default_lp->arg = s_strdup("0");
4996 	} else {
4997 		(void) snprintf(buf, sizeof (buf), "%d",
4998 		    default_entry->entryNum);
4999 		default_lp->arg = s_strdup(buf);
5000 	}
5001 
5002 	/*
5003 	 * The following is required since only the line field gets
5004 	 * written back to menu.lst
5005 	 */
5006 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
5007 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
5008 	default_lp->line = s_strdup(buf);
5009 }
5010 
5011 
5012 static menu_t *
5013 menu_read(char *menu_path)
5014 {
5015 	FILE *fp;
5016 	char buf[BAM_MAXLINE], *cp;
5017 	menu_t *mp;
5018 	int line, entry, len, n;
5019 
5020 	mp = s_calloc(1, sizeof (menu_t));
5021 
5022 	fp = fopen(menu_path, "r");
5023 	if (fp == NULL) { /* Let the caller handle this error */
5024 		free(mp);
5025 		return (NULL);
5026 	}
5027 
5028 	/* Note: GRUB boot entry number starts with 0 */
5029 	line = LINE_INIT;
5030 	entry = ENTRY_INIT;
5031 	cp = buf;
5032 	len = sizeof (buf);
5033 	while (s_fgets(cp, len, fp) != NULL) {
5034 		n = strlen(cp);
5035 		if (cp[n - 1] == '\\') {
5036 			len -= n - 1;
5037 			assert(len >= 2);
5038 			cp += n - 1;
5039 			continue;
5040 		}
5041 		line_parser(mp, buf, &line, &entry);
5042 		cp = buf;
5043 		len = sizeof (buf);
5044 	}
5045 
5046 	if (fclose(fp) == EOF) {
5047 		bam_error(_("failed to close file: %s: %s\n"), menu_path,
5048 		    strerror(errno));
5049 	}
5050 
5051 	return (mp);
5052 }
5053 
5054 static error_t
5055 selector(menu_t *mp, char *opt, int *entry, char **title)
5056 {
5057 	char *eq;
5058 	char *opt_dup;
5059 	int entryNum;
5060 
5061 	assert(mp);
5062 	assert(mp->start);
5063 	assert(opt);
5064 
5065 	opt_dup = s_strdup(opt);
5066 
5067 	if (entry)
5068 		*entry = ENTRY_INIT;
5069 	if (title)
5070 		*title = NULL;
5071 
5072 	eq = strchr(opt_dup, '=');
5073 	if (eq == NULL) {
5074 		bam_error(_("invalid option: %s\n"), opt);
5075 		free(opt_dup);
5076 		return (BAM_ERROR);
5077 	}
5078 
5079 	*eq = '\0';
5080 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
5081 		assert(mp->end);
5082 		entryNum = s_strtol(eq + 1);
5083 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
5084 			bam_error(_("invalid boot entry number: %s\n"), eq + 1);
5085 			free(opt_dup);
5086 			return (BAM_ERROR);
5087 		}
5088 		*entry = entryNum;
5089 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
5090 		*title = opt + (eq - opt_dup) + 1;
5091 	} else {
5092 		bam_error(_("invalid option: %s\n"), opt);
5093 		free(opt_dup);
5094 		return (BAM_ERROR);
5095 	}
5096 
5097 	free(opt_dup);
5098 	return (BAM_SUCCESS);
5099 }
5100 
5101 /*
5102  * If invoked with no titles/entries (opt == NULL)
5103  * only title lines in file are printed.
5104  *
5105  * If invoked with a title or entry #, all
5106  * lines in *every* matching entry are listed
5107  */
5108 static error_t
5109 list_entry(menu_t *mp, char *menu_path, char *opt)
5110 {
5111 	line_t *lp;
5112 	int entry = ENTRY_INIT;
5113 	int found;
5114 	char *title = NULL;
5115 
5116 	assert(mp);
5117 	assert(menu_path);
5118 
5119 	/* opt is optional */
5120 	BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path,
5121 	    opt ? opt : "<NULL>"));
5122 
5123 	if (mp->start == NULL) {
5124 		bam_error(_("menu file not found: %s\n"), menu_path);
5125 		return (BAM_ERROR);
5126 	}
5127 
5128 	if (opt != NULL) {
5129 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
5130 			return (BAM_ERROR);
5131 		}
5132 		assert((entry != ENTRY_INIT) ^ (title != NULL));
5133 	} else {
5134 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
5135 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
5136 	}
5137 
5138 	found = 0;
5139 	for (lp = mp->start; lp; lp = lp->next) {
5140 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
5141 			continue;
5142 		if (opt == NULL && lp->flags == BAM_TITLE) {
5143 			bam_print(_("%d %s\n"), lp->entryNum,
5144 			    lp->arg);
5145 			found = 1;
5146 			continue;
5147 		}
5148 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
5149 			bam_print(_("%s\n"), lp->line);
5150 			found = 1;
5151 			continue;
5152 		}
5153 
5154 		/*
5155 		 * We set the entry value here so that all lines
5156 		 * in entry get printed. If we subsequently match
5157 		 * title in other entries, all lines in those
5158 		 * entries get printed as well.
5159 		 */
5160 		if (title && lp->flags == BAM_TITLE && lp->arg &&
5161 		    strncmp(title, lp->arg, strlen(title)) == 0) {
5162 			bam_print(_("%s\n"), lp->line);
5163 			entry = lp->entryNum;
5164 			found = 1;
5165 			continue;
5166 		}
5167 	}
5168 
5169 	if (!found) {
5170 		bam_error(_("no matching entry found\n"));
5171 		return (BAM_ERROR);
5172 	}
5173 
5174 	return (BAM_SUCCESS);
5175 }
5176 
5177 int
5178 add_boot_entry(menu_t *mp,
5179     char *title,
5180     char *findroot,
5181     char *kernel,
5182     char *mod_kernel,
5183     char *module,
5184     char *bootfs)
5185 {
5186 	int		lineNum;
5187 	int		entryNum;
5188 	char		linebuf[BAM_MAXLINE];
5189 	menu_cmd_t	k_cmd;
5190 	menu_cmd_t	m_cmd;
5191 	const char	*fcn = "add_boot_entry()";
5192 
5193 	assert(mp);
5194 
5195 	INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
5196 	if (findroot == NULL) {
5197 		bam_error(_("can't find argument for findroot command\n"));
5198 		return (BAM_ERROR);
5199 	}
5200 
5201 	if (title == NULL) {
5202 		title = "Solaris";	/* default to Solaris */
5203 	}
5204 	if (kernel == NULL) {
5205 		bam_error(_("missing suboption: %s\n"), menu_cmds[KERNEL_CMD]);
5206 		return (BAM_ERROR);
5207 	}
5208 	if (module == NULL) {
5209 		if (bam_direct != BAM_DIRECT_DBOOT) {
5210 			bam_error(_("missing suboption: %s\n"),
5211 			    menu_cmds[MODULE_CMD]);
5212 			return (BAM_ERROR);
5213 		}
5214 
5215 		/* Figure the commands out from the kernel line */
5216 		if (strstr(kernel, "$ISADIR") != NULL) {
5217 			module = DIRECT_BOOT_ARCHIVE;
5218 		} else if (strstr(kernel, "amd64") != NULL) {
5219 			module = DIRECT_BOOT_ARCHIVE_64;
5220 		} else {
5221 			module = DIRECT_BOOT_ARCHIVE_32;
5222 		}
5223 	}
5224 
5225 	k_cmd = KERNEL_DOLLAR_CMD;
5226 	m_cmd = MODULE_DOLLAR_CMD;
5227 
5228 	if (mp->start) {
5229 		lineNum = mp->end->lineNum;
5230 		entryNum = mp->end->entryNum;
5231 	} else {
5232 		lineNum = LINE_INIT;
5233 		entryNum = ENTRY_INIT;
5234 	}
5235 
5236 	/*
5237 	 * No separator for comment (HDR/FTR) commands
5238 	 * The syntax for comments is #<comment>
5239 	 */
5240 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5241 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5242 	line_parser(mp, linebuf, &lineNum, &entryNum);
5243 
5244 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5245 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5246 	line_parser(mp, linebuf, &lineNum, &entryNum);
5247 
5248 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5249 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5250 	line_parser(mp, linebuf, &lineNum, &entryNum);
5251 	BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5252 	    fcn, lineNum, entryNum));
5253 
5254 	if (bootfs != NULL) {
5255 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5256 		    menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5257 		line_parser(mp, linebuf, &lineNum, &entryNum);
5258 	}
5259 
5260 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5261 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5262 	line_parser(mp, linebuf, &lineNum, &entryNum);
5263 
5264 	if (mod_kernel != NULL) {
5265 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5266 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5267 		line_parser(mp, linebuf, &lineNum, &entryNum);
5268 	}
5269 
5270 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5271 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5272 	line_parser(mp, linebuf, &lineNum, &entryNum);
5273 
5274 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5275 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5276 	line_parser(mp, linebuf, &lineNum, &entryNum);
5277 
5278 	return (entryNum);
5279 }
5280 
5281 error_t
5282 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5283 {
5284 	line_t		*lp;
5285 	line_t		*freed;
5286 	entry_t		*ent;
5287 	entry_t		*tmp;
5288 	int		deleted = 0;
5289 	const char	*fcn = "delete_boot_entry()";
5290 
5291 	assert(entryNum != ENTRY_INIT);
5292 
5293 	tmp = NULL;
5294 
5295 	ent = mp->entries;
5296 	while (ent) {
5297 		lp = ent->start;
5298 
5299 		/*
5300 		 * Check entry number and make sure it's a modifiable entry.
5301 		 *
5302 		 * Guidelines:
5303 		 *	+ We can modify a bootadm-created entry
5304 		 *	+ We can modify a libbe-created entry
5305 		 */
5306 		if ((lp->flags != BAM_COMMENT &&
5307 		    (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5308 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5309 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5310 			ent = ent->next;
5311 			continue;
5312 		}
5313 
5314 		/* free the entry content */
5315 		do {
5316 			freed = lp;
5317 			lp = lp->next;	/* prev stays the same */
5318 			BAM_DPRINTF(("%s: freeing line: %d\n",
5319 			    fcn, freed->lineNum));
5320 			unlink_line(mp, freed);
5321 			line_free(freed);
5322 		} while (freed != ent->end);
5323 
5324 		/* free the entry_t structure */
5325 		assert(tmp == NULL);
5326 		tmp = ent;
5327 		ent = ent->next;
5328 		if (tmp->prev)
5329 			tmp->prev->next = ent;
5330 		else
5331 			mp->entries = ent;
5332 		if (ent)
5333 			ent->prev = tmp->prev;
5334 		BAM_DPRINTF(("%s: freeing entry: %d\n", fcn, tmp->entryNum));
5335 		free(tmp);
5336 		tmp = NULL;
5337 		deleted = 1;
5338 	}
5339 
5340 	assert(tmp == NULL);
5341 
5342 	if (!deleted && entryNum != ALL_ENTRIES) {
5343 		if (quiet == DBE_PRINTERR)
5344 			bam_error(_("no matching bootadm entry found\n"));
5345 		return (BAM_ERROR);
5346 	}
5347 
5348 	/*
5349 	 * Now that we have deleted an entry, update
5350 	 * the entry numbering and the default cmd.
5351 	 */
5352 	update_numbering(mp);
5353 
5354 	return (BAM_SUCCESS);
5355 }
5356 
5357 static error_t
5358 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5359 {
5360 	assert(mp);
5361 	assert(dummy == NULL);
5362 	assert(opt == NULL);
5363 
5364 	BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5365 
5366 	if (mp->start == NULL) {
5367 		bam_print(_("the GRUB menu is empty\n"));
5368 		return (BAM_SUCCESS);
5369 	}
5370 
5371 	if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5372 		return (BAM_ERROR);
5373 	}
5374 
5375 	return (BAM_WRITE);
5376 }
5377 
5378 static FILE *
5379 create_diskmap(char *osroot)
5380 {
5381 	FILE *fp;
5382 	char cmd[PATH_MAX + 16];
5383 	char path[PATH_MAX];
5384 	const char *fcn = "create_diskmap()";
5385 
5386 	/* make sure we have a map file */
5387 	fp = fopen(GRUBDISK_MAP, "r");
5388 	if (fp == NULL) {
5389 		int	ret;
5390 
5391 		ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5392 		    CREATE_DISKMAP);
5393 		if (ret >= sizeof (path)) {
5394 			bam_error(_("unable to create path on mountpoint %s, "
5395 			    "path too long\n"), osroot);
5396 			return (NULL);
5397 		}
5398 		if (is_safe_exec(path) == BAM_ERROR)
5399 			return (NULL);
5400 
5401 		(void) snprintf(cmd, sizeof (cmd),
5402 		    "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5403 		if (exec_cmd(cmd, NULL) != 0)
5404 			return (NULL);
5405 		fp = fopen(GRUBDISK_MAP, "r");
5406 		INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5407 		if (fp) {
5408 			BAM_DPRINTF(("%s: created diskmap file: %s\n",
5409 			    fcn, GRUBDISK_MAP));
5410 		} else {
5411 			BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5412 			    fcn, GRUBDISK_MAP));
5413 		}
5414 	}
5415 	return (fp);
5416 }
5417 
5418 #define	SECTOR_SIZE	512
5419 
5420 static int
5421 get_partition(char *device)
5422 {
5423 	int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5424 	struct mboot *mboot;
5425 	char boot_sect[SECTOR_SIZE];
5426 	char *wholedisk, *slice;
5427 #ifdef i386
5428 	ext_part_t *epp;
5429 	uint32_t secnum, numsec;
5430 	int rval, pno, ext_partno = PARTNO_NOTFOUND;
5431 #endif
5432 
5433 	/* form whole disk (p0) */
5434 	slice = device + strlen(device) - 2;
5435 	is_pcfs = (*slice != 's');
5436 	if (!is_pcfs)
5437 		*slice = '\0';
5438 	wholedisk = s_calloc(1, strlen(device) + 3);
5439 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5440 	if (!is_pcfs)
5441 		*slice = 's';
5442 
5443 	/* read boot sector */
5444 	fd = open(wholedisk, O_RDONLY);
5445 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5446 		return (partno);
5447 	}
5448 	(void) close(fd);
5449 
5450 #ifdef i386
5451 	/* Read/Initialize extended partition information */
5452 	if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5453 	    != FDISK_SUCCESS) {
5454 		switch (rval) {
5455 			/*
5456 			 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5457 			 * be considered as soft errors and hence
5458 			 * we do not return
5459 			 */
5460 			case FDISK_EBADLOGDRIVE:
5461 				break;
5462 			case FDISK_ENOLOGDRIVE:
5463 				break;
5464 			case FDISK_EBADMAGIC:
5465 				/*FALLTHROUGH*/
5466 			default:
5467 				free(wholedisk);
5468 				libfdisk_fini(&epp);
5469 				return (partno);
5470 		}
5471 	}
5472 #endif
5473 	free(wholedisk);
5474 
5475 	/* parse fdisk table */
5476 	mboot = (struct mboot *)((void *)boot_sect);
5477 	for (i = 0; i < FD_NUMPART; i++) {
5478 		struct ipart *part =
5479 		    (struct ipart *)(uintptr_t)mboot->parts + i;
5480 		if (is_pcfs) {	/* looking for solaris boot part */
5481 			if (part->systid == 0xbe) {
5482 				partno = i;
5483 				break;
5484 			}
5485 		} else {	/* look for solaris partition, old and new */
5486 			if (part->systid == EFI_PMBR) {
5487 				partno = PARTNO_EFI;
5488 				break;
5489 			}
5490 
5491 #ifdef i386
5492 			if ((part->systid == SUNIXOS &&
5493 			    (fdisk_is_linux_swap(epp, part->relsect,
5494 			    NULL) != 0)) || part->systid == SUNIXOS2) {
5495 #else
5496 			if (part->systid == SUNIXOS ||
5497 			    part->systid == SUNIXOS2) {
5498 #endif
5499 				partno = i;
5500 				break;
5501 			}
5502 
5503 #ifdef i386
5504 			if (fdisk_is_dos_extended(part->systid))
5505 				ext_partno = i;
5506 #endif
5507 		}
5508 	}
5509 #ifdef i386
5510 	/* If no primary solaris partition, check extended partition */
5511 	if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5512 		rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5513 		if (rval == FDISK_SUCCESS) {
5514 			partno = pno - 1;
5515 		}
5516 	}
5517 	libfdisk_fini(&epp);
5518 #endif
5519 	return (partno);
5520 }
5521 
5522 char *
5523 get_grubroot(char *osroot, char *osdev, char *menu_root)
5524 {
5525 	char		*grubroot;	/* (hd#,#,#) */
5526 	char		*slice;
5527 	char		*grubhd = NULL;
5528 	int		fdiskpart;
5529 	int		found = 0;
5530 	char		*devname;
5531 	char		*ctdname = strstr(osdev, "dsk/");
5532 	char		linebuf[PATH_MAX];
5533 	FILE		*fp;
5534 
5535 	INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5536 	if (ctdname == NULL) {
5537 		bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev);
5538 		return (NULL);
5539 	}
5540 
5541 	if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5542 		/* menu bears no resemblance to our reality */
5543 		bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5544 		    "bootdisk: %s\n"), osdev);
5545 		return (NULL);
5546 	}
5547 
5548 	ctdname += strlen("dsk/");
5549 	slice = strrchr(ctdname, 's');
5550 	if (slice)
5551 		*slice = '\0';
5552 
5553 	fp = create_diskmap(osroot);
5554 	if (fp == NULL) {
5555 		bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5556 		    osroot);
5557 		return (NULL);
5558 	}
5559 
5560 	rewind(fp);
5561 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5562 		grubhd = strtok(linebuf, " \t\n");
5563 		if (grubhd)
5564 			devname = strtok(NULL, " \t\n");
5565 		else
5566 			devname = NULL;
5567 		if (devname && strcmp(devname, ctdname) == 0) {
5568 			found = 1;
5569 			break;
5570 		}
5571 	}
5572 
5573 	if (slice)
5574 		*slice = 's';
5575 
5576 	(void) fclose(fp);
5577 	fp = NULL;
5578 
5579 	INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5580 	if (found == 0) {
5581 		bam_error(_("not using biosdev command for disk: %s.\n"),
5582 		    osdev);
5583 		return (NULL);
5584 	}
5585 
5586 	fdiskpart = get_partition(osdev);
5587 	INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5588 	if (fdiskpart == PARTNO_NOTFOUND) {
5589 		bam_error(_("failed to determine fdisk partition: %s\n"),
5590 		    osdev);
5591 		return (NULL);
5592 	}
5593 
5594 	grubroot = s_calloc(1, 10);
5595 	if (fdiskpart == PARTNO_EFI) {
5596 		fdiskpart = atoi(&slice[1]);
5597 		slice = NULL;
5598 	}
5599 
5600 	if (slice) {
5601 		(void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5602 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
5603 	} else
5604 		(void) snprintf(grubroot, 10, "(hd%s,%d)",
5605 		    grubhd, fdiskpart);
5606 
5607 	assert(fp == NULL);
5608 	assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5609 	return (grubroot);
5610 }
5611 
5612 static char *
5613 find_primary_common(char *mntpt, char *fstype)
5614 {
5615 	char		signdir[PATH_MAX];
5616 	char		tmpsign[MAXNAMELEN + 1];
5617 	char		*lu;
5618 	char		*ufs;
5619 	char		*zfs;
5620 	DIR		*dirp = NULL;
5621 	struct dirent	*entp;
5622 	struct stat	sb;
5623 	const char	*fcn = "find_primary_common()";
5624 
5625 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
5626 	    mntpt, GRUBSIGN_DIR);
5627 
5628 	if (stat(signdir, &sb) == -1) {
5629 		BAM_DPRINTF(("%s: no sign dir: %s\n", fcn, signdir));
5630 		return (NULL);
5631 	}
5632 
5633 	dirp = opendir(signdir);
5634 	INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5635 	if (dirp == NULL) {
5636 		bam_error(_("opendir of %s failed: %s\n"), signdir,
5637 		    strerror(errno));
5638 		return (NULL);
5639 	}
5640 
5641 	ufs = zfs = lu = NULL;
5642 
5643 	while ((entp = readdir(dirp)) != NULL) {
5644 		if (strcmp(entp->d_name, ".") == 0 ||
5645 		    strcmp(entp->d_name, "..") == 0)
5646 			continue;
5647 
5648 		(void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5649 
5650 		if (lu == NULL &&
5651 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5652 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5653 			lu = s_strdup(tmpsign);
5654 		}
5655 
5656 		if (ufs == NULL &&
5657 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5658 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5659 			ufs = s_strdup(tmpsign);
5660 		}
5661 
5662 		if (zfs == NULL &&
5663 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5664 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5665 			zfs = s_strdup(tmpsign);
5666 		}
5667 	}
5668 
5669 	BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn,
5670 	    zfs ? zfs : "NULL",
5671 	    ufs ? ufs : "NULL",
5672 	    lu ? lu : "NULL"));
5673 
5674 	if (dirp) {
5675 		(void) closedir(dirp);
5676 		dirp = NULL;
5677 	}
5678 
5679 	if (strcmp(fstype, "ufs") == 0 && zfs) {
5680 		bam_error(_("found mismatched boot signature %s for "
5681 		    "filesystem type: %s.\n"), zfs, "ufs");
5682 		free(zfs);
5683 		zfs = NULL;
5684 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
5685 		bam_error(_("found mismatched boot signature %s for "
5686 		    "filesystem type: %s.\n"), ufs, "zfs");
5687 		free(ufs);
5688 		ufs = NULL;
5689 	}
5690 
5691 	assert(dirp == NULL);
5692 
5693 	/* For now, we let Live Upgrade take care of its signature itself */
5694 	if (lu) {
5695 		BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5696 		free(lu);
5697 		lu = NULL;
5698 	}
5699 
5700 	return (zfs ? zfs : ufs);
5701 }
5702 
5703 static char *
5704 find_backup_common(char *mntpt, char *fstype)
5705 {
5706 	FILE		*bfp = NULL;
5707 	char		tmpsign[MAXNAMELEN + 1];
5708 	char		backup[PATH_MAX];
5709 	char		*ufs;
5710 	char		*zfs;
5711 	char		*lu;
5712 	int		error;
5713 	const char	*fcn = "find_backup_common()";
5714 
5715 	/*
5716 	 * We didn't find it in the primary directory.
5717 	 * Look at the backup
5718 	 */
5719 	(void) snprintf(backup, sizeof (backup), "%s%s",
5720 	    mntpt, GRUBSIGN_BACKUP);
5721 
5722 	bfp = fopen(backup, "r");
5723 	if (bfp == NULL) {
5724 		error = errno;
5725 		if (bam_verbose) {
5726 			bam_error(_("failed to open file: %s: %s\n"),
5727 			    backup, strerror(error));
5728 		}
5729 		BAM_DPRINTF(("%s: failed to open %s: %s\n",
5730 		    fcn, backup, strerror(error)));
5731 		return (NULL);
5732 	}
5733 
5734 	ufs = zfs = lu = NULL;
5735 
5736 	while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5737 
5738 		if (lu == NULL &&
5739 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5740 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5741 			lu = s_strdup(tmpsign);
5742 		}
5743 
5744 		if (ufs == NULL &&
5745 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5746 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5747 			ufs = s_strdup(tmpsign);
5748 		}
5749 
5750 		if (zfs == NULL &&
5751 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5752 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5753 			zfs = s_strdup(tmpsign);
5754 		}
5755 	}
5756 
5757 	BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn,
5758 	    zfs ? zfs : "NULL",
5759 	    ufs ? ufs : "NULL",
5760 	    lu ? lu : "NULL"));
5761 
5762 	if (bfp) {
5763 		(void) fclose(bfp);
5764 		bfp = NULL;
5765 	}
5766 
5767 	if (strcmp(fstype, "ufs") == 0 && zfs) {
5768 		bam_error(_("found mismatched boot signature %s for "
5769 		    "filesystem type: %s.\n"), zfs, "ufs");
5770 		free(zfs);
5771 		zfs = NULL;
5772 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
5773 		bam_error(_("found mismatched boot signature %s for "
5774 		    "filesystem type: %s.\n"), ufs, "zfs");
5775 		free(ufs);
5776 		ufs = NULL;
5777 	}
5778 
5779 	assert(bfp == NULL);
5780 
5781 	/* For now, we let Live Upgrade take care of its signature itself */
5782 	if (lu) {
5783 		BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5784 		free(lu);
5785 		lu = NULL;
5786 	}
5787 
5788 	return (zfs ? zfs : ufs);
5789 }
5790 
5791 static char *
5792 find_ufs_existing(char *osroot)
5793 {
5794 	char		*sign;
5795 	const char	*fcn = "find_ufs_existing()";
5796 
5797 	sign = find_primary_common(osroot, "ufs");
5798 	if (sign == NULL) {
5799 		sign = find_backup_common(osroot, "ufs");
5800 		BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
5801 		    sign ? sign : "NULL"));
5802 	} else {
5803 		BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
5804 	}
5805 
5806 	return (sign);
5807 }
5808 
5809 char *
5810 get_mountpoint(char *special, char *fstype)
5811 {
5812 	FILE		*mntfp;
5813 	struct mnttab	mp = {0};
5814 	struct mnttab	mpref = {0};
5815 	int		error;
5816 	int		ret;
5817 	const char	*fcn = "get_mountpoint()";
5818 
5819 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
5820 
5821 	mntfp = fopen(MNTTAB, "r");
5822 	error = errno;
5823 	INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5824 	if (mntfp == NULL) {
5825 		bam_error(_("failed to open file: %s: %s\n"),
5826 		    MNTTAB, strerror(error));
5827 		return (NULL);
5828 	}
5829 
5830 	mpref.mnt_special = special;
5831 	mpref.mnt_fstype = fstype;
5832 
5833 	ret = getmntany(mntfp, &mp, &mpref);
5834 	INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5835 	if (ret != 0) {
5836 		(void) fclose(mntfp);
5837 		BAM_DPRINTF(("%s: no mount-point for special=%s and "
5838 		    "fstype=%s\n", fcn, special, fstype));
5839 		return (NULL);
5840 	}
5841 	(void) fclose(mntfp);
5842 
5843 	assert(mp.mnt_mountp);
5844 
5845 	BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5846 	    fcn, special, mp.mnt_mountp));
5847 
5848 	return (s_strdup(mp.mnt_mountp));
5849 }
5850 
5851 /*
5852  * Mounts a "legacy" top dataset (if needed)
5853  * Returns:	The mountpoint of the legacy top dataset or NULL on error
5854  *		mnted returns one of the above values defined for zfs_mnted_t
5855  */
5856 static char *
5857 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5858 {
5859 	char		cmd[PATH_MAX];
5860 	char		tmpmnt[PATH_MAX];
5861 	filelist_t	flist = {0};
5862 	char		*is_mounted;
5863 	struct stat	sb;
5864 	int		ret;
5865 	const char	*fcn = "mount_legacy_dataset()";
5866 
5867 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5868 
5869 	*mnted = ZFS_MNT_ERROR;
5870 
5871 	(void) snprintf(cmd, sizeof (cmd),
5872 	    "/sbin/zfs get -Ho value mounted %s",
5873 	    pool);
5874 
5875 	ret = exec_cmd(cmd, &flist);
5876 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5877 	if (ret != 0) {
5878 		bam_error(_("failed to determine mount status of ZFS "
5879 		    "pool %s\n"), pool);
5880 		return (NULL);
5881 	}
5882 
5883 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5884 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
5885 		bam_error(_("ZFS pool %s has bad mount status\n"), pool);
5886 		filelist_free(&flist);
5887 		return (NULL);
5888 	}
5889 
5890 	is_mounted = strtok(flist.head->line, " \t\n");
5891 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5892 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5893 	if (strcmp(is_mounted, "no") != 0) {
5894 		filelist_free(&flist);
5895 		*mnted = LEGACY_ALREADY;
5896 		/* get_mountpoint returns a strdup'ed string */
5897 		BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
5898 		    fcn, pool));
5899 		return (get_mountpoint(pool, "zfs"));
5900 	}
5901 
5902 	filelist_free(&flist);
5903 
5904 	/*
5905 	 * legacy top dataset is not mounted. Mount it now
5906 	 * First create a mountpoint.
5907 	 */
5908 	(void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5909 	    ZFS_LEGACY_MNTPT, getpid());
5910 
5911 	ret = stat(tmpmnt, &sb);
5912 	if (ret == -1) {
5913 		BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
5914 		    fcn, pool, tmpmnt));
5915 		ret = mkdirp(tmpmnt, DIR_PERMS);
5916 		INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5917 		if (ret == -1) {
5918 			bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
5919 			    strerror(errno));
5920 			return (NULL);
5921 		}
5922 	} else {
5923 		BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
5924 		    "present\n", fcn, pool, tmpmnt));
5925 	}
5926 
5927 	(void) snprintf(cmd, sizeof (cmd),
5928 	    "/sbin/mount -F zfs %s %s",
5929 	    pool, tmpmnt);
5930 
5931 	ret = exec_cmd(cmd, NULL);
5932 	INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5933 	if (ret != 0) {
5934 		bam_error(_("mount of ZFS pool %s failed\n"), pool);
5935 		(void) rmdir(tmpmnt);
5936 		return (NULL);
5937 	}
5938 
5939 	*mnted = LEGACY_MOUNTED;
5940 	BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
5941 	    fcn, pool, tmpmnt));
5942 	return (s_strdup(tmpmnt));
5943 }
5944 
5945 /*
5946  * Mounts the top dataset (if needed)
5947  * Returns:	The mountpoint of the top dataset or NULL on error
5948  *		mnted returns one of the above values defined for zfs_mnted_t
5949  */
5950 char *
5951 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5952 {
5953 	char		cmd[PATH_MAX];
5954 	filelist_t	flist = {0};
5955 	char		*is_mounted;
5956 	char		*mntpt;
5957 	char		*zmntpt;
5958 	int		ret;
5959 	const char	*fcn = "mount_top_dataset()";
5960 
5961 	*mnted = ZFS_MNT_ERROR;
5962 
5963 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5964 
5965 	/*
5966 	 * First check if the top dataset is a "legacy" dataset
5967 	 */
5968 	(void) snprintf(cmd, sizeof (cmd),
5969 	    "/sbin/zfs get -Ho value mountpoint %s",
5970 	    pool);
5971 	ret = exec_cmd(cmd, &flist);
5972 	INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5973 	if (ret != 0) {
5974 		bam_error(_("failed to determine mount point of ZFS pool %s\n"),
5975 		    pool);
5976 		return (NULL);
5977 	}
5978 
5979 	if (flist.head && (flist.head == flist.tail)) {
5980 		char *legacy = strtok(flist.head->line, " \t\n");
5981 		if (legacy && strcmp(legacy, "legacy") == 0) {
5982 			filelist_free(&flist);
5983 			BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
5984 			return (mount_legacy_dataset(pool, mnted));
5985 		}
5986 	}
5987 
5988 	filelist_free(&flist);
5989 
5990 	BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
5991 
5992 	(void) snprintf(cmd, sizeof (cmd),
5993 	    "/sbin/zfs get -Ho value mounted %s",
5994 	    pool);
5995 
5996 	ret = exec_cmd(cmd, &flist);
5997 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5998 	if (ret != 0) {
5999 		bam_error(_("failed to determine mount status of ZFS "
6000 		    "pool %s\n"), pool);
6001 		return (NULL);
6002 	}
6003 
6004 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
6005 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6006 		bam_error(_("ZFS pool %s has bad mount status\n"), pool);
6007 		filelist_free(&flist);
6008 		return (NULL);
6009 	}
6010 
6011 	is_mounted = strtok(flist.head->line, " \t\n");
6012 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
6013 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
6014 	if (strcmp(is_mounted, "no") != 0) {
6015 		filelist_free(&flist);
6016 		*mnted = ZFS_ALREADY;
6017 		BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
6018 		    fcn, pool));
6019 		goto mounted;
6020 	}
6021 
6022 	filelist_free(&flist);
6023 	BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
6024 	    fcn, pool));
6025 
6026 	/* top dataset is not mounted. Mount it now */
6027 	(void) snprintf(cmd, sizeof (cmd),
6028 	    "/sbin/zfs mount %s", pool);
6029 	ret = exec_cmd(cmd, NULL);
6030 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
6031 	if (ret != 0) {
6032 		bam_error(_("mount of ZFS pool %s failed\n"), pool);
6033 		return (NULL);
6034 	}
6035 	*mnted = ZFS_MOUNTED;
6036 	BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
6037 	/*FALLTHRU*/
6038 mounted:
6039 	/*
6040 	 * Now get the mountpoint
6041 	 */
6042 	(void) snprintf(cmd, sizeof (cmd),
6043 	    "/sbin/zfs get -Ho value mountpoint %s",
6044 	    pool);
6045 
6046 	ret = exec_cmd(cmd, &flist);
6047 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
6048 	if (ret != 0) {
6049 		bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6050 		    pool);
6051 		goto error;
6052 	}
6053 
6054 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
6055 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6056 		bam_error(_("ZFS pool %s has no mount-point\n"), pool);
6057 		goto error;
6058 	}
6059 
6060 	mntpt = strtok(flist.head->line, " \t\n");
6061 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
6062 	if (*mntpt != '/') {
6063 		bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6064 		    pool, mntpt);
6065 		goto error;
6066 	}
6067 	zmntpt = s_strdup(mntpt);
6068 
6069 	filelist_free(&flist);
6070 
6071 	BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6072 	    fcn, pool, zmntpt));
6073 
6074 	return (zmntpt);
6075 
6076 error:
6077 	filelist_free(&flist);
6078 	(void) umount_top_dataset(pool, *mnted, NULL);
6079 	BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6080 	return (NULL);
6081 }
6082 
6083 int
6084 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
6085 {
6086 	char		cmd[PATH_MAX];
6087 	int		ret;
6088 	const char	*fcn = "umount_top_dataset()";
6089 
6090 	INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
6091 	switch (mnted) {
6092 	case LEGACY_ALREADY:
6093 	case ZFS_ALREADY:
6094 		/* nothing to do */
6095 		BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6096 		    "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
6097 		free(mntpt);
6098 		return (BAM_SUCCESS);
6099 	case LEGACY_MOUNTED:
6100 		(void) snprintf(cmd, sizeof (cmd),
6101 		    "/sbin/umount %s", pool);
6102 		ret = exec_cmd(cmd, NULL);
6103 		INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
6104 		if (ret != 0) {
6105 			bam_error(_("umount of %s failed\n"), pool);
6106 			free(mntpt);
6107 			return (BAM_ERROR);
6108 		}
6109 		if (mntpt)
6110 			(void) rmdir(mntpt);
6111 		free(mntpt);
6112 		BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6113 		    "successfully unmounted\n", fcn, pool));
6114 		return (BAM_SUCCESS);
6115 	case ZFS_MOUNTED:
6116 		free(mntpt);
6117 		(void) snprintf(cmd, sizeof (cmd),
6118 		    "/sbin/zfs unmount %s", pool);
6119 		ret = exec_cmd(cmd, NULL);
6120 		INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
6121 		if (ret != 0) {
6122 			bam_error(_("umount of %s failed\n"), pool);
6123 			return (BAM_ERROR);
6124 		}
6125 		BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6126 		    "successfully unmounted\n", fcn, pool));
6127 		return (BAM_SUCCESS);
6128 	default:
6129 		bam_error(_("Internal error: bad saved mount state for "
6130 		    "pool %s\n"), pool);
6131 		return (BAM_ERROR);
6132 	}
6133 	/*NOTREACHED*/
6134 }
6135 
6136 /*
6137  * For ZFS, osdev can be one of two forms
6138  * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6139  * It can be a /dev/[r]dsk special file. We handle both instances
6140  */
6141 static char *
6142 get_pool(char *osdev)
6143 {
6144 	char		cmd[PATH_MAX];
6145 	char		buf[PATH_MAX];
6146 	filelist_t	flist = {0};
6147 	char		*pool;
6148 	char		*cp;
6149 	char		*slash;
6150 	int		ret;
6151 	const char	*fcn = "get_pool()";
6152 
6153 	INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
6154 	if (osdev == NULL) {
6155 		bam_error(_("NULL device: cannot determine pool name\n"));
6156 		return (NULL);
6157 	}
6158 
6159 	BAM_DPRINTF(("%s: osdev arg = %s\n", fcn, osdev));
6160 
6161 	if (osdev[0] != '/') {
6162 		(void) strlcpy(buf, osdev, sizeof (buf));
6163 		slash = strchr(buf, '/');
6164 		if (slash)
6165 			*slash = '\0';
6166 		pool = s_strdup(buf);
6167 		BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6168 		return (pool);
6169 	} else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6170 	    strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6171 		bam_error(_("invalid device %s: cannot determine pool name\n"),
6172 		    osdev);
6173 		return (NULL);
6174 	}
6175 
6176 	/*
6177 	 * Call the zfs fstyp directly since this is a zpool. This avoids
6178 	 * potential pcfs conflicts if the first block wasn't cleared.
6179 	 */
6180 	(void) snprintf(cmd, sizeof (cmd),
6181 	    "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6182 	    osdev);
6183 
6184 	ret = exec_cmd(cmd, &flist);
6185 	INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
6186 	if (ret != 0) {
6187 		bam_error(_("fstyp -a on device %s failed\n"), osdev);
6188 		return (NULL);
6189 	}
6190 
6191 	INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
6192 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6193 		bam_error(_("NULL fstyp -a output for device %s\n"), osdev);
6194 		filelist_free(&flist);
6195 		return (NULL);
6196 	}
6197 
6198 	(void) strtok(flist.head->line, "'");
6199 	cp = strtok(NULL, "'");
6200 	INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6201 	if (cp == NULL) {
6202 		bam_error(_("bad fstyp -a output for device %s\n"), osdev);
6203 		filelist_free(&flist);
6204 		return (NULL);
6205 	}
6206 
6207 	pool = s_strdup(cp);
6208 
6209 	filelist_free(&flist);
6210 
6211 	BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6212 
6213 	return (pool);
6214 }
6215 
6216 static char *
6217 find_zfs_existing(char *osdev)
6218 {
6219 	char		*pool;
6220 	zfs_mnted_t	mnted;
6221 	char		*mntpt;
6222 	char		*sign;
6223 	const char	*fcn = "find_zfs_existing()";
6224 
6225 	pool = get_pool(osdev);
6226 	INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6227 	if (pool == NULL) {
6228 		bam_error(_("failed to get pool for device: %s\n"), osdev);
6229 		return (NULL);
6230 	}
6231 
6232 	mntpt = mount_top_dataset(pool, &mnted);
6233 	INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
6234 	if (mntpt == NULL) {
6235 		bam_error(_("failed to mount top dataset for pool: %s\n"),
6236 		    pool);
6237 		free(pool);
6238 		return (NULL);
6239 	}
6240 
6241 	sign = find_primary_common(mntpt, "zfs");
6242 	if (sign == NULL) {
6243 		sign = find_backup_common(mntpt, "zfs");
6244 		BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
6245 		    sign ? sign : "NULL"));
6246 	} else {
6247 		BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
6248 	}
6249 
6250 	(void) umount_top_dataset(pool, mnted, mntpt);
6251 
6252 	free(pool);
6253 
6254 	return (sign);
6255 }
6256 
6257 static char *
6258 find_existing_sign(char *osroot, char *osdev, char *fstype)
6259 {
6260 	const char		*fcn = "find_existing_sign()";
6261 
6262 	INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6263 	if (strcmp(fstype, "ufs") == 0) {
6264 		BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn));
6265 		return (find_ufs_existing(osroot));
6266 	} else if (strcmp(fstype, "zfs") == 0) {
6267 		BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn));
6268 		return (find_zfs_existing(osdev));
6269 	} else {
6270 		bam_error(_("boot signature not supported for fstype: %s\n"),
6271 		    fstype);
6272 		return (NULL);
6273 	}
6274 }
6275 
6276 #define	MH_HASH_SZ	16
6277 
6278 typedef enum {
6279 	MH_ERROR = -1,
6280 	MH_NOMATCH,
6281 	MH_MATCH
6282 } mh_search_t;
6283 
6284 typedef struct mcache {
6285 	char	*mc_special;
6286 	char	*mc_mntpt;
6287 	char	*mc_fstype;
6288 	struct mcache *mc_next;
6289 } mcache_t;
6290 
6291 typedef struct mhash {
6292 	mcache_t *mh_hash[MH_HASH_SZ];
6293 } mhash_t;
6294 
6295 static int
6296 mhash_fcn(char *key)
6297 {
6298 	int		i;
6299 	uint64_t	sum = 0;
6300 
6301 	for (i = 0; key[i] != '\0'; i++) {
6302 		sum += (uchar_t)key[i];
6303 	}
6304 
6305 	sum %= MH_HASH_SZ;
6306 
6307 	assert(sum < MH_HASH_SZ);
6308 
6309 	return (sum);
6310 }
6311 
6312 static mhash_t *
6313 cache_mnttab(void)
6314 {
6315 	FILE		*mfp;
6316 	struct extmnttab mnt;
6317 	mcache_t	*mcp;
6318 	mhash_t		*mhp;
6319 	char		*ctds;
6320 	int		idx;
6321 	int		error;
6322 	char		*special_dup;
6323 	const char	*fcn = "cache_mnttab()";
6324 
6325 	mfp = fopen(MNTTAB, "r");
6326 	error = errno;
6327 	INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6328 	if (mfp == NULL) {
6329 		bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
6330 		    strerror(error));
6331 		return (NULL);
6332 	}
6333 
6334 	mhp = s_calloc(1, sizeof (mhash_t));
6335 
6336 	resetmnttab(mfp);
6337 
6338 	while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6339 		/* only cache ufs */
6340 		if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6341 			continue;
6342 
6343 		/* basename() modifies its arg, so dup it */
6344 		special_dup = s_strdup(mnt.mnt_special);
6345 		ctds = basename(special_dup);
6346 
6347 		mcp = s_calloc(1, sizeof (mcache_t));
6348 		mcp->mc_special = s_strdup(ctds);
6349 		mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6350 		mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6351 		BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6352 		    "fstype=%s\n", fcn, ctds, mnt.mnt_mountp, mnt.mnt_fstype));
6353 		idx = mhash_fcn(ctds);
6354 		mcp->mc_next = mhp->mh_hash[idx];
6355 		mhp->mh_hash[idx] = mcp;
6356 		free(special_dup);
6357 	}
6358 
6359 	(void) fclose(mfp);
6360 
6361 	return (mhp);
6362 }
6363 
6364 static void
6365 free_mnttab(mhash_t *mhp)
6366 {
6367 	mcache_t	*mcp;
6368 	int		i;
6369 
6370 	for (i = 0; i < MH_HASH_SZ; i++) {
6371 		while ((mcp = mhp->mh_hash[i]) != NULL) {
6372 			mhp->mh_hash[i] = mcp->mc_next;
6373 			free(mcp->mc_special);
6374 			free(mcp->mc_mntpt);
6375 			free(mcp->mc_fstype);
6376 			free(mcp);
6377 		}
6378 	}
6379 
6380 	for (i = 0; i < MH_HASH_SZ; i++) {
6381 		assert(mhp->mh_hash[i] == NULL);
6382 	}
6383 	free(mhp);
6384 }
6385 
6386 static mh_search_t
6387 search_hash(mhash_t *mhp, char *special, char **mntpt)
6388 {
6389 	int		idx;
6390 	mcache_t	*mcp;
6391 	const char	*fcn = "search_hash()";
6392 
6393 	assert(mntpt);
6394 
6395 	*mntpt = NULL;
6396 
6397 	INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6398 	if (strchr(special, '/')) {
6399 		bam_error(_("invalid key for mnttab hash: %s\n"), special);
6400 		return (MH_ERROR);
6401 	}
6402 
6403 	idx = mhash_fcn(special);
6404 
6405 	for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6406 		if (strcmp(mcp->mc_special, special) == 0)
6407 			break;
6408 	}
6409 
6410 	if (mcp == NULL) {
6411 		BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn, special));
6412 		return (MH_NOMATCH);
6413 	}
6414 
6415 	assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6416 	*mntpt = mcp->mc_mntpt;
6417 	BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn, special));
6418 	return (MH_MATCH);
6419 }
6420 
6421 static int
6422 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6423 {
6424 	char		*sign;
6425 	char		*signline;
6426 	char		signbuf[MAXNAMELEN];
6427 	int		len;
6428 	int		error;
6429 	const char	*fcn = "check_add_ufs_sign_to_list()";
6430 
6431 	/* safe to specify NULL as "osdev" arg for UFS */
6432 	sign = find_existing_sign(mntpt, NULL, "ufs");
6433 	if (sign == NULL) {
6434 		/* No existing signature, nothing to add to list */
6435 		BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6436 		    fcn, mntpt));
6437 		return (0);
6438 	}
6439 
6440 	(void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6441 	signline = signbuf;
6442 
6443 	INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6444 	if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6445 	    strlen(GRUBSIGN_UFS_PREFIX))) {
6446 		bam_error(_("invalid UFS boot signature %s\n"), sign);
6447 		free(sign);
6448 		/* ignore invalid signatures */
6449 		return (0);
6450 	}
6451 
6452 	len = fputs(signline, tfp);
6453 	error = errno;
6454 	INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6455 	if (len != strlen(signline)) {
6456 		bam_error(_("failed to write signature %s to signature "
6457 		    "list: %s\n"), sign, strerror(error));
6458 		free(sign);
6459 		return (-1);
6460 	}
6461 
6462 	free(sign);
6463 
6464 	BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6465 	    fcn, mntpt));
6466 	return (0);
6467 }
6468 
6469 /*
6470  * slice is a basename not a full pathname
6471  */
6472 static int
6473 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6474 {
6475 	int		ret;
6476 	char		cmd[PATH_MAX];
6477 	char		path[PATH_MAX];
6478 	struct stat	sbuf;
6479 	char		*mntpt;
6480 	filelist_t	flist = {0};
6481 	char		*fstype;
6482 	char		blkslice[PATH_MAX];
6483 	const char	*fcn = "process_slice_common()";
6484 
6485 
6486 	ret = search_hash(mhp, slice, &mntpt);
6487 	switch (ret) {
6488 		case MH_MATCH:
6489 			if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6490 				return (-1);
6491 			else
6492 				return (0);
6493 		case MH_NOMATCH:
6494 			break;
6495 		case MH_ERROR:
6496 		default:
6497 			return (-1);
6498 	}
6499 
6500 	(void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6501 	if (stat(path, &sbuf) == -1) {
6502 		BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn, path));
6503 		return (0);
6504 	}
6505 
6506 	/* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6507 	(void) snprintf(cmd, sizeof (cmd),
6508 	    "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6509 	    slice);
6510 
6511 	if (exec_cmd(cmd, &flist) != 0) {
6512 		if (bam_verbose)
6513 			bam_print(_("fstyp failed for slice: %s\n"), slice);
6514 		return (0);
6515 	}
6516 
6517 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6518 		if (bam_verbose)
6519 			bam_print(_("bad output from fstyp for slice: %s\n"),
6520 			    slice);
6521 		filelist_free(&flist);
6522 		return (0);
6523 	}
6524 
6525 	fstype = strtok(flist.head->line, " \t\n");
6526 	if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6527 		if (bam_verbose)
6528 			bam_print(_("%s is not a ufs slice: %s\n"),
6529 			    slice, fstype);
6530 		filelist_free(&flist);
6531 		return (0);
6532 	}
6533 
6534 	filelist_free(&flist);
6535 
6536 	/*
6537 	 * Since we are mounting the filesystem read-only, the
6538 	 * the last mount field of the superblock is unchanged
6539 	 * and does not need to be fixed up post-mount;
6540 	 */
6541 
6542 	(void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6543 	    slice);
6544 
6545 	(void) snprintf(cmd, sizeof (cmd),
6546 	    "/usr/sbin/mount -F ufs -o ro %s %s "
6547 	    "> /dev/null 2>&1", blkslice, tmpmnt);
6548 
6549 	if (exec_cmd(cmd, NULL) != 0) {
6550 		if (bam_verbose)
6551 			bam_print(_("mount of %s (fstype %s) failed\n"),
6552 			    blkslice, "ufs");
6553 		return (0);
6554 	}
6555 
6556 	ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6557 
6558 	(void) snprintf(cmd, sizeof (cmd),
6559 	    "/usr/sbin/umount -f %s > /dev/null 2>&1",
6560 	    tmpmnt);
6561 
6562 	if (exec_cmd(cmd, NULL) != 0) {
6563 		bam_print(_("umount of %s failed\n"), slice);
6564 		return (0);
6565 	}
6566 
6567 	return (ret);
6568 }
6569 
6570 static int
6571 process_vtoc_slices(
6572 	char *s0,
6573 	struct vtoc *vtoc,
6574 	FILE *tfp,
6575 	mhash_t *mhp,
6576 	char *tmpmnt)
6577 {
6578 	int		idx;
6579 	char		slice[PATH_MAX];
6580 	size_t		len;
6581 	char		*cp;
6582 	const char	*fcn = "process_vtoc_slices()";
6583 
6584 	len = strlen(s0);
6585 
6586 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6587 
6588 	s0[len - 1] = '\0';
6589 
6590 	(void) strlcpy(slice, s0, sizeof (slice));
6591 
6592 	s0[len - 1] = '0';
6593 
6594 	cp = slice + len - 1;
6595 
6596 	for (idx = 0; idx < vtoc->v_nparts; idx++) {
6597 
6598 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6599 
6600 		if (vtoc->v_part[idx].p_size == 0) {
6601 			BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6602 			    fcn, slice));
6603 			continue;
6604 		}
6605 
6606 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6607 		switch (vtoc->v_part[idx].p_tag) {
6608 		case V_SWAP:
6609 		case V_USR:
6610 		case V_BACKUP:
6611 		case V_VAR:
6612 		case V_HOME:
6613 		case V_ALTSCTR:
6614 			BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6615 			    "skipping: %s\n", fcn, slice));
6616 			continue;
6617 		default:
6618 			BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6619 			    fcn, slice));
6620 			break;
6621 		}
6622 
6623 		/* skip unmountable and readonly slices */
6624 		switch (vtoc->v_part[idx].p_flag) {
6625 		case V_UNMNT:
6626 		case V_RONLY:
6627 			BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6628 			    fcn, slice));
6629 			continue;
6630 		default:
6631 			BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6632 			    fcn, slice));
6633 			break;
6634 		}
6635 
6636 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6637 			return (-1);
6638 		}
6639 	}
6640 
6641 	return (0);
6642 }
6643 
6644 static int
6645 process_efi_slices(
6646 	char *s0,
6647 	struct dk_gpt *efi,
6648 	FILE *tfp,
6649 	mhash_t *mhp,
6650 	char *tmpmnt)
6651 {
6652 	int		idx;
6653 	char		slice[PATH_MAX];
6654 	size_t		len;
6655 	char		*cp;
6656 	const char	*fcn = "process_efi_slices()";
6657 
6658 	len = strlen(s0);
6659 
6660 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6661 
6662 	s0[len - 1] = '\0';
6663 
6664 	(void) strlcpy(slice, s0, sizeof (slice));
6665 
6666 	s0[len - 1] = '0';
6667 
6668 	cp = slice + len - 1;
6669 
6670 	for (idx = 0; idx < efi->efi_nparts; idx++) {
6671 
6672 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6673 
6674 		if (efi->efi_parts[idx].p_size == 0) {
6675 			BAM_DPRINTF(("%s: EFI: skipping 0-length slice: %s\n",
6676 			    fcn, slice));
6677 			continue;
6678 		}
6679 
6680 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6681 		switch (efi->efi_parts[idx].p_tag) {
6682 		case V_SWAP:
6683 		case V_USR:
6684 		case V_BACKUP:
6685 		case V_VAR:
6686 		case V_HOME:
6687 		case V_ALTSCTR:
6688 			BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6689 			    fcn, slice));
6690 			continue;
6691 		default:
6692 			BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6693 			    fcn, slice));
6694 			break;
6695 		}
6696 
6697 		/* skip unmountable and readonly slices */
6698 		switch (efi->efi_parts[idx].p_flag) {
6699 		case V_UNMNT:
6700 		case V_RONLY:
6701 			BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6702 			    fcn, slice));
6703 			continue;
6704 		default:
6705 			BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6706 			    fcn, slice));
6707 			break;
6708 		}
6709 
6710 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6711 			return (-1);
6712 		}
6713 	}
6714 
6715 	return (0);
6716 }
6717 
6718 /*
6719  * s0 is a basename not a full path
6720  */
6721 static int
6722 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6723 {
6724 	struct vtoc		vtoc;
6725 	struct dk_gpt		*efi;
6726 	char			s0path[PATH_MAX];
6727 	struct stat		sbuf;
6728 	int			e_flag;
6729 	int			v_flag;
6730 	int			retval;
6731 	int			err;
6732 	int			fd;
6733 	const char		*fcn = "process_slice0()";
6734 
6735 	(void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6736 
6737 	if (stat(s0path, &sbuf) == -1) {
6738 		BAM_DPRINTF(("%s: slice 0 does not exist: %s\n", fcn, s0path));
6739 		return (0);
6740 	}
6741 
6742 	fd = open(s0path, O_NONBLOCK|O_RDONLY);
6743 	if (fd == -1) {
6744 		bam_error(_("failed to open file: %s: %s\n"), s0path,
6745 		    strerror(errno));
6746 		return (0);
6747 	}
6748 
6749 	e_flag = v_flag = 0;
6750 	retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6751 	switch (retval) {
6752 		case VT_EIO:
6753 			BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6754 			    fcn, s0path));
6755 			break;
6756 		case VT_EINVAL:
6757 			BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6758 			    fcn, s0path));
6759 			break;
6760 		case VT_ERROR:
6761 			BAM_DPRINTF(("%s: VTOC: unknown error while "
6762 			    "reading: %s\n", fcn, s0path));
6763 			break;
6764 		case VT_ENOTSUP:
6765 			e_flag = 1;
6766 			BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6767 			    fcn, s0path));
6768 			break;
6769 		case 0:
6770 			v_flag = 1;
6771 			BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6772 			    fcn, s0path));
6773 			break;
6774 		default:
6775 			BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6776 			    "code: %s\n", fcn, s0path));
6777 			break;
6778 	}
6779 
6780 
6781 	if (e_flag) {
6782 		e_flag = 0;
6783 		retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6784 		switch (retval) {
6785 		case VT_EIO:
6786 			BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6787 			    fcn, s0path));
6788 			break;
6789 		case VT_EINVAL:
6790 			BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn, s0path));
6791 			break;
6792 		case VT_ERROR:
6793 			BAM_DPRINTF(("%s: EFI: unknown error while "
6794 			    "reading: %s\n", fcn, s0path));
6795 			break;
6796 		case VT_ENOTSUP:
6797 			BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6798 			    fcn, s0path));
6799 			break;
6800 		case 0:
6801 			e_flag = 1;
6802 			BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6803 			    fcn, s0path));
6804 			break;
6805 		default:
6806 			BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6807 			    fcn, s0path));
6808 			break;
6809 		}
6810 	}
6811 
6812 	(void) close(fd);
6813 
6814 	if (v_flag) {
6815 		retval = process_vtoc_slices(s0,
6816 		    &vtoc, tfp, mhp, tmpmnt);
6817 	} else if (e_flag) {
6818 		retval = process_efi_slices(s0,
6819 		    efi, tfp, mhp, tmpmnt);
6820 	} else {
6821 		BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6822 		    fcn, s0path));
6823 		return (0);
6824 	}
6825 
6826 	return (retval);
6827 }
6828 
6829 /*
6830  * Find and create a list of all existing UFS boot signatures
6831  */
6832 static int
6833 FindAllUfsSignatures(void)
6834 {
6835 	mhash_t		*mnttab_hash;
6836 	DIR		*dirp = NULL;
6837 	struct dirent	*dp;
6838 	char		tmpmnt[PATH_MAX];
6839 	char		cmd[PATH_MAX];
6840 	struct stat	sb;
6841 	int		fd;
6842 	FILE		*tfp;
6843 	size_t		len;
6844 	int		ret;
6845 	int		error;
6846 	const char	*fcn = "FindAllUfsSignatures()";
6847 
6848 	if (stat(UFS_SIGNATURE_LIST, &sb) != -1)  {
6849 		bam_print(_("       - signature list %s exists\n"),
6850 		    UFS_SIGNATURE_LIST);
6851 		return (0);
6852 	}
6853 
6854 	fd = open(UFS_SIGNATURE_LIST".tmp",
6855 	    O_RDWR|O_CREAT|O_TRUNC, 0644);
6856 	error = errno;
6857 	INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6858 	if (fd == -1) {
6859 		bam_error(_("failed to open file: %s: %s\n"),
6860 		    UFS_SIGNATURE_LIST".tmp", strerror(error));
6861 		return (-1);
6862 	}
6863 
6864 	ret = close(fd);
6865 	error = errno;
6866 	INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6867 	if (ret == -1) {
6868 		bam_error(_("failed to close file: %s: %s\n"),
6869 		    UFS_SIGNATURE_LIST".tmp", strerror(error));
6870 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
6871 		return (-1);
6872 	}
6873 
6874 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6875 	error = errno;
6876 	INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6877 	if (tfp == NULL) {
6878 		bam_error(_("failed to open file: %s: %s\n"),
6879 		    UFS_SIGNATURE_LIST".tmp", strerror(error));
6880 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
6881 		return (-1);
6882 	}
6883 
6884 	mnttab_hash = cache_mnttab();
6885 	INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6886 	if (mnttab_hash == NULL) {
6887 		(void) fclose(tfp);
6888 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
6889 		bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn);
6890 		return (-1);
6891 	}
6892 
6893 	(void) snprintf(tmpmnt, sizeof (tmpmnt),
6894 	    "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6895 	(void) unlink(tmpmnt);
6896 
6897 	ret = mkdirp(tmpmnt, DIR_PERMS);
6898 	error = errno;
6899 	INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6900 	if (ret == -1) {
6901 		bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
6902 		    strerror(error));
6903 		free_mnttab(mnttab_hash);
6904 		(void) fclose(tfp);
6905 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
6906 		return (-1);
6907 	}
6908 
6909 	dirp = opendir("/dev/rdsk");
6910 	error = errno;
6911 	INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6912 	if (dirp == NULL) {
6913 		bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
6914 		    strerror(error));
6915 		goto fail;
6916 	}
6917 
6918 	while ((dp = readdir(dirp)) != NULL) {
6919 		if (strcmp(dp->d_name, ".") == 0 ||
6920 		    strcmp(dp->d_name, "..") == 0)
6921 			continue;
6922 
6923 		/*
6924 		 * we only look for the s0 slice. This is guranteed to
6925 		 * have 's' at len - 2.
6926 		 */
6927 		len = strlen(dp->d_name);
6928 		if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6929 			BAM_DPRINTF(("%s: skipping non-s0 slice: %s\n",
6930 			    fcn, dp->d_name));
6931 			continue;
6932 		}
6933 
6934 		ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6935 		INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6936 		if (ret == -1)
6937 			goto fail;
6938 	}
6939 
6940 	(void) closedir(dirp);
6941 	free_mnttab(mnttab_hash);
6942 	(void) rmdir(tmpmnt);
6943 
6944 	ret = fclose(tfp);
6945 	error = errno;
6946 	INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6947 	if (ret == EOF) {
6948 		bam_error(_("failed to close file: %s: %s\n"),
6949 		    UFS_SIGNATURE_LIST".tmp", strerror(error));
6950 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
6951 		return (-1);
6952 	}
6953 
6954 	/* We have a list of existing GRUB signatures. Sort it first */
6955 	(void) snprintf(cmd, sizeof (cmd),
6956 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
6957 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6958 
6959 	ret = exec_cmd(cmd, NULL);
6960 	INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6961 	if (ret != 0) {
6962 		bam_error(_("error sorting GRUB UFS boot signatures\n"));
6963 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
6964 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
6965 		return (-1);
6966 	}
6967 
6968 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
6969 
6970 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6971 	error = errno;
6972 	INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6973 	if (ret == -1) {
6974 		bam_error(_("rename to file failed: %s: %s\n"),
6975 		    UFS_SIGNATURE_LIST, strerror(error));
6976 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
6977 		return (-1);
6978 	}
6979 
6980 	if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6981 		BAM_DPRINTF(("%s: generated zero length signlist: %s.\n",
6982 		    fcn, UFS_SIGNATURE_LIST));
6983 	}
6984 
6985 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
6986 	return (0);
6987 
6988 fail:
6989 	if (dirp)
6990 		(void) closedir(dirp);
6991 	free_mnttab(mnttab_hash);
6992 	(void) rmdir(tmpmnt);
6993 	(void) fclose(tfp);
6994 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
6995 	BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6996 	return (-1);
6997 }
6998 
6999 static char *
7000 create_ufs_sign(void)
7001 {
7002 	struct stat	sb;
7003 	int		signnum = -1;
7004 	char		tmpsign[MAXNAMELEN + 1];
7005 	char		*numstr;
7006 	int		i;
7007 	FILE		*tfp;
7008 	int		ret;
7009 	int		error;
7010 	const char	*fcn = "create_ufs_sign()";
7011 
7012 	bam_print(_("  - searching for UFS boot signatures\n"));
7013 
7014 	ret = FindAllUfsSignatures();
7015 	INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
7016 	if (ret == -1) {
7017 		bam_error(_("search for UFS boot signatures failed\n"));
7018 		return (NULL);
7019 	}
7020 
7021 	/* Make sure the list exists and is owned by root */
7022 	INJECT_ERROR1("SIGNLIST_NOT_CREATED",
7023 	    (void) unlink(UFS_SIGNATURE_LIST));
7024 	if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
7025 		(void) unlink(UFS_SIGNATURE_LIST);
7026 		bam_error(_("missing UFS signature list file: %s\n"),
7027 		    UFS_SIGNATURE_LIST);
7028 		return (NULL);
7029 	}
7030 
7031 	if (sb.st_size == 0) {
7032 		bam_print(_("   - no existing UFS boot signatures\n"));
7033 		i = 0;
7034 		goto found;
7035 	}
7036 
7037 	/* The signature list was sorted when it was created */
7038 	tfp = fopen(UFS_SIGNATURE_LIST, "r");
7039 	error = errno;
7040 	INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
7041 	if (tfp == NULL) {
7042 		bam_error(_("error opening UFS boot signature list "
7043 		    "file %s: %s\n"), UFS_SIGNATURE_LIST, strerror(error));
7044 		(void) unlink(UFS_SIGNATURE_LIST);
7045 		return (NULL);
7046 	}
7047 
7048 	for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
7049 
7050 		if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
7051 		    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7052 			(void) fclose(tfp);
7053 			(void) unlink(UFS_SIGNATURE_LIST);
7054 			bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7055 			return (NULL);
7056 		}
7057 		numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
7058 
7059 		if (numstr[0] == '\0' || !isdigit(numstr[0])) {
7060 			(void) fclose(tfp);
7061 			(void) unlink(UFS_SIGNATURE_LIST);
7062 			bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7063 			return (NULL);
7064 		}
7065 
7066 		signnum = atoi(numstr);
7067 		INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
7068 		if (signnum < 0) {
7069 			(void) fclose(tfp);
7070 			(void) unlink(UFS_SIGNATURE_LIST);
7071 			bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7072 			return (NULL);
7073 		}
7074 
7075 		if (i != signnum) {
7076 			BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7077 			    fcn, i));
7078 			break;
7079 		}
7080 	}
7081 
7082 	(void) fclose(tfp);
7083 
7084 found:
7085 	(void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
7086 
7087 	/* add the ufs signature to the /var/run list of signatures */
7088 	ret = ufs_add_to_sign_list(tmpsign);
7089 	INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
7090 	if (ret == -1) {
7091 		(void) unlink(UFS_SIGNATURE_LIST);
7092 		bam_error(_("failed to add sign %s to signlist.\n"), tmpsign);
7093 		return (NULL);
7094 	}
7095 
7096 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7097 
7098 	return (s_strdup(tmpsign));
7099 }
7100 
7101 static char *
7102 get_fstype(char *osroot)
7103 {
7104 	FILE		*mntfp;
7105 	struct mnttab	mp = {0};
7106 	struct mnttab	mpref = {0};
7107 	int		error;
7108 	int		ret;
7109 	const char	*fcn = "get_fstype()";
7110 
7111 	INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
7112 	if (osroot == NULL) {
7113 		bam_error(_("no OS mountpoint. Cannot determine fstype\n"));
7114 		return (NULL);
7115 	}
7116 
7117 	mntfp = fopen(MNTTAB, "r");
7118 	error = errno;
7119 	INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
7120 	if (mntfp == NULL) {
7121 		bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7122 		    strerror(error));
7123 		return (NULL);
7124 	}
7125 
7126 	if (*osroot == '\0')
7127 		mpref.mnt_mountp = "/";
7128 	else
7129 		mpref.mnt_mountp = osroot;
7130 
7131 	ret = getmntany(mntfp, &mp, &mpref);
7132 	INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
7133 	if (ret != 0) {
7134 		bam_error(_("failed to find OS mountpoint %s in %s\n"),
7135 		    osroot, MNTTAB);
7136 		(void) fclose(mntfp);
7137 		return (NULL);
7138 	}
7139 	(void) fclose(mntfp);
7140 
7141 	INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
7142 	if (mp.mnt_fstype == NULL) {
7143 		bam_error(_("NULL fstype found for OS root %s\n"), osroot);
7144 		return (NULL);
7145 	}
7146 
7147 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7148 
7149 	return (s_strdup(mp.mnt_fstype));
7150 }
7151 
7152 static char *
7153 create_zfs_sign(char *osdev)
7154 {
7155 	char		tmpsign[PATH_MAX];
7156 	char		*pool;
7157 	const char	*fcn = "create_zfs_sign()";
7158 
7159 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, osdev));
7160 
7161 	/*
7162 	 * First find the pool name
7163 	 */
7164 	pool = get_pool(osdev);
7165 	INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
7166 	if (pool == NULL) {
7167 		bam_error(_("failed to get pool name from %s\n"), osdev);
7168 		return (NULL);
7169 	}
7170 
7171 	(void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
7172 
7173 	BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn, tmpsign));
7174 
7175 	free(pool);
7176 
7177 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7178 
7179 	return (s_strdup(tmpsign));
7180 }
7181 
7182 static char *
7183 create_new_sign(char *osdev, char *fstype)
7184 {
7185 	char		*sign;
7186 	const char	*fcn = "create_new_sign()";
7187 
7188 	INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
7189 
7190 	if (strcmp(fstype, "zfs") == 0) {
7191 		BAM_DPRINTF(("%s: created new ZFS sign\n", fcn));
7192 		sign = create_zfs_sign(osdev);
7193 	} else if (strcmp(fstype, "ufs") == 0) {
7194 		BAM_DPRINTF(("%s: created new UFS sign\n", fcn));
7195 		sign = create_ufs_sign();
7196 	} else {
7197 		bam_error(_("boot signature not supported for fstype: %s\n"),
7198 		    fstype);
7199 		sign = NULL;
7200 	}
7201 
7202 	BAM_DPRINTF(("%s: created new sign: %s\n", fcn,
7203 	    sign ? sign : "<NULL>"));
7204 	return (sign);
7205 }
7206 
7207 static int
7208 set_backup_common(char *mntpt, char *sign)
7209 {
7210 	FILE		*bfp;
7211 	char		backup[PATH_MAX];
7212 	char		tmpsign[PATH_MAX];
7213 	int		error;
7214 	char		*bdir;
7215 	char		*backup_dup;
7216 	struct stat	sb;
7217 	int		ret;
7218 	const char	*fcn = "set_backup_common()";
7219 
7220 	(void) snprintf(backup, sizeof (backup), "%s%s",
7221 	    mntpt, GRUBSIGN_BACKUP);
7222 
7223 	/* First read the backup */
7224 	bfp = fopen(backup, "r");
7225 	if (bfp != NULL) {
7226 		while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
7227 			if (strcmp(tmpsign, sign) == 0) {
7228 				BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7229 				    fcn, sign));
7230 				(void) fclose(bfp);
7231 				return (0);
7232 			}
7233 		}
7234 		(void) fclose(bfp);
7235 		BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7236 		    fcn, sign));
7237 	} else {
7238 		BAM_DPRINTF(("%s: no backup file (%s) found.\n", fcn, backup));
7239 	}
7240 
7241 	/*
7242 	 * Didn't find the correct signature. First create
7243 	 * the directory if necessary.
7244 	 */
7245 
7246 	/* dirname() modifies its argument so dup it */
7247 	backup_dup = s_strdup(backup);
7248 	bdir = dirname(backup_dup);
7249 	assert(bdir);
7250 
7251 	ret = stat(bdir, &sb);
7252 	INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
7253 	if (ret == -1) {
7254 		BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7255 		    fcn, bdir));
7256 		ret = mkdirp(bdir, DIR_PERMS);
7257 		error = errno;
7258 		INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
7259 		if (ret == -1) {
7260 			bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7261 			    GRUBSIGN_BACKUP, strerror(error));
7262 			free(backup_dup);
7263 			return (-1);
7264 		}
7265 	}
7266 	free(backup_dup);
7267 
7268 	/*
7269 	 * Open the backup in append mode to add the correct
7270 	 * signature;
7271 	 */
7272 	bfp = fopen(backup, "a");
7273 	error = errno;
7274 	INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
7275 	if (bfp == NULL) {
7276 		bam_error(_("error opening boot signature backup "
7277 		    "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7278 		return (-1);
7279 	}
7280 
7281 	(void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
7282 
7283 	ret = fputs(tmpsign, bfp);
7284 	error = errno;
7285 	INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
7286 	if (ret != strlen(tmpsign)) {
7287 		bam_error(_("error writing boot signature backup "
7288 		    "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7289 		(void) fclose(bfp);
7290 		return (-1);
7291 	}
7292 
7293 	(void) fclose(bfp);
7294 
7295 	if (bam_verbose)
7296 		bam_print(_("updated boot signature backup file %s\n"),
7297 		    GRUBSIGN_BACKUP);
7298 
7299 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7300 
7301 	return (0);
7302 }
7303 
7304 static int
7305 set_backup_ufs(char *osroot, char *sign)
7306 {
7307 	const char	*fcn = "set_backup_ufs()";
7308 
7309 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7310 	return (set_backup_common(osroot, sign));
7311 }
7312 
7313 static int
7314 set_backup_zfs(char *osdev, char *sign)
7315 {
7316 	char		*pool;
7317 	char		*mntpt;
7318 	zfs_mnted_t	mnted;
7319 	int		ret;
7320 	const char	*fcn = "set_backup_zfs()";
7321 
7322 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7323 
7324 	pool = get_pool(osdev);
7325 	INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7326 	if (pool == NULL) {
7327 		bam_error(_("failed to get pool name from %s\n"), osdev);
7328 		return (-1);
7329 	}
7330 
7331 	mntpt = mount_top_dataset(pool, &mnted);
7332 	INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7333 	if (mntpt == NULL) {
7334 		bam_error(_("failed to mount top dataset for %s\n"), pool);
7335 		free(pool);
7336 		return (-1);
7337 	}
7338 
7339 	ret = set_backup_common(mntpt, sign);
7340 
7341 	(void) umount_top_dataset(pool, mnted, mntpt);
7342 
7343 	free(pool);
7344 
7345 	INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7346 	if (ret == 0) {
7347 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7348 	} else {
7349 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7350 	}
7351 
7352 	return (ret);
7353 }
7354 
7355 static int
7356 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7357 {
7358 	const char	*fcn = "set_backup()";
7359 	int		ret;
7360 
7361 	INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7362 
7363 	if (strcmp(fstype, "ufs") == 0) {
7364 		BAM_DPRINTF(("%s: setting UFS backup sign\n", fcn));
7365 		ret = set_backup_ufs(osroot, sign);
7366 	} else if (strcmp(fstype, "zfs") == 0) {
7367 		BAM_DPRINTF(("%s: setting ZFS backup sign\n", fcn));
7368 		ret = set_backup_zfs(osdev, sign);
7369 	} else {
7370 		bam_error(_("boot signature not supported for fstype: %s\n"),
7371 		    fstype);
7372 		ret = -1;
7373 	}
7374 
7375 	if (ret == 0) {
7376 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7377 	} else {
7378 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7379 	}
7380 
7381 	return (ret);
7382 }
7383 
7384 static int
7385 set_primary_common(char *mntpt, char *sign)
7386 {
7387 	char		signfile[PATH_MAX];
7388 	char		signdir[PATH_MAX];
7389 	struct stat	sb;
7390 	int		fd;
7391 	int		error;
7392 	int		ret;
7393 	const char	*fcn = "set_primary_common()";
7394 
7395 	(void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7396 	    mntpt, GRUBSIGN_DIR, sign);
7397 
7398 	if (stat(signfile, &sb) != -1) {
7399 		if (bam_verbose)
7400 			bam_print(_("primary sign %s exists\n"), sign);
7401 		return (0);
7402 	} else {
7403 		BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7404 		    fcn, signfile));
7405 	}
7406 
7407 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
7408 	    mntpt, GRUBSIGN_DIR);
7409 
7410 	if (stat(signdir, &sb) == -1) {
7411 		BAM_DPRINTF(("%s: primary signdir (%s) does not exist\n",
7412 		    fcn, signdir));
7413 		ret = mkdirp(signdir, DIR_PERMS);
7414 		error = errno;
7415 		INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7416 		if (ret == -1) {
7417 			bam_error(_("error creating boot signature "
7418 			    "directory %s: %s\n"), signdir, strerror(errno));
7419 			return (-1);
7420 		}
7421 	}
7422 
7423 	fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7424 	error = errno;
7425 	INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7426 	if (fd == -1) {
7427 		bam_error(_("error creating primary boot signature %s: %s\n"),
7428 		    signfile, strerror(error));
7429 		return (-1);
7430 	}
7431 
7432 	ret = fsync(fd);
7433 	error = errno;
7434 	INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7435 	if (ret != 0) {
7436 		bam_error(_("error syncing primary boot signature %s: %s\n"),
7437 		    signfile, strerror(error));
7438 	}
7439 
7440 	(void) close(fd);
7441 
7442 	if (bam_verbose)
7443 		bam_print(_("created primary GRUB boot signature: %s\n"),
7444 		    signfile);
7445 
7446 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7447 
7448 	return (0);
7449 }
7450 
7451 static int
7452 set_primary_ufs(char *osroot, char *sign)
7453 {
7454 	const char	*fcn = "set_primary_ufs()";
7455 
7456 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7457 	return (set_primary_common(osroot, sign));
7458 }
7459 
7460 static int
7461 set_primary_zfs(char *osdev, char *sign)
7462 {
7463 	char		*pool;
7464 	char		*mntpt;
7465 	zfs_mnted_t	mnted;
7466 	int		ret;
7467 	const char	*fcn = "set_primary_zfs()";
7468 
7469 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7470 
7471 	pool = get_pool(osdev);
7472 	INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7473 	if (pool == NULL) {
7474 		bam_error(_("failed to get pool name from %s\n"), osdev);
7475 		return (-1);
7476 	}
7477 
7478 	/* Pool name must exist in the sign */
7479 	ret = (strstr(sign, pool) != NULL);
7480 	INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7481 	if (ret == 0) {
7482 		bam_error(_("pool name %s not present in signature %s\n"),
7483 		    pool, sign);
7484 		free(pool);
7485 		return (-1);
7486 	}
7487 
7488 	mntpt = mount_top_dataset(pool, &mnted);
7489 	INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7490 	if (mntpt == NULL) {
7491 		bam_error(_("failed to mount top dataset for %s\n"), pool);
7492 		free(pool);
7493 		return (-1);
7494 	}
7495 
7496 	ret = set_primary_common(mntpt, sign);
7497 
7498 	(void) umount_top_dataset(pool, mnted, mntpt);
7499 
7500 	free(pool);
7501 
7502 	INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7503 	if (ret == 0) {
7504 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7505 	} else {
7506 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7507 	}
7508 
7509 	return (ret);
7510 }
7511 
7512 static int
7513 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7514 {
7515 	const char	*fcn = "set_primary()";
7516 	int		ret;
7517 
7518 	INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7519 	if (strcmp(fstype, "ufs") == 0) {
7520 		BAM_DPRINTF(("%s: setting UFS primary sign\n", fcn));
7521 		ret = set_primary_ufs(osroot, sign);
7522 	} else if (strcmp(fstype, "zfs") == 0) {
7523 		BAM_DPRINTF(("%s: setting ZFS primary sign\n", fcn));
7524 		ret = set_primary_zfs(osdev, sign);
7525 	} else {
7526 		bam_error(_("boot signature not supported for fstype: %s\n"),
7527 		    fstype);
7528 		ret = -1;
7529 	}
7530 
7531 	if (ret == 0) {
7532 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7533 	} else {
7534 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7535 	}
7536 
7537 	return (ret);
7538 }
7539 
7540 static int
7541 ufs_add_to_sign_list(char *sign)
7542 {
7543 	FILE		*tfp;
7544 	char		signline[MAXNAMELEN];
7545 	char		cmd[PATH_MAX];
7546 	int		ret;
7547 	int		error;
7548 	const char	*fcn = "ufs_add_to_sign_list()";
7549 
7550 	INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7551 	if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7552 	    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7553 		bam_error(_("invalid UFS boot signature %s\n"), sign);
7554 		(void) unlink(UFS_SIGNATURE_LIST);
7555 		return (-1);
7556 	}
7557 
7558 	/*
7559 	 * most failures in this routine are not a fatal error
7560 	 * We simply unlink the /var/run file and continue
7561 	 */
7562 
7563 	ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7564 	error = errno;
7565 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7566 	if (ret == -1) {
7567 		bam_error(_("rename to file failed: %s: %s\n"),
7568 		    UFS_SIGNATURE_LIST".tmp", strerror(error));
7569 		(void) unlink(UFS_SIGNATURE_LIST);
7570 		return (0);
7571 	}
7572 
7573 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7574 	error = errno;
7575 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7576 	if (tfp == NULL) {
7577 		bam_error(_("failed to open file: %s: %s\n"),
7578 		    UFS_SIGNATURE_LIST".tmp", strerror(error));
7579 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
7580 		return (0);
7581 	}
7582 
7583 	(void) snprintf(signline, sizeof (signline), "%s\n", sign);
7584 
7585 	ret = fputs(signline, tfp);
7586 	error = errno;
7587 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7588 	if (ret != strlen(signline)) {
7589 		bam_error(_("failed to write signature %s to signature "
7590 		    "list: %s\n"), sign, strerror(error));
7591 		(void) fclose(tfp);
7592 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
7593 		return (0);
7594 	}
7595 
7596 	ret = fclose(tfp);
7597 	error = errno;
7598 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7599 	if (ret == EOF) {
7600 		bam_error(_("failed to close file: %s: %s\n"),
7601 		    UFS_SIGNATURE_LIST".tmp", strerror(error));
7602 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
7603 		return (0);
7604 	}
7605 
7606 	/* Sort the list again */
7607 	(void) snprintf(cmd, sizeof (cmd),
7608 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
7609 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7610 
7611 	ret = exec_cmd(cmd, NULL);
7612 	INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7613 	if (ret != 0) {
7614 		bam_error(_("error sorting GRUB UFS boot signatures\n"));
7615 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
7616 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
7617 		return (0);
7618 	}
7619 
7620 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
7621 
7622 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7623 	error = errno;
7624 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7625 	if (ret == -1) {
7626 		bam_error(_("rename to file failed: %s: %s\n"),
7627 		    UFS_SIGNATURE_LIST, strerror(error));
7628 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
7629 		return (0);
7630 	}
7631 
7632 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7633 
7634 	return (0);
7635 }
7636 
7637 static int
7638 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7639 {
7640 	int		ret;
7641 	const char	*fcn = "set_signature()";
7642 
7643 	BAM_DPRINTF(("%s: entered. args: %s %s %s %s\n", fcn,
7644 	    osroot, osdev, sign, fstype));
7645 
7646 	ret = set_backup(osroot, osdev, sign, fstype);
7647 	INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7648 	if (ret == -1) {
7649 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7650 		bam_error(_("failed to set backup sign (%s) for %s: %s\n"),
7651 		    sign, osroot, osdev);
7652 		return (-1);
7653 	}
7654 
7655 	ret = set_primary(osroot, osdev, sign, fstype);
7656 	INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7657 
7658 	if (ret == 0) {
7659 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7660 	} else {
7661 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7662 		bam_error(_("failed to set primary sign (%s) for %s: %s\n"),
7663 		    sign, osroot, osdev);
7664 
7665 	}
7666 	return (ret);
7667 }
7668 
7669 char *
7670 get_grubsign(char *osroot, char *osdev)
7671 {
7672 	char		*grubsign;	/* (<sign>,#,#) */
7673 	char		*slice;
7674 	int		fdiskpart;
7675 	char		*sign;
7676 	char		*fstype;
7677 	int		ret;
7678 	const char	*fcn = "get_grubsign()";
7679 
7680 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, osdev));
7681 	fstype = get_fstype(osroot);
7682 	INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7683 	if (fstype == NULL) {
7684 		bam_error(_("failed to get fstype for %s\n"), osroot);
7685 		return (NULL);
7686 	}
7687 
7688 	sign = find_existing_sign(osroot, osdev, fstype);
7689 	INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7690 	if (sign == NULL) {
7691 		BAM_DPRINTF(("%s: no existing grubsign for %s: %s\n",
7692 		    fcn, osroot, osdev));
7693 		sign = create_new_sign(osdev, fstype);
7694 		INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7695 		if (sign == NULL) {
7696 			bam_error(_("failed to create GRUB boot signature for "
7697 			    "device: %s\n"), osdev);
7698 			free(fstype);
7699 			return (NULL);
7700 		}
7701 	}
7702 
7703 	ret = set_signature(osroot, osdev, sign, fstype);
7704 	INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7705 	if (ret == -1) {
7706 		bam_error(_("failed to write GRUB boot signature for "
7707 		    "device: %s\n"), osdev);
7708 		free(sign);
7709 		free(fstype);
7710 		(void) unlink(UFS_SIGNATURE_LIST);
7711 		return (NULL);
7712 	}
7713 
7714 	free(fstype);
7715 
7716 	if (bam_verbose)
7717 		bam_print(_("found or created GRUB signature %s for %s\n"),
7718 		    sign, osdev);
7719 
7720 	fdiskpart = get_partition(osdev);
7721 	INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7722 	if (fdiskpart == PARTNO_NOTFOUND) {
7723 		bam_error(_("failed to determine fdisk partition: %s\n"),
7724 		    osdev);
7725 		free(sign);
7726 		return (NULL);
7727 	}
7728 
7729 	slice = strrchr(osdev, 's');
7730 
7731 	if (fdiskpart == PARTNO_EFI) {
7732 		fdiskpart = atoi(&slice[1]);
7733 		slice = NULL;
7734 	}
7735 
7736 	grubsign = s_calloc(1, MAXNAMELEN + 10);
7737 	if (slice) {
7738 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7739 		    sign, fdiskpart, slice[1] + 'a' - '0');
7740 	} else
7741 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7742 		    sign, fdiskpart);
7743 
7744 	free(sign);
7745 
7746 	BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn, grubsign));
7747 
7748 	return (grubsign);
7749 }
7750 
7751 static char *
7752 get_title(char *rootdir)
7753 {
7754 	static char	title[80];
7755 	char		*cp = NULL;
7756 	char		release[PATH_MAX];
7757 	FILE		*fp;
7758 	const char	*fcn = "get_title()";
7759 
7760 	/* open the /etc/release file */
7761 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7762 
7763 	fp = fopen(release, "r");
7764 	if (fp == NULL) {
7765 		bam_error(_("failed to open file: %s: %s\n"), release,
7766 		    strerror(errno));
7767 		cp = NULL;
7768 		goto out;
7769 	}
7770 
7771 	/* grab first line of /etc/release */
7772 	cp = s_fgets(title, sizeof (title), fp);
7773 	if (cp) {
7774 		while (isspace(*cp))    /* remove leading spaces */
7775 			cp++;
7776 	}
7777 
7778 	(void) fclose(fp);
7779 
7780 out:
7781 	cp = cp ? cp : "Oracle Solaris";
7782 
7783 	BAM_DPRINTF(("%s: got title: %s\n", fcn, cp));
7784 
7785 	return (cp);
7786 }
7787 
7788 char *
7789 get_special(char *mountp)
7790 {
7791 	FILE		*mntfp;
7792 	struct mnttab	mp = {0};
7793 	struct mnttab	mpref = {0};
7794 	int		error;
7795 	int		ret;
7796 	const char	*fcn = "get_special()";
7797 
7798 	INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7799 	if (mountp == NULL) {
7800 		bam_error(_("cannot get special file: NULL mount-point\n"));
7801 		return (NULL);
7802 	}
7803 
7804 	mntfp = fopen(MNTTAB, "r");
7805 	error = errno;
7806 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7807 	if (mntfp == NULL) {
7808 		bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7809 		    strerror(error));
7810 		return (NULL);
7811 	}
7812 
7813 	if (*mountp == '\0')
7814 		mpref.mnt_mountp = "/";
7815 	else
7816 		mpref.mnt_mountp = mountp;
7817 
7818 	ret = getmntany(mntfp, &mp, &mpref);
7819 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7820 	if (ret != 0) {
7821 		(void) fclose(mntfp);
7822 		BAM_DPRINTF(("%s: Cannot get special file:  mount-point %s "
7823 		    "not in mnttab\n", fcn, mountp));
7824 		return (NULL);
7825 	}
7826 	(void) fclose(mntfp);
7827 
7828 	BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
7829 
7830 	return (s_strdup(mp.mnt_special));
7831 }
7832 
7833 static void
7834 free_physarray(char **physarray, int n)
7835 {
7836 	int			i;
7837 	const char		*fcn = "free_physarray()";
7838 
7839 	assert(physarray);
7840 	assert(n);
7841 
7842 	BAM_DPRINTF(("%s: entering args: %d\n", fcn, n));
7843 
7844 	for (i = 0; i < n; i++) {
7845 		free(physarray[i]);
7846 	}
7847 	free(physarray);
7848 
7849 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7850 }
7851 
7852 static int
7853 zfs_get_physical(char *special, char ***physarray, int *n)
7854 {
7855 	char			sdup[PATH_MAX];
7856 	char			cmd[PATH_MAX];
7857 	char			dsk[PATH_MAX];
7858 	char			*pool;
7859 	filelist_t		flist = {0};
7860 	line_t			*lp;
7861 	line_t			*startlp;
7862 	char			*comp1;
7863 	int			i;
7864 	int			ret;
7865 	const char		*fcn = "zfs_get_physical()";
7866 
7867 	assert(special);
7868 
7869 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, special));
7870 
7871 	INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7872 	if (special[0] == '/') {
7873 		bam_error(_("invalid device for ZFS filesystem: %s\n"),
7874 		    special);
7875 		return (-1);
7876 	}
7877 
7878 	(void) strlcpy(sdup, special, sizeof (sdup));
7879 
7880 	pool = strtok(sdup, "/");
7881 	INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7882 	if (pool == NULL) {
7883 		bam_error(_("cannot derive ZFS pool from special: %s\n"),
7884 		    special);
7885 		return (-1);
7886 	}
7887 
7888 	(void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7889 
7890 	ret = exec_cmd(cmd, &flist);
7891 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7892 	if (ret != 0) {
7893 		bam_error(_("cannot get zpool status for pool: %s\n"), pool);
7894 		return (-1);
7895 	}
7896 
7897 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7898 	if (flist.head == NULL) {
7899 		bam_error(_("bad zpool status for pool=%s\n"), pool);
7900 		filelist_free(&flist);
7901 		return (-1);
7902 	}
7903 
7904 	for (lp = flist.head; lp; lp = lp->next) {
7905 		BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
7906 		    fcn, lp->line));
7907 		comp1 = strtok(lp->line, " \t");
7908 		if (comp1 == NULL) {
7909 			free(lp->line);
7910 			lp->line = NULL;
7911 		} else {
7912 			comp1 = s_strdup(comp1);
7913 			free(lp->line);
7914 			lp->line = comp1;
7915 		}
7916 	}
7917 
7918 	for (lp = flist.head; lp; lp = lp->next) {
7919 		if (lp->line == NULL)
7920 			continue;
7921 		if (strcmp(lp->line, pool) == 0) {
7922 			BAM_DPRINTF(("%s: found pool name: %s in zpool "
7923 			    "status\n", fcn, pool));
7924 			break;
7925 		}
7926 	}
7927 
7928 	if (lp == NULL) {
7929 		bam_error(_("no pool name %s in zpool status\n"), pool);
7930 		filelist_free(&flist);
7931 		return (-1);
7932 	}
7933 
7934 	startlp = lp->next;
7935 	for (i = 0, lp = startlp; lp; lp = lp->next) {
7936 		if (lp->line == NULL)
7937 			continue;
7938 		if (strcmp(lp->line, "mirror") == 0)
7939 			continue;
7940 		if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7941 			break;
7942 		i++;
7943 		BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
7944 		    fcn, i));
7945 	}
7946 
7947 	if (i == 0) {
7948 		bam_error(_("no physical device in zpool status for pool=%s\n"),
7949 		    pool);
7950 		filelist_free(&flist);
7951 		return (-1);
7952 	}
7953 
7954 	*n = i;
7955 	*physarray = s_calloc(*n, sizeof (char *));
7956 	for (i = 0, lp = startlp; lp; lp = lp->next) {
7957 		if (lp->line == NULL)
7958 			continue;
7959 		if (strcmp(lp->line, "mirror") == 0)
7960 			continue;
7961 		if (strcmp(lp->line, "errors:") == 0)
7962 			break;
7963 		if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7964 		    strncmp(lp->line, "/dev/rdsk/",
7965 		    strlen("/dev/rdsk/")) != 0)  {
7966 			(void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7967 			    lp->line);
7968 		} else {
7969 			(void) strlcpy(dsk, lp->line, sizeof (dsk));
7970 		}
7971 		BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
7972 		    fcn, dsk, pool));
7973 		(*physarray)[i++] = s_strdup(dsk);
7974 	}
7975 
7976 	assert(i == *n);
7977 
7978 	filelist_free(&flist);
7979 
7980 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7981 	return (0);
7982 }
7983 
7984 static int
7985 get_physical(char *menu_root, char ***physarray, int *n)
7986 {
7987 	char			*special;
7988 	int			ret;
7989 	const char		*fcn = "get_physical()";
7990 
7991 	assert(menu_root);
7992 	assert(physarray);
7993 	assert(n);
7994 
7995 	*physarray = NULL;
7996 	*n = 0;
7997 
7998 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_root));
7999 
8000 	/* First get the device special file from /etc/mnttab */
8001 	special = get_special(menu_root);
8002 	INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
8003 	if (special == NULL) {
8004 		bam_error(_("cannot get special file for mount-point: %s\n"),
8005 		    menu_root);
8006 		return (-1);
8007 	}
8008 
8009 	/* If already a physical device nothing to do */
8010 	if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
8011 	    strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
8012 		BAM_DPRINTF(("%s: got physical device already directly for "
8013 		    "menu_root=%s special=%s\n", fcn, menu_root, special));
8014 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8015 		*physarray = s_calloc(1, sizeof (char *));
8016 		(*physarray)[0] = special;
8017 		*n = 1;
8018 		return (0);
8019 	}
8020 
8021 	if (is_zfs(menu_root)) {
8022 		ret = zfs_get_physical(special, physarray, n);
8023 	} else {
8024 		bam_error(_("cannot derive physical device for %s (%s), "
8025 		    "unsupported filesystem\n"), menu_root, special);
8026 		ret = -1;
8027 	}
8028 
8029 	free(special);
8030 
8031 	INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
8032 	if (ret == -1) {
8033 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8034 	} else {
8035 		int	i;
8036 		assert (*n > 0);
8037 		for (i = 0; i < *n; i++) {
8038 			BAM_DPRINTF(("%s: returning physical=%s\n",
8039 			    fcn, (*physarray)[i]));
8040 		}
8041 	}
8042 
8043 	return (ret);
8044 }
8045 
8046 static int
8047 is_bootdisk(char *osroot, char *physical)
8048 {
8049 	int			ret;
8050 	char			*grubroot;
8051 	char			*bootp;
8052 	const char		*fcn = "is_bootdisk()";
8053 
8054 	assert(osroot);
8055 	assert(physical);
8056 
8057 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, physical));
8058 
8059 	bootp = strstr(physical, "p0:boot");
8060 	if (bootp)
8061 		*bootp = '\0';
8062 	/*
8063 	 * We just want the BIOS mapping for menu disk.
8064 	 * Don't pass menu_root to get_grubroot() as the
8065 	 * check that it is used for is not relevant here.
8066 	 * The osroot is immaterial as well - it is only used to
8067 	 * to find create_diskmap script. Everything hinges on
8068 	 * "physical"
8069 	 */
8070 	grubroot = get_grubroot(osroot, physical, NULL);
8071 
8072 	INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
8073 	if (grubroot == NULL) {
8074 		if (bam_verbose)
8075 			bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8076 			    "disk: %s\n"), physical);
8077 		return (0);
8078 	}
8079 	ret = grubroot[3] == '0';
8080 	free(grubroot);
8081 
8082 	BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
8083 
8084 	return (ret);
8085 }
8086 
8087 /*
8088  * Check if menu is on the boot device
8089  * Return 0 (false) on error
8090  */
8091 static int
8092 menu_on_bootdisk(char *osroot, char *menu_root)
8093 {
8094 	char		**physarray;
8095 	int		ret;
8096 	int		n;
8097 	int		i;
8098 	int		on_bootdisk;
8099 	const char	*fcn = "menu_on_bootdisk()";
8100 
8101 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8102 
8103 	ret = get_physical(menu_root, &physarray, &n);
8104 	INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
8105 	if (ret != 0) {
8106 		bam_error(_("cannot get physical device special file for menu "
8107 		    "root: %s\n"), menu_root);
8108 		return (0);
8109 	}
8110 
8111 	assert(physarray);
8112 	assert(n > 0);
8113 
8114 	on_bootdisk = 0;
8115 	for (i = 0; i < n; i++) {
8116 		assert(strncmp(physarray[i], "/dev/dsk/",
8117 		    strlen("/dev/dsk/")) == 0 ||
8118 		    strncmp(physarray[i], "/dev/rdsk/",
8119 		    strlen("/dev/rdsk/")) == 0);
8120 
8121 		BAM_DPRINTF(("%s: checking if phys-device=%s is on bootdisk\n",
8122 		    fcn, physarray[i]));
8123 		if (is_bootdisk(osroot, physarray[i])) {
8124 			on_bootdisk = 1;
8125 			BAM_DPRINTF(("%s: phys-device=%s *IS* on bootdisk\n",
8126 			    fcn, physarray[i]));
8127 		}
8128 	}
8129 
8130 	free_physarray(physarray, n);
8131 
8132 	INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8133 	INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8134 	if (on_bootdisk) {
8135 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8136 	} else {
8137 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8138 	}
8139 
8140 	return (on_bootdisk);
8141 }
8142 
8143 void
8144 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8145 {
8146 	const char	*fcn = "bam_add_line()";
8147 
8148 	assert(mp);
8149 	assert(entry);
8150 	assert(prev);
8151 	assert(lp);
8152 
8153 	lp->next = prev->next;
8154 	if (prev->next) {
8155 		BAM_DPRINTF(("%s: previous next exists\n", fcn));
8156 		prev->next->prev = lp;
8157 	} else {
8158 		BAM_DPRINTF(("%s: previous next does not exist\n", fcn));
8159 	}
8160 	prev->next = lp;
8161 	lp->prev = prev;
8162 
8163 	if (entry->end == prev) {
8164 		BAM_DPRINTF(("%s: last line in entry\n", fcn));
8165 		entry->end = lp;
8166 	}
8167 	if (mp->end == prev) {
8168 		assert(lp->next == NULL);
8169 		mp->end = lp;
8170 		BAM_DPRINTF(("%s: last line in menu\n", fcn));
8171 	}
8172 }
8173 
8174 /*
8175  * look for matching bootadm entry with specified parameters
8176  * Here are the rules (based on existing usage):
8177  * - If title is specified, match on title only
8178  * - Else, match on root/findroot, kernel, and module.
8179  *   Note that, if root_opt is non-zero, the absence of
8180  *   root line is considered a match.
8181  */
8182 static entry_t *
8183 find_boot_entry(
8184 	menu_t *mp,
8185 	char *title,
8186 	char *kernel,
8187 	char *findroot,
8188 	char *root,
8189 	char *module,
8190 	int root_opt,
8191 	int *entry_num)
8192 {
8193 	int		i;
8194 	line_t		*lp;
8195 	entry_t		*ent;
8196 	const char	*fcn = "find_boot_entry()";
8197 
8198 	if (entry_num)
8199 		*entry_num = BAM_ERROR;
8200 
8201 	/* find matching entry */
8202 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8203 		lp = ent->start;
8204 
8205 		/* first line of entry must be bootadm comment */
8206 		lp = ent->start;
8207 		if (lp->flags != BAM_COMMENT ||
8208 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8209 			continue;
8210 		}
8211 
8212 		/* advance to title line */
8213 		lp = lp->next;
8214 		if (title) {
8215 			if (lp->flags == BAM_TITLE && lp->arg &&
8216 			    strcmp(lp->arg, title) == 0) {
8217 				BAM_DPRINTF(("%s: matched title: %s\n",
8218 				    fcn, title));
8219 				break;
8220 			}
8221 			BAM_DPRINTF(("%s: no match title: %s, %s\n",
8222 			    fcn, title, lp->arg));
8223 			continue;	/* check title only */
8224 		}
8225 
8226 		lp = lp->next;	/* advance to root line */
8227 		if (lp == NULL) {
8228 			continue;
8229 		} else if (lp->cmd != NULL &&
8230 		    strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8231 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8232 			    findroot = NULL);
8233 			if (findroot == NULL) {
8234 				BAM_DPRINTF(("%s: no match line has findroot, "
8235 				    "we don't: %s\n", fcn, lp->arg));
8236 				continue;
8237 			}
8238 			/* findroot command found, try match  */
8239 			if (strcmp(lp->arg, findroot) != 0) {
8240 				BAM_DPRINTF(("%s: no match findroot: %s, %s\n",
8241 				    fcn, findroot, lp->arg));
8242 				continue;
8243 			}
8244 			BAM_DPRINTF(("%s: matched findroot: %s\n",
8245 			    fcn, findroot));
8246 			lp = lp->next;	/* advance to kernel line */
8247 		} else if (lp->cmd != NULL &&
8248 		    strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8249 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8250 			if (root == NULL) {
8251 				BAM_DPRINTF(("%s: no match, line has root, we "
8252 				    "don't: %s\n", fcn, lp->arg));
8253 				continue;
8254 			}
8255 			/* root cmd found, try match */
8256 			if (strcmp(lp->arg, root) != 0) {
8257 				BAM_DPRINTF(("%s: no match root: %s, %s\n",
8258 				    fcn, root, lp->arg));
8259 				continue;
8260 			}
8261 			BAM_DPRINTF(("%s: matched root: %s\n", fcn, root));
8262 			lp = lp->next;	/* advance to kernel line */
8263 		} else {
8264 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8265 			    root_opt = 0);
8266 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8267 			    root_opt = 1);
8268 			/* no root command, see if root is optional */
8269 			if (root_opt == 0) {
8270 				BAM_DPRINTF(("%s: root NOT optional\n", fcn));
8271 				continue;
8272 			}
8273 			BAM_DPRINTF(("%s: root IS optional\n", fcn));
8274 		}
8275 
8276 		if (lp == NULL || lp->next == NULL) {
8277 			continue;
8278 		}
8279 
8280 		if (kernel &&
8281 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8282 			if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8283 			    !(ent->flags & BAM_ENTRY_DBOOT) ||
8284 			    strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8285 				continue;
8286 
8287 			ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8288 
8289 		}
8290 		BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn,
8291 		    kernel, lp->arg));
8292 
8293 		/*
8294 		 * Check for matching module entry (failsafe or normal).
8295 		 * If it fails to match, we go around the loop again.
8296 		 * For xpv entries, there are two module lines, so we
8297 		 * do the check twice.
8298 		 */
8299 		lp = lp->next;	/* advance to module line */
8300 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8301 		    (((lp = lp->next) != NULL) &&
8302 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8303 			/* match found */
8304 			BAM_DPRINTF(("%s: module match: %s, %s\n", fcn,
8305 			    module, lp->arg));
8306 			break;
8307 		}
8308 
8309 		if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8310 		    (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8311 		    strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8312 			ent->flags |= BAM_ENTRY_UPGFSMODULE;
8313 			break;
8314 		}
8315 
8316 	}
8317 
8318 	if (ent && entry_num) {
8319 		*entry_num = i;
8320 	}
8321 
8322 	if (ent) {
8323 		BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8324 	} else {
8325 		BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_ERROR));
8326 	}
8327 	return (ent);
8328 }
8329 
8330 static int
8331 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8332     char *kernel, char *mod_kernel, char *module, int root_opt)
8333 {
8334 	int		i;
8335 	int		change_kernel = 0;
8336 	entry_t		*ent;
8337 	line_t		*lp;
8338 	line_t		*tlp;
8339 	char		linebuf[BAM_MAXLINE];
8340 	const char	*fcn = "update_boot_entry()";
8341 
8342 	/* note: don't match on title, it's updated on upgrade */
8343 	ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8344 	    root_opt, &i);
8345 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8346 		/*
8347 		 * We may be upgrading a kernel from multiboot to
8348 		 * directboot.  Look for a multiboot entry. A multiboot
8349 		 * entry will not have a findroot line.
8350 		 */
8351 		ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8352 		    MULTIBOOT_ARCHIVE, root_opt, &i);
8353 		if (ent != NULL) {
8354 			BAM_DPRINTF(("%s: upgrading entry from dboot to "
8355 			    "multiboot: root = %s\n", fcn, root));
8356 			change_kernel = 1;
8357 		}
8358 	} else if (ent) {
8359 		BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8360 		    fcn, findroot));
8361 	}
8362 
8363 	if (ent == NULL) {
8364 		BAM_DPRINTF(("%s: boot entry not found in menu. Creating "
8365 		    "new entry, findroot = %s\n", fcn, findroot));
8366 		return (add_boot_entry(mp, title, findroot,
8367 		    kernel, mod_kernel, module, NULL));
8368 	}
8369 
8370 	/* replace title of existing entry and update findroot line */
8371 	lp = ent->start;
8372 	lp = lp->next;	/* title line */
8373 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8374 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8375 	free(lp->arg);
8376 	free(lp->line);
8377 	lp->arg = s_strdup(title);
8378 	lp->line = s_strdup(linebuf);
8379 	BAM_DPRINTF(("%s: changing title to: %s\n", fcn, title));
8380 
8381 	tlp = lp;	/* title line */
8382 	lp = lp->next;	/* root line */
8383 
8384 	/* if no root or findroot command, create a new line_t */
8385 	if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8386 	    strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8387 		lp = s_calloc(1, sizeof (line_t));
8388 		bam_add_line(mp, ent, tlp, lp);
8389 	} else {
8390 		if (lp->cmd != NULL)
8391 			free(lp->cmd);
8392 
8393 		free(lp->sep);
8394 		free(lp->arg);
8395 		free(lp->line);
8396 	}
8397 
8398 	lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8399 	lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8400 	lp->arg = s_strdup(findroot);
8401 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8402 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8403 	lp->line = s_strdup(linebuf);
8404 	BAM_DPRINTF(("%s: adding findroot line: %s\n", fcn, findroot));
8405 
8406 	/* kernel line */
8407 	lp = lp->next;
8408 
8409 	if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8410 		char		*params = NULL;
8411 
8412 		params = strstr(lp->line, "-s");
8413 		if (params != NULL)
8414 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8415 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8416 			    kernel, params+2);
8417 		else
8418 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8419 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8420 			    kernel);
8421 
8422 		if (lp->cmd != NULL)
8423 			free(lp->cmd);
8424 
8425 		free(lp->arg);
8426 		free(lp->line);
8427 		lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8428 		lp->arg = s_strdup(strstr(linebuf, "/"));
8429 		lp->line = s_strdup(linebuf);
8430 		ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8431 		BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8432 		    fcn, lp->prev->cmd));
8433 	}
8434 
8435 	if (change_kernel) {
8436 		/*
8437 		 * We're upgrading from multiboot to directboot.
8438 		 */
8439 		if (lp->cmd != NULL &&
8440 		    strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8441 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8442 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8443 			    kernel);
8444 			free(lp->cmd);
8445 			free(lp->arg);
8446 			free(lp->line);
8447 			lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8448 			lp->arg = s_strdup(kernel);
8449 			lp->line = s_strdup(linebuf);
8450 			lp = lp->next;
8451 			BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8452 			    fcn, kernel));
8453 		}
8454 		if (lp->cmd != NULL &&
8455 		    strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8456 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8457 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8458 			    module);
8459 			free(lp->cmd);
8460 			free(lp->arg);
8461 			free(lp->line);
8462 			lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8463 			lp->arg = s_strdup(module);
8464 			lp->line = s_strdup(linebuf);
8465 			lp = lp->next;
8466 			BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8467 			    fcn, module));
8468 		}
8469 	}
8470 
8471 	/* module line */
8472 	lp = lp->next;
8473 
8474 	if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8475 		if (lp->cmd != NULL &&
8476 		    strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8477 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8478 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8479 			    module);
8480 			free(lp->cmd);
8481 			free(lp->arg);
8482 			free(lp->line);
8483 			lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8484 			lp->arg = s_strdup(module);
8485 			lp->line = s_strdup(linebuf);
8486 			lp = lp->next;
8487 			ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8488 			BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8489 			    fcn, module));
8490 		}
8491 	}
8492 
8493 	BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8494 	return (i);
8495 }
8496 
8497 int
8498 root_optional(char *osroot, char *menu_root)
8499 {
8500 	char			*ospecial;
8501 	char			*mspecial;
8502 	char			*slash;
8503 	int			root_opt;
8504 	int			ret1;
8505 	int			ret2;
8506 	const char		*fcn = "root_optional()";
8507 
8508 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8509 
8510 	/*
8511 	 * For all filesystems except ZFS, a straight compare of osroot
8512 	 * and menu_root will tell us if root is optional.
8513 	 * For ZFS, the situation is complicated by the fact that
8514 	 * menu_root and osroot are always different
8515 	 */
8516 	ret1 = is_zfs(osroot);
8517 	ret2 = is_zfs(menu_root);
8518 	INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8519 	if (!ret1 || !ret2) {
8520 		BAM_DPRINTF(("%s: one or more non-ZFS filesystems (%s, %s)\n",
8521 		    fcn, osroot, menu_root));
8522 		root_opt = (strcmp(osroot, menu_root) == 0);
8523 		goto out;
8524 	}
8525 
8526 	ospecial = get_special(osroot);
8527 	INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8528 	if (ospecial == NULL) {
8529 		bam_error(_("failed to get special file for osroot: %s\n"),
8530 		    osroot);
8531 		return (0);
8532 	}
8533 	BAM_DPRINTF(("%s: ospecial=%s for osroot=%s\n", fcn, ospecial, osroot));
8534 
8535 	mspecial = get_special(menu_root);
8536 	INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8537 	if (mspecial == NULL) {
8538 		bam_error(_("failed to get special file for menu_root: %s\n"),
8539 		    menu_root);
8540 		free(ospecial);
8541 		return (0);
8542 	}
8543 	BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8544 	    fcn, mspecial, menu_root));
8545 
8546 	slash = strchr(ospecial, '/');
8547 	if (slash)
8548 		*slash = '\0';
8549 	BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8550 	    fcn, ospecial, osroot));
8551 
8552 	root_opt = (strcmp(ospecial, mspecial) == 0);
8553 
8554 	free(ospecial);
8555 	free(mspecial);
8556 
8557 out:
8558 	INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8559 	INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8560 	if (root_opt) {
8561 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8562 	} else {
8563 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8564 	}
8565 
8566 	return (root_opt);
8567 }
8568 
8569 /*ARGSUSED*/
8570 static error_t
8571 update_entry(menu_t *mp, char *menu_root, char *osdev)
8572 {
8573 	int		entry;
8574 	char		*grubsign;
8575 	char		*grubroot;
8576 	char		*title;
8577 	char		osroot[PATH_MAX];
8578 	char		*failsafe_kernel = NULL;
8579 	struct stat	sbuf;
8580 	char		failsafe[256];
8581 	char		failsafe_64[256];
8582 	int		ret;
8583 	const char	*fcn = "update_entry()";
8584 
8585 	assert(mp);
8586 	assert(menu_root);
8587 	assert(osdev);
8588 	assert(bam_root);
8589 
8590 	BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, menu_root, osdev,
8591 	    bam_root));
8592 
8593 	(void) strlcpy(osroot, bam_root, sizeof (osroot));
8594 
8595 	title = get_title(osroot);
8596 	assert(title);
8597 
8598 	grubsign = get_grubsign(osroot, osdev);
8599 	INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8600 	if (grubsign == NULL) {
8601 		bam_error(_("failed to get grubsign for root: %s, device %s\n"),
8602 		    osroot, osdev);
8603 		return (BAM_ERROR);
8604 	}
8605 
8606 	/*
8607 	 * It is not a fatal error if get_grubroot() fails
8608 	 * We no longer rely on biosdev to populate the
8609 	 * menu
8610 	 */
8611 	grubroot = get_grubroot(osroot, osdev, menu_root);
8612 	INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8613 	if (grubroot) {
8614 		BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8615 		    "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8616 	} else {
8617 		BAM_DPRINTF(("%s: get_grubroot failed. osroot=%s, osdev=%s, "
8618 		    "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8619 	}
8620 
8621 	/* add the entry for normal Solaris */
8622 	INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8623 	    bam_direct = BAM_DIRECT_MULTIBOOT);
8624 	if (bam_direct == BAM_DIRECT_DBOOT) {
8625 		entry = update_boot_entry(mp, title, grubsign, grubroot,
8626 		    (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8627 		    NULL, DIRECT_BOOT_ARCHIVE,
8628 		    root_optional(osroot, menu_root));
8629 		BAM_DPRINTF(("%s: updated boot entry bam_zfs=%d, "
8630 		    "grubsign = %s\n", fcn, bam_zfs, grubsign));
8631 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8632 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8633 			    grubroot, XEN_MENU, bam_zfs ?
8634 			    XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8635 			    DIRECT_BOOT_ARCHIVE,
8636 			    root_optional(osroot, menu_root));
8637 			BAM_DPRINTF(("%s: updated HV entry bam_zfs=%d, "
8638 			    "grubsign = %s\n", fcn, bam_zfs, grubsign));
8639 		}
8640 	} else {
8641 		entry = update_boot_entry(mp, title, grubsign, grubroot,
8642 		    MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8643 		    root_optional(osroot, menu_root));
8644 
8645 		BAM_DPRINTF(("%s: updated MULTIBOOT entry grubsign = %s\n",
8646 		    fcn, grubsign));
8647 	}
8648 
8649 	/*
8650 	 * Add the entry for failsafe archive.  On a bfu'd system, the
8651 	 * failsafe may be different than the installed kernel.
8652 	 */
8653 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8654 	    osroot, FAILSAFE_ARCHIVE_32);
8655 	(void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8656 	    osroot, FAILSAFE_ARCHIVE_64);
8657 
8658 	/*
8659 	 * Check if at least one of the two archives exists
8660 	 * Using $ISADIR as the default line, we have an entry which works
8661 	 * for both the cases.
8662 	 */
8663 
8664 	if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8665 
8666 		/* Figure out where the kernel line should point */
8667 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8668 		    DIRECT_BOOT_FAILSAFE_32);
8669 		(void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8670 		    osroot, DIRECT_BOOT_FAILSAFE_64);
8671 		if (stat(failsafe, &sbuf) == 0 ||
8672 		    stat(failsafe_64, &sbuf) == 0) {
8673 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8674 		} else {
8675 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8676 			    osroot, MULTI_BOOT_FAILSAFE);
8677 			if (stat(failsafe, &sbuf) == 0) {
8678 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8679 			}
8680 		}
8681 		if (failsafe_kernel != NULL) {
8682 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8683 			    grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8684 			    root_optional(osroot, menu_root));
8685 			BAM_DPRINTF(("%s: updated FAILSAFE entry "
8686 			    "failsafe_kernel = %s\n", fcn, failsafe_kernel));
8687 		}
8688 	}
8689 	free(grubroot);
8690 
8691 	INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8692 	if (entry == BAM_ERROR) {
8693 		bam_error(_("failed to add boot entry with title=%s, grub "
8694 		    "signature=%s\n"), title, grubsign);
8695 		free(grubsign);
8696 		return (BAM_ERROR);
8697 	}
8698 	free(grubsign);
8699 
8700 	update_numbering(mp);
8701 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8702 	INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8703 	if (ret == BAM_ERROR) {
8704 		bam_error(_("failed to set GRUB menu default to %d\n"), entry);
8705 	}
8706 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8707 	return (BAM_WRITE);
8708 }
8709 
8710 static void
8711 save_default_entry(menu_t *mp, const char *which)
8712 {
8713 	int		lineNum;
8714 	int		entryNum;
8715 	int		entry = 0;	/* default is 0 */
8716 	char		linebuf[BAM_MAXLINE];
8717 	line_t		*lp = mp->curdefault;
8718 	const char	*fcn = "save_default_entry()";
8719 
8720 	if (mp->start) {
8721 		lineNum = mp->end->lineNum;
8722 		entryNum = mp->end->entryNum;
8723 	} else {
8724 		lineNum = LINE_INIT;
8725 		entryNum = ENTRY_INIT;
8726 	}
8727 
8728 	if (lp)
8729 		entry = s_strtol(lp->arg);
8730 
8731 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8732 	BAM_DPRINTF(("%s: saving default to: %s\n", fcn, linebuf));
8733 	line_parser(mp, linebuf, &lineNum, &entryNum);
8734 	BAM_DPRINTF(("%s: saved default to lineNum=%d, entryNum=%d\n", fcn,
8735 	    lineNum, entryNum));
8736 }
8737 
8738 static void
8739 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8740 {
8741 	int		entry;
8742 	char		*str;
8743 	const char	*fcn = "restore_default_entry()";
8744 
8745 	if (lp == NULL) {
8746 		BAM_DPRINTF(("%s: NULL saved default\n", fcn));
8747 		return;		/* nothing to restore */
8748 	}
8749 
8750 	BAM_DPRINTF(("%s: saved default string: %s\n", fcn, which));
8751 
8752 	str = lp->arg + strlen(which);
8753 	entry = s_strtol(str);
8754 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8755 
8756 	BAM_DPRINTF(("%s: restored default to entryNum: %d\n", fcn, entry));
8757 
8758 	/* delete saved old default line */
8759 	unlink_line(mp, lp);
8760 	line_free(lp);
8761 }
8762 
8763 /*
8764  * This function is for supporting reboot with args.
8765  * The opt value can be:
8766  * NULL		delete temp entry, if present
8767  * entry=<n>	switches default entry to <n>
8768  * else		treated as boot-args and setup a temperary menu entry
8769  *		and make it the default
8770  * Note that we are always rebooting the current OS instance
8771  * so osroot == / always.
8772  */
8773 #define	REBOOT_TITLE	"Solaris_reboot_transient"
8774 
8775 /*ARGSUSED*/
8776 static error_t
8777 update_temp(menu_t *mp, char *dummy, char *opt)
8778 {
8779 	int		entry;
8780 	char		*osdev;
8781 	char		*fstype;
8782 	char		*sign;
8783 	char		*opt_ptr;
8784 	char		*path;
8785 	char		kernbuf[BUFSIZ];
8786 	char		args_buf[BUFSIZ];
8787 	char		signbuf[PATH_MAX];
8788 	int		ret;
8789 	const char	*fcn = "update_temp()";
8790 
8791 	assert(mp);
8792 	assert(dummy == NULL);
8793 
8794 	/* opt can be NULL */
8795 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt ? opt : "<NULL>"));
8796 	BAM_DPRINTF(("%s: bam_alt_root: %d, bam_root: %s\n", fcn,
8797 	    bam_alt_root, bam_root));
8798 
8799 	if (bam_alt_root || bam_rootlen != 1 ||
8800 	    strcmp(bam_root, "/") != 0 ||
8801 	    strcmp(rootbuf, "/") != 0) {
8802 		bam_error(_("an alternate root (%s) cannot be used with this "
8803 		    "sub-command\n"), bam_root);
8804 		return (BAM_ERROR);
8805 	}
8806 
8807 	/* If no option, delete exiting reboot menu entry */
8808 	if (opt == NULL) {
8809 		entry_t		*ent;
8810 		BAM_DPRINTF(("%s: opt is NULL\n", fcn));
8811 		ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8812 		    NULL, NULL, 0, &entry);
8813 		if (ent == NULL) {	/* not found is ok */
8814 			BAM_DPRINTF(("%s: transient entry not found\n", fcn));
8815 			return (BAM_SUCCESS);
8816 		}
8817 		(void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8818 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8819 		mp->olddefault = NULL;
8820 		BAM_DPRINTF(("%s: restored old default\n", fcn));
8821 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8822 		return (BAM_WRITE);
8823 	}
8824 
8825 	/* if entry= is specified, set the default entry */
8826 	if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8827 		int entryNum = s_strtol(opt + strlen("entry="));
8828 		BAM_DPRINTF(("%s: opt has entry=: %s\n", fcn, opt));
8829 		if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8830 			/* this is entry=# option */
8831 			ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8832 			BAM_DPRINTF(("%s: default set to %d, "
8833 			    "set_default ret=%d\n", fcn, entry, ret));
8834 			return (ret);
8835 		} else {
8836 			bam_error(_("failed to set GRUB menu default to %d\n"),
8837 			    entryNum);
8838 			return (BAM_ERROR);
8839 		}
8840 	}
8841 
8842 	/*
8843 	 * add a new menu entry based on opt and make it the default
8844 	 */
8845 
8846 	fstype = get_fstype("/");
8847 	INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8848 	if (fstype == NULL) {
8849 		bam_error(_("failed to determine filesystem type for \"/\". "
8850 		    "Reboot with \narguments failed.\n"));
8851 		return (BAM_ERROR);
8852 	}
8853 
8854 	osdev = get_special("/");
8855 	INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8856 	if (osdev == NULL) {
8857 		free(fstype);
8858 		bam_error(_("failed to find device special file for \"/\". "
8859 		    "Reboot with \narguments failed.\n"));
8860 		return (BAM_ERROR);
8861 	}
8862 
8863 	sign = find_existing_sign("/", osdev, fstype);
8864 	INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8865 	if (sign == NULL) {
8866 		free(fstype);
8867 		free(osdev);
8868 		bam_error(_("failed to find boot signature. Reboot with "
8869 		    "arguments failed.\n"));
8870 		return (BAM_ERROR);
8871 	}
8872 
8873 	free(osdev);
8874 	(void) strlcpy(signbuf, sign, sizeof (signbuf));
8875 	free(sign);
8876 
8877 	assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8878 	    strchr(signbuf, ')') == NULL);
8879 
8880 	/*
8881 	 * There is no alternate root while doing reboot with args
8882 	 * This version of bootadm is only delivered with a DBOOT
8883 	 * version of Solaris.
8884 	 */
8885 	INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8886 	if (bam_direct != BAM_DIRECT_DBOOT) {
8887 		free(fstype);
8888 		bam_error(_("the root filesystem is not a dboot Solaris "
8889 		    "instance. \nThis version of bootadm is not supported "
8890 		    "on this version of Solaris.\n"));
8891 		return (BAM_ERROR);
8892 	}
8893 
8894 	/* add an entry for Solaris reboot */
8895 	if (opt[0] == '-') {
8896 		/* It's an option - first see if boot-file is set */
8897 		ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8898 		INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8899 		if (ret != BAM_SUCCESS) {
8900 			free(fstype);
8901 			bam_error(_("reboot with arguments: error querying "
8902 			    "current boot-file settings\n"));
8903 			return (BAM_ERROR);
8904 		}
8905 		if (kernbuf[0] == '\0')
8906 			(void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8907 			    sizeof (kernbuf));
8908 		/*
8909 		 * If this is a zfs file system and kernbuf does not
8910 		 * have "-B $ZFS-BOOTFS" string yet, add it.
8911 		 */
8912 		if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8913 			(void) strlcat(kernbuf, " ", sizeof (kernbuf));
8914 			(void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8915 		}
8916 		(void) strlcat(kernbuf, " ", sizeof (kernbuf));
8917 		(void) strlcat(kernbuf, opt, sizeof (kernbuf));
8918 		BAM_DPRINTF(("%s: reboot with args, option specified: "
8919 		    "kern=%s\n", fcn, kernbuf));
8920 	} else if (opt[0] == '/') {
8921 		/* It's a full path, so write it out. */
8922 		(void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8923 
8924 		/*
8925 		 * If someone runs:
8926 		 *
8927 		 *	# eeprom boot-args='-kd'
8928 		 *	# reboot /platform/i86pc/kernel/unix
8929 		 *
8930 		 * we want to use the boot-args as part of the boot
8931 		 * line.  On the other hand, if someone runs:
8932 		 *
8933 		 *	# reboot "/platform/i86pc/kernel/unix -kd"
8934 		 *
8935 		 * we don't need to mess with boot-args.  If there's
8936 		 * no space in the options string, assume we're in the
8937 		 * first case.
8938 		 */
8939 		if (strchr(opt, ' ') == NULL) {
8940 			ret = get_kernel(mp, ARGS_CMD, args_buf,
8941 			    sizeof (args_buf));
8942 			INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8943 			if (ret != BAM_SUCCESS) {
8944 				free(fstype);
8945 				bam_error(_("reboot with arguments: error "
8946 				    "querying current boot-args settings\n"));
8947 				return (BAM_ERROR);
8948 			}
8949 
8950 			if (args_buf[0] != '\0') {
8951 				(void) strlcat(kernbuf, " ", sizeof (kernbuf));
8952 				(void) strlcat(kernbuf, args_buf,
8953 				    sizeof (kernbuf));
8954 			}
8955 		}
8956 		BAM_DPRINTF(("%s: reboot with args, abspath specified: "
8957 		    "kern=%s\n", fcn, kernbuf));
8958 	} else {
8959 		/*
8960 		 * It may be a partial path, or it may be a partial
8961 		 * path followed by options.  Assume that only options
8962 		 * follow a space.  If someone sends us a kernel path
8963 		 * that includes a space, they deserve to be broken.
8964 		 */
8965 		opt_ptr = strchr(opt, ' ');
8966 		if (opt_ptr != NULL) {
8967 			*opt_ptr = '\0';
8968 		}
8969 
8970 		path = expand_path(opt);
8971 		if (path != NULL) {
8972 			(void) strlcpy(kernbuf, path, sizeof (kernbuf));
8973 			free(path);
8974 
8975 			/*
8976 			 * If there were options given, use those.
8977 			 * Otherwise, copy over the default options.
8978 			 */
8979 			if (opt_ptr != NULL) {
8980 				/* Restore the space in opt string */
8981 				*opt_ptr = ' ';
8982 				(void) strlcat(kernbuf, opt_ptr,
8983 				    sizeof (kernbuf));
8984 			} else {
8985 				ret = get_kernel(mp, ARGS_CMD, args_buf,
8986 				    sizeof (args_buf));
8987 				INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8988 				    ret = BAM_ERROR);
8989 				if (ret != BAM_SUCCESS) {
8990 					free(fstype);
8991 					bam_error(_("reboot with arguments: "
8992 					    "error querying current boot-args "
8993 					    "settings\n"));
8994 					return (BAM_ERROR);
8995 				}
8996 
8997 				if (args_buf[0] != '\0') {
8998 					(void) strlcat(kernbuf, " ",
8999 					    sizeof (kernbuf));
9000 					(void) strlcat(kernbuf,
9001 					    args_buf, sizeof (kernbuf));
9002 				}
9003 			}
9004 			BAM_DPRINTF(("%s: resolved partial path: %s\n",
9005 			    fcn, kernbuf));
9006 		} else {
9007 			free(fstype);
9008 			bam_error(_("unable to expand %s to a full file"
9009 			    " path.\n"), opt);
9010 			bam_print_stderr(_("Rebooting with default kernel "
9011 			    "and options.\n"));
9012 			return (BAM_ERROR);
9013 		}
9014 	}
9015 	free(fstype);
9016 	entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
9017 	    NULL, NULL, NULL);
9018 	INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
9019 	if (entry == BAM_ERROR) {
9020 		bam_error(_("Cannot update menu. Cannot reboot with "
9021 		    "requested arguments\n"));
9022 		return (BAM_ERROR);
9023 	}
9024 
9025 	save_default_entry(mp, BAM_OLDDEF);
9026 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
9027 	INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
9028 	if (ret == BAM_ERROR) {
9029 		bam_error(_("reboot with arguments: setting GRUB menu default "
9030 		    "to %d failed\n"), entry);
9031 	}
9032 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9033 	return (BAM_WRITE);
9034 }
9035 
9036 error_t
9037 set_global(menu_t *mp, char *globalcmd, int val)
9038 {
9039 	line_t		*lp;
9040 	line_t		*found;
9041 	line_t		*last;
9042 	char		*cp;
9043 	char		*str;
9044 	char		prefix[BAM_MAXLINE];
9045 	size_t		len;
9046 	const char	*fcn = "set_global()";
9047 
9048 	assert(mp);
9049 	assert(globalcmd);
9050 
9051 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
9052 		INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
9053 		INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
9054 		INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
9055 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
9056 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
9057 			bam_error(_("invalid boot entry number: %s\n"), prefix);
9058 			return (BAM_ERROR);
9059 		}
9060 	}
9061 
9062 	found = last = NULL;
9063 	for (lp = mp->start; lp; lp = lp->next) {
9064 		if (lp->flags != BAM_GLOBAL)
9065 			continue;
9066 
9067 		last = lp; /* track the last global found */
9068 
9069 		INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
9070 		if (lp->cmd == NULL) {
9071 			bam_error(_("no command at line %d\n"), lp->lineNum);
9072 			continue;
9073 		}
9074 		if (strcmp(globalcmd, lp->cmd) != 0)
9075 			continue;
9076 
9077 		BAM_DPRINTF(("%s: found matching global command: %s\n",
9078 		    fcn, globalcmd));
9079 
9080 		if (found) {
9081 			bam_error(_("duplicate command %s at line %d of "
9082 			    "%sboot/grub/menu.lst\n"), globalcmd,
9083 			    lp->lineNum, bam_root);
9084 		}
9085 		found = lp;
9086 	}
9087 
9088 	if (found == NULL) {
9089 		lp = s_calloc(1, sizeof (line_t));
9090 		if (last == NULL) {
9091 			lp->next = mp->start;
9092 			mp->start = lp;
9093 			mp->end = (mp->end) ? mp->end : lp;
9094 		} else {
9095 			lp->next = last->next;
9096 			last->next = lp;
9097 			if (lp->next == NULL)
9098 				mp->end = lp;
9099 		}
9100 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
9101 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
9102 		len += 10;	/* val < 10 digits */
9103 		lp->line = s_calloc(1, len);
9104 		(void) snprintf(lp->line, len, "%s%s%d",
9105 		    globalcmd, menu_cmds[SEP_CMD], val);
9106 		BAM_DPRINTF(("%s: wrote new global line: %s\n", fcn, lp->line));
9107 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9108 		return (BAM_WRITE);
9109 	}
9110 
9111 	/*
9112 	 * We are changing an existing entry. Retain any prefix whitespace,
9113 	 * but overwrite everything else. This preserves tabs added for
9114 	 * readability.
9115 	 */
9116 	str = found->line;
9117 	cp = prefix;
9118 	while (*str == ' ' || *str == '\t')
9119 		*(cp++) = *(str++);
9120 	*cp = '\0'; /* Terminate prefix */
9121 	len = strlen(prefix) + strlen(globalcmd);
9122 	len += strlen(menu_cmds[SEP_CMD]) + 10;
9123 
9124 	free(found->line);
9125 	found->line = s_calloc(1, len);
9126 	(void) snprintf(found->line, len,
9127 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
9128 
9129 	BAM_DPRINTF(("%s: replaced global line with: %s\n", fcn, found->line));
9130 	BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9131 	return (BAM_WRITE); /* need a write to menu */
9132 }
9133 
9134 /*
9135  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
9136  * expand it to a full unix path.  The calling function is expected to
9137  * output a message if an error occurs and NULL is returned.
9138  */
9139 static char *
9140 expand_path(const char *partial_path)
9141 {
9142 	int		new_path_len;
9143 	char		*new_path;
9144 	char		new_path2[PATH_MAX];
9145 	struct stat	sb;
9146 	const char	*fcn = "expand_path()";
9147 
9148 	new_path_len = strlen(partial_path) + 64;
9149 	new_path = s_calloc(1, new_path_len);
9150 
9151 	/* First, try the simplest case - something like "kernel/unix" */
9152 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
9153 	    partial_path);
9154 	if (stat(new_path, &sb) == 0) {
9155 		BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9156 		return (new_path);
9157 	}
9158 
9159 	if (strcmp(partial_path, "kmdb") == 0) {
9160 		(void) snprintf(new_path, new_path_len, "%s -k",
9161 		    DIRECT_BOOT_KERNEL);
9162 		BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9163 		return (new_path);
9164 	}
9165 
9166 	/*
9167 	 * We've quickly reached unsupported usage.  Try once more to
9168 	 * see if we were just given a glom name.
9169 	 */
9170 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9171 	    partial_path);
9172 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9173 	    partial_path);
9174 	if (stat(new_path, &sb) == 0) {
9175 		if (stat(new_path2, &sb) == 0) {
9176 			/*
9177 			 * We matched both, so we actually
9178 			 * want to write the $ISADIR version.
9179 			 */
9180 			(void) snprintf(new_path, new_path_len,
9181 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
9182 			    partial_path);
9183 		}
9184 		BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9185 		return (new_path);
9186 	}
9187 
9188 	free(new_path);
9189 	BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9190 	return (NULL);
9191 }
9192 
9193 /*
9194  * The kernel cmd and arg have been changed, so
9195  * check whether the archive line needs to change.
9196  */
9197 static void
9198 set_archive_line(entry_t *entryp, line_t *kernelp)
9199 {
9200 	line_t		*lp = entryp->start;
9201 	char		*new_archive;
9202 	menu_cmd_t	m_cmd;
9203 	const char	*fcn = "set_archive_line()";
9204 
9205 	for (; lp != NULL; lp = lp->next) {
9206 		if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9207 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9208 			break;
9209 		}
9210 
9211 		INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9212 		if (lp == entryp->end) {
9213 			BAM_DPRINTF(("%s: no module/archive line for entry: "
9214 			    "%d\n", fcn, entryp->entryNum));
9215 			return;
9216 		}
9217 	}
9218 	INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9219 	if (lp == NULL) {
9220 		BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9221 		    fcn, entryp->entryNum));
9222 		return;
9223 	}
9224 
9225 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9226 		new_archive = DIRECT_BOOT_ARCHIVE;
9227 		m_cmd = MODULE_DOLLAR_CMD;
9228 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
9229 		new_archive = DIRECT_BOOT_ARCHIVE_64;
9230 		m_cmd = MODULE_CMD;
9231 	} else {
9232 		new_archive = DIRECT_BOOT_ARCHIVE_32;
9233 		m_cmd = MODULE_CMD;
9234 	}
9235 
9236 	if (strcmp(lp->arg, new_archive) == 0) {
9237 		BAM_DPRINTF(("%s: no change for line: %s\n", fcn, lp->arg));
9238 		return;
9239 	}
9240 
9241 	if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9242 		free(lp->cmd);
9243 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
9244 	}
9245 
9246 	free(lp->arg);
9247 	lp->arg = s_strdup(new_archive);
9248 	update_line(lp);
9249 	BAM_DPRINTF(("%s: replaced for line: %s\n", fcn, lp->line));
9250 }
9251 
9252 /*
9253  * Title for an entry to set properties that once went in bootenv.rc.
9254  */
9255 #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
9256 
9257 /*
9258  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9259  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
9260  * string, reset the value to the default.  If path is a non-zero-length
9261  * string, set the kernel or arguments.
9262  */
9263 static error_t
9264 get_set_kernel(
9265 	menu_t *mp,
9266 	menu_cmd_t optnum,
9267 	char *path,
9268 	char *buf,
9269 	size_t bufsize)
9270 {
9271 	int		entryNum;
9272 	int		rv = BAM_SUCCESS;
9273 	int		free_new_path = 0;
9274 	entry_t		*entryp;
9275 	line_t		*ptr;
9276 	line_t		*kernelp;
9277 	char		*new_arg;
9278 	char		*old_args;
9279 	char		*space;
9280 	char		*new_path;
9281 	char		old_space;
9282 	size_t		old_kernel_len = 0;
9283 	size_t		new_str_len;
9284 	char		*fstype;
9285 	char		*osdev;
9286 	char		*sign;
9287 	char		signbuf[PATH_MAX];
9288 	int		ret;
9289 	const char	*fcn = "get_set_kernel()";
9290 
9291 	assert(bufsize > 0);
9292 
9293 	ptr = kernelp = NULL;
9294 	new_arg = old_args = space = NULL;
9295 	new_path = NULL;
9296 	buf[0] = '\0';
9297 
9298 	INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9299 	    bam_direct = BAM_DIRECT_MULTIBOOT);
9300 	if (bam_direct != BAM_DIRECT_DBOOT) {
9301 		bam_error(_("bootadm set-menu %s may only be run on "
9302 		    "directboot kernels.\n"),
9303 		    optnum == KERNEL_CMD ? "kernel" : "args");
9304 		return (BAM_ERROR);
9305 	}
9306 
9307 	/*
9308 	 * If a user changed the default entry to a non-bootadm controlled
9309 	 * one, we don't want to mess with it.  Just print an error and
9310 	 * return.
9311 	 */
9312 	if (mp->curdefault) {
9313 		entryNum = s_strtol(mp->curdefault->arg);
9314 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
9315 			if (entryp->entryNum == entryNum)
9316 				break;
9317 		}
9318 		if ((entryp != NULL) &&
9319 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9320 			bam_error(_("Default /boot/grub/menu.lst entry is not "
9321 			    "controlled by bootadm.  Exiting\n"));
9322 			return (BAM_ERROR);
9323 		}
9324 	}
9325 
9326 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9327 	    0, &entryNum);
9328 
9329 	if (entryp != NULL) {
9330 		for (ptr = entryp->start; ptr && ptr != entryp->end;
9331 		    ptr = ptr->next) {
9332 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9333 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9334 				kernelp = ptr;
9335 				break;
9336 			}
9337 		}
9338 		if (kernelp == NULL) {
9339 			bam_error(_("no kernel line found in entry %d\n"),
9340 			    entryNum);
9341 			return (BAM_ERROR);
9342 		}
9343 
9344 		old_kernel_len = strcspn(kernelp->arg, " \t");
9345 		space = old_args = kernelp->arg + old_kernel_len;
9346 		while ((*old_args == ' ') || (*old_args == '\t'))
9347 			old_args++;
9348 	}
9349 
9350 	if (path == NULL) {
9351 		if (entryp == NULL) {
9352 			BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9353 			    fcn));
9354 			BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9355 			return (BAM_SUCCESS);
9356 		}
9357 		assert(kernelp);
9358 		if (optnum == ARGS_CMD) {
9359 			if (old_args[0] != '\0') {
9360 				(void) strlcpy(buf, old_args, bufsize);
9361 				BAM_DPRINTF(("%s: read menu boot-args: %s\n",
9362 				    fcn, buf));
9363 			}
9364 		} else {
9365 			/*
9366 			 * We need to print the kernel, so we just turn the
9367 			 * first space into a '\0' and print the beginning.
9368 			 * We don't print anything if it's the default kernel.
9369 			 */
9370 			old_space = *space;
9371 			*space = '\0';
9372 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9373 				(void) strlcpy(buf, kernelp->arg, bufsize);
9374 				BAM_DPRINTF(("%s: read menu boot-file: %s\n",
9375 				    fcn, buf));
9376 			}
9377 			*space = old_space;
9378 		}
9379 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9380 		return (BAM_SUCCESS);
9381 	}
9382 
9383 	/*
9384 	 * First, check if we're resetting an entry to the default.
9385 	 */
9386 	if ((path[0] == '\0') ||
9387 	    ((optnum == KERNEL_CMD) &&
9388 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9389 		if ((entryp == NULL) || (kernelp == NULL)) {
9390 			/* No previous entry, it's already the default */
9391 			BAM_DPRINTF(("%s: no reset, already has default\n",
9392 			    fcn));
9393 			return (BAM_SUCCESS);
9394 		}
9395 
9396 		/*
9397 		 * Check if we can delete the entry.  If we're resetting the
9398 		 * kernel command, and the args is already empty, or if we're
9399 		 * resetting the args command, and the kernel is already the
9400 		 * default, we can restore the old default and delete the entry.
9401 		 */
9402 		if (((optnum == KERNEL_CMD) &&
9403 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
9404 		    ((optnum == ARGS_CMD) &&
9405 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9406 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9407 			kernelp = NULL;
9408 			(void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9409 			restore_default_entry(mp, BAM_OLD_RC_DEF,
9410 			    mp->old_rc_default);
9411 			mp->old_rc_default = NULL;
9412 			rv = BAM_WRITE;
9413 			BAM_DPRINTF(("%s: resetting to default\n", fcn));
9414 			goto done;
9415 		}
9416 
9417 		if (optnum == KERNEL_CMD) {
9418 			/*
9419 			 * At this point, we've already checked that old_args
9420 			 * and entryp are valid pointers.  The "+ 2" is for
9421 			 * a space a the string termination character.
9422 			 */
9423 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9424 			    strlen(old_args) + 2;
9425 			new_arg = s_calloc(1, new_str_len);
9426 			(void) snprintf(new_arg, new_str_len, "%s %s",
9427 			    DIRECT_BOOT_KERNEL, old_args);
9428 			free(kernelp->arg);
9429 			kernelp->arg = new_arg;
9430 
9431 			/*
9432 			 * We have changed the kernel line, so we may need
9433 			 * to update the archive line as well.
9434 			 */
9435 			set_archive_line(entryp, kernelp);
9436 			BAM_DPRINTF(("%s: reset kernel to default, but "
9437 			    "retained old args: %s\n", fcn, kernelp->arg));
9438 		} else {
9439 			/*
9440 			 * We're resetting the boot args to nothing, so
9441 			 * we only need to copy the kernel.  We've already
9442 			 * checked that the kernel is not the default.
9443 			 */
9444 			new_arg = s_calloc(1, old_kernel_len + 1);
9445 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
9446 			    kernelp->arg);
9447 			free(kernelp->arg);
9448 			kernelp->arg = new_arg;
9449 			BAM_DPRINTF(("%s: reset args to default, but retained "
9450 			    "old kernel: %s\n", fcn, kernelp->arg));
9451 		}
9452 		rv = BAM_WRITE;
9453 		goto done;
9454 	}
9455 
9456 	/*
9457 	 * Expand the kernel file to a full path, if necessary
9458 	 */
9459 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9460 		new_path = expand_path(path);
9461 		if (new_path == NULL) {
9462 			bam_error(_("unable to expand %s to a full file "
9463 			    "path.\n"), path);
9464 			BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9465 			return (BAM_ERROR);
9466 		}
9467 		free_new_path = 1;
9468 	} else {
9469 		new_path = path;
9470 		free_new_path = 0;
9471 	}
9472 
9473 	/*
9474 	 * At this point, we know we're setting a new value.  First, take care
9475 	 * of the case where there was no previous entry.
9476 	 */
9477 	if (entryp == NULL) {
9478 
9479 		/* Similar to code in update_temp */
9480 		fstype = get_fstype("/");
9481 		INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9482 		if (fstype == NULL) {
9483 			bam_error(_("cannot determine filesystem type for "
9484 			    "\"/\".\nCannot generate GRUB menu entry with "
9485 			    "EEPROM arguments.\n"));
9486 			rv = BAM_ERROR;
9487 			goto done;
9488 		}
9489 
9490 		osdev = get_special("/");
9491 		INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9492 		if (osdev == NULL) {
9493 			free(fstype);
9494 			bam_error(_("cannot determine device special file for "
9495 			    "\"/\".\nCannot generate GRUB menu entry with "
9496 			    "EEPROM arguments.\n"));
9497 			rv = BAM_ERROR;
9498 			goto done;
9499 		}
9500 
9501 		sign = find_existing_sign("/", osdev, fstype);
9502 		INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9503 		if (sign == NULL) {
9504 			free(fstype);
9505 			free(osdev);
9506 			bam_error(_("cannot determine boot signature for "
9507 			    "\"/\".\nCannot generate GRUB menu entry with "
9508 			    "EEPROM arguments.\n"));
9509 			rv = BAM_ERROR;
9510 			goto done;
9511 		}
9512 
9513 		free(osdev);
9514 		(void) strlcpy(signbuf, sign, sizeof (signbuf));
9515 		free(sign);
9516 		assert(strchr(signbuf, '(') == NULL &&
9517 		    strchr(signbuf, ',') == NULL &&
9518 		    strchr(signbuf, ')') == NULL);
9519 
9520 		if (optnum == KERNEL_CMD) {
9521 			if (strcmp(fstype, "zfs") == 0) {
9522 				new_str_len = strlen(new_path) +
9523 				    strlen(ZFS_BOOT) + 8;
9524 				new_arg = s_calloc(1, new_str_len);
9525 				(void) snprintf(new_arg, new_str_len, "%s %s",
9526 				    new_path, ZFS_BOOT);
9527 				BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9528 				    new_arg));
9529 				entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9530 				    signbuf, new_arg, NULL, NULL, NULL);
9531 				free(new_arg);
9532 			} else {
9533 				BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9534 				    new_path));
9535 				entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9536 				    signbuf, new_path, NULL, NULL, NULL);
9537 			}
9538 		} else {
9539 			new_str_len = strlen(path) + 8;
9540 			if (strcmp(fstype, "zfs") == 0) {
9541 				new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9542 				new_arg = s_calloc(1, new_str_len);
9543 				(void) snprintf(new_arg, new_str_len, "%s %s",
9544 				    DIRECT_BOOT_KERNEL_ZFS, path);
9545 			} else {
9546 				new_str_len += strlen(DIRECT_BOOT_KERNEL);
9547 				new_arg = s_calloc(1, new_str_len);
9548 				(void) snprintf(new_arg, new_str_len, "%s %s",
9549 				    DIRECT_BOOT_KERNEL, path);
9550 			}
9551 
9552 			BAM_DPRINTF(("%s: new args=%s\n", fcn, new_arg));
9553 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9554 			    signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9555 			free(new_arg);
9556 		}
9557 		free(fstype);
9558 		INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9559 		    entryNum = BAM_ERROR);
9560 		if (entryNum == BAM_ERROR) {
9561 			bam_error(_("failed to add boot entry: %s\n"),
9562 			    BOOTENV_RC_TITLE);
9563 			rv = BAM_ERROR;
9564 			goto done;
9565 		}
9566 		save_default_entry(mp, BAM_OLD_RC_DEF);
9567 		ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9568 		INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9569 		if (ret == BAM_ERROR) {
9570 			bam_error(_("failed to set default to: %d\n"),
9571 			    entryNum);
9572 		}
9573 		rv = BAM_WRITE;
9574 		goto done;
9575 	}
9576 
9577 	/*
9578 	 * There was already an bootenv entry which we need to edit.
9579 	 */
9580 	if (optnum == KERNEL_CMD) {
9581 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
9582 		new_arg = s_calloc(1, new_str_len);
9583 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9584 		    old_args);
9585 		free(kernelp->arg);
9586 		kernelp->arg = new_arg;
9587 
9588 		/*
9589 		 * If we have changed the kernel line, we may need to update
9590 		 * the archive line as well.
9591 		 */
9592 		set_archive_line(entryp, kernelp);
9593 		BAM_DPRINTF(("%s: rc line exists, replaced kernel, same "
9594 		    "args: %s\n", fcn, kernelp->arg));
9595 	} else {
9596 		new_str_len = old_kernel_len + strlen(path) + 8;
9597 		new_arg = s_calloc(1, new_str_len);
9598 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9599 		(void) strlcat(new_arg, " ", new_str_len);
9600 		(void) strlcat(new_arg, path, new_str_len);
9601 		free(kernelp->arg);
9602 		kernelp->arg = new_arg;
9603 		BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9604 		    "args: %s\n", fcn, kernelp->arg));
9605 	}
9606 	rv = BAM_WRITE;
9607 
9608 done:
9609 	if ((rv == BAM_WRITE) && kernelp)
9610 		update_line(kernelp);
9611 	if (free_new_path)
9612 		free(new_path);
9613 	if (rv == BAM_WRITE) {
9614 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9615 	} else {
9616 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9617 	}
9618 	return (rv);
9619 }
9620 
9621 static error_t
9622 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9623 {
9624 	const char	*fcn = "get_kernel()";
9625 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_cmds[optnum]));
9626 	return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9627 }
9628 
9629 static error_t
9630 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9631 {
9632 	const char	*fcn = "set_kernel()";
9633 	assert(path != NULL);
9634 	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn,
9635 	    menu_cmds[optnum], path));
9636 	return (get_set_kernel(mp, optnum, path, buf, bufsize));
9637 }
9638 
9639 /*ARGSUSED*/
9640 static error_t
9641 set_option(menu_t *mp, char *dummy, char *opt)
9642 {
9643 	int		optnum;
9644 	int		optval;
9645 	char		*val;
9646 	char		buf[BUFSIZ] = "";
9647 	error_t		rv;
9648 	const char	*fcn = "set_option()";
9649 
9650 	assert(mp);
9651 	assert(opt);
9652 	assert(dummy == NULL);
9653 
9654 	/* opt is set from bam_argv[0] and is always non-NULL */
9655 	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt));
9656 
9657 	val = strchr(opt, '=');
9658 	if (val != NULL) {
9659 		*val = '\0';
9660 	}
9661 
9662 	if (strcmp(opt, "default") == 0) {
9663 		optnum = DEFAULT_CMD;
9664 	} else if (strcmp(opt, "timeout") == 0) {
9665 		optnum = TIMEOUT_CMD;
9666 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9667 		optnum = KERNEL_CMD;
9668 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9669 		optnum = ARGS_CMD;
9670 	} else {
9671 		bam_error(_("invalid option: %s\n"), opt);
9672 		return (BAM_ERROR);
9673 	}
9674 
9675 	/*
9676 	 * kernel and args are allowed without "=new_value" strings.  All
9677 	 * others cause errors
9678 	 */
9679 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9680 		bam_error(_("option has no argument: %s\n"), opt);
9681 		return (BAM_ERROR);
9682 	} else if (val != NULL) {
9683 		*val = '=';
9684 	}
9685 
9686 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9687 		BAM_DPRINTF(("%s: setting %s option to %s\n",
9688 		    fcn, menu_cmds[optnum], val ? val + 1 : "NULL"));
9689 
9690 		if (val)
9691 			rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9692 		else
9693 			rv = get_kernel(mp, optnum, buf, sizeof (buf));
9694 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9695 			(void) printf("%s\n", buf);
9696 	} else {
9697 		optval = s_strtol(val + 1);
9698 		BAM_DPRINTF(("%s: setting %s option to %s\n", fcn,
9699 		    menu_cmds[optnum], val + 1));
9700 		rv = set_global(mp, menu_cmds[optnum], optval);
9701 	}
9702 
9703 	if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9704 		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9705 	} else {
9706 		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9707 	}
9708 
9709 	return (rv);
9710 }
9711 
9712 /*
9713  * The quiet argument suppresses messages. This is used
9714  * when invoked in the context of other commands (e.g. list_entry)
9715  */
9716 static error_t
9717 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9718 {
9719 	line_t *lp;
9720 	char *arg;
9721 	int done, ret = BAM_SUCCESS;
9722 
9723 	assert(mp);
9724 	assert(menu_path);
9725 	assert(globalcmd);
9726 
9727 	if (mp->start == NULL) {
9728 		if (!quiet)
9729 			bam_error(_("menu file not found: %s\n"), menu_path);
9730 		return (BAM_ERROR);
9731 	}
9732 
9733 	done = 0;
9734 	for (lp = mp->start; lp; lp = lp->next) {
9735 		if (lp->flags != BAM_GLOBAL)
9736 			continue;
9737 
9738 		if (lp->cmd == NULL) {
9739 			if (!quiet)
9740 				bam_error(_("no command at line %d\n"),
9741 				    lp->lineNum);
9742 			continue;
9743 		}
9744 
9745 		if (strcmp(globalcmd, lp->cmd) != 0)
9746 			continue;
9747 
9748 		/* Found global. Check for duplicates */
9749 		if (done && !quiet) {
9750 			bam_error(_("duplicate command %s at line %d of "
9751 			    "%sboot/grub/menu.lst\n"), globalcmd,
9752 			    lp->lineNum, bam_root);
9753 			ret = BAM_ERROR;
9754 		}
9755 
9756 		arg = lp->arg ? lp->arg : "";
9757 		bam_print(_("%s %s\n"), globalcmd, arg);
9758 		done = 1;
9759 	}
9760 
9761 	if (!done && bam_verbose)
9762 		bam_print(_("no %s entry found\n"), globalcmd);
9763 
9764 	return (ret);
9765 }
9766 
9767 static error_t
9768 menu_write(char *root, menu_t *mp)
9769 {
9770 	const char *fcn = "menu_write()";
9771 
9772 	BAM_DPRINTF(("%s: entered menu_write() for root: <%s>\n", fcn, root));
9773 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9774 }
9775 
9776 void
9777 line_free(line_t *lp)
9778 {
9779 	if (lp == NULL)
9780 		return;
9781 
9782 	if (lp->cmd != NULL)
9783 		free(lp->cmd);
9784 	if (lp->sep)
9785 		free(lp->sep);
9786 	if (lp->arg)
9787 		free(lp->arg);
9788 	if (lp->line)
9789 		free(lp->line);
9790 	free(lp);
9791 }
9792 
9793 static void
9794 linelist_free(line_t *start)
9795 {
9796 	line_t *lp;
9797 
9798 	while (start) {
9799 		lp = start;
9800 		start = start->next;
9801 		line_free(lp);
9802 	}
9803 }
9804 
9805 static void
9806 filelist_free(filelist_t *flistp)
9807 {
9808 	linelist_free(flistp->head);
9809 	flistp->head = NULL;
9810 	flistp->tail = NULL;
9811 }
9812 
9813 static void
9814 menu_free(menu_t *mp)
9815 {
9816 	entry_t *ent, *tmp;
9817 	assert(mp);
9818 
9819 	if (mp->start)
9820 		linelist_free(mp->start);
9821 	ent = mp->entries;
9822 	while (ent) {
9823 		tmp = ent;
9824 		ent = tmp->next;
9825 		free(tmp);
9826 	}
9827 
9828 	free(mp);
9829 }
9830 
9831 /*
9832  * Utility routines
9833  */
9834 
9835 
9836 /*
9837  * Returns 0 on success
9838  * Any other value indicates an error
9839  */
9840 static int
9841 exec_cmd(char *cmdline, filelist_t *flistp)
9842 {
9843 	char buf[BUFSIZ];
9844 	int ret;
9845 	FILE *ptr;
9846 	sigset_t set;
9847 	void (*disp)(int);
9848 
9849 	/*
9850 	 * For security
9851 	 * - only absolute paths are allowed
9852 	 * - set IFS to space and tab
9853 	 */
9854 	if (*cmdline != '/') {
9855 		bam_error(_("path is not absolute: %s\n"), cmdline);
9856 		return (-1);
9857 	}
9858 	(void) putenv("IFS= \t");
9859 
9860 	/*
9861 	 * We may have been exec'ed with SIGCHLD blocked
9862 	 * unblock it here
9863 	 */
9864 	(void) sigemptyset(&set);
9865 	(void) sigaddset(&set, SIGCHLD);
9866 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9867 		bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno));
9868 		return (-1);
9869 	}
9870 
9871 	/*
9872 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9873 	 */
9874 	disp = sigset(SIGCHLD, SIG_DFL);
9875 	if (disp == SIG_ERR) {
9876 		bam_error(_("cannot set SIGCHLD disposition: %s\n"),
9877 		    strerror(errno));
9878 		return (-1);
9879 	}
9880 	if (disp == SIG_HOLD) {
9881 		bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
9882 		    cmdline);
9883 		return (-1);
9884 	}
9885 
9886 	ptr = popen(cmdline, "r");
9887 	if (ptr == NULL) {
9888 		bam_error(_("popen failed: %s: %s\n"), cmdline,
9889 		    strerror(errno));
9890 		return (-1);
9891 	}
9892 
9893 	/*
9894 	 * If we simply do a pclose() following a popen(), pclose()
9895 	 * will close the reader end of the pipe immediately even
9896 	 * if the child process has not started/exited. pclose()
9897 	 * does wait for cmd to terminate before returning though.
9898 	 * When the executed command writes its output to the pipe
9899 	 * there is no reader process and the command dies with
9900 	 * SIGPIPE. To avoid this we read repeatedly until read
9901 	 * terminates with EOF. This indicates that the command
9902 	 * (writer) has closed the pipe and we can safely do a
9903 	 * pclose().
9904 	 *
9905 	 * Since pclose() does wait for the command to exit,
9906 	 * we can safely reap the exit status of the command
9907 	 * from the value returned by pclose()
9908 	 */
9909 	while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9910 		if (flistp == NULL) {
9911 			/* s_fgets strips newlines, so insert them at the end */
9912 			bam_print(_("%s\n"), buf);
9913 		} else {
9914 			append_to_flist(flistp, buf);
9915 		}
9916 	}
9917 
9918 	ret = pclose(ptr);
9919 	if (ret == -1) {
9920 		bam_error(_("pclose failed: %s: %s\n"), cmdline,
9921 		    strerror(errno));
9922 		return (-1);
9923 	}
9924 
9925 	if (WIFEXITED(ret)) {
9926 		return (WEXITSTATUS(ret));
9927 	} else {
9928 		bam_error(_("command terminated abnormally: %s: %d\n"),
9929 		    cmdline, ret);
9930 		return (-1);
9931 	}
9932 }
9933 
9934 /*
9935  * Since this function returns -1 on error
9936  * it cannot be used to convert -1. However,
9937  * that is sufficient for what we need.
9938  */
9939 static long
9940 s_strtol(char *str)
9941 {
9942 	long l;
9943 	char *res = NULL;
9944 
9945 	if (str == NULL) {
9946 		return (-1);
9947 	}
9948 
9949 	errno = 0;
9950 	l = strtol(str, &res, 10);
9951 	if (errno || *res != '\0') {
9952 		return (-1);
9953 	}
9954 
9955 	return (l);
9956 }
9957 
9958 /*
9959  * Wrapper around fputs, that adds a newline (since fputs doesn't)
9960  */
9961 static int
9962 s_fputs(char *str, FILE *fp)
9963 {
9964 	char linebuf[BAM_MAXLINE];
9965 
9966 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9967 	return (fputs(linebuf, fp));
9968 }
9969 
9970 /*
9971  * Wrapper around fgets, that strips newlines returned by fgets
9972  */
9973 char *
9974 s_fgets(char *buf, int buflen, FILE *fp)
9975 {
9976 	int n;
9977 
9978 	buf = fgets(buf, buflen, fp);
9979 	if (buf) {
9980 		n = strlen(buf);
9981 		if (n == buflen - 1 && buf[n-1] != '\n')
9982 			bam_error(_("the following line is too long "
9983 			    "(> %d chars)\n\t%s\n"), buflen - 1, buf);
9984 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9985 	}
9986 
9987 	return (buf);
9988 }
9989 
9990 void *
9991 s_calloc(size_t nelem, size_t sz)
9992 {
9993 	void *ptr;
9994 
9995 	ptr = calloc(nelem, sz);
9996 	if (ptr == NULL) {
9997 		bam_error(_("could not allocate memory: size = %u\n"),
9998 		    nelem*sz);
9999 		bam_exit(1);
10000 	}
10001 	return (ptr);
10002 }
10003 
10004 void *
10005 s_realloc(void *ptr, size_t sz)
10006 {
10007 	ptr = realloc(ptr, sz);
10008 	if (ptr == NULL) {
10009 		bam_error(_("could not allocate memory: size = %u\n"), sz);
10010 		bam_exit(1);
10011 	}
10012 	return (ptr);
10013 }
10014 
10015 char *
10016 s_strdup(char *str)
10017 {
10018 	char *ptr;
10019 
10020 	if (str == NULL)
10021 		return (NULL);
10022 
10023 	ptr = strdup(str);
10024 	if (ptr == NULL) {
10025 		bam_error(_("could not allocate memory: size = %u\n"),
10026 		    strlen(str) + 1);
10027 		bam_exit(1);
10028 	}
10029 	return (ptr);
10030 }
10031 
10032 /*
10033  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
10034  * Returns 0 otherwise
10035  */
10036 static int
10037 is_amd64(void)
10038 {
10039 	static int amd64 = -1;
10040 	char isabuf[257];	/* from sysinfo(2) manpage */
10041 
10042 	if (amd64 != -1)
10043 		return (amd64);
10044 
10045 	if (bam_alt_platform) {
10046 		if (strcmp(bam_platform, "i86pc") == 0) {
10047 			amd64 = 1;		/* diskless server */
10048 		}
10049 	} else {
10050 		if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
10051 		    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
10052 			amd64 = 1;
10053 		} else if (strstr(isabuf, "i386") == NULL) {
10054 			amd64 = 1;		/* diskless server */
10055 		}
10056 	}
10057 	if (amd64 == -1)
10058 		amd64 = 0;
10059 
10060 	return (amd64);
10061 }
10062 
10063 static char *
10064 get_machine(void)
10065 {
10066 	static int cached = -1;
10067 	static char mbuf[257];	/* from sysinfo(2) manpage */
10068 
10069 	if (cached == 0)
10070 		return (mbuf);
10071 
10072 	if (bam_alt_platform) {
10073 		return (bam_platform);
10074 	} else {
10075 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
10076 			cached = 1;
10077 		}
10078 	}
10079 	if (cached == -1) {
10080 		mbuf[0] = '\0';
10081 		cached = 0;
10082 	}
10083 
10084 	return (mbuf);
10085 }
10086 
10087 int
10088 is_sparc(void)
10089 {
10090 	static int issparc = -1;
10091 	char mbuf[257];	/* from sysinfo(2) manpage */
10092 
10093 	if (issparc != -1)
10094 		return (issparc);
10095 
10096 	if (bam_alt_platform) {
10097 		if (strncmp(bam_platform, "sun4", 4) == 0) {
10098 			issparc = 1;
10099 		}
10100 	} else {
10101 		if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
10102 		    strcmp(mbuf, "sparc") == 0) {
10103 			issparc = 1;
10104 		}
10105 	}
10106 	if (issparc == -1)
10107 		issparc = 0;
10108 
10109 	return (issparc);
10110 }
10111 
10112 static void
10113 append_to_flist(filelist_t *flistp, char *s)
10114 {
10115 	line_t *lp;
10116 
10117 	lp = s_calloc(1, sizeof (line_t));
10118 	lp->line = s_strdup(s);
10119 	if (flistp->head == NULL)
10120 		flistp->head = lp;
10121 	else
10122 		flistp->tail->next = lp;
10123 	flistp->tail = lp;
10124 }
10125 
10126 #if !defined(_OBP)
10127 
10128 UCODE_VENDORS;
10129 
10130 /*ARGSUSED*/
10131 static void
10132 ucode_install(char *root)
10133 {
10134 	int i;
10135 
10136 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
10137 		int cmd_len = PATH_MAX + 256;
10138 		char cmd[PATH_MAX + 256];
10139 		char file[PATH_MAX];
10140 		char timestamp[PATH_MAX];
10141 		struct stat fstatus, tstatus;
10142 		struct utimbuf u_times;
10143 
10144 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
10145 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
10146 		    ucode_vendors[i].extstr);
10147 
10148 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
10149 			continue;
10150 
10151 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
10152 
10153 		if (stat(timestamp, &tstatus) == 0 &&
10154 		    fstatus.st_mtime <= tstatus.st_mtime)
10155 			continue;
10156 
10157 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
10158 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
10159 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
10160 		if (system(cmd) != 0)
10161 			return;
10162 
10163 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
10164 			return;
10165 
10166 		u_times.actime = fstatus.st_atime;
10167 		u_times.modtime = fstatus.st_mtime;
10168 		(void) utime(timestamp, &u_times);
10169 	}
10170 }
10171 #endif
10172