xref: /titanic_41/usr/src/cmd/boot/bootadm/bootadm.c (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5fbac2b2bSvikram  * Common Development and Distribution License (the "License").
6fbac2b2bSvikram  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22d876c67dSjg  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * bootadm(1M) is a new utility for managing bootability of
307c478bd9Sstevel@tonic-gate  * Solaris *Newboot* environments. It has two primary tasks:
317c478bd9Sstevel@tonic-gate  * 	- Allow end users to manage bootability of Newboot Solaris instances
327c478bd9Sstevel@tonic-gate  *	- Provide services to other subsystems in Solaris (primarily Install)
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate /* Headers */
367c478bd9Sstevel@tonic-gate #include <stdio.h>
377c478bd9Sstevel@tonic-gate #include <errno.h>
387c478bd9Sstevel@tonic-gate #include <stdlib.h>
397c478bd9Sstevel@tonic-gate #include <string.h>
407c478bd9Sstevel@tonic-gate #include <unistd.h>
417c478bd9Sstevel@tonic-gate #include <sys/types.h>
427c478bd9Sstevel@tonic-gate #include <sys/stat.h>
437c478bd9Sstevel@tonic-gate #include <stdarg.h>
447c478bd9Sstevel@tonic-gate #include <limits.h>
457c478bd9Sstevel@tonic-gate #include <signal.h>
467c478bd9Sstevel@tonic-gate #include <sys/wait.h>
477c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
487c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
497c478bd9Sstevel@tonic-gate #include <libnvpair.h>
507c478bd9Sstevel@tonic-gate #include <ftw.h>
517c478bd9Sstevel@tonic-gate #include <fcntl.h>
527c478bd9Sstevel@tonic-gate #include <strings.h>
532449e17fSsherrym #include <utime.h>
547c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
557c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
5658091fd8Ssetje #include <sys/param.h>
57*e7cbe64fSgw25295 #include <sys/sysmacros.h>
58986fd29aSsetje 
59986fd29aSsetje #if !defined(_OPB)
602449e17fSsherrym #include <sys/ucode.h>
612449e17fSsherrym #endif
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #include <pwd.h>
647c478bd9Sstevel@tonic-gate #include <grp.h>
657c478bd9Sstevel@tonic-gate #include <device_info.h>
667c478bd9Sstevel@tonic-gate #include <locale.h>
677c478bd9Sstevel@tonic-gate #include <assert.h>
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #include "message.h"
70ae115bc7Smrj #include "bootadm.h"
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #ifndef TEXT_DOMAIN
737c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
747c478bd9Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /* Type definitions */
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /* Primary subcmds */
797c478bd9Sstevel@tonic-gate typedef enum {
807c478bd9Sstevel@tonic-gate 	BAM_MENU = 3,
817c478bd9Sstevel@tonic-gate 	BAM_ARCHIVE
827c478bd9Sstevel@tonic-gate } subcmd_t;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate typedef enum {
857c478bd9Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
867c478bd9Sstevel@tonic-gate     OPT_REQ,		/* option required */
877c478bd9Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
887c478bd9Sstevel@tonic-gate } option_t;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate typedef struct {
917c478bd9Sstevel@tonic-gate 	char	*subcmd;
927c478bd9Sstevel@tonic-gate 	option_t option;
937c478bd9Sstevel@tonic-gate 	error_t (*handler)();
941a97e40eSvikram 	int	unpriv;			/* is this an unprivileged command */
957c478bd9Sstevel@tonic-gate } subcmd_defn_t;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
987c478bd9Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
997c478bd9Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
1027c478bd9Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
1037c478bd9Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1047c478bd9Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
10540541d5dSvikram #define	STUBBOOT		"/stubboot"
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate /* lock related */
1087c478bd9Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1097c478bd9Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1107c478bd9Sstevel@tonic-gate 
111986fd29aSsetje #define	CREATE_RAMDISK		"boot/solaris/bin/create_ramdisk"
112986fd29aSsetje #define	CREATE_DISKMAP		"boot/solaris/bin/create_diskmap"
113986fd29aSsetje #define	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
1147c478bd9Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
1157c478bd9Sstevel@tonic-gate 
116b610f78eSvikram #define	GRUB_slice		"/etc/lu/GRUB_slice"
117b610f78eSvikram #define	GRUB_root		"/etc/lu/GRUB_root"
118b610f78eSvikram #define	GRUB_backup_menu	"/etc/lu/GRUB_backup_menu"
119b610f78eSvikram #define	GRUB_slice_mntpt	"/tmp/GRUB_slice_mntpt"
120b610f78eSvikram #define	LU_ACTIVATE_FILE	"/etc/lu/DelayUpdate/activate.sh"
121fbac2b2bSvikram #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
122fbac2b2bSvikram #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
123b610f78eSvikram 
124b610f78eSvikram #define	INSTALLGRUB		"/sbin/installgrub"
125b610f78eSvikram #define	STAGE1			"/boot/grub/stage1"
126b610f78eSvikram #define	STAGE2			"/boot/grub/stage2"
127b610f78eSvikram 
1287c478bd9Sstevel@tonic-gate /*
12998892a30Snadkarni  * The following two defines are used to detect and create the correct
13098892a30Snadkarni  * boot archive  when safemode patching is underway.  LOFS_PATCH_FILE is a
13198892a30Snadkarni  * contracted private interface between bootadm and the install
13298892a30Snadkarni  * consolidation.  It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE
13398892a30Snadkarni  * is applied.
13498892a30Snadkarni  */
13598892a30Snadkarni 
13698892a30Snadkarni #define	LOFS_PATCH_FILE		"/var/run/.patch_loopback_mode"
13798892a30Snadkarni #define	LOFS_PATCH_MNT		"/var/run/.patch_root_loopbackmnt"
13898892a30Snadkarni 
13998892a30Snadkarni /*
1407c478bd9Sstevel@tonic-gate  * Default file attributes
1417c478bd9Sstevel@tonic-gate  */
1427c478bd9Sstevel@tonic-gate #define	DEFAULT_DEV_MODE	0644	/* default permissions */
1437c478bd9Sstevel@tonic-gate #define	DEFAULT_DEV_UID		0	/* user root */
1447c478bd9Sstevel@tonic-gate #define	DEFAULT_DEV_GID		3	/* group sys */
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate /*
1477c478bd9Sstevel@tonic-gate  * Menu related
1487c478bd9Sstevel@tonic-gate  * menu_cmd_t and menu_cmds must be kept in sync
1497c478bd9Sstevel@tonic-gate  */
150ae115bc7Smrj char *menu_cmds[] = {
1517c478bd9Sstevel@tonic-gate 	"default",	/* DEFAULT_CMD */
1527c478bd9Sstevel@tonic-gate 	"timeout",	/* TIMEOUT_CMD */
1537c478bd9Sstevel@tonic-gate 	"title",	/* TITLE_CMD */
1547c478bd9Sstevel@tonic-gate 	"root",		/* ROOT_CMD */
1557c478bd9Sstevel@tonic-gate 	"kernel",	/* KERNEL_CMD */
156ae115bc7Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1577c478bd9Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
158ae115bc7Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1597c478bd9Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1607c478bd9Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
161ae115bc7Smrj 	"chainloader",	/* CHAINLOADER_CMD */
162ae115bc7Smrj 	"args",		/* ARGS_CMD */
1637c478bd9Sstevel@tonic-gate 	NULL
1647c478bd9Sstevel@tonic-gate };
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate #define	OPT_ENTRY_NUM	"entry"
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate /*
1697c478bd9Sstevel@tonic-gate  * archive related
1707c478bd9Sstevel@tonic-gate  */
1717c478bd9Sstevel@tonic-gate typedef struct {
1727c478bd9Sstevel@tonic-gate 	line_t *head;
1737c478bd9Sstevel@tonic-gate 	line_t *tail;
1747c478bd9Sstevel@tonic-gate } filelist_t;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
1777c478bd9Sstevel@tonic-gate #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
1807c478bd9Sstevel@tonic-gate #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
1817c478bd9Sstevel@tonic-gate #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
1827c478bd9Sstevel@tonic-gate #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate /* Globals */
185ae115bc7Smrj int bam_verbose;
186ae115bc7Smrj int bam_force;
1877c478bd9Sstevel@tonic-gate static char *prog;
1887c478bd9Sstevel@tonic-gate static subcmd_t bam_cmd;
1897c478bd9Sstevel@tonic-gate static char *bam_root;
1907c478bd9Sstevel@tonic-gate static int bam_rootlen;
1917c478bd9Sstevel@tonic-gate static int bam_root_readonly;
19240541d5dSvikram static int bam_alt_root;
1937c478bd9Sstevel@tonic-gate static char *bam_subcmd;
1947c478bd9Sstevel@tonic-gate static char *bam_opt;
1957c478bd9Sstevel@tonic-gate static int bam_debug;
1967c478bd9Sstevel@tonic-gate static char **bam_argv;
1977c478bd9Sstevel@tonic-gate static int bam_argc;
1987c478bd9Sstevel@tonic-gate static int bam_check;
1997c478bd9Sstevel@tonic-gate static int bam_smf_check;
2007c478bd9Sstevel@tonic-gate static int bam_lock_fd = -1;
201*e7cbe64fSgw25295 static int bam_zfs;
2027c478bd9Sstevel@tonic-gate static char rootbuf[PATH_MAX] = "/";
203b610f78eSvikram static int bam_update_all;
204d876c67dSjg static int bam_alt_platform;
205d876c67dSjg static char *bam_platform;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate /* function prototypes */
208986fd29aSsetje static void parse_args_internal(int, char *[]);
209986fd29aSsetje static void parse_args(int, char *argv[]);
210986fd29aSsetje static error_t bam_menu(char *, char *, int, char *[]);
211986fd29aSsetje static error_t bam_archive(char *, char *);
2127c478bd9Sstevel@tonic-gate 
213986fd29aSsetje static void bam_print(char *, ...);
214986fd29aSsetje static void bam_exit(int);
2157c478bd9Sstevel@tonic-gate static void bam_lock(void);
2167c478bd9Sstevel@tonic-gate static void bam_unlock(void);
2177c478bd9Sstevel@tonic-gate 
218986fd29aSsetje static int exec_cmd(char *, filelist_t *);
219986fd29aSsetje static error_t read_globals(menu_t *, char *, char *, int);
2207c478bd9Sstevel@tonic-gate 
221986fd29aSsetje static menu_t *menu_read(char *);
222986fd29aSsetje static error_t menu_write(char *, menu_t *);
223986fd29aSsetje static void linelist_free(line_t *);
224986fd29aSsetje static void menu_free(menu_t *);
225986fd29aSsetje static void line_free(line_t *);
226986fd29aSsetje static void filelist_free(filelist_t *);
227986fd29aSsetje static error_t list2file(char *, char *, char *, line_t *);
228986fd29aSsetje static error_t list_entry(menu_t *, char *, char *);
229986fd29aSsetje static error_t delete_all_entries(menu_t *, char *, char *);
230986fd29aSsetje static error_t update_entry(menu_t *, char *, char *);
231986fd29aSsetje static error_t update_temp(menu_t *, char *, char *);
2327c478bd9Sstevel@tonic-gate 
233986fd29aSsetje static error_t update_archive(char *, char *);
234986fd29aSsetje static error_t list_archive(char *, char *);
235986fd29aSsetje static error_t update_all(char *, char *);
236986fd29aSsetje static error_t read_list(char *, filelist_t *);
237986fd29aSsetje static error_t set_global(menu_t *, char *, int);
238986fd29aSsetje static error_t set_option(menu_t *, char *, char *);
239986fd29aSsetje static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
240986fd29aSsetje static char *expand_path(const char *);
2417c478bd9Sstevel@tonic-gate 
242986fd29aSsetje static long s_strtol(char *);
243986fd29aSsetje static int s_fputs(char *, FILE *);
2447c478bd9Sstevel@tonic-gate 
245986fd29aSsetje static char *s_strdup(char *);
2467c478bd9Sstevel@tonic-gate static int is_readonly(char *);
247*e7cbe64fSgw25295 static int is_zfs(char *, char **);
2487c478bd9Sstevel@tonic-gate static int is_amd64(void);
249986fd29aSsetje static int is_sun4u(void);
250986fd29aSsetje static int is_sun4v(void);
2517c478bd9Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2527c478bd9Sstevel@tonic-gate 
253986fd29aSsetje #if !defined(_OPB)
2542449e17fSsherrym static void ucode_install();
2552449e17fSsherrym #endif
2562449e17fSsherrym 
2577c478bd9Sstevel@tonic-gate /* Menu related sub commands */
2587c478bd9Sstevel@tonic-gate static subcmd_defn_t menu_subcmds[] = {
2591a97e40eSvikram 	"set_option",		OPT_OPTIONAL,	set_option, 0,	/* PUB */
2601a97e40eSvikram 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
2611a97e40eSvikram 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
2621a97e40eSvikram 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
2631a97e40eSvikram 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
264ae115bc7Smrj 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
2651a97e40eSvikram 	NULL,			0,		NULL, 0	/* must be last */
2667c478bd9Sstevel@tonic-gate };
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate /* Archive related sub commands */
2697c478bd9Sstevel@tonic-gate static subcmd_defn_t arch_subcmds[] = {
2701a97e40eSvikram 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
2711a97e40eSvikram 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
2721a97e40eSvikram 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
2731a97e40eSvikram 	NULL,			0,		NULL, 0	/* must be last */
2747c478bd9Sstevel@tonic-gate };
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate static struct {
2777c478bd9Sstevel@tonic-gate 	nvlist_t *new_nvlp;
2787c478bd9Sstevel@tonic-gate 	nvlist_t *old_nvlp;
2797c478bd9Sstevel@tonic-gate 	int need_update;
2807c478bd9Sstevel@tonic-gate } walk_arg;
2817c478bd9Sstevel@tonic-gate 
28258091fd8Ssetje 
2839632cfadSsetje struct safefile {
28458091fd8Ssetje 	char *name;
28558091fd8Ssetje 	struct safefile *next;
28658091fd8Ssetje };
28758091fd8Ssetje 
288ae115bc7Smrj static struct safefile *safefiles = NULL;
28958091fd8Ssetje #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
29058091fd8Ssetje 
2917c478bd9Sstevel@tonic-gate static void
2927c478bd9Sstevel@tonic-gate usage(void)
2937c478bd9Sstevel@tonic-gate {
2947c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "USAGE:\n");
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	/* archive usage */
298d876c67dSjg 	(void) fprintf(stderr,
299d876c67dSjg 	    "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog);
300d876c67dSjg 	(void) fprintf(stderr,
301d876c67dSjg 	    "\t%s list-archive [-R altroot [-p platform>]]\n", prog);
302986fd29aSsetje #if !defined(_OPB)
3037c478bd9Sstevel@tonic-gate 	/* x86 only */
3047c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
3057c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
3067c478bd9Sstevel@tonic-gate #endif
3077c478bd9Sstevel@tonic-gate }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate int
3107c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
3117c478bd9Sstevel@tonic-gate {
3127c478bd9Sstevel@tonic-gate 	error_t ret;
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
3157c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
3187c478bd9Sstevel@tonic-gate 		prog = argv[0];
3197c478bd9Sstevel@tonic-gate 	} else {
3207c478bd9Sstevel@tonic-gate 		prog++;
3217c478bd9Sstevel@tonic-gate 	}
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	/*
3257c478bd9Sstevel@tonic-gate 	 * Don't depend on caller's umask
3267c478bd9Sstevel@tonic-gate 	 */
3277c478bd9Sstevel@tonic-gate 	(void) umask(0022);
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	parse_args(argc, argv);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	switch (bam_cmd) {
3327c478bd9Sstevel@tonic-gate 		case BAM_MENU:
3337c478bd9Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
3347c478bd9Sstevel@tonic-gate 			break;
3357c478bd9Sstevel@tonic-gate 		case BAM_ARCHIVE:
3367c478bd9Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
3377c478bd9Sstevel@tonic-gate 			break;
3387c478bd9Sstevel@tonic-gate 		default:
3397c478bd9Sstevel@tonic-gate 			usage();
3407c478bd9Sstevel@tonic-gate 			bam_exit(1);
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
3447c478bd9Sstevel@tonic-gate 		bam_exit(1);
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	bam_unlock();
3477c478bd9Sstevel@tonic-gate 	return (0);
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate /*
3517c478bd9Sstevel@tonic-gate  * Equivalence of public and internal commands:
3527c478bd9Sstevel@tonic-gate  *	update-archive  -- -a update
3537c478bd9Sstevel@tonic-gate  *	list-archive	-- -a list
3547c478bd9Sstevel@tonic-gate  *	set-menu	-- -m set_option
3557c478bd9Sstevel@tonic-gate  *	list-menu	-- -m list_entry
3567c478bd9Sstevel@tonic-gate  *	update-menu	-- -m update_entry
3577c478bd9Sstevel@tonic-gate  */
3587c478bd9Sstevel@tonic-gate static struct cmd_map {
3597c478bd9Sstevel@tonic-gate 	char *bam_cmdname;
3607c478bd9Sstevel@tonic-gate 	int bam_cmd;
3617c478bd9Sstevel@tonic-gate 	char *bam_subcmd;
3627c478bd9Sstevel@tonic-gate } cmd_map[] = {
3637c478bd9Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
3647c478bd9Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
3657c478bd9Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
3667c478bd9Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
3677c478bd9Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
3687c478bd9Sstevel@tonic-gate 	{ NULL,			0,		NULL}
3697c478bd9Sstevel@tonic-gate };
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate /*
3727c478bd9Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
3737c478bd9Sstevel@tonic-gate  */
3747c478bd9Sstevel@tonic-gate static void
3757c478bd9Sstevel@tonic-gate parse_args(int argc, char *argv[])
3767c478bd9Sstevel@tonic-gate {
3777c478bd9Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	/* command conforming to the final spec */
3807c478bd9Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
3817c478bd9Sstevel@tonic-gate 		/*
3827c478bd9Sstevel@tonic-gate 		 * Map commands to internal table.
3837c478bd9Sstevel@tonic-gate 		 */
3847c478bd9Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
3857c478bd9Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
3867c478bd9Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
3877c478bd9Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
3887c478bd9Sstevel@tonic-gate 				break;
3897c478bd9Sstevel@tonic-gate 			}
3907c478bd9Sstevel@tonic-gate 			cmp++;
3917c478bd9Sstevel@tonic-gate 		}
3927c478bd9Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
3937c478bd9Sstevel@tonic-gate 			usage();
3947c478bd9Sstevel@tonic-gate 			bam_exit(1);
3957c478bd9Sstevel@tonic-gate 		}
3967c478bd9Sstevel@tonic-gate 		argc--;
3977c478bd9Sstevel@tonic-gate 		argv++;
3987c478bd9Sstevel@tonic-gate 	}
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	parse_args_internal(argc, argv);
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate /*
4047c478bd9Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
4057c478bd9Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
4067c478bd9Sstevel@tonic-gate  *	-a update	-- update-archive
4077c478bd9Sstevel@tonic-gate  *	-a list		-- list-archive
4087c478bd9Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
4097c478bd9Sstevel@tonic-gate  *	-m update_entry	-- update-menu
4107c478bd9Sstevel@tonic-gate  *	-m list_entry	-- list-menu
4117c478bd9Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
4127c478bd9Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
4137c478bd9Sstevel@tonic-gate  */
4147c478bd9Sstevel@tonic-gate static void
4157c478bd9Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
4167c478bd9Sstevel@tonic-gate {
4177c478bd9Sstevel@tonic-gate 	int c, error;
4187c478bd9Sstevel@tonic-gate 	extern char *optarg;
4197c478bd9Sstevel@tonic-gate 	extern int optind, opterr;
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	/* Suppress error message from getopt */
4227c478bd9Sstevel@tonic-gate 	opterr = 0;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	error = 0;
425*e7cbe64fSgw25295 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:p:Z")) != -1) {
4267c478bd9Sstevel@tonic-gate 		switch (c) {
4277c478bd9Sstevel@tonic-gate 		case 'a':
4287c478bd9Sstevel@tonic-gate 			if (bam_cmd) {
4297c478bd9Sstevel@tonic-gate 				error = 1;
4307c478bd9Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4317c478bd9Sstevel@tonic-gate 			}
4327c478bd9Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
4337c478bd9Sstevel@tonic-gate 			bam_subcmd = optarg;
4347c478bd9Sstevel@tonic-gate 			break;
4357c478bd9Sstevel@tonic-gate 		case 'd':
4367c478bd9Sstevel@tonic-gate 			if (bam_debug) {
4377c478bd9Sstevel@tonic-gate 				error = 1;
4387c478bd9Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4397c478bd9Sstevel@tonic-gate 			}
4407c478bd9Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
4417c478bd9Sstevel@tonic-gate 			break;
4427c478bd9Sstevel@tonic-gate 		case 'f':
4437c478bd9Sstevel@tonic-gate 			if (bam_force) {
4447c478bd9Sstevel@tonic-gate 				error = 1;
4457c478bd9Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4467c478bd9Sstevel@tonic-gate 			}
4477c478bd9Sstevel@tonic-gate 			bam_force = 1;
4487c478bd9Sstevel@tonic-gate 			break;
4497c478bd9Sstevel@tonic-gate 		case 'm':
4507c478bd9Sstevel@tonic-gate 			if (bam_cmd) {
4517c478bd9Sstevel@tonic-gate 				error = 1;
4527c478bd9Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4537c478bd9Sstevel@tonic-gate 			}
4547c478bd9Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
4557c478bd9Sstevel@tonic-gate 			bam_subcmd = optarg;
4567c478bd9Sstevel@tonic-gate 			break;
4577c478bd9Sstevel@tonic-gate 		case 'n':
4587c478bd9Sstevel@tonic-gate 			if (bam_check) {
4597c478bd9Sstevel@tonic-gate 				error = 1;
4607c478bd9Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4617c478bd9Sstevel@tonic-gate 			}
4627c478bd9Sstevel@tonic-gate 			bam_check = 1;
4637c478bd9Sstevel@tonic-gate 			break;
4647c478bd9Sstevel@tonic-gate 		case 'o':
4657c478bd9Sstevel@tonic-gate 			if (bam_opt) {
4667c478bd9Sstevel@tonic-gate 				error = 1;
4677c478bd9Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4687c478bd9Sstevel@tonic-gate 			}
4697c478bd9Sstevel@tonic-gate 			bam_opt = optarg;
4707c478bd9Sstevel@tonic-gate 			break;
4717c478bd9Sstevel@tonic-gate 		case 'v':
4727c478bd9Sstevel@tonic-gate 			if (bam_verbose) {
4737c478bd9Sstevel@tonic-gate 				error = 1;
4747c478bd9Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4757c478bd9Sstevel@tonic-gate 			}
4767c478bd9Sstevel@tonic-gate 			bam_verbose = 1;
4777c478bd9Sstevel@tonic-gate 			break;
4788c1b6884Sszhou 		case 'C':
4798c1b6884Sszhou 			bam_smf_check = 1;
4808c1b6884Sszhou 			break;
4817c478bd9Sstevel@tonic-gate 		case 'R':
4827c478bd9Sstevel@tonic-gate 			if (bam_root) {
4837c478bd9Sstevel@tonic-gate 				error = 1;
4847c478bd9Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4857c478bd9Sstevel@tonic-gate 				break;
4867c478bd9Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
4877c478bd9Sstevel@tonic-gate 				error = 1;
4887c478bd9Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
4897c478bd9Sstevel@tonic-gate 				    strerror(errno));
4907c478bd9Sstevel@tonic-gate 				break;
4917c478bd9Sstevel@tonic-gate 			}
49240541d5dSvikram 			bam_alt_root = 1;
4937c478bd9Sstevel@tonic-gate 			bam_root = rootbuf;
4947c478bd9Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
4957c478bd9Sstevel@tonic-gate 			break;
496d876c67dSjg 		case 'p':
497d876c67dSjg 			bam_alt_platform = 1;
498d876c67dSjg 			bam_platform = optarg;
499d876c67dSjg 			if ((strcmp(bam_platform, "i86pc") != 0) &&
500d876c67dSjg 			    (strcmp(bam_platform, "sun4u") != 0) &&
501d876c67dSjg 			    (strcmp(bam_platform, "sun4v") != 0)) {
502d876c67dSjg 				error = 1;
503d876c67dSjg 				bam_error(INVALID_PLAT, bam_platform);
504d876c67dSjg 			}
505d876c67dSjg 			break;
506*e7cbe64fSgw25295 		case 'Z':
507*e7cbe64fSgw25295 			bam_zfs = 1;
508*e7cbe64fSgw25295 			break;
5097c478bd9Sstevel@tonic-gate 		case '?':
5107c478bd9Sstevel@tonic-gate 			error = 1;
5117c478bd9Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
5127c478bd9Sstevel@tonic-gate 			break;
5137c478bd9Sstevel@tonic-gate 		default :
5147c478bd9Sstevel@tonic-gate 			error = 1;
5157c478bd9Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
5167c478bd9Sstevel@tonic-gate 			break;
5177c478bd9Sstevel@tonic-gate 		}
5187c478bd9Sstevel@tonic-gate 	}
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	/*
521d876c67dSjg 	 * An alternate platform requires an alternate root
522d876c67dSjg 	 */
523d876c67dSjg 	if (bam_alt_platform && bam_alt_root == 0) {
524d876c67dSjg 		usage();
525d876c67dSjg 		bam_exit(0);
526d876c67dSjg 	}
527d876c67dSjg 
528d876c67dSjg 	/*
5297c478bd9Sstevel@tonic-gate 	 * A command option must be specfied
5307c478bd9Sstevel@tonic-gate 	 */
5317c478bd9Sstevel@tonic-gate 	if (!bam_cmd) {
5327c478bd9Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
5337c478bd9Sstevel@tonic-gate 			usage();
5347c478bd9Sstevel@tonic-gate 			bam_exit(0);
5357c478bd9Sstevel@tonic-gate 		}
5367c478bd9Sstevel@tonic-gate 		bam_error(NEED_CMD);
5377c478bd9Sstevel@tonic-gate 		error = 1;
5387c478bd9Sstevel@tonic-gate 	}
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	if (error) {
5417c478bd9Sstevel@tonic-gate 		usage();
5427c478bd9Sstevel@tonic-gate 		bam_exit(1);
5437c478bd9Sstevel@tonic-gate 	}
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	if (optind > argc) {
5467c478bd9Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
5477c478bd9Sstevel@tonic-gate 		bam_exit(1);
5487c478bd9Sstevel@tonic-gate 	} else if (optind < argc) {
5497c478bd9Sstevel@tonic-gate 		bam_argv = &argv[optind];
5507c478bd9Sstevel@tonic-gate 		bam_argc = argc - optind;
5517c478bd9Sstevel@tonic-gate 	}
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	/*
5547c478bd9Sstevel@tonic-gate 	 * -n implies verbose mode
5557c478bd9Sstevel@tonic-gate 	 */
5567c478bd9Sstevel@tonic-gate 	if (bam_check)
5577c478bd9Sstevel@tonic-gate 		bam_verbose = 1;
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate static error_t
5617c478bd9Sstevel@tonic-gate check_subcmd_and_options(
5627c478bd9Sstevel@tonic-gate 	char *subcmd,
5637c478bd9Sstevel@tonic-gate 	char *opt,
5647c478bd9Sstevel@tonic-gate 	subcmd_defn_t *table,
5657c478bd9Sstevel@tonic-gate 	error_t (**fp)())
5667c478bd9Sstevel@tonic-gate {
5677c478bd9Sstevel@tonic-gate 	int i;
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	if (subcmd == NULL) {
5707c478bd9Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
5717c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
5727c478bd9Sstevel@tonic-gate 	}
5737c478bd9Sstevel@tonic-gate 
5741a97e40eSvikram 	if (bam_argc != 0 || bam_argv) {
5751a97e40eSvikram 		if (strcmp(subcmd, "set_option") != 0 || bam_argc != 1) {
5761a97e40eSvikram 			bam_error(TRAILING_ARGS);
5771a97e40eSvikram 			usage();
5781a97e40eSvikram 			return (BAM_ERROR);
5791a97e40eSvikram 		}
5801a97e40eSvikram 	}
5811a97e40eSvikram 
5827c478bd9Sstevel@tonic-gate 	if (bam_root == NULL) {
5837c478bd9Sstevel@tonic-gate 		bam_root = rootbuf;
5847c478bd9Sstevel@tonic-gate 		bam_rootlen = 1;
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	/* verify that subcmd is valid */
5887c478bd9Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
5897c478bd9Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
5907c478bd9Sstevel@tonic-gate 			break;
5917c478bd9Sstevel@tonic-gate 	}
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
5947c478bd9Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
5957c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5981a97e40eSvikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
5991a97e40eSvikram 		bam_error(MUST_BE_ROOT);
6001a97e40eSvikram 		return (BAM_ERROR);
6011a97e40eSvikram 	}
6021a97e40eSvikram 
6031a97e40eSvikram 	/*
6041a97e40eSvikram 	 * Currently only privileged commands need a lock
6051a97e40eSvikram 	 */
6061a97e40eSvikram 	if (table[i].unpriv == 0)
6071a97e40eSvikram 		bam_lock();
6081a97e40eSvikram 
6097c478bd9Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
6107c478bd9Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
6117c478bd9Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
6127c478bd9Sstevel@tonic-gate 			if (opt)
6137c478bd9Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
6147c478bd9Sstevel@tonic-gate 			else
6157c478bd9Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
6167c478bd9Sstevel@tonic-gate 			return (BAM_ERROR);
6177c478bd9Sstevel@tonic-gate 		}
6187c478bd9Sstevel@tonic-gate 	}
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	*fp = table[i].handler;
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
6237c478bd9Sstevel@tonic-gate }
6247c478bd9Sstevel@tonic-gate 
625b610f78eSvikram 
626b610f78eSvikram static char *
62740541d5dSvikram mount_grub_slice(int *mnted, char **physlice, char **logslice, char **fs_type)
628b610f78eSvikram {
629b610f78eSvikram 	struct extmnttab mnt;
630b610f78eSvikram 	struct stat sb;
631b610f78eSvikram 	char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32];
632f0f2c544Svikram 	char cmd[PATH_MAX];
633b610f78eSvikram 	char *mntpt;
634b610f78eSvikram 	int p, l, f;
635b610f78eSvikram 	FILE *fp;
636b610f78eSvikram 
637b610f78eSvikram 	assert(mnted);
638b610f78eSvikram 	*mnted = 0;
639b610f78eSvikram 
640b610f78eSvikram 	/*
64140541d5dSvikram 	 * physlice, logslice, fs_type  args may be NULL
642b610f78eSvikram 	 */
643b610f78eSvikram 	if (physlice)
644b610f78eSvikram 		*physlice = NULL;
64540541d5dSvikram 	if (logslice)
64640541d5dSvikram 		*logslice = NULL;
64740541d5dSvikram 	if (fs_type)
64840541d5dSvikram 		*fs_type = NULL;
649b610f78eSvikram 
650b610f78eSvikram 	if (stat(GRUB_slice, &sb) != 0) {
651b610f78eSvikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
652b610f78eSvikram 		return (NULL);
653b610f78eSvikram 	}
654b610f78eSvikram 
655b610f78eSvikram 	fp = fopen(GRUB_slice, "r");
656b610f78eSvikram 	if (fp == NULL) {
657b610f78eSvikram 		bam_error(OPEN_FAIL, GRUB_slice, strerror(errno));
658b610f78eSvikram 		return (NULL);
659b610f78eSvikram 	}
660b610f78eSvikram 
661b610f78eSvikram 	dev[0] = fstype[0] = phys[0] = '\0';
662b610f78eSvikram 	p = sizeof ("PHYS_SLICE=") - 1;
663b610f78eSvikram 	l = sizeof ("LOG_SLICE=") - 1;
664b610f78eSvikram 	f = sizeof ("LOG_FSTYP=") - 1;
665b610f78eSvikram 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
666b610f78eSvikram 		if (strncmp(buf, "PHYS_SLICE=", p) == 0) {
667b610f78eSvikram 			(void) strlcpy(phys, buf + p, sizeof (phys));
668b610f78eSvikram 			continue;
669b610f78eSvikram 		}
670b610f78eSvikram 		if (strncmp(buf, "LOG_SLICE=", l) == 0) {
671b610f78eSvikram 			(void) strlcpy(dev, buf + l, sizeof (dev));
672b610f78eSvikram 			continue;
673b610f78eSvikram 		}
674b610f78eSvikram 		if (strncmp(buf, "LOG_FSTYP=", f) == 0) {
675b610f78eSvikram 			(void) strlcpy(fstype, buf + f, sizeof (fstype));
676b610f78eSvikram 			continue;
677b610f78eSvikram 		}
678b610f78eSvikram 	}
679b610f78eSvikram 	(void) fclose(fp);
680b610f78eSvikram 
681b610f78eSvikram 	if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') {
682b610f78eSvikram 		bam_error(BAD_SLICE_FILE, GRUB_slice);
683b610f78eSvikram 		return (NULL);
684b610f78eSvikram 	}
685b610f78eSvikram 
686b610f78eSvikram 	if (physlice) {
687b610f78eSvikram 		*physlice = s_strdup(phys);
688b610f78eSvikram 	}
68940541d5dSvikram 	if (logslice) {
69040541d5dSvikram 		*logslice = s_strdup(dev);
69140541d5dSvikram 	}
69240541d5dSvikram 	if (fs_type) {
69340541d5dSvikram 		*fs_type = s_strdup(fstype);
69440541d5dSvikram 	}
695b610f78eSvikram 
696b610f78eSvikram 	/*
697b610f78eSvikram 	 * Check if the slice is already mounted
698b610f78eSvikram 	 */
699b610f78eSvikram 	fp = fopen(MNTTAB, "r");
700b610f78eSvikram 	if (fp == NULL) {
701b610f78eSvikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
70240541d5dSvikram 		goto error;
703b610f78eSvikram 	}
704b610f78eSvikram 
705b610f78eSvikram 	resetmnttab(fp);
706b610f78eSvikram 
707b610f78eSvikram 	mntpt = NULL;
708b610f78eSvikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
709b610f78eSvikram 		if (strcmp(mnt.mnt_special, dev) == 0) {
710b610f78eSvikram 			mntpt = s_strdup(mnt.mnt_mountp);
711b610f78eSvikram 			break;
712b610f78eSvikram 		}
713b610f78eSvikram 	}
714b610f78eSvikram 
715b610f78eSvikram 	(void) fclose(fp);
716b610f78eSvikram 
717b610f78eSvikram 	if (mntpt) {
718b610f78eSvikram 		return (mntpt);
719b610f78eSvikram 	}
720b610f78eSvikram 
721b610f78eSvikram 
722b610f78eSvikram 	/*
723b610f78eSvikram 	 * GRUB slice is not mounted, we need to mount it now.
724b610f78eSvikram 	 * First create the mountpoint
725b610f78eSvikram 	 */
726b610f78eSvikram 	mntpt = s_calloc(1, PATH_MAX);
727b610f78eSvikram 	(void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid());
728b610f78eSvikram 	if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) {
729b610f78eSvikram 		bam_error(MKDIR_FAILED, mntpt, strerror(errno));
730b610f78eSvikram 		free(mntpt);
73140541d5dSvikram 		goto error;
732b610f78eSvikram 	}
733b610f78eSvikram 
734f0f2c544Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount -F %s %s %s",
735f0f2c544Svikram 	    fstype, dev, mntpt);
736f0f2c544Svikram 
737986fd29aSsetje 	if (exec_cmd(cmd, NULL) != 0) {
73840541d5dSvikram 		bam_error(MOUNT_FAILED, dev, fstype);
739b610f78eSvikram 		if (rmdir(mntpt) != 0) {
740b610f78eSvikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
741b610f78eSvikram 		}
742b610f78eSvikram 		free(mntpt);
74340541d5dSvikram 		goto error;
744b610f78eSvikram 	}
745b610f78eSvikram 
746b610f78eSvikram 	*mnted = 1;
747b610f78eSvikram 	return (mntpt);
74840541d5dSvikram 
74940541d5dSvikram error:
75040541d5dSvikram 	if (physlice) {
75140541d5dSvikram 		free(*physlice);
75240541d5dSvikram 		*physlice = NULL;
75340541d5dSvikram 	}
75440541d5dSvikram 	if (logslice) {
75540541d5dSvikram 		free(*logslice);
75640541d5dSvikram 		*logslice = NULL;
75740541d5dSvikram 	}
75840541d5dSvikram 	if (fs_type) {
75940541d5dSvikram 		free(*fs_type);
76040541d5dSvikram 		*fs_type = NULL;
76140541d5dSvikram 	}
76240541d5dSvikram 	return (NULL);
763b610f78eSvikram }
764b610f78eSvikram 
765b610f78eSvikram static void
76640541d5dSvikram umount_grub_slice(
76740541d5dSvikram 	int mnted,
76840541d5dSvikram 	char *mntpt,
76940541d5dSvikram 	char *physlice,
77040541d5dSvikram 	char *logslice,
77140541d5dSvikram 	char *fs_type)
772b610f78eSvikram {
773f0f2c544Svikram 	char cmd[PATH_MAX];
774f0f2c544Svikram 
775b610f78eSvikram 	/*
776b610f78eSvikram 	 * If we have not dealt with GRUB slice
777b610f78eSvikram 	 * we have nothing to do - just return.
778b610f78eSvikram 	 */
779b610f78eSvikram 	if (mntpt == NULL)
780b610f78eSvikram 		return;
781b610f78eSvikram 
782b610f78eSvikram 
783b610f78eSvikram 	/*
784b610f78eSvikram 	 * If we mounted the filesystem earlier in mount_grub_slice()
785b610f78eSvikram 	 * unmount it now.
786b610f78eSvikram 	 */
787b610f78eSvikram 	if (mnted) {
788f0f2c544Svikram 		(void) snprintf(cmd, sizeof (cmd), "/sbin/umount %s",
789f0f2c544Svikram 		    mntpt);
790986fd29aSsetje 		if (exec_cmd(cmd, NULL) != 0) {
791f0f2c544Svikram 			bam_error(UMOUNT_FAILED, mntpt);
792b610f78eSvikram 		}
793b610f78eSvikram 		if (rmdir(mntpt) != 0) {
794b610f78eSvikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
795b610f78eSvikram 		}
796b610f78eSvikram 	}
79740541d5dSvikram 
798b610f78eSvikram 	if (physlice)
799b610f78eSvikram 		free(physlice);
80040541d5dSvikram 	if (logslice)
80140541d5dSvikram 		free(logslice);
80240541d5dSvikram 	if (fs_type)
80340541d5dSvikram 		free(fs_type);
80440541d5dSvikram 
805b610f78eSvikram 	free(mntpt);
806b610f78eSvikram }
807b610f78eSvikram 
80840541d5dSvikram static char *
80940541d5dSvikram use_stubboot(void)
81040541d5dSvikram {
81140541d5dSvikram 	int mnted;
81240541d5dSvikram 	struct stat sb;
81340541d5dSvikram 	struct extmnttab mnt;
81440541d5dSvikram 	FILE *fp;
81540541d5dSvikram 	char cmd[PATH_MAX];
81640541d5dSvikram 
81740541d5dSvikram 	if (stat(STUBBOOT, &sb) != 0) {
81840541d5dSvikram 		bam_error(STUBBOOT_DIR_NOT_FOUND);
81940541d5dSvikram 		return (NULL);
82040541d5dSvikram 	}
82140541d5dSvikram 
82240541d5dSvikram 	/*
82340541d5dSvikram 	 * Check if stubboot is mounted. If not, mount it
82440541d5dSvikram 	 */
82540541d5dSvikram 	fp = fopen(MNTTAB, "r");
82640541d5dSvikram 	if (fp == NULL) {
82740541d5dSvikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
82840541d5dSvikram 		return (NULL);
82940541d5dSvikram 	}
83040541d5dSvikram 
83140541d5dSvikram 	resetmnttab(fp);
83240541d5dSvikram 
83340541d5dSvikram 	mnted = 0;
83440541d5dSvikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
83540541d5dSvikram 		if (strcmp(mnt.mnt_mountp, STUBBOOT) == 0) {
83640541d5dSvikram 			mnted = 1;
83740541d5dSvikram 			break;
83840541d5dSvikram 		}
83940541d5dSvikram 	}
84040541d5dSvikram 
84140541d5dSvikram 	(void) fclose(fp);
84240541d5dSvikram 
84340541d5dSvikram 	if (mnted)
84440541d5dSvikram 		return (STUBBOOT);
84540541d5dSvikram 
84640541d5dSvikram 	/*
84740541d5dSvikram 	 * Stubboot is not mounted, mount it now.
84840541d5dSvikram 	 * It should exist in /etc/vfstab
84940541d5dSvikram 	 */
85040541d5dSvikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount %s",
85140541d5dSvikram 	    STUBBOOT);
852986fd29aSsetje 	if (exec_cmd(cmd, NULL) != 0) {
85340541d5dSvikram 		bam_error(MOUNT_MNTPT_FAILED, STUBBOOT);
85440541d5dSvikram 		return (NULL);
85540541d5dSvikram 	}
85640541d5dSvikram 
85740541d5dSvikram 	return (STUBBOOT);
85840541d5dSvikram }
85940541d5dSvikram 
86040541d5dSvikram static void
86140541d5dSvikram disp_active_menu_locn(char *menu_path, char *logslice, char *fstype, int mnted)
86240541d5dSvikram {
86340541d5dSvikram 	/*
86440541d5dSvikram 	 * Check if we did a temp mount of an unmounted device.
86540541d5dSvikram 	 * If yes, print the block device and fstype for that device
86640541d5dSvikram 	 * else it is already mounted, so we print the path to the GRUB menu.
86740541d5dSvikram 	 */
86840541d5dSvikram 	if (mnted) {
86940541d5dSvikram 		bam_print(GRUB_MENU_DEVICE, logslice);
87040541d5dSvikram 		bam_print(GRUB_MENU_FSTYPE, fstype);
87140541d5dSvikram 	} else {
87240541d5dSvikram 		bam_print(GRUB_MENU_PATH, menu_path);
87340541d5dSvikram 	}
87440541d5dSvikram }
87540541d5dSvikram 
87640541d5dSvikram /*
87740541d5dSvikram  * NOTE: A single "/" is also considered a trailing slash and will
87840541d5dSvikram  * be deleted.
87940541d5dSvikram  */
88040541d5dSvikram static void
88140541d5dSvikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
88240541d5dSvikram {
88340541d5dSvikram 	size_t dstlen;
88440541d5dSvikram 
88540541d5dSvikram 	assert(src);
88640541d5dSvikram 	assert(dst);
88740541d5dSvikram 
88840541d5dSvikram 	(void) strlcpy(dst, src, dstsize);
88940541d5dSvikram 
89040541d5dSvikram 	dstlen = strlen(dst);
89140541d5dSvikram 	if (dst[dstlen - 1] == '/') {
89240541d5dSvikram 		dst[dstlen - 1] = '\0';
89340541d5dSvikram 	}
89440541d5dSvikram }
89540541d5dSvikram 
8967c478bd9Sstevel@tonic-gate static error_t
8977c478bd9Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
8987c478bd9Sstevel@tonic-gate {
8997c478bd9Sstevel@tonic-gate 	error_t ret;
9007c478bd9Sstevel@tonic-gate 	char menu_path[PATH_MAX];
901ae115bc7Smrj 	char path[PATH_MAX];
902*e7cbe64fSgw25295 	char full_menu_root[PATH_MAX];
9037c478bd9Sstevel@tonic-gate 	menu_t *menu;
904*e7cbe64fSgw25295 	char *mntpt, *menu_root, *logslice, *fstype, *grubSLICEpool, *pool;
905b610f78eSvikram 	struct stat sb;
906b610f78eSvikram 	int mnted;	/* set if we did a mount */
9077c478bd9Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
908986fd29aSsetje 	char *rootpath;
909986fd29aSsetje 
910986fd29aSsetje 	/*
911986fd29aSsetje 	 * Menu sub-command only applies to GRUB (i.e. x86)
912986fd29aSsetje 	 */
913986fd29aSsetje 	rootpath = (bam_alt_root) ? bam_root : "/";
914986fd29aSsetje 	if (!is_grub((const char *)rootpath)) {
915986fd29aSsetje 		bam_error(NOT_ON_SPARC);
916986fd29aSsetje 		return (BAM_ERROR);
917986fd29aSsetje 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	/*
9207c478bd9Sstevel@tonic-gate 	 * Check arguments
9217c478bd9Sstevel@tonic-gate 	 */
9227c478bd9Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
9237c478bd9Sstevel@tonic-gate 	if (ret == BAM_ERROR) {
9247c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
9257c478bd9Sstevel@tonic-gate 	}
9267c478bd9Sstevel@tonic-gate 
927b610f78eSvikram 	mntpt = NULL;
928b610f78eSvikram 	mnted = 0;
929*e7cbe64fSgw25295 	logslice = fstype = grubSLICEpool = pool = NULL;
93040541d5dSvikram 
93140541d5dSvikram 	/*
932ae115bc7Smrj 	 * Check for the menu.list file:
933ae115bc7Smrj 	 *
934ae115bc7Smrj 	 * 1. Check for a GRUB_slice file, be it on / or
935ae115bc7Smrj 	 *    on the user-provided alternate root.
936ae115bc7Smrj 	 * 2. Use the alternate root, if given.
937ae115bc7Smrj 	 * 3. Check /stubboot
938ae115bc7Smrj 	 * 4. Use /
93940541d5dSvikram 	 */
94040541d5dSvikram 	if (bam_alt_root) {
941*e7cbe64fSgw25295 		if (is_zfs(bam_root, &grubSLICEpool))
942*e7cbe64fSgw25295 			(void) snprintf(path, sizeof (path), "%s/%s%s",
943*e7cbe64fSgw25295 			    bam_root, grubSLICEpool, GRUB_slice);
944*e7cbe64fSgw25295 		else
945*e7cbe64fSgw25295 			(void) snprintf(path, sizeof (path), "%s%s",
946*e7cbe64fSgw25295 			    bam_root, GRUB_slice);
947ae115bc7Smrj 	} else {
948*e7cbe64fSgw25295 		if (is_zfs(bam_root, &grubSLICEpool))
949*e7cbe64fSgw25295 			(void) snprintf(path, sizeof (path), "/%s%s",
950*e7cbe64fSgw25295 			    grubSLICEpool, GRUB_slice);
951*e7cbe64fSgw25295 		else
952ae115bc7Smrj 			(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
953ae115bc7Smrj 	}
954ae115bc7Smrj 
955ae115bc7Smrj 	if (stat(path, &sb) == 0) {
95640541d5dSvikram 		mntpt = mount_grub_slice(&mnted, NULL, &logslice, &fstype);
957b610f78eSvikram 		menu_root = mntpt;
958ae115bc7Smrj 	} else if (bam_alt_root) {
959ae115bc7Smrj 		menu_root = bam_root;
96040541d5dSvikram 	} else if (stat(STUBBOOT, &sb) == 0) {
96140541d5dSvikram 		menu_root = use_stubboot();
962b610f78eSvikram 	} else {
963b610f78eSvikram 		menu_root = bam_root;
964b610f78eSvikram 	}
965b610f78eSvikram 
96640541d5dSvikram 	if (menu_root == NULL) {
96740541d5dSvikram 		bam_error(CANNOT_LOCATE_GRUB_MENU);
96840541d5dSvikram 		return (BAM_ERROR);
96940541d5dSvikram 	}
97040541d5dSvikram 
971*e7cbe64fSgw25295 	/*
972*e7cbe64fSgw25295 	 * menu_root is the root file system of the boot environment being
973*e7cbe64fSgw25295 	 *    operated on.
974*e7cbe64fSgw25295 	 * full_menu_root is the location of the /boot/grub directory.
975*e7cbe64fSgw25295 	 *    With a ufs root, this is simply menu_root.  With a zfs
976*e7cbe64fSgw25295 	 *    root, it's <menu_root>/<poolname>
977*e7cbe64fSgw25295 	 */
978*e7cbe64fSgw25295 	elide_trailing_slash(menu_root, full_menu_root,
979*e7cbe64fSgw25295 	    sizeof (full_menu_root));
980*e7cbe64fSgw25295 
981*e7cbe64fSgw25295 	if (is_zfs(menu_root, &pool)) {
982*e7cbe64fSgw25295 		(void) strlcat(full_menu_root, "/", sizeof (full_menu_root));
983*e7cbe64fSgw25295 		(void) strlcat(full_menu_root, pool, sizeof (full_menu_root));
984*e7cbe64fSgw25295 	}
985*e7cbe64fSgw25295 
986*e7cbe64fSgw25295 	/*
987*e7cbe64fSgw25295 	 * menu_path is the directory that contains the menu.lst file
988*e7cbe64fSgw25295 	 */
989*e7cbe64fSgw25295 	(void) strlcpy(menu_path, full_menu_root, sizeof (menu_path));
99040541d5dSvikram 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
99140541d5dSvikram 
99240541d5dSvikram 	/*
99340541d5dSvikram 	 * If listing the menu, display the active menu
99440541d5dSvikram 	 * location
99540541d5dSvikram 	 */
99640541d5dSvikram 	if (strcmp(subcmd, "list_entry") == 0) {
99740541d5dSvikram 		disp_active_menu_locn(menu_path, logslice, fstype, mnted);
99840541d5dSvikram 	}
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	menu = menu_read(menu_path);
10017c478bd9Sstevel@tonic-gate 	assert(menu);
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 	/*
10047c478bd9Sstevel@tonic-gate 	 * Special handling for setting timeout and default
10057c478bd9Sstevel@tonic-gate 	 */
10067c478bd9Sstevel@tonic-gate 	if (strcmp(subcmd, "set_option") == 0) {
10077c478bd9Sstevel@tonic-gate 		if (largc != 1 || largv[0] == NULL) {
10087c478bd9Sstevel@tonic-gate 			usage();
100940541d5dSvikram 			menu_free(menu);
101040541d5dSvikram 			umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
10117c478bd9Sstevel@tonic-gate 			return (BAM_ERROR);
10127c478bd9Sstevel@tonic-gate 		}
10137c478bd9Sstevel@tonic-gate 		opt = largv[0];
10147c478bd9Sstevel@tonic-gate 	} else if (largc != 0) {
10157c478bd9Sstevel@tonic-gate 		usage();
101640541d5dSvikram 		menu_free(menu);
101740541d5dSvikram 		umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
10187c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
10197c478bd9Sstevel@tonic-gate 	}
10207c478bd9Sstevel@tonic-gate 
1021ae115bc7Smrj 	ret = dboot_or_multiboot(bam_root);
1022ae115bc7Smrj 	if (ret != BAM_SUCCESS)
1023ae115bc7Smrj 		return (ret);
1024ae115bc7Smrj 
10257c478bd9Sstevel@tonic-gate 	/*
10267c478bd9Sstevel@tonic-gate 	 * Once the sub-cmd handler has run
10277c478bd9Sstevel@tonic-gate 	 * only the line field is guaranteed to have valid values
10287c478bd9Sstevel@tonic-gate 	 */
1029ae115bc7Smrj 	if ((strcmp(subcmd, "update_entry") == 0) ||
1030ae115bc7Smrj 	    (strcmp(subcmd, "upgrade") == 0))
10317c478bd9Sstevel@tonic-gate 		ret = f(menu, bam_root, opt);
10327c478bd9Sstevel@tonic-gate 	else
10337c478bd9Sstevel@tonic-gate 		ret = f(menu, menu_path, opt);
10347c478bd9Sstevel@tonic-gate 	if (ret == BAM_WRITE) {
1035*e7cbe64fSgw25295 		ret = menu_write(full_menu_root, menu);
10367c478bd9Sstevel@tonic-gate 	}
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	menu_free(menu);
10397c478bd9Sstevel@tonic-gate 
104040541d5dSvikram 	umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
1041b610f78eSvikram 
1042*e7cbe64fSgw25295 	if (grubSLICEpool)
1043*e7cbe64fSgw25295 		free(grubSLICEpool);
1044*e7cbe64fSgw25295 	if (pool)
1045*e7cbe64fSgw25295 		free(pool);
10467c478bd9Sstevel@tonic-gate 	return (ret);
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate static error_t
10517c478bd9Sstevel@tonic-gate bam_archive(
10527c478bd9Sstevel@tonic-gate 	char *subcmd,
10537c478bd9Sstevel@tonic-gate 	char *opt)
10547c478bd9Sstevel@tonic-gate {
10557c478bd9Sstevel@tonic-gate 	error_t ret;
10567c478bd9Sstevel@tonic-gate 	error_t (*f)(char *root, char *opt);
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	/*
10598c1b6884Sszhou 	 * Add trailing / for archive subcommands
10608c1b6884Sszhou 	 */
10618c1b6884Sszhou 	if (rootbuf[strlen(rootbuf) - 1] != '/')
10628c1b6884Sszhou 		(void) strcat(rootbuf, "/");
10638c1b6884Sszhou 	bam_rootlen = strlen(rootbuf);
10648c1b6884Sszhou 
10658c1b6884Sszhou 	/*
10667c478bd9Sstevel@tonic-gate 	 * Check arguments
10677c478bd9Sstevel@tonic-gate 	 */
10687c478bd9Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
10697c478bd9Sstevel@tonic-gate 	if (ret != BAM_SUCCESS) {
10707c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
10717c478bd9Sstevel@tonic-gate 	}
10727c478bd9Sstevel@tonic-gate 
1073ae115bc7Smrj 	ret = dboot_or_multiboot(rootbuf);
1074ae115bc7Smrj 	if (ret != BAM_SUCCESS)
1075ae115bc7Smrj 		return (ret);
1076ae115bc7Smrj 
10777c478bd9Sstevel@tonic-gate 	/*
10787c478bd9Sstevel@tonic-gate 	 * Check archive not supported with update_all
10797c478bd9Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
10807c478bd9Sstevel@tonic-gate 	 * information for each BE.
10817c478bd9Sstevel@tonic-gate 	 */
10827c478bd9Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
10837c478bd9Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
10847c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
10857c478bd9Sstevel@tonic-gate 	}
10867c478bd9Sstevel@tonic-gate 
1087b610f78eSvikram 	if (strcmp(subcmd, "update_all") == 0)
1088b610f78eSvikram 		bam_update_all = 1;
1089b610f78eSvikram 
1090986fd29aSsetje #if !defined(_OPB)
10912449e17fSsherrym 	ucode_install(bam_root);
10922449e17fSsherrym #endif
10932449e17fSsherrym 
1094b610f78eSvikram 	ret = f(bam_root, opt);
1095b610f78eSvikram 
1096b610f78eSvikram 	bam_update_all = 0;
1097b610f78eSvikram 
1098b610f78eSvikram 	return (ret);
10997c478bd9Sstevel@tonic-gate }
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
1102ae115bc7Smrj void
11037c478bd9Sstevel@tonic-gate bam_error(char *format, ...)
11047c478bd9Sstevel@tonic-gate {
11057c478bd9Sstevel@tonic-gate 	va_list ap;
11067c478bd9Sstevel@tonic-gate 
11077c478bd9Sstevel@tonic-gate 	va_start(ap, format);
11087c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
11097c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
11107c478bd9Sstevel@tonic-gate 	va_end(ap);
11117c478bd9Sstevel@tonic-gate }
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
11147c478bd9Sstevel@tonic-gate static void
11157c478bd9Sstevel@tonic-gate bam_print(char *format, ...)
11167c478bd9Sstevel@tonic-gate {
11177c478bd9Sstevel@tonic-gate 	va_list ap;
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	va_start(ap, format);
11207c478bd9Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
11217c478bd9Sstevel@tonic-gate 	va_end(ap);
11227c478bd9Sstevel@tonic-gate }
11237c478bd9Sstevel@tonic-gate 
1124ae115bc7Smrj /*PRINTFLIKE1*/
1125ae115bc7Smrj void
1126ae115bc7Smrj bam_print_stderr(char *format, ...)
1127ae115bc7Smrj {
1128ae115bc7Smrj 	va_list ap;
1129ae115bc7Smrj 
1130ae115bc7Smrj 	va_start(ap, format);
1131ae115bc7Smrj 	(void) vfprintf(stderr, format, ap);
1132ae115bc7Smrj 	va_end(ap);
1133ae115bc7Smrj }
1134ae115bc7Smrj 
11357c478bd9Sstevel@tonic-gate static void
11367c478bd9Sstevel@tonic-gate bam_exit(int excode)
11377c478bd9Sstevel@tonic-gate {
11387c478bd9Sstevel@tonic-gate 	bam_unlock();
11397c478bd9Sstevel@tonic-gate 	exit(excode);
11407c478bd9Sstevel@tonic-gate }
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate static void
11437c478bd9Sstevel@tonic-gate bam_lock(void)
11447c478bd9Sstevel@tonic-gate {
11457c478bd9Sstevel@tonic-gate 	struct flock lock;
11467c478bd9Sstevel@tonic-gate 	pid_t pid;
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
11497c478bd9Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11507c478bd9Sstevel@tonic-gate 		/*
11517c478bd9Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
11527c478bd9Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
11537c478bd9Sstevel@tonic-gate 		 * Proceed without the lock
11547c478bd9Sstevel@tonic-gate 		 */
11557c478bd9Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
11567c478bd9Sstevel@tonic-gate 			bam_root_readonly = 1;
11577c478bd9Sstevel@tonic-gate 			return;
11587c478bd9Sstevel@tonic-gate 		}
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
11617c478bd9Sstevel@tonic-gate 		bam_exit(1);
11627c478bd9Sstevel@tonic-gate 	}
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
11657c478bd9Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
11667c478bd9Sstevel@tonic-gate 	lock.l_start = 0;
11677c478bd9Sstevel@tonic-gate 	lock.l_len = 0;
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
11707c478bd9Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
11717c478bd9Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11727c478bd9Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11737c478bd9Sstevel@tonic-gate 			bam_lock_fd = -1;
11747c478bd9Sstevel@tonic-gate 			bam_exit(1);
11757c478bd9Sstevel@tonic-gate 		}
11767c478bd9Sstevel@tonic-gate 		pid = 0;
11777c478bd9Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
11787c478bd9Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
11817c478bd9Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
11827c478bd9Sstevel@tonic-gate 		lock.l_start = 0;
11837c478bd9Sstevel@tonic-gate 		lock.l_len = 0;
11847c478bd9Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
11857c478bd9Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11867c478bd9Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11877c478bd9Sstevel@tonic-gate 			bam_lock_fd = -1;
11887c478bd9Sstevel@tonic-gate 			bam_exit(1);
11897c478bd9Sstevel@tonic-gate 		}
11907c478bd9Sstevel@tonic-gate 	}
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate 	/* We own the lock now */
11937c478bd9Sstevel@tonic-gate 	pid = getpid();
11947c478bd9Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
11957c478bd9Sstevel@tonic-gate }
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate static void
11987c478bd9Sstevel@tonic-gate bam_unlock(void)
11997c478bd9Sstevel@tonic-gate {
12007c478bd9Sstevel@tonic-gate 	struct flock unlock;
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 	/*
12037c478bd9Sstevel@tonic-gate 	 * NOP if we don't hold the lock
12047c478bd9Sstevel@tonic-gate 	 */
12057c478bd9Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
12067c478bd9Sstevel@tonic-gate 		return;
12077c478bd9Sstevel@tonic-gate 	}
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
12107c478bd9Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
12117c478bd9Sstevel@tonic-gate 	unlock.l_start = 0;
12127c478bd9Sstevel@tonic-gate 	unlock.l_len = 0;
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
12157c478bd9Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
12167c478bd9Sstevel@tonic-gate 	}
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
12197c478bd9Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
12207c478bd9Sstevel@tonic-gate 	}
12217c478bd9Sstevel@tonic-gate 	bam_lock_fd = -1;
12227c478bd9Sstevel@tonic-gate }
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate static error_t
12257c478bd9Sstevel@tonic-gate list_archive(char *root, char *opt)
12267c478bd9Sstevel@tonic-gate {
12277c478bd9Sstevel@tonic-gate 	filelist_t flist;
12287c478bd9Sstevel@tonic-gate 	filelist_t *flistp = &flist;
12297c478bd9Sstevel@tonic-gate 	line_t *lp;
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 	assert(root);
12327c478bd9Sstevel@tonic-gate 	assert(opt == NULL);
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
12357c478bd9Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
12367c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
12377c478bd9Sstevel@tonic-gate 	}
12387c478bd9Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
12397c478bd9Sstevel@tonic-gate 
12407c478bd9Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
12417c478bd9Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
12427c478bd9Sstevel@tonic-gate 	}
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	filelist_free(flistp);
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
12477c478bd9Sstevel@tonic-gate }
12487c478bd9Sstevel@tonic-gate 
12497c478bd9Sstevel@tonic-gate /*
12507c478bd9Sstevel@tonic-gate  * This routine writes a list of lines to a file.
12517c478bd9Sstevel@tonic-gate  * The list is *not* freed
12527c478bd9Sstevel@tonic-gate  */
12537c478bd9Sstevel@tonic-gate static error_t
12547c478bd9Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
12557c478bd9Sstevel@tonic-gate {
12567c478bd9Sstevel@tonic-gate 	char tmpfile[PATH_MAX];
12577c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
12587c478bd9Sstevel@tonic-gate 	FILE *fp;
12597c478bd9Sstevel@tonic-gate 	int ret;
12607c478bd9Sstevel@tonic-gate 	struct stat sb;
12617c478bd9Sstevel@tonic-gate 	mode_t mode;
12627c478bd9Sstevel@tonic-gate 	uid_t root_uid;
12637c478bd9Sstevel@tonic-gate 	gid_t sys_gid;
12647c478bd9Sstevel@tonic-gate 	struct passwd *pw;
12657c478bd9Sstevel@tonic-gate 	struct group *gp;
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 	if (start == NULL) {
12717c478bd9Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
12727c478bd9Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
12737c478bd9Sstevel@tonic-gate 			if (unlink(path) != 0) {
12747c478bd9Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
12757c478bd9Sstevel@tonic-gate 				return (BAM_ERROR);
12767c478bd9Sstevel@tonic-gate 			} else {
12777c478bd9Sstevel@tonic-gate 				return (BAM_SUCCESS);
12787c478bd9Sstevel@tonic-gate 			}
12797c478bd9Sstevel@tonic-gate 		}
12807c478bd9Sstevel@tonic-gate 	}
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 	/*
12837c478bd9Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
12847c478bd9Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
12857c478bd9Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
12867c478bd9Sstevel@tonic-gate 	 */
12877c478bd9Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
12887c478bd9Sstevel@tonic-gate 		mode = sb.st_mode;
12897c478bd9Sstevel@tonic-gate 		root_uid = sb.st_uid;
12907c478bd9Sstevel@tonic-gate 		sys_gid = sb.st_gid;
12917c478bd9Sstevel@tonic-gate 	} else {
12927c478bd9Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
12937c478bd9Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
12947c478bd9Sstevel@tonic-gate 			root_uid = pw->pw_uid;
12957c478bd9Sstevel@tonic-gate 		} else {
12967c478bd9Sstevel@tonic-gate 			if (bam_verbose)
12977c478bd9Sstevel@tonic-gate 				bam_error(CANT_FIND_USER,
12987c478bd9Sstevel@tonic-gate 				    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
12997c478bd9Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
13007c478bd9Sstevel@tonic-gate 		}
13017c478bd9Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
13027c478bd9Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
13037c478bd9Sstevel@tonic-gate 		} else {
13047c478bd9Sstevel@tonic-gate 			if (bam_verbose)
13057c478bd9Sstevel@tonic-gate 				bam_error(CANT_FIND_GROUP,
13067c478bd9Sstevel@tonic-gate 				    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
13077c478bd9Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
13087c478bd9Sstevel@tonic-gate 		}
13097c478bd9Sstevel@tonic-gate 	}
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 	/* Truncate tmpfile first */
13147c478bd9Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
13157c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
13167c478bd9Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
13177c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
13187c478bd9Sstevel@tonic-gate 	}
13197c478bd9Sstevel@tonic-gate 	ret = fclose(fp);
13207c478bd9Sstevel@tonic-gate 	if (ret == EOF) {
13217c478bd9Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
13227c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
13237c478bd9Sstevel@tonic-gate 	}
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 	/* Now open it in append mode */
13267c478bd9Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
13277c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
13287c478bd9Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
13297c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
13307c478bd9Sstevel@tonic-gate 	}
13317c478bd9Sstevel@tonic-gate 
13327c478bd9Sstevel@tonic-gate 	for (; start; start = start->next) {
13337c478bd9Sstevel@tonic-gate 		ret = s_fputs(start->line, fp);
13347c478bd9Sstevel@tonic-gate 		if (ret == EOF) {
13357c478bd9Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
13367c478bd9Sstevel@tonic-gate 			(void) fclose(fp);
13377c478bd9Sstevel@tonic-gate 			return (BAM_ERROR);
13387c478bd9Sstevel@tonic-gate 		}
13397c478bd9Sstevel@tonic-gate 	}
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate 	ret = fclose(fp);
13427c478bd9Sstevel@tonic-gate 	if (ret == EOF) {
13437c478bd9Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
13447c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
13457c478bd9Sstevel@tonic-gate 	}
13467c478bd9Sstevel@tonic-gate 
13477c478bd9Sstevel@tonic-gate 	/*
1348de531be4Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1349de531be4Sjg 	 * not supporting these operations - pcfs reports unsupported
1350de531be4Sjg 	 * operations as EINVAL.
13517c478bd9Sstevel@tonic-gate 	 */
13527c478bd9Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1353de531be4Sjg 	if (ret == -1 &&
1354de531be4Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13557c478bd9Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
13567c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
13577c478bd9Sstevel@tonic-gate 	}
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1360de531be4Sjg 	if (ret == -1 &&
1361de531be4Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13627c478bd9Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
13637c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
13647c478bd9Sstevel@tonic-gate 	}
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 
13677c478bd9Sstevel@tonic-gate 	/*
13687c478bd9Sstevel@tonic-gate 	 * Do an atomic rename
13697c478bd9Sstevel@tonic-gate 	 */
13707c478bd9Sstevel@tonic-gate 	ret = rename(tmpfile, path);
13717c478bd9Sstevel@tonic-gate 	if (ret != 0) {
13727c478bd9Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
13737c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
13747c478bd9Sstevel@tonic-gate 	}
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
13777c478bd9Sstevel@tonic-gate }
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate /*
13807c478bd9Sstevel@tonic-gate  * This function should always return 0 - since we want
13817c478bd9Sstevel@tonic-gate  * to create stat data for *all* files in the list.
13827c478bd9Sstevel@tonic-gate  */
13837c478bd9Sstevel@tonic-gate /*ARGSUSED*/
13847c478bd9Sstevel@tonic-gate static int
13857c478bd9Sstevel@tonic-gate cmpstat(
13867c478bd9Sstevel@tonic-gate 	const char *file,
13877c478bd9Sstevel@tonic-gate 	const struct stat *stat,
13887c478bd9Sstevel@tonic-gate 	int flags,
13897c478bd9Sstevel@tonic-gate 	struct FTW *ftw)
13907c478bd9Sstevel@tonic-gate {
13917c478bd9Sstevel@tonic-gate 	uint_t sz;
13927c478bd9Sstevel@tonic-gate 	uint64_t *value;
13937c478bd9Sstevel@tonic-gate 	uint64_t filestat[2];
13947c478bd9Sstevel@tonic-gate 	int error;
13957c478bd9Sstevel@tonic-gate 
139658091fd8Ssetje 	struct safefile *safefilep;
139758091fd8Ssetje 	FILE *fp;
139858091fd8Ssetje 
13997c478bd9Sstevel@tonic-gate 	/*
14007c478bd9Sstevel@tonic-gate 	 * We only want regular files
14017c478bd9Sstevel@tonic-gate 	 */
14027c478bd9Sstevel@tonic-gate 	if (!S_ISREG(stat->st_mode))
14037c478bd9Sstevel@tonic-gate 		return (0);
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 	/*
14067c478bd9Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
14077c478bd9Sstevel@tonic-gate 	 * but this is not fatal to update determination.
14087c478bd9Sstevel@tonic-gate 	 */
14097c478bd9Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
14107c478bd9Sstevel@tonic-gate 		filestat[0] = stat->st_size;
14117c478bd9Sstevel@tonic-gate 		filestat[1] = stat->st_mtime;
14127c478bd9Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
14137c478bd9Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
14147c478bd9Sstevel@tonic-gate 		if (error)
14157c478bd9Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
14167c478bd9Sstevel@tonic-gate 	}
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate 	/*
14197c478bd9Sstevel@tonic-gate 	 * The remaining steps are only required if we haven't made a
14207c478bd9Sstevel@tonic-gate 	 * decision about update or if we are checking (-n)
14217c478bd9Sstevel@tonic-gate 	 */
14227c478bd9Sstevel@tonic-gate 	if (walk_arg.need_update && !bam_check)
14237c478bd9Sstevel@tonic-gate 		return (0);
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 	/*
1426d876c67dSjg 	 * If we are invoked as part of system/filesystem/boot-archive, then
142758091fd8Ssetje 	 * there are a number of things we should not worry about
14287c478bd9Sstevel@tonic-gate 	 */
142958091fd8Ssetje 	if (bam_smf_check) {
143058091fd8Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
143158091fd8Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
14327c478bd9Sstevel@tonic-gate 			return (0);
14337c478bd9Sstevel@tonic-gate 
143458091fd8Ssetje 		/* read in list of safe files */
143558091fd8Ssetje 		if (safefiles == NULL)
143658091fd8Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
143758091fd8Ssetje 				safefiles = s_calloc(1,
143858091fd8Ssetje 				    sizeof (struct safefile));
143958091fd8Ssetje 				safefilep = safefiles;
144058091fd8Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
144158091fd8Ssetje 				    MAXNAMELEN);
144258091fd8Ssetje 				safefilep->next = NULL;
144358091fd8Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
144458091fd8Ssetje 				    MAXNAMELEN, fp) != NULL) {
144558091fd8Ssetje 					safefilep->next = s_calloc(1,
144658091fd8Ssetje 					    sizeof (struct safefile));
144758091fd8Ssetje 					safefilep = safefilep->next;
144858091fd8Ssetje 					safefilep->name = s_calloc(1,
144958091fd8Ssetje 					    MAXPATHLEN + MAXNAMELEN);
145058091fd8Ssetje 					safefilep->next = NULL;
145158091fd8Ssetje 				}
145258091fd8Ssetje 				(void) fclose(fp);
145358091fd8Ssetje 			}
145458091fd8Ssetje 	}
145558091fd8Ssetje 
14567c478bd9Sstevel@tonic-gate 	/*
14577c478bd9Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
14587c478bd9Sstevel@tonic-gate 	 */
14597c478bd9Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
14607c478bd9Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
14617c478bd9Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
14627c478bd9Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
14637c478bd9Sstevel@tonic-gate 			return (0);
14647c478bd9Sstevel@tonic-gate 		walk_arg.need_update = 1;
14657c478bd9Sstevel@tonic-gate 		if (bam_verbose)
14667c478bd9Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
14677c478bd9Sstevel@tonic-gate 		return (0);
14687c478bd9Sstevel@tonic-gate 	}
14697c478bd9Sstevel@tonic-gate 
14707c478bd9Sstevel@tonic-gate 	/*
14717c478bd9Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
14727c478bd9Sstevel@tonic-gate 	 */
14737c478bd9Sstevel@tonic-gate 	assert(sz == 2);
14747c478bd9Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 	if (filestat[0] != stat->st_size ||
14777c478bd9Sstevel@tonic-gate 	    filestat[1] != stat->st_mtime) {
14787c609327Ssetje 		if (bam_smf_check) {
14797c609327Ssetje 			safefilep = safefiles;
14807c609327Ssetje 			while (safefilep != NULL) {
14817c609327Ssetje 				if (strcmp(file + bam_rootlen,
14827c609327Ssetje 				    safefilep->name) == 0) {
14837c609327Ssetje 					(void) creat(NEED_UPDATE_FILE, 0644);
14847c609327Ssetje 					return (0);
14857c609327Ssetje 				}
14867c609327Ssetje 				safefilep = safefilep->next;
14877c609327Ssetje 			}
14887c609327Ssetje 		}
14897c478bd9Sstevel@tonic-gate 		walk_arg.need_update = 1;
14907c478bd9Sstevel@tonic-gate 		if (bam_verbose)
14917c478bd9Sstevel@tonic-gate 			if (bam_smf_check)
14927c478bd9Sstevel@tonic-gate 				bam_print("    %s\n", file);
14937c478bd9Sstevel@tonic-gate 			else
14947c478bd9Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
14957c478bd9Sstevel@tonic-gate 	}
14967c478bd9Sstevel@tonic-gate 
14977c478bd9Sstevel@tonic-gate 	return (0);
14987c478bd9Sstevel@tonic-gate }
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate /*
15017c478bd9Sstevel@tonic-gate  * Check flags and presence of required files.
15027c478bd9Sstevel@tonic-gate  * The force flag and/or absence of files should
15037c478bd9Sstevel@tonic-gate  * trigger an update.
15047c478bd9Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
15057c478bd9Sstevel@tonic-gate  * (as -n should only produce parseable output.)
15067c478bd9Sstevel@tonic-gate  */
15077c478bd9Sstevel@tonic-gate static void
15087c478bd9Sstevel@tonic-gate check_flags_and_files(char *root)
15097c478bd9Sstevel@tonic-gate {
15107c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
15117c478bd9Sstevel@tonic-gate 	struct stat sb;
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate 	/*
15147c478bd9Sstevel@tonic-gate 	 * if force, create archive unconditionally
15157c478bd9Sstevel@tonic-gate 	 */
15167c478bd9Sstevel@tonic-gate 	if (bam_force) {
15177c478bd9Sstevel@tonic-gate 		walk_arg.need_update = 1;
15187c478bd9Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
15197c478bd9Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
15207c478bd9Sstevel@tonic-gate 		return;
15217c478bd9Sstevel@tonic-gate 	}
15227c478bd9Sstevel@tonic-gate 
15237c478bd9Sstevel@tonic-gate 	/*
15247c478bd9Sstevel@tonic-gate 	 * If archive is missing, create archive
15257c478bd9Sstevel@tonic-gate 	 */
1526d876c67dSjg 	if (is_sun4u()) {
1527ae115bc7Smrj 		(void) snprintf(path, sizeof (path), "%s%s", root,
1528d876c67dSjg 		    SUN4U_ARCHIVE);
1529d876c67dSjg 	} else if (is_sun4v()) {
1530986fd29aSsetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
1531d876c67dSjg 		    SUN4V_ARCHIVE);
1532d876c67dSjg 	} else {
1533ae115bc7Smrj 		if (bam_direct == BAM_DIRECT_DBOOT) {
1534ae115bc7Smrj 			(void) snprintf(path, sizeof (path), "%s%s", root,
1535ae115bc7Smrj 			    DIRECT_BOOT_ARCHIVE_64);
1536ae115bc7Smrj 			if (stat(path, &sb) != 0) {
1537ae115bc7Smrj 				if (bam_verbose && !bam_check)
1538ae115bc7Smrj 					bam_print(UPDATE_ARCH_MISS, path);
1539ae115bc7Smrj 				walk_arg.need_update = 1;
1540ae115bc7Smrj 				return;
1541ae115bc7Smrj 			}
1542ae115bc7Smrj 		}
1543986fd29aSsetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
1544986fd29aSsetje 		    DIRECT_BOOT_ARCHIVE_32);
1545986fd29aSsetje 	}
1546986fd29aSsetje 
1547986fd29aSsetje 	if (stat(path, &sb) != 0) {
1548986fd29aSsetje 		if (bam_verbose && !bam_check)
1549986fd29aSsetje 			bam_print(UPDATE_ARCH_MISS, path);
1550986fd29aSsetje 		walk_arg.need_update = 1;
1551986fd29aSsetje 		return;
1552986fd29aSsetje 	}
15537c478bd9Sstevel@tonic-gate }
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate static error_t
15567c478bd9Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
15577c478bd9Sstevel@tonic-gate {
15587c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
15597c478bd9Sstevel@tonic-gate 	FILE *fp;
15607c478bd9Sstevel@tonic-gate 	char buf[BAM_MAXLINE];
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	fp = fopen(path, "r");
15657c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
15667c478bd9Sstevel@tonic-gate 		if (bam_debug)
15677c478bd9Sstevel@tonic-gate 			bam_error(FLIST_FAIL, path, strerror(errno));
15687c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
15697c478bd9Sstevel@tonic-gate 	}
15707c478bd9Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1571b610f78eSvikram 		/* skip blank lines */
1572b610f78eSvikram 		if (strspn(buf, " \t") == strlen(buf))
1573b610f78eSvikram 			continue;
15747c478bd9Sstevel@tonic-gate 		append_to_flist(flistp, buf);
15757c478bd9Sstevel@tonic-gate 	}
15767c478bd9Sstevel@tonic-gate 	if (fclose(fp) != 0) {
15777c478bd9Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
15787c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
15797c478bd9Sstevel@tonic-gate 	}
15807c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
15817c478bd9Sstevel@tonic-gate }
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate static error_t
15847c478bd9Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
15857c478bd9Sstevel@tonic-gate {
1586986fd29aSsetje 	char path[PATH_MAX];
1587986fd29aSsetje 	char cmd[PATH_MAX];
1588986fd29aSsetje 	struct stat sb;
1589986fd29aSsetje 	int n, rval;
15907c478bd9Sstevel@tonic-gate 
15917c478bd9Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 	/*
1594986fd29aSsetje 	 * build and check path to extract_boot_filelist.ksh
1595986fd29aSsetje 	 */
1596986fd29aSsetje 	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
1597986fd29aSsetje 	if (n >= sizeof (path)) {
1598986fd29aSsetje 		bam_error(NO_FLIST);
1599986fd29aSsetje 		return (BAM_ERROR);
1600986fd29aSsetje 	}
1601986fd29aSsetje 
1602986fd29aSsetje 	/*
1603986fd29aSsetje 	 * If extract_boot_filelist is present, exec it, otherwise read
1604986fd29aSsetje 	 * the filelists directly, for compatibility with older images.
1605986fd29aSsetje 	 */
1606986fd29aSsetje 	if (stat(path, &sb) == 0) {
1607986fd29aSsetje 		/*
1608986fd29aSsetje 		 * build arguments to exec extract_boot_filelist.ksh
1609986fd29aSsetje 		 */
1610d876c67dSjg 		char *rootarg, *platarg;
1611d876c67dSjg 		int platarglen = 1, rootarglen = 1;
1612d876c67dSjg 		if (strlen(root) > 1)
1613d876c67dSjg 			rootarglen += strlen(root) + strlen("-R ");
1614d876c67dSjg 		if (bam_alt_platform)
1615d876c67dSjg 			platarglen += strlen(bam_platform) + strlen("-p ");
1616d876c67dSjg 		platarg = s_calloc(1, platarglen);
1617d876c67dSjg 		rootarg = s_calloc(1, rootarglen);
1618d876c67dSjg 		*platarg = 0;
1619d876c67dSjg 		*rootarg = 0;
1620d876c67dSjg 
1621986fd29aSsetje 		if (strlen(root) > 1) {
1622d876c67dSjg 			(void) snprintf(rootarg, rootarglen,
1623d876c67dSjg 			    "-R %s", root);
1624986fd29aSsetje 		}
1625d876c67dSjg 		if (bam_alt_platform) {
1626d876c67dSjg 			(void) snprintf(platarg, platarglen,
1627d876c67dSjg 			    "-p %s", bam_platform);
1628d876c67dSjg 		}
1629d876c67dSjg 		n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
1630d876c67dSjg 		    path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
1631d876c67dSjg 		free(platarg);
1632d876c67dSjg 		free(rootarg);
1633986fd29aSsetje 		if (n >= sizeof (cmd)) {
1634986fd29aSsetje 			bam_error(NO_FLIST);
1635986fd29aSsetje 			return (BAM_ERROR);
1636986fd29aSsetje 		}
1637986fd29aSsetje 		if (exec_cmd(cmd, flistp) != 0) {
1638986fd29aSsetje 			if (bam_debug)
1639986fd29aSsetje 				bam_error(FLIST_FAIL, path, strerror(errno));
1640986fd29aSsetje 			return (BAM_ERROR);
1641986fd29aSsetje 		}
1642986fd29aSsetje 	} else {
1643986fd29aSsetje 		/*
16447c478bd9Sstevel@tonic-gate 		 * Read current lists of files - only the first is mandatory
16457c478bd9Sstevel@tonic-gate 		 */
16467c478bd9Sstevel@tonic-gate 		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
16477c478bd9Sstevel@tonic-gate 		if (rval != BAM_SUCCESS)
16487c478bd9Sstevel@tonic-gate 			return (rval);
16497c478bd9Sstevel@tonic-gate 		(void) read_one_list(root, flistp, ETC_FILE_LIST);
1650986fd29aSsetje 	}
16517c478bd9Sstevel@tonic-gate 
16527c478bd9Sstevel@tonic-gate 	if (flistp->head == NULL) {
16537c478bd9Sstevel@tonic-gate 		bam_error(NO_FLIST);
16547c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
16557c478bd9Sstevel@tonic-gate 	}
16567c478bd9Sstevel@tonic-gate 
16577c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
16587c478bd9Sstevel@tonic-gate }
16597c478bd9Sstevel@tonic-gate 
16607c478bd9Sstevel@tonic-gate static void
16617c478bd9Sstevel@tonic-gate getoldstat(char *root)
16627c478bd9Sstevel@tonic-gate {
16637c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
16647c478bd9Sstevel@tonic-gate 	int fd, error;
16657c478bd9Sstevel@tonic-gate 	struct stat sb;
16667c478bd9Sstevel@tonic-gate 	char *ostat;
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
16697c478bd9Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
16707c478bd9Sstevel@tonic-gate 	if (fd == -1) {
16717c478bd9Sstevel@tonic-gate 		if (bam_verbose)
16727c478bd9Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
16737c478bd9Sstevel@tonic-gate 		walk_arg.need_update = 1;
16747c478bd9Sstevel@tonic-gate 		return;
16757c478bd9Sstevel@tonic-gate 	}
16767c478bd9Sstevel@tonic-gate 
16777c478bd9Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
16787c478bd9Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
16797c478bd9Sstevel@tonic-gate 		(void) close(fd);
16807c478bd9Sstevel@tonic-gate 		walk_arg.need_update = 1;
16817c478bd9Sstevel@tonic-gate 		return;
16827c478bd9Sstevel@tonic-gate 	}
16837c478bd9Sstevel@tonic-gate 
16847c478bd9Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
16877c478bd9Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
16887c478bd9Sstevel@tonic-gate 		(void) close(fd);
16897c478bd9Sstevel@tonic-gate 		free(ostat);
16907c478bd9Sstevel@tonic-gate 		walk_arg.need_update = 1;
16917c478bd9Sstevel@tonic-gate 		return;
16927c478bd9Sstevel@tonic-gate 	}
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate 	(void) close(fd);
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
16977c478bd9Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 	free(ostat);
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 	if (error) {
17027c478bd9Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
17037c478bd9Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
17047c478bd9Sstevel@tonic-gate 		walk_arg.need_update = 1;
17057c478bd9Sstevel@tonic-gate 		return;
17067c478bd9Sstevel@tonic-gate 	}
17077c478bd9Sstevel@tonic-gate }
17087c478bd9Sstevel@tonic-gate 
1709a28d77b8Svikram /*
1710a28d77b8Svikram  * Checks if a file in the current (old) archive has
1711a28d77b8Svikram  * been deleted from the root filesystem. This is needed for
1712a28d77b8Svikram  * software like Trusted Extensions (TX) that switch early
1713a28d77b8Svikram  * in boot based on presence/absence of a kernel module.
1714a28d77b8Svikram  */
1715a28d77b8Svikram static void
1716a28d77b8Svikram check4stale(char *root)
1717a28d77b8Svikram {
1718a28d77b8Svikram 	nvpair_t	*nvp;
1719a28d77b8Svikram 	nvlist_t	*nvlp;
1720a28d77b8Svikram 	char 		*file;
1721a28d77b8Svikram 	char		path[PATH_MAX];
1722a28d77b8Svikram 	struct stat	sb;
1723a28d77b8Svikram 
1724a28d77b8Svikram 	/*
1725a28d77b8Svikram 	 * Skip stale file check during smf check
1726a28d77b8Svikram 	 */
1727a28d77b8Svikram 	if (bam_smf_check)
1728a28d77b8Svikram 		return;
1729a28d77b8Svikram 
1730a28d77b8Svikram 	/* Nothing to do if no old stats */
1731a28d77b8Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
1732a28d77b8Svikram 		return;
1733a28d77b8Svikram 
1734a28d77b8Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
1735a28d77b8Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
1736a28d77b8Svikram 		file = nvpair_name(nvp);
1737a28d77b8Svikram 		if (file == NULL)
1738a28d77b8Svikram 			continue;
1739a28d77b8Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
1740a28d77b8Svikram 		    root, file);
1741a28d77b8Svikram 		if (stat(path, &sb) == -1) {
1742a28d77b8Svikram 			walk_arg.need_update = 1;
1743a28d77b8Svikram 			if (bam_verbose)
1744a28d77b8Svikram 				bam_print(PARSEABLE_STALE_FILE, path);
1745a28d77b8Svikram 		}
1746a28d77b8Svikram 	}
1747a28d77b8Svikram }
1748a28d77b8Svikram 
17497c478bd9Sstevel@tonic-gate static void
17507c478bd9Sstevel@tonic-gate create_newstat(void)
17517c478bd9Sstevel@tonic-gate {
17527c478bd9Sstevel@tonic-gate 	int error;
17537c478bd9Sstevel@tonic-gate 
17547c478bd9Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
17557c478bd9Sstevel@tonic-gate 	if (error) {
17567c478bd9Sstevel@tonic-gate 		/*
17577c478bd9Sstevel@tonic-gate 		 * Not fatal - we can still create archive
17587c478bd9Sstevel@tonic-gate 		 */
17597c478bd9Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
17607c478bd9Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
17617c478bd9Sstevel@tonic-gate 	}
17627c478bd9Sstevel@tonic-gate }
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate static void
17657c478bd9Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
17667c478bd9Sstevel@tonic-gate {
17677c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
17687c478bd9Sstevel@tonic-gate 	line_t *lp;
17697c478bd9Sstevel@tonic-gate 
17707c478bd9Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
1771986fd29aSsetje 		/*
1772986fd29aSsetje 		 * Don't follow symlinks.  A symlink must refer to
1773986fd29aSsetje 		 * a file that would appear in the archive through
1774986fd29aSsetje 		 * a direct reference.  This matches the archive
1775986fd29aSsetje 		 * construction behavior.
1776986fd29aSsetje 		 */
17777c478bd9Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
1778986fd29aSsetje 		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
17797c478bd9Sstevel@tonic-gate 			/*
17807c478bd9Sstevel@tonic-gate 			 * Some files may not exist.
17817c478bd9Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
17827c478bd9Sstevel@tonic-gate 			 * Emit verbose message only
17837c478bd9Sstevel@tonic-gate 			 */
17847c478bd9Sstevel@tonic-gate 			if (bam_verbose)
17857c478bd9Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
17867c478bd9Sstevel@tonic-gate 		}
17877c478bd9Sstevel@tonic-gate 	}
17887c478bd9Sstevel@tonic-gate }
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate static void
17917c478bd9Sstevel@tonic-gate savenew(char *root)
17927c478bd9Sstevel@tonic-gate {
17937c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
17947c478bd9Sstevel@tonic-gate 	char path2[PATH_MAX];
17957c478bd9Sstevel@tonic-gate 	size_t sz;
17967c478bd9Sstevel@tonic-gate 	char *nstat;
17977c478bd9Sstevel@tonic-gate 	int fd, wrote, error;
17987c478bd9Sstevel@tonic-gate 
17997c478bd9Sstevel@tonic-gate 	nstat = NULL;
18007c478bd9Sstevel@tonic-gate 	sz = 0;
18017c478bd9Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
18027c478bd9Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
18037c478bd9Sstevel@tonic-gate 	if (error) {
18047c478bd9Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
18057c478bd9Sstevel@tonic-gate 		return;
18067c478bd9Sstevel@tonic-gate 	}
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
18097c478bd9Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
18107c478bd9Sstevel@tonic-gate 	if (fd == -1) {
18117c478bd9Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
18127c478bd9Sstevel@tonic-gate 		free(nstat);
18137c478bd9Sstevel@tonic-gate 		return;
18147c478bd9Sstevel@tonic-gate 	}
18157c478bd9Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
18167c478bd9Sstevel@tonic-gate 	if (wrote != sz) {
18177c478bd9Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
18187c478bd9Sstevel@tonic-gate 		(void) close(fd);
18197c478bd9Sstevel@tonic-gate 		free(nstat);
18207c478bd9Sstevel@tonic-gate 		return;
18217c478bd9Sstevel@tonic-gate 	}
18227c478bd9Sstevel@tonic-gate 	(void) close(fd);
18237c478bd9Sstevel@tonic-gate 	free(nstat);
18247c478bd9Sstevel@tonic-gate 
18257c478bd9Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
18267c478bd9Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
18277c478bd9Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
18287c478bd9Sstevel@tonic-gate 	}
18297c478bd9Sstevel@tonic-gate }
18307c478bd9Sstevel@tonic-gate 
18317c478bd9Sstevel@tonic-gate static void
18327c478bd9Sstevel@tonic-gate clear_walk_args(void)
18337c478bd9Sstevel@tonic-gate {
18347c478bd9Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
18357c478bd9Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
18367c478bd9Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
18377c478bd9Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
18387c478bd9Sstevel@tonic-gate 	walk_arg.need_update = 0;
18397c478bd9Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
18407c478bd9Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
18417c478bd9Sstevel@tonic-gate }
18427c478bd9Sstevel@tonic-gate 
18437c478bd9Sstevel@tonic-gate /*
18447c478bd9Sstevel@tonic-gate  * Returns:
18457c478bd9Sstevel@tonic-gate  *	0 - no update necessary
18467c478bd9Sstevel@tonic-gate  *	1 - update required.
18477c478bd9Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
18487c478bd9Sstevel@tonic-gate  *
18497c478bd9Sstevel@tonic-gate  * Special handling for check (-n):
18507c478bd9Sstevel@tonic-gate  * ================================
18517c478bd9Sstevel@tonic-gate  * The check (-n) option produces parseable output.
18527c478bd9Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
18537c478bd9Sstevel@tonic-gate  * to out of sync files.
18547c478bd9Sstevel@tonic-gate  * All stderr messages are still printed though.
18557c478bd9Sstevel@tonic-gate  *
18567c478bd9Sstevel@tonic-gate  */
18577c478bd9Sstevel@tonic-gate static int
18587c478bd9Sstevel@tonic-gate update_required(char *root)
18597c478bd9Sstevel@tonic-gate {
18607c478bd9Sstevel@tonic-gate 	struct stat sb;
18617c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
18627c478bd9Sstevel@tonic-gate 	filelist_t flist;
18637c478bd9Sstevel@tonic-gate 	filelist_t *flistp = &flist;
18647c478bd9Sstevel@tonic-gate 	int need_update;
18657c478bd9Sstevel@tonic-gate 
18667c478bd9Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
18677c478bd9Sstevel@tonic-gate 
18687c478bd9Sstevel@tonic-gate 	walk_arg.need_update = 0;
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate 	/*
18717c478bd9Sstevel@tonic-gate 	 * Without consulting stat data, check if we need update
18727c478bd9Sstevel@tonic-gate 	 */
18737c478bd9Sstevel@tonic-gate 	check_flags_and_files(root);
18747c478bd9Sstevel@tonic-gate 
18757c478bd9Sstevel@tonic-gate 	/*
18767c478bd9Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
18777c478bd9Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
18787c478bd9Sstevel@tonic-gate 	 */
18797c478bd9Sstevel@tonic-gate 	if (bam_smf_check) {
18807c478bd9Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
18817c478bd9Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
18827c478bd9Sstevel@tonic-gate 			return (0);
18837c478bd9Sstevel@tonic-gate 	}
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate 	/*
18867c478bd9Sstevel@tonic-gate 	 * consult stat data only if we haven't made a decision
18877c478bd9Sstevel@tonic-gate 	 * about update. If checking (-n) however, we always
18887c478bd9Sstevel@tonic-gate 	 * need stat data (since we want to compare old and new)
18897c478bd9Sstevel@tonic-gate 	 */
18907c478bd9Sstevel@tonic-gate 	if (!walk_arg.need_update || bam_check)
18917c478bd9Sstevel@tonic-gate 		getoldstat(root);
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	/*
1894a28d77b8Svikram 	 * Check if the archive contains files that are no longer
1895a28d77b8Svikram 	 * present on the root filesystem.
1896a28d77b8Svikram 	 */
1897a28d77b8Svikram 	if (!walk_arg.need_update || bam_check)
1898a28d77b8Svikram 		check4stale(root);
1899a28d77b8Svikram 
1900a28d77b8Svikram 	/*
19017c478bd9Sstevel@tonic-gate 	 * read list of files
19027c478bd9Sstevel@tonic-gate 	 */
19037c478bd9Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
19047c478bd9Sstevel@tonic-gate 		clear_walk_args();
19057c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
19067c478bd9Sstevel@tonic-gate 	}
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	/*
19117c478bd9Sstevel@tonic-gate 	 * At this point either the update is required
19127c478bd9Sstevel@tonic-gate 	 * or the decision is pending. In either case
19137c478bd9Sstevel@tonic-gate 	 * we need to create new stat nvlist
19147c478bd9Sstevel@tonic-gate 	 */
19157c478bd9Sstevel@tonic-gate 	create_newstat();
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	/*
19187c478bd9Sstevel@tonic-gate 	 * This walk does 2 things:
19197c478bd9Sstevel@tonic-gate 	 *  	- gets new stat data for every file
19207c478bd9Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
19217c478bd9Sstevel@tonic-gate 	 */
19227c478bd9Sstevel@tonic-gate 	walk_list(root, &flist);
19237c478bd9Sstevel@tonic-gate 
19247c478bd9Sstevel@tonic-gate 	/* done with the file list */
19257c478bd9Sstevel@tonic-gate 	filelist_free(flistp);
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate 	/*
19287c478bd9Sstevel@tonic-gate 	 * if we didn't succeed in  creating new stat data above
19297c478bd9Sstevel@tonic-gate 	 * just return result of update check so that archive is built.
19307c478bd9Sstevel@tonic-gate 	 */
19317c478bd9Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
19327c478bd9Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
19337c478bd9Sstevel@tonic-gate 		need_update = walk_arg.need_update;
19347c478bd9Sstevel@tonic-gate 		clear_walk_args();
19357c478bd9Sstevel@tonic-gate 		return (need_update ? 1 : 0);
19367c478bd9Sstevel@tonic-gate 	}
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	/*
19407c478bd9Sstevel@tonic-gate 	 * If no update required, discard newstat
19417c478bd9Sstevel@tonic-gate 	 */
19427c478bd9Sstevel@tonic-gate 	if (!walk_arg.need_update) {
19437c478bd9Sstevel@tonic-gate 		clear_walk_args();
19447c478bd9Sstevel@tonic-gate 		return (0);
19457c478bd9Sstevel@tonic-gate 	}
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate 	return (1);
19487c478bd9Sstevel@tonic-gate }
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate static error_t
19517c478bd9Sstevel@tonic-gate create_ramdisk(char *root)
19527c478bd9Sstevel@tonic-gate {
19537c478bd9Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
19547c478bd9Sstevel@tonic-gate 	size_t len;
19557c478bd9Sstevel@tonic-gate 	struct stat sb;
19567c478bd9Sstevel@tonic-gate 
19577c478bd9Sstevel@tonic-gate 	/*
19587c478bd9Sstevel@tonic-gate 	 * Setup command args for create_ramdisk.ksh
19597c478bd9Sstevel@tonic-gate 	 */
1960986fd29aSsetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
19617c478bd9Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
19627c478bd9Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
19637c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
19647c478bd9Sstevel@tonic-gate 	}
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
1967d876c67dSjg 	if (bam_alt_platform)
1968d876c67dSjg 		len += strlen(bam_platform) + strlen("-p ");
19697c478bd9Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
19707c478bd9Sstevel@tonic-gate 
1971d876c67dSjg 	if (bam_alt_platform) {
1972d876c67dSjg 		assert(strlen(root) > 1);
1973d876c67dSjg 		(void) snprintf(cmdline, len, "%s -p %s -R %s",
1974d876c67dSjg 		    path, bam_platform, root);
1975d876c67dSjg 		/* chop off / at the end */
1976d876c67dSjg 		cmdline[strlen(cmdline) - 1] = '\0';
1977d876c67dSjg 	} else if (strlen(root) > 1) {
19787c478bd9Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
19797c478bd9Sstevel@tonic-gate 		/* chop off / at the end */
19807c478bd9Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
19817c478bd9Sstevel@tonic-gate 	} else
19827c478bd9Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
19837c478bd9Sstevel@tonic-gate 
1984986fd29aSsetje 	if (exec_cmd(cmdline, NULL) != 0) {
19857c478bd9Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
19867c478bd9Sstevel@tonic-gate 		free(cmdline);
19877c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
19887c478bd9Sstevel@tonic-gate 	}
19897c478bd9Sstevel@tonic-gate 	free(cmdline);
19907c478bd9Sstevel@tonic-gate 
19917c478bd9Sstevel@tonic-gate 	/*
1992986fd29aSsetje 	 * The existence of the expected archives used to be
1993986fd29aSsetje 	 * verified here. This check is done in create_ramdisk as
1994986fd29aSsetje 	 * it needs to be in sync with the altroot operated upon.
19957c478bd9Sstevel@tonic-gate 	 */
19967c478bd9Sstevel@tonic-gate 
19977c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
19987c478bd9Sstevel@tonic-gate }
19997c478bd9Sstevel@tonic-gate 
20007c478bd9Sstevel@tonic-gate /*
20017c478bd9Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
20027c478bd9Sstevel@tonic-gate  * 1 - is miniroot
20037c478bd9Sstevel@tonic-gate  * 0 - is not
20047c478bd9Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
20057c478bd9Sstevel@tonic-gate  */
20067c478bd9Sstevel@tonic-gate static int
20077c478bd9Sstevel@tonic-gate is_ramdisk(char *root)
20087c478bd9Sstevel@tonic-gate {
20097c478bd9Sstevel@tonic-gate 	struct extmnttab mnt;
20107c478bd9Sstevel@tonic-gate 	FILE *fp;
20117c478bd9Sstevel@tonic-gate 	int found;
2012b610f78eSvikram 	char mntpt[PATH_MAX];
2013b610f78eSvikram 	char *cp;
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 	/*
20167c478bd9Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
20177c478bd9Sstevel@tonic-gate 	 * of dubious value:
2018b610f78eSvikram 	 *	- create boot_archive on a lofi-mounted boot_archive
20197c478bd9Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
20207c478bd9Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
20217c478bd9Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
20227c478bd9Sstevel@tonic-gate 	 * worth it.
20237c478bd9Sstevel@tonic-gate 	 * The other two conditions are handled here
20247c478bd9Sstevel@tonic-gate 	 */
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
20277c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
20287c478bd9Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
20297c478bd9Sstevel@tonic-gate 		return (0);
20307c478bd9Sstevel@tonic-gate 	}
20317c478bd9Sstevel@tonic-gate 
20327c478bd9Sstevel@tonic-gate 	resetmnttab(fp);
20337c478bd9Sstevel@tonic-gate 
2034b610f78eSvikram 	/*
2035b610f78eSvikram 	 * Remove any trailing / from the mount point
2036b610f78eSvikram 	 */
2037b610f78eSvikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
2038b610f78eSvikram 	if (strcmp(root, "/") != 0) {
2039b610f78eSvikram 		cp = mntpt + strlen(mntpt) - 1;
2040b610f78eSvikram 		if (*cp == '/')
2041b610f78eSvikram 			*cp = '\0';
2042b610f78eSvikram 	}
20437c478bd9Sstevel@tonic-gate 	found = 0;
20447c478bd9Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
2045b610f78eSvikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
20467c478bd9Sstevel@tonic-gate 			found = 1;
20477c478bd9Sstevel@tonic-gate 			break;
20487c478bd9Sstevel@tonic-gate 		}
20497c478bd9Sstevel@tonic-gate 	}
20507c478bd9Sstevel@tonic-gate 
20517c478bd9Sstevel@tonic-gate 	if (!found) {
20527c478bd9Sstevel@tonic-gate 		if (bam_verbose)
2053b610f78eSvikram 			bam_error(NOT_IN_MNTTAB, mntpt);
20547c478bd9Sstevel@tonic-gate 		(void) fclose(fp);
20557c478bd9Sstevel@tonic-gate 		return (0);
20567c478bd9Sstevel@tonic-gate 	}
20577c478bd9Sstevel@tonic-gate 
20587c478bd9Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
20597c478bd9Sstevel@tonic-gate 		if (bam_verbose)
20607c478bd9Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
20617c478bd9Sstevel@tonic-gate 		(void) fclose(fp);
20627c478bd9Sstevel@tonic-gate 		return (1);
20637c478bd9Sstevel@tonic-gate 	}
20647c478bd9Sstevel@tonic-gate 
20657c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate 	return (0);
20687c478bd9Sstevel@tonic-gate }
20697c478bd9Sstevel@tonic-gate 
20707c478bd9Sstevel@tonic-gate static int
2071986fd29aSsetje is_boot_archive(char *root)
20727c478bd9Sstevel@tonic-gate {
20737c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
20747c478bd9Sstevel@tonic-gate 	struct stat sb;
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 	/*
2077986fd29aSsetje 	 * We can't create an archive without the create_ramdisk script
20787c478bd9Sstevel@tonic-gate 	 */
2079986fd29aSsetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
20807c478bd9Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
20817c478bd9Sstevel@tonic-gate 		if (bam_verbose)
20827c478bd9Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
20837c478bd9Sstevel@tonic-gate 		return (0);
20847c478bd9Sstevel@tonic-gate 	}
20857c478bd9Sstevel@tonic-gate 
2086986fd29aSsetje 	return (1);
2087986fd29aSsetje }
2088986fd29aSsetje 
20897c478bd9Sstevel@tonic-gate /*
2090986fd29aSsetje  * Need to call this for anything that operates on the GRUB menu
2091986fd29aSsetje  */
2092986fd29aSsetje int
2093986fd29aSsetje is_grub(const char *root)
2094986fd29aSsetje {
2095986fd29aSsetje 	char path[PATH_MAX];
2096986fd29aSsetje 	struct stat sb;
2097986fd29aSsetje 
2098986fd29aSsetje 	/*
2099986fd29aSsetje 	 * GRUB_DIR is required to modify the menu
21007c478bd9Sstevel@tonic-gate 	 */
21017c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR);
21027c478bd9Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
2103986fd29aSsetje 		if (bam_debug)
21047c478bd9Sstevel@tonic-gate 			bam_print(DIR_MISS, path);
21057c478bd9Sstevel@tonic-gate 		return (0);
21067c478bd9Sstevel@tonic-gate 	}
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 	return (1);
21097c478bd9Sstevel@tonic-gate }
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate static int
21127c478bd9Sstevel@tonic-gate is_readonly(char *root)
21137c478bd9Sstevel@tonic-gate {
21147c478bd9Sstevel@tonic-gate 	struct statvfs vfs;
21157c478bd9Sstevel@tonic-gate 
21167c478bd9Sstevel@tonic-gate 	/*
21177c478bd9Sstevel@tonic-gate 	 * Check for RDONLY filesystem
21187c478bd9Sstevel@tonic-gate 	 * When in doubt assume it is not readonly
21197c478bd9Sstevel@tonic-gate 	 */
21207c478bd9Sstevel@tonic-gate 	if (statvfs(root, &vfs) != 0) {
21217c478bd9Sstevel@tonic-gate 		if (bam_verbose)
21227c478bd9Sstevel@tonic-gate 			bam_error(STATVFS_FAIL, root, strerror(errno));
21237c478bd9Sstevel@tonic-gate 		return (0);
21247c478bd9Sstevel@tonic-gate 	}
21257c478bd9Sstevel@tonic-gate 
21267c478bd9Sstevel@tonic-gate 	if (vfs.f_flag & ST_RDONLY) {
21277c478bd9Sstevel@tonic-gate 		return (1);
21287c478bd9Sstevel@tonic-gate 	}
21297c478bd9Sstevel@tonic-gate 
21307c478bd9Sstevel@tonic-gate 	return (0);
21317c478bd9Sstevel@tonic-gate }
21327c478bd9Sstevel@tonic-gate 
2133*e7cbe64fSgw25295 static int
2134*e7cbe64fSgw25295 is_zfs(char *root, char **poolname)
2135*e7cbe64fSgw25295 {
2136*e7cbe64fSgw25295 	struct statvfs64 vfs;
2137*e7cbe64fSgw25295 	FILE *fp;
2138*e7cbe64fSgw25295 	struct extmnttab mnt;
2139*e7cbe64fSgw25295 	dev_t devicenum;
2140*e7cbe64fSgw25295 	char *special = NULL;
2141*e7cbe64fSgw25295 	char *cp;
2142*e7cbe64fSgw25295 
2143*e7cbe64fSgw25295 	/* poolname can be null */
2144*e7cbe64fSgw25295 	if (poolname)
2145*e7cbe64fSgw25295 		*poolname = NULL;
2146*e7cbe64fSgw25295 
2147*e7cbe64fSgw25295 	if (statvfs64(root, &vfs) != 0) {
2148*e7cbe64fSgw25295 		if (bam_verbose)
2149*e7cbe64fSgw25295 			bam_error(STATVFS_FAIL, root, strerror(errno));
2150*e7cbe64fSgw25295 		return (0);
2151*e7cbe64fSgw25295 	}
2152*e7cbe64fSgw25295 
2153*e7cbe64fSgw25295 	if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) != 0)
2154*e7cbe64fSgw25295 		return (0);
2155*e7cbe64fSgw25295 
2156*e7cbe64fSgw25295 	if (poolname == NULL)
2157*e7cbe64fSgw25295 		return (1);
2158*e7cbe64fSgw25295 
2159*e7cbe64fSgw25295 	/*
2160*e7cbe64fSgw25295 	 * Now find the mnttab entry so that we can extract the
2161*e7cbe64fSgw25295 	 * pool name from the special device field.
2162*e7cbe64fSgw25295 	 */
2163*e7cbe64fSgw25295 	fp = fopen(MNTTAB, "r");
2164*e7cbe64fSgw25295 	if (fp == NULL) {
2165*e7cbe64fSgw25295 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2166*e7cbe64fSgw25295 		return (0);
2167*e7cbe64fSgw25295 	}
2168*e7cbe64fSgw25295 
2169*e7cbe64fSgw25295 	resetmnttab(fp);
2170*e7cbe64fSgw25295 
2171*e7cbe64fSgw25295 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
2172*e7cbe64fSgw25295 		devicenum = makedevice(mnt.mnt_major, mnt.mnt_minor);
2173*e7cbe64fSgw25295 		if (devicenum == vfs.f_fsid) {
2174*e7cbe64fSgw25295 			special = s_strdup(mnt.mnt_special);
2175*e7cbe64fSgw25295 			if ((cp = strchr(special, '/')) != NULL)
2176*e7cbe64fSgw25295 				*cp = '\0';
2177*e7cbe64fSgw25295 			*poolname = s_strdup(special);
2178*e7cbe64fSgw25295 			break;
2179*e7cbe64fSgw25295 		}
2180*e7cbe64fSgw25295 	}
2181*e7cbe64fSgw25295 
2182*e7cbe64fSgw25295 	(void) fclose(fp);
2183*e7cbe64fSgw25295 
2184*e7cbe64fSgw25295 	if (special) {
2185*e7cbe64fSgw25295 		free(special);
2186*e7cbe64fSgw25295 		return (1);
2187*e7cbe64fSgw25295 	}
2188*e7cbe64fSgw25295 
2189*e7cbe64fSgw25295 	return (0);
2190*e7cbe64fSgw25295 }
2191*e7cbe64fSgw25295 
21927c478bd9Sstevel@tonic-gate static error_t
21937c478bd9Sstevel@tonic-gate update_archive(char *root, char *opt)
21947c478bd9Sstevel@tonic-gate {
21957c478bd9Sstevel@tonic-gate 	error_t ret;
21967c478bd9Sstevel@tonic-gate 
21977c478bd9Sstevel@tonic-gate 	assert(root);
21987c478bd9Sstevel@tonic-gate 	assert(opt == NULL);
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate 	/*
2201986fd29aSsetje 	 * root must belong to a boot archive based  OS,
22027c478bd9Sstevel@tonic-gate 	 */
2203986fd29aSsetje 	if (!is_boot_archive(root)) {
2204b610f78eSvikram 		/*
2205b610f78eSvikram 		 * Emit message only if not in context of update_all.
2206b610f78eSvikram 		 * If in update_all, emit only if verbose flag is set.
2207b610f78eSvikram 		 */
2208b610f78eSvikram 		if (!bam_update_all || bam_verbose)
2209b610f78eSvikram 			bam_print(NOT_GRUB_BOOT, root);
22107c478bd9Sstevel@tonic-gate 		return (BAM_SUCCESS);
22117c478bd9Sstevel@tonic-gate 	}
22127c478bd9Sstevel@tonic-gate 
22137c478bd9Sstevel@tonic-gate 	/*
22148c1b6884Sszhou 	 * If smf check is requested when / is writable (can happen
22158c1b6884Sszhou 	 * on first reboot following an upgrade because service
22168c1b6884Sszhou 	 * dependency is messed up), skip the check.
22178c1b6884Sszhou 	 */
22188c1b6884Sszhou 	if (bam_smf_check && !bam_root_readonly)
22198c1b6884Sszhou 		return (BAM_SUCCESS);
22208c1b6884Sszhou 
22218c1b6884Sszhou 	/*
22228c1b6884Sszhou 	 * root must be writable. This check applies to alternate
22238c1b6884Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
22247c478bd9Sstevel@tonic-gate 	 * Note: statvfs() does not always report the truth
22257c478bd9Sstevel@tonic-gate 	 */
222661cb17bdSsetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
22278c1b6884Sszhou 		if (bam_verbose)
22287c478bd9Sstevel@tonic-gate 			bam_print(RDONLY_FS, root);
22297c478bd9Sstevel@tonic-gate 		return (BAM_SUCCESS);
22307c478bd9Sstevel@tonic-gate 	}
22317c478bd9Sstevel@tonic-gate 
22327c478bd9Sstevel@tonic-gate 	/*
22337c478bd9Sstevel@tonic-gate 	 * Don't generate archive on ramdisk
22347c478bd9Sstevel@tonic-gate 	 */
22357c478bd9Sstevel@tonic-gate 	if (is_ramdisk(root)) {
22367c478bd9Sstevel@tonic-gate 		if (bam_verbose)
22377c478bd9Sstevel@tonic-gate 			bam_print(SKIP_RAMDISK);
22387c478bd9Sstevel@tonic-gate 		return (BAM_SUCCESS);
22397c478bd9Sstevel@tonic-gate 	}
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 	/*
22427c478bd9Sstevel@tonic-gate 	 * Now check if updated is really needed
22437c478bd9Sstevel@tonic-gate 	 */
22447c478bd9Sstevel@tonic-gate 	ret = update_required(root);
22457c478bd9Sstevel@tonic-gate 
22467c478bd9Sstevel@tonic-gate 	/*
22477c478bd9Sstevel@tonic-gate 	 * The check command (-n) is *not* a dry run
22487c478bd9Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
22497c478bd9Sstevel@tonic-gate 	 */
22507c478bd9Sstevel@tonic-gate 	if (bam_check) {
22517c478bd9Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
22527c478bd9Sstevel@tonic-gate 	}
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 	if (ret == 1) {
22557c478bd9Sstevel@tonic-gate 		/* create the ramdisk */
22567c478bd9Sstevel@tonic-gate 		ret = create_ramdisk(root);
22577c478bd9Sstevel@tonic-gate 	}
2258986fd29aSsetje 
2259986fd29aSsetje 	/* if the archive is updated, save the new stat data */
2260986fd29aSsetje 	if (ret == 0 && walk_arg.new_nvlp != NULL) {
2261986fd29aSsetje 		savenew(root);
2262986fd29aSsetje 	}
2263986fd29aSsetje 
2264986fd29aSsetje 	clear_walk_args();
2265986fd29aSsetje 
22667c478bd9Sstevel@tonic-gate 	return (ret);
22677c478bd9Sstevel@tonic-gate }
22687c478bd9Sstevel@tonic-gate 
2269b610f78eSvikram static void
2270fbac2b2bSvikram update_fdisk(void)
2271fbac2b2bSvikram {
2272fbac2b2bSvikram 	struct stat sb;
2273fbac2b2bSvikram 	char cmd[PATH_MAX];
2274fbac2b2bSvikram 	int ret1, ret2;
2275fbac2b2bSvikram 
2276fbac2b2bSvikram 	assert(stat(GRUB_fdisk, &sb) == 0);
2277fbac2b2bSvikram 	assert(stat(GRUB_fdisk_target, &sb) == 0);
2278fbac2b2bSvikram 
2279fbac2b2bSvikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`",
2280fbac2b2bSvikram 	    GRUB_fdisk, GRUB_fdisk_target);
2281fbac2b2bSvikram 
2282fbac2b2bSvikram 	bam_print(UPDATING_FDISK);
2283986fd29aSsetje 	if (exec_cmd(cmd, NULL) != 0) {
2284fbac2b2bSvikram 		bam_error(FDISK_UPDATE_FAILED);
2285fbac2b2bSvikram 	}
2286fbac2b2bSvikram 
2287fbac2b2bSvikram 	/*
2288fbac2b2bSvikram 	 * We are done, remove the files.
2289fbac2b2bSvikram 	 */
2290fbac2b2bSvikram 	ret1 = unlink(GRUB_fdisk);
2291fbac2b2bSvikram 	ret2 = unlink(GRUB_fdisk_target);
2292fbac2b2bSvikram 	if (ret1 != 0 || ret2 != 0) {
2293fbac2b2bSvikram 		bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target);
2294fbac2b2bSvikram 	}
2295fbac2b2bSvikram }
2296fbac2b2bSvikram 
2297fbac2b2bSvikram static void
2298b610f78eSvikram restore_grub_slice(void)
2299b610f78eSvikram {
2300b610f78eSvikram 	struct stat sb;
2301b610f78eSvikram 	char *mntpt, *physlice;
2302b610f78eSvikram 	int mnted;	/* set if we did a mount */
2303b610f78eSvikram 	char menupath[PATH_MAX], cmd[PATH_MAX];
2304b610f78eSvikram 
2305b610f78eSvikram 	if (stat(GRUB_slice, &sb) != 0) {
2306b610f78eSvikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
2307b610f78eSvikram 		return;
2308b610f78eSvikram 	}
2309b610f78eSvikram 
2310b610f78eSvikram 	/*
2311b610f78eSvikram 	 * If we are doing an luactivate, don't attempt to restore GRUB or else
2312b610f78eSvikram 	 * we may not be able to get to DCA boot environments. Let luactivate
2313b610f78eSvikram 	 * handle GRUB/DCA installation
2314b610f78eSvikram 	 */
2315b610f78eSvikram 	if (stat(LU_ACTIVATE_FILE, &sb) == 0) {
2316b610f78eSvikram 		return;
2317b610f78eSvikram 	}
2318b610f78eSvikram 
2319b610f78eSvikram 	mnted = 0;
2320b610f78eSvikram 	physlice = NULL;
232140541d5dSvikram 	mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL);
2322b610f78eSvikram 	if (mntpt == NULL) {
2323b610f78eSvikram 		bam_error(CANNOT_RESTORE_GRUB_SLICE);
2324b610f78eSvikram 		return;
2325b610f78eSvikram 	}
2326b610f78eSvikram 
2327b610f78eSvikram 	(void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU);
2328b610f78eSvikram 	if (stat(menupath, &sb) == 0) {
232940541d5dSvikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2330b610f78eSvikram 		return;
2331b610f78eSvikram 	}
2332b610f78eSvikram 
2333b610f78eSvikram 	/*
2334b610f78eSvikram 	 * The menu is missing - we need to do a restore
2335b610f78eSvikram 	 */
2336b610f78eSvikram 	bam_print(RESTORING_GRUB);
2337b610f78eSvikram 
2338b610f78eSvikram 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s %s",
2339b610f78eSvikram 	    INSTALLGRUB, STAGE1, STAGE2, physlice);
2340b610f78eSvikram 
2341986fd29aSsetje 	if (exec_cmd(cmd, NULL) != 0) {
2342b610f78eSvikram 		bam_error(RESTORE_GRUB_FAILED);
234340541d5dSvikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2344b610f78eSvikram 		return;
2345b610f78eSvikram 	}
2346b610f78eSvikram 
2347b610f78eSvikram 	if (stat(GRUB_backup_menu, &sb) != 0) {
2348b610f78eSvikram 		bam_error(MISSING_BACKUP_MENU,
2349b610f78eSvikram 		    GRUB_backup_menu, strerror(errno));
235040541d5dSvikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2351b610f78eSvikram 		return;
2352b610f78eSvikram 	}
2353b610f78eSvikram 
2354b610f78eSvikram 	(void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s",
2355b610f78eSvikram 	    GRUB_backup_menu, menupath);
2356b610f78eSvikram 
2357986fd29aSsetje 	if (exec_cmd(cmd, NULL) != 0) {
2358b610f78eSvikram 		bam_error(RESTORE_MENU_FAILED, menupath);
235940541d5dSvikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2360b610f78eSvikram 		return;
2361b610f78eSvikram 	}
2362b610f78eSvikram 
2363b610f78eSvikram 	/* Success */
236440541d5dSvikram 	umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2365b610f78eSvikram }
2366b610f78eSvikram 
23677c478bd9Sstevel@tonic-gate static error_t
23687c478bd9Sstevel@tonic-gate update_all(char *root, char *opt)
23697c478bd9Sstevel@tonic-gate {
23707c478bd9Sstevel@tonic-gate 	struct extmnttab mnt;
23717c478bd9Sstevel@tonic-gate 	struct stat sb;
23727c478bd9Sstevel@tonic-gate 	FILE *fp;
23737c478bd9Sstevel@tonic-gate 	char multibt[PATH_MAX];
2374986fd29aSsetje 	char creatram[PATH_MAX];
23757c478bd9Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
2376fbac2b2bSvikram 	int ret1, ret2;
23777c478bd9Sstevel@tonic-gate 
237840541d5dSvikram 	assert(root);
23797c478bd9Sstevel@tonic-gate 	assert(opt == NULL);
23807c478bd9Sstevel@tonic-gate 
238140541d5dSvikram 	if (bam_rootlen != 1 || *root != '/') {
238240541d5dSvikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
238340541d5dSvikram 		bam_error(ALT_ROOT_INVALID, multibt);
238440541d5dSvikram 		return (BAM_ERROR);
238540541d5dSvikram 	}
238640541d5dSvikram 
23877c478bd9Sstevel@tonic-gate 	/*
238898892a30Snadkarni 	 * Check to see if we are in the midst of safemode patching
238998892a30Snadkarni 	 * If so skip building the archive for /. Instead build it
239098892a30Snadkarni 	 * against the latest bits obtained by creating a fresh lofs
239198892a30Snadkarni 	 * mount of root.
239298892a30Snadkarni 	 */
239398892a30Snadkarni 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
239498892a30Snadkarni 		if (mkdir(LOFS_PATCH_MNT, 0755) == -1 &&
239598892a30Snadkarni 		    errno != EEXIST) {
239698892a30Snadkarni 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
239798892a30Snadkarni 			    strerror(errno));
239898892a30Snadkarni 			ret = BAM_ERROR;
239998892a30Snadkarni 			goto out;
240098892a30Snadkarni 		}
240198892a30Snadkarni 		(void) snprintf(multibt, sizeof (multibt),
240298892a30Snadkarni 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
2403986fd29aSsetje 		if (exec_cmd(multibt, NULL) != 0) {
240498892a30Snadkarni 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
240598892a30Snadkarni 			ret = BAM_ERROR;
240698892a30Snadkarni 		}
240798892a30Snadkarni 		if (ret != BAM_ERROR) {
240898892a30Snadkarni 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
240998892a30Snadkarni 			    LOFS_PATCH_MNT);
241098892a30Snadkarni 			bam_rootlen = strlen(rootbuf);
241198892a30Snadkarni 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
241298892a30Snadkarni 				ret = BAM_ERROR;
241395b1e0e9Snadkarni 			/*
241495b1e0e9Snadkarni 			 * unmount the lofs mount since there could be
241595b1e0e9Snadkarni 			 * multiple invocations of bootadm -a update_all
241695b1e0e9Snadkarni 			 */
241795b1e0e9Snadkarni 			(void) snprintf(multibt, sizeof (multibt),
241895b1e0e9Snadkarni 			    "/sbin/umount %s", LOFS_PATCH_MNT);
2419986fd29aSsetje 			if (exec_cmd(multibt, NULL) != 0) {
242095b1e0e9Snadkarni 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
242195b1e0e9Snadkarni 				ret = BAM_ERROR;
242295b1e0e9Snadkarni 			}
242398892a30Snadkarni 		}
242498892a30Snadkarni 	} else {
242598892a30Snadkarni 		/*
24267c478bd9Sstevel@tonic-gate 		 * First update archive for current root
24277c478bd9Sstevel@tonic-gate 		 */
24287c478bd9Sstevel@tonic-gate 		if (update_archive(root, opt) != BAM_SUCCESS)
24297c478bd9Sstevel@tonic-gate 			ret = BAM_ERROR;
243098892a30Snadkarni 	}
243198892a30Snadkarni 
243298892a30Snadkarni 	if (ret == BAM_ERROR)
243398892a30Snadkarni 		goto out;
24347c478bd9Sstevel@tonic-gate 
24357c478bd9Sstevel@tonic-gate 	/*
24367c478bd9Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
24377c478bd9Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
24387c478bd9Sstevel@tonic-gate 	 */
24397c478bd9Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
24407c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
24417c478bd9Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2442b610f78eSvikram 		ret = BAM_ERROR;
2443b610f78eSvikram 		goto out;
24447c478bd9Sstevel@tonic-gate 	}
24457c478bd9Sstevel@tonic-gate 
24467c478bd9Sstevel@tonic-gate 	resetmnttab(fp);
24477c478bd9Sstevel@tonic-gate 
24487c478bd9Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
24497c478bd9Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
24507c478bd9Sstevel@tonic-gate 			continue;
24517c478bd9Sstevel@tonic-gate 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
24527c478bd9Sstevel@tonic-gate 			continue;
24537c478bd9Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
24547c478bd9Sstevel@tonic-gate 			continue;
24557c478bd9Sstevel@tonic-gate 
2456986fd29aSsetje 		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
2457986fd29aSsetje 		    mnt.mnt_mountp, CREATE_RAMDISK);
24587c478bd9Sstevel@tonic-gate 
2459986fd29aSsetje 		if (stat(creatram, &sb) == -1)
24607c478bd9Sstevel@tonic-gate 			continue;
24617c478bd9Sstevel@tonic-gate 
24627c478bd9Sstevel@tonic-gate 		/*
24637c478bd9Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
24647c478bd9Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
24657c478bd9Sstevel@tonic-gate 		 */
24667c478bd9Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
24677c478bd9Sstevel@tonic-gate 		    mnt.mnt_mountp);
24687c478bd9Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
2469ae115bc7Smrj 
2470ae115bc7Smrj 		/*
2471ae115bc7Smrj 		 * It's possible that other mounts may be an alternate boot
2472ae115bc7Smrj 		 * architecture, so check it again.
2473ae115bc7Smrj 		 */
2474ae115bc7Smrj 		if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) ||
2475ae115bc7Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
24767c478bd9Sstevel@tonic-gate 			ret = BAM_ERROR;
24777c478bd9Sstevel@tonic-gate 	}
24787c478bd9Sstevel@tonic-gate 
24797c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
24807c478bd9Sstevel@tonic-gate 
2481b610f78eSvikram out:
2482b610f78eSvikram 	if (stat(GRUB_slice, &sb) == 0) {
2483b610f78eSvikram 		restore_grub_slice();
2484b610f78eSvikram 	}
2485b610f78eSvikram 
2486fbac2b2bSvikram 	/*
2487fbac2b2bSvikram 	 * Update fdisk table as we go down. Updating it when
2488fbac2b2bSvikram 	 * the system is running will confuse biosdev.
2489fbac2b2bSvikram 	 */
2490fbac2b2bSvikram 	ret1 = stat(GRUB_fdisk, &sb);
2491fbac2b2bSvikram 	ret2 = stat(GRUB_fdisk_target, &sb);
2492fbac2b2bSvikram 	if ((ret1 == 0) && (ret2 == 0)) {
2493fbac2b2bSvikram 		update_fdisk();
2494fbac2b2bSvikram 	} else if ((ret1 == 0) ^ (ret2 == 0)) {
2495fbac2b2bSvikram 		/*
2496fbac2b2bSvikram 		 * It is an error for one file to be
2497fbac2b2bSvikram 		 * present and the other absent.
2498fbac2b2bSvikram 		 * It is normal for both files to be
2499fbac2b2bSvikram 		 * absent - it indicates that no fdisk
2500fbac2b2bSvikram 		 * update is required.
2501fbac2b2bSvikram 		 */
2502fbac2b2bSvikram 		bam_error(MISSING_FDISK_FILE,
2503fbac2b2bSvikram 		    ret1 ? GRUB_fdisk : GRUB_fdisk_target);
2504fbac2b2bSvikram 		ret = BAM_ERROR;
2505fbac2b2bSvikram 	}
2506fbac2b2bSvikram 
25077c478bd9Sstevel@tonic-gate 	return (ret);
25087c478bd9Sstevel@tonic-gate }
25097c478bd9Sstevel@tonic-gate 
25107c478bd9Sstevel@tonic-gate static void
25117c478bd9Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
25127c478bd9Sstevel@tonic-gate {
25137c478bd9Sstevel@tonic-gate 	if (mp->start == NULL) {
25147c478bd9Sstevel@tonic-gate 		mp->start = lp;
25157c478bd9Sstevel@tonic-gate 	} else {
25167c478bd9Sstevel@tonic-gate 		mp->end->next = lp;
25178c1b6884Sszhou 		lp->prev = mp->end;
25187c478bd9Sstevel@tonic-gate 	}
25197c478bd9Sstevel@tonic-gate 	mp->end = lp;
25207c478bd9Sstevel@tonic-gate }
25217c478bd9Sstevel@tonic-gate 
25228c1b6884Sszhou static void
25238c1b6884Sszhou unlink_line(menu_t *mp, line_t *lp)
25248c1b6884Sszhou {
25258c1b6884Sszhou 	/* unlink from list */
25268c1b6884Sszhou 	if (lp->prev)
25278c1b6884Sszhou 		lp->prev->next = lp->next;
25288c1b6884Sszhou 	else
25298c1b6884Sszhou 		mp->start = lp->next;
25308c1b6884Sszhou 	if (lp->next)
25318c1b6884Sszhou 		lp->next->prev = lp->prev;
25328c1b6884Sszhou 	else
25338c1b6884Sszhou 		mp->end = lp->prev;
25348c1b6884Sszhou }
25358c1b6884Sszhou 
25368c1b6884Sszhou static entry_t *
25378c1b6884Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
25388c1b6884Sszhou {
25398c1b6884Sszhou 	entry_t *ent, *prev;
25408c1b6884Sszhou 
25418c1b6884Sszhou 	ent = s_calloc(1, sizeof (entry_t));
25428c1b6884Sszhou 	ent->start = start;
25438c1b6884Sszhou 	ent->end = end;
25448c1b6884Sszhou 
25458c1b6884Sszhou 	if (mp->entries == NULL) {
25468c1b6884Sszhou 		mp->entries = ent;
25478c1b6884Sszhou 		return (ent);
25488c1b6884Sszhou 	}
25498c1b6884Sszhou 
25508c1b6884Sszhou 	prev = mp->entries;
25518c1b6884Sszhou 	while (prev->next)
25528c1b6884Sszhou 		prev = prev-> next;
25538c1b6884Sszhou 	prev->next = ent;
25548c1b6884Sszhou 	ent->prev = prev;
25558c1b6884Sszhou 	return (ent);
25568c1b6884Sszhou }
25578c1b6884Sszhou 
25588c1b6884Sszhou static void
25598c1b6884Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
25608c1b6884Sszhou {
25618c1b6884Sszhou 	if (ent)
25628c1b6884Sszhou 		ent->end = lp;
25638c1b6884Sszhou }
25648c1b6884Sszhou 
25657c478bd9Sstevel@tonic-gate /*
2566843e1988Sjohnlev  * Check whether cmd matches the one indexed by which, and whether arg matches
2567843e1988Sjohnlev  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
2568843e1988Sjohnlev  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
2569843e1988Sjohnlev  * strstr(), so it can be a partial match.
2570843e1988Sjohnlev  */
2571843e1988Sjohnlev static int
2572843e1988Sjohnlev check_cmd(const char *cmd, const int which, const char *arg, const char *str)
2573843e1988Sjohnlev {
2574843e1988Sjohnlev 	if ((strcmp(cmd, menu_cmds[which]) != 0) &&
2575843e1988Sjohnlev 	    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
2576843e1988Sjohnlev 		return (0);
2577843e1988Sjohnlev 	}
2578843e1988Sjohnlev 	return (strstr(arg, str) != NULL);
2579843e1988Sjohnlev }
2580843e1988Sjohnlev 
2581843e1988Sjohnlev /*
25827c478bd9Sstevel@tonic-gate  * A line in menu.lst looks like
25837c478bd9Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
25847c478bd9Sstevel@tonic-gate  */
25857c478bd9Sstevel@tonic-gate static void
25867c478bd9Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
25877c478bd9Sstevel@tonic-gate {
25887c478bd9Sstevel@tonic-gate 	/*
25897c478bd9Sstevel@tonic-gate 	 * save state across calls. This is so that
25907c478bd9Sstevel@tonic-gate 	 * header gets the right entry# after title has
25917c478bd9Sstevel@tonic-gate 	 * been processed
25927c478bd9Sstevel@tonic-gate 	 */
25938c1b6884Sszhou 	static line_t *prev = NULL;
25948c1b6884Sszhou 	static entry_t *curr_ent = NULL;
2595ae115bc7Smrj 	static int in_liveupgrade = 0;
25967c478bd9Sstevel@tonic-gate 
25977c478bd9Sstevel@tonic-gate 	line_t	*lp;
25987c478bd9Sstevel@tonic-gate 	char *cmd, *sep, *arg;
25997c478bd9Sstevel@tonic-gate 	char save, *cp, *line;
26007c478bd9Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
26017c478bd9Sstevel@tonic-gate 
26027c478bd9Sstevel@tonic-gate 	if (str == NULL) {
26037c478bd9Sstevel@tonic-gate 		return;
26047c478bd9Sstevel@tonic-gate 	}
26057c478bd9Sstevel@tonic-gate 
26067c478bd9Sstevel@tonic-gate 	/*
26077c478bd9Sstevel@tonic-gate 	 * First save a copy of the entire line.
26087c478bd9Sstevel@tonic-gate 	 * We use this later to set the line field.
26097c478bd9Sstevel@tonic-gate 	 */
26107c478bd9Sstevel@tonic-gate 	line = s_strdup(str);
26117c478bd9Sstevel@tonic-gate 
26127c478bd9Sstevel@tonic-gate 	/* Eat up leading whitespace */
26137c478bd9Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
26147c478bd9Sstevel@tonic-gate 		str++;
26157c478bd9Sstevel@tonic-gate 
26167c478bd9Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
26177c478bd9Sstevel@tonic-gate 		cmd = s_strdup("#");
26187c478bd9Sstevel@tonic-gate 		sep = NULL;
26197c478bd9Sstevel@tonic-gate 		arg = s_strdup(str + 1);
26207c478bd9Sstevel@tonic-gate 		flag = BAM_COMMENT;
2621ae115bc7Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
2622ae115bc7Smrj 			in_liveupgrade = 1;
2623ae115bc7Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
2624ae115bc7Smrj 			in_liveupgrade = 0;
2625ae115bc7Smrj 		}
26267c478bd9Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
26277c478bd9Sstevel@tonic-gate 		cmd = sep = arg = NULL;
26287c478bd9Sstevel@tonic-gate 		flag = BAM_EMPTY;
26297c478bd9Sstevel@tonic-gate 	} else {
26307c478bd9Sstevel@tonic-gate 		/*
26317c478bd9Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
26327c478bd9Sstevel@tonic-gate 		 * However various development bits use '=' as a
26337c478bd9Sstevel@tonic-gate 		 * separator. In addition, external users also
26347c478bd9Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
26357c478bd9Sstevel@tonic-gate 		 */
26367c478bd9Sstevel@tonic-gate 		cp = str;
26377c478bd9Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
26387c478bd9Sstevel@tonic-gate 			if (*str == '\0') {
26397c478bd9Sstevel@tonic-gate 				cmd = s_strdup(cp);
26407c478bd9Sstevel@tonic-gate 				sep = arg = NULL;
26417c478bd9Sstevel@tonic-gate 				break;
26427c478bd9Sstevel@tonic-gate 			}
26437c478bd9Sstevel@tonic-gate 			str++;
26447c478bd9Sstevel@tonic-gate 		}
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 		if (*str != '\0') {
26477c478bd9Sstevel@tonic-gate 			save = *str;
26487c478bd9Sstevel@tonic-gate 			*str = '\0';
26497c478bd9Sstevel@tonic-gate 			cmd = s_strdup(cp);
26507c478bd9Sstevel@tonic-gate 			*str = save;
26517c478bd9Sstevel@tonic-gate 
26527c478bd9Sstevel@tonic-gate 			str++;
26537c478bd9Sstevel@tonic-gate 			save = *str;
26547c478bd9Sstevel@tonic-gate 			*str = '\0';
26557c478bd9Sstevel@tonic-gate 			sep = s_strdup(str - 1);
26567c478bd9Sstevel@tonic-gate 			*str = save;
26577c478bd9Sstevel@tonic-gate 
26587c478bd9Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
26597c478bd9Sstevel@tonic-gate 				str++;
26607c478bd9Sstevel@tonic-gate 			if (*str == '\0')
26617c478bd9Sstevel@tonic-gate 				arg = NULL;
26627c478bd9Sstevel@tonic-gate 			else
26637c478bd9Sstevel@tonic-gate 				arg = s_strdup(str);
26647c478bd9Sstevel@tonic-gate 		}
26657c478bd9Sstevel@tonic-gate 	}
26667c478bd9Sstevel@tonic-gate 
26677c478bd9Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
26687c478bd9Sstevel@tonic-gate 
26697c478bd9Sstevel@tonic-gate 	lp->cmd = cmd;
26707c478bd9Sstevel@tonic-gate 	lp->sep = sep;
26717c478bd9Sstevel@tonic-gate 	lp->arg = arg;
26727c478bd9Sstevel@tonic-gate 	lp->line = line;
26737c478bd9Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
26747c478bd9Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
26757c478bd9Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
26767c478bd9Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
26777c478bd9Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
2678ae115bc7Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
26797c478bd9Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
26808c1b6884Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
2681ae115bc7Smrj 			curr_ent->flags = BAM_ENTRY_BOOTADM;
26828c1b6884Sszhou 		} else {
26838c1b6884Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
2684ae115bc7Smrj 			if (in_liveupgrade) {
2685ae115bc7Smrj 				curr_ent->flags = BAM_ENTRY_LU;
26868c1b6884Sszhou 			}
2687ae115bc7Smrj 		}
2688ae115bc7Smrj 		curr_ent->entryNum = *entryNum;
26897c478bd9Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
26907c478bd9Sstevel@tonic-gate 		/*
26917c478bd9Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
26927c478bd9Sstevel@tonic-gate 		 * by the subsequent title
26937c478bd9Sstevel@tonic-gate 		 */
26947c478bd9Sstevel@tonic-gate 		lp->entryNum = *entryNum;
26957c478bd9Sstevel@tonic-gate 		lp->flags = flag;
26967c478bd9Sstevel@tonic-gate 	} else {
26977c478bd9Sstevel@tonic-gate 		lp->entryNum = *entryNum;
2698ae115bc7Smrj 
2699ae115bc7Smrj 		if (*entryNum == ENTRY_INIT) {
2700ae115bc7Smrj 			lp->flags = BAM_GLOBAL;
2701ae115bc7Smrj 		} else {
2702ae115bc7Smrj 			lp->flags = BAM_ENTRY;
2703ae115bc7Smrj 
2704ae115bc7Smrj 			if (cmd && arg) {
2705ae115bc7Smrj 				/*
2706ae115bc7Smrj 				 * We only compare for the length of "module"
2707ae115bc7Smrj 				 * so that "module$" will also match.
2708ae115bc7Smrj 				 */
2709843e1988Sjohnlev 				if (check_cmd(cmd, MODULE_CMD, arg, MINIROOT))
2710ae115bc7Smrj 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
2711843e1988Sjohnlev 				else if (check_cmd(cmd, KERNEL_CMD, arg,
2712843e1988Sjohnlev 				    "xen.gz"))
2713843e1988Sjohnlev 					curr_ent->flags |= BAM_ENTRY_HV;
2714ae115bc7Smrj 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
2715ae115bc7Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
2716ae115bc7Smrj 				else if (strcmp(cmd,
2717ae115bc7Smrj 				    menu_cmds[CHAINLOADER_CMD]) == 0)
2718ae115bc7Smrj 					curr_ent->flags |=
2719ae115bc7Smrj 					    BAM_ENTRY_CHAINLOADER;
2720ae115bc7Smrj 			}
2721ae115bc7Smrj 		}
27227c478bd9Sstevel@tonic-gate 	}
27237c478bd9Sstevel@tonic-gate 
27248c1b6884Sszhou 	/* record default, old default, and entry line ranges */
27258c1b6884Sszhou 	if (lp->flags == BAM_GLOBAL &&
27268c1b6884Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
27278c1b6884Sszhou 		mp->curdefault = lp;
27288c1b6884Sszhou 	} else if (lp->flags == BAM_COMMENT &&
27298c1b6884Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
27308c1b6884Sszhou 		mp->olddefault = lp;
2731ae115bc7Smrj 	} else if (lp->flags == BAM_COMMENT &&
2732ae115bc7Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
2733ae115bc7Smrj 		mp->old_rc_default = lp;
27348c1b6884Sszhou 	} else if (lp->flags == BAM_ENTRY ||
2735ae115bc7Smrj 	    (lp->flags == BAM_COMMENT &&
2736ae115bc7Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
27378c1b6884Sszhou 		boot_entry_addline(curr_ent, lp);
27388c1b6884Sszhou 	}
27397c478bd9Sstevel@tonic-gate 	append_line(mp, lp);
27407c478bd9Sstevel@tonic-gate 
27417c478bd9Sstevel@tonic-gate 	prev = lp;
27427c478bd9Sstevel@tonic-gate }
27437c478bd9Sstevel@tonic-gate 
274440541d5dSvikram static void
274540541d5dSvikram update_numbering(menu_t *mp)
274640541d5dSvikram {
274740541d5dSvikram 	int lineNum;
274840541d5dSvikram 	int entryNum;
274940541d5dSvikram 	int old_default_value;
275040541d5dSvikram 	line_t *lp, *prev, *default_lp, *default_entry;
275140541d5dSvikram 	char buf[PATH_MAX];
275240541d5dSvikram 
275340541d5dSvikram 	if (mp->start == NULL) {
275440541d5dSvikram 		return;
275540541d5dSvikram 	}
275640541d5dSvikram 
275740541d5dSvikram 	lineNum = LINE_INIT;
275840541d5dSvikram 	entryNum = ENTRY_INIT;
275940541d5dSvikram 	old_default_value = ENTRY_INIT;
276040541d5dSvikram 	lp = default_lp = default_entry = NULL;
276140541d5dSvikram 
276240541d5dSvikram 	prev = NULL;
276340541d5dSvikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
276440541d5dSvikram 		lp->lineNum = ++lineNum;
276540541d5dSvikram 
276640541d5dSvikram 		/*
276740541d5dSvikram 		 * Get the value of the default command
276840541d5dSvikram 		 */
276940541d5dSvikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
277040541d5dSvikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
277140541d5dSvikram 		    lp->arg) {
277240541d5dSvikram 			old_default_value = atoi(lp->arg);
277340541d5dSvikram 			default_lp = lp;
277440541d5dSvikram 		}
277540541d5dSvikram 
277640541d5dSvikram 		/*
277740541d5dSvikram 		 * If not boot entry, nothing else to fix for this
277840541d5dSvikram 		 * entry
277940541d5dSvikram 		 */
278040541d5dSvikram 		if (lp->entryNum == ENTRY_INIT)
278140541d5dSvikram 			continue;
278240541d5dSvikram 
278340541d5dSvikram 		/*
278440541d5dSvikram 		 * Record the position of the default entry.
278540541d5dSvikram 		 * The following works because global
278640541d5dSvikram 		 * commands like default and timeout should precede
278740541d5dSvikram 		 * actual boot entries, so old_default_value
278840541d5dSvikram 		 * is already known (or default cmd is missing).
278940541d5dSvikram 		 */
279040541d5dSvikram 		if (default_entry == NULL &&
279140541d5dSvikram 		    old_default_value != ENTRY_INIT &&
279240541d5dSvikram 		    lp->entryNum == old_default_value) {
279340541d5dSvikram 			default_entry = lp;
279440541d5dSvikram 		}
279540541d5dSvikram 
279640541d5dSvikram 		/*
279740541d5dSvikram 		 * Now fixup the entry number
279840541d5dSvikram 		 */
279940541d5dSvikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
280040541d5dSvikram 			lp->entryNum = ++entryNum;
280140541d5dSvikram 			/* fixup the bootadm header */
280240541d5dSvikram 			if (prev && prev->flags == BAM_COMMENT &&
2803ae115bc7Smrj 			    prev->arg &&
2804ae115bc7Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
280540541d5dSvikram 				prev->entryNum = lp->entryNum;
280640541d5dSvikram 			}
280740541d5dSvikram 		} else {
280840541d5dSvikram 			lp->entryNum = entryNum;
280940541d5dSvikram 		}
281040541d5dSvikram 	}
281140541d5dSvikram 
281240541d5dSvikram 	/*
281340541d5dSvikram 	 * No default command in menu, simply return
281440541d5dSvikram 	 */
281540541d5dSvikram 	if (default_lp == NULL) {
281640541d5dSvikram 		return;
281740541d5dSvikram 	}
281840541d5dSvikram 
281940541d5dSvikram 	free(default_lp->arg);
282040541d5dSvikram 	free(default_lp->line);
282140541d5dSvikram 
282240541d5dSvikram 	if (default_entry == NULL) {
282340541d5dSvikram 		default_lp->arg = s_strdup("0");
282440541d5dSvikram 	} else {
282540541d5dSvikram 		(void) snprintf(buf, sizeof (buf), "%d",
282640541d5dSvikram 		    default_entry->entryNum);
282740541d5dSvikram 		default_lp->arg = s_strdup(buf);
282840541d5dSvikram 	}
282940541d5dSvikram 
283040541d5dSvikram 	/*
283140541d5dSvikram 	 * The following is required since only the line field gets
283240541d5dSvikram 	 * written back to menu.lst
283340541d5dSvikram 	 */
283440541d5dSvikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
283540541d5dSvikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
283640541d5dSvikram 	default_lp->line = s_strdup(buf);
283740541d5dSvikram }
283840541d5dSvikram 
283940541d5dSvikram 
28407c478bd9Sstevel@tonic-gate static menu_t *
28417c478bd9Sstevel@tonic-gate menu_read(char *menu_path)
28427c478bd9Sstevel@tonic-gate {
28437c478bd9Sstevel@tonic-gate 	FILE *fp;
28447c478bd9Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
28457c478bd9Sstevel@tonic-gate 	menu_t *mp;
28467c478bd9Sstevel@tonic-gate 	int line, entry, len, n;
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
28497c478bd9Sstevel@tonic-gate 
28507c478bd9Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
28517c478bd9Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
28527c478bd9Sstevel@tonic-gate 		return (mp);
28537c478bd9Sstevel@tonic-gate 	}
28547c478bd9Sstevel@tonic-gate 
28557c478bd9Sstevel@tonic-gate 
28567c478bd9Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
28577c478bd9Sstevel@tonic-gate 	line = LINE_INIT;
28587c478bd9Sstevel@tonic-gate 	entry = ENTRY_INIT;
28597c478bd9Sstevel@tonic-gate 	cp = buf;
28607c478bd9Sstevel@tonic-gate 	len = sizeof (buf);
28617c478bd9Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
28627c478bd9Sstevel@tonic-gate 		n = strlen(cp);
28637c478bd9Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
28647c478bd9Sstevel@tonic-gate 			len -= n - 1;
28657c478bd9Sstevel@tonic-gate 			assert(len >= 2);
28667c478bd9Sstevel@tonic-gate 			cp += n - 1;
28677c478bd9Sstevel@tonic-gate 			continue;
28687c478bd9Sstevel@tonic-gate 		}
28697c478bd9Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
28707c478bd9Sstevel@tonic-gate 		cp = buf;
28717c478bd9Sstevel@tonic-gate 		len = sizeof (buf);
28727c478bd9Sstevel@tonic-gate 	}
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
28757c478bd9Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
28767c478bd9Sstevel@tonic-gate 	}
28777c478bd9Sstevel@tonic-gate 
28787c478bd9Sstevel@tonic-gate 	return (mp);
28797c478bd9Sstevel@tonic-gate }
28807c478bd9Sstevel@tonic-gate 
28817c478bd9Sstevel@tonic-gate static error_t
28827c478bd9Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
28837c478bd9Sstevel@tonic-gate {
28847c478bd9Sstevel@tonic-gate 	char *eq;
28857c478bd9Sstevel@tonic-gate 	char *opt_dup;
28867c478bd9Sstevel@tonic-gate 	int entryNum;
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate 	assert(mp);
28897c478bd9Sstevel@tonic-gate 	assert(mp->start);
28907c478bd9Sstevel@tonic-gate 	assert(opt);
28917c478bd9Sstevel@tonic-gate 
28927c478bd9Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
28937c478bd9Sstevel@tonic-gate 
28947c478bd9Sstevel@tonic-gate 	if (entry)
28957c478bd9Sstevel@tonic-gate 		*entry = ENTRY_INIT;
28967c478bd9Sstevel@tonic-gate 	if (title)
28977c478bd9Sstevel@tonic-gate 		*title = NULL;
28987c478bd9Sstevel@tonic-gate 
28997c478bd9Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
29007c478bd9Sstevel@tonic-gate 	if (eq == NULL) {
29017c478bd9Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
29027c478bd9Sstevel@tonic-gate 		free(opt_dup);
29037c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
29047c478bd9Sstevel@tonic-gate 	}
29057c478bd9Sstevel@tonic-gate 
29067c478bd9Sstevel@tonic-gate 	*eq = '\0';
29077c478bd9Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
29087c478bd9Sstevel@tonic-gate 		assert(mp->end);
29097c478bd9Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
29107c478bd9Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
29117c478bd9Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
29127c478bd9Sstevel@tonic-gate 			free(opt_dup);
29137c478bd9Sstevel@tonic-gate 			return (BAM_ERROR);
29147c478bd9Sstevel@tonic-gate 		}
29157c478bd9Sstevel@tonic-gate 		*entry = entryNum;
29167c478bd9Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
29177c478bd9Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
29187c478bd9Sstevel@tonic-gate 	} else {
29197c478bd9Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
29207c478bd9Sstevel@tonic-gate 		free(opt_dup);
29217c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
29227c478bd9Sstevel@tonic-gate 	}
29237c478bd9Sstevel@tonic-gate 
29247c478bd9Sstevel@tonic-gate 	free(opt_dup);
29257c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
29267c478bd9Sstevel@tonic-gate }
29277c478bd9Sstevel@tonic-gate 
29287c478bd9Sstevel@tonic-gate /*
29297c478bd9Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
29307c478bd9Sstevel@tonic-gate  * only title lines in file are printed.
29317c478bd9Sstevel@tonic-gate  *
29327c478bd9Sstevel@tonic-gate  * If invoked with a title or entry #, all
29337c478bd9Sstevel@tonic-gate  * lines in *every* matching entry are listed
29347c478bd9Sstevel@tonic-gate  */
29357c478bd9Sstevel@tonic-gate static error_t
29367c478bd9Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
29377c478bd9Sstevel@tonic-gate {
29387c478bd9Sstevel@tonic-gate 	line_t *lp;
29397c478bd9Sstevel@tonic-gate 	int entry = ENTRY_INIT;
29407c478bd9Sstevel@tonic-gate 	int found;
29417c478bd9Sstevel@tonic-gate 	char *title = NULL;
29427c478bd9Sstevel@tonic-gate 
29437c478bd9Sstevel@tonic-gate 	assert(mp);
29447c478bd9Sstevel@tonic-gate 	assert(menu_path);
29457c478bd9Sstevel@tonic-gate 
29467c478bd9Sstevel@tonic-gate 	if (mp->start == NULL) {
29477c478bd9Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
29487c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
29497c478bd9Sstevel@tonic-gate 	}
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate 	if (opt != NULL) {
29527c478bd9Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
29537c478bd9Sstevel@tonic-gate 			return (BAM_ERROR);
29547c478bd9Sstevel@tonic-gate 		}
29557c478bd9Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
29567c478bd9Sstevel@tonic-gate 	} else {
29577c478bd9Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
29587c478bd9Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
29597c478bd9Sstevel@tonic-gate 	}
29607c478bd9Sstevel@tonic-gate 
29617c478bd9Sstevel@tonic-gate 	found = 0;
29627c478bd9Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
29637c478bd9Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
29647c478bd9Sstevel@tonic-gate 			continue;
29657c478bd9Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
29667c478bd9Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
29677c478bd9Sstevel@tonic-gate 			    lp->arg);
29687c478bd9Sstevel@tonic-gate 			found = 1;
29697c478bd9Sstevel@tonic-gate 			continue;
29707c478bd9Sstevel@tonic-gate 		}
29717c478bd9Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
29727c478bd9Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
29737c478bd9Sstevel@tonic-gate 			found = 1;
29747c478bd9Sstevel@tonic-gate 			continue;
29757c478bd9Sstevel@tonic-gate 		}
29767c478bd9Sstevel@tonic-gate 
29777c478bd9Sstevel@tonic-gate 		/*
29787c478bd9Sstevel@tonic-gate 		 * We set the entry value here so that all lines
29797c478bd9Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
29807c478bd9Sstevel@tonic-gate 		 * title in other entries, all lines in those
29817c478bd9Sstevel@tonic-gate 		 * entries get printed as well.
29827c478bd9Sstevel@tonic-gate 		 */
29837c478bd9Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
29847c478bd9Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
29857c478bd9Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
29867c478bd9Sstevel@tonic-gate 			entry = lp->entryNum;
29877c478bd9Sstevel@tonic-gate 			found = 1;
29887c478bd9Sstevel@tonic-gate 			continue;
29897c478bd9Sstevel@tonic-gate 		}
29907c478bd9Sstevel@tonic-gate 	}
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 	if (!found) {
29937c478bd9Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
29947c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
29957c478bd9Sstevel@tonic-gate 	}
29967c478bd9Sstevel@tonic-gate 
29977c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
29987c478bd9Sstevel@tonic-gate }
29997c478bd9Sstevel@tonic-gate 
3000843e1988Sjohnlev int
30017c478bd9Sstevel@tonic-gate add_boot_entry(menu_t *mp,
30027c478bd9Sstevel@tonic-gate 	char *title,
30037c478bd9Sstevel@tonic-gate 	char *root,
30047c478bd9Sstevel@tonic-gate 	char *kernel,
3005843e1988Sjohnlev 	char *mod_kernel,
30067c478bd9Sstevel@tonic-gate 	char *module)
30077c478bd9Sstevel@tonic-gate {
30087c478bd9Sstevel@tonic-gate 	int lineNum, entryNum;
30097c478bd9Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
3010ae115bc7Smrj 	menu_cmd_t k_cmd, m_cmd;
30117c478bd9Sstevel@tonic-gate 
30127c478bd9Sstevel@tonic-gate 	assert(mp);
30137c478bd9Sstevel@tonic-gate 
30147c478bd9Sstevel@tonic-gate 	if (title == NULL) {
3015ee3b8144Sszhou 		title = "Solaris";	/* default to Solaris */
30167c478bd9Sstevel@tonic-gate 	}
30177c478bd9Sstevel@tonic-gate 	if (kernel == NULL) {
30187c478bd9Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
30197c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
30207c478bd9Sstevel@tonic-gate 	}
30217c478bd9Sstevel@tonic-gate 	if (module == NULL) {
3022ae115bc7Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
30237c478bd9Sstevel@tonic-gate 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
30247c478bd9Sstevel@tonic-gate 			return (BAM_ERROR);
30257c478bd9Sstevel@tonic-gate 		}
30267c478bd9Sstevel@tonic-gate 
3027ae115bc7Smrj 		/* Figure the commands out from the kernel line */
3028ae115bc7Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
3029ae115bc7Smrj 			module = DIRECT_BOOT_ARCHIVE;
3030ae115bc7Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
3031ae115bc7Smrj 			m_cmd = MODULE_DOLLAR_CMD;
3032ae115bc7Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
3033ae115bc7Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
3034ae115bc7Smrj 			k_cmd = KERNEL_CMD;
3035ae115bc7Smrj 			m_cmd = MODULE_CMD;
3036ae115bc7Smrj 		} else {
3037ae115bc7Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
3038ae115bc7Smrj 			k_cmd = KERNEL_CMD;
3039ae115bc7Smrj 			m_cmd = MODULE_CMD;
3040ae115bc7Smrj 		}
3041ae115bc7Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
3042ae115bc7Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
3043ae115bc7Smrj 		/*
3044ae115bc7Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
3045ae115bc7Smrj 		 * command.  Otherwise, use "kernel".
3046ae115bc7Smrj 		 */
3047ae115bc7Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
3048ae115bc7Smrj 		m_cmd = MODULE_DOLLAR_CMD;
3049ae115bc7Smrj 	} else {
3050ae115bc7Smrj 		k_cmd = KERNEL_CMD;
3051ae115bc7Smrj 		m_cmd = MODULE_CMD;
3052ae115bc7Smrj 	}
3053ae115bc7Smrj 
30547c478bd9Sstevel@tonic-gate 	if (mp->start) {
30557c478bd9Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
30567c478bd9Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
30577c478bd9Sstevel@tonic-gate 	} else {
30587c478bd9Sstevel@tonic-gate 		lineNum = LINE_INIT;
30597c478bd9Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
30607c478bd9Sstevel@tonic-gate 	}
30617c478bd9Sstevel@tonic-gate 
30627c478bd9Sstevel@tonic-gate 	/*
30637c478bd9Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
30647c478bd9Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
30657c478bd9Sstevel@tonic-gate 	 */
30667c478bd9Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
3067ae115bc7Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
30688c1b6884Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
30697c478bd9Sstevel@tonic-gate 
30707c478bd9Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
30717c478bd9Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
30728c1b6884Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
30737c478bd9Sstevel@tonic-gate 
30748c1b6884Sszhou 	if (root) {
30757c478bd9Sstevel@tonic-gate 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
30767c478bd9Sstevel@tonic-gate 		    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
30778c1b6884Sszhou 		line_parser(mp, linebuf, &lineNum, &entryNum);
30787c478bd9Sstevel@tonic-gate 	}
30797c478bd9Sstevel@tonic-gate 
30807c478bd9Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3081ae115bc7Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
30828c1b6884Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
30837c478bd9Sstevel@tonic-gate 
3084843e1988Sjohnlev 	if (mod_kernel != NULL) {
3085843e1988Sjohnlev 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3086843e1988Sjohnlev 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
3087843e1988Sjohnlev 		line_parser(mp, linebuf, &lineNum, &entryNum);
3088843e1988Sjohnlev 	}
3089843e1988Sjohnlev 
30907c478bd9Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3091ae115bc7Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
30928c1b6884Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
30937c478bd9Sstevel@tonic-gate 
30947c478bd9Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
3095ae115bc7Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
30968c1b6884Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
30977c478bd9Sstevel@tonic-gate 
30987c478bd9Sstevel@tonic-gate 	return (entryNum);
30997c478bd9Sstevel@tonic-gate }
31007c478bd9Sstevel@tonic-gate 
31017c478bd9Sstevel@tonic-gate static error_t
31027c478bd9Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
31037c478bd9Sstevel@tonic-gate {
31048c1b6884Sszhou 	line_t *lp, *freed;
31058c1b6884Sszhou 	entry_t *ent, *tmp;
31067c478bd9Sstevel@tonic-gate 	int deleted;
31077c478bd9Sstevel@tonic-gate 
31087c478bd9Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
31097c478bd9Sstevel@tonic-gate 
31108c1b6884Sszhou 	ent = mp->entries;
31118c1b6884Sszhou 	while (ent) {
31128c1b6884Sszhou 		lp = ent->start;
31138c1b6884Sszhou 		/* check entry number and make sure it's a bootadm entry */
31148c1b6884Sszhou 		if (lp->flags != BAM_COMMENT ||
3115ae115bc7Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
31168c1b6884Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
31178c1b6884Sszhou 			ent = ent->next;
31187c478bd9Sstevel@tonic-gate 			continue;
31197c478bd9Sstevel@tonic-gate 		}
31207c478bd9Sstevel@tonic-gate 
31218c1b6884Sszhou 		/* free the entry content */
31228c1b6884Sszhou 		do {
31238c1b6884Sszhou 			freed = lp;
31248c1b6884Sszhou 			lp = lp->next;	/* prev stays the same */
31258c1b6884Sszhou 			unlink_line(mp, freed);
31268c1b6884Sszhou 			line_free(freed);
31278c1b6884Sszhou 		} while (freed != ent->end);
31287c478bd9Sstevel@tonic-gate 
31298c1b6884Sszhou 		/* free the entry_t structure */
31308c1b6884Sszhou 		tmp = ent;
31318c1b6884Sszhou 		ent = ent->next;
31328c1b6884Sszhou 		if (tmp->prev)
31338c1b6884Sszhou 			tmp->prev->next = ent;
31347c478bd9Sstevel@tonic-gate 		else
31358c1b6884Sszhou 			mp->entries = ent;
31368c1b6884Sszhou 		if (ent)
31378c1b6884Sszhou 			ent->prev = tmp->prev;
31387c478bd9Sstevel@tonic-gate 		deleted = 1;
31397c478bd9Sstevel@tonic-gate 	}
31407c478bd9Sstevel@tonic-gate 
31417c478bd9Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
31427c478bd9Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
31437c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
31447c478bd9Sstevel@tonic-gate 	}
31457c478bd9Sstevel@tonic-gate 
314640541d5dSvikram 	/*
314740541d5dSvikram 	 * Now that we have deleted an entry, update
314840541d5dSvikram 	 * the entry numbering and the default cmd.
314940541d5dSvikram 	 */
315040541d5dSvikram 	update_numbering(mp);
315140541d5dSvikram 
31527c478bd9Sstevel@tonic-gate 	return (BAM_SUCCESS);
31537c478bd9Sstevel@tonic-gate }
31547c478bd9Sstevel@tonic-gate 
31557c478bd9Sstevel@tonic-gate static error_t
31567c478bd9Sstevel@tonic-gate delete_all_entries(menu_t *mp, char *menu_path, char *opt)
31577c478bd9Sstevel@tonic-gate {
31587c478bd9Sstevel@tonic-gate 	assert(mp);
31597c478bd9Sstevel@tonic-gate 	assert(opt == NULL);
31607c478bd9Sstevel@tonic-gate 
31617c478bd9Sstevel@tonic-gate 	if (mp->start == NULL) {
31627c478bd9Sstevel@tonic-gate 		bam_print(EMPTY_FILE, menu_path);
31637c478bd9Sstevel@tonic-gate 		return (BAM_SUCCESS);
31647c478bd9Sstevel@tonic-gate 	}
31657c478bd9Sstevel@tonic-gate 
31667c478bd9Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
31677c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
31687c478bd9Sstevel@tonic-gate 	}
31697c478bd9Sstevel@tonic-gate 
31707c478bd9Sstevel@tonic-gate 	return (BAM_WRITE);
31717c478bd9Sstevel@tonic-gate }
31727c478bd9Sstevel@tonic-gate 
31737c478bd9Sstevel@tonic-gate static FILE *
31748c1b6884Sszhou open_diskmap(char *root)
31757c478bd9Sstevel@tonic-gate {
31767c478bd9Sstevel@tonic-gate 	FILE *fp;
31777c478bd9Sstevel@tonic-gate 	char cmd[PATH_MAX];
31787c478bd9Sstevel@tonic-gate 
31797c478bd9Sstevel@tonic-gate 	/* make sure we have a map file */
31807c478bd9Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
31817c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
31827c478bd9Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
3183986fd29aSsetje 		    "%s/%s > /dev/null", root, CREATE_DISKMAP);
31847c478bd9Sstevel@tonic-gate 		(void) system(cmd);
31857c478bd9Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
31867c478bd9Sstevel@tonic-gate 	}
31877c478bd9Sstevel@tonic-gate 	return (fp);
31887c478bd9Sstevel@tonic-gate }
31897c478bd9Sstevel@tonic-gate 
31907c478bd9Sstevel@tonic-gate #define	SECTOR_SIZE	512
31917c478bd9Sstevel@tonic-gate 
31927c478bd9Sstevel@tonic-gate static int
31937c478bd9Sstevel@tonic-gate get_partition(char *device)
31947c478bd9Sstevel@tonic-gate {
31957c478bd9Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
31967c478bd9Sstevel@tonic-gate 	struct mboot *mboot;
31977c478bd9Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
31987c478bd9Sstevel@tonic-gate 	char *wholedisk, *slice;
31997c478bd9Sstevel@tonic-gate 
32007c478bd9Sstevel@tonic-gate 	/* form whole disk (p0) */
32017c478bd9Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
32027c478bd9Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
32037c478bd9Sstevel@tonic-gate 	if (!is_pcfs)
32047c478bd9Sstevel@tonic-gate 		*slice = '\0';
32057c478bd9Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
32067c478bd9Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
32077c478bd9Sstevel@tonic-gate 	if (!is_pcfs)
32087c478bd9Sstevel@tonic-gate 		*slice = 's';
32097c478bd9Sstevel@tonic-gate 
32107c478bd9Sstevel@tonic-gate 	/* read boot sector */
32117c478bd9Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
32127c478bd9Sstevel@tonic-gate 	free(wholedisk);
32137c478bd9Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
32147c478bd9Sstevel@tonic-gate 		return (partno);
32157c478bd9Sstevel@tonic-gate 	}
32167c478bd9Sstevel@tonic-gate 	(void) close(fd);
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate 	/* parse fdisk table */
32197c478bd9Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
32207c478bd9Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
32217c478bd9Sstevel@tonic-gate 		struct ipart *part =
32227c478bd9Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
32237c478bd9Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
32247c478bd9Sstevel@tonic-gate 			if (part->systid == 0xbe) {
32257c478bd9Sstevel@tonic-gate 				partno = i;
32267c478bd9Sstevel@tonic-gate 				break;
32277c478bd9Sstevel@tonic-gate 			}
32287c478bd9Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
32297c478bd9Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
32307c478bd9Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
32317c478bd9Sstevel@tonic-gate 				partno = i;
32327c478bd9Sstevel@tonic-gate 				break;
32337c478bd9Sstevel@tonic-gate 			}
32347c478bd9Sstevel@tonic-gate 		}
32357c478bd9Sstevel@tonic-gate 	}
32367c478bd9Sstevel@tonic-gate 	return (partno);
32377c478bd9Sstevel@tonic-gate }
32387c478bd9Sstevel@tonic-gate 
32397c478bd9Sstevel@tonic-gate static char *
32407c478bd9Sstevel@tonic-gate get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
32417c478bd9Sstevel@tonic-gate {
32427c478bd9Sstevel@tonic-gate 	char *grubdisk;	/* (hd#,#,#) */
32437c478bd9Sstevel@tonic-gate 	char *slice;
32447c478bd9Sstevel@tonic-gate 	char *grubhd;
32457c478bd9Sstevel@tonic-gate 	int fdiskpart;
32467c478bd9Sstevel@tonic-gate 	int found = 0;
32477c478bd9Sstevel@tonic-gate 	char *devname, *ctdname = strstr(rootdev, "dsk/");
32487c478bd9Sstevel@tonic-gate 	char linebuf[PATH_MAX];
32497c478bd9Sstevel@tonic-gate 
32507c478bd9Sstevel@tonic-gate 	if (ctdname == NULL)
32517c478bd9Sstevel@tonic-gate 		return (NULL);
32527c478bd9Sstevel@tonic-gate 
32537c478bd9Sstevel@tonic-gate 	ctdname += strlen("dsk/");
32547c478bd9Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
32557c478bd9Sstevel@tonic-gate 	if (slice)
32567c478bd9Sstevel@tonic-gate 		*slice = '\0';
32577c478bd9Sstevel@tonic-gate 
32587c478bd9Sstevel@tonic-gate 	rewind(fp);
32597c478bd9Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
32607c478bd9Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
32617c478bd9Sstevel@tonic-gate 		if (grubhd)
32627c478bd9Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
32637c478bd9Sstevel@tonic-gate 		else
32647c478bd9Sstevel@tonic-gate 			devname = NULL;
32657c478bd9Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
32667c478bd9Sstevel@tonic-gate 			found = 1;
32677c478bd9Sstevel@tonic-gate 			break;
32687c478bd9Sstevel@tonic-gate 		}
32697c478bd9Sstevel@tonic-gate 	}
32707c478bd9Sstevel@tonic-gate 
32717c478bd9Sstevel@tonic-gate 	if (slice)
32727c478bd9Sstevel@tonic-gate 		*slice = 's';
32737c478bd9Sstevel@tonic-gate 
32747c478bd9Sstevel@tonic-gate 	if (found == 0) {
32757c478bd9Sstevel@tonic-gate 		if (bam_verbose)
32767c478bd9Sstevel@tonic-gate 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
32777c478bd9Sstevel@tonic-gate 		grubhd = "0";	/* assume disk 0 if can't match */
32787c478bd9Sstevel@tonic-gate 	}
32797c478bd9Sstevel@tonic-gate 
32807c478bd9Sstevel@tonic-gate 	fdiskpart = get_partition(rootdev);
32817c478bd9Sstevel@tonic-gate 	if (fdiskpart == -1)
32827c478bd9Sstevel@tonic-gate 		return (NULL);
32837c478bd9Sstevel@tonic-gate 
32847c478bd9Sstevel@tonic-gate 	grubdisk = s_calloc(1, 10);
32857c478bd9Sstevel@tonic-gate 	if (slice) {
32867c478bd9Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
32877c478bd9Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
32887c478bd9Sstevel@tonic-gate 	} else
32897c478bd9Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
32907c478bd9Sstevel@tonic-gate 		    grubhd, fdiskpart);
32917c478bd9Sstevel@tonic-gate 
32927c478bd9Sstevel@tonic-gate 	/* if root not on bootdev, change GRUB disk to 0 */
32937c478bd9Sstevel@tonic-gate 	if (!on_bootdev)
32947c478bd9Sstevel@tonic-gate 		grubdisk[3] = '0';
32957c478bd9Sstevel@tonic-gate 	return (grubdisk);
32967c478bd9Sstevel@tonic-gate }
32977c478bd9Sstevel@tonic-gate 
3298ee3b8144Sszhou static char *
3299ee3b8144Sszhou get_title(char *rootdir)
33007c478bd9Sstevel@tonic-gate {
33017c478bd9Sstevel@tonic-gate 	static char title[80];	/* from /etc/release */
33028c1b6884Sszhou 	char *cp = NULL, release[PATH_MAX];
33037c478bd9Sstevel@tonic-gate 	FILE *fp;
33047c478bd9Sstevel@tonic-gate 
33057c478bd9Sstevel@tonic-gate 	/* open the /etc/release file */
33067c478bd9Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
33077c478bd9Sstevel@tonic-gate 
33087c478bd9Sstevel@tonic-gate 	fp = fopen(release, "r");
33097c478bd9Sstevel@tonic-gate 	if (fp == NULL)
3310ee3b8144Sszhou 		return (NULL);
33117c478bd9Sstevel@tonic-gate 
33127c478bd9Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
33137c478bd9Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
33147c478bd9Sstevel@tonic-gate 		if (cp)
33157c478bd9Sstevel@tonic-gate 			break;
33167c478bd9Sstevel@tonic-gate 	}
33177c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
33188c1b6884Sszhou 	return (cp == NULL ? "Solaris" : cp);
33197c478bd9Sstevel@tonic-gate }
33207c478bd9Sstevel@tonic-gate 
3321ae115bc7Smrj char *
33227c478bd9Sstevel@tonic-gate get_special(char *mountp)
33237c478bd9Sstevel@tonic-gate {
33247c478bd9Sstevel@tonic-gate 	FILE *mntfp;
33257c478bd9Sstevel@tonic-gate 	struct mnttab mp = {0}, mpref = {0};
33267c478bd9Sstevel@tonic-gate 
33277c478bd9Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
33287c478bd9Sstevel@tonic-gate 	if (mntfp == NULL) {
33297c478bd9Sstevel@tonic-gate 		return (0);
33307c478bd9Sstevel@tonic-gate 	}
33317c478bd9Sstevel@tonic-gate 
33327c478bd9Sstevel@tonic-gate 	if (*mountp == '\0')
33337c478bd9Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
33347c478bd9Sstevel@tonic-gate 	else
33357c478bd9Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
33367c478bd9Sstevel@tonic-gate 	if (getmntany(mntfp, &mp, &mpref) != 0) {
33377c478bd9Sstevel@tonic-gate 		(void) fclose(mntfp);
33387c478bd9Sstevel@tonic-gate 		return (NULL);
33397c478bd9Sstevel@tonic-gate 	}
33407c478bd9Sstevel@tonic-gate 	(void) fclose(mntfp);
33417c478bd9Sstevel@tonic-gate 
33427c478bd9Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
33437c478bd9Sstevel@tonic-gate }
33447c478bd9Sstevel@tonic-gate 
3345ae115bc7Smrj char *
33467c478bd9Sstevel@tonic-gate os_to_grubdisk(char *osdisk, int on_bootdev)
33477c478bd9Sstevel@tonic-gate {
33487c478bd9Sstevel@tonic-gate 	FILE *fp;
33497c478bd9Sstevel@tonic-gate 	char *grubdisk;
33507c478bd9Sstevel@tonic-gate 
33517c478bd9Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
33528c1b6884Sszhou 	fp = open_diskmap("");
33537c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
33547c478bd9Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdisk);
33557c478bd9Sstevel@tonic-gate 		return (NULL);
33567c478bd9Sstevel@tonic-gate 	}
33577c478bd9Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
33587c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
33597c478bd9Sstevel@tonic-gate 	return (grubdisk);
33607c478bd9Sstevel@tonic-gate }
33617c478bd9Sstevel@tonic-gate 
33627c478bd9Sstevel@tonic-gate /*
33637c478bd9Sstevel@tonic-gate  * Check if root is on the boot device
33647c478bd9Sstevel@tonic-gate  * Return 0 (false) on error
33657c478bd9Sstevel@tonic-gate  */
33667c478bd9Sstevel@tonic-gate static int
33677c478bd9Sstevel@tonic-gate menu_on_bootdev(char *menu_root, FILE *fp)
33687c478bd9Sstevel@tonic-gate {
33697c478bd9Sstevel@tonic-gate 	int ret;
33707c478bd9Sstevel@tonic-gate 	char *grubhd, *bootp, *special;
33717c478bd9Sstevel@tonic-gate 
33727c478bd9Sstevel@tonic-gate 	special = get_special(menu_root);
33737c478bd9Sstevel@tonic-gate 	if (special == NULL)
33747c478bd9Sstevel@tonic-gate 		return (0);
33757c478bd9Sstevel@tonic-gate 	bootp = strstr(special, "p0:boot");
33767c478bd9Sstevel@tonic-gate 	if (bootp)
33777c478bd9Sstevel@tonic-gate 		*bootp = '\0';
33787c478bd9Sstevel@tonic-gate 	grubhd = get_grubdisk(special, fp, 1);
33797c478bd9Sstevel@tonic-gate 	free(special);
33807c478bd9Sstevel@tonic-gate 
33817c478bd9Sstevel@tonic-gate 	if (grubhd == NULL)
33827c478bd9Sstevel@tonic-gate 		return (0);
33837c478bd9Sstevel@tonic-gate 	ret = grubhd[3] == '0';
33847c478bd9Sstevel@tonic-gate 	free(grubhd);
33857c478bd9Sstevel@tonic-gate 	return (ret);
33867c478bd9Sstevel@tonic-gate }
33877c478bd9Sstevel@tonic-gate 
33888c1b6884Sszhou /*
33898c1b6884Sszhou  * look for matching bootadm entry with specified parameters
33908c1b6884Sszhou  * Here are the rules (based on existing usage):
33918c1b6884Sszhou  * - If title is specified, match on title only
3392843e1988Sjohnlev  * - Else, match on kernel, grubdisk and module.  Note that, if root_opt is
3393843e1988Sjohnlev  *   non-zero, the absence of root line is considered a match.
33948c1b6884Sszhou  */
33958c1b6884Sszhou static entry_t *
3396843e1988Sjohnlev find_boot_entry(menu_t *mp, char *title, char *kernel, char *root,
3397843e1988Sjohnlev     char *module, int root_opt, int *entry_num)
33988c1b6884Sszhou {
33998c1b6884Sszhou 	int i;
34008c1b6884Sszhou 	line_t *lp;
34018c1b6884Sszhou 	entry_t *ent;
34028c1b6884Sszhou 
34038c1b6884Sszhou 	/* find matching entry */
34048c1b6884Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
34058c1b6884Sszhou 		lp = ent->start;
34068c1b6884Sszhou 
34078c1b6884Sszhou 		/* first line of entry must be bootadm comment */
34088c1b6884Sszhou 		lp = ent->start;
3409ae115bc7Smrj 		if (lp->flags != BAM_COMMENT ||
3410ae115bc7Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
34118c1b6884Sszhou 			continue;
34128c1b6884Sszhou 		}
34138c1b6884Sszhou 
34148c1b6884Sszhou 		/* advance to title line */
34158c1b6884Sszhou 		lp = lp->next;
34168c1b6884Sszhou 		if (title) {
34178c1b6884Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
34188c1b6884Sszhou 			    strcmp(lp->arg, title) == 0)
34198c1b6884Sszhou 				break;
34208c1b6884Sszhou 			continue;	/* check title only */
34218c1b6884Sszhou 		}
34228c1b6884Sszhou 
34238c1b6884Sszhou 		lp = lp->next;	/* advance to root line */
3424843e1988Sjohnlev 		if (lp == NULL) {
3425843e1988Sjohnlev 			continue;
3426843e1988Sjohnlev 		} else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
34278c1b6884Sszhou 			/* root command found, match grub disk */
34288c1b6884Sszhou 			if (strcmp(lp->arg, root) != 0) {
34298c1b6884Sszhou 				continue;
34308c1b6884Sszhou 			}
34318c1b6884Sszhou 			lp = lp->next;	/* advance to kernel line */
34328c1b6884Sszhou 		} else {
34338c1b6884Sszhou 			/* no root command, see if root is optional */
34348c1b6884Sszhou 			if (root_opt == 0) {
34358c1b6884Sszhou 				continue;
34368c1b6884Sszhou 			}
34378c1b6884Sszhou 		}
34388c1b6884Sszhou 
34398c1b6884Sszhou 		if (lp == NULL || lp->next == NULL) {
34408c1b6884Sszhou 			continue;
34418c1b6884Sszhou 		}
34428c1b6884Sszhou 
3443843e1988Sjohnlev 		if (kernel &&
3444843e1988Sjohnlev 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
34458c1b6884Sszhou 			continue;
34468c1b6884Sszhou 		}
3447843e1988Sjohnlev 
3448843e1988Sjohnlev 		/*
3449843e1988Sjohnlev 		 * Check for matching module entry (failsafe or normal).
3450843e1988Sjohnlev 		 * If it fails to match, we go around the loop again.
3451843e1988Sjohnlev 		 * For xpv entries, there are two module lines, so we
3452843e1988Sjohnlev 		 * do the check twice.
3453843e1988Sjohnlev 		 */
3454843e1988Sjohnlev 		lp = lp->next;	/* advance to module line */
3455843e1988Sjohnlev 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
3456843e1988Sjohnlev 		    (((lp = lp->next) != NULL) &&
3457843e1988Sjohnlev 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
3458843e1988Sjohnlev 			/* match found */
3459843e1988Sjohnlev 			break;
3460843e1988Sjohnlev 		}
34618c1b6884Sszhou 	}
34628c1b6884Sszhou 
3463843e1988Sjohnlev 	if (entry_num && ent) {
34648c1b6884Sszhou 		*entry_num = i;
3465843e1988Sjohnlev 	}
34668c1b6884Sszhou 	return (ent);
34678c1b6884Sszhou }
34688c1b6884Sszhou 
34698c1b6884Sszhou static int
34708c1b6884Sszhou update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
3471843e1988Sjohnlev     char *mod_kernel, char *module, int root_opt)
34728c1b6884Sszhou {
3473ae115bc7Smrj 	int i, change_kernel = 0;
34748c1b6884Sszhou 	entry_t *ent;
34758c1b6884Sszhou 	line_t *lp;
34768c1b6884Sszhou 	char linebuf[BAM_MAXLINE];
34778c1b6884Sszhou 
34788c1b6884Sszhou 	/* note: don't match on title, it's updated on upgrade */
3479843e1988Sjohnlev 	ent = find_boot_entry(mp, NULL, kernel, root, module, root_opt, &i);
3480ae115bc7Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
3481ae115bc7Smrj 		/*
3482ae115bc7Smrj 		 * We may be upgrading a kernel from multiboot to
3483ae115bc7Smrj 		 * directboot.  Look for a multiboot entry.
3484ae115bc7Smrj 		 */
3485843e1988Sjohnlev 		ent = find_boot_entry(mp, NULL, "multiboot", root,
3486843e1988Sjohnlev 		    MULTI_BOOT_ARCHIVE, root_opt, NULL);
3487ae115bc7Smrj 		if (ent != NULL) {
3488ae115bc7Smrj 			change_kernel = 1;
3489ae115bc7Smrj 		}
3490ae115bc7Smrj 	}
34918c1b6884Sszhou 	if (ent == NULL)
34928c1b6884Sszhou 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
3493843e1988Sjohnlev 		    kernel, mod_kernel, module));
34948c1b6884Sszhou 
34958c1b6884Sszhou 	/* replace title of exiting entry and delete root line */
34968c1b6884Sszhou 	lp = ent->start;
34978c1b6884Sszhou 	lp = lp->next;	/* title line */
34988c1b6884Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
34998c1b6884Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
35008c1b6884Sszhou 	free(lp->arg);
35018c1b6884Sszhou 	free(lp->line);
35028c1b6884Sszhou 	lp->arg = s_strdup(title);
35038c1b6884Sszhou 	lp->line = s_strdup(linebuf);
35048c1b6884Sszhou 
35058c1b6884Sszhou 	lp = lp->next;	/* root line */
35068c1b6884Sszhou 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
35078c1b6884Sszhou 		if (root_opt) {		/* root line not needed */
35088c1b6884Sszhou 			line_t *tmp = lp;
35098c1b6884Sszhou 			lp = lp->next;
35108c1b6884Sszhou 			unlink_line(mp, tmp);
35118c1b6884Sszhou 			line_free(tmp);
35128c1b6884Sszhou 		} else
35138c1b6884Sszhou 			lp = lp->next;
35148c1b6884Sszhou 	}
3515ae115bc7Smrj 
3516ae115bc7Smrj 	if (change_kernel) {
3517ae115bc7Smrj 		/*
3518ae115bc7Smrj 		 * We're upgrading from multiboot to directboot.
3519ae115bc7Smrj 		 */
3520ae115bc7Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
3521ae115bc7Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3522ae115bc7Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
3523ae115bc7Smrj 			    kernel);
3524ae115bc7Smrj 			free(lp->arg);
3525ae115bc7Smrj 			free(lp->line);
3526ae115bc7Smrj 			lp->arg = s_strdup(kernel);
3527ae115bc7Smrj 			lp->line = s_strdup(linebuf);
3528ae115bc7Smrj 			lp = lp->next;
3529ae115bc7Smrj 		}
3530ae115bc7Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
3531ae115bc7Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3532ae115bc7Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
3533ae115bc7Smrj 			    module);
3534ae115bc7Smrj 			free(lp->arg);
3535ae115bc7Smrj 			free(lp->line);
3536ae115bc7Smrj 			lp->arg = s_strdup(module);
3537ae115bc7Smrj 			lp->line = s_strdup(linebuf);
3538ae115bc7Smrj 			lp = lp->next;
3539ae115bc7Smrj 		}
3540ae115bc7Smrj 	}
35418c1b6884Sszhou 	return (i);
35428c1b6884Sszhou }
35438c1b6884Sszhou 
35447c478bd9Sstevel@tonic-gate /*ARGSUSED*/
35457c478bd9Sstevel@tonic-gate static error_t
35467c478bd9Sstevel@tonic-gate update_entry(menu_t *mp, char *menu_root, char *opt)
35477c478bd9Sstevel@tonic-gate {
35487c478bd9Sstevel@tonic-gate 	FILE *fp;
35497c478bd9Sstevel@tonic-gate 	int entry;
355060d0a590Srscott 	char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL;
35518c1b6884Sszhou 	struct stat sbuf;
35528c1b6884Sszhou 	char failsafe[256];
35537c478bd9Sstevel@tonic-gate 
35547c478bd9Sstevel@tonic-gate 	assert(mp);
35557c478bd9Sstevel@tonic-gate 	assert(opt);
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate 	osdev = strtok(opt, ",");
35587c478bd9Sstevel@tonic-gate 	osroot = strtok(NULL, ",");
35597c478bd9Sstevel@tonic-gate 	if (osroot == NULL)
35607c478bd9Sstevel@tonic-gate 		osroot = menu_root;
35617c478bd9Sstevel@tonic-gate 	title = get_title(osroot);
35627c478bd9Sstevel@tonic-gate 
35637c478bd9Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
35648c1b6884Sszhou 	fp = open_diskmap(osroot);
35657c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
35667c478bd9Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
35677c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
35687c478bd9Sstevel@tonic-gate 	}
35697c478bd9Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
35707c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
35717c478bd9Sstevel@tonic-gate 	if (grubdisk == NULL) {
35727c478bd9Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
35737c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
35747c478bd9Sstevel@tonic-gate 	}
35757c478bd9Sstevel@tonic-gate 
35767c478bd9Sstevel@tonic-gate 	/* add the entry for normal Solaris */
3577ae115bc7Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
35788c1b6884Sszhou 		entry = update_boot_entry(mp, title, grubdisk,
3579*e7cbe64fSgw25295 		    (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
3580*e7cbe64fSgw25295 		    NULL, DIRECT_BOOT_ARCHIVE, osroot == menu_root);
3581843e1988Sjohnlev 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
3582843e1988Sjohnlev 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubdisk,
3583*e7cbe64fSgw25295 			    XEN_MENU, (bam_zfs ?
3584*e7cbe64fSgw25295 			    KERNEL_MODULE_LINE_ZFS : KERNEL_MODULE_LINE),
3585*e7cbe64fSgw25295 			    DIRECT_BOOT_ARCHIVE, osroot == menu_root);
3586ae115bc7Smrj 		}
3587843e1988Sjohnlev 	} else {
3588843e1988Sjohnlev 		entry = update_boot_entry(mp, title, grubdisk, MULTI_BOOT,
3589843e1988Sjohnlev 		    NULL, MULTI_BOOT_ARCHIVE, osroot == menu_root);
3590843e1988Sjohnlev 	}
35917c478bd9Sstevel@tonic-gate 
3592843e1988Sjohnlev 	/*
3593843e1988Sjohnlev 	 * Add the entry for failsafe archive.  On a bfu'd system, the
3594843e1988Sjohnlev 	 * failsafe may be different than the installed kernel.
3595843e1988Sjohnlev 	 */
3596ae115bc7Smrj 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
3597ae115bc7Smrj 	if (stat(failsafe, &sbuf) == 0) {
359860d0a590Srscott 
359960d0a590Srscott 		/* Figure out where the kernel line should point */
360060d0a590Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
360160d0a590Srscott 		    DIRECT_BOOT_FAILSAFE_KERNEL);
360260d0a590Srscott 		if (stat(failsafe, &sbuf) == 0) {
3603*e7cbe64fSgw25295 			failsafe_kernel =
3604*e7cbe64fSgw25295 			    (bam_zfs ? DIRECT_BOOT_FAILSAFE_LINE_ZFS :
3605*e7cbe64fSgw25295 			    DIRECT_BOOT_FAILSAFE_LINE);
360660d0a590Srscott 		} else {
360760d0a590Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
360860d0a590Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
360960d0a590Srscott 			if (stat(failsafe, &sbuf) == 0) {
361060d0a590Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
361160d0a590Srscott 			}
361260d0a590Srscott 		}
361360d0a590Srscott 		if (failsafe_kernel != NULL) {
3614ae115bc7Smrj 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
3615843e1988Sjohnlev 			    failsafe_kernel, NULL, MINIROOT,
3616843e1988Sjohnlev 			    osroot == menu_root);
361760d0a590Srscott 		}
3618ae115bc7Smrj 	}
36197c478bd9Sstevel@tonic-gate 	free(grubdisk);
36207c478bd9Sstevel@tonic-gate 
36217c478bd9Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
36227c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
36237c478bd9Sstevel@tonic-gate 	}
36247c478bd9Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
36257c478bd9Sstevel@tonic-gate 	return (BAM_WRITE);
36267c478bd9Sstevel@tonic-gate }
36277c478bd9Sstevel@tonic-gate 
3628b610f78eSvikram static char *
3629b610f78eSvikram read_grub_root(void)
3630b610f78eSvikram {
3631b610f78eSvikram 	FILE *fp;
3632b610f78eSvikram 	struct stat sb;
3633b610f78eSvikram 	char buf[BAM_MAXLINE];
3634b610f78eSvikram 	char *rootstr;
3635b610f78eSvikram 
3636b610f78eSvikram 	if (stat(GRUB_slice, &sb) != 0) {
3637b610f78eSvikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3638b610f78eSvikram 		return (NULL);
3639b610f78eSvikram 	}
3640b610f78eSvikram 
3641b610f78eSvikram 	if (stat(GRUB_root, &sb) != 0) {
3642b610f78eSvikram 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3643b610f78eSvikram 		return (NULL);
3644b610f78eSvikram 	}
3645b610f78eSvikram 
3646b610f78eSvikram 	fp = fopen(GRUB_root, "r");
3647b610f78eSvikram 	if (fp == NULL) {
3648b610f78eSvikram 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3649b610f78eSvikram 		return (NULL);
3650b610f78eSvikram 	}
3651b610f78eSvikram 
3652b610f78eSvikram 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3653b610f78eSvikram 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3654b610f78eSvikram 		(void) fclose(fp);
3655b610f78eSvikram 		return (NULL);
3656b610f78eSvikram 	}
3657b610f78eSvikram 
3658b610f78eSvikram 	/*
3659b610f78eSvikram 	 * Copy buf here as check below may trash the buffer
3660b610f78eSvikram 	 */
3661b610f78eSvikram 	rootstr = s_strdup(buf);
3662b610f78eSvikram 
3663b610f78eSvikram 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3664b610f78eSvikram 		bam_error(BAD_ROOT_FILE, GRUB_root);
3665b610f78eSvikram 		free(rootstr);
3666b610f78eSvikram 		rootstr = NULL;
3667b610f78eSvikram 	}
3668b610f78eSvikram 
3669b610f78eSvikram 	(void) fclose(fp);
3670b610f78eSvikram 
3671b610f78eSvikram 	return (rootstr);
3672b610f78eSvikram }
3673b610f78eSvikram 
36748c1b6884Sszhou static void
3675ae115bc7Smrj save_default_entry(menu_t *mp, const char *which)
36768c1b6884Sszhou {
36778c1b6884Sszhou 	int lineNum, entryNum;
36788c1b6884Sszhou 	int entry = 0;	/* default is 0 */
36798c1b6884Sszhou 	char linebuf[BAM_MAXLINE];
36808c1b6884Sszhou 	line_t *lp = mp->curdefault;
36818c1b6884Sszhou 
3682bff269d0Svikram 	if (mp->start) {
3683bff269d0Svikram 		lineNum = mp->end->lineNum;
3684bff269d0Svikram 		entryNum = mp->end->entryNum;
3685bff269d0Svikram 	} else {
3686bff269d0Svikram 		lineNum = LINE_INIT;
3687bff269d0Svikram 		entryNum = ENTRY_INIT;
3688bff269d0Svikram 	}
3689bff269d0Svikram 
36908c1b6884Sszhou 	if (lp)
36918c1b6884Sszhou 		entry = s_strtol(lp->arg);
36928c1b6884Sszhou 
3693ae115bc7Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
36948c1b6884Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
36958c1b6884Sszhou }
36968c1b6884Sszhou 
36978c1b6884Sszhou static void
3698ae115bc7Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
36998c1b6884Sszhou {
37008c1b6884Sszhou 	int entry;
37018c1b6884Sszhou 	char *str;
37028c1b6884Sszhou 
37038c1b6884Sszhou 	if (lp == NULL)
37048c1b6884Sszhou 		return;		/* nothing to restore */
37058c1b6884Sszhou 
3706ae115bc7Smrj 	str = lp->arg + strlen(which);
37078c1b6884Sszhou 	entry = s_strtol(str);
37088c1b6884Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
37098c1b6884Sszhou 
37108c1b6884Sszhou 	/* delete saved old default line */
37118c1b6884Sszhou 	unlink_line(mp, lp);
37128c1b6884Sszhou 	line_free(lp);
37138c1b6884Sszhou }
37148c1b6884Sszhou 
37157c478bd9Sstevel@tonic-gate /*
37167c478bd9Sstevel@tonic-gate  * This function is for supporting reboot with args.
37177c478bd9Sstevel@tonic-gate  * The opt value can be:
37187c478bd9Sstevel@tonic-gate  * NULL		delete temp entry, if present
37197c478bd9Sstevel@tonic-gate  * entry=#	switches default entry to 1
37207c478bd9Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
37217c478bd9Sstevel@tonic-gate  *		and make it the default
37227c478bd9Sstevel@tonic-gate  */
37237c478bd9Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
37247c478bd9Sstevel@tonic-gate 
37258c1b6884Sszhou /*ARGSUSED*/
37267c478bd9Sstevel@tonic-gate static error_t
37277c478bd9Sstevel@tonic-gate update_temp(menu_t *mp, char *menupath, char *opt)
37287c478bd9Sstevel@tonic-gate {
37297c478bd9Sstevel@tonic-gate 	int entry;
3730455710d3Srscott 	char *grubdisk, *rootdev, *path, *opt_ptr;
3731ae115bc7Smrj 	char kernbuf[BUFSIZ];
3732ae115bc7Smrj 	char args_buf[BUFSIZ];
3733b610f78eSvikram 	struct stat sb;
37347c478bd9Sstevel@tonic-gate 
37357c478bd9Sstevel@tonic-gate 	assert(mp);
37367c478bd9Sstevel@tonic-gate 
37378c1b6884Sszhou 	/* If no option, delete exiting reboot menu entry */
37388c1b6884Sszhou 	if (opt == NULL) {
37398c1b6884Sszhou 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
3740843e1988Sjohnlev 		    NULL, 0, &entry);
37418c1b6884Sszhou 		if (ent == NULL)	/* not found is ok */
37428c1b6884Sszhou 			return (BAM_SUCCESS);
37438c1b6884Sszhou 		(void) do_delete(mp, entry);
3744ae115bc7Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
3745ae115bc7Smrj 		mp->olddefault = NULL;
37468c1b6884Sszhou 		return (BAM_WRITE);
37478c1b6884Sszhou 	}
37488c1b6884Sszhou 
37498c1b6884Sszhou 	/* if entry= is specified, set the default entry */
37508c1b6884Sszhou 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
37517c478bd9Sstevel@tonic-gate 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
37527c478bd9Sstevel@tonic-gate 		/* this is entry=# option */
37537c478bd9Sstevel@tonic-gate 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
37547c478bd9Sstevel@tonic-gate 	}
37557c478bd9Sstevel@tonic-gate 
37567c478bd9Sstevel@tonic-gate 	/*
37577c478bd9Sstevel@tonic-gate 	 * add a new menu entry base on opt and make it the default
3758b610f78eSvikram 	 */
3759b610f78eSvikram 	grubdisk = NULL;
3760b610f78eSvikram 	if (stat(GRUB_slice, &sb) != 0) {
3761b610f78eSvikram 		/*
37627c478bd9Sstevel@tonic-gate 		 * 1. First get root disk name from mnttab
37637c478bd9Sstevel@tonic-gate 		 * 2. Translate disk name to grub name
37647c478bd9Sstevel@tonic-gate 		 * 3. Add the new menu entry
37657c478bd9Sstevel@tonic-gate 		 */
37667c478bd9Sstevel@tonic-gate 		rootdev = get_special("/");
37677c478bd9Sstevel@tonic-gate 		if (rootdev) {
37687c478bd9Sstevel@tonic-gate 			grubdisk = os_to_grubdisk(rootdev, 1);
37697c478bd9Sstevel@tonic-gate 			free(rootdev);
37707c478bd9Sstevel@tonic-gate 		}
3771b610f78eSvikram 	} else {
3772b610f78eSvikram 		/*
3773b610f78eSvikram 		 * This is an LU BE. The GRUB_root file
3774b610f78eSvikram 		 * contains entry for GRUB's "root" cmd.
3775b610f78eSvikram 		 */
3776b610f78eSvikram 		grubdisk = read_grub_root();
3777b610f78eSvikram 	}
37787c478bd9Sstevel@tonic-gate 	if (grubdisk == NULL) {
3779b610f78eSvikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
37807c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
37817c478bd9Sstevel@tonic-gate 	}
37827c478bd9Sstevel@tonic-gate 
37837c478bd9Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
3784ae115bc7Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
3785ae115bc7Smrj 		if (opt[0] == '-') {
3786ae115bc7Smrj 			/* It's an option - first see if boot-file is set */
3787ae115bc7Smrj 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
3788ae115bc7Smrj 			    != BAM_SUCCESS)
3789ae115bc7Smrj 				return (BAM_ERROR);
3790ae115bc7Smrj 			if (kernbuf[0] == '\0')
3791ae115bc7Smrj 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
3792ae115bc7Smrj 				    BUFSIZ);
3793ae115bc7Smrj 			(void) strlcat(kernbuf, " ", BUFSIZ);
3794ae115bc7Smrj 			(void) strlcat(kernbuf, opt, BUFSIZ);
3795ae115bc7Smrj 		} else if (opt[0] == '/') {
3796455710d3Srscott 			/* It's a full path, so write it out. */
3797ae115bc7Smrj 			(void) strlcpy(kernbuf, opt, BUFSIZ);
3798455710d3Srscott 
3799455710d3Srscott 			/*
3800455710d3Srscott 			 * If someone runs:
3801455710d3Srscott 			 *
3802455710d3Srscott 			 *	# eeprom boot-args='-kd'
3803455710d3Srscott 			 *	# reboot /platform/i86pc/kernel/unix
3804455710d3Srscott 			 *
3805455710d3Srscott 			 * we want to use the boot-args as part of the boot
3806455710d3Srscott 			 * line.  On the other hand, if someone runs:
3807455710d3Srscott 			 *
3808455710d3Srscott 			 *	# reboot "/platform/i86pc/kernel/unix -kd"
3809455710d3Srscott 			 *
3810455710d3Srscott 			 * we don't need to mess with boot-args.  If there's
3811455710d3Srscott 			 * no space in the options string, assume we're in the
3812455710d3Srscott 			 * first case.
3813455710d3Srscott 			 */
3814455710d3Srscott 			if (strchr(opt, ' ') == NULL) {
3815455710d3Srscott 				if (set_kernel(mp, ARGS_CMD, NULL, args_buf,
3816455710d3Srscott 				    BUFSIZ) != BAM_SUCCESS)
3817455710d3Srscott 					return (BAM_ERROR);
3818455710d3Srscott 
3819455710d3Srscott 				if (args_buf[0] != '\0') {
3820455710d3Srscott 					(void) strlcat(kernbuf, " ", BUFSIZ);
3821455710d3Srscott 					(void) strlcat(kernbuf, args_buf,
3822455710d3Srscott 					    BUFSIZ);
3823455710d3Srscott 				}
3824455710d3Srscott 			}
3825ae115bc7Smrj 		} else {
3826455710d3Srscott 			/*
3827455710d3Srscott 			 * It may be a partial path, or it may be a partial
3828455710d3Srscott 			 * path followed by options.  Assume that only options
3829455710d3Srscott 			 * follow a space.  If someone sends us a kernel path
3830455710d3Srscott 			 * that includes a space, they deserve to be broken.
3831455710d3Srscott 			 */
3832455710d3Srscott 			opt_ptr = strchr(opt, ' ');
3833455710d3Srscott 			if (opt_ptr != NULL) {
3834455710d3Srscott 				*opt_ptr = '\0';
3835455710d3Srscott 			}
3836455710d3Srscott 
3837ae115bc7Smrj 			path = expand_path(opt);
3838ae115bc7Smrj 			if (path != NULL) {
3839ae115bc7Smrj 				(void) strlcpy(kernbuf, path, BUFSIZ);
3840ae115bc7Smrj 				free(path);
3841455710d3Srscott 
3842455710d3Srscott 				/*
3843455710d3Srscott 				 * If there were options given, use those.
3844455710d3Srscott 				 * Otherwise, copy over the default options.
3845455710d3Srscott 				 */
3846455710d3Srscott 				if (opt_ptr != NULL) {
3847455710d3Srscott 					/* Restore the space in opt string */
3848455710d3Srscott 					*opt_ptr = ' ';
3849455710d3Srscott 					(void) strlcat(kernbuf, opt_ptr,
3850455710d3Srscott 					    BUFSIZ);
3851455710d3Srscott 				} else {
3852ae115bc7Smrj 					if (set_kernel(mp, ARGS_CMD, NULL,
3853ae115bc7Smrj 					    args_buf, BUFSIZ) != BAM_SUCCESS)
3854ae115bc7Smrj 						return (BAM_ERROR);
3855ae115bc7Smrj 
3856ae115bc7Smrj 					if (args_buf[0] != '\0') {
3857ae115bc7Smrj 						(void) strlcat(kernbuf, " ",
3858ae115bc7Smrj 						    BUFSIZ);
3859ae115bc7Smrj 						(void) strlcat(kernbuf,
3860ae115bc7Smrj 						    args_buf, BUFSIZ);
3861ae115bc7Smrj 					}
3862ae115bc7Smrj 				}
3863455710d3Srscott 			} else {
3864455710d3Srscott 				bam_error(UNKNOWN_KERNEL, opt);
3865455710d3Srscott 				bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
3866455710d3Srscott 				return (BAM_ERROR);
3867ae115bc7Smrj 			}
3868ae115bc7Smrj 		}
38697c478bd9Sstevel@tonic-gate 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3870843e1988Sjohnlev 		    NULL, NULL);
3871ae115bc7Smrj 	} else {
3872ae115bc7Smrj 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
3873ae115bc7Smrj 		    MULTI_BOOT, opt);
3874ae115bc7Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3875843e1988Sjohnlev 		    NULL, MULTI_BOOT_ARCHIVE);
3876ae115bc7Smrj 	}
38777c478bd9Sstevel@tonic-gate 	free(grubdisk);
38787c478bd9Sstevel@tonic-gate 
38797c478bd9Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
3880b610f78eSvikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
38817c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
38827c478bd9Sstevel@tonic-gate 	}
38838c1b6884Sszhou 
3884ae115bc7Smrj 	save_default_entry(mp, BAM_OLDDEF);
38857c478bd9Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
38867c478bd9Sstevel@tonic-gate 	return (BAM_WRITE);
38877c478bd9Sstevel@tonic-gate }
38887c478bd9Sstevel@tonic-gate 
38897c478bd9Sstevel@tonic-gate static error_t
38907c478bd9Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
38917c478bd9Sstevel@tonic-gate {
38927c478bd9Sstevel@tonic-gate 	line_t *lp, *found, *last;
38937c478bd9Sstevel@tonic-gate 	char *cp, *str;
38947c478bd9Sstevel@tonic-gate 	char prefix[BAM_MAXLINE];
38957c478bd9Sstevel@tonic-gate 	size_t len;
38967c478bd9Sstevel@tonic-gate 
38977c478bd9Sstevel@tonic-gate 	assert(mp);
38987c478bd9Sstevel@tonic-gate 	assert(globalcmd);
38997c478bd9Sstevel@tonic-gate 
3900b610f78eSvikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3901b610f78eSvikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3902b610f78eSvikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3903b610f78eSvikram 			bam_error(INVALID_ENTRY, prefix);
3904b610f78eSvikram 			return (BAM_ERROR);
3905b610f78eSvikram 		}
3906b610f78eSvikram 	}
3907b610f78eSvikram 
39087c478bd9Sstevel@tonic-gate 	found = last = NULL;
39097c478bd9Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
39107c478bd9Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
39117c478bd9Sstevel@tonic-gate 			continue;
39127c478bd9Sstevel@tonic-gate 
39137c478bd9Sstevel@tonic-gate 		last = lp; /* track the last global found */
39147c478bd9Sstevel@tonic-gate 
39157c478bd9Sstevel@tonic-gate 		if (lp->cmd == NULL) {
39167c478bd9Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
39177c478bd9Sstevel@tonic-gate 			continue;
39187c478bd9Sstevel@tonic-gate 		}
39197c478bd9Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
39207c478bd9Sstevel@tonic-gate 			continue;
39217c478bd9Sstevel@tonic-gate 
39227c478bd9Sstevel@tonic-gate 		if (found) {
39237c478bd9Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
39247c478bd9Sstevel@tonic-gate 		}
39257c478bd9Sstevel@tonic-gate 		found = lp;
39267c478bd9Sstevel@tonic-gate 	}
39277c478bd9Sstevel@tonic-gate 
39287c478bd9Sstevel@tonic-gate 	if (found == NULL) {
39297c478bd9Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
39307c478bd9Sstevel@tonic-gate 		if (last == NULL) {
39317c478bd9Sstevel@tonic-gate 			lp->next = mp->start;
39327c478bd9Sstevel@tonic-gate 			mp->start = lp;
39337c478bd9Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
39347c478bd9Sstevel@tonic-gate 		} else {
39357c478bd9Sstevel@tonic-gate 			lp->next = last->next;
39367c478bd9Sstevel@tonic-gate 			last->next = lp;
39377c478bd9Sstevel@tonic-gate 			if (lp->next == NULL)
39387c478bd9Sstevel@tonic-gate 				mp->end = lp;
39397c478bd9Sstevel@tonic-gate 		}
39407c478bd9Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
39417c478bd9Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
39427c478bd9Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
39437c478bd9Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
39447c478bd9Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
39457c478bd9Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
39467c478bd9Sstevel@tonic-gate 		return (BAM_WRITE);
39477c478bd9Sstevel@tonic-gate 	}
39487c478bd9Sstevel@tonic-gate 
39497c478bd9Sstevel@tonic-gate 	/*
39507c478bd9Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
39517c478bd9Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
39527c478bd9Sstevel@tonic-gate 	 * readability.
39537c478bd9Sstevel@tonic-gate 	 */
39547c478bd9Sstevel@tonic-gate 	str = found->line;
39557c478bd9Sstevel@tonic-gate 	cp = prefix;
39567c478bd9Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
39577c478bd9Sstevel@tonic-gate 		*(cp++) = *(str++);
39587c478bd9Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
39597c478bd9Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
39607c478bd9Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
39617c478bd9Sstevel@tonic-gate 
39627c478bd9Sstevel@tonic-gate 	free(found->line);
39637c478bd9Sstevel@tonic-gate 	found->line = s_calloc(1, len);
39647c478bd9Sstevel@tonic-gate 	(void) snprintf(found->line, len,
39657c478bd9Sstevel@tonic-gate 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
39667c478bd9Sstevel@tonic-gate 
39677c478bd9Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
39687c478bd9Sstevel@tonic-gate }
39697c478bd9Sstevel@tonic-gate 
3970ae115bc7Smrj /*
3971ae115bc7Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
3972455710d3Srscott  * expand it to a full unix path.  The calling function is expected to
3973455710d3Srscott  * output a message if an error occurs and NULL is returned.
3974ae115bc7Smrj  */
3975ae115bc7Smrj static char *
3976ae115bc7Smrj expand_path(const char *partial_path)
3977ae115bc7Smrj {
3978ae115bc7Smrj 	int new_path_len;
3979ae115bc7Smrj 	char *new_path, new_path2[PATH_MAX];
3980ae115bc7Smrj 	struct stat sb;
3981ae115bc7Smrj 
3982ae115bc7Smrj 	new_path_len = strlen(partial_path) + 64;
3983ae115bc7Smrj 	new_path = s_calloc(1, new_path_len);
3984ae115bc7Smrj 
3985ae115bc7Smrj 	/* First, try the simplest case - something like "kernel/unix" */
3986ae115bc7Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
3987ae115bc7Smrj 	    partial_path);
3988ae115bc7Smrj 	if (stat(new_path, &sb) == 0) {
3989ae115bc7Smrj 		return (new_path);
3990ae115bc7Smrj 	}
3991ae115bc7Smrj 
3992ae115bc7Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
3993ae115bc7Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
3994ae115bc7Smrj 		    DIRECT_BOOT_KERNEL);
3995ae115bc7Smrj 		return (new_path);
3996ae115bc7Smrj 	}
3997ae115bc7Smrj 
3998ae115bc7Smrj 	/*
3999ae115bc7Smrj 	 * We've quickly reached unsupported usage.  Try once more to
4000ae115bc7Smrj 	 * see if we were just given a glom name.
4001ae115bc7Smrj 	 */
4002ae115bc7Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
4003ae115bc7Smrj 	    partial_path);
4004ae115bc7Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
4005ae115bc7Smrj 	    partial_path);
4006ae115bc7Smrj 	if (stat(new_path, &sb) == 0) {
4007ae115bc7Smrj 		if (stat(new_path2, &sb) == 0) {
4008ae115bc7Smrj 			/*
4009ae115bc7Smrj 			 * We matched both, so we actually
4010ae115bc7Smrj 			 * want to write the $ISADIR version.
4011ae115bc7Smrj 			 */
4012ae115bc7Smrj 			(void) snprintf(new_path, new_path_len,
4013ae115bc7Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
4014ae115bc7Smrj 			    partial_path);
4015ae115bc7Smrj 		}
4016ae115bc7Smrj 		return (new_path);
4017ae115bc7Smrj 	}
4018ae115bc7Smrj 
4019ae115bc7Smrj 	free(new_path);
4020ae115bc7Smrj 	return (NULL);
4021ae115bc7Smrj }
4022ae115bc7Smrj 
4023ae115bc7Smrj /*
4024ae115bc7Smrj  * The kernel cmd and arg have been changed, so
4025ae115bc7Smrj  * check whether the archive line needs to change.
4026ae115bc7Smrj  */
4027ae115bc7Smrj static void
4028ae115bc7Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
4029ae115bc7Smrj {
4030ae115bc7Smrj 	line_t *lp = entryp->start;
4031ae115bc7Smrj 	char *new_archive;
4032ae115bc7Smrj 	menu_cmd_t m_cmd;
4033ae115bc7Smrj 
4034ae115bc7Smrj 	for (; lp != NULL; lp = lp->next) {
4035ae115bc7Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
4036ae115bc7Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
4037ae115bc7Smrj 			break;
4038ae115bc7Smrj 		}
4039ae115bc7Smrj 		if (lp == entryp->end)
4040ae115bc7Smrj 			return;
4041ae115bc7Smrj 	}
4042ae115bc7Smrj 	if (lp == NULL)
4043ae115bc7Smrj 		return;
4044ae115bc7Smrj 
4045ae115bc7Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
4046ae115bc7Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
4047ae115bc7Smrj 		m_cmd = MODULE_DOLLAR_CMD;
4048ae115bc7Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
4049ae115bc7Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
4050ae115bc7Smrj 		m_cmd = MODULE_CMD;
4051ae115bc7Smrj 	} else {
4052ae115bc7Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
4053ae115bc7Smrj 		m_cmd = MODULE_CMD;
4054ae115bc7Smrj 	}
4055ae115bc7Smrj 
4056ae115bc7Smrj 	if (strcmp(lp->arg, new_archive) == 0)
4057ae115bc7Smrj 		return;
4058ae115bc7Smrj 
4059ae115bc7Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
4060ae115bc7Smrj 		free(lp->cmd);
4061ae115bc7Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
4062ae115bc7Smrj 	}
4063ae115bc7Smrj 
4064ae115bc7Smrj 	free(lp->arg);
4065ae115bc7Smrj 	lp->arg = s_strdup(new_archive);
4066ae115bc7Smrj 	update_line(lp);
4067ae115bc7Smrj }
4068ae115bc7Smrj 
4069ae115bc7Smrj /*
4070ae115bc7Smrj  * Title for an entry to set properties that once went in bootenv.rc.
4071ae115bc7Smrj  */
4072ae115bc7Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
4073ae115bc7Smrj 
4074ae115bc7Smrj /*
4075ae115bc7Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
4076ae115bc7Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
4077ae115bc7Smrj  * string, reset the value to the default.  If path is a non-zero-length
4078ae115bc7Smrj  * string, set the kernel or arguments.
4079ae115bc7Smrj  */
4080ae115bc7Smrj static error_t
4081ae115bc7Smrj set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
4082ae115bc7Smrj {
4083ae115bc7Smrj 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
4084ae115bc7Smrj 	entry_t *entryp;
4085ae115bc7Smrj 	line_t *ptr, *kernelp;
4086ae115bc7Smrj 	char *new_arg, *old_args, *space;
4087ae115bc7Smrj 	char *grubdisk, *rootdev, *new_path;
4088ae115bc7Smrj 	char old_space;
4089ae115bc7Smrj 	size_t old_kernel_len, new_str_len;
4090ae115bc7Smrj 	struct stat sb;
4091ae115bc7Smrj 
4092ae115bc7Smrj 	assert(bufsize > 0);
4093ae115bc7Smrj 
4094ae115bc7Smrj 	ptr = kernelp = NULL;
4095ae115bc7Smrj 	new_arg = old_args = space = NULL;
4096ae115bc7Smrj 	grubdisk = rootdev = new_path = NULL;
4097ae115bc7Smrj 	buf[0] = '\0';
4098ae115bc7Smrj 
4099ae115bc7Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
4100ae115bc7Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
4101ae115bc7Smrj 		return (BAM_ERROR);
4102ae115bc7Smrj 	}
4103ae115bc7Smrj 
4104ae115bc7Smrj 	/*
4105ae115bc7Smrj 	 * If a user changed the default entry to a non-bootadm controlled
4106ae115bc7Smrj 	 * one, we don't want to mess with it.  Just print an error and
4107ae115bc7Smrj 	 * return.
4108ae115bc7Smrj 	 */
4109ae115bc7Smrj 	if (mp->curdefault) {
4110ae115bc7Smrj 		entryNum = s_strtol(mp->curdefault->arg);
4111ae115bc7Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
4112ae115bc7Smrj 			if (entryp->entryNum == entryNum)
4113ae115bc7Smrj 				break;
4114ae115bc7Smrj 		}
4115ae115bc7Smrj 		if ((entryp != NULL) &&
4116ae115bc7Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
4117ae115bc7Smrj 			bam_error(DEFAULT_NOT_BAM);
4118ae115bc7Smrj 			return (BAM_ERROR);
4119ae115bc7Smrj 		}
4120ae115bc7Smrj 	}
4121ae115bc7Smrj 
4122ae115bc7Smrj 	entryNum = -1;
4123843e1988Sjohnlev 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, 0,
4124ae115bc7Smrj 	    &entryNum);
4125ae115bc7Smrj 
4126ae115bc7Smrj 	if (entryp != NULL) {
4127ae115bc7Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
4128ae115bc7Smrj 		    ptr = ptr->next) {
4129ae115bc7Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
4130ae115bc7Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
4131ae115bc7Smrj 				kernelp = ptr;
4132ae115bc7Smrj 				break;
4133ae115bc7Smrj 			}
4134ae115bc7Smrj 		}
4135ae115bc7Smrj 		if (kernelp == NULL) {
4136ae115bc7Smrj 			bam_error(NO_KERNEL, entryNum);
4137ae115bc7Smrj 			return (BAM_ERROR);
4138ae115bc7Smrj 		}
4139ae115bc7Smrj 
4140ae115bc7Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
4141ae115bc7Smrj 		space = old_args = kernelp->arg + old_kernel_len;
4142ae115bc7Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
4143ae115bc7Smrj 			old_args++;
4144ae115bc7Smrj 	}
4145ae115bc7Smrj 
4146ae115bc7Smrj 	if (path == NULL) {
4147ae115bc7Smrj 		/* Simply report what was found */
4148ae115bc7Smrj 		if (kernelp == NULL)
4149ae115bc7Smrj 			return (BAM_SUCCESS);
4150ae115bc7Smrj 
4151ae115bc7Smrj 		if (optnum == ARGS_CMD) {
4152ae115bc7Smrj 			if (old_args[0] != '\0')
4153ae115bc7Smrj 				(void) strlcpy(buf, old_args, bufsize);
4154ae115bc7Smrj 		} else {
4155ae115bc7Smrj 			/*
4156ae115bc7Smrj 			 * We need to print the kernel, so we just turn the
4157ae115bc7Smrj 			 * first space into a '\0' and print the beginning.
4158ae115bc7Smrj 			 * We don't print anything if it's the default kernel.
4159ae115bc7Smrj 			 */
4160ae115bc7Smrj 			old_space = *space;
4161ae115bc7Smrj 			*space = '\0';
4162ae115bc7Smrj 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
4163ae115bc7Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
4164ae115bc7Smrj 			*space = old_space;
4165ae115bc7Smrj 		}
4166ae115bc7Smrj 		return (BAM_SUCCESS);
4167ae115bc7Smrj 	}
4168ae115bc7Smrj 
4169ae115bc7Smrj 	/*
4170ae115bc7Smrj 	 * First, check if we're resetting an entry to the default.
4171ae115bc7Smrj 	 */
4172ae115bc7Smrj 	if ((path[0] == '\0') ||
4173ae115bc7Smrj 	    ((optnum == KERNEL_CMD) &&
4174ae115bc7Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
4175ae115bc7Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
4176ae115bc7Smrj 			/* No previous entry, it's already the default */
4177ae115bc7Smrj 			return (BAM_SUCCESS);
4178ae115bc7Smrj 		}
4179ae115bc7Smrj 
4180ae115bc7Smrj 		/*
4181ae115bc7Smrj 		 * Check if we can delete the entry.  If we're resetting the
4182ae115bc7Smrj 		 * kernel command, and the args is already empty, or if we're
4183ae115bc7Smrj 		 * resetting the args command, and the kernel is already the
4184ae115bc7Smrj 		 * default, we can restore the old default and delete the entry.
4185ae115bc7Smrj 		 */
4186ae115bc7Smrj 		if (((optnum == KERNEL_CMD) &&
4187ae115bc7Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
4188ae115bc7Smrj 		    ((optnum == ARGS_CMD) &&
4189ae115bc7Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
4190ae115bc7Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
4191ae115bc7Smrj 			kernelp = NULL;
4192ae115bc7Smrj 			(void) do_delete(mp, entryNum);
4193ae115bc7Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
4194ae115bc7Smrj 			    mp->old_rc_default);
4195ae115bc7Smrj 			mp->old_rc_default = NULL;
4196ae115bc7Smrj 			rv = BAM_WRITE;
4197ae115bc7Smrj 			goto done;
4198ae115bc7Smrj 		}
4199ae115bc7Smrj 
4200ae115bc7Smrj 		if (optnum == KERNEL_CMD) {
4201ae115bc7Smrj 			/*
4202ae115bc7Smrj 			 * At this point, we've already checked that old_args
4203ae115bc7Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
4204ae115bc7Smrj 			 * a space a the string termination character.
4205ae115bc7Smrj 			 */
4206ae115bc7Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
4207ae115bc7Smrj 			    strlen(old_args) + 2;
4208ae115bc7Smrj 			new_arg = s_calloc(1, new_str_len);
4209ae115bc7Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
4210ae115bc7Smrj 			    DIRECT_BOOT_KERNEL, old_args);
4211ae115bc7Smrj 			free(kernelp->arg);
4212ae115bc7Smrj 			kernelp->arg = new_arg;
4213ae115bc7Smrj 
4214ae115bc7Smrj 			/*
4215ae115bc7Smrj 			 * We have changed the kernel line, so we may need
4216ae115bc7Smrj 			 * to update the archive line as well.
4217ae115bc7Smrj 			 */
4218ae115bc7Smrj 			set_archive_line(entryp, kernelp);
4219ae115bc7Smrj 		} else {
4220ae115bc7Smrj 			/*
4221ae115bc7Smrj 			 * We're resetting the boot args to nothing, so
4222ae115bc7Smrj 			 * we only need to copy the kernel.  We've already
4223ae115bc7Smrj 			 * checked that the kernel is not the default.
4224ae115bc7Smrj 			 */
4225ae115bc7Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
4226ae115bc7Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
4227ae115bc7Smrj 			    kernelp->arg);
4228ae115bc7Smrj 			free(kernelp->arg);
4229ae115bc7Smrj 			kernelp->arg = new_arg;
4230ae115bc7Smrj 		}
4231ae115bc7Smrj 		rv = BAM_WRITE;
4232ae115bc7Smrj 		goto done;
4233ae115bc7Smrj 	}
4234ae115bc7Smrj 
4235ae115bc7Smrj 	/*
4236ae115bc7Smrj 	 * Expand the kernel file to a full path, if necessary
4237ae115bc7Smrj 	 */
4238ae115bc7Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
4239ae115bc7Smrj 		new_path = expand_path(path);
4240ae115bc7Smrj 		if (new_path == NULL) {
4241455710d3Srscott 			bam_error(UNKNOWN_KERNEL, path);
4242ae115bc7Smrj 			return (BAM_ERROR);
4243ae115bc7Smrj 		}
4244ae115bc7Smrj 		free_new_path = 1;
4245ae115bc7Smrj 	} else {
4246ae115bc7Smrj 		new_path = path;
4247ae115bc7Smrj 		free_new_path = 0;
4248ae115bc7Smrj 	}
4249ae115bc7Smrj 
4250ae115bc7Smrj 	/*
4251ae115bc7Smrj 	 * At this point, we know we're setting a new value.  First, take care
4252ae115bc7Smrj 	 * of the case where there was no previous entry.
4253ae115bc7Smrj 	 */
4254ae115bc7Smrj 	if (entryp == NULL) {
4255ae115bc7Smrj 		/* Similar to code in update_temp */
4256ae115bc7Smrj 		if (stat(GRUB_slice, &sb) != 0) {
4257ae115bc7Smrj 			/*
4258ae115bc7Smrj 			 * 1. First get root disk name from mnttab
4259ae115bc7Smrj 			 * 2. Translate disk name to grub name
4260ae115bc7Smrj 			 * 3. Add the new menu entry
4261ae115bc7Smrj 			 */
4262ae115bc7Smrj 			rootdev = get_special("/");
4263ae115bc7Smrj 			if (rootdev) {
4264ae115bc7Smrj 				grubdisk = os_to_grubdisk(rootdev, 1);
4265ae115bc7Smrj 				free(rootdev);
4266ae115bc7Smrj 			}
4267ae115bc7Smrj 		} else {
4268ae115bc7Smrj 			/*
4269ae115bc7Smrj 			 * This is an LU BE. The GRUB_root file
4270ae115bc7Smrj 			 * contains entry for GRUB's "root" cmd.
4271ae115bc7Smrj 			 */
4272ae115bc7Smrj 			grubdisk = read_grub_root();
4273ae115bc7Smrj 		}
4274ae115bc7Smrj 		if (grubdisk == NULL) {
4275ae115bc7Smrj 			bam_error(REBOOT_WITH_ARGS_FAILED);
4276ae115bc7Smrj 			rv = BAM_ERROR;
4277ae115bc7Smrj 			goto done;
4278ae115bc7Smrj 		}
4279ae115bc7Smrj 		if (optnum == KERNEL_CMD) {
4280ae115bc7Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
4281843e1988Sjohnlev 			    grubdisk, new_path, NULL, NULL);
4282ae115bc7Smrj 		} else {
4283ae115bc7Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
4284ae115bc7Smrj 			    strlen(path) + 8;
4285ae115bc7Smrj 			new_arg = s_calloc(1, new_str_len);
4286ae115bc7Smrj 
4287ae115bc7Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
4288ae115bc7Smrj 			    DIRECT_BOOT_KERNEL, path);
4289ae115bc7Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
4290843e1988Sjohnlev 			    grubdisk, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
4291ae115bc7Smrj 		}
4292ae115bc7Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
4293ae115bc7Smrj 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
4294ae115bc7Smrj 		rv = BAM_WRITE;
4295ae115bc7Smrj 		goto done;
4296ae115bc7Smrj 	}
4297ae115bc7Smrj 
4298ae115bc7Smrj 	/*
4299ae115bc7Smrj 	 * There was already an bootenv entry which we need to edit.
4300ae115bc7Smrj 	 */
4301ae115bc7Smrj 	if (optnum == KERNEL_CMD) {
4302ae115bc7Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
4303ae115bc7Smrj 		new_arg = s_calloc(1, new_str_len);
4304ae115bc7Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
4305ae115bc7Smrj 		    old_args);
4306ae115bc7Smrj 		free(kernelp->arg);
4307ae115bc7Smrj 		kernelp->arg = new_arg;
4308ae115bc7Smrj 
4309ae115bc7Smrj 		/*
4310ae115bc7Smrj 		 * If we have changed the kernel line, we may need to update
4311ae115bc7Smrj 		 * the archive line as well.
4312ae115bc7Smrj 		 */
4313ae115bc7Smrj 		set_archive_line(entryp, kernelp);
4314ae115bc7Smrj 	} else {
4315ae115bc7Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
4316ae115bc7Smrj 		new_arg = s_calloc(1, new_str_len);
4317ae115bc7Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
4318ae115bc7Smrj 		(void) strlcat(new_arg, " ", new_str_len);
4319ae115bc7Smrj 		(void) strlcat(new_arg, path, new_str_len);
4320ae115bc7Smrj 		free(kernelp->arg);
4321ae115bc7Smrj 		kernelp->arg = new_arg;
4322ae115bc7Smrj 	}
4323ae115bc7Smrj 	rv = BAM_WRITE;
4324ae115bc7Smrj 
4325ae115bc7Smrj done:
4326ae115bc7Smrj 	if ((rv == BAM_WRITE) && kernelp)
4327ae115bc7Smrj 		update_line(kernelp);
4328ae115bc7Smrj 	if (free_new_path)
4329ae115bc7Smrj 		free(new_path);
4330ae115bc7Smrj 	return (rv);
4331ae115bc7Smrj }
4332ae115bc7Smrj 
43337c478bd9Sstevel@tonic-gate /*ARGSUSED*/
43347c478bd9Sstevel@tonic-gate static error_t
43357c478bd9Sstevel@tonic-gate set_option(menu_t *mp, char *menu_path, char *opt)
43367c478bd9Sstevel@tonic-gate {
43377c478bd9Sstevel@tonic-gate 	int optnum, optval;
43387c478bd9Sstevel@tonic-gate 	char *val;
4339ae115bc7Smrj 	char buf[BUFSIZ] = "";
4340ae115bc7Smrj 	error_t rv;
43417c478bd9Sstevel@tonic-gate 
43427c478bd9Sstevel@tonic-gate 	assert(mp);
43437c478bd9Sstevel@tonic-gate 	assert(opt);
43447c478bd9Sstevel@tonic-gate 
43457c478bd9Sstevel@tonic-gate 	val = strchr(opt, '=');
4346ae115bc7Smrj 	if (val != NULL) {
4347ae115bc7Smrj 		*val = '\0';
43487c478bd9Sstevel@tonic-gate 	}
43497c478bd9Sstevel@tonic-gate 
43507c478bd9Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
43517c478bd9Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
43527c478bd9Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
43537c478bd9Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
4354ae115bc7Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
4355ae115bc7Smrj 		optnum = KERNEL_CMD;
4356ae115bc7Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
4357ae115bc7Smrj 		optnum = ARGS_CMD;
43587c478bd9Sstevel@tonic-gate 	} else {
43597c478bd9Sstevel@tonic-gate 		bam_error(INVALID_ENTRY, opt);
43607c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
43617c478bd9Sstevel@tonic-gate 	}
43627c478bd9Sstevel@tonic-gate 
4363ae115bc7Smrj 	/*
4364ae115bc7Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
4365ae115bc7Smrj 	 * others cause errors
4366ae115bc7Smrj 	 */
4367ae115bc7Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
4368ae115bc7Smrj 		bam_error(INVALID_ENTRY, opt);
4369ae115bc7Smrj 		return (BAM_ERROR);
4370ae115bc7Smrj 	} else if (val != NULL) {
43717c478bd9Sstevel@tonic-gate 		*val = '=';
4372ae115bc7Smrj 	}
4373ae115bc7Smrj 
4374ae115bc7Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
4375ae115bc7Smrj 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
4376ae115bc7Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
4377ae115bc7Smrj 			(void) printf("%s\n", buf);
4378ae115bc7Smrj 		return (rv);
4379ae115bc7Smrj 	} else {
4380ae115bc7Smrj 		optval = s_strtol(val + 1);
43817c478bd9Sstevel@tonic-gate 		return (set_global(mp, menu_cmds[optnum], optval));
43827c478bd9Sstevel@tonic-gate 	}
4383ae115bc7Smrj }
43847c478bd9Sstevel@tonic-gate 
43857c478bd9Sstevel@tonic-gate /*
43867c478bd9Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
43877c478bd9Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
43887c478bd9Sstevel@tonic-gate  */
43897c478bd9Sstevel@tonic-gate static error_t
43907c478bd9Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
43917c478bd9Sstevel@tonic-gate {
43927c478bd9Sstevel@tonic-gate 	line_t *lp;
43937c478bd9Sstevel@tonic-gate 	char *arg;
43947c478bd9Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
43957c478bd9Sstevel@tonic-gate 
43967c478bd9Sstevel@tonic-gate 	assert(mp);
43977c478bd9Sstevel@tonic-gate 	assert(menu_path);
43987c478bd9Sstevel@tonic-gate 	assert(globalcmd);
43997c478bd9Sstevel@tonic-gate 
44007c478bd9Sstevel@tonic-gate 	if (mp->start == NULL) {
44017c478bd9Sstevel@tonic-gate 		if (!quiet)
44027c478bd9Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
44037c478bd9Sstevel@tonic-gate 		return (BAM_ERROR);
44047c478bd9Sstevel@tonic-gate 	}
44057c478bd9Sstevel@tonic-gate 
44067c478bd9Sstevel@tonic-gate 	done = 0;
44077c478bd9Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
44087c478bd9Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
44097c478bd9Sstevel@tonic-gate 			continue;
44107c478bd9Sstevel@tonic-gate 
44117c478bd9Sstevel@tonic-gate 		if (lp->cmd == NULL) {
44127c478bd9Sstevel@tonic-gate 			if (!quiet)
44137c478bd9Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
44147c478bd9Sstevel@tonic-gate 			continue;
44157c478bd9Sstevel@tonic-gate 		}
44167c478bd9Sstevel@tonic-gate 
44177c478bd9Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
44187c478bd9Sstevel@tonic-gate 			continue;
44197c478bd9Sstevel@tonic-gate 
44207c478bd9Sstevel@tonic-gate 		/* Found global. Check for duplicates */
44217c478bd9Sstevel@tonic-gate 		if (done && !quiet) {
44227c478bd9Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
44237c478bd9Sstevel@tonic-gate 			ret = BAM_ERROR;
44247c478bd9Sstevel@tonic-gate 		}
44257c478bd9Sstevel@tonic-gate 
44267c478bd9Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
44277c478bd9Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
44287c478bd9Sstevel@tonic-gate 		done = 1;
44297c478bd9Sstevel@tonic-gate 	}
44307c478bd9Sstevel@tonic-gate 
44317c478bd9Sstevel@tonic-gate 	if (!done && bam_verbose)
44327c478bd9Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
44337c478bd9Sstevel@tonic-gate 
44347c478bd9Sstevel@tonic-gate 	return (ret);
44357c478bd9Sstevel@tonic-gate }
44367c478bd9Sstevel@tonic-gate 
44377c478bd9Sstevel@tonic-gate static error_t
44387c478bd9Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
44397c478bd9Sstevel@tonic-gate {
44407c478bd9Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
44417c478bd9Sstevel@tonic-gate }
44427c478bd9Sstevel@tonic-gate 
44437c478bd9Sstevel@tonic-gate static void
44447c478bd9Sstevel@tonic-gate line_free(line_t *lp)
44457c478bd9Sstevel@tonic-gate {
44467c478bd9Sstevel@tonic-gate 	if (lp == NULL)
44477c478bd9Sstevel@tonic-gate 		return;
44487c478bd9Sstevel@tonic-gate 
44497c478bd9Sstevel@tonic-gate 	if (lp->cmd)
44507c478bd9Sstevel@tonic-gate 		free(lp->cmd);
44517c478bd9Sstevel@tonic-gate 	if (lp->sep)
44527c478bd9Sstevel@tonic-gate 		free(lp->sep);
44537c478bd9Sstevel@tonic-gate 	if (lp->arg)
44547c478bd9Sstevel@tonic-gate 		free(lp->arg);
44557c478bd9Sstevel@tonic-gate 	if (lp->line)
44567c478bd9Sstevel@tonic-gate 		free(lp->line);
44577c478bd9Sstevel@tonic-gate 	free(lp);
44587c478bd9Sstevel@tonic-gate }
44597c478bd9Sstevel@tonic-gate 
44607c478bd9Sstevel@tonic-gate static void
44617c478bd9Sstevel@tonic-gate linelist_free(line_t *start)
44627c478bd9Sstevel@tonic-gate {
44637c478bd9Sstevel@tonic-gate 	line_t *lp;
44647c478bd9Sstevel@tonic-gate 
44657c478bd9Sstevel@tonic-gate 	while (start) {
44667c478bd9Sstevel@tonic-gate 		lp = start;
44677c478bd9Sstevel@tonic-gate 		start = start->next;
44687c478bd9Sstevel@tonic-gate 		line_free(lp);
44697c478bd9Sstevel@tonic-gate 	}
44707c478bd9Sstevel@tonic-gate }
44717c478bd9Sstevel@tonic-gate 
44727c478bd9Sstevel@tonic-gate static void
44737c478bd9Sstevel@tonic-gate filelist_free(filelist_t *flistp)
44747c478bd9Sstevel@tonic-gate {
44757c478bd9Sstevel@tonic-gate 	linelist_free(flistp->head);
44767c478bd9Sstevel@tonic-gate 	flistp->head = NULL;
44777c478bd9Sstevel@tonic-gate 	flistp->tail = NULL;
44787c478bd9Sstevel@tonic-gate }
44797c478bd9Sstevel@tonic-gate 
44807c478bd9Sstevel@tonic-gate static void
44817c478bd9Sstevel@tonic-gate menu_free(menu_t *mp)
44827c478bd9Sstevel@tonic-gate {
44838c1b6884Sszhou 	entry_t *ent, *tmp;
44847c478bd9Sstevel@tonic-gate 	assert(mp);
44857c478bd9Sstevel@tonic-gate 
44867c478bd9Sstevel@tonic-gate 	if (mp->start)
44877c478bd9Sstevel@tonic-gate 		linelist_free(mp->start);
44888c1b6884Sszhou 	ent = mp->entries;
44898c1b6884Sszhou 	while (ent) {
44908c1b6884Sszhou 		tmp = ent;
44918c1b6884Sszhou 		ent = tmp->next;
44928c1b6884Sszhou 		free(tmp);
44938c1b6884Sszhou 	}
44947c478bd9Sstevel@tonic-gate 
44958c1b6884Sszhou 	free(mp);
44967c478bd9Sstevel@tonic-gate }
44977c478bd9Sstevel@tonic-gate 
44987c478bd9Sstevel@tonic-gate /*
44997c478bd9Sstevel@tonic-gate  * Utility routines
45007c478bd9Sstevel@tonic-gate  */
45017c478bd9Sstevel@tonic-gate 
45027c478bd9Sstevel@tonic-gate 
45037c478bd9Sstevel@tonic-gate /*
45047c478bd9Sstevel@tonic-gate  * Returns 0 on success
45057c478bd9Sstevel@tonic-gate  * Any other value indicates an error
45067c478bd9Sstevel@tonic-gate  */
45077c478bd9Sstevel@tonic-gate static int
4508986fd29aSsetje exec_cmd(char *cmdline, filelist_t *flistp)
45097c478bd9Sstevel@tonic-gate {
45107c478bd9Sstevel@tonic-gate 	char buf[BUFSIZ];
45117c478bd9Sstevel@tonic-gate 	int ret;
45127c478bd9Sstevel@tonic-gate 	FILE *ptr;
45137c478bd9Sstevel@tonic-gate 	sigset_t set;
45147c478bd9Sstevel@tonic-gate 	void (*disp)(int);
45157c478bd9Sstevel@tonic-gate 
45167c478bd9Sstevel@tonic-gate 	/*
45177c478bd9Sstevel@tonic-gate 	 * For security
45187c478bd9Sstevel@tonic-gate 	 * - only absolute paths are allowed
45197c478bd9Sstevel@tonic-gate 	 * - set IFS to space and tab
45207c478bd9Sstevel@tonic-gate 	 */
45217c478bd9Sstevel@tonic-gate 	if (*cmdline != '/') {
45227c478bd9Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
45237c478bd9Sstevel@tonic-gate 		return (-1);
45247c478bd9Sstevel@tonic-gate 	}
45257c478bd9Sstevel@tonic-gate 	(void) putenv("IFS= \t");
45267c478bd9Sstevel@tonic-gate 
45277c478bd9Sstevel@tonic-gate 	/*
45287c478bd9Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
45297c478bd9Sstevel@tonic-gate 	 * unblock it here
45307c478bd9Sstevel@tonic-gate 	 */
45317c478bd9Sstevel@tonic-gate 	(void) sigemptyset(&set);
45327c478bd9Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
45337c478bd9Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
45347c478bd9Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
45357c478bd9Sstevel@tonic-gate 		return (-1);
45367c478bd9Sstevel@tonic-gate 	}
45377c478bd9Sstevel@tonic-gate 
45387c478bd9Sstevel@tonic-gate 	/*
45397c478bd9Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
45407c478bd9Sstevel@tonic-gate 	 */
45417c478bd9Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
45427c478bd9Sstevel@tonic-gate 	if (disp == SIG_ERR) {
45437c478bd9Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
45447c478bd9Sstevel@tonic-gate 		return (-1);
45457c478bd9Sstevel@tonic-gate 	}
45467c478bd9Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
45477c478bd9Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
45487c478bd9Sstevel@tonic-gate 		return (-1);
45497c478bd9Sstevel@tonic-gate 	}
45507c478bd9Sstevel@tonic-gate 
45517c478bd9Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
45527c478bd9Sstevel@tonic-gate 	if (ptr == NULL) {
45537c478bd9Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
45547c478bd9Sstevel@tonic-gate 		return (-1);
45557c478bd9Sstevel@tonic-gate 	}
45567c478bd9Sstevel@tonic-gate 
45577c478bd9Sstevel@tonic-gate 	/*
45587c478bd9Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
45597c478bd9Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
45607c478bd9Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
45617c478bd9Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
45627c478bd9Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
45637c478bd9Sstevel@tonic-gate 	 * there is no reader process and the command dies with
45647c478bd9Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
45657c478bd9Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
45667c478bd9Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
45677c478bd9Sstevel@tonic-gate 	 * pclose().
45687c478bd9Sstevel@tonic-gate 	 *
45697c478bd9Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
45707c478bd9Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
45717c478bd9Sstevel@tonic-gate 	 * from the value returned by pclose()
45727c478bd9Sstevel@tonic-gate 	 */
4573986fd29aSsetje 	while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
4574986fd29aSsetje 		if (flistp == NULL) {
4575986fd29aSsetje 			/* s_fgets strips newlines, so insert them at the end */
4576986fd29aSsetje 			bam_print(PRINT, buf);
4577986fd29aSsetje 		} else {
4578986fd29aSsetje 			append_to_flist(flistp, buf);
45797c478bd9Sstevel@tonic-gate 		}
45807c478bd9Sstevel@tonic-gate 	}
45817c478bd9Sstevel@tonic-gate 
45827c478bd9Sstevel@tonic-gate 	ret = pclose(ptr);
45837c478bd9Sstevel@tonic-gate 	if (ret == -1) {
45847c478bd9Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
45857c478bd9Sstevel@tonic-gate 		return (-1);
45867c478bd9Sstevel@tonic-gate 	}
45877c478bd9Sstevel@tonic-gate 
45887c478bd9Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
45897c478bd9Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
45907c478bd9Sstevel@tonic-gate 	} else {
45917c478bd9Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
45927c478bd9Sstevel@tonic-gate 		return (-1);
45937c478bd9Sstevel@tonic-gate 	}
45947c478bd9Sstevel@tonic-gate }
45957c478bd9Sstevel@tonic-gate 
45967c478bd9Sstevel@tonic-gate /*
45977c478bd9Sstevel@tonic-gate  * Since this function returns -1 on error
45987c478bd9Sstevel@tonic-gate  * it cannot be used to convert -1. However,
45997c478bd9Sstevel@tonic-gate  * that is sufficient for what we need.
46007c478bd9Sstevel@tonic-gate  */
46017c478bd9Sstevel@tonic-gate static long
46027c478bd9Sstevel@tonic-gate s_strtol(char *str)
46037c478bd9Sstevel@tonic-gate {
46047c478bd9Sstevel@tonic-gate 	long l;
46057c478bd9Sstevel@tonic-gate 	char *res = NULL;
46067c478bd9Sstevel@tonic-gate 
46077c478bd9Sstevel@tonic-gate 	if (str == NULL) {
46087c478bd9Sstevel@tonic-gate 		return (-1);
46097c478bd9Sstevel@tonic-gate 	}
46107c478bd9Sstevel@tonic-gate 
46117c478bd9Sstevel@tonic-gate 	errno = 0;
46127c478bd9Sstevel@tonic-gate 	l = strtol(str, &res, 10);
46137c478bd9Sstevel@tonic-gate 	if (errno || *res != '\0') {
46147c478bd9Sstevel@tonic-gate 		return (-1);
46157c478bd9Sstevel@tonic-gate 	}
46167c478bd9Sstevel@tonic-gate 
46177c478bd9Sstevel@tonic-gate 	return (l);
46187c478bd9Sstevel@tonic-gate }
46197c478bd9Sstevel@tonic-gate 
46207c478bd9Sstevel@tonic-gate /*
46217c478bd9Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
46227c478bd9Sstevel@tonic-gate  */
46237c478bd9Sstevel@tonic-gate static int
46247c478bd9Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
46257c478bd9Sstevel@tonic-gate {
46267c478bd9Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
46277c478bd9Sstevel@tonic-gate 
46287c478bd9Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
46297c478bd9Sstevel@tonic-gate 	return (fputs(linebuf, fp));
46307c478bd9Sstevel@tonic-gate }
46317c478bd9Sstevel@tonic-gate 
46327c478bd9Sstevel@tonic-gate /*
46337c478bd9Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
46347c478bd9Sstevel@tonic-gate  */
4635ae115bc7Smrj char *
46367c478bd9Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
46377c478bd9Sstevel@tonic-gate {
46387c478bd9Sstevel@tonic-gate 	int n;
46397c478bd9Sstevel@tonic-gate 
46407c478bd9Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
46417c478bd9Sstevel@tonic-gate 	if (buf) {
46427c478bd9Sstevel@tonic-gate 		n = strlen(buf);
46437c478bd9Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
46447c478bd9Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
46457c478bd9Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
46467c478bd9Sstevel@tonic-gate 	}
46477c478bd9Sstevel@tonic-gate 
46487c478bd9Sstevel@tonic-gate 	return (buf);
46497c478bd9Sstevel@tonic-gate }
46507c478bd9Sstevel@tonic-gate 
4651ae115bc7Smrj void *
46527c478bd9Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
46537c478bd9Sstevel@tonic-gate {
46547c478bd9Sstevel@tonic-gate 	void *ptr;
46557c478bd9Sstevel@tonic-gate 
46567c478bd9Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
46577c478bd9Sstevel@tonic-gate 	if (ptr == NULL) {
46587c478bd9Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
46597c478bd9Sstevel@tonic-gate 		bam_exit(1);
46607c478bd9Sstevel@tonic-gate 	}
46617c478bd9Sstevel@tonic-gate 	return (ptr);
46627c478bd9Sstevel@tonic-gate }
46637c478bd9Sstevel@tonic-gate 
4664ae115bc7Smrj void *
4665ae115bc7Smrj s_realloc(void *ptr, size_t sz)
4666ae115bc7Smrj {
4667ae115bc7Smrj 	ptr = realloc(ptr, sz);
4668ae115bc7Smrj 	if (ptr == NULL) {
4669ae115bc7Smrj 		bam_error(NO_MEM, sz);
4670ae115bc7Smrj 		bam_exit(1);
4671ae115bc7Smrj 	}
4672ae115bc7Smrj 	return (ptr);
4673ae115bc7Smrj }
4674ae115bc7Smrj 
46757c478bd9Sstevel@tonic-gate static char *
46767c478bd9Sstevel@tonic-gate s_strdup(char *str)
46777c478bd9Sstevel@tonic-gate {
46787c478bd9Sstevel@tonic-gate 	char *ptr;
46797c478bd9Sstevel@tonic-gate 
46807c478bd9Sstevel@tonic-gate 	if (str == NULL)
46817c478bd9Sstevel@tonic-gate 		return (NULL);
46827c478bd9Sstevel@tonic-gate 
46837c478bd9Sstevel@tonic-gate 	ptr = strdup(str);
46847c478bd9Sstevel@tonic-gate 	if (ptr == NULL) {
46857c478bd9Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
46867c478bd9Sstevel@tonic-gate 		bam_exit(1);
46877c478bd9Sstevel@tonic-gate 	}
46887c478bd9Sstevel@tonic-gate 	return (ptr);
46897c478bd9Sstevel@tonic-gate }
46907c478bd9Sstevel@tonic-gate 
46917c478bd9Sstevel@tonic-gate /*
46927c478bd9Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
46937c478bd9Sstevel@tonic-gate  * Returns 0 otherwise
46947c478bd9Sstevel@tonic-gate  */
46957c478bd9Sstevel@tonic-gate static int
46967c478bd9Sstevel@tonic-gate is_amd64(void)
46977c478bd9Sstevel@tonic-gate {
46987c478bd9Sstevel@tonic-gate 	static int amd64 = -1;
46997c478bd9Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
47007c478bd9Sstevel@tonic-gate 
47017c478bd9Sstevel@tonic-gate 	if (amd64 != -1)
47027c478bd9Sstevel@tonic-gate 		return (amd64);
47037c478bd9Sstevel@tonic-gate 
4704d876c67dSjg 	if (bam_alt_platform) {
4705d876c67dSjg 		if (strcmp(bam_platform, "i86pc") == 0) {
47067c478bd9Sstevel@tonic-gate 			amd64 = 1;		/* diskless server */
4707d876c67dSjg 		}
4708d876c67dSjg 	} else {
4709d876c67dSjg 		if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
4710d876c67dSjg 		    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
4711d876c67dSjg 			amd64 = 1;
4712d876c67dSjg 		} else if (strstr(isabuf, "i386") == NULL) {
4713d876c67dSjg 			amd64 = 1;		/* diskless server */
4714d876c67dSjg 		}
4715d876c67dSjg 	}
4716d876c67dSjg 	if (amd64 == -1)
47177c478bd9Sstevel@tonic-gate 		amd64 = 0;
47187c478bd9Sstevel@tonic-gate 
47197c478bd9Sstevel@tonic-gate 	return (amd64);
47207c478bd9Sstevel@tonic-gate }
47217c478bd9Sstevel@tonic-gate 
4722986fd29aSsetje static int
4723986fd29aSsetje is_sun4u(void)
4724986fd29aSsetje {
4725d876c67dSjg 	static int sun4u = -1;
4726986fd29aSsetje 	char mbuf[257];	/* from sysinfo(2) manpage */
4727986fd29aSsetje 
4728d876c67dSjg 	if (sun4u != -1)
4729d876c67dSjg 		return (sun4u);
4730d876c67dSjg 
4731d876c67dSjg 	if (bam_alt_platform) {
4732d876c67dSjg 		if (strcmp(bam_platform, "sun4u") == 0) {
4733986fd29aSsetje 			sun4u = 1;
4734d876c67dSjg 		}
4735d876c67dSjg 	} else {
4736d876c67dSjg 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 &&
4737d876c67dSjg 		    strncmp(mbuf, "sun4u", strlen("sun4u")) == 0) {
4738d876c67dSjg 			sun4u = 1;
4739d876c67dSjg 		}
4740d876c67dSjg 	}
4741d876c67dSjg 	if (sun4u == -1)
4742d876c67dSjg 		sun4u = 0;
4743986fd29aSsetje 
4744986fd29aSsetje 	return (sun4u);
4745986fd29aSsetje }
4746986fd29aSsetje 
4747986fd29aSsetje static int
4748986fd29aSsetje is_sun4v(void)
4749986fd29aSsetje {
4750d876c67dSjg 	static int sun4v = -1;
4751986fd29aSsetje 	char mbuf[257];	/* from sysinfo(2) manpage */
4752986fd29aSsetje 
4753d876c67dSjg 	if (sun4v != -1)
4754d876c67dSjg 		return (sun4v);
4755d876c67dSjg 
4756d876c67dSjg 	if (bam_alt_platform) {
4757d876c67dSjg 		if (strcmp(bam_platform, "sun4v") == 0) {
4758986fd29aSsetje 			sun4v = 1;
4759d876c67dSjg 		}
4760d876c67dSjg 	} else {
4761d876c67dSjg 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 &&
4762d876c67dSjg 		    strncmp(mbuf, "sun4v", strlen("sun4v")) == 0) {
4763d876c67dSjg 			sun4v = 1;
4764d876c67dSjg 		}
4765d876c67dSjg 	}
4766d876c67dSjg 	if (sun4v == -1)
4767d876c67dSjg 		sun4v = 0;
4768986fd29aSsetje 
4769986fd29aSsetje 	return (sun4v);
4770986fd29aSsetje }
4771986fd29aSsetje 
4772986fd29aSsetje 
47737c478bd9Sstevel@tonic-gate static void
47747c478bd9Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
47757c478bd9Sstevel@tonic-gate {
47767c478bd9Sstevel@tonic-gate 	line_t *lp;
47777c478bd9Sstevel@tonic-gate 
47787c478bd9Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
47797c478bd9Sstevel@tonic-gate 	lp->line = s_strdup(s);
47807c478bd9Sstevel@tonic-gate 	if (flistp->head == NULL)
47817c478bd9Sstevel@tonic-gate 		flistp->head = lp;
47827c478bd9Sstevel@tonic-gate 	else
47837c478bd9Sstevel@tonic-gate 		flistp->tail->next = lp;
47847c478bd9Sstevel@tonic-gate 	flistp->tail = lp;
47857c478bd9Sstevel@tonic-gate }
47862449e17fSsherrym 
4787986fd29aSsetje #if !defined(_OPB)
47882449e17fSsherrym 
47892449e17fSsherrym UCODE_VENDORS;
47902449e17fSsherrym 
47912449e17fSsherrym /*ARGSUSED*/
47922449e17fSsherrym static void
47932449e17fSsherrym ucode_install(char *root)
47942449e17fSsherrym {
47952449e17fSsherrym 	int i;
47962449e17fSsherrym 
47972449e17fSsherrym 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
47982449e17fSsherrym 		int cmd_len = PATH_MAX + 256;
47992449e17fSsherrym 		char cmd[PATH_MAX + 256];
48002449e17fSsherrym 		char file[PATH_MAX];
48012449e17fSsherrym 		char timestamp[PATH_MAX];
48022449e17fSsherrym 		struct stat fstatus, tstatus;
48032449e17fSsherrym 		struct utimbuf u_times;
48042449e17fSsherrym 
48052449e17fSsherrym 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt",
48062449e17fSsherrym 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr);
48072449e17fSsherrym 
48082449e17fSsherrym 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
48092449e17fSsherrym 			continue;
48102449e17fSsherrym 
48112449e17fSsherrym 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
48122449e17fSsherrym 
48132449e17fSsherrym 		if (stat(timestamp, &tstatus) == 0 &&
48142449e17fSsherrym 		    fstatus.st_mtime <= tstatus.st_mtime)
48152449e17fSsherrym 			continue;
48162449e17fSsherrym 
48172449e17fSsherrym 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
48182449e17fSsherrym 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
48192449e17fSsherrym 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
48202449e17fSsherrym 		if (system(cmd) != 0)
48212449e17fSsherrym 			return;
48222449e17fSsherrym 
48232449e17fSsherrym 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
48242449e17fSsherrym 			return;
48252449e17fSsherrym 
48262449e17fSsherrym 		u_times.actime = fstatus.st_atime;
48272449e17fSsherrym 		u_times.modtime = fstatus.st_mtime;
48282449e17fSsherrym 		(void) utime(timestamp, &u_times);
48292449e17fSsherrym 	}
48302449e17fSsherrym }
48312449e17fSsherrym #endif
4832