xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 5648:161f8007cab9)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51746Svikram  * Common Development and Distribution License (the "License").
61746Svikram  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
223381Svikram  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * bootadm(1M) is a new utility for managing bootability of
300Sstevel@tonic-gate  * Solaris *Newboot* environments. It has two primary tasks:
310Sstevel@tonic-gate  * 	- Allow end users to manage bootability of Newboot Solaris instances
320Sstevel@tonic-gate  *	- Provide services to other subsystems in Solaris (primarily Install)
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /* Headers */
360Sstevel@tonic-gate #include <stdio.h>
370Sstevel@tonic-gate #include <errno.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <string.h>
400Sstevel@tonic-gate #include <unistd.h>
410Sstevel@tonic-gate #include <sys/types.h>
420Sstevel@tonic-gate #include <sys/stat.h>
430Sstevel@tonic-gate #include <stdarg.h>
440Sstevel@tonic-gate #include <limits.h>
450Sstevel@tonic-gate #include <signal.h>
460Sstevel@tonic-gate #include <sys/wait.h>
470Sstevel@tonic-gate #include <sys/mnttab.h>
480Sstevel@tonic-gate #include <sys/statvfs.h>
490Sstevel@tonic-gate #include <libnvpair.h>
500Sstevel@tonic-gate #include <ftw.h>
510Sstevel@tonic-gate #include <fcntl.h>
520Sstevel@tonic-gate #include <strings.h>
534581Ssherrym #include <utime.h>
540Sstevel@tonic-gate #include <sys/systeminfo.h>
550Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
562334Ssetje #include <sys/param.h>
57*5648Ssetje 
58*5648Ssetje #if !defined(_OPB)
594581Ssherrym #include <sys/ucode.h>
604581Ssherrym #endif
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #include <pwd.h>
630Sstevel@tonic-gate #include <grp.h>
640Sstevel@tonic-gate #include <device_info.h>
650Sstevel@tonic-gate 
660Sstevel@tonic-gate #include <locale.h>
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #include <assert.h>
690Sstevel@tonic-gate 
700Sstevel@tonic-gate #include "message.h"
713446Smrj #include "bootadm.h"
720Sstevel@tonic-gate 
730Sstevel@tonic-gate #ifndef TEXT_DOMAIN
740Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
750Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /* Type definitions */
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /* Primary subcmds */
800Sstevel@tonic-gate typedef enum {
810Sstevel@tonic-gate 	BAM_MENU = 3,
820Sstevel@tonic-gate 	BAM_ARCHIVE
830Sstevel@tonic-gate } subcmd_t;
840Sstevel@tonic-gate 
850Sstevel@tonic-gate typedef enum {
860Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
870Sstevel@tonic-gate     OPT_REQ,		/* option required */
880Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
890Sstevel@tonic-gate } option_t;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate typedef struct {
920Sstevel@tonic-gate 	char	*subcmd;
930Sstevel@tonic-gate 	option_t option;
940Sstevel@tonic-gate 	error_t (*handler)();
952115Svikram 	int	unpriv;			/* is this an unprivileged command */
960Sstevel@tonic-gate } subcmd_defn_t;
970Sstevel@tonic-gate 
980Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
990Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
1000Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
1030Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
1040Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1050Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
106621Svikram #define	STUBBOOT		"/stubboot"
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate /* lock related */
1090Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1100Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1110Sstevel@tonic-gate 
112*5648Ssetje #define	CREATE_RAMDISK		"boot/solaris/bin/create_ramdisk"
113*5648Ssetje #define	CREATE_DISKMAP		"boot/solaris/bin/create_diskmap"
114*5648Ssetje #define	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
1150Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
1160Sstevel@tonic-gate 
117316Svikram #define	GRUB_slice		"/etc/lu/GRUB_slice"
118316Svikram #define	GRUB_root		"/etc/lu/GRUB_root"
119316Svikram #define	GRUB_backup_menu	"/etc/lu/GRUB_backup_menu"
120316Svikram #define	GRUB_slice_mntpt	"/tmp/GRUB_slice_mntpt"
121316Svikram #define	LU_ACTIVATE_FILE	"/etc/lu/DelayUpdate/activate.sh"
1221746Svikram #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
1231746Svikram #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
124316Svikram 
125316Svikram #define	INSTALLGRUB		"/sbin/installgrub"
126316Svikram #define	STAGE1			"/boot/grub/stage1"
127316Svikram #define	STAGE2			"/boot/grub/stage2"
128316Svikram 
1290Sstevel@tonic-gate /*
1304493Snadkarni  * The following two defines are used to detect and create the correct
1314493Snadkarni  * boot archive  when safemode patching is underway.  LOFS_PATCH_FILE is a
1324493Snadkarni  * contracted private interface between bootadm and the install
1334493Snadkarni  * consolidation.  It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE
1344493Snadkarni  * is applied.
1354493Snadkarni  */
1364493Snadkarni 
1374493Snadkarni #define	LOFS_PATCH_FILE		"/var/run/.patch_loopback_mode"
1384493Snadkarni #define	LOFS_PATCH_MNT		"/var/run/.patch_root_loopbackmnt"
1394493Snadkarni 
1404493Snadkarni /*
1410Sstevel@tonic-gate  * Default file attributes
1420Sstevel@tonic-gate  */
1430Sstevel@tonic-gate #define	DEFAULT_DEV_MODE	0644	/* default permissions */
1440Sstevel@tonic-gate #define	DEFAULT_DEV_UID		0	/* user root */
1450Sstevel@tonic-gate #define	DEFAULT_DEV_GID		3	/* group sys */
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate /*
1480Sstevel@tonic-gate  * Menu related
1490Sstevel@tonic-gate  * menu_cmd_t and menu_cmds must be kept in sync
1500Sstevel@tonic-gate  */
1513446Smrj char *menu_cmds[] = {
1520Sstevel@tonic-gate 	"default",	/* DEFAULT_CMD */
1530Sstevel@tonic-gate 	"timeout",	/* TIMEOUT_CMD */
1540Sstevel@tonic-gate 	"title",	/* TITLE_CMD */
1550Sstevel@tonic-gate 	"root",		/* ROOT_CMD */
1560Sstevel@tonic-gate 	"kernel",	/* KERNEL_CMD */
1573446Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1580Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
1593446Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1600Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1610Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
1623446Smrj 	"chainloader",	/* CHAINLOADER_CMD */
1633446Smrj 	"args",		/* ARGS_CMD */
1640Sstevel@tonic-gate 	NULL
1650Sstevel@tonic-gate };
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate #define	OPT_ENTRY_NUM	"entry"
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate /*
1700Sstevel@tonic-gate  * archive related
1710Sstevel@tonic-gate  */
1720Sstevel@tonic-gate typedef struct {
1730Sstevel@tonic-gate 	line_t *head;
1740Sstevel@tonic-gate 	line_t *tail;
1750Sstevel@tonic-gate } filelist_t;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
1780Sstevel@tonic-gate #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
1810Sstevel@tonic-gate #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
1820Sstevel@tonic-gate #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
1830Sstevel@tonic-gate #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /* Globals */
1863446Smrj int bam_verbose;
1873446Smrj int bam_force;
1880Sstevel@tonic-gate static char *prog;
1890Sstevel@tonic-gate static subcmd_t bam_cmd;
1900Sstevel@tonic-gate static char *bam_root;
1910Sstevel@tonic-gate static int bam_rootlen;
1920Sstevel@tonic-gate static int bam_root_readonly;
193621Svikram static int bam_alt_root;
1940Sstevel@tonic-gate static char *bam_subcmd;
1950Sstevel@tonic-gate static char *bam_opt;
1960Sstevel@tonic-gate static int bam_debug;
1970Sstevel@tonic-gate static char **bam_argv;
1980Sstevel@tonic-gate static int bam_argc;
1990Sstevel@tonic-gate static int bam_check;
2000Sstevel@tonic-gate static int bam_smf_check;
2010Sstevel@tonic-gate static int bam_lock_fd = -1;
2020Sstevel@tonic-gate static char rootbuf[PATH_MAX] = "/";
203316Svikram static int bam_update_all;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate /* function prototypes */
206*5648Ssetje static void parse_args_internal(int, char *[]);
207*5648Ssetje static void parse_args(int, char *argv[]);
208*5648Ssetje static error_t bam_menu(char *, char *, int, char *[]);
209*5648Ssetje static error_t bam_archive(char *, char *);
210*5648Ssetje 
211*5648Ssetje static void bam_print(char *, ...);
212*5648Ssetje static void bam_exit(int);
2130Sstevel@tonic-gate static void bam_lock(void);
2140Sstevel@tonic-gate static void bam_unlock(void);
2150Sstevel@tonic-gate 
216*5648Ssetje static int exec_cmd(char *, filelist_t *);
217*5648Ssetje static error_t read_globals(menu_t *, char *, char *, int);
218*5648Ssetje 
219*5648Ssetje static menu_t *menu_read(char *);
220*5648Ssetje static error_t menu_write(char *, menu_t *);
221*5648Ssetje static void linelist_free(line_t *);
222*5648Ssetje static void menu_free(menu_t *);
223*5648Ssetje static void line_free(line_t *);
224*5648Ssetje static void filelist_free(filelist_t *);
225*5648Ssetje static error_t list2file(char *, char *, char *, line_t *);
226*5648Ssetje static error_t list_entry(menu_t *, char *, char *);
227*5648Ssetje static error_t delete_all_entries(menu_t *, char *, char *);
228*5648Ssetje static error_t update_entry(menu_t *, char *, char *);
229*5648Ssetje static error_t update_temp(menu_t *, char *, char *);
230*5648Ssetje 
231*5648Ssetje static error_t update_archive(char *, char *);
232*5648Ssetje static error_t list_archive(char *, char *);
233*5648Ssetje static error_t update_all(char *, char *);
234*5648Ssetje static error_t read_list(char *, filelist_t *);
235*5648Ssetje static error_t set_global(menu_t *, char *, int);
236*5648Ssetje static error_t set_option(menu_t *, char *, char *);
237*5648Ssetje static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
238*5648Ssetje static char *expand_path(const char *);
239*5648Ssetje 
240*5648Ssetje static long s_strtol(char *);
241*5648Ssetje static int s_fputs(char *, FILE *);
242*5648Ssetje 
243*5648Ssetje static char *s_strdup(char *);
2440Sstevel@tonic-gate static int is_readonly(char *);
2450Sstevel@tonic-gate static int is_amd64(void);
246*5648Ssetje static int is_sun4u(void);
247*5648Ssetje static int is_sun4v(void);
2480Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2490Sstevel@tonic-gate 
250*5648Ssetje #if !defined(_OPB)
2514581Ssherrym static void ucode_install();
2524581Ssherrym #endif
2534581Ssherrym 
2540Sstevel@tonic-gate /* Menu related sub commands */
2550Sstevel@tonic-gate static subcmd_defn_t menu_subcmds[] = {
2562115Svikram 	"set_option",		OPT_OPTIONAL,	set_option, 0,	/* PUB */
2572115Svikram 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
2582115Svikram 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
2592115Svikram 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
2602115Svikram 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
2613446Smrj 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
2622115Svikram 	NULL,			0,		NULL, 0	/* must be last */
2630Sstevel@tonic-gate };
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate /* Archive related sub commands */
2660Sstevel@tonic-gate static subcmd_defn_t arch_subcmds[] = {
2672115Svikram 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
2682115Svikram 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
2692115Svikram 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
2702115Svikram 	NULL,			0,		NULL, 0	/* must be last */
2710Sstevel@tonic-gate };
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate static struct {
2740Sstevel@tonic-gate 	nvlist_t *new_nvlp;
2750Sstevel@tonic-gate 	nvlist_t *old_nvlp;
2760Sstevel@tonic-gate 	int need_update;
2770Sstevel@tonic-gate } walk_arg;
2780Sstevel@tonic-gate 
2792334Ssetje 
2802345Ssetje struct safefile {
2812334Ssetje 	char *name;
2822334Ssetje 	struct safefile *next;
2832334Ssetje };
2842334Ssetje 
2853446Smrj static struct safefile *safefiles = NULL;
2862334Ssetje #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
2872334Ssetje 
2880Sstevel@tonic-gate static void
2890Sstevel@tonic-gate usage(void)
2900Sstevel@tonic-gate {
2910Sstevel@tonic-gate 	(void) fprintf(stderr, "USAGE:\n");
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	/* archive usage */
2950Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s update-archive [-vn] [-R altroot]\n",
2960Sstevel@tonic-gate 	    prog);
2970Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-archive [-R altroot]\n", prog);
298*5648Ssetje #if !defined(_OPB)
2990Sstevel@tonic-gate 	/* x86 only */
3000Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
3010Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
3020Sstevel@tonic-gate #endif
3030Sstevel@tonic-gate }
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate int
3060Sstevel@tonic-gate main(int argc, char *argv[])
3070Sstevel@tonic-gate {
3080Sstevel@tonic-gate 	error_t ret;
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
3110Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
3140Sstevel@tonic-gate 		prog = argv[0];
3150Sstevel@tonic-gate 	} else {
3160Sstevel@tonic-gate 		prog++;
3170Sstevel@tonic-gate 	}
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	/*
3210Sstevel@tonic-gate 	 * Don't depend on caller's umask
3220Sstevel@tonic-gate 	 */
3230Sstevel@tonic-gate 	(void) umask(0022);
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	parse_args(argc, argv);
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	switch (bam_cmd) {
3280Sstevel@tonic-gate 		case BAM_MENU:
3290Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
3300Sstevel@tonic-gate 			break;
3310Sstevel@tonic-gate 		case BAM_ARCHIVE:
3320Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
3330Sstevel@tonic-gate 			break;
3340Sstevel@tonic-gate 		default:
3350Sstevel@tonic-gate 			usage();
3360Sstevel@tonic-gate 			bam_exit(1);
3370Sstevel@tonic-gate 	}
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
3400Sstevel@tonic-gate 		bam_exit(1);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	bam_unlock();
3430Sstevel@tonic-gate 	return (0);
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate /*
3470Sstevel@tonic-gate  * Equivalence of public and internal commands:
3480Sstevel@tonic-gate  *	update-archive  -- -a update
3490Sstevel@tonic-gate  *	list-archive	-- -a list
3500Sstevel@tonic-gate  *	set-menu	-- -m set_option
3510Sstevel@tonic-gate  *	list-menu	-- -m list_entry
3520Sstevel@tonic-gate  *	update-menu	-- -m update_entry
3530Sstevel@tonic-gate  */
3540Sstevel@tonic-gate static struct cmd_map {
3550Sstevel@tonic-gate 	char *bam_cmdname;
3560Sstevel@tonic-gate 	int bam_cmd;
3570Sstevel@tonic-gate 	char *bam_subcmd;
3580Sstevel@tonic-gate } cmd_map[] = {
3590Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
3600Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
3610Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
3620Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
3630Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
3640Sstevel@tonic-gate 	{ NULL,			0,		NULL}
3650Sstevel@tonic-gate };
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate /*
3680Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
3690Sstevel@tonic-gate  */
3700Sstevel@tonic-gate static void
3710Sstevel@tonic-gate parse_args(int argc, char *argv[])
3720Sstevel@tonic-gate {
3730Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	/* command conforming to the final spec */
3760Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
3770Sstevel@tonic-gate 		/*
3780Sstevel@tonic-gate 		 * Map commands to internal table.
3790Sstevel@tonic-gate 		 */
3800Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
3810Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
3820Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
3830Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
3840Sstevel@tonic-gate 				break;
3850Sstevel@tonic-gate 			}
3860Sstevel@tonic-gate 			cmp++;
3870Sstevel@tonic-gate 		}
3880Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
3890Sstevel@tonic-gate 			usage();
3900Sstevel@tonic-gate 			bam_exit(1);
3910Sstevel@tonic-gate 		}
3920Sstevel@tonic-gate 		argc--;
3930Sstevel@tonic-gate 		argv++;
3940Sstevel@tonic-gate 	}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	parse_args_internal(argc, argv);
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
4010Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
4020Sstevel@tonic-gate  *	-a update	-- update-archive
4030Sstevel@tonic-gate  *	-a list		-- list-archive
4040Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
4050Sstevel@tonic-gate  *	-m update_entry	-- update-menu
4060Sstevel@tonic-gate  *	-m list_entry	-- list-menu
4070Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
4080Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
4090Sstevel@tonic-gate  */
4100Sstevel@tonic-gate static void
4110Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
4120Sstevel@tonic-gate {
4130Sstevel@tonic-gate 	int c, error;
4140Sstevel@tonic-gate 	extern char *optarg;
4150Sstevel@tonic-gate 	extern int optind, opterr;
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	/* Suppress error message from getopt */
4180Sstevel@tonic-gate 	opterr = 0;
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 	error = 0;
4213467Srscott 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:")) != -1) {
4220Sstevel@tonic-gate 		switch (c) {
4230Sstevel@tonic-gate 		case 'a':
4240Sstevel@tonic-gate 			if (bam_cmd) {
4250Sstevel@tonic-gate 				error = 1;
4260Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4270Sstevel@tonic-gate 			}
4280Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
4290Sstevel@tonic-gate 			bam_subcmd = optarg;
4300Sstevel@tonic-gate 			break;
4310Sstevel@tonic-gate 		case 'd':
4320Sstevel@tonic-gate 			if (bam_debug) {
4330Sstevel@tonic-gate 				error = 1;
4340Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4350Sstevel@tonic-gate 			}
4360Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
4370Sstevel@tonic-gate 			break;
4380Sstevel@tonic-gate 		case 'f':
4390Sstevel@tonic-gate 			if (bam_force) {
4400Sstevel@tonic-gate 				error = 1;
4410Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4420Sstevel@tonic-gate 			}
4430Sstevel@tonic-gate 			bam_force = 1;
4440Sstevel@tonic-gate 			break;
4450Sstevel@tonic-gate 		case 'm':
4460Sstevel@tonic-gate 			if (bam_cmd) {
4470Sstevel@tonic-gate 				error = 1;
4480Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4490Sstevel@tonic-gate 			}
4500Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
4510Sstevel@tonic-gate 			bam_subcmd = optarg;
4520Sstevel@tonic-gate 			break;
4530Sstevel@tonic-gate 		case 'n':
4540Sstevel@tonic-gate 			if (bam_check) {
4550Sstevel@tonic-gate 				error = 1;
4560Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4570Sstevel@tonic-gate 			}
4580Sstevel@tonic-gate 			bam_check = 1;
4590Sstevel@tonic-gate 			break;
4600Sstevel@tonic-gate 		case 'o':
4610Sstevel@tonic-gate 			if (bam_opt) {
4620Sstevel@tonic-gate 				error = 1;
4630Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4640Sstevel@tonic-gate 			}
4650Sstevel@tonic-gate 			bam_opt = optarg;
4660Sstevel@tonic-gate 			break;
4670Sstevel@tonic-gate 		case 'v':
4680Sstevel@tonic-gate 			if (bam_verbose) {
4690Sstevel@tonic-gate 				error = 1;
4700Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4710Sstevel@tonic-gate 			}
4720Sstevel@tonic-gate 			bam_verbose = 1;
4730Sstevel@tonic-gate 			break;
474662Sszhou 		case 'C':
475662Sszhou 			bam_smf_check = 1;
476662Sszhou 			break;
4770Sstevel@tonic-gate 		case 'R':
4780Sstevel@tonic-gate 			if (bam_root) {
4790Sstevel@tonic-gate 				error = 1;
4800Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4810Sstevel@tonic-gate 				break;
4820Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
4830Sstevel@tonic-gate 				error = 1;
4840Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
4850Sstevel@tonic-gate 				    strerror(errno));
4860Sstevel@tonic-gate 				break;
4870Sstevel@tonic-gate 			}
488621Svikram 			bam_alt_root = 1;
4890Sstevel@tonic-gate 			bam_root = rootbuf;
4900Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
4910Sstevel@tonic-gate 			break;
4920Sstevel@tonic-gate 		case '?':
4930Sstevel@tonic-gate 			error = 1;
4940Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
4950Sstevel@tonic-gate 			break;
4960Sstevel@tonic-gate 		default :
4970Sstevel@tonic-gate 			error = 1;
4980Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
4990Sstevel@tonic-gate 			break;
5000Sstevel@tonic-gate 		}
5010Sstevel@tonic-gate 	}
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 	/*
5040Sstevel@tonic-gate 	 * A command option must be specfied
5050Sstevel@tonic-gate 	 */
5060Sstevel@tonic-gate 	if (!bam_cmd) {
5070Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
5080Sstevel@tonic-gate 			usage();
5090Sstevel@tonic-gate 			bam_exit(0);
5100Sstevel@tonic-gate 		}
5110Sstevel@tonic-gate 		bam_error(NEED_CMD);
5120Sstevel@tonic-gate 		error = 1;
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	if (error) {
5160Sstevel@tonic-gate 		usage();
5170Sstevel@tonic-gate 		bam_exit(1);
5180Sstevel@tonic-gate 	}
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	if (optind > argc) {
5210Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
5220Sstevel@tonic-gate 		bam_exit(1);
5230Sstevel@tonic-gate 	} else if (optind < argc) {
5240Sstevel@tonic-gate 		bam_argv = &argv[optind];
5250Sstevel@tonic-gate 		bam_argc = argc - optind;
5260Sstevel@tonic-gate 	}
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	/*
5290Sstevel@tonic-gate 	 * -n implies verbose mode
5300Sstevel@tonic-gate 	 */
5310Sstevel@tonic-gate 	if (bam_check)
5320Sstevel@tonic-gate 		bam_verbose = 1;
5330Sstevel@tonic-gate }
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate static error_t
5360Sstevel@tonic-gate check_subcmd_and_options(
5370Sstevel@tonic-gate 	char *subcmd,
5380Sstevel@tonic-gate 	char *opt,
5390Sstevel@tonic-gate 	subcmd_defn_t *table,
5400Sstevel@tonic-gate 	error_t (**fp)())
5410Sstevel@tonic-gate {
5420Sstevel@tonic-gate 	int i;
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	if (subcmd == NULL) {
5450Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
5460Sstevel@tonic-gate 		return (BAM_ERROR);
5470Sstevel@tonic-gate 	}
5480Sstevel@tonic-gate 
5492115Svikram 	if (bam_argc != 0 || bam_argv) {
5502115Svikram 		if (strcmp(subcmd, "set_option") != 0 || bam_argc != 1) {
5512115Svikram 			bam_error(TRAILING_ARGS);
5522115Svikram 			usage();
5532115Svikram 			return (BAM_ERROR);
5542115Svikram 		}
5552115Svikram 	}
5562115Svikram 
5570Sstevel@tonic-gate 	if (bam_root == NULL) {
5580Sstevel@tonic-gate 		bam_root = rootbuf;
5590Sstevel@tonic-gate 		bam_rootlen = 1;
5600Sstevel@tonic-gate 	}
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	/* verify that subcmd is valid */
5630Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
5640Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
5650Sstevel@tonic-gate 			break;
5660Sstevel@tonic-gate 	}
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
5690Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
5700Sstevel@tonic-gate 		return (BAM_ERROR);
5710Sstevel@tonic-gate 	}
5720Sstevel@tonic-gate 
5732115Svikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
5742115Svikram 		bam_error(MUST_BE_ROOT);
5752115Svikram 		return (BAM_ERROR);
5762115Svikram 	}
5772115Svikram 
5782115Svikram 	/*
5792115Svikram 	 * Currently only privileged commands need a lock
5802115Svikram 	 */
5812115Svikram 	if (table[i].unpriv == 0)
5822115Svikram 		bam_lock();
5832115Svikram 
5840Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
5850Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
5860Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
5870Sstevel@tonic-gate 			if (opt)
5880Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
5890Sstevel@tonic-gate 			else
5900Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
5910Sstevel@tonic-gate 			return (BAM_ERROR);
5920Sstevel@tonic-gate 		}
5930Sstevel@tonic-gate 	}
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	*fp = table[i].handler;
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	return (BAM_SUCCESS);
5980Sstevel@tonic-gate }
5990Sstevel@tonic-gate 
600316Svikram 
601316Svikram static char *
602621Svikram mount_grub_slice(int *mnted, char **physlice, char **logslice, char **fs_type)
603316Svikram {
604316Svikram 	struct extmnttab mnt;
605316Svikram 	struct stat sb;
606316Svikram 	char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32];
607348Svikram 	char cmd[PATH_MAX];
608316Svikram 	char *mntpt;
609316Svikram 	int p, l, f;
610316Svikram 	FILE *fp;
611316Svikram 
612316Svikram 	assert(mnted);
613316Svikram 	*mnted = 0;
614316Svikram 
615316Svikram 	/*
616621Svikram 	 * physlice, logslice, fs_type  args may be NULL
617316Svikram 	 */
618316Svikram 	if (physlice)
619316Svikram 		*physlice = NULL;
620621Svikram 	if (logslice)
621621Svikram 		*logslice = NULL;
622621Svikram 	if (fs_type)
623621Svikram 		*fs_type = NULL;
624316Svikram 
625316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
626316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
627316Svikram 		return (NULL);
628316Svikram 	}
629316Svikram 
630316Svikram 	fp = fopen(GRUB_slice, "r");
631316Svikram 	if (fp == NULL) {
632316Svikram 		bam_error(OPEN_FAIL, GRUB_slice, strerror(errno));
633316Svikram 		return (NULL);
634316Svikram 	}
635316Svikram 
636316Svikram 	dev[0] = fstype[0] = phys[0] = '\0';
637316Svikram 	p = sizeof ("PHYS_SLICE=") - 1;
638316Svikram 	l = sizeof ("LOG_SLICE=") - 1;
639316Svikram 	f = sizeof ("LOG_FSTYP=") - 1;
640316Svikram 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
641316Svikram 		if (strncmp(buf, "PHYS_SLICE=", p) == 0) {
642316Svikram 			(void) strlcpy(phys, buf + p, sizeof (phys));
643316Svikram 			continue;
644316Svikram 		}
645316Svikram 		if (strncmp(buf, "LOG_SLICE=", l) == 0) {
646316Svikram 			(void) strlcpy(dev, buf + l, sizeof (dev));
647316Svikram 			continue;
648316Svikram 		}
649316Svikram 		if (strncmp(buf, "LOG_FSTYP=", f) == 0) {
650316Svikram 			(void) strlcpy(fstype, buf + f, sizeof (fstype));
651316Svikram 			continue;
652316Svikram 		}
653316Svikram 	}
654316Svikram 	(void) fclose(fp);
655316Svikram 
656316Svikram 	if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') {
657316Svikram 		bam_error(BAD_SLICE_FILE, GRUB_slice);
658316Svikram 		return (NULL);
659316Svikram 	}
660316Svikram 
661316Svikram 	if (physlice) {
662316Svikram 		*physlice = s_strdup(phys);
663316Svikram 	}
664621Svikram 	if (logslice) {
665621Svikram 		*logslice = s_strdup(dev);
666621Svikram 	}
667621Svikram 	if (fs_type) {
668621Svikram 		*fs_type = s_strdup(fstype);
669621Svikram 	}
670316Svikram 
671316Svikram 	/*
672316Svikram 	 * Check if the slice is already mounted
673316Svikram 	 */
674316Svikram 	fp = fopen(MNTTAB, "r");
675316Svikram 	if (fp == NULL) {
676316Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
677621Svikram 		goto error;
678316Svikram 	}
679316Svikram 
680316Svikram 	resetmnttab(fp);
681316Svikram 
682316Svikram 	mntpt = NULL;
683316Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
684316Svikram 		if (strcmp(mnt.mnt_special, dev) == 0) {
685316Svikram 			mntpt = s_strdup(mnt.mnt_mountp);
686316Svikram 			break;
687316Svikram 		}
688316Svikram 	}
689316Svikram 
690316Svikram 	(void) fclose(fp);
691316Svikram 
692316Svikram 	if (mntpt) {
693316Svikram 		return (mntpt);
694316Svikram 	}
695316Svikram 
696316Svikram 
697316Svikram 	/*
698316Svikram 	 * GRUB slice is not mounted, we need to mount it now.
699316Svikram 	 * First create the mountpoint
700316Svikram 	 */
701316Svikram 	mntpt = s_calloc(1, PATH_MAX);
702316Svikram 	(void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid());
703316Svikram 	if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) {
704316Svikram 		bam_error(MKDIR_FAILED, mntpt, strerror(errno));
705316Svikram 		free(mntpt);
706621Svikram 		goto error;
707316Svikram 	}
708316Svikram 
709348Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount -F %s %s %s",
710348Svikram 	    fstype, dev, mntpt);
711348Svikram 
712*5648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
713621Svikram 		bam_error(MOUNT_FAILED, dev, fstype);
714316Svikram 		if (rmdir(mntpt) != 0) {
715316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
716316Svikram 		}
717316Svikram 		free(mntpt);
718621Svikram 		goto error;
719316Svikram 	}
720316Svikram 
721316Svikram 	*mnted = 1;
722316Svikram 	return (mntpt);
723621Svikram 
724621Svikram error:
725621Svikram 	if (physlice) {
726621Svikram 		free(*physlice);
727621Svikram 		*physlice = NULL;
728621Svikram 	}
729621Svikram 	if (logslice) {
730621Svikram 		free(*logslice);
731621Svikram 		*logslice = NULL;
732621Svikram 	}
733621Svikram 	if (fs_type) {
734621Svikram 		free(*fs_type);
735621Svikram 		*fs_type = NULL;
736621Svikram 	}
737621Svikram 	return (NULL);
738316Svikram }
739316Svikram 
740316Svikram static void
741621Svikram umount_grub_slice(
742621Svikram 	int mnted,
743621Svikram 	char *mntpt,
744621Svikram 	char *physlice,
745621Svikram 	char *logslice,
746621Svikram 	char *fs_type)
747316Svikram {
748348Svikram 	char cmd[PATH_MAX];
749348Svikram 
750316Svikram 	/*
751316Svikram 	 * If we have not dealt with GRUB slice
752316Svikram 	 * we have nothing to do - just return.
753316Svikram 	 */
754316Svikram 	if (mntpt == NULL)
755316Svikram 		return;
756316Svikram 
757316Svikram 
758316Svikram 	/*
759316Svikram 	 * If we mounted the filesystem earlier in mount_grub_slice()
760316Svikram 	 * unmount it now.
761316Svikram 	 */
762316Svikram 	if (mnted) {
763348Svikram 		(void) snprintf(cmd, sizeof (cmd), "/sbin/umount %s",
764348Svikram 		    mntpt);
765*5648Ssetje 		if (exec_cmd(cmd, NULL) != 0) {
766348Svikram 			bam_error(UMOUNT_FAILED, mntpt);
767316Svikram 		}
768316Svikram 		if (rmdir(mntpt) != 0) {
769316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
770316Svikram 		}
771316Svikram 	}
772621Svikram 
773316Svikram 	if (physlice)
774316Svikram 		free(physlice);
775621Svikram 	if (logslice)
776621Svikram 		free(logslice);
777621Svikram 	if (fs_type)
778621Svikram 		free(fs_type);
779621Svikram 
780316Svikram 	free(mntpt);
781316Svikram }
782316Svikram 
783621Svikram static char *
784621Svikram use_stubboot(void)
785621Svikram {
786621Svikram 	int mnted;
787621Svikram 	struct stat sb;
788621Svikram 	struct extmnttab mnt;
789621Svikram 	FILE *fp;
790621Svikram 	char cmd[PATH_MAX];
791621Svikram 
792621Svikram 	if (stat(STUBBOOT, &sb) != 0) {
793621Svikram 		bam_error(STUBBOOT_DIR_NOT_FOUND);
794621Svikram 		return (NULL);
795621Svikram 	}
796621Svikram 
797621Svikram 	/*
798621Svikram 	 * Check if stubboot is mounted. If not, mount it
799621Svikram 	 */
800621Svikram 	fp = fopen(MNTTAB, "r");
801621Svikram 	if (fp == NULL) {
802621Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
803621Svikram 		return (NULL);
804621Svikram 	}
805621Svikram 
806621Svikram 	resetmnttab(fp);
807621Svikram 
808621Svikram 	mnted = 0;
809621Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
810621Svikram 		if (strcmp(mnt.mnt_mountp, STUBBOOT) == 0) {
811621Svikram 			mnted = 1;
812621Svikram 			break;
813621Svikram 		}
814621Svikram 	}
815621Svikram 
816621Svikram 	(void) fclose(fp);
817621Svikram 
818621Svikram 	if (mnted)
819621Svikram 		return (STUBBOOT);
820621Svikram 
821621Svikram 	/*
822621Svikram 	 * Stubboot is not mounted, mount it now.
823621Svikram 	 * It should exist in /etc/vfstab
824621Svikram 	 */
825621Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount %s",
826621Svikram 	    STUBBOOT);
827*5648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
828621Svikram 		bam_error(MOUNT_MNTPT_FAILED, STUBBOOT);
829621Svikram 		return (NULL);
830621Svikram 	}
831621Svikram 
832621Svikram 	return (STUBBOOT);
833621Svikram }
834621Svikram 
835621Svikram static void
836621Svikram disp_active_menu_locn(char *menu_path, char *logslice, char *fstype, int mnted)
837621Svikram {
838621Svikram 	/*
839621Svikram 	 * Check if we did a temp mount of an unmounted device.
840621Svikram 	 * If yes, print the block device and fstype for that device
841621Svikram 	 * else it is already mounted, so we print the path to the GRUB menu.
842621Svikram 	 */
843621Svikram 	if (mnted) {
844621Svikram 		bam_print(GRUB_MENU_DEVICE, logslice);
845621Svikram 		bam_print(GRUB_MENU_FSTYPE, fstype);
846621Svikram 	} else {
847621Svikram 		bam_print(GRUB_MENU_PATH, menu_path);
848621Svikram 	}
849621Svikram }
850621Svikram 
851621Svikram /*
852621Svikram  * NOTE: A single "/" is also considered a trailing slash and will
853621Svikram  * be deleted.
854621Svikram  */
855621Svikram static void
856621Svikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
857621Svikram {
858621Svikram 	size_t dstlen;
859621Svikram 
860621Svikram 	assert(src);
861621Svikram 	assert(dst);
862621Svikram 
863621Svikram 	(void) strlcpy(dst, src, dstsize);
864621Svikram 
865621Svikram 	dstlen = strlen(dst);
866621Svikram 	if (dst[dstlen - 1] == '/') {
867621Svikram 		dst[dstlen - 1] = '\0';
868621Svikram 	}
869621Svikram }
870621Svikram 
8710Sstevel@tonic-gate static error_t
8720Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
8730Sstevel@tonic-gate {
8740Sstevel@tonic-gate 	error_t ret;
8750Sstevel@tonic-gate 	char menu_path[PATH_MAX];
8763446Smrj 	char path[PATH_MAX];
8770Sstevel@tonic-gate 	menu_t *menu;
878621Svikram 	char *mntpt, *menu_root, *logslice, *fstype;
879316Svikram 	struct stat sb;
880316Svikram 	int mnted;	/* set if we did a mount */
8810Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
882*5648Ssetje 	char *rootpath;
883*5648Ssetje 
884*5648Ssetje 	/*
885*5648Ssetje 	 * Menu sub-command only applies to GRUB (i.e. x86)
886*5648Ssetje 	 */
887*5648Ssetje 	rootpath = (bam_alt_root) ? bam_root : "/";
888*5648Ssetje 	if (!is_grub((const char *)rootpath)) {
889*5648Ssetje 		bam_error(NOT_ON_SPARC);
890*5648Ssetje 		return (BAM_ERROR);
891*5648Ssetje 	}
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	/*
8940Sstevel@tonic-gate 	 * Check arguments
8950Sstevel@tonic-gate 	 */
8960Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
8970Sstevel@tonic-gate 	if (ret == BAM_ERROR) {
8980Sstevel@tonic-gate 		return (BAM_ERROR);
8990Sstevel@tonic-gate 	}
9000Sstevel@tonic-gate 
901316Svikram 	mntpt = NULL;
902316Svikram 	mnted = 0;
903621Svikram 	logslice = fstype = NULL;
904621Svikram 
905621Svikram 	/*
9063446Smrj 	 * Check for the menu.list file:
9073446Smrj 	 *
9083446Smrj 	 * 1. Check for a GRUB_slice file, be it on / or
9093446Smrj 	 *    on the user-provided alternate root.
9103446Smrj 	 * 2. Use the alternate root, if given.
9113446Smrj 	 * 3. Check /stubboot
9123446Smrj 	 * 4. Use /
913621Svikram 	 */
914621Svikram 	if (bam_alt_root) {
9153446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
9163446Smrj 		    GRUB_slice);
9173446Smrj 	} else {
9183446Smrj 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
9193446Smrj 	}
9203446Smrj 
9213446Smrj 	if (stat(path, &sb) == 0) {
922621Svikram 		mntpt = mount_grub_slice(&mnted, NULL, &logslice, &fstype);
923316Svikram 		menu_root = mntpt;
9243446Smrj 	} else if (bam_alt_root) {
9253446Smrj 		menu_root = bam_root;
926621Svikram 	} else if (stat(STUBBOOT, &sb) == 0) {
927621Svikram 		menu_root = use_stubboot();
928316Svikram 	} else {
929316Svikram 		menu_root = bam_root;
930316Svikram 	}
931316Svikram 
932621Svikram 	if (menu_root == NULL) {
933621Svikram 		bam_error(CANNOT_LOCATE_GRUB_MENU);
934621Svikram 		return (BAM_ERROR);
935621Svikram 	}
936621Svikram 
937621Svikram 	elide_trailing_slash(menu_root, menu_path, sizeof (menu_path));
938621Svikram 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
939621Svikram 
940621Svikram 	/*
941621Svikram 	 * If listing the menu, display the active menu
942621Svikram 	 * location
943621Svikram 	 */
944621Svikram 	if (strcmp(subcmd, "list_entry") == 0) {
945621Svikram 		disp_active_menu_locn(menu_path, logslice, fstype, mnted);
946621Svikram 	}
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	menu = menu_read(menu_path);
9490Sstevel@tonic-gate 	assert(menu);
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 	/*
9520Sstevel@tonic-gate 	 * Special handling for setting timeout and default
9530Sstevel@tonic-gate 	 */
9540Sstevel@tonic-gate 	if (strcmp(subcmd, "set_option") == 0) {
9550Sstevel@tonic-gate 		if (largc != 1 || largv[0] == NULL) {
9560Sstevel@tonic-gate 			usage();
957621Svikram 			menu_free(menu);
958621Svikram 			umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
9590Sstevel@tonic-gate 			return (BAM_ERROR);
9600Sstevel@tonic-gate 		}
9610Sstevel@tonic-gate 		opt = largv[0];
9620Sstevel@tonic-gate 	} else if (largc != 0) {
9630Sstevel@tonic-gate 		usage();
964621Svikram 		menu_free(menu);
965621Svikram 		umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
9660Sstevel@tonic-gate 		return (BAM_ERROR);
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 
9693446Smrj 	ret = dboot_or_multiboot(bam_root);
9703446Smrj 	if (ret != BAM_SUCCESS)
9713446Smrj 		return (ret);
9723446Smrj 
9730Sstevel@tonic-gate 	/*
9740Sstevel@tonic-gate 	 * Once the sub-cmd handler has run
9750Sstevel@tonic-gate 	 * only the line field is guaranteed to have valid values
9760Sstevel@tonic-gate 	 */
9773446Smrj 	if ((strcmp(subcmd, "update_entry") == 0) ||
9783446Smrj 	    (strcmp(subcmd, "upgrade") == 0))
9790Sstevel@tonic-gate 		ret = f(menu, bam_root, opt);
9800Sstevel@tonic-gate 	else
9810Sstevel@tonic-gate 		ret = f(menu, menu_path, opt);
9820Sstevel@tonic-gate 	if (ret == BAM_WRITE) {
983316Svikram 		ret = menu_write(menu_root, menu);
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 	menu_free(menu);
9870Sstevel@tonic-gate 
988621Svikram 	umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
989316Svikram 
9900Sstevel@tonic-gate 	return (ret);
9910Sstevel@tonic-gate }
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate static error_t
9950Sstevel@tonic-gate bam_archive(
9960Sstevel@tonic-gate 	char *subcmd,
9970Sstevel@tonic-gate 	char *opt)
9980Sstevel@tonic-gate {
9990Sstevel@tonic-gate 	error_t ret;
10000Sstevel@tonic-gate 	error_t (*f)(char *root, char *opt);
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	/*
1003662Sszhou 	 * Add trailing / for archive subcommands
1004662Sszhou 	 */
1005662Sszhou 	if (rootbuf[strlen(rootbuf) - 1] != '/')
1006662Sszhou 		(void) strcat(rootbuf, "/");
1007662Sszhou 	bam_rootlen = strlen(rootbuf);
1008662Sszhou 
1009662Sszhou 	/*
10100Sstevel@tonic-gate 	 * Check arguments
10110Sstevel@tonic-gate 	 */
10120Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
10130Sstevel@tonic-gate 	if (ret != BAM_SUCCESS) {
10140Sstevel@tonic-gate 		return (BAM_ERROR);
10150Sstevel@tonic-gate 	}
10160Sstevel@tonic-gate 
10173446Smrj 	ret = dboot_or_multiboot(rootbuf);
10183446Smrj 	if (ret != BAM_SUCCESS)
10193446Smrj 		return (ret);
10203446Smrj 
10210Sstevel@tonic-gate 	/*
10220Sstevel@tonic-gate 	 * Check archive not supported with update_all
10230Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
10240Sstevel@tonic-gate 	 * information for each BE.
10250Sstevel@tonic-gate 	 */
10260Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
10270Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
10280Sstevel@tonic-gate 		return (BAM_ERROR);
10290Sstevel@tonic-gate 	}
10300Sstevel@tonic-gate 
1031316Svikram 	if (strcmp(subcmd, "update_all") == 0)
1032316Svikram 		bam_update_all = 1;
1033316Svikram 
1034*5648Ssetje #if !defined(_OPB)
10354581Ssherrym 	ucode_install(bam_root);
10364581Ssherrym #endif
10374581Ssherrym 
1038316Svikram 	ret = f(bam_root, opt);
1039316Svikram 
1040316Svikram 	bam_update_all = 0;
1041316Svikram 
1042316Svikram 	return (ret);
10430Sstevel@tonic-gate }
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate /*PRINTFLIKE1*/
10463446Smrj void
10470Sstevel@tonic-gate bam_error(char *format, ...)
10480Sstevel@tonic-gate {
10490Sstevel@tonic-gate 	va_list ap;
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 	va_start(ap, format);
10520Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
10530Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
10540Sstevel@tonic-gate 	va_end(ap);
10550Sstevel@tonic-gate }
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate /*PRINTFLIKE1*/
10580Sstevel@tonic-gate static void
10590Sstevel@tonic-gate bam_print(char *format, ...)
10600Sstevel@tonic-gate {
10610Sstevel@tonic-gate 	va_list ap;
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate 	va_start(ap, format);
10640Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
10650Sstevel@tonic-gate 	va_end(ap);
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate 
10683446Smrj /*PRINTFLIKE1*/
10693446Smrj void
10703446Smrj bam_print_stderr(char *format, ...)
10713446Smrj {
10723446Smrj 	va_list ap;
10733446Smrj 
10743446Smrj 	va_start(ap, format);
10753446Smrj 	(void) vfprintf(stderr, format, ap);
10763446Smrj 	va_end(ap);
10773446Smrj }
10783446Smrj 
10790Sstevel@tonic-gate static void
10800Sstevel@tonic-gate bam_exit(int excode)
10810Sstevel@tonic-gate {
10820Sstevel@tonic-gate 	bam_unlock();
10830Sstevel@tonic-gate 	exit(excode);
10840Sstevel@tonic-gate }
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate static void
10870Sstevel@tonic-gate bam_lock(void)
10880Sstevel@tonic-gate {
10890Sstevel@tonic-gate 	struct flock lock;
10900Sstevel@tonic-gate 	pid_t pid;
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
10930Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
10940Sstevel@tonic-gate 		/*
10950Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
10960Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
10970Sstevel@tonic-gate 		 * Proceed without the lock
10980Sstevel@tonic-gate 		 */
10990Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
11000Sstevel@tonic-gate 			bam_root_readonly = 1;
11010Sstevel@tonic-gate 			return;
11020Sstevel@tonic-gate 		}
11030Sstevel@tonic-gate 
11040Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
11050Sstevel@tonic-gate 		bam_exit(1);
11060Sstevel@tonic-gate 	}
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
11090Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
11100Sstevel@tonic-gate 	lock.l_start = 0;
11110Sstevel@tonic-gate 	lock.l_len = 0;
11120Sstevel@tonic-gate 
11130Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
11140Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
11150Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11160Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11170Sstevel@tonic-gate 			bam_lock_fd = -1;
11180Sstevel@tonic-gate 			bam_exit(1);
11190Sstevel@tonic-gate 		}
11200Sstevel@tonic-gate 		pid = 0;
11210Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
11220Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
11250Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
11260Sstevel@tonic-gate 		lock.l_start = 0;
11270Sstevel@tonic-gate 		lock.l_len = 0;
11280Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
11290Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11300Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11310Sstevel@tonic-gate 			bam_lock_fd = -1;
11320Sstevel@tonic-gate 			bam_exit(1);
11330Sstevel@tonic-gate 		}
11340Sstevel@tonic-gate 	}
11350Sstevel@tonic-gate 
11360Sstevel@tonic-gate 	/* We own the lock now */
11370Sstevel@tonic-gate 	pid = getpid();
11380Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
11390Sstevel@tonic-gate }
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate static void
11420Sstevel@tonic-gate bam_unlock(void)
11430Sstevel@tonic-gate {
11440Sstevel@tonic-gate 	struct flock unlock;
11450Sstevel@tonic-gate 
11460Sstevel@tonic-gate 	/*
11470Sstevel@tonic-gate 	 * NOP if we don't hold the lock
11480Sstevel@tonic-gate 	 */
11490Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11500Sstevel@tonic-gate 		return;
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
11540Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
11550Sstevel@tonic-gate 	unlock.l_start = 0;
11560Sstevel@tonic-gate 	unlock.l_len = 0;
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
11590Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11600Sstevel@tonic-gate 	}
11610Sstevel@tonic-gate 
11620Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
11630Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
11640Sstevel@tonic-gate 	}
11650Sstevel@tonic-gate 	bam_lock_fd = -1;
11660Sstevel@tonic-gate }
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate static error_t
11690Sstevel@tonic-gate list_archive(char *root, char *opt)
11700Sstevel@tonic-gate {
11710Sstevel@tonic-gate 	filelist_t flist;
11720Sstevel@tonic-gate 	filelist_t *flistp = &flist;
11730Sstevel@tonic-gate 	line_t *lp;
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 	assert(root);
11760Sstevel@tonic-gate 	assert(opt == NULL);
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
11790Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
11800Sstevel@tonic-gate 		return (BAM_ERROR);
11810Sstevel@tonic-gate 	}
11820Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
11850Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
11860Sstevel@tonic-gate 	}
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate 	filelist_free(flistp);
11890Sstevel@tonic-gate 
11900Sstevel@tonic-gate 	return (BAM_SUCCESS);
11910Sstevel@tonic-gate }
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate /*
11940Sstevel@tonic-gate  * This routine writes a list of lines to a file.
11950Sstevel@tonic-gate  * The list is *not* freed
11960Sstevel@tonic-gate  */
11970Sstevel@tonic-gate static error_t
11980Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
11990Sstevel@tonic-gate {
12000Sstevel@tonic-gate 	char tmpfile[PATH_MAX];
12010Sstevel@tonic-gate 	char path[PATH_MAX];
12020Sstevel@tonic-gate 	FILE *fp;
12030Sstevel@tonic-gate 	int ret;
12040Sstevel@tonic-gate 	struct stat sb;
12050Sstevel@tonic-gate 	mode_t mode;
12060Sstevel@tonic-gate 	uid_t root_uid;
12070Sstevel@tonic-gate 	gid_t sys_gid;
12080Sstevel@tonic-gate 	struct passwd *pw;
12090Sstevel@tonic-gate 	struct group *gp;
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	if (start == NULL) {
12150Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
12160Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
12170Sstevel@tonic-gate 			if (unlink(path) != 0) {
12180Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
12190Sstevel@tonic-gate 				return (BAM_ERROR);
12200Sstevel@tonic-gate 			} else {
12210Sstevel@tonic-gate 				return (BAM_SUCCESS);
12220Sstevel@tonic-gate 			}
12230Sstevel@tonic-gate 		}
12240Sstevel@tonic-gate 	}
12250Sstevel@tonic-gate 
12260Sstevel@tonic-gate 	/*
12270Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
12280Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
12290Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
12300Sstevel@tonic-gate 	 */
12310Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
12320Sstevel@tonic-gate 		mode = sb.st_mode;
12330Sstevel@tonic-gate 		root_uid = sb.st_uid;
12340Sstevel@tonic-gate 		sys_gid = sb.st_gid;
12350Sstevel@tonic-gate 	} else {
12360Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
12370Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
12380Sstevel@tonic-gate 			root_uid = pw->pw_uid;
12390Sstevel@tonic-gate 		} else {
12400Sstevel@tonic-gate 			if (bam_verbose)
12410Sstevel@tonic-gate 				bam_error(CANT_FIND_USER,
12420Sstevel@tonic-gate 				    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
12430Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
12440Sstevel@tonic-gate 		}
12450Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
12460Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
12470Sstevel@tonic-gate 		} else {
12480Sstevel@tonic-gate 			if (bam_verbose)
12490Sstevel@tonic-gate 				bam_error(CANT_FIND_GROUP,
12500Sstevel@tonic-gate 				    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
12510Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
12520Sstevel@tonic-gate 		}
12530Sstevel@tonic-gate 	}
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	/* Truncate tmpfile first */
12580Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
12590Sstevel@tonic-gate 	if (fp == NULL) {
12600Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12610Sstevel@tonic-gate 		return (BAM_ERROR);
12620Sstevel@tonic-gate 	}
12630Sstevel@tonic-gate 	ret = fclose(fp);
12640Sstevel@tonic-gate 	if (ret == EOF) {
12650Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12660Sstevel@tonic-gate 		return (BAM_ERROR);
12670Sstevel@tonic-gate 	}
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate 	/* Now open it in append mode */
12700Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
12710Sstevel@tonic-gate 	if (fp == NULL) {
12720Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12730Sstevel@tonic-gate 		return (BAM_ERROR);
12740Sstevel@tonic-gate 	}
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	for (; start; start = start->next) {
12770Sstevel@tonic-gate 		ret = s_fputs(start->line, fp);
12780Sstevel@tonic-gate 		if (ret == EOF) {
12790Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
12800Sstevel@tonic-gate 			(void) fclose(fp);
12810Sstevel@tonic-gate 			return (BAM_ERROR);
12820Sstevel@tonic-gate 		}
12830Sstevel@tonic-gate 	}
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 	ret = fclose(fp);
12860Sstevel@tonic-gate 	if (ret == EOF) {
12870Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12880Sstevel@tonic-gate 		return (BAM_ERROR);
12890Sstevel@tonic-gate 	}
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	/*
1292271Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1293271Sjg 	 * not supporting these operations - pcfs reports unsupported
1294271Sjg 	 * operations as EINVAL.
12950Sstevel@tonic-gate 	 */
12960Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1297271Sjg 	if (ret == -1 &&
1298271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
12990Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
13000Sstevel@tonic-gate 		return (BAM_ERROR);
13010Sstevel@tonic-gate 	}
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1304271Sjg 	if (ret == -1 &&
1305271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13060Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
13070Sstevel@tonic-gate 		return (BAM_ERROR);
13080Sstevel@tonic-gate 	}
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate 
13110Sstevel@tonic-gate 	/*
13120Sstevel@tonic-gate 	 * Do an atomic rename
13130Sstevel@tonic-gate 	 */
13140Sstevel@tonic-gate 	ret = rename(tmpfile, path);
13150Sstevel@tonic-gate 	if (ret != 0) {
13160Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
13170Sstevel@tonic-gate 		return (BAM_ERROR);
13180Sstevel@tonic-gate 	}
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	return (BAM_SUCCESS);
13210Sstevel@tonic-gate }
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate /*
13240Sstevel@tonic-gate  * This function should always return 0 - since we want
13250Sstevel@tonic-gate  * to create stat data for *all* files in the list.
13260Sstevel@tonic-gate  */
13270Sstevel@tonic-gate /*ARGSUSED*/
13280Sstevel@tonic-gate static int
13290Sstevel@tonic-gate cmpstat(
13300Sstevel@tonic-gate 	const char *file,
13310Sstevel@tonic-gate 	const struct stat *stat,
13320Sstevel@tonic-gate 	int flags,
13330Sstevel@tonic-gate 	struct FTW *ftw)
13340Sstevel@tonic-gate {
13350Sstevel@tonic-gate 	uint_t sz;
13360Sstevel@tonic-gate 	uint64_t *value;
13370Sstevel@tonic-gate 	uint64_t filestat[2];
13380Sstevel@tonic-gate 	int error;
13390Sstevel@tonic-gate 
13402334Ssetje 	struct safefile *safefilep;
13412334Ssetje 	FILE *fp;
13422334Ssetje 
13430Sstevel@tonic-gate 	/*
13440Sstevel@tonic-gate 	 * We only want regular files
13450Sstevel@tonic-gate 	 */
13460Sstevel@tonic-gate 	if (!S_ISREG(stat->st_mode))
13470Sstevel@tonic-gate 		return (0);
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate 	/*
13500Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
13510Sstevel@tonic-gate 	 * but this is not fatal to update determination.
13520Sstevel@tonic-gate 	 */
13530Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
13540Sstevel@tonic-gate 		filestat[0] = stat->st_size;
13550Sstevel@tonic-gate 		filestat[1] = stat->st_mtime;
13560Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
13570Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
13580Sstevel@tonic-gate 		if (error)
13590Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
13600Sstevel@tonic-gate 	}
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate 	/*
13630Sstevel@tonic-gate 	 * The remaining steps are only required if we haven't made a
13640Sstevel@tonic-gate 	 * decision about update or if we are checking (-n)
13650Sstevel@tonic-gate 	 */
13660Sstevel@tonic-gate 	if (walk_arg.need_update && !bam_check)
13670Sstevel@tonic-gate 		return (0);
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 	/*
13702334Ssetje 	 * If we are invoked as part of system/filesyste/boot-archive, then
13712334Ssetje 	 * there are a number of things we should not worry about
13720Sstevel@tonic-gate 	 */
13732334Ssetje 	if (bam_smf_check) {
13742334Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
13752334Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
13762334Ssetje 			return (0);
13772334Ssetje 
13782334Ssetje 		/* read in list of safe files */
13792334Ssetje 		if (safefiles == NULL)
13802334Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
13812334Ssetje 				safefiles = s_calloc(1,
13822334Ssetje 				    sizeof (struct safefile));
13832334Ssetje 				safefilep = safefiles;
13842334Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
13852334Ssetje 				    MAXNAMELEN);
13862334Ssetje 				safefilep->next = NULL;
13872334Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
13882334Ssetje 				    MAXNAMELEN, fp) != NULL) {
13892334Ssetje 					safefilep->next = s_calloc(1,
13902334Ssetje 					    sizeof (struct safefile));
13912334Ssetje 					safefilep = safefilep->next;
13922334Ssetje 					safefilep->name = s_calloc(1,
13932334Ssetje 					    MAXPATHLEN + MAXNAMELEN);
13942334Ssetje 					safefilep->next = NULL;
13952334Ssetje 				}
13962334Ssetje 				(void) fclose(fp);
13972334Ssetje 			}
13982334Ssetje 	}
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate 	/*
14010Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
14020Sstevel@tonic-gate 	 */
14030Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
14040Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
14050Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
14060Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
14070Sstevel@tonic-gate 			return (0);
14080Sstevel@tonic-gate 		walk_arg.need_update = 1;
14090Sstevel@tonic-gate 		if (bam_verbose)
14100Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
14110Sstevel@tonic-gate 		return (0);
14120Sstevel@tonic-gate 	}
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 	/*
14150Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
14160Sstevel@tonic-gate 	 */
14170Sstevel@tonic-gate 	assert(sz == 2);
14180Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 	if (filestat[0] != stat->st_size ||
14210Sstevel@tonic-gate 	    filestat[1] != stat->st_mtime) {
14223615Ssetje 		if (bam_smf_check) {
14233615Ssetje 			safefilep = safefiles;
14243615Ssetje 			while (safefilep != NULL) {
14253615Ssetje 				if (strcmp(file + bam_rootlen,
14263615Ssetje 				    safefilep->name) == 0) {
14273615Ssetje 					(void) creat(NEED_UPDATE_FILE, 0644);
14283615Ssetje 					return (0);
14293615Ssetje 				}
14303615Ssetje 				safefilep = safefilep->next;
14313615Ssetje 			}
14323615Ssetje 		}
14330Sstevel@tonic-gate 		walk_arg.need_update = 1;
14340Sstevel@tonic-gate 		if (bam_verbose)
14350Sstevel@tonic-gate 			if (bam_smf_check)
14360Sstevel@tonic-gate 				bam_print("    %s\n", file);
14370Sstevel@tonic-gate 			else
14380Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
14390Sstevel@tonic-gate 	}
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	return (0);
14420Sstevel@tonic-gate }
14430Sstevel@tonic-gate 
14440Sstevel@tonic-gate /*
14450Sstevel@tonic-gate  * Check flags and presence of required files.
14460Sstevel@tonic-gate  * The force flag and/or absence of files should
14470Sstevel@tonic-gate  * trigger an update.
14480Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
14490Sstevel@tonic-gate  * (as -n should only produce parseable output.)
14500Sstevel@tonic-gate  */
14510Sstevel@tonic-gate static void
14520Sstevel@tonic-gate check_flags_and_files(char *root)
14530Sstevel@tonic-gate {
14540Sstevel@tonic-gate 	char path[PATH_MAX];
14550Sstevel@tonic-gate 	struct stat sb;
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 	/*
14580Sstevel@tonic-gate 	 * if force, create archive unconditionally
14590Sstevel@tonic-gate 	 */
14600Sstevel@tonic-gate 	if (bam_force) {
14610Sstevel@tonic-gate 		walk_arg.need_update = 1;
14620Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
14630Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
14640Sstevel@tonic-gate 		return;
14650Sstevel@tonic-gate 	}
14660Sstevel@tonic-gate 
14670Sstevel@tonic-gate 	/*
14680Sstevel@tonic-gate 	 * If archive is missing, create archive
14690Sstevel@tonic-gate 	 */
1470*5648Ssetje 
1471*5648Ssetje 	if (is_sun4u())
1472*5648Ssetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
1473*5648Ssetje 		    SUN4U__ARCHIVE);
1474*5648Ssetje 	else if (is_sun4v())
1475*5648Ssetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
1476*5648Ssetje 		    SUN4V__ARCHIVE);
1477*5648Ssetje 	else {
1478*5648Ssetje 		if (bam_direct == BAM_DIRECT_DBOOT) {
1479*5648Ssetje 			(void) snprintf(path, sizeof (path), "%s%s", root,
1480*5648Ssetje 			    DIRECT_BOOT_ARCHIVE_64);
1481*5648Ssetje 			if (stat(path, &sb) != 0) {
1482*5648Ssetje 				if (bam_verbose && !bam_check)
1483*5648Ssetje 					bam_print(UPDATE_ARCH_MISS, path);
1484*5648Ssetje 				walk_arg.need_update = 1;
1485*5648Ssetje 				return;
1486*5648Ssetje 			}
1487*5648Ssetje 		}
1488*5648Ssetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
1489*5648Ssetje 		    DIRECT_BOOT_ARCHIVE_32);
1490*5648Ssetje 	}
1491*5648Ssetje 
14920Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
14930Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
14940Sstevel@tonic-gate 			bam_print(UPDATE_ARCH_MISS, path);
14950Sstevel@tonic-gate 		walk_arg.need_update = 1;
14960Sstevel@tonic-gate 		return;
14970Sstevel@tonic-gate 	}
14980Sstevel@tonic-gate }
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate static error_t
15010Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
15020Sstevel@tonic-gate {
15030Sstevel@tonic-gate 	char path[PATH_MAX];
15040Sstevel@tonic-gate 	FILE *fp;
15050Sstevel@tonic-gate 	char buf[BAM_MAXLINE];
15060Sstevel@tonic-gate 
15070Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate 	fp = fopen(path, "r");
15100Sstevel@tonic-gate 	if (fp == NULL) {
15110Sstevel@tonic-gate 		if (bam_debug)
15120Sstevel@tonic-gate 			bam_error(FLIST_FAIL, path, strerror(errno));
15130Sstevel@tonic-gate 		return (BAM_ERROR);
15140Sstevel@tonic-gate 	}
15150Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1516316Svikram 		/* skip blank lines */
1517316Svikram 		if (strspn(buf, " \t") == strlen(buf))
1518316Svikram 			continue;
15190Sstevel@tonic-gate 		append_to_flist(flistp, buf);
15200Sstevel@tonic-gate 	}
15210Sstevel@tonic-gate 	if (fclose(fp) != 0) {
15220Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
15230Sstevel@tonic-gate 		return (BAM_ERROR);
15240Sstevel@tonic-gate 	}
15250Sstevel@tonic-gate 	return (BAM_SUCCESS);
15260Sstevel@tonic-gate }
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate static error_t
15290Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
15300Sstevel@tonic-gate {
1531*5648Ssetje 	char path[PATH_MAX];
1532*5648Ssetje 	char cmd[PATH_MAX];
1533*5648Ssetje 	struct stat sb;
1534*5648Ssetje 	int n, rval;
15350Sstevel@tonic-gate 
15360Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate 	/*
1539*5648Ssetje 	 * build and check path to extract_boot_filelist.ksh
1540*5648Ssetje 	 */
1541*5648Ssetje 	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
1542*5648Ssetje 	if (n >= sizeof (path)) {
1543*5648Ssetje 		bam_error(NO_FLIST);
1544*5648Ssetje 		return (BAM_ERROR);
1545*5648Ssetje 	}
1546*5648Ssetje 
1547*5648Ssetje 	/*
1548*5648Ssetje 	 * If extract_boot_filelist is present, exec it, otherwise read
1549*5648Ssetje 	 * the filelists directly, for compatibility with older images.
15500Sstevel@tonic-gate 	 */
1551*5648Ssetje 	if (stat(path, &sb) == 0) {
1552*5648Ssetje 		/*
1553*5648Ssetje 		 * build arguments to exec extract_boot_filelist.ksh
1554*5648Ssetje 		 */
1555*5648Ssetje 		if (strlen(root) > 1) {
1556*5648Ssetje 			n = snprintf(cmd, sizeof (cmd), "%s -R %s /%s /%s",
1557*5648Ssetje 			    path, root, BOOT_FILE_LIST, ETC_FILE_LIST);
1558*5648Ssetje 		} else {
1559*5648Ssetje 			n = snprintf(cmd, sizeof (cmd), "%s /%s /%s",
1560*5648Ssetje 			    path, BOOT_FILE_LIST, ETC_FILE_LIST);
1561*5648Ssetje 		}
1562*5648Ssetje 		if (n >= sizeof (cmd)) {
1563*5648Ssetje 			bam_error(NO_FLIST);
1564*5648Ssetje 			return (BAM_ERROR);
1565*5648Ssetje 		}
1566*5648Ssetje 		if (exec_cmd(cmd, flistp) != 0) {
1567*5648Ssetje 			if (bam_debug)
1568*5648Ssetje 				bam_error(FLIST_FAIL, path, strerror(errno));
1569*5648Ssetje 			return (BAM_ERROR);
1570*5648Ssetje 		}
1571*5648Ssetje 	} else {
1572*5648Ssetje 		/*
1573*5648Ssetje 		 * Read current lists of files - only the first is mandatory
1574*5648Ssetje 		 */
1575*5648Ssetje 		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
1576*5648Ssetje 		if (rval != BAM_SUCCESS)
1577*5648Ssetje 			return (rval);
1578*5648Ssetje 		(void) read_one_list(root, flistp, ETC_FILE_LIST);
1579*5648Ssetje 	}
15800Sstevel@tonic-gate 
15810Sstevel@tonic-gate 	if (flistp->head == NULL) {
15820Sstevel@tonic-gate 		bam_error(NO_FLIST);
15830Sstevel@tonic-gate 		return (BAM_ERROR);
15840Sstevel@tonic-gate 	}
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 	return (BAM_SUCCESS);
15870Sstevel@tonic-gate }
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate static void
15900Sstevel@tonic-gate getoldstat(char *root)
15910Sstevel@tonic-gate {
15920Sstevel@tonic-gate 	char path[PATH_MAX];
15930Sstevel@tonic-gate 	int fd, error;
15940Sstevel@tonic-gate 	struct stat sb;
15950Sstevel@tonic-gate 	char *ostat;
15960Sstevel@tonic-gate 
15970Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
15980Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
15990Sstevel@tonic-gate 	if (fd == -1) {
16000Sstevel@tonic-gate 		if (bam_verbose)
16010Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
16020Sstevel@tonic-gate 		walk_arg.need_update = 1;
16030Sstevel@tonic-gate 		return;
16040Sstevel@tonic-gate 	}
16050Sstevel@tonic-gate 
16060Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
16070Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
16080Sstevel@tonic-gate 		(void) close(fd);
16090Sstevel@tonic-gate 		walk_arg.need_update = 1;
16100Sstevel@tonic-gate 		return;
16110Sstevel@tonic-gate 	}
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
16160Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
16170Sstevel@tonic-gate 		(void) close(fd);
16180Sstevel@tonic-gate 		free(ostat);
16190Sstevel@tonic-gate 		walk_arg.need_update = 1;
16200Sstevel@tonic-gate 		return;
16210Sstevel@tonic-gate 	}
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate 	(void) close(fd);
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
16260Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 	free(ostat);
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 	if (error) {
16310Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
16320Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
16330Sstevel@tonic-gate 		walk_arg.need_update = 1;
16340Sstevel@tonic-gate 		return;
16350Sstevel@tonic-gate 	}
16360Sstevel@tonic-gate }
16370Sstevel@tonic-gate 
16382583Svikram /*
16392583Svikram  * Checks if a file in the current (old) archive has
16402583Svikram  * been deleted from the root filesystem. This is needed for
16412583Svikram  * software like Trusted Extensions (TX) that switch early
16422583Svikram  * in boot based on presence/absence of a kernel module.
16432583Svikram  */
16442583Svikram static void
16452583Svikram check4stale(char *root)
16462583Svikram {
16472583Svikram 	nvpair_t	*nvp;
16482583Svikram 	nvlist_t	*nvlp;
16492583Svikram 	char 		*file;
16502583Svikram 	char		path[PATH_MAX];
16512583Svikram 	struct stat	sb;
16522583Svikram 
16532583Svikram 	/*
16542583Svikram 	 * Skip stale file check during smf check
16552583Svikram 	 */
16562583Svikram 	if (bam_smf_check)
16572583Svikram 		return;
16582583Svikram 
16592583Svikram 	/* Nothing to do if no old stats */
16602583Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
16612583Svikram 		return;
16622583Svikram 
16632583Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
16642583Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
16652583Svikram 		file = nvpair_name(nvp);
16662583Svikram 		if (file == NULL)
16672583Svikram 			continue;
16682583Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
16692583Svikram 		    root, file);
16702583Svikram 		if (stat(path, &sb) == -1) {
16712583Svikram 			walk_arg.need_update = 1;
16722583Svikram 			if (bam_verbose)
16732583Svikram 				bam_print(PARSEABLE_STALE_FILE, path);
16742583Svikram 		}
16752583Svikram 	}
16762583Svikram }
16772583Svikram 
16780Sstevel@tonic-gate static void
16790Sstevel@tonic-gate create_newstat(void)
16800Sstevel@tonic-gate {
16810Sstevel@tonic-gate 	int error;
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
16840Sstevel@tonic-gate 	if (error) {
16850Sstevel@tonic-gate 		/*
16860Sstevel@tonic-gate 		 * Not fatal - we can still create archive
16870Sstevel@tonic-gate 		 */
16880Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
16890Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
16900Sstevel@tonic-gate 	}
16910Sstevel@tonic-gate }
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate static void
16940Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
16950Sstevel@tonic-gate {
16960Sstevel@tonic-gate 	char path[PATH_MAX];
16970Sstevel@tonic-gate 	line_t *lp;
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
1700*5648Ssetje 		/*
1701*5648Ssetje 		 * Don't follow symlinks.  A symlink must refer to
1702*5648Ssetje 		 * a file that would appear in the archive through
1703*5648Ssetje 		 * a direct reference.  This matches the archive
1704*5648Ssetje 		 * construction behavior.
1705*5648Ssetje 		 */
17060Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
1707*5648Ssetje 		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
17080Sstevel@tonic-gate 			/*
17090Sstevel@tonic-gate 			 * Some files may not exist.
17100Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
17110Sstevel@tonic-gate 			 * Emit verbose message only
17120Sstevel@tonic-gate 			 */
17130Sstevel@tonic-gate 			if (bam_verbose)
17140Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
17150Sstevel@tonic-gate 		}
17160Sstevel@tonic-gate 	}
17170Sstevel@tonic-gate }
17180Sstevel@tonic-gate 
17190Sstevel@tonic-gate static void
17200Sstevel@tonic-gate savenew(char *root)
17210Sstevel@tonic-gate {
17220Sstevel@tonic-gate 	char path[PATH_MAX];
17230Sstevel@tonic-gate 	char path2[PATH_MAX];
17240Sstevel@tonic-gate 	size_t sz;
17250Sstevel@tonic-gate 	char *nstat;
17260Sstevel@tonic-gate 	int fd, wrote, error;
17270Sstevel@tonic-gate 
17280Sstevel@tonic-gate 	nstat = NULL;
17290Sstevel@tonic-gate 	sz = 0;
17300Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
17310Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
17320Sstevel@tonic-gate 	if (error) {
17330Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
17340Sstevel@tonic-gate 		return;
17350Sstevel@tonic-gate 	}
17360Sstevel@tonic-gate 
17370Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
17380Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
17390Sstevel@tonic-gate 	if (fd == -1) {
17400Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
17410Sstevel@tonic-gate 		free(nstat);
17420Sstevel@tonic-gate 		return;
17430Sstevel@tonic-gate 	}
17440Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
17450Sstevel@tonic-gate 	if (wrote != sz) {
17460Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
17470Sstevel@tonic-gate 		(void) close(fd);
17480Sstevel@tonic-gate 		free(nstat);
17490Sstevel@tonic-gate 		return;
17500Sstevel@tonic-gate 	}
17510Sstevel@tonic-gate 	(void) close(fd);
17520Sstevel@tonic-gate 	free(nstat);
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
17550Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
17560Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
17570Sstevel@tonic-gate 	}
17580Sstevel@tonic-gate }
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate static void
17610Sstevel@tonic-gate clear_walk_args(void)
17620Sstevel@tonic-gate {
17630Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
17640Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
17650Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
17660Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
17670Sstevel@tonic-gate 	walk_arg.need_update = 0;
17680Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
17690Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
17700Sstevel@tonic-gate }
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate /*
17730Sstevel@tonic-gate  * Returns:
17740Sstevel@tonic-gate  *	0 - no update necessary
17750Sstevel@tonic-gate  *	1 - update required.
17760Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
17770Sstevel@tonic-gate  *
17780Sstevel@tonic-gate  * Special handling for check (-n):
17790Sstevel@tonic-gate  * ================================
17800Sstevel@tonic-gate  * The check (-n) option produces parseable output.
17810Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
17820Sstevel@tonic-gate  * to out of sync files.
17830Sstevel@tonic-gate  * All stderr messages are still printed though.
17840Sstevel@tonic-gate  *
17850Sstevel@tonic-gate  */
17860Sstevel@tonic-gate static int
17870Sstevel@tonic-gate update_required(char *root)
17880Sstevel@tonic-gate {
17890Sstevel@tonic-gate 	struct stat sb;
17900Sstevel@tonic-gate 	char path[PATH_MAX];
17910Sstevel@tonic-gate 	filelist_t flist;
17920Sstevel@tonic-gate 	filelist_t *flistp = &flist;
17930Sstevel@tonic-gate 	int need_update;
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
17960Sstevel@tonic-gate 
17970Sstevel@tonic-gate 	walk_arg.need_update = 0;
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate 	/*
18000Sstevel@tonic-gate 	 * Without consulting stat data, check if we need update
18010Sstevel@tonic-gate 	 */
18020Sstevel@tonic-gate 	check_flags_and_files(root);
18030Sstevel@tonic-gate 
18040Sstevel@tonic-gate 	/*
18050Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
18060Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
18070Sstevel@tonic-gate 	 */
18080Sstevel@tonic-gate 	if (bam_smf_check) {
18090Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
18100Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
18110Sstevel@tonic-gate 			return (0);
18120Sstevel@tonic-gate 	}
18130Sstevel@tonic-gate 
18140Sstevel@tonic-gate 	/*
18150Sstevel@tonic-gate 	 * consult stat data only if we haven't made a decision
18160Sstevel@tonic-gate 	 * about update. If checking (-n) however, we always
18170Sstevel@tonic-gate 	 * need stat data (since we want to compare old and new)
18180Sstevel@tonic-gate 	 */
18190Sstevel@tonic-gate 	if (!walk_arg.need_update || bam_check)
18200Sstevel@tonic-gate 		getoldstat(root);
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate 	/*
18232583Svikram 	 * Check if the archive contains files that are no longer
18242583Svikram 	 * present on the root filesystem.
18252583Svikram 	 */
18262583Svikram 	if (!walk_arg.need_update || bam_check)
18272583Svikram 		check4stale(root);
18282583Svikram 
18292583Svikram 	/*
18300Sstevel@tonic-gate 	 * read list of files
18310Sstevel@tonic-gate 	 */
18320Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
18330Sstevel@tonic-gate 		clear_walk_args();
18340Sstevel@tonic-gate 		return (BAM_ERROR);
18350Sstevel@tonic-gate 	}
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
18380Sstevel@tonic-gate 
18390Sstevel@tonic-gate 	/*
18400Sstevel@tonic-gate 	 * At this point either the update is required
18410Sstevel@tonic-gate 	 * or the decision is pending. In either case
18420Sstevel@tonic-gate 	 * we need to create new stat nvlist
18430Sstevel@tonic-gate 	 */
18440Sstevel@tonic-gate 	create_newstat();
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate 	/*
18470Sstevel@tonic-gate 	 * This walk does 2 things:
18480Sstevel@tonic-gate 	 *  	- gets new stat data for every file
18490Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
18500Sstevel@tonic-gate 	 */
18510Sstevel@tonic-gate 	walk_list(root, &flist);
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate 	/* done with the file list */
18540Sstevel@tonic-gate 	filelist_free(flistp);
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 	/*
18570Sstevel@tonic-gate 	 * if we didn't succeed in  creating new stat data above
18580Sstevel@tonic-gate 	 * just return result of update check so that archive is built.
18590Sstevel@tonic-gate 	 */
18600Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
18610Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
18620Sstevel@tonic-gate 		need_update = walk_arg.need_update;
18630Sstevel@tonic-gate 		clear_walk_args();
18640Sstevel@tonic-gate 		return (need_update ? 1 : 0);
18650Sstevel@tonic-gate 	}
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate 
18680Sstevel@tonic-gate 	/*
18690Sstevel@tonic-gate 	 * If no update required, discard newstat
18700Sstevel@tonic-gate 	 */
18710Sstevel@tonic-gate 	if (!walk_arg.need_update) {
18720Sstevel@tonic-gate 		clear_walk_args();
18730Sstevel@tonic-gate 		return (0);
18740Sstevel@tonic-gate 	}
18750Sstevel@tonic-gate 
18760Sstevel@tonic-gate 	return (1);
18770Sstevel@tonic-gate }
18780Sstevel@tonic-gate 
18790Sstevel@tonic-gate static error_t
18800Sstevel@tonic-gate create_ramdisk(char *root)
18810Sstevel@tonic-gate {
18820Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
18830Sstevel@tonic-gate 	size_t len;
18840Sstevel@tonic-gate 	struct stat sb;
18850Sstevel@tonic-gate 
18860Sstevel@tonic-gate 	/*
18870Sstevel@tonic-gate 	 * Setup command args for create_ramdisk.ksh
18880Sstevel@tonic-gate 	 */
1889*5648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
18900Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
18910Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
18920Sstevel@tonic-gate 		return (BAM_ERROR);
18930Sstevel@tonic-gate 	}
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
18960Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
18970Sstevel@tonic-gate 
18980Sstevel@tonic-gate 	if (strlen(root) > 1) {
18990Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
19000Sstevel@tonic-gate 		/* chop off / at the end */
19010Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
19020Sstevel@tonic-gate 	} else
19030Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
19040Sstevel@tonic-gate 
1905*5648Ssetje 	if (exec_cmd(cmdline, NULL) != 0) {
19060Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
19070Sstevel@tonic-gate 		free(cmdline);
19080Sstevel@tonic-gate 		return (BAM_ERROR);
19090Sstevel@tonic-gate 	}
19100Sstevel@tonic-gate 	free(cmdline);
19110Sstevel@tonic-gate 
19120Sstevel@tonic-gate 	/*
1913*5648Ssetje 	 * The existence of the expected archives used to be
1914*5648Ssetje 	 * verified here. This check is done in create_ramdisk as
1915*5648Ssetje 	 * it needs to be in sync with the altroot operated upon.
19160Sstevel@tonic-gate 	 */
19170Sstevel@tonic-gate 
19180Sstevel@tonic-gate 	return (BAM_SUCCESS);
19190Sstevel@tonic-gate }
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate /*
19220Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
19230Sstevel@tonic-gate  * 1 - is miniroot
19240Sstevel@tonic-gate  * 0 - is not
19250Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
19260Sstevel@tonic-gate  */
19270Sstevel@tonic-gate static int
19280Sstevel@tonic-gate is_ramdisk(char *root)
19290Sstevel@tonic-gate {
19300Sstevel@tonic-gate 	struct extmnttab mnt;
19310Sstevel@tonic-gate 	FILE *fp;
19320Sstevel@tonic-gate 	int found;
1933316Svikram 	char mntpt[PATH_MAX];
1934316Svikram 	char *cp;
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	/*
19370Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
19380Sstevel@tonic-gate 	 * of dubious value:
1939316Svikram 	 *	- create boot_archive on a lofi-mounted boot_archive
19400Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
19410Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
19420Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
19430Sstevel@tonic-gate 	 * worth it.
19440Sstevel@tonic-gate 	 * The other two conditions are handled here
19450Sstevel@tonic-gate 	 */
19460Sstevel@tonic-gate 
19470Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
19480Sstevel@tonic-gate 	if (fp == NULL) {
19490Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
19500Sstevel@tonic-gate 		return (0);
19510Sstevel@tonic-gate 	}
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate 	resetmnttab(fp);
19540Sstevel@tonic-gate 
1955316Svikram 	/*
1956316Svikram 	 * Remove any trailing / from the mount point
1957316Svikram 	 */
1958316Svikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
1959316Svikram 	if (strcmp(root, "/") != 0) {
1960316Svikram 		cp = mntpt + strlen(mntpt) - 1;
1961316Svikram 		if (*cp == '/')
1962316Svikram 			*cp = '\0';
1963316Svikram 	}
19640Sstevel@tonic-gate 	found = 0;
19650Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1966316Svikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
19670Sstevel@tonic-gate 			found = 1;
19680Sstevel@tonic-gate 			break;
19690Sstevel@tonic-gate 		}
19700Sstevel@tonic-gate 	}
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	if (!found) {
19730Sstevel@tonic-gate 		if (bam_verbose)
1974316Svikram 			bam_error(NOT_IN_MNTTAB, mntpt);
19750Sstevel@tonic-gate 		(void) fclose(fp);
19760Sstevel@tonic-gate 		return (0);
19770Sstevel@tonic-gate 	}
19780Sstevel@tonic-gate 
19790Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
19800Sstevel@tonic-gate 		if (bam_verbose)
19810Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
19820Sstevel@tonic-gate 		(void) fclose(fp);
19830Sstevel@tonic-gate 		return (1);
19840Sstevel@tonic-gate 	}
19850Sstevel@tonic-gate 
19860Sstevel@tonic-gate 	(void) fclose(fp);
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate 	return (0);
19890Sstevel@tonic-gate }
19900Sstevel@tonic-gate 
19910Sstevel@tonic-gate static int
1992*5648Ssetje is_boot_archive(char *root)
19930Sstevel@tonic-gate {
19940Sstevel@tonic-gate 	char path[PATH_MAX];
19950Sstevel@tonic-gate 	struct stat sb;
19960Sstevel@tonic-gate 
19970Sstevel@tonic-gate 	/*
1998*5648Ssetje 	 * We can't create an archive without the create_ramdisk script
19990Sstevel@tonic-gate 	 */
2000*5648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
20010Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
20020Sstevel@tonic-gate 		if (bam_verbose)
20030Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
20040Sstevel@tonic-gate 		return (0);
20050Sstevel@tonic-gate 	}
20060Sstevel@tonic-gate 
2007*5648Ssetje 	return (1);
2008*5648Ssetje }
2009*5648Ssetje 
2010*5648Ssetje /*
2011*5648Ssetje  * Need to call this for anything that operates on the GRUB menu
2012*5648Ssetje  */
2013*5648Ssetje int
2014*5648Ssetje is_grub(const char *root)
2015*5648Ssetje {
2016*5648Ssetje 	char path[PATH_MAX];
2017*5648Ssetje 	struct stat sb;
2018*5648Ssetje 
20190Sstevel@tonic-gate 	/*
2020*5648Ssetje 	 * GRUB_DIR is required to modify the menu
20210Sstevel@tonic-gate 	 */
20220Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR);
20230Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
2024*5648Ssetje 		if (bam_debug)
20250Sstevel@tonic-gate 			bam_print(DIR_MISS, path);
20260Sstevel@tonic-gate 		return (0);
20270Sstevel@tonic-gate 	}
20280Sstevel@tonic-gate 
20290Sstevel@tonic-gate 	return (1);
20300Sstevel@tonic-gate }
20310Sstevel@tonic-gate 
20320Sstevel@tonic-gate static int
20330Sstevel@tonic-gate is_readonly(char *root)
20340Sstevel@tonic-gate {
20350Sstevel@tonic-gate 	struct statvfs vfs;
20360Sstevel@tonic-gate 
20370Sstevel@tonic-gate 	/*
20380Sstevel@tonic-gate 	 * Check for RDONLY filesystem
20390Sstevel@tonic-gate 	 * When in doubt assume it is not readonly
20400Sstevel@tonic-gate 	 */
20410Sstevel@tonic-gate 	if (statvfs(root, &vfs) != 0) {
20420Sstevel@tonic-gate 		if (bam_verbose)
20430Sstevel@tonic-gate 			bam_error(STATVFS_FAIL, root, strerror(errno));
20440Sstevel@tonic-gate 		return (0);
20450Sstevel@tonic-gate 	}
20460Sstevel@tonic-gate 
20470Sstevel@tonic-gate 	if (vfs.f_flag & ST_RDONLY) {
20480Sstevel@tonic-gate 		return (1);
20490Sstevel@tonic-gate 	}
20500Sstevel@tonic-gate 
20510Sstevel@tonic-gate 	return (0);
20520Sstevel@tonic-gate }
20530Sstevel@tonic-gate 
20540Sstevel@tonic-gate static error_t
20550Sstevel@tonic-gate update_archive(char *root, char *opt)
20560Sstevel@tonic-gate {
20570Sstevel@tonic-gate 	error_t ret;
20580Sstevel@tonic-gate 
20590Sstevel@tonic-gate 	assert(root);
20600Sstevel@tonic-gate 	assert(opt == NULL);
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate 	/*
2063*5648Ssetje 	 * root must belong to a boot archive based  OS,
20640Sstevel@tonic-gate 	 */
2065*5648Ssetje 	if (!is_boot_archive(root)) {
2066316Svikram 		/*
2067316Svikram 		 * Emit message only if not in context of update_all.
2068316Svikram 		 * If in update_all, emit only if verbose flag is set.
2069316Svikram 		 */
2070316Svikram 		if (!bam_update_all || bam_verbose)
2071316Svikram 			bam_print(NOT_GRUB_BOOT, root);
20720Sstevel@tonic-gate 		return (BAM_SUCCESS);
20730Sstevel@tonic-gate 	}
20740Sstevel@tonic-gate 
20750Sstevel@tonic-gate 	/*
2076662Sszhou 	 * If smf check is requested when / is writable (can happen
2077662Sszhou 	 * on first reboot following an upgrade because service
2078662Sszhou 	 * dependency is messed up), skip the check.
2079662Sszhou 	 */
2080662Sszhou 	if (bam_smf_check && !bam_root_readonly)
2081662Sszhou 		return (BAM_SUCCESS);
2082662Sszhou 
2083662Sszhou 	/*
2084662Sszhou 	 * root must be writable. This check applies to alternate
2085662Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
20860Sstevel@tonic-gate 	 * Note: statvfs() does not always report the truth
20870Sstevel@tonic-gate 	 */
2088756Ssetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
2089662Sszhou 		if (bam_verbose)
20900Sstevel@tonic-gate 			bam_print(RDONLY_FS, root);
20910Sstevel@tonic-gate 		return (BAM_SUCCESS);
20920Sstevel@tonic-gate 	}
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate 	/*
20950Sstevel@tonic-gate 	 * Don't generate archive on ramdisk
20960Sstevel@tonic-gate 	 */
20970Sstevel@tonic-gate 	if (is_ramdisk(root)) {
20980Sstevel@tonic-gate 		if (bam_verbose)
20990Sstevel@tonic-gate 			bam_print(SKIP_RAMDISK);
21000Sstevel@tonic-gate 		return (BAM_SUCCESS);
21010Sstevel@tonic-gate 	}
21020Sstevel@tonic-gate 
21030Sstevel@tonic-gate 	/*
21040Sstevel@tonic-gate 	 * Now check if updated is really needed
21050Sstevel@tonic-gate 	 */
21060Sstevel@tonic-gate 	ret = update_required(root);
21070Sstevel@tonic-gate 
21080Sstevel@tonic-gate 	/*
21090Sstevel@tonic-gate 	 * The check command (-n) is *not* a dry run
21100Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
21110Sstevel@tonic-gate 	 */
21120Sstevel@tonic-gate 	if (bam_check) {
21130Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
21140Sstevel@tonic-gate 	}
21150Sstevel@tonic-gate 
21160Sstevel@tonic-gate 	if (ret == 1) {
21170Sstevel@tonic-gate 		/* create the ramdisk */
21180Sstevel@tonic-gate 		ret = create_ramdisk(root);
21190Sstevel@tonic-gate 	}
2120*5648Ssetje 
2121*5648Ssetje 	/* if the archive is updated, save the new stat data */
2122*5648Ssetje 	if (ret == 0 && walk_arg.new_nvlp != NULL) {
2123*5648Ssetje 		savenew(root);
2124*5648Ssetje 	}
2125*5648Ssetje 
2126*5648Ssetje 	clear_walk_args();
2127*5648Ssetje 
21280Sstevel@tonic-gate 	return (ret);
21290Sstevel@tonic-gate }
21300Sstevel@tonic-gate 
2131316Svikram static void
21321746Svikram update_fdisk(void)
21331746Svikram {
21341746Svikram 	struct stat sb;
21351746Svikram 	char cmd[PATH_MAX];
21361746Svikram 	int ret1, ret2;
21371746Svikram 
21381746Svikram 	assert(stat(GRUB_fdisk, &sb) == 0);
21391746Svikram 	assert(stat(GRUB_fdisk_target, &sb) == 0);
21401746Svikram 
21411746Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`",
21421746Svikram 	    GRUB_fdisk, GRUB_fdisk_target);
21431746Svikram 
21441746Svikram 	bam_print(UPDATING_FDISK);
2145*5648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
21461746Svikram 		bam_error(FDISK_UPDATE_FAILED);
21471746Svikram 	}
21481746Svikram 
21491746Svikram 	/*
21501746Svikram 	 * We are done, remove the files.
21511746Svikram 	 */
21521746Svikram 	ret1 = unlink(GRUB_fdisk);
21531746Svikram 	ret2 = unlink(GRUB_fdisk_target);
21541746Svikram 	if (ret1 != 0 || ret2 != 0) {
21551746Svikram 		bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target);
21561746Svikram 	}
21571746Svikram }
21581746Svikram 
21591746Svikram static void
2160316Svikram restore_grub_slice(void)
2161316Svikram {
2162316Svikram 	struct stat sb;
2163316Svikram 	char *mntpt, *physlice;
2164316Svikram 	int mnted;	/* set if we did a mount */
2165316Svikram 	char menupath[PATH_MAX], cmd[PATH_MAX];
2166316Svikram 
2167316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
2168316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
2169316Svikram 		return;
2170316Svikram 	}
2171316Svikram 
2172316Svikram 	/*
2173316Svikram 	 * If we are doing an luactivate, don't attempt to restore GRUB or else
2174316Svikram 	 * we may not be able to get to DCA boot environments. Let luactivate
2175316Svikram 	 * handle GRUB/DCA installation
2176316Svikram 	 */
2177316Svikram 	if (stat(LU_ACTIVATE_FILE, &sb) == 0) {
2178316Svikram 		return;
2179316Svikram 	}
2180316Svikram 
2181316Svikram 	mnted = 0;
2182316Svikram 	physlice = NULL;
2183621Svikram 	mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL);
2184316Svikram 	if (mntpt == NULL) {
2185316Svikram 		bam_error(CANNOT_RESTORE_GRUB_SLICE);
2186316Svikram 		return;
2187316Svikram 	}
2188316Svikram 
2189316Svikram 	(void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU);
2190316Svikram 	if (stat(menupath, &sb) == 0) {
2191621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2192316Svikram 		return;
2193316Svikram 	}
2194316Svikram 
2195316Svikram 	/*
2196316Svikram 	 * The menu is missing - we need to do a restore
2197316Svikram 	 */
2198316Svikram 	bam_print(RESTORING_GRUB);
2199316Svikram 
2200316Svikram 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s %s",
2201316Svikram 	    INSTALLGRUB, STAGE1, STAGE2, physlice);
2202316Svikram 
2203*5648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
2204316Svikram 		bam_error(RESTORE_GRUB_FAILED);
2205621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2206316Svikram 		return;
2207316Svikram 	}
2208316Svikram 
2209316Svikram 	if (stat(GRUB_backup_menu, &sb) != 0) {
2210316Svikram 		bam_error(MISSING_BACKUP_MENU,
2211316Svikram 		    GRUB_backup_menu, strerror(errno));
2212621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2213316Svikram 		return;
2214316Svikram 	}
2215316Svikram 
2216316Svikram 	(void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s",
2217316Svikram 	    GRUB_backup_menu, menupath);
2218316Svikram 
2219*5648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
2220316Svikram 		bam_error(RESTORE_MENU_FAILED, menupath);
2221621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2222316Svikram 		return;
2223316Svikram 	}
2224316Svikram 
2225316Svikram 	/* Success */
2226621Svikram 	umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2227316Svikram }
2228316Svikram 
22290Sstevel@tonic-gate static error_t
22300Sstevel@tonic-gate update_all(char *root, char *opt)
22310Sstevel@tonic-gate {
22320Sstevel@tonic-gate 	struct extmnttab mnt;
22330Sstevel@tonic-gate 	struct stat sb;
22340Sstevel@tonic-gate 	FILE *fp;
22350Sstevel@tonic-gate 	char multibt[PATH_MAX];
2236*5648Ssetje 	char creatram[PATH_MAX];
22370Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
22381746Svikram 	int ret1, ret2;
22390Sstevel@tonic-gate 
2240621Svikram 	assert(root);
22410Sstevel@tonic-gate 	assert(opt == NULL);
22420Sstevel@tonic-gate 
2243621Svikram 	if (bam_rootlen != 1 || *root != '/') {
2244621Svikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
2245621Svikram 		bam_error(ALT_ROOT_INVALID, multibt);
2246621Svikram 		return (BAM_ERROR);
2247621Svikram 	}
2248621Svikram 
22490Sstevel@tonic-gate 	/*
22504493Snadkarni 	 * Check to see if we are in the midst of safemode patching
22514493Snadkarni 	 * If so skip building the archive for /. Instead build it
22524493Snadkarni 	 * against the latest bits obtained by creating a fresh lofs
22534493Snadkarni 	 * mount of root.
22540Sstevel@tonic-gate 	 */
22554493Snadkarni 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
22564493Snadkarni 		if (mkdir(LOFS_PATCH_MNT, 0755) == -1 &&
22574493Snadkarni 		    errno != EEXIST) {
22584493Snadkarni 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
22594493Snadkarni 			    strerror(errno));
22604493Snadkarni 			ret = BAM_ERROR;
22614493Snadkarni 			goto out;
22624493Snadkarni 		}
22634493Snadkarni 		(void) snprintf(multibt, sizeof (multibt),
22644493Snadkarni 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
2265*5648Ssetje 		if (exec_cmd(multibt, NULL) != 0) {
22664493Snadkarni 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
22674493Snadkarni 			ret = BAM_ERROR;
22684493Snadkarni 		}
22694493Snadkarni 		if (ret != BAM_ERROR) {
22704493Snadkarni 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
22714493Snadkarni 			    LOFS_PATCH_MNT);
22724493Snadkarni 			bam_rootlen = strlen(rootbuf);
22734493Snadkarni 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
22744493Snadkarni 				ret = BAM_ERROR;
22754550Snadkarni 			/*
22764550Snadkarni 			 * unmount the lofs mount since there could be
22774550Snadkarni 			 * multiple invocations of bootadm -a update_all
22784550Snadkarni 			 */
22794550Snadkarni 			(void) snprintf(multibt, sizeof (multibt),
22804550Snadkarni 			    "/sbin/umount %s", LOFS_PATCH_MNT);
2281*5648Ssetje 			if (exec_cmd(multibt, NULL) != 0) {
22824550Snadkarni 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
22834550Snadkarni 				ret = BAM_ERROR;
22844550Snadkarni 			}
22854493Snadkarni 		}
22864493Snadkarni 	} else {
22874493Snadkarni 		/*
22884493Snadkarni 		 * First update archive for current root
22894493Snadkarni 		 */
22904493Snadkarni 		if (update_archive(root, opt) != BAM_SUCCESS)
22914493Snadkarni 			ret = BAM_ERROR;
22924493Snadkarni 	}
22934493Snadkarni 
22944493Snadkarni 	if (ret == BAM_ERROR)
22954493Snadkarni 		goto out;
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate 	/*
22980Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
22990Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
23000Sstevel@tonic-gate 	 */
23010Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
23020Sstevel@tonic-gate 	if (fp == NULL) {
23030Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2304316Svikram 		ret = BAM_ERROR;
2305316Svikram 		goto out;
23060Sstevel@tonic-gate 	}
23070Sstevel@tonic-gate 
23080Sstevel@tonic-gate 	resetmnttab(fp);
23090Sstevel@tonic-gate 
23100Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
23110Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
23120Sstevel@tonic-gate 			continue;
23130Sstevel@tonic-gate 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
23140Sstevel@tonic-gate 			continue;
23150Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
23160Sstevel@tonic-gate 			continue;
23170Sstevel@tonic-gate 
2318*5648Ssetje 		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
2319*5648Ssetje 		    mnt.mnt_mountp, CREATE_RAMDISK);
2320*5648Ssetje 
2321*5648Ssetje 		if (stat(creatram, &sb) == -1)
23220Sstevel@tonic-gate 			continue;
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate 		/*
23250Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
23260Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
23270Sstevel@tonic-gate 		 */
23280Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
23290Sstevel@tonic-gate 		    mnt.mnt_mountp);
23300Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
23313446Smrj 
23323446Smrj 		/*
23333446Smrj 		 * It's possible that other mounts may be an alternate boot
23343446Smrj 		 * architecture, so check it again.
23353446Smrj 		 */
23363446Smrj 		if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) ||
23373446Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
23380Sstevel@tonic-gate 			ret = BAM_ERROR;
23390Sstevel@tonic-gate 	}
23400Sstevel@tonic-gate 
23410Sstevel@tonic-gate 	(void) fclose(fp);
23420Sstevel@tonic-gate 
2343316Svikram out:
2344316Svikram 	if (stat(GRUB_slice, &sb) == 0) {
2345316Svikram 		restore_grub_slice();
2346316Svikram 	}
2347316Svikram 
23481746Svikram 	/*
23491746Svikram 	 * Update fdisk table as we go down. Updating it when
23501746Svikram 	 * the system is running will confuse biosdev.
23511746Svikram 	 */
23521746Svikram 	ret1 = stat(GRUB_fdisk, &sb);
23531746Svikram 	ret2 = stat(GRUB_fdisk_target, &sb);
23541746Svikram 	if ((ret1 == 0) && (ret2 == 0)) {
23551746Svikram 		update_fdisk();
23561746Svikram 	} else if ((ret1 == 0) ^ (ret2 == 0)) {
23571746Svikram 		/*
23581746Svikram 		 * It is an error for one file to be
23591746Svikram 		 * present and the other absent.
23601746Svikram 		 * It is normal for both files to be
23611746Svikram 		 * absent - it indicates that no fdisk
23621746Svikram 		 * update is required.
23631746Svikram 		 */
23641746Svikram 		bam_error(MISSING_FDISK_FILE,
23651746Svikram 		    ret1 ? GRUB_fdisk : GRUB_fdisk_target);
23661746Svikram 		ret = BAM_ERROR;
23671746Svikram 	}
23681746Svikram 
23690Sstevel@tonic-gate 	return (ret);
23700Sstevel@tonic-gate }
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate static void
23730Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
23740Sstevel@tonic-gate {
23750Sstevel@tonic-gate 	if (mp->start == NULL) {
23760Sstevel@tonic-gate 		mp->start = lp;
23770Sstevel@tonic-gate 	} else {
23780Sstevel@tonic-gate 		mp->end->next = lp;
2379662Sszhou 		lp->prev = mp->end;
23800Sstevel@tonic-gate 	}
23810Sstevel@tonic-gate 	mp->end = lp;
23820Sstevel@tonic-gate }
23830Sstevel@tonic-gate 
2384662Sszhou static void
2385662Sszhou unlink_line(menu_t *mp, line_t *lp)
2386662Sszhou {
2387662Sszhou 	/* unlink from list */
2388662Sszhou 	if (lp->prev)
2389662Sszhou 		lp->prev->next = lp->next;
2390662Sszhou 	else
2391662Sszhou 		mp->start = lp->next;
2392662Sszhou 	if (lp->next)
2393662Sszhou 		lp->next->prev = lp->prev;
2394662Sszhou 	else
2395662Sszhou 		mp->end = lp->prev;
2396662Sszhou }
2397662Sszhou 
2398662Sszhou static entry_t *
2399662Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
2400662Sszhou {
2401662Sszhou 	entry_t *ent, *prev;
2402662Sszhou 
2403662Sszhou 	ent = s_calloc(1, sizeof (entry_t));
2404662Sszhou 	ent->start = start;
2405662Sszhou 	ent->end = end;
2406662Sszhou 
2407662Sszhou 	if (mp->entries == NULL) {
2408662Sszhou 		mp->entries = ent;
2409662Sszhou 		return (ent);
2410662Sszhou 	}
2411662Sszhou 
2412662Sszhou 	prev = mp->entries;
2413662Sszhou 	while (prev->next)
2414662Sszhou 		prev = prev-> next;
2415662Sszhou 	prev->next = ent;
2416662Sszhou 	ent->prev = prev;
2417662Sszhou 	return (ent);
2418662Sszhou }
2419662Sszhou 
2420662Sszhou static void
2421662Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
2422662Sszhou {
2423662Sszhou 	if (ent)
2424662Sszhou 		ent->end = lp;
2425662Sszhou }
2426662Sszhou 
24270Sstevel@tonic-gate /*
24285084Sjohnlev  * Check whether cmd matches the one indexed by which, and whether arg matches
24295084Sjohnlev  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
24305084Sjohnlev  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
24315084Sjohnlev  * strstr(), so it can be a partial match.
24325084Sjohnlev  */
24335084Sjohnlev static int
24345084Sjohnlev check_cmd(const char *cmd, const int which, const char *arg, const char *str)
24355084Sjohnlev {
24365084Sjohnlev 	if ((strcmp(cmd, menu_cmds[which]) != 0) &&
24375084Sjohnlev 	    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
24385084Sjohnlev 		return (0);
24395084Sjohnlev 	}
24405084Sjohnlev 	return (strstr(arg, str) != NULL);
24415084Sjohnlev }
24425084Sjohnlev 
24435084Sjohnlev /*
24440Sstevel@tonic-gate  * A line in menu.lst looks like
24450Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
24460Sstevel@tonic-gate  */
24470Sstevel@tonic-gate static void
24480Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
24490Sstevel@tonic-gate {
24500Sstevel@tonic-gate 	/*
24510Sstevel@tonic-gate 	 * save state across calls. This is so that
24520Sstevel@tonic-gate 	 * header gets the right entry# after title has
24530Sstevel@tonic-gate 	 * been processed
24540Sstevel@tonic-gate 	 */
2455662Sszhou 	static line_t *prev = NULL;
2456662Sszhou 	static entry_t *curr_ent = NULL;
24573446Smrj 	static int in_liveupgrade = 0;
24580Sstevel@tonic-gate 
24590Sstevel@tonic-gate 	line_t	*lp;
24600Sstevel@tonic-gate 	char *cmd, *sep, *arg;
24610Sstevel@tonic-gate 	char save, *cp, *line;
24620Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
24630Sstevel@tonic-gate 
24640Sstevel@tonic-gate 	if (str == NULL) {
24650Sstevel@tonic-gate 		return;
24660Sstevel@tonic-gate 	}
24670Sstevel@tonic-gate 
24680Sstevel@tonic-gate 	/*
24690Sstevel@tonic-gate 	 * First save a copy of the entire line.
24700Sstevel@tonic-gate 	 * We use this later to set the line field.
24710Sstevel@tonic-gate 	 */
24720Sstevel@tonic-gate 	line = s_strdup(str);
24730Sstevel@tonic-gate 
24740Sstevel@tonic-gate 	/* Eat up leading whitespace */
24750Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
24760Sstevel@tonic-gate 		str++;
24770Sstevel@tonic-gate 
24780Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
24790Sstevel@tonic-gate 		cmd = s_strdup("#");
24800Sstevel@tonic-gate 		sep = NULL;
24810Sstevel@tonic-gate 		arg = s_strdup(str + 1);
24820Sstevel@tonic-gate 		flag = BAM_COMMENT;
24833446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
24843446Smrj 			in_liveupgrade = 1;
24853446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
24863446Smrj 			in_liveupgrade = 0;
24873446Smrj 		}
24880Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
24890Sstevel@tonic-gate 		cmd = sep = arg = NULL;
24900Sstevel@tonic-gate 		flag = BAM_EMPTY;
24910Sstevel@tonic-gate 	} else {
24920Sstevel@tonic-gate 		/*
24930Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
24940Sstevel@tonic-gate 		 * However various development bits use '=' as a
24950Sstevel@tonic-gate 		 * separator. In addition, external users also
24960Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
24970Sstevel@tonic-gate 		 */
24980Sstevel@tonic-gate 		cp = str;
24990Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
25000Sstevel@tonic-gate 			if (*str == '\0') {
25010Sstevel@tonic-gate 				cmd = s_strdup(cp);
25020Sstevel@tonic-gate 				sep = arg = NULL;
25030Sstevel@tonic-gate 				break;
25040Sstevel@tonic-gate 			}
25050Sstevel@tonic-gate 			str++;
25060Sstevel@tonic-gate 		}
25070Sstevel@tonic-gate 
25080Sstevel@tonic-gate 		if (*str != '\0') {
25090Sstevel@tonic-gate 			save = *str;
25100Sstevel@tonic-gate 			*str = '\0';
25110Sstevel@tonic-gate 			cmd = s_strdup(cp);
25120Sstevel@tonic-gate 			*str = save;
25130Sstevel@tonic-gate 
25140Sstevel@tonic-gate 			str++;
25150Sstevel@tonic-gate 			save = *str;
25160Sstevel@tonic-gate 			*str = '\0';
25170Sstevel@tonic-gate 			sep = s_strdup(str - 1);
25180Sstevel@tonic-gate 			*str = save;
25190Sstevel@tonic-gate 
25200Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
25210Sstevel@tonic-gate 				str++;
25220Sstevel@tonic-gate 			if (*str == '\0')
25230Sstevel@tonic-gate 				arg = NULL;
25240Sstevel@tonic-gate 			else
25250Sstevel@tonic-gate 				arg = s_strdup(str);
25260Sstevel@tonic-gate 		}
25270Sstevel@tonic-gate 	}
25280Sstevel@tonic-gate 
25290Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
25300Sstevel@tonic-gate 
25310Sstevel@tonic-gate 	lp->cmd = cmd;
25320Sstevel@tonic-gate 	lp->sep = sep;
25330Sstevel@tonic-gate 	lp->arg = arg;
25340Sstevel@tonic-gate 	lp->line = line;
25350Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
25360Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
25370Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
25380Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
25390Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
25403446Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
25410Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
2542662Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
25433446Smrj 			curr_ent->flags = BAM_ENTRY_BOOTADM;
2544662Sszhou 		} else {
2545662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
25463446Smrj 			if (in_liveupgrade) {
25473446Smrj 				curr_ent->flags = BAM_ENTRY_LU;
25483446Smrj 			}
2549662Sszhou 		}
25503446Smrj 		curr_ent->entryNum = *entryNum;
25510Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
25520Sstevel@tonic-gate 		/*
25530Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
25540Sstevel@tonic-gate 		 * by the subsequent title
25550Sstevel@tonic-gate 		 */
25560Sstevel@tonic-gate 		lp->entryNum = *entryNum;
25570Sstevel@tonic-gate 		lp->flags = flag;
25580Sstevel@tonic-gate 	} else {
25590Sstevel@tonic-gate 		lp->entryNum = *entryNum;
25603446Smrj 
25613446Smrj 		if (*entryNum == ENTRY_INIT) {
25623446Smrj 			lp->flags = BAM_GLOBAL;
25633446Smrj 		} else {
25643446Smrj 			lp->flags = BAM_ENTRY;
25653446Smrj 
25663446Smrj 			if (cmd && arg) {
25673446Smrj 				/*
25683446Smrj 				 * We only compare for the length of "module"
25693446Smrj 				 * so that "module$" will also match.
25703446Smrj 				 */
25715084Sjohnlev 				if (check_cmd(cmd, MODULE_CMD, arg, MINIROOT))
25723446Smrj 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
25735084Sjohnlev 				else if (check_cmd(cmd, KERNEL_CMD, arg,
25745084Sjohnlev 				    "xen.gz"))
25755084Sjohnlev 					curr_ent->flags |= BAM_ENTRY_HV;
25763446Smrj 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
25773446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
25783446Smrj 				else if (strcmp(cmd,
25793446Smrj 				    menu_cmds[CHAINLOADER_CMD]) == 0)
25803446Smrj 					curr_ent->flags |=
25813446Smrj 					    BAM_ENTRY_CHAINLOADER;
25823446Smrj 			}
25833446Smrj 		}
25840Sstevel@tonic-gate 	}
25850Sstevel@tonic-gate 
2586662Sszhou 	/* record default, old default, and entry line ranges */
2587662Sszhou 	if (lp->flags == BAM_GLOBAL &&
2588662Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
2589662Sszhou 		mp->curdefault = lp;
2590662Sszhou 	} else if (lp->flags == BAM_COMMENT &&
2591662Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
2592662Sszhou 		mp->olddefault = lp;
25933446Smrj 	} else if (lp->flags == BAM_COMMENT &&
25943446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
25953446Smrj 		mp->old_rc_default = lp;
2596662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
25973446Smrj 	    (lp->flags == BAM_COMMENT &&
25983446Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
2599662Sszhou 		boot_entry_addline(curr_ent, lp);
2600662Sszhou 	}
26010Sstevel@tonic-gate 	append_line(mp, lp);
26020Sstevel@tonic-gate 
26030Sstevel@tonic-gate 	prev = lp;
26040Sstevel@tonic-gate }
26050Sstevel@tonic-gate 
2606621Svikram static void
2607621Svikram update_numbering(menu_t *mp)
2608621Svikram {
2609621Svikram 	int lineNum;
2610621Svikram 	int entryNum;
2611621Svikram 	int old_default_value;
2612621Svikram 	line_t *lp, *prev, *default_lp, *default_entry;
2613621Svikram 	char buf[PATH_MAX];
2614621Svikram 
2615621Svikram 	if (mp->start == NULL) {
2616621Svikram 		return;
2617621Svikram 	}
2618621Svikram 
2619621Svikram 	lineNum = LINE_INIT;
2620621Svikram 	entryNum = ENTRY_INIT;
2621621Svikram 	old_default_value = ENTRY_INIT;
2622621Svikram 	lp = default_lp = default_entry = NULL;
2623621Svikram 
2624621Svikram 	prev = NULL;
2625621Svikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
2626621Svikram 		lp->lineNum = ++lineNum;
2627621Svikram 
2628621Svikram 		/*
2629621Svikram 		 * Get the value of the default command
2630621Svikram 		 */
2631621Svikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
2632621Svikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
2633621Svikram 		    lp->arg) {
2634621Svikram 			old_default_value = atoi(lp->arg);
2635621Svikram 			default_lp = lp;
2636621Svikram 		}
2637621Svikram 
2638621Svikram 		/*
2639621Svikram 		 * If not boot entry, nothing else to fix for this
2640621Svikram 		 * entry
2641621Svikram 		 */
2642621Svikram 		if (lp->entryNum == ENTRY_INIT)
2643621Svikram 			continue;
2644621Svikram 
2645621Svikram 		/*
2646621Svikram 		 * Record the position of the default entry.
2647621Svikram 		 * The following works because global
2648621Svikram 		 * commands like default and timeout should precede
2649621Svikram 		 * actual boot entries, so old_default_value
2650621Svikram 		 * is already known (or default cmd is missing).
2651621Svikram 		 */
2652621Svikram 		if (default_entry == NULL &&
2653621Svikram 		    old_default_value != ENTRY_INIT &&
2654621Svikram 		    lp->entryNum == old_default_value) {
2655621Svikram 			default_entry = lp;
2656621Svikram 		}
2657621Svikram 
2658621Svikram 		/*
2659621Svikram 		 * Now fixup the entry number
2660621Svikram 		 */
2661621Svikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
2662621Svikram 			lp->entryNum = ++entryNum;
2663621Svikram 			/* fixup the bootadm header */
2664621Svikram 			if (prev && prev->flags == BAM_COMMENT &&
26653446Smrj 			    prev->arg &&
26663446Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2667621Svikram 				prev->entryNum = lp->entryNum;
2668621Svikram 			}
2669621Svikram 		} else {
2670621Svikram 			lp->entryNum = entryNum;
2671621Svikram 		}
2672621Svikram 	}
2673621Svikram 
2674621Svikram 	/*
2675621Svikram 	 * No default command in menu, simply return
2676621Svikram 	 */
2677621Svikram 	if (default_lp == NULL) {
2678621Svikram 		return;
2679621Svikram 	}
2680621Svikram 
2681621Svikram 	free(default_lp->arg);
2682621Svikram 	free(default_lp->line);
2683621Svikram 
2684621Svikram 	if (default_entry == NULL) {
2685621Svikram 		default_lp->arg = s_strdup("0");
2686621Svikram 	} else {
2687621Svikram 		(void) snprintf(buf, sizeof (buf), "%d",
2688621Svikram 		    default_entry->entryNum);
2689621Svikram 		default_lp->arg = s_strdup(buf);
2690621Svikram 	}
2691621Svikram 
2692621Svikram 	/*
2693621Svikram 	 * The following is required since only the line field gets
2694621Svikram 	 * written back to menu.lst
2695621Svikram 	 */
2696621Svikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
2697621Svikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
2698621Svikram 	default_lp->line = s_strdup(buf);
2699621Svikram }
2700621Svikram 
2701621Svikram 
27020Sstevel@tonic-gate static menu_t *
27030Sstevel@tonic-gate menu_read(char *menu_path)
27040Sstevel@tonic-gate {
27050Sstevel@tonic-gate 	FILE *fp;
27060Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
27070Sstevel@tonic-gate 	menu_t *mp;
27080Sstevel@tonic-gate 	int line, entry, len, n;
27090Sstevel@tonic-gate 
27100Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
27110Sstevel@tonic-gate 
27120Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
27130Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
27140Sstevel@tonic-gate 		return (mp);
27150Sstevel@tonic-gate 	}
27160Sstevel@tonic-gate 
27170Sstevel@tonic-gate 
27180Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
27190Sstevel@tonic-gate 	line = LINE_INIT;
27200Sstevel@tonic-gate 	entry = ENTRY_INIT;
27210Sstevel@tonic-gate 	cp = buf;
27220Sstevel@tonic-gate 	len = sizeof (buf);
27230Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
27240Sstevel@tonic-gate 		n = strlen(cp);
27250Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
27260Sstevel@tonic-gate 			len -= n - 1;
27270Sstevel@tonic-gate 			assert(len >= 2);
27280Sstevel@tonic-gate 			cp += n - 1;
27290Sstevel@tonic-gate 			continue;
27300Sstevel@tonic-gate 		}
27310Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
27320Sstevel@tonic-gate 		cp = buf;
27330Sstevel@tonic-gate 		len = sizeof (buf);
27340Sstevel@tonic-gate 	}
27350Sstevel@tonic-gate 
27360Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
27370Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
27380Sstevel@tonic-gate 	}
27390Sstevel@tonic-gate 
27400Sstevel@tonic-gate 	return (mp);
27410Sstevel@tonic-gate }
27420Sstevel@tonic-gate 
27430Sstevel@tonic-gate static error_t
27440Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
27450Sstevel@tonic-gate {
27460Sstevel@tonic-gate 	char *eq;
27470Sstevel@tonic-gate 	char *opt_dup;
27480Sstevel@tonic-gate 	int entryNum;
27490Sstevel@tonic-gate 
27500Sstevel@tonic-gate 	assert(mp);
27510Sstevel@tonic-gate 	assert(mp->start);
27520Sstevel@tonic-gate 	assert(opt);
27530Sstevel@tonic-gate 
27540Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
27550Sstevel@tonic-gate 
27560Sstevel@tonic-gate 	if (entry)
27570Sstevel@tonic-gate 		*entry = ENTRY_INIT;
27580Sstevel@tonic-gate 	if (title)
27590Sstevel@tonic-gate 		*title = NULL;
27600Sstevel@tonic-gate 
27610Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
27620Sstevel@tonic-gate 	if (eq == NULL) {
27630Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
27640Sstevel@tonic-gate 		free(opt_dup);
27650Sstevel@tonic-gate 		return (BAM_ERROR);
27660Sstevel@tonic-gate 	}
27670Sstevel@tonic-gate 
27680Sstevel@tonic-gate 	*eq = '\0';
27690Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
27700Sstevel@tonic-gate 		assert(mp->end);
27710Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
27720Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
27730Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
27740Sstevel@tonic-gate 			free(opt_dup);
27750Sstevel@tonic-gate 			return (BAM_ERROR);
27760Sstevel@tonic-gate 		}
27770Sstevel@tonic-gate 		*entry = entryNum;
27780Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
27790Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
27800Sstevel@tonic-gate 	} else {
27810Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
27820Sstevel@tonic-gate 		free(opt_dup);
27830Sstevel@tonic-gate 		return (BAM_ERROR);
27840Sstevel@tonic-gate 	}
27850Sstevel@tonic-gate 
27860Sstevel@tonic-gate 	free(opt_dup);
27870Sstevel@tonic-gate 	return (BAM_SUCCESS);
27880Sstevel@tonic-gate }
27890Sstevel@tonic-gate 
27900Sstevel@tonic-gate /*
27910Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
27920Sstevel@tonic-gate  * only title lines in file are printed.
27930Sstevel@tonic-gate  *
27940Sstevel@tonic-gate  * If invoked with a title or entry #, all
27950Sstevel@tonic-gate  * lines in *every* matching entry are listed
27960Sstevel@tonic-gate  */
27970Sstevel@tonic-gate static error_t
27980Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
27990Sstevel@tonic-gate {
28000Sstevel@tonic-gate 	line_t *lp;
28010Sstevel@tonic-gate 	int entry = ENTRY_INIT;
28020Sstevel@tonic-gate 	int found;
28030Sstevel@tonic-gate 	char *title = NULL;
28040Sstevel@tonic-gate 
28050Sstevel@tonic-gate 	assert(mp);
28060Sstevel@tonic-gate 	assert(menu_path);
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate 	if (mp->start == NULL) {
28090Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
28100Sstevel@tonic-gate 		return (BAM_ERROR);
28110Sstevel@tonic-gate 	}
28120Sstevel@tonic-gate 
28130Sstevel@tonic-gate 	if (opt != NULL) {
28140Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
28150Sstevel@tonic-gate 			return (BAM_ERROR);
28160Sstevel@tonic-gate 		}
28170Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
28180Sstevel@tonic-gate 	} else {
28190Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
28200Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
28210Sstevel@tonic-gate 	}
28220Sstevel@tonic-gate 
28230Sstevel@tonic-gate 	found = 0;
28240Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
28250Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
28260Sstevel@tonic-gate 			continue;
28270Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
28280Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
28290Sstevel@tonic-gate 			    lp->arg);
28300Sstevel@tonic-gate 			found = 1;
28310Sstevel@tonic-gate 			continue;
28320Sstevel@tonic-gate 		}
28330Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
28340Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
28350Sstevel@tonic-gate 			found = 1;
28360Sstevel@tonic-gate 			continue;
28370Sstevel@tonic-gate 		}
28380Sstevel@tonic-gate 
28390Sstevel@tonic-gate 		/*
28400Sstevel@tonic-gate 		 * We set the entry value here so that all lines
28410Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
28420Sstevel@tonic-gate 		 * title in other entries, all lines in those
28430Sstevel@tonic-gate 		 * entries get printed as well.
28440Sstevel@tonic-gate 		 */
28450Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
28460Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
28470Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
28480Sstevel@tonic-gate 			entry = lp->entryNum;
28490Sstevel@tonic-gate 			found = 1;
28500Sstevel@tonic-gate 			continue;
28510Sstevel@tonic-gate 		}
28520Sstevel@tonic-gate 	}
28530Sstevel@tonic-gate 
28540Sstevel@tonic-gate 	if (!found) {
28550Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
28560Sstevel@tonic-gate 		return (BAM_ERROR);
28570Sstevel@tonic-gate 	}
28580Sstevel@tonic-gate 
28590Sstevel@tonic-gate 	return (BAM_SUCCESS);
28600Sstevel@tonic-gate }
28610Sstevel@tonic-gate 
28625084Sjohnlev int
28630Sstevel@tonic-gate add_boot_entry(menu_t *mp,
28640Sstevel@tonic-gate 	char *title,
28650Sstevel@tonic-gate 	char *root,
28660Sstevel@tonic-gate 	char *kernel,
28675084Sjohnlev 	char *mod_kernel,
28680Sstevel@tonic-gate 	char *module)
28690Sstevel@tonic-gate {
28700Sstevel@tonic-gate 	int lineNum, entryNum;
28710Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
28723446Smrj 	menu_cmd_t k_cmd, m_cmd;
28730Sstevel@tonic-gate 
28740Sstevel@tonic-gate 	assert(mp);
28750Sstevel@tonic-gate 
28760Sstevel@tonic-gate 	if (title == NULL) {
2877656Sszhou 		title = "Solaris";	/* default to Solaris */
28780Sstevel@tonic-gate 	}
28790Sstevel@tonic-gate 	if (kernel == NULL) {
28800Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
28810Sstevel@tonic-gate 		return (BAM_ERROR);
28820Sstevel@tonic-gate 	}
28830Sstevel@tonic-gate 	if (module == NULL) {
28843446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
28853446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
28863446Smrj 			return (BAM_ERROR);
28873446Smrj 		}
28883446Smrj 
28893446Smrj 		/* Figure the commands out from the kernel line */
28903446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
28913446Smrj 			module = DIRECT_BOOT_ARCHIVE;
28923446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
28933446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
28943446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
28953446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
28963446Smrj 			k_cmd = KERNEL_CMD;
28973446Smrj 			m_cmd = MODULE_CMD;
28983446Smrj 		} else {
28993446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
29003446Smrj 			k_cmd = KERNEL_CMD;
29013446Smrj 			m_cmd = MODULE_CMD;
29023446Smrj 		}
29033446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
29043446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
29053446Smrj 		/*
29063446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
29073446Smrj 		 * command.  Otherwise, use "kernel".
29083446Smrj 		 */
29093446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
29103446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
29113446Smrj 	} else {
29123446Smrj 		k_cmd = KERNEL_CMD;
29133446Smrj 		m_cmd = MODULE_CMD;
29140Sstevel@tonic-gate 	}
29150Sstevel@tonic-gate 
29160Sstevel@tonic-gate 	if (mp->start) {
29170Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
29180Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
29190Sstevel@tonic-gate 	} else {
29200Sstevel@tonic-gate 		lineNum = LINE_INIT;
29210Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
29220Sstevel@tonic-gate 	}
29230Sstevel@tonic-gate 
29240Sstevel@tonic-gate 	/*
29250Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
29260Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
29270Sstevel@tonic-gate 	 */
29280Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
29293446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
2930662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29310Sstevel@tonic-gate 
29320Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29330Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
2934662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
2935662Sszhou 
2936662Sszhou 	if (root) {
2937662Sszhou 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2938662Sszhou 		    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
2939662Sszhou 		line_parser(mp, linebuf, &lineNum, &entryNum);
29400Sstevel@tonic-gate 	}
29410Sstevel@tonic-gate 
29420Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29433446Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
2944662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29450Sstevel@tonic-gate 
29465084Sjohnlev 	if (mod_kernel != NULL) {
29475084Sjohnlev 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29485084Sjohnlev 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
29495084Sjohnlev 		line_parser(mp, linebuf, &lineNum, &entryNum);
29505084Sjohnlev 	}
29515084Sjohnlev 
29520Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29533446Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
2954662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29550Sstevel@tonic-gate 
29560Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
29573446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
2958662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29590Sstevel@tonic-gate 
29600Sstevel@tonic-gate 	return (entryNum);
29610Sstevel@tonic-gate }
29620Sstevel@tonic-gate 
29630Sstevel@tonic-gate static error_t
29640Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
29650Sstevel@tonic-gate {
2966662Sszhou 	line_t *lp, *freed;
2967662Sszhou 	entry_t *ent, *tmp;
29680Sstevel@tonic-gate 	int deleted;
29690Sstevel@tonic-gate 
29700Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
29710Sstevel@tonic-gate 
2972662Sszhou 	ent = mp->entries;
2973662Sszhou 	while (ent) {
2974662Sszhou 		lp = ent->start;
2975662Sszhou 		/* check entry number and make sure it's a bootadm entry */
2976662Sszhou 		if (lp->flags != BAM_COMMENT ||
29773446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
2978662Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
2979662Sszhou 			ent = ent->next;
29800Sstevel@tonic-gate 			continue;
29810Sstevel@tonic-gate 		}
29820Sstevel@tonic-gate 
2983662Sszhou 		/* free the entry content */
2984662Sszhou 		do {
2985662Sszhou 			freed = lp;
2986662Sszhou 			lp = lp->next;	/* prev stays the same */
2987662Sszhou 			unlink_line(mp, freed);
2988662Sszhou 			line_free(freed);
2989662Sszhou 		} while (freed != ent->end);
2990662Sszhou 
2991662Sszhou 		/* free the entry_t structure */
2992662Sszhou 		tmp = ent;
2993662Sszhou 		ent = ent->next;
2994662Sszhou 		if (tmp->prev)
2995662Sszhou 			tmp->prev->next = ent;
29960Sstevel@tonic-gate 		else
2997662Sszhou 			mp->entries = ent;
2998662Sszhou 		if (ent)
2999662Sszhou 			ent->prev = tmp->prev;
30000Sstevel@tonic-gate 		deleted = 1;
30010Sstevel@tonic-gate 	}
30020Sstevel@tonic-gate 
30030Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
30040Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
30050Sstevel@tonic-gate 		return (BAM_ERROR);
30060Sstevel@tonic-gate 	}
30070Sstevel@tonic-gate 
3008621Svikram 	/*
3009621Svikram 	 * Now that we have deleted an entry, update
3010621Svikram 	 * the entry numbering and the default cmd.
3011621Svikram 	 */
3012621Svikram 	update_numbering(mp);
3013621Svikram 
30140Sstevel@tonic-gate 	return (BAM_SUCCESS);
30150Sstevel@tonic-gate }
30160Sstevel@tonic-gate 
30170Sstevel@tonic-gate static error_t
30180Sstevel@tonic-gate delete_all_entries(menu_t *mp, char *menu_path, char *opt)
30190Sstevel@tonic-gate {
30200Sstevel@tonic-gate 	assert(mp);
30210Sstevel@tonic-gate 	assert(opt == NULL);
30220Sstevel@tonic-gate 
30230Sstevel@tonic-gate 	if (mp->start == NULL) {
30240Sstevel@tonic-gate 		bam_print(EMPTY_FILE, menu_path);
30250Sstevel@tonic-gate 		return (BAM_SUCCESS);
30260Sstevel@tonic-gate 	}
30270Sstevel@tonic-gate 
30280Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
30290Sstevel@tonic-gate 		return (BAM_ERROR);
30300Sstevel@tonic-gate 	}
30310Sstevel@tonic-gate 
30320Sstevel@tonic-gate 	return (BAM_WRITE);
30330Sstevel@tonic-gate }
30340Sstevel@tonic-gate 
30350Sstevel@tonic-gate static FILE *
3036662Sszhou open_diskmap(char *root)
30370Sstevel@tonic-gate {
30380Sstevel@tonic-gate 	FILE *fp;
30390Sstevel@tonic-gate 	char cmd[PATH_MAX];
30400Sstevel@tonic-gate 
30410Sstevel@tonic-gate 	/* make sure we have a map file */
30420Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
30430Sstevel@tonic-gate 	if (fp == NULL) {
30440Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
3045*5648Ssetje 		    "%s/%s > /dev/null", root, CREATE_DISKMAP);
30460Sstevel@tonic-gate 		(void) system(cmd);
30470Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
30480Sstevel@tonic-gate 	}
30490Sstevel@tonic-gate 	return (fp);
30500Sstevel@tonic-gate }
30510Sstevel@tonic-gate 
30520Sstevel@tonic-gate #define	SECTOR_SIZE	512
30530Sstevel@tonic-gate 
30540Sstevel@tonic-gate static int
30550Sstevel@tonic-gate get_partition(char *device)
30560Sstevel@tonic-gate {
30570Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
30580Sstevel@tonic-gate 	struct mboot *mboot;
30590Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
30600Sstevel@tonic-gate 	char *wholedisk, *slice;
30610Sstevel@tonic-gate 
30620Sstevel@tonic-gate 	/* form whole disk (p0) */
30630Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
30640Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
30650Sstevel@tonic-gate 	if (!is_pcfs)
30660Sstevel@tonic-gate 		*slice = '\0';
30670Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
30680Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
30690Sstevel@tonic-gate 	if (!is_pcfs)
30700Sstevel@tonic-gate 		*slice = 's';
30710Sstevel@tonic-gate 
30720Sstevel@tonic-gate 	/* read boot sector */
30730Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
30740Sstevel@tonic-gate 	free(wholedisk);
30750Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
30760Sstevel@tonic-gate 		return (partno);
30770Sstevel@tonic-gate 	}
30780Sstevel@tonic-gate 	(void) close(fd);
30790Sstevel@tonic-gate 
30800Sstevel@tonic-gate 	/* parse fdisk table */
30810Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
30820Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
30830Sstevel@tonic-gate 		struct ipart *part =
30840Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
30850Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
30860Sstevel@tonic-gate 			if (part->systid == 0xbe) {
30870Sstevel@tonic-gate 				partno = i;
30880Sstevel@tonic-gate 				break;
30890Sstevel@tonic-gate 			}
30900Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
30910Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
30920Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
30930Sstevel@tonic-gate 				partno = i;
30940Sstevel@tonic-gate 				break;
30950Sstevel@tonic-gate 			}
30960Sstevel@tonic-gate 		}
30970Sstevel@tonic-gate 	}
30980Sstevel@tonic-gate 	return (partno);
30990Sstevel@tonic-gate }
31000Sstevel@tonic-gate 
31010Sstevel@tonic-gate static char *
31020Sstevel@tonic-gate get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
31030Sstevel@tonic-gate {
31040Sstevel@tonic-gate 	char *grubdisk;	/* (hd#,#,#) */
31050Sstevel@tonic-gate 	char *slice;
31060Sstevel@tonic-gate 	char *grubhd;
31070Sstevel@tonic-gate 	int fdiskpart;
31080Sstevel@tonic-gate 	int found = 0;
31090Sstevel@tonic-gate 	char *devname, *ctdname = strstr(rootdev, "dsk/");
31100Sstevel@tonic-gate 	char linebuf[PATH_MAX];
31110Sstevel@tonic-gate 
31120Sstevel@tonic-gate 	if (ctdname == NULL)
31130Sstevel@tonic-gate 		return (NULL);
31140Sstevel@tonic-gate 
31150Sstevel@tonic-gate 	ctdname += strlen("dsk/");
31160Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
31170Sstevel@tonic-gate 	if (slice)
31180Sstevel@tonic-gate 		*slice = '\0';
31190Sstevel@tonic-gate 
31200Sstevel@tonic-gate 	rewind(fp);
31210Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
31220Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
31230Sstevel@tonic-gate 		if (grubhd)
31240Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
31250Sstevel@tonic-gate 		else
31260Sstevel@tonic-gate 			devname = NULL;
31270Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
31280Sstevel@tonic-gate 			found = 1;
31290Sstevel@tonic-gate 			break;
31300Sstevel@tonic-gate 		}
31310Sstevel@tonic-gate 	}
31320Sstevel@tonic-gate 
31330Sstevel@tonic-gate 	if (slice)
31340Sstevel@tonic-gate 		*slice = 's';
31350Sstevel@tonic-gate 
31360Sstevel@tonic-gate 	if (found == 0) {
31370Sstevel@tonic-gate 		if (bam_verbose)
31380Sstevel@tonic-gate 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
31390Sstevel@tonic-gate 		grubhd = "0";	/* assume disk 0 if can't match */
31400Sstevel@tonic-gate 	}
31410Sstevel@tonic-gate 
31420Sstevel@tonic-gate 	fdiskpart = get_partition(rootdev);
31430Sstevel@tonic-gate 	if (fdiskpart == -1)
31440Sstevel@tonic-gate 		return (NULL);
31450Sstevel@tonic-gate 
31460Sstevel@tonic-gate 	grubdisk = s_calloc(1, 10);
31470Sstevel@tonic-gate 	if (slice) {
31480Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
31490Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
31500Sstevel@tonic-gate 	} else
31510Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
31520Sstevel@tonic-gate 		    grubhd, fdiskpart);
31530Sstevel@tonic-gate 
31540Sstevel@tonic-gate 	/* if root not on bootdev, change GRUB disk to 0 */
31550Sstevel@tonic-gate 	if (!on_bootdev)
31560Sstevel@tonic-gate 		grubdisk[3] = '0';
31570Sstevel@tonic-gate 	return (grubdisk);
31580Sstevel@tonic-gate }
31590Sstevel@tonic-gate 
3160656Sszhou static char *
3161656Sszhou get_title(char *rootdir)
31620Sstevel@tonic-gate {
31630Sstevel@tonic-gate 	static char title[80];	/* from /etc/release */
3164662Sszhou 	char *cp = NULL, release[PATH_MAX];
31650Sstevel@tonic-gate 	FILE *fp;
31660Sstevel@tonic-gate 
31670Sstevel@tonic-gate 	/* open the /etc/release file */
31680Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
31690Sstevel@tonic-gate 
31700Sstevel@tonic-gate 	fp = fopen(release, "r");
31710Sstevel@tonic-gate 	if (fp == NULL)
3172656Sszhou 		return (NULL);
31730Sstevel@tonic-gate 
31740Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
31750Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
31760Sstevel@tonic-gate 		if (cp)
31770Sstevel@tonic-gate 			break;
31780Sstevel@tonic-gate 	}
31790Sstevel@tonic-gate 	(void) fclose(fp);
3180662Sszhou 	return (cp == NULL ? "Solaris" : cp);
31810Sstevel@tonic-gate }
31820Sstevel@tonic-gate 
31833446Smrj char *
31840Sstevel@tonic-gate get_special(char *mountp)
31850Sstevel@tonic-gate {
31860Sstevel@tonic-gate 	FILE *mntfp;
31870Sstevel@tonic-gate 	struct mnttab mp = {0}, mpref = {0};
31880Sstevel@tonic-gate 
31890Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
31900Sstevel@tonic-gate 	if (mntfp == NULL) {
31910Sstevel@tonic-gate 		return (0);
31920Sstevel@tonic-gate 	}
31930Sstevel@tonic-gate 
31940Sstevel@tonic-gate 	if (*mountp == '\0')
31950Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
31960Sstevel@tonic-gate 	else
31970Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
31980Sstevel@tonic-gate 	if (getmntany(mntfp, &mp, &mpref) != 0) {
31990Sstevel@tonic-gate 		(void) fclose(mntfp);
32000Sstevel@tonic-gate 		return (NULL);
32010Sstevel@tonic-gate 	}
32020Sstevel@tonic-gate 	(void) fclose(mntfp);
32030Sstevel@tonic-gate 
32040Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
32050Sstevel@tonic-gate }
32060Sstevel@tonic-gate 
32073446Smrj char *
32080Sstevel@tonic-gate os_to_grubdisk(char *osdisk, int on_bootdev)
32090Sstevel@tonic-gate {
32100Sstevel@tonic-gate 	FILE *fp;
32110Sstevel@tonic-gate 	char *grubdisk;
32120Sstevel@tonic-gate 
32130Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3214662Sszhou 	fp = open_diskmap("");
32150Sstevel@tonic-gate 	if (fp == NULL) {
32160Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdisk);
32170Sstevel@tonic-gate 		return (NULL);
32180Sstevel@tonic-gate 	}
32190Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
32200Sstevel@tonic-gate 	(void) fclose(fp);
32210Sstevel@tonic-gate 	return (grubdisk);
32220Sstevel@tonic-gate }
32230Sstevel@tonic-gate 
32240Sstevel@tonic-gate /*
32250Sstevel@tonic-gate  * Check if root is on the boot device
32260Sstevel@tonic-gate  * Return 0 (false) on error
32270Sstevel@tonic-gate  */
32280Sstevel@tonic-gate static int
32290Sstevel@tonic-gate menu_on_bootdev(char *menu_root, FILE *fp)
32300Sstevel@tonic-gate {
32310Sstevel@tonic-gate 	int ret;
32320Sstevel@tonic-gate 	char *grubhd, *bootp, *special;
32330Sstevel@tonic-gate 
32340Sstevel@tonic-gate 	special = get_special(menu_root);
32350Sstevel@tonic-gate 	if (special == NULL)
32360Sstevel@tonic-gate 		return (0);
32370Sstevel@tonic-gate 	bootp = strstr(special, "p0:boot");
32380Sstevel@tonic-gate 	if (bootp)
32390Sstevel@tonic-gate 		*bootp = '\0';
32400Sstevel@tonic-gate 	grubhd = get_grubdisk(special, fp, 1);
32410Sstevel@tonic-gate 	free(special);
32420Sstevel@tonic-gate 
32430Sstevel@tonic-gate 	if (grubhd == NULL)
32440Sstevel@tonic-gate 		return (0);
32450Sstevel@tonic-gate 	ret = grubhd[3] == '0';
32460Sstevel@tonic-gate 	free(grubhd);
32470Sstevel@tonic-gate 	return (ret);
32480Sstevel@tonic-gate }
32490Sstevel@tonic-gate 
3250662Sszhou /*
3251662Sszhou  * look for matching bootadm entry with specified parameters
3252662Sszhou  * Here are the rules (based on existing usage):
3253662Sszhou  * - If title is specified, match on title only
32545084Sjohnlev  * - Else, match on kernel, grubdisk and module.  Note that, if root_opt is
32555084Sjohnlev  *   non-zero, the absence of root line is considered a match.
3256662Sszhou  */
3257662Sszhou static entry_t *
32585084Sjohnlev find_boot_entry(menu_t *mp, char *title, char *kernel, char *root,
32595084Sjohnlev     char *module, int root_opt, int *entry_num)
3260662Sszhou {
3261662Sszhou 	int i;
3262662Sszhou 	line_t *lp;
3263662Sszhou 	entry_t *ent;
3264662Sszhou 
3265662Sszhou 	/* find matching entry */
3266662Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
3267662Sszhou 		lp = ent->start;
3268662Sszhou 
3269662Sszhou 		/* first line of entry must be bootadm comment */
3270662Sszhou 		lp = ent->start;
32713446Smrj 		if (lp->flags != BAM_COMMENT ||
32723446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
3273662Sszhou 			continue;
3274662Sszhou 		}
3275662Sszhou 
3276662Sszhou 		/* advance to title line */
3277662Sszhou 		lp = lp->next;
3278662Sszhou 		if (title) {
3279662Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
3280662Sszhou 			    strcmp(lp->arg, title) == 0)
3281662Sszhou 				break;
3282662Sszhou 			continue;	/* check title only */
3283662Sszhou 		}
3284662Sszhou 
3285662Sszhou 		lp = lp->next;	/* advance to root line */
32865084Sjohnlev 		if (lp == NULL) {
32875084Sjohnlev 			continue;
32885084Sjohnlev 		} else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3289662Sszhou 			/* root command found, match grub disk */
3290662Sszhou 			if (strcmp(lp->arg, root) != 0) {
3291662Sszhou 				continue;
3292662Sszhou 			}
3293662Sszhou 			lp = lp->next;	/* advance to kernel line */
3294662Sszhou 		} else {
3295662Sszhou 			/* no root command, see if root is optional */
3296662Sszhou 			if (root_opt == 0) {
3297662Sszhou 				continue;
3298662Sszhou 			}
3299662Sszhou 		}
3300662Sszhou 
3301662Sszhou 		if (lp == NULL || lp->next == NULL) {
3302662Sszhou 			continue;
3303662Sszhou 		}
3304662Sszhou 
33055084Sjohnlev 		if (kernel &&
33065084Sjohnlev 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
33075084Sjohnlev 			continue;
33085084Sjohnlev 		}
33095084Sjohnlev 
33103467Srscott 		/*
33115084Sjohnlev 		 * Check for matching module entry (failsafe or normal).
33125084Sjohnlev 		 * If it fails to match, we go around the loop again.
33135084Sjohnlev 		 * For xpv entries, there are two module lines, so we
33145084Sjohnlev 		 * do the check twice.
33153467Srscott 		 */
3316662Sszhou 		lp = lp->next;	/* advance to module line */
33175084Sjohnlev 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
33185084Sjohnlev 		    (((lp = lp->next) != NULL) &&
33195084Sjohnlev 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
33205084Sjohnlev 			/* match found */
33215084Sjohnlev 			break;
3322662Sszhou 		}
33235084Sjohnlev 	}
33245084Sjohnlev 
33255084Sjohnlev 	if (entry_num && ent) {
33265084Sjohnlev 		*entry_num = i;
33275084Sjohnlev 	}
3328662Sszhou 	return (ent);
3329662Sszhou }
3330662Sszhou 
3331662Sszhou static int
3332662Sszhou update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
33335084Sjohnlev     char *mod_kernel, char *module, int root_opt)
3334662Sszhou {
33353446Smrj 	int i, change_kernel = 0;
3336662Sszhou 	entry_t *ent;
3337662Sszhou 	line_t *lp;
3338662Sszhou 	char linebuf[BAM_MAXLINE];
3339662Sszhou 
3340662Sszhou 	/* note: don't match on title, it's updated on upgrade */
33415084Sjohnlev 	ent = find_boot_entry(mp, NULL, kernel, root, module, root_opt, &i);
33423446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
33433446Smrj 		/*
33443446Smrj 		 * We may be upgrading a kernel from multiboot to
33453446Smrj 		 * directboot.  Look for a multiboot entry.
33463446Smrj 		 */
33475084Sjohnlev 		ent = find_boot_entry(mp, NULL, "multiboot", root,
33485084Sjohnlev 		    MULTI_BOOT_ARCHIVE, root_opt, NULL);
33493446Smrj 		if (ent != NULL) {
33503446Smrj 			change_kernel = 1;
33513446Smrj 		}
33523446Smrj 	}
3353662Sszhou 	if (ent == NULL)
3354662Sszhou 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
33555084Sjohnlev 		    kernel, mod_kernel, module));
3356662Sszhou 
3357662Sszhou 	/* replace title of exiting entry and delete root line */
3358662Sszhou 	lp = ent->start;
3359662Sszhou 	lp = lp->next;	/* title line */
3360662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3361662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3362662Sszhou 	free(lp->arg);
3363662Sszhou 	free(lp->line);
3364662Sszhou 	lp->arg = s_strdup(title);
3365662Sszhou 	lp->line = s_strdup(linebuf);
3366662Sszhou 
3367662Sszhou 	lp = lp->next;	/* root line */
3368662Sszhou 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3369662Sszhou 		if (root_opt) {		/* root line not needed */
3370662Sszhou 			line_t *tmp = lp;
3371662Sszhou 			lp = lp->next;
3372662Sszhou 			unlink_line(mp, tmp);
3373662Sszhou 			line_free(tmp);
3374662Sszhou 		} else
3375662Sszhou 			lp = lp->next;
3376662Sszhou 	}
33773446Smrj 
33783446Smrj 	if (change_kernel) {
33793446Smrj 		/*
33803446Smrj 		 * We're upgrading from multiboot to directboot.
33813446Smrj 		 */
33823446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
33833446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
33843446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
33853446Smrj 			    kernel);
33863446Smrj 			free(lp->arg);
33873446Smrj 			free(lp->line);
33883446Smrj 			lp->arg = s_strdup(kernel);
33893446Smrj 			lp->line = s_strdup(linebuf);
33903446Smrj 			lp = lp->next;
33913446Smrj 		}
33923446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
33933446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
33943446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
33953446Smrj 			    module);
33963446Smrj 			free(lp->arg);
33973446Smrj 			free(lp->line);
33983446Smrj 			lp->arg = s_strdup(module);
33993446Smrj 			lp->line = s_strdup(linebuf);
34003446Smrj 			lp = lp->next;
34013446Smrj 		}
34023446Smrj 	}
3403662Sszhou 	return (i);
3404662Sszhou }
3405662Sszhou 
34060Sstevel@tonic-gate /*ARGSUSED*/
34070Sstevel@tonic-gate static error_t
34080Sstevel@tonic-gate update_entry(menu_t *mp, char *menu_root, char *opt)
34090Sstevel@tonic-gate {
34100Sstevel@tonic-gate 	FILE *fp;
34110Sstevel@tonic-gate 	int entry;
34123449Srscott 	char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL;
3413662Sszhou 	struct stat sbuf;
3414662Sszhou 	char failsafe[256];
34150Sstevel@tonic-gate 
34160Sstevel@tonic-gate 	assert(mp);
34170Sstevel@tonic-gate 	assert(opt);
34180Sstevel@tonic-gate 
34190Sstevel@tonic-gate 	osdev = strtok(opt, ",");
34200Sstevel@tonic-gate 	osroot = strtok(NULL, ",");
34210Sstevel@tonic-gate 	if (osroot == NULL)
34220Sstevel@tonic-gate 		osroot = menu_root;
34230Sstevel@tonic-gate 	title = get_title(osroot);
34240Sstevel@tonic-gate 
34250Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3426662Sszhou 	fp = open_diskmap(osroot);
34270Sstevel@tonic-gate 	if (fp == NULL) {
34280Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
34290Sstevel@tonic-gate 		return (BAM_ERROR);
34300Sstevel@tonic-gate 	}
34310Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
34320Sstevel@tonic-gate 	(void) fclose(fp);
34330Sstevel@tonic-gate 	if (grubdisk == NULL) {
34340Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
34350Sstevel@tonic-gate 		return (BAM_ERROR);
34360Sstevel@tonic-gate 	}
34370Sstevel@tonic-gate 
34380Sstevel@tonic-gate 	/* add the entry for normal Solaris */
34393446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
34403446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
34415084Sjohnlev 		    DIRECT_BOOT_KERNEL, NULL, DIRECT_BOOT_ARCHIVE,
34423446Smrj 		    osroot == menu_root);
34435084Sjohnlev 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
34445084Sjohnlev 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubdisk,
34455084Sjohnlev 			    XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE,
34465084Sjohnlev 			    osroot == menu_root);
34475084Sjohnlev 		}
34483446Smrj 	} else {
34495084Sjohnlev 		entry = update_boot_entry(mp, title, grubdisk, MULTI_BOOT,
34505084Sjohnlev 		    NULL, MULTI_BOOT_ARCHIVE, osroot == menu_root);
34515084Sjohnlev 	}
34525084Sjohnlev 
34535084Sjohnlev 	/*
34545084Sjohnlev 	 * Add the entry for failsafe archive.  On a bfu'd system, the
34555084Sjohnlev 	 * failsafe may be different than the installed kernel.
34565084Sjohnlev 	 */
34573446Smrj 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
34583446Smrj 	if (stat(failsafe, &sbuf) == 0) {
34593449Srscott 
34603449Srscott 		/* Figure out where the kernel line should point */
34613449Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
34623449Srscott 		    DIRECT_BOOT_FAILSAFE_KERNEL);
34633449Srscott 		if (stat(failsafe, &sbuf) == 0) {
34643449Srscott 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
34653449Srscott 		} else {
34663449Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
34673449Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
34683449Srscott 			if (stat(failsafe, &sbuf) == 0) {
34693449Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
34703449Srscott 			}
34713449Srscott 		}
34723449Srscott 		if (failsafe_kernel != NULL) {
34733449Srscott 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
34745084Sjohnlev 			    failsafe_kernel, NULL, MINIROOT,
34755084Sjohnlev 			    osroot == menu_root);
34763449Srscott 		}
34773446Smrj 	}
34780Sstevel@tonic-gate 	free(grubdisk);
34790Sstevel@tonic-gate 
34800Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
34810Sstevel@tonic-gate 		return (BAM_ERROR);
34820Sstevel@tonic-gate 	}
34830Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
34840Sstevel@tonic-gate 	return (BAM_WRITE);
34850Sstevel@tonic-gate }
34860Sstevel@tonic-gate 
3487316Svikram static char *
3488316Svikram read_grub_root(void)
3489316Svikram {
3490316Svikram 	FILE *fp;
3491316Svikram 	struct stat sb;
3492316Svikram 	char buf[BAM_MAXLINE];
3493316Svikram 	char *rootstr;
3494316Svikram 
3495316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3496316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3497316Svikram 		return (NULL);
3498316Svikram 	}
3499316Svikram 
3500316Svikram 	if (stat(GRUB_root, &sb) != 0) {
3501316Svikram 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3502316Svikram 		return (NULL);
3503316Svikram 	}
3504316Svikram 
3505316Svikram 	fp = fopen(GRUB_root, "r");
3506316Svikram 	if (fp == NULL) {
3507316Svikram 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3508316Svikram 		return (NULL);
3509316Svikram 	}
3510316Svikram 
3511316Svikram 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3512316Svikram 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3513316Svikram 		(void) fclose(fp);
3514316Svikram 		return (NULL);
3515316Svikram 	}
3516316Svikram 
3517316Svikram 	/*
3518316Svikram 	 * Copy buf here as check below may trash the buffer
3519316Svikram 	 */
3520316Svikram 	rootstr = s_strdup(buf);
3521316Svikram 
3522316Svikram 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3523316Svikram 		bam_error(BAD_ROOT_FILE, GRUB_root);
3524316Svikram 		free(rootstr);
3525316Svikram 		rootstr = NULL;
3526316Svikram 	}
3527316Svikram 
3528316Svikram 	(void) fclose(fp);
3529316Svikram 
3530316Svikram 	return (rootstr);
3531316Svikram }
3532316Svikram 
3533662Sszhou static void
35343446Smrj save_default_entry(menu_t *mp, const char *which)
3535662Sszhou {
3536662Sszhou 	int lineNum, entryNum;
3537662Sszhou 	int entry = 0;	/* default is 0 */
3538662Sszhou 	char linebuf[BAM_MAXLINE];
3539662Sszhou 	line_t *lp = mp->curdefault;
3540662Sszhou 
35413381Svikram 	if (mp->start) {
35423381Svikram 		lineNum = mp->end->lineNum;
35433381Svikram 		entryNum = mp->end->entryNum;
35443381Svikram 	} else {
35453381Svikram 		lineNum = LINE_INIT;
35463381Svikram 		entryNum = ENTRY_INIT;
35473381Svikram 	}
35483381Svikram 
3549662Sszhou 	if (lp)
3550662Sszhou 		entry = s_strtol(lp->arg);
3551662Sszhou 
35523446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
3553662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
3554662Sszhou }
3555662Sszhou 
3556662Sszhou static void
35573446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
3558662Sszhou {
3559662Sszhou 	int entry;
3560662Sszhou 	char *str;
3561662Sszhou 
3562662Sszhou 	if (lp == NULL)
3563662Sszhou 		return;		/* nothing to restore */
3564662Sszhou 
35653446Smrj 	str = lp->arg + strlen(which);
3566662Sszhou 	entry = s_strtol(str);
3567662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3568662Sszhou 
3569662Sszhou 	/* delete saved old default line */
3570662Sszhou 	unlink_line(mp, lp);
3571662Sszhou 	line_free(lp);
3572662Sszhou }
3573662Sszhou 
35740Sstevel@tonic-gate /*
35750Sstevel@tonic-gate  * This function is for supporting reboot with args.
35760Sstevel@tonic-gate  * The opt value can be:
35770Sstevel@tonic-gate  * NULL		delete temp entry, if present
35780Sstevel@tonic-gate  * entry=#	switches default entry to 1
35790Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
35800Sstevel@tonic-gate  *		and make it the default
35810Sstevel@tonic-gate  */
35820Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
35830Sstevel@tonic-gate 
3584662Sszhou /*ARGSUSED*/
35850Sstevel@tonic-gate static error_t
35860Sstevel@tonic-gate update_temp(menu_t *mp, char *menupath, char *opt)
35870Sstevel@tonic-gate {
35880Sstevel@tonic-gate 	int entry;
35894346Srscott 	char *grubdisk, *rootdev, *path, *opt_ptr;
35903446Smrj 	char kernbuf[BUFSIZ];
35913446Smrj 	char args_buf[BUFSIZ];
3592316Svikram 	struct stat sb;
35930Sstevel@tonic-gate 
35940Sstevel@tonic-gate 	assert(mp);
35950Sstevel@tonic-gate 
3596662Sszhou 	/* If no option, delete exiting reboot menu entry */
3597662Sszhou 	if (opt == NULL) {
3598662Sszhou 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
35995084Sjohnlev 		    NULL, 0, &entry);
3600662Sszhou 		if (ent == NULL)	/* not found is ok */
3601662Sszhou 			return (BAM_SUCCESS);
3602662Sszhou 		(void) do_delete(mp, entry);
36033446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
36043446Smrj 		mp->olddefault = NULL;
3605662Sszhou 		return (BAM_WRITE);
3606662Sszhou 	}
3607662Sszhou 
3608662Sszhou 	/* if entry= is specified, set the default entry */
3609662Sszhou 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
36100Sstevel@tonic-gate 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
36110Sstevel@tonic-gate 		/* this is entry=# option */
36120Sstevel@tonic-gate 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
36130Sstevel@tonic-gate 	}
36140Sstevel@tonic-gate 
36150Sstevel@tonic-gate 	/*
36160Sstevel@tonic-gate 	 * add a new menu entry base on opt and make it the default
36170Sstevel@tonic-gate 	 */
3618316Svikram 	grubdisk = NULL;
3619316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3620316Svikram 		/*
3621316Svikram 		 * 1. First get root disk name from mnttab
3622316Svikram 		 * 2. Translate disk name to grub name
3623316Svikram 		 * 3. Add the new menu entry
3624316Svikram 		 */
3625316Svikram 		rootdev = get_special("/");
3626316Svikram 		if (rootdev) {
3627316Svikram 			grubdisk = os_to_grubdisk(rootdev, 1);
3628316Svikram 			free(rootdev);
3629316Svikram 		}
3630316Svikram 	} else {
3631316Svikram 		/*
3632316Svikram 		 * This is an LU BE. The GRUB_root file
3633316Svikram 		 * contains entry for GRUB's "root" cmd.
3634316Svikram 		 */
3635316Svikram 		grubdisk = read_grub_root();
36360Sstevel@tonic-gate 	}
36370Sstevel@tonic-gate 	if (grubdisk == NULL) {
3638316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
36390Sstevel@tonic-gate 		return (BAM_ERROR);
36400Sstevel@tonic-gate 	}
36410Sstevel@tonic-gate 
36420Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
36433446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
36443446Smrj 		if (opt[0] == '-') {
36453446Smrj 			/* It's an option - first see if boot-file is set */
36463446Smrj 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
36473446Smrj 			    != BAM_SUCCESS)
36483446Smrj 				return (BAM_ERROR);
36493446Smrj 			if (kernbuf[0] == '\0')
36503446Smrj 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
36513446Smrj 				    BUFSIZ);
36523446Smrj 			(void) strlcat(kernbuf, " ", BUFSIZ);
36533446Smrj 			(void) strlcat(kernbuf, opt, BUFSIZ);
36543446Smrj 		} else if (opt[0] == '/') {
36554346Srscott 			/* It's a full path, so write it out. */
36563446Smrj 			(void) strlcpy(kernbuf, opt, BUFSIZ);
36574346Srscott 
36584346Srscott 			/*
36594346Srscott 			 * If someone runs:
36604346Srscott 			 *
36614346Srscott 			 *	# eeprom boot-args='-kd'
36624346Srscott 			 *	# reboot /platform/i86pc/kernel/unix
36634346Srscott 			 *
36644346Srscott 			 * we want to use the boot-args as part of the boot
36654346Srscott 			 * line.  On the other hand, if someone runs:
36664346Srscott 			 *
36674346Srscott 			 *	# reboot "/platform/i86pc/kernel/unix -kd"
36684346Srscott 			 *
36694346Srscott 			 * we don't need to mess with boot-args.  If there's
36704346Srscott 			 * no space in the options string, assume we're in the
36714346Srscott 			 * first case.
36724346Srscott 			 */
36734346Srscott 			if (strchr(opt, ' ') == NULL) {
36744346Srscott 				if (set_kernel(mp, ARGS_CMD, NULL, args_buf,
36754346Srscott 				    BUFSIZ) != BAM_SUCCESS)
36764346Srscott 					return (BAM_ERROR);
36774346Srscott 
36784346Srscott 				if (args_buf[0] != '\0') {
36794346Srscott 					(void) strlcat(kernbuf, " ", BUFSIZ);
36804346Srscott 					(void) strlcat(kernbuf, args_buf,
36814346Srscott 					    BUFSIZ);
36824346Srscott 				}
36834346Srscott 			}
36843446Smrj 		} else {
36854346Srscott 			/*
36864346Srscott 			 * It may be a partial path, or it may be a partial
36874346Srscott 			 * path followed by options.  Assume that only options
36884346Srscott 			 * follow a space.  If someone sends us a kernel path
36894346Srscott 			 * that includes a space, they deserve to be broken.
36904346Srscott 			 */
36914346Srscott 			opt_ptr = strchr(opt, ' ');
36924346Srscott 			if (opt_ptr != NULL) {
36934346Srscott 				*opt_ptr = '\0';
36944346Srscott 			}
36954346Srscott 
36963446Smrj 			path = expand_path(opt);
36973446Smrj 			if (path != NULL) {
36983446Smrj 				(void) strlcpy(kernbuf, path, BUFSIZ);
36993446Smrj 				free(path);
37004346Srscott 
37014346Srscott 				/*
37024346Srscott 				 * If there were options given, use those.
37034346Srscott 				 * Otherwise, copy over the default options.
37044346Srscott 				 */
37054346Srscott 				if (opt_ptr != NULL) {
37064346Srscott 					/* Restore the space in opt string */
37074346Srscott 					*opt_ptr = ' ';
37084346Srscott 					(void) strlcat(kernbuf, opt_ptr,
37094346Srscott 					    BUFSIZ);
37104346Srscott 				} else {
37113446Smrj 					if (set_kernel(mp, ARGS_CMD, NULL,
37123446Smrj 					    args_buf, BUFSIZ) != BAM_SUCCESS)
37133446Smrj 						return (BAM_ERROR);
37143446Smrj 
37153446Smrj 					if (args_buf[0] != '\0') {
37163446Smrj 						(void) strlcat(kernbuf, " ",
37173446Smrj 						    BUFSIZ);
37183446Smrj 						(void) strlcat(kernbuf,
37193446Smrj 						    args_buf, BUFSIZ);
37203446Smrj 					}
37213446Smrj 				}
37224346Srscott 			} else {
37234346Srscott 				bam_error(UNKNOWN_KERNEL, opt);
37244346Srscott 				bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
37254346Srscott 				return (BAM_ERROR);
37263446Smrj 			}
37273446Smrj 		}
37283446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
37295084Sjohnlev 		    NULL, NULL);
37303446Smrj 	} else {
37313446Smrj 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
37323446Smrj 		    MULTI_BOOT, opt);
37333446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
37345084Sjohnlev 		    NULL, MULTI_BOOT_ARCHIVE);
37353446Smrj 	}
37360Sstevel@tonic-gate 	free(grubdisk);
37370Sstevel@tonic-gate 
37380Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
3739316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
37400Sstevel@tonic-gate 		return (BAM_ERROR);
37410Sstevel@tonic-gate 	}
3742662Sszhou 
37433446Smrj 	save_default_entry(mp, BAM_OLDDEF);
37440Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
37450Sstevel@tonic-gate 	return (BAM_WRITE);
37460Sstevel@tonic-gate }
37470Sstevel@tonic-gate 
37480Sstevel@tonic-gate static error_t
37490Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
37500Sstevel@tonic-gate {
37510Sstevel@tonic-gate 	line_t *lp, *found, *last;
37520Sstevel@tonic-gate 	char *cp, *str;
37530Sstevel@tonic-gate 	char prefix[BAM_MAXLINE];
37540Sstevel@tonic-gate 	size_t len;
37550Sstevel@tonic-gate 
37560Sstevel@tonic-gate 	assert(mp);
37570Sstevel@tonic-gate 	assert(globalcmd);
37580Sstevel@tonic-gate 
3759316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3760316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3761316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3762316Svikram 			bam_error(INVALID_ENTRY, prefix);
3763316Svikram 			return (BAM_ERROR);
3764316Svikram 		}
3765316Svikram 	}
3766316Svikram 
37670Sstevel@tonic-gate 	found = last = NULL;
37680Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
37690Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
37700Sstevel@tonic-gate 			continue;
37710Sstevel@tonic-gate 
37720Sstevel@tonic-gate 		last = lp; /* track the last global found */
37730Sstevel@tonic-gate 
37740Sstevel@tonic-gate 		if (lp->cmd == NULL) {
37750Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
37760Sstevel@tonic-gate 			continue;
37770Sstevel@tonic-gate 		}
37780Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
37790Sstevel@tonic-gate 			continue;
37800Sstevel@tonic-gate 
37810Sstevel@tonic-gate 		if (found) {
37820Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
37830Sstevel@tonic-gate 		}
37840Sstevel@tonic-gate 		found = lp;
37850Sstevel@tonic-gate 	}
37860Sstevel@tonic-gate 
37870Sstevel@tonic-gate 	if (found == NULL) {
37880Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
37890Sstevel@tonic-gate 		if (last == NULL) {
37900Sstevel@tonic-gate 			lp->next = mp->start;
37910Sstevel@tonic-gate 			mp->start = lp;
37920Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
37930Sstevel@tonic-gate 		} else {
37940Sstevel@tonic-gate 			lp->next = last->next;
37950Sstevel@tonic-gate 			last->next = lp;
37960Sstevel@tonic-gate 			if (lp->next == NULL)
37970Sstevel@tonic-gate 				mp->end = lp;
37980Sstevel@tonic-gate 		}
37990Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
38000Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
38010Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
38020Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
38030Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
38040Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
38050Sstevel@tonic-gate 		return (BAM_WRITE);
38060Sstevel@tonic-gate 	}
38070Sstevel@tonic-gate 
38080Sstevel@tonic-gate 	/*
38090Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
38100Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
38110Sstevel@tonic-gate 	 * readability.
38120Sstevel@tonic-gate 	 */
38130Sstevel@tonic-gate 	str = found->line;
38140Sstevel@tonic-gate 	cp = prefix;
38150Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
38160Sstevel@tonic-gate 		*(cp++) = *(str++);
38170Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
38180Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
38190Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
38200Sstevel@tonic-gate 
38210Sstevel@tonic-gate 	free(found->line);
38220Sstevel@tonic-gate 	found->line = s_calloc(1, len);
38230Sstevel@tonic-gate 	(void) snprintf(found->line, len,
38244346Srscott 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
38250Sstevel@tonic-gate 
38260Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
38270Sstevel@tonic-gate }
38280Sstevel@tonic-gate 
38293446Smrj /*
38303446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
38314346Srscott  * expand it to a full unix path.  The calling function is expected to
38324346Srscott  * output a message if an error occurs and NULL is returned.
38333446Smrj  */
38343446Smrj static char *
38353446Smrj expand_path(const char *partial_path)
38363446Smrj {
38373446Smrj 	int new_path_len;
38383446Smrj 	char *new_path, new_path2[PATH_MAX];
38393446Smrj 	struct stat sb;
38403446Smrj 
38413446Smrj 	new_path_len = strlen(partial_path) + 64;
38423446Smrj 	new_path = s_calloc(1, new_path_len);
38433446Smrj 
38443446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
38453446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
38463446Smrj 	    partial_path);
38473446Smrj 	if (stat(new_path, &sb) == 0) {
38483446Smrj 		return (new_path);
38493446Smrj 	}
38503446Smrj 
38513446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
38523446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
38533446Smrj 		    DIRECT_BOOT_KERNEL);
38543446Smrj 		return (new_path);
38553446Smrj 	}
38563446Smrj 
38573446Smrj 	/*
38583446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
38593446Smrj 	 * see if we were just given a glom name.
38603446Smrj 	 */
38613446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
38623446Smrj 	    partial_path);
38633446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
38643446Smrj 	    partial_path);
38653446Smrj 	if (stat(new_path, &sb) == 0) {
38663446Smrj 		if (stat(new_path2, &sb) == 0) {
38673446Smrj 			/*
38683446Smrj 			 * We matched both, so we actually
38693446Smrj 			 * want to write the $ISADIR version.
38703446Smrj 			 */
38713446Smrj 			(void) snprintf(new_path, new_path_len,
38723446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
38733446Smrj 			    partial_path);
38743446Smrj 		}
38753446Smrj 		return (new_path);
38763446Smrj 	}
38773446Smrj 
38783446Smrj 	free(new_path);
38793446Smrj 	return (NULL);
38803446Smrj }
38813446Smrj 
38823446Smrj /*
38833446Smrj  * The kernel cmd and arg have been changed, so
38843446Smrj  * check whether the archive line needs to change.
38853446Smrj  */
38863446Smrj static void
38873446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
38883446Smrj {
38893446Smrj 	line_t *lp = entryp->start;
38903446Smrj 	char *new_archive;
38913446Smrj 	menu_cmd_t m_cmd;
38923446Smrj 
38933446Smrj 	for (; lp != NULL; lp = lp->next) {
38943446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
38953446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
38963446Smrj 			break;
38973446Smrj 		}
38983446Smrj 		if (lp == entryp->end)
38993446Smrj 			return;
39003446Smrj 	}
39013446Smrj 	if (lp == NULL)
39023446Smrj 		return;
39033446Smrj 
39043446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
39053446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
39063446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
39073446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
39083446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
39093446Smrj 		m_cmd = MODULE_CMD;
39103446Smrj 	} else {
39113446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
39123446Smrj 		m_cmd = MODULE_CMD;
39133446Smrj 	}
39143446Smrj 
39153446Smrj 	if (strcmp(lp->arg, new_archive) == 0)
39163446Smrj 		return;
39173446Smrj 
39183446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
39193446Smrj 		free(lp->cmd);
39203446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
39213446Smrj 	}
39223446Smrj 
39233446Smrj 	free(lp->arg);
39243446Smrj 	lp->arg = s_strdup(new_archive);
39253446Smrj 	update_line(lp);
39263446Smrj }
39273446Smrj 
39283446Smrj /*
39293446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
39303446Smrj  */
39313446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
39323446Smrj 
39333446Smrj /*
39343446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
39353446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
39363446Smrj  * string, reset the value to the default.  If path is a non-zero-length
39373446Smrj  * string, set the kernel or arguments.
39383446Smrj  */
39393446Smrj static error_t
39403446Smrj set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
39413446Smrj {
39423446Smrj 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
39433446Smrj 	entry_t *entryp;
39443446Smrj 	line_t *ptr, *kernelp;
39453446Smrj 	char *new_arg, *old_args, *space;
39463446Smrj 	char *grubdisk, *rootdev, *new_path;
39473446Smrj 	char old_space;
39483446Smrj 	size_t old_kernel_len, new_str_len;
39493446Smrj 	struct stat sb;
39503446Smrj 
39513446Smrj 	assert(bufsize > 0);
39523446Smrj 
39533446Smrj 	ptr = kernelp = NULL;
39543446Smrj 	new_arg = old_args = space = NULL;
39553446Smrj 	grubdisk = rootdev = new_path = NULL;
39563446Smrj 	buf[0] = '\0';
39573446Smrj 
39583446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
39593446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
39603446Smrj 		return (BAM_ERROR);
39613446Smrj 	}
39623446Smrj 
39633446Smrj 	/*
39643446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
39653446Smrj 	 * one, we don't want to mess with it.  Just print an error and
39663446Smrj 	 * return.
39673446Smrj 	 */
39683446Smrj 	if (mp->curdefault) {
39693446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
39703446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
39713446Smrj 			if (entryp->entryNum == entryNum)
39723446Smrj 				break;
39733446Smrj 		}
39743446Smrj 		if ((entryp != NULL) &&
39753446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
39763446Smrj 			bam_error(DEFAULT_NOT_BAM);
39773446Smrj 			return (BAM_ERROR);
39783446Smrj 		}
39793446Smrj 	}
39803446Smrj 
39813446Smrj 	entryNum = -1;
39825084Sjohnlev 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, 0,
39833446Smrj 	    &entryNum);
39843446Smrj 
39853446Smrj 	if (entryp != NULL) {
39863446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
39873446Smrj 		    ptr = ptr->next) {
39883446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
39893446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
39903446Smrj 				kernelp = ptr;
39913446Smrj 				break;
39923446Smrj 			}
39933446Smrj 		}
39943446Smrj 		if (kernelp == NULL) {
39953446Smrj 			bam_error(NO_KERNEL, entryNum);
39963446Smrj 			return (BAM_ERROR);
39973446Smrj 		}
39983446Smrj 
39993446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
40003446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
40013446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
40023446Smrj 			old_args++;
40033446Smrj 	}
40043446Smrj 
40053446Smrj 	if (path == NULL) {
40063446Smrj 		/* Simply report what was found */
40073446Smrj 		if (kernelp == NULL)
40083446Smrj 			return (BAM_SUCCESS);
40093446Smrj 
40103446Smrj 		if (optnum == ARGS_CMD) {
40113446Smrj 			if (old_args[0] != '\0')
40123446Smrj 				(void) strlcpy(buf, old_args, bufsize);
40133446Smrj 		} else {
40143446Smrj 			/*
40153446Smrj 			 * We need to print the kernel, so we just turn the
40163446Smrj 			 * first space into a '\0' and print the beginning.
40173446Smrj 			 * We don't print anything if it's the default kernel.
40183446Smrj 			 */
40193446Smrj 			old_space = *space;
40203446Smrj 			*space = '\0';
40213446Smrj 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
40223446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
40233446Smrj 			*space = old_space;
40243446Smrj 		}
40253446Smrj 		return (BAM_SUCCESS);
40263446Smrj 	}
40273446Smrj 
40283446Smrj 	/*
40293446Smrj 	 * First, check if we're resetting an entry to the default.
40303446Smrj 	 */
40313446Smrj 	if ((path[0] == '\0') ||
40323446Smrj 	    ((optnum == KERNEL_CMD) &&
40333446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
40343446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
40353446Smrj 			/* No previous entry, it's already the default */
40363446Smrj 			return (BAM_SUCCESS);
40373446Smrj 		}
40383446Smrj 
40393446Smrj 		/*
40403446Smrj 		 * Check if we can delete the entry.  If we're resetting the
40413446Smrj 		 * kernel command, and the args is already empty, or if we're
40423446Smrj 		 * resetting the args command, and the kernel is already the
40433446Smrj 		 * default, we can restore the old default and delete the entry.
40443446Smrj 		 */
40453446Smrj 		if (((optnum == KERNEL_CMD) &&
40463446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
40473446Smrj 		    ((optnum == ARGS_CMD) &&
40483446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
40493446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
40503446Smrj 			kernelp = NULL;
40513446Smrj 			(void) do_delete(mp, entryNum);
40523446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
40533446Smrj 			    mp->old_rc_default);
40543446Smrj 			mp->old_rc_default = NULL;
40553446Smrj 			rv = BAM_WRITE;
40563446Smrj 			goto done;
40573446Smrj 		}
40583446Smrj 
40593446Smrj 		if (optnum == KERNEL_CMD) {
40603446Smrj 			/*
40613446Smrj 			 * At this point, we've already checked that old_args
40623446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
40633446Smrj 			 * a space a the string termination character.
40643446Smrj 			 */
40653446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
40663446Smrj 			    strlen(old_args) + 2;
40673446Smrj 			new_arg = s_calloc(1, new_str_len);
40683446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
40693446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
40703446Smrj 			free(kernelp->arg);
40713446Smrj 			kernelp->arg = new_arg;
40723446Smrj 
40733446Smrj 			/*
40743446Smrj 			 * We have changed the kernel line, so we may need
40753446Smrj 			 * to update the archive line as well.
40763446Smrj 			 */
40773446Smrj 			set_archive_line(entryp, kernelp);
40783446Smrj 		} else {
40793446Smrj 			/*
40803446Smrj 			 * We're resetting the boot args to nothing, so
40813446Smrj 			 * we only need to copy the kernel.  We've already
40823446Smrj 			 * checked that the kernel is not the default.
40833446Smrj 			 */
40843446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
40853446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
40863446Smrj 			    kernelp->arg);
40873446Smrj 			free(kernelp->arg);
40883446Smrj 			kernelp->arg = new_arg;
40893446Smrj 		}
40903446Smrj 		rv = BAM_WRITE;
40913446Smrj 		goto done;
40923446Smrj 	}
40933446Smrj 
40943446Smrj 	/*
40953446Smrj 	 * Expand the kernel file to a full path, if necessary
40963446Smrj 	 */
40973446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
40983446Smrj 		new_path = expand_path(path);
40993446Smrj 		if (new_path == NULL) {
41004346Srscott 			bam_error(UNKNOWN_KERNEL, path);
41013446Smrj 			return (BAM_ERROR);
41023446Smrj 		}
41033446Smrj 		free_new_path = 1;
41043446Smrj 	} else {
41053446Smrj 		new_path = path;
41063446Smrj 		free_new_path = 0;
41073446Smrj 	}
41083446Smrj 
41093446Smrj 	/*
41103446Smrj 	 * At this point, we know we're setting a new value.  First, take care
41113446Smrj 	 * of the case where there was no previous entry.
41123446Smrj 	 */
41133446Smrj 	if (entryp == NULL) {
41143446Smrj 		/* Similar to code in update_temp */
41153446Smrj 		if (stat(GRUB_slice, &sb) != 0) {
41163446Smrj 			/*
41173446Smrj 			 * 1. First get root disk name from mnttab
41183446Smrj 			 * 2. Translate disk name to grub name
41193446Smrj 			 * 3. Add the new menu entry
41203446Smrj 			 */
41213446Smrj 			rootdev = get_special("/");
41223446Smrj 			if (rootdev) {
41233446Smrj 				grubdisk = os_to_grubdisk(rootdev, 1);
41243446Smrj 				free(rootdev);
41253446Smrj 			}
41263446Smrj 		} else {
41273446Smrj 			/*
41283446Smrj 			 * This is an LU BE. The GRUB_root file
41293446Smrj 			 * contains entry for GRUB's "root" cmd.
41303446Smrj 			 */
41313446Smrj 			grubdisk = read_grub_root();
41323446Smrj 		}
41333446Smrj 		if (grubdisk == NULL) {
41343446Smrj 			bam_error(REBOOT_WITH_ARGS_FAILED);
41353446Smrj 			rv = BAM_ERROR;
41363446Smrj 			goto done;
41373446Smrj 		}
41383446Smrj 		if (optnum == KERNEL_CMD) {
41393446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
41405084Sjohnlev 			    grubdisk, new_path, NULL, NULL);
41413446Smrj 		} else {
41423446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
41433446Smrj 			    strlen(path) + 8;
41443446Smrj 			new_arg = s_calloc(1, new_str_len);
41453446Smrj 
41463446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
41473446Smrj 			    DIRECT_BOOT_KERNEL, path);
41483446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
41495084Sjohnlev 			    grubdisk, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
41503446Smrj 		}
41513446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
41523446Smrj 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
41533446Smrj 		rv = BAM_WRITE;
41543446Smrj 		goto done;
41553446Smrj 	}
41563446Smrj 
41573446Smrj 	/*
41583446Smrj 	 * There was already an bootenv entry which we need to edit.
41593446Smrj 	 */
41603446Smrj 	if (optnum == KERNEL_CMD) {
41613446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
41623446Smrj 		new_arg = s_calloc(1, new_str_len);
41633446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
41643446Smrj 		    old_args);
41653446Smrj 		free(kernelp->arg);
41663446Smrj 		kernelp->arg = new_arg;
41673446Smrj 
41683446Smrj 		/*
41693446Smrj 		 * If we have changed the kernel line, we may need to update
41703446Smrj 		 * the archive line as well.
41713446Smrj 		 */
41723446Smrj 		set_archive_line(entryp, kernelp);
41733446Smrj 	} else {
41743446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
41753446Smrj 		new_arg = s_calloc(1, new_str_len);
41763446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
41773446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
41783446Smrj 		(void) strlcat(new_arg, path, new_str_len);
41793446Smrj 		free(kernelp->arg);
41803446Smrj 		kernelp->arg = new_arg;
41813446Smrj 	}
41823446Smrj 	rv = BAM_WRITE;
41833446Smrj 
41843446Smrj done:
41853446Smrj 	if ((rv == BAM_WRITE) && kernelp)
41863446Smrj 		update_line(kernelp);
41873446Smrj 	if (free_new_path)
41883446Smrj 		free(new_path);
41893446Smrj 	return (rv);
41903446Smrj }
41913446Smrj 
41920Sstevel@tonic-gate /*ARGSUSED*/
41930Sstevel@tonic-gate static error_t
41940Sstevel@tonic-gate set_option(menu_t *mp, char *menu_path, char *opt)
41950Sstevel@tonic-gate {
41960Sstevel@tonic-gate 	int optnum, optval;
41970Sstevel@tonic-gate 	char *val;
41983446Smrj 	char buf[BUFSIZ] = "";
41993446Smrj 	error_t rv;
42000Sstevel@tonic-gate 
42010Sstevel@tonic-gate 	assert(mp);
42020Sstevel@tonic-gate 	assert(opt);
42030Sstevel@tonic-gate 
42040Sstevel@tonic-gate 	val = strchr(opt, '=');
42053446Smrj 	if (val != NULL) {
42063446Smrj 		*val = '\0';
42070Sstevel@tonic-gate 	}
42080Sstevel@tonic-gate 
42090Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
42100Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
42110Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
42120Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
42133446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
42143446Smrj 		optnum = KERNEL_CMD;
42153446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
42163446Smrj 		optnum = ARGS_CMD;
42170Sstevel@tonic-gate 	} else {
42180Sstevel@tonic-gate 		bam_error(INVALID_ENTRY, opt);
42190Sstevel@tonic-gate 		return (BAM_ERROR);
42200Sstevel@tonic-gate 	}
42210Sstevel@tonic-gate 
42223446Smrj 	/*
42233446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
42243446Smrj 	 * others cause errors
42253446Smrj 	 */
42263446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
42273446Smrj 		bam_error(INVALID_ENTRY, opt);
42283446Smrj 		return (BAM_ERROR);
42293446Smrj 	} else if (val != NULL) {
42303446Smrj 		*val = '=';
42313446Smrj 	}
42323446Smrj 
42333446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
42343446Smrj 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
42353446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
42363446Smrj 			(void) printf("%s\n", buf);
42373446Smrj 		return (rv);
42383446Smrj 	} else {
42393446Smrj 		optval = s_strtol(val + 1);
42403446Smrj 		return (set_global(mp, menu_cmds[optnum], optval));
42413446Smrj 	}
42420Sstevel@tonic-gate }
42430Sstevel@tonic-gate 
42440Sstevel@tonic-gate /*
42450Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
42460Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
42470Sstevel@tonic-gate  */
42480Sstevel@tonic-gate static error_t
42490Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
42500Sstevel@tonic-gate {
42510Sstevel@tonic-gate 	line_t *lp;
42520Sstevel@tonic-gate 	char *arg;
42530Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
42540Sstevel@tonic-gate 
42550Sstevel@tonic-gate 	assert(mp);
42560Sstevel@tonic-gate 	assert(menu_path);
42570Sstevel@tonic-gate 	assert(globalcmd);
42580Sstevel@tonic-gate 
42590Sstevel@tonic-gate 	if (mp->start == NULL) {
42600Sstevel@tonic-gate 		if (!quiet)
42610Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
42620Sstevel@tonic-gate 		return (BAM_ERROR);
42630Sstevel@tonic-gate 	}
42640Sstevel@tonic-gate 
42650Sstevel@tonic-gate 	done = 0;
42660Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
42670Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
42680Sstevel@tonic-gate 			continue;
42690Sstevel@tonic-gate 
42700Sstevel@tonic-gate 		if (lp->cmd == NULL) {
42710Sstevel@tonic-gate 			if (!quiet)
42720Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
42730Sstevel@tonic-gate 			continue;
42740Sstevel@tonic-gate 		}
42750Sstevel@tonic-gate 
42760Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
42770Sstevel@tonic-gate 			continue;
42780Sstevel@tonic-gate 
42790Sstevel@tonic-gate 		/* Found global. Check for duplicates */
42800Sstevel@tonic-gate 		if (done && !quiet) {
42810Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
42820Sstevel@tonic-gate 			ret = BAM_ERROR;
42830Sstevel@tonic-gate 		}
42840Sstevel@tonic-gate 
42850Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
42860Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
42870Sstevel@tonic-gate 		done = 1;
42880Sstevel@tonic-gate 	}
42890Sstevel@tonic-gate 
42900Sstevel@tonic-gate 	if (!done && bam_verbose)
42910Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
42920Sstevel@tonic-gate 
42930Sstevel@tonic-gate 	return (ret);
42940Sstevel@tonic-gate }
42950Sstevel@tonic-gate 
42960Sstevel@tonic-gate static error_t
42970Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
42980Sstevel@tonic-gate {
42990Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
43000Sstevel@tonic-gate }
43010Sstevel@tonic-gate 
43020Sstevel@tonic-gate static void
43030Sstevel@tonic-gate line_free(line_t *lp)
43040Sstevel@tonic-gate {
43050Sstevel@tonic-gate 	if (lp == NULL)
43060Sstevel@tonic-gate 		return;
43070Sstevel@tonic-gate 
43080Sstevel@tonic-gate 	if (lp->cmd)
43090Sstevel@tonic-gate 		free(lp->cmd);
43100Sstevel@tonic-gate 	if (lp->sep)
43110Sstevel@tonic-gate 		free(lp->sep);
43120Sstevel@tonic-gate 	if (lp->arg)
43130Sstevel@tonic-gate 		free(lp->arg);
43140Sstevel@tonic-gate 	if (lp->line)
43150Sstevel@tonic-gate 		free(lp->line);
43160Sstevel@tonic-gate 	free(lp);
43170Sstevel@tonic-gate }
43180Sstevel@tonic-gate 
43190Sstevel@tonic-gate static void
43200Sstevel@tonic-gate linelist_free(line_t *start)
43210Sstevel@tonic-gate {
43220Sstevel@tonic-gate 	line_t *lp;
43230Sstevel@tonic-gate 
43240Sstevel@tonic-gate 	while (start) {
43250Sstevel@tonic-gate 		lp = start;
43260Sstevel@tonic-gate 		start = start->next;
43270Sstevel@tonic-gate 		line_free(lp);
43280Sstevel@tonic-gate 	}
43290Sstevel@tonic-gate }
43300Sstevel@tonic-gate 
43310Sstevel@tonic-gate static void
43320Sstevel@tonic-gate filelist_free(filelist_t *flistp)
43330Sstevel@tonic-gate {
43340Sstevel@tonic-gate 	linelist_free(flistp->head);
43350Sstevel@tonic-gate 	flistp->head = NULL;
43360Sstevel@tonic-gate 	flistp->tail = NULL;
43370Sstevel@tonic-gate }
43380Sstevel@tonic-gate 
43390Sstevel@tonic-gate static void
43400Sstevel@tonic-gate menu_free(menu_t *mp)
43410Sstevel@tonic-gate {
4342662Sszhou 	entry_t *ent, *tmp;
43430Sstevel@tonic-gate 	assert(mp);
43440Sstevel@tonic-gate 
43450Sstevel@tonic-gate 	if (mp->start)
43460Sstevel@tonic-gate 		linelist_free(mp->start);
4347662Sszhou 	ent = mp->entries;
4348662Sszhou 	while (ent) {
4349662Sszhou 		tmp = ent;
4350662Sszhou 		ent = tmp->next;
4351662Sszhou 		free(tmp);
4352662Sszhou 	}
4353662Sszhou 
43540Sstevel@tonic-gate 	free(mp);
43550Sstevel@tonic-gate }
43560Sstevel@tonic-gate 
43570Sstevel@tonic-gate /*
43580Sstevel@tonic-gate  * Utility routines
43590Sstevel@tonic-gate  */
43600Sstevel@tonic-gate 
43610Sstevel@tonic-gate 
43620Sstevel@tonic-gate /*
43630Sstevel@tonic-gate  * Returns 0 on success
43640Sstevel@tonic-gate  * Any other value indicates an error
43650Sstevel@tonic-gate  */
43660Sstevel@tonic-gate static int
4367*5648Ssetje exec_cmd(char *cmdline, filelist_t *flistp)
43680Sstevel@tonic-gate {
43690Sstevel@tonic-gate 	char buf[BUFSIZ];
43700Sstevel@tonic-gate 	int ret;
43710Sstevel@tonic-gate 	FILE *ptr;
43720Sstevel@tonic-gate 	sigset_t set;
43730Sstevel@tonic-gate 	void (*disp)(int);
43740Sstevel@tonic-gate 
43750Sstevel@tonic-gate 	/*
43760Sstevel@tonic-gate 	 * For security
43770Sstevel@tonic-gate 	 * - only absolute paths are allowed
43780Sstevel@tonic-gate 	 * - set IFS to space and tab
43790Sstevel@tonic-gate 	 */
43800Sstevel@tonic-gate 	if (*cmdline != '/') {
43810Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
43820Sstevel@tonic-gate 		return (-1);
43830Sstevel@tonic-gate 	}
43840Sstevel@tonic-gate 	(void) putenv("IFS= \t");
43850Sstevel@tonic-gate 
43860Sstevel@tonic-gate 	/*
43870Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
43880Sstevel@tonic-gate 	 * unblock it here
43890Sstevel@tonic-gate 	 */
43900Sstevel@tonic-gate 	(void) sigemptyset(&set);
43910Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
43920Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
43930Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
43940Sstevel@tonic-gate 		return (-1);
43950Sstevel@tonic-gate 	}
43960Sstevel@tonic-gate 
43970Sstevel@tonic-gate 	/*
43980Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
43990Sstevel@tonic-gate 	 */
44000Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
44010Sstevel@tonic-gate 	if (disp == SIG_ERR) {
44020Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
44030Sstevel@tonic-gate 		return (-1);
44040Sstevel@tonic-gate 	}
44050Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
44060Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
44070Sstevel@tonic-gate 		return (-1);
44080Sstevel@tonic-gate 	}
44090Sstevel@tonic-gate 
44100Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
44110Sstevel@tonic-gate 	if (ptr == NULL) {
44120Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
44130Sstevel@tonic-gate 		return (-1);
44140Sstevel@tonic-gate 	}
44150Sstevel@tonic-gate 
44160Sstevel@tonic-gate 	/*
44170Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
44180Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
44190Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
44200Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
44210Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
44220Sstevel@tonic-gate 	 * there is no reader process and the command dies with
44230Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
44240Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
44250Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
44260Sstevel@tonic-gate 	 * pclose().
44270Sstevel@tonic-gate 	 *
44280Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
44290Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
44300Sstevel@tonic-gate 	 * from the value returned by pclose()
44310Sstevel@tonic-gate 	 */
4432*5648Ssetje 	while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
4433*5648Ssetje 		if (flistp == NULL) {
4434*5648Ssetje 			/* s_fgets strips newlines, so insert them at the end */
4435*5648Ssetje 			bam_print(PRINT, buf);
4436*5648Ssetje 		} else {
4437*5648Ssetje 			append_to_flist(flistp, buf);
44380Sstevel@tonic-gate 		}
44390Sstevel@tonic-gate 	}
44400Sstevel@tonic-gate 
44410Sstevel@tonic-gate 	ret = pclose(ptr);
44420Sstevel@tonic-gate 	if (ret == -1) {
44430Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
44440Sstevel@tonic-gate 		return (-1);
44450Sstevel@tonic-gate 	}
44460Sstevel@tonic-gate 
44470Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
44480Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
44490Sstevel@tonic-gate 	} else {
44500Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
44510Sstevel@tonic-gate 		return (-1);
44520Sstevel@tonic-gate 	}
44530Sstevel@tonic-gate }
44540Sstevel@tonic-gate 
44550Sstevel@tonic-gate /*
44560Sstevel@tonic-gate  * Since this function returns -1 on error
44570Sstevel@tonic-gate  * it cannot be used to convert -1. However,
44580Sstevel@tonic-gate  * that is sufficient for what we need.
44590Sstevel@tonic-gate  */
44600Sstevel@tonic-gate static long
44610Sstevel@tonic-gate s_strtol(char *str)
44620Sstevel@tonic-gate {
44630Sstevel@tonic-gate 	long l;
44640Sstevel@tonic-gate 	char *res = NULL;
44650Sstevel@tonic-gate 
44660Sstevel@tonic-gate 	if (str == NULL) {
44670Sstevel@tonic-gate 		return (-1);
44680Sstevel@tonic-gate 	}
44690Sstevel@tonic-gate 
44700Sstevel@tonic-gate 	errno = 0;
44710Sstevel@tonic-gate 	l = strtol(str, &res, 10);
44720Sstevel@tonic-gate 	if (errno || *res != '\0') {
44730Sstevel@tonic-gate 		return (-1);
44740Sstevel@tonic-gate 	}
44750Sstevel@tonic-gate 
44760Sstevel@tonic-gate 	return (l);
44770Sstevel@tonic-gate }
44780Sstevel@tonic-gate 
44790Sstevel@tonic-gate /*
44800Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
44810Sstevel@tonic-gate  */
44820Sstevel@tonic-gate static int
44830Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
44840Sstevel@tonic-gate {
44850Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
44860Sstevel@tonic-gate 
44870Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
44880Sstevel@tonic-gate 	return (fputs(linebuf, fp));
44890Sstevel@tonic-gate }
44900Sstevel@tonic-gate 
44910Sstevel@tonic-gate /*
44920Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
44930Sstevel@tonic-gate  */
44943446Smrj char *
44950Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
44960Sstevel@tonic-gate {
44970Sstevel@tonic-gate 	int n;
44980Sstevel@tonic-gate 
44990Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
45000Sstevel@tonic-gate 	if (buf) {
45010Sstevel@tonic-gate 		n = strlen(buf);
45020Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
45030Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
45040Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
45050Sstevel@tonic-gate 	}
45060Sstevel@tonic-gate 
45070Sstevel@tonic-gate 	return (buf);
45080Sstevel@tonic-gate }
45090Sstevel@tonic-gate 
45103446Smrj void *
45110Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
45120Sstevel@tonic-gate {
45130Sstevel@tonic-gate 	void *ptr;
45140Sstevel@tonic-gate 
45150Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
45160Sstevel@tonic-gate 	if (ptr == NULL) {
45170Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
45180Sstevel@tonic-gate 		bam_exit(1);
45190Sstevel@tonic-gate 	}
45200Sstevel@tonic-gate 	return (ptr);
45210Sstevel@tonic-gate }
45220Sstevel@tonic-gate 
45233446Smrj void *
45243446Smrj s_realloc(void *ptr, size_t sz)
45253446Smrj {
45263446Smrj 	ptr = realloc(ptr, sz);
45273446Smrj 	if (ptr == NULL) {
45283446Smrj 		bam_error(NO_MEM, sz);
45293446Smrj 		bam_exit(1);
45303446Smrj 	}
45313446Smrj 	return (ptr);
45323446Smrj }
45333446Smrj 
45340Sstevel@tonic-gate static char *
45350Sstevel@tonic-gate s_strdup(char *str)
45360Sstevel@tonic-gate {
45370Sstevel@tonic-gate 	char *ptr;
45380Sstevel@tonic-gate 
45390Sstevel@tonic-gate 	if (str == NULL)
45400Sstevel@tonic-gate 		return (NULL);
45410Sstevel@tonic-gate 
45420Sstevel@tonic-gate 	ptr = strdup(str);
45430Sstevel@tonic-gate 	if (ptr == NULL) {
45440Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
45450Sstevel@tonic-gate 		bam_exit(1);
45460Sstevel@tonic-gate 	}
45470Sstevel@tonic-gate 	return (ptr);
45480Sstevel@tonic-gate }
45490Sstevel@tonic-gate 
45500Sstevel@tonic-gate /*
45510Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
45520Sstevel@tonic-gate  * Returns 0 otherwise
45530Sstevel@tonic-gate  */
45540Sstevel@tonic-gate static int
45550Sstevel@tonic-gate is_amd64(void)
45560Sstevel@tonic-gate {
45570Sstevel@tonic-gate 	static int amd64 = -1;
45580Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
45590Sstevel@tonic-gate 
45600Sstevel@tonic-gate 	if (amd64 != -1)
45610Sstevel@tonic-gate 		return (amd64);
45620Sstevel@tonic-gate 
45630Sstevel@tonic-gate 	if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
45640Sstevel@tonic-gate 	    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0)
45650Sstevel@tonic-gate 		amd64 = 1;
45660Sstevel@tonic-gate 	else if (strstr(isabuf, "i386") == NULL)
45670Sstevel@tonic-gate 		amd64 = 1;		/* diskless server */
45680Sstevel@tonic-gate 	else
45690Sstevel@tonic-gate 		amd64 = 0;
45700Sstevel@tonic-gate 
45710Sstevel@tonic-gate 	return (amd64);
45720Sstevel@tonic-gate }
45730Sstevel@tonic-gate 
4574*5648Ssetje static int
4575*5648Ssetje is_sun4u(void)
4576*5648Ssetje {
4577*5648Ssetje 	static int sun4u = 0;
4578*5648Ssetje 	char mbuf[257];	/* from sysinfo(2) manpage */
4579*5648Ssetje 
4580*5648Ssetje 	if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 &&
4581*5648Ssetje 	    strncmp(mbuf, "sun4u", strlen("sun4u")) == 0)
4582*5648Ssetje 		sun4u = 1;
4583*5648Ssetje 
4584*5648Ssetje 	return (sun4u);
4585*5648Ssetje }
4586*5648Ssetje 
4587*5648Ssetje static int
4588*5648Ssetje is_sun4v(void)
4589*5648Ssetje {
4590*5648Ssetje 	static int sun4v = 0;
4591*5648Ssetje 	char mbuf[257];	/* from sysinfo(2) manpage */
4592*5648Ssetje 
4593*5648Ssetje 	if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 &&
4594*5648Ssetje 	    strncmp(mbuf, "sun4v", strlen("sun4v")) == 0)
4595*5648Ssetje 		sun4v = 1;
4596*5648Ssetje 
4597*5648Ssetje 	return (sun4v);
4598*5648Ssetje }
4599*5648Ssetje 
4600*5648Ssetje 
46010Sstevel@tonic-gate static void
46020Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
46030Sstevel@tonic-gate {
46040Sstevel@tonic-gate 	line_t *lp;
46050Sstevel@tonic-gate 
46060Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
46070Sstevel@tonic-gate 	lp->line = s_strdup(s);
46080Sstevel@tonic-gate 	if (flistp->head == NULL)
46090Sstevel@tonic-gate 		flistp->head = lp;
46100Sstevel@tonic-gate 	else
46110Sstevel@tonic-gate 		flistp->tail->next = lp;
46120Sstevel@tonic-gate 	flistp->tail = lp;
46130Sstevel@tonic-gate }
46144581Ssherrym 
4615*5648Ssetje #if !defined(_OPB)
46164581Ssherrym 
46174581Ssherrym UCODE_VENDORS;
46184581Ssherrym 
46194581Ssherrym /*ARGSUSED*/
46204581Ssherrym static void
46214581Ssherrym ucode_install(char *root)
46224581Ssherrym {
46234581Ssherrym 	int i;
46244581Ssherrym 
46254581Ssherrym 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
46264581Ssherrym 		int cmd_len = PATH_MAX + 256;
46274581Ssherrym 		char cmd[PATH_MAX + 256];
46284581Ssherrym 		char file[PATH_MAX];
46294581Ssherrym 		char timestamp[PATH_MAX];
46304581Ssherrym 		struct stat fstatus, tstatus;
46314581Ssherrym 		struct utimbuf u_times;
46324581Ssherrym 
46334581Ssherrym 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt",
46344581Ssherrym 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr);
46354581Ssherrym 
46364581Ssherrym 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
46374581Ssherrym 			continue;
46384581Ssherrym 
46394581Ssherrym 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
46404581Ssherrym 
46414581Ssherrym 		if (stat(timestamp, &tstatus) == 0 &&
46424581Ssherrym 		    fstatus.st_mtime <= tstatus.st_mtime)
46434581Ssherrym 			continue;
46444581Ssherrym 
46454581Ssherrym 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
46464581Ssherrym 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
46474581Ssherrym 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
46484581Ssherrym 		if (system(cmd) != 0)
46494581Ssherrym 			return;
46504581Ssherrym 
46514581Ssherrym 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
46524581Ssherrym 			return;
46534581Ssherrym 
46544581Ssherrym 		u_times.actime = fstatus.st_atime;
46554581Ssherrym 		u_times.modtime = fstatus.st_mtime;
46564581Ssherrym 		(void) utime(timestamp, &u_times);
46574581Ssherrym 	}
46584581Ssherrym }
46594581Ssherrym #endif
4660