xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 6319:03c1299ed2bb)
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 /*
22*6319Sjg  * Copyright 2008 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>
575648Ssetje 
585648Ssetje #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 #include <locale.h>
660Sstevel@tonic-gate #include <assert.h>
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #include "message.h"
693446Smrj #include "bootadm.h"
700Sstevel@tonic-gate 
710Sstevel@tonic-gate #ifndef TEXT_DOMAIN
720Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
730Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /* Type definitions */
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /* Primary subcmds */
780Sstevel@tonic-gate typedef enum {
790Sstevel@tonic-gate 	BAM_MENU = 3,
800Sstevel@tonic-gate 	BAM_ARCHIVE
810Sstevel@tonic-gate } subcmd_t;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate typedef enum {
840Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
850Sstevel@tonic-gate     OPT_REQ,		/* option required */
860Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
870Sstevel@tonic-gate } option_t;
880Sstevel@tonic-gate 
890Sstevel@tonic-gate typedef struct {
900Sstevel@tonic-gate 	char	*subcmd;
910Sstevel@tonic-gate 	option_t option;
920Sstevel@tonic-gate 	error_t (*handler)();
932115Svikram 	int	unpriv;			/* is this an unprivileged command */
940Sstevel@tonic-gate } subcmd_defn_t;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
970Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
980Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
1010Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
1020Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1030Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
104621Svikram #define	STUBBOOT		"/stubboot"
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate /* lock related */
1070Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1080Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1090Sstevel@tonic-gate 
1105648Ssetje #define	CREATE_RAMDISK		"boot/solaris/bin/create_ramdisk"
1115648Ssetje #define	CREATE_DISKMAP		"boot/solaris/bin/create_diskmap"
1125648Ssetje #define	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
1130Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
1140Sstevel@tonic-gate 
115316Svikram #define	GRUB_slice		"/etc/lu/GRUB_slice"
116316Svikram #define	GRUB_root		"/etc/lu/GRUB_root"
117316Svikram #define	GRUB_backup_menu	"/etc/lu/GRUB_backup_menu"
118316Svikram #define	GRUB_slice_mntpt	"/tmp/GRUB_slice_mntpt"
119316Svikram #define	LU_ACTIVATE_FILE	"/etc/lu/DelayUpdate/activate.sh"
1201746Svikram #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
1211746Svikram #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
122316Svikram 
123316Svikram #define	INSTALLGRUB		"/sbin/installgrub"
124316Svikram #define	STAGE1			"/boot/grub/stage1"
125316Svikram #define	STAGE2			"/boot/grub/stage2"
126316Svikram 
1270Sstevel@tonic-gate /*
1284493Snadkarni  * The following two defines are used to detect and create the correct
1294493Snadkarni  * boot archive  when safemode patching is underway.  LOFS_PATCH_FILE is a
1304493Snadkarni  * contracted private interface between bootadm and the install
1314493Snadkarni  * consolidation.  It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE
1324493Snadkarni  * is applied.
1334493Snadkarni  */
1344493Snadkarni 
1354493Snadkarni #define	LOFS_PATCH_FILE		"/var/run/.patch_loopback_mode"
1364493Snadkarni #define	LOFS_PATCH_MNT		"/var/run/.patch_root_loopbackmnt"
1374493Snadkarni 
1384493Snadkarni /*
1390Sstevel@tonic-gate  * Default file attributes
1400Sstevel@tonic-gate  */
1410Sstevel@tonic-gate #define	DEFAULT_DEV_MODE	0644	/* default permissions */
1420Sstevel@tonic-gate #define	DEFAULT_DEV_UID		0	/* user root */
1430Sstevel@tonic-gate #define	DEFAULT_DEV_GID		3	/* group sys */
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate /*
1460Sstevel@tonic-gate  * Menu related
1470Sstevel@tonic-gate  * menu_cmd_t and menu_cmds must be kept in sync
1480Sstevel@tonic-gate  */
1493446Smrj char *menu_cmds[] = {
1500Sstevel@tonic-gate 	"default",	/* DEFAULT_CMD */
1510Sstevel@tonic-gate 	"timeout",	/* TIMEOUT_CMD */
1520Sstevel@tonic-gate 	"title",	/* TITLE_CMD */
1530Sstevel@tonic-gate 	"root",		/* ROOT_CMD */
1540Sstevel@tonic-gate 	"kernel",	/* KERNEL_CMD */
1553446Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1560Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
1573446Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1580Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1590Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
1603446Smrj 	"chainloader",	/* CHAINLOADER_CMD */
1613446Smrj 	"args",		/* ARGS_CMD */
1620Sstevel@tonic-gate 	NULL
1630Sstevel@tonic-gate };
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate #define	OPT_ENTRY_NUM	"entry"
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate /*
1680Sstevel@tonic-gate  * archive related
1690Sstevel@tonic-gate  */
1700Sstevel@tonic-gate typedef struct {
1710Sstevel@tonic-gate 	line_t *head;
1720Sstevel@tonic-gate 	line_t *tail;
1730Sstevel@tonic-gate } filelist_t;
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
1760Sstevel@tonic-gate #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
1790Sstevel@tonic-gate #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
1800Sstevel@tonic-gate #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
1810Sstevel@tonic-gate #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /* Globals */
1843446Smrj int bam_verbose;
1853446Smrj int bam_force;
1860Sstevel@tonic-gate static char *prog;
1870Sstevel@tonic-gate static subcmd_t bam_cmd;
1880Sstevel@tonic-gate static char *bam_root;
1890Sstevel@tonic-gate static int bam_rootlen;
1900Sstevel@tonic-gate static int bam_root_readonly;
191621Svikram static int bam_alt_root;
1920Sstevel@tonic-gate static char *bam_subcmd;
1930Sstevel@tonic-gate static char *bam_opt;
1940Sstevel@tonic-gate static int bam_debug;
1950Sstevel@tonic-gate static char **bam_argv;
1960Sstevel@tonic-gate static int bam_argc;
1970Sstevel@tonic-gate static int bam_check;
1980Sstevel@tonic-gate static int bam_smf_check;
1990Sstevel@tonic-gate static int bam_lock_fd = -1;
2000Sstevel@tonic-gate static char rootbuf[PATH_MAX] = "/";
201316Svikram static int bam_update_all;
202*6319Sjg static int bam_alt_platform;
203*6319Sjg static char *bam_platform;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate /* function prototypes */
2065648Ssetje static void parse_args_internal(int, char *[]);
2075648Ssetje static void parse_args(int, char *argv[]);
2085648Ssetje static error_t bam_menu(char *, char *, int, char *[]);
2095648Ssetje static error_t bam_archive(char *, char *);
2105648Ssetje 
2115648Ssetje static void bam_print(char *, ...);
2125648Ssetje static void bam_exit(int);
2130Sstevel@tonic-gate static void bam_lock(void);
2140Sstevel@tonic-gate static void bam_unlock(void);
2150Sstevel@tonic-gate 
2165648Ssetje static int exec_cmd(char *, filelist_t *);
2175648Ssetje static error_t read_globals(menu_t *, char *, char *, int);
2185648Ssetje 
2195648Ssetje static menu_t *menu_read(char *);
2205648Ssetje static error_t menu_write(char *, menu_t *);
2215648Ssetje static void linelist_free(line_t *);
2225648Ssetje static void menu_free(menu_t *);
2235648Ssetje static void line_free(line_t *);
2245648Ssetje static void filelist_free(filelist_t *);
2255648Ssetje static error_t list2file(char *, char *, char *, line_t *);
2265648Ssetje static error_t list_entry(menu_t *, char *, char *);
2275648Ssetje static error_t delete_all_entries(menu_t *, char *, char *);
2285648Ssetje static error_t update_entry(menu_t *, char *, char *);
2295648Ssetje static error_t update_temp(menu_t *, char *, char *);
2305648Ssetje 
2315648Ssetje static error_t update_archive(char *, char *);
2325648Ssetje static error_t list_archive(char *, char *);
2335648Ssetje static error_t update_all(char *, char *);
2345648Ssetje static error_t read_list(char *, filelist_t *);
2355648Ssetje static error_t set_global(menu_t *, char *, int);
2365648Ssetje static error_t set_option(menu_t *, char *, char *);
2375648Ssetje static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
2385648Ssetje static char *expand_path(const char *);
2395648Ssetje 
2405648Ssetje static long s_strtol(char *);
2415648Ssetje static int s_fputs(char *, FILE *);
2425648Ssetje 
2435648Ssetje static char *s_strdup(char *);
2440Sstevel@tonic-gate static int is_readonly(char *);
2450Sstevel@tonic-gate static int is_amd64(void);
2465648Ssetje static int is_sun4u(void);
2475648Ssetje static int is_sun4v(void);
2480Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2490Sstevel@tonic-gate 
2505648Ssetje #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 */
295*6319Sjg 	(void) fprintf(stderr,
296*6319Sjg 	    "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog);
297*6319Sjg 	(void) fprintf(stderr,
298*6319Sjg 	    "\t%s list-archive [-R altroot [-p platform>]]\n", prog);
2995648Ssetje #if !defined(_OPB)
3000Sstevel@tonic-gate 	/* x86 only */
3010Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
3020Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
3030Sstevel@tonic-gate #endif
3040Sstevel@tonic-gate }
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate int
3070Sstevel@tonic-gate main(int argc, char *argv[])
3080Sstevel@tonic-gate {
3090Sstevel@tonic-gate 	error_t ret;
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
3120Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
3150Sstevel@tonic-gate 		prog = argv[0];
3160Sstevel@tonic-gate 	} else {
3170Sstevel@tonic-gate 		prog++;
3180Sstevel@tonic-gate 	}
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	/*
3220Sstevel@tonic-gate 	 * Don't depend on caller's umask
3230Sstevel@tonic-gate 	 */
3240Sstevel@tonic-gate 	(void) umask(0022);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	parse_args(argc, argv);
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	switch (bam_cmd) {
3290Sstevel@tonic-gate 		case BAM_MENU:
3300Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
3310Sstevel@tonic-gate 			break;
3320Sstevel@tonic-gate 		case BAM_ARCHIVE:
3330Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
3340Sstevel@tonic-gate 			break;
3350Sstevel@tonic-gate 		default:
3360Sstevel@tonic-gate 			usage();
3370Sstevel@tonic-gate 			bam_exit(1);
3380Sstevel@tonic-gate 	}
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
3410Sstevel@tonic-gate 		bam_exit(1);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	bam_unlock();
3440Sstevel@tonic-gate 	return (0);
3450Sstevel@tonic-gate }
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate /*
3480Sstevel@tonic-gate  * Equivalence of public and internal commands:
3490Sstevel@tonic-gate  *	update-archive  -- -a update
3500Sstevel@tonic-gate  *	list-archive	-- -a list
3510Sstevel@tonic-gate  *	set-menu	-- -m set_option
3520Sstevel@tonic-gate  *	list-menu	-- -m list_entry
3530Sstevel@tonic-gate  *	update-menu	-- -m update_entry
3540Sstevel@tonic-gate  */
3550Sstevel@tonic-gate static struct cmd_map {
3560Sstevel@tonic-gate 	char *bam_cmdname;
3570Sstevel@tonic-gate 	int bam_cmd;
3580Sstevel@tonic-gate 	char *bam_subcmd;
3590Sstevel@tonic-gate } cmd_map[] = {
3600Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
3610Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
3620Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
3630Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
3640Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
3650Sstevel@tonic-gate 	{ NULL,			0,		NULL}
3660Sstevel@tonic-gate };
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate /*
3690Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
3700Sstevel@tonic-gate  */
3710Sstevel@tonic-gate static void
3720Sstevel@tonic-gate parse_args(int argc, char *argv[])
3730Sstevel@tonic-gate {
3740Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	/* command conforming to the final spec */
3770Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
3780Sstevel@tonic-gate 		/*
3790Sstevel@tonic-gate 		 * Map commands to internal table.
3800Sstevel@tonic-gate 		 */
3810Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
3820Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
3830Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
3840Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
3850Sstevel@tonic-gate 				break;
3860Sstevel@tonic-gate 			}
3870Sstevel@tonic-gate 			cmp++;
3880Sstevel@tonic-gate 		}
3890Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
3900Sstevel@tonic-gate 			usage();
3910Sstevel@tonic-gate 			bam_exit(1);
3920Sstevel@tonic-gate 		}
3930Sstevel@tonic-gate 		argc--;
3940Sstevel@tonic-gate 		argv++;
3950Sstevel@tonic-gate 	}
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	parse_args_internal(argc, argv);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate /*
4010Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
4020Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
4030Sstevel@tonic-gate  *	-a update	-- update-archive
4040Sstevel@tonic-gate  *	-a list		-- list-archive
4050Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
4060Sstevel@tonic-gate  *	-m update_entry	-- update-menu
4070Sstevel@tonic-gate  *	-m list_entry	-- list-menu
4080Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
4090Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
4100Sstevel@tonic-gate  */
4110Sstevel@tonic-gate static void
4120Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
4130Sstevel@tonic-gate {
4140Sstevel@tonic-gate 	int c, error;
4150Sstevel@tonic-gate 	extern char *optarg;
4160Sstevel@tonic-gate 	extern int optind, opterr;
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	/* Suppress error message from getopt */
4190Sstevel@tonic-gate 	opterr = 0;
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	error = 0;
422*6319Sjg 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:p:")) != -1) {
4230Sstevel@tonic-gate 		switch (c) {
4240Sstevel@tonic-gate 		case 'a':
4250Sstevel@tonic-gate 			if (bam_cmd) {
4260Sstevel@tonic-gate 				error = 1;
4270Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4280Sstevel@tonic-gate 			}
4290Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
4300Sstevel@tonic-gate 			bam_subcmd = optarg;
4310Sstevel@tonic-gate 			break;
4320Sstevel@tonic-gate 		case 'd':
4330Sstevel@tonic-gate 			if (bam_debug) {
4340Sstevel@tonic-gate 				error = 1;
4350Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4360Sstevel@tonic-gate 			}
4370Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
4380Sstevel@tonic-gate 			break;
4390Sstevel@tonic-gate 		case 'f':
4400Sstevel@tonic-gate 			if (bam_force) {
4410Sstevel@tonic-gate 				error = 1;
4420Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4430Sstevel@tonic-gate 			}
4440Sstevel@tonic-gate 			bam_force = 1;
4450Sstevel@tonic-gate 			break;
4460Sstevel@tonic-gate 		case 'm':
4470Sstevel@tonic-gate 			if (bam_cmd) {
4480Sstevel@tonic-gate 				error = 1;
4490Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4500Sstevel@tonic-gate 			}
4510Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
4520Sstevel@tonic-gate 			bam_subcmd = optarg;
4530Sstevel@tonic-gate 			break;
4540Sstevel@tonic-gate 		case 'n':
4550Sstevel@tonic-gate 			if (bam_check) {
4560Sstevel@tonic-gate 				error = 1;
4570Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4580Sstevel@tonic-gate 			}
4590Sstevel@tonic-gate 			bam_check = 1;
4600Sstevel@tonic-gate 			break;
4610Sstevel@tonic-gate 		case 'o':
4620Sstevel@tonic-gate 			if (bam_opt) {
4630Sstevel@tonic-gate 				error = 1;
4640Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4650Sstevel@tonic-gate 			}
4660Sstevel@tonic-gate 			bam_opt = optarg;
4670Sstevel@tonic-gate 			break;
4680Sstevel@tonic-gate 		case 'v':
4690Sstevel@tonic-gate 			if (bam_verbose) {
4700Sstevel@tonic-gate 				error = 1;
4710Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4720Sstevel@tonic-gate 			}
4730Sstevel@tonic-gate 			bam_verbose = 1;
4740Sstevel@tonic-gate 			break;
475662Sszhou 		case 'C':
476662Sszhou 			bam_smf_check = 1;
477662Sszhou 			break;
4780Sstevel@tonic-gate 		case 'R':
4790Sstevel@tonic-gate 			if (bam_root) {
4800Sstevel@tonic-gate 				error = 1;
4810Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4820Sstevel@tonic-gate 				break;
4830Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
4840Sstevel@tonic-gate 				error = 1;
4850Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
4860Sstevel@tonic-gate 				    strerror(errno));
4870Sstevel@tonic-gate 				break;
4880Sstevel@tonic-gate 			}
489621Svikram 			bam_alt_root = 1;
4900Sstevel@tonic-gate 			bam_root = rootbuf;
4910Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
4920Sstevel@tonic-gate 			break;
493*6319Sjg 		case 'p':
494*6319Sjg 			bam_alt_platform = 1;
495*6319Sjg 			bam_platform = optarg;
496*6319Sjg 			if ((strcmp(bam_platform, "i86pc") != 0) &&
497*6319Sjg 			    (strcmp(bam_platform, "sun4u") != 0) &&
498*6319Sjg 			    (strcmp(bam_platform, "sun4v") != 0)) {
499*6319Sjg 				error = 1;
500*6319Sjg 				bam_error(INVALID_PLAT, bam_platform);
501*6319Sjg 			}
502*6319Sjg 			break;
5030Sstevel@tonic-gate 		case '?':
5040Sstevel@tonic-gate 			error = 1;
5050Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
5060Sstevel@tonic-gate 			break;
5070Sstevel@tonic-gate 		default :
5080Sstevel@tonic-gate 			error = 1;
5090Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
5100Sstevel@tonic-gate 			break;
5110Sstevel@tonic-gate 		}
5120Sstevel@tonic-gate 	}
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	/*
515*6319Sjg 	 * An alternate platform requires an alternate root
516*6319Sjg 	 */
517*6319Sjg 	if (bam_alt_platform && bam_alt_root == 0) {
518*6319Sjg 		usage();
519*6319Sjg 		bam_exit(0);
520*6319Sjg 	}
521*6319Sjg 
522*6319Sjg 	/*
5230Sstevel@tonic-gate 	 * A command option must be specfied
5240Sstevel@tonic-gate 	 */
5250Sstevel@tonic-gate 	if (!bam_cmd) {
5260Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
5270Sstevel@tonic-gate 			usage();
5280Sstevel@tonic-gate 			bam_exit(0);
5290Sstevel@tonic-gate 		}
5300Sstevel@tonic-gate 		bam_error(NEED_CMD);
5310Sstevel@tonic-gate 		error = 1;
5320Sstevel@tonic-gate 	}
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	if (error) {
5350Sstevel@tonic-gate 		usage();
5360Sstevel@tonic-gate 		bam_exit(1);
5370Sstevel@tonic-gate 	}
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	if (optind > argc) {
5400Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
5410Sstevel@tonic-gate 		bam_exit(1);
5420Sstevel@tonic-gate 	} else if (optind < argc) {
5430Sstevel@tonic-gate 		bam_argv = &argv[optind];
5440Sstevel@tonic-gate 		bam_argc = argc - optind;
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	/*
5480Sstevel@tonic-gate 	 * -n implies verbose mode
5490Sstevel@tonic-gate 	 */
5500Sstevel@tonic-gate 	if (bam_check)
5510Sstevel@tonic-gate 		bam_verbose = 1;
5520Sstevel@tonic-gate }
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate static error_t
5550Sstevel@tonic-gate check_subcmd_and_options(
5560Sstevel@tonic-gate 	char *subcmd,
5570Sstevel@tonic-gate 	char *opt,
5580Sstevel@tonic-gate 	subcmd_defn_t *table,
5590Sstevel@tonic-gate 	error_t (**fp)())
5600Sstevel@tonic-gate {
5610Sstevel@tonic-gate 	int i;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	if (subcmd == NULL) {
5640Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
5650Sstevel@tonic-gate 		return (BAM_ERROR);
5660Sstevel@tonic-gate 	}
5670Sstevel@tonic-gate 
5682115Svikram 	if (bam_argc != 0 || bam_argv) {
5692115Svikram 		if (strcmp(subcmd, "set_option") != 0 || bam_argc != 1) {
5702115Svikram 			bam_error(TRAILING_ARGS);
5712115Svikram 			usage();
5722115Svikram 			return (BAM_ERROR);
5732115Svikram 		}
5742115Svikram 	}
5752115Svikram 
5760Sstevel@tonic-gate 	if (bam_root == NULL) {
5770Sstevel@tonic-gate 		bam_root = rootbuf;
5780Sstevel@tonic-gate 		bam_rootlen = 1;
5790Sstevel@tonic-gate 	}
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	/* verify that subcmd is valid */
5820Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
5830Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
5840Sstevel@tonic-gate 			break;
5850Sstevel@tonic-gate 	}
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
5880Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
5890Sstevel@tonic-gate 		return (BAM_ERROR);
5900Sstevel@tonic-gate 	}
5910Sstevel@tonic-gate 
5922115Svikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
5932115Svikram 		bam_error(MUST_BE_ROOT);
5942115Svikram 		return (BAM_ERROR);
5952115Svikram 	}
5962115Svikram 
5972115Svikram 	/*
5982115Svikram 	 * Currently only privileged commands need a lock
5992115Svikram 	 */
6002115Svikram 	if (table[i].unpriv == 0)
6012115Svikram 		bam_lock();
6022115Svikram 
6030Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
6040Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
6050Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
6060Sstevel@tonic-gate 			if (opt)
6070Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
6080Sstevel@tonic-gate 			else
6090Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
6100Sstevel@tonic-gate 			return (BAM_ERROR);
6110Sstevel@tonic-gate 		}
6120Sstevel@tonic-gate 	}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	*fp = table[i].handler;
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	return (BAM_SUCCESS);
6170Sstevel@tonic-gate }
6180Sstevel@tonic-gate 
619316Svikram 
620316Svikram static char *
621621Svikram mount_grub_slice(int *mnted, char **physlice, char **logslice, char **fs_type)
622316Svikram {
623316Svikram 	struct extmnttab mnt;
624316Svikram 	struct stat sb;
625316Svikram 	char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32];
626348Svikram 	char cmd[PATH_MAX];
627316Svikram 	char *mntpt;
628316Svikram 	int p, l, f;
629316Svikram 	FILE *fp;
630316Svikram 
631316Svikram 	assert(mnted);
632316Svikram 	*mnted = 0;
633316Svikram 
634316Svikram 	/*
635621Svikram 	 * physlice, logslice, fs_type  args may be NULL
636316Svikram 	 */
637316Svikram 	if (physlice)
638316Svikram 		*physlice = NULL;
639621Svikram 	if (logslice)
640621Svikram 		*logslice = NULL;
641621Svikram 	if (fs_type)
642621Svikram 		*fs_type = NULL;
643316Svikram 
644316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
645316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
646316Svikram 		return (NULL);
647316Svikram 	}
648316Svikram 
649316Svikram 	fp = fopen(GRUB_slice, "r");
650316Svikram 	if (fp == NULL) {
651316Svikram 		bam_error(OPEN_FAIL, GRUB_slice, strerror(errno));
652316Svikram 		return (NULL);
653316Svikram 	}
654316Svikram 
655316Svikram 	dev[0] = fstype[0] = phys[0] = '\0';
656316Svikram 	p = sizeof ("PHYS_SLICE=") - 1;
657316Svikram 	l = sizeof ("LOG_SLICE=") - 1;
658316Svikram 	f = sizeof ("LOG_FSTYP=") - 1;
659316Svikram 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
660316Svikram 		if (strncmp(buf, "PHYS_SLICE=", p) == 0) {
661316Svikram 			(void) strlcpy(phys, buf + p, sizeof (phys));
662316Svikram 			continue;
663316Svikram 		}
664316Svikram 		if (strncmp(buf, "LOG_SLICE=", l) == 0) {
665316Svikram 			(void) strlcpy(dev, buf + l, sizeof (dev));
666316Svikram 			continue;
667316Svikram 		}
668316Svikram 		if (strncmp(buf, "LOG_FSTYP=", f) == 0) {
669316Svikram 			(void) strlcpy(fstype, buf + f, sizeof (fstype));
670316Svikram 			continue;
671316Svikram 		}
672316Svikram 	}
673316Svikram 	(void) fclose(fp);
674316Svikram 
675316Svikram 	if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') {
676316Svikram 		bam_error(BAD_SLICE_FILE, GRUB_slice);
677316Svikram 		return (NULL);
678316Svikram 	}
679316Svikram 
680316Svikram 	if (physlice) {
681316Svikram 		*physlice = s_strdup(phys);
682316Svikram 	}
683621Svikram 	if (logslice) {
684621Svikram 		*logslice = s_strdup(dev);
685621Svikram 	}
686621Svikram 	if (fs_type) {
687621Svikram 		*fs_type = s_strdup(fstype);
688621Svikram 	}
689316Svikram 
690316Svikram 	/*
691316Svikram 	 * Check if the slice is already mounted
692316Svikram 	 */
693316Svikram 	fp = fopen(MNTTAB, "r");
694316Svikram 	if (fp == NULL) {
695316Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
696621Svikram 		goto error;
697316Svikram 	}
698316Svikram 
699316Svikram 	resetmnttab(fp);
700316Svikram 
701316Svikram 	mntpt = NULL;
702316Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
703316Svikram 		if (strcmp(mnt.mnt_special, dev) == 0) {
704316Svikram 			mntpt = s_strdup(mnt.mnt_mountp);
705316Svikram 			break;
706316Svikram 		}
707316Svikram 	}
708316Svikram 
709316Svikram 	(void) fclose(fp);
710316Svikram 
711316Svikram 	if (mntpt) {
712316Svikram 		return (mntpt);
713316Svikram 	}
714316Svikram 
715316Svikram 
716316Svikram 	/*
717316Svikram 	 * GRUB slice is not mounted, we need to mount it now.
718316Svikram 	 * First create the mountpoint
719316Svikram 	 */
720316Svikram 	mntpt = s_calloc(1, PATH_MAX);
721316Svikram 	(void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid());
722316Svikram 	if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) {
723316Svikram 		bam_error(MKDIR_FAILED, mntpt, strerror(errno));
724316Svikram 		free(mntpt);
725621Svikram 		goto error;
726316Svikram 	}
727316Svikram 
728348Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount -F %s %s %s",
729348Svikram 	    fstype, dev, mntpt);
730348Svikram 
7315648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
732621Svikram 		bam_error(MOUNT_FAILED, dev, fstype);
733316Svikram 		if (rmdir(mntpt) != 0) {
734316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
735316Svikram 		}
736316Svikram 		free(mntpt);
737621Svikram 		goto error;
738316Svikram 	}
739316Svikram 
740316Svikram 	*mnted = 1;
741316Svikram 	return (mntpt);
742621Svikram 
743621Svikram error:
744621Svikram 	if (physlice) {
745621Svikram 		free(*physlice);
746621Svikram 		*physlice = NULL;
747621Svikram 	}
748621Svikram 	if (logslice) {
749621Svikram 		free(*logslice);
750621Svikram 		*logslice = NULL;
751621Svikram 	}
752621Svikram 	if (fs_type) {
753621Svikram 		free(*fs_type);
754621Svikram 		*fs_type = NULL;
755621Svikram 	}
756621Svikram 	return (NULL);
757316Svikram }
758316Svikram 
759316Svikram static void
760621Svikram umount_grub_slice(
761621Svikram 	int mnted,
762621Svikram 	char *mntpt,
763621Svikram 	char *physlice,
764621Svikram 	char *logslice,
765621Svikram 	char *fs_type)
766316Svikram {
767348Svikram 	char cmd[PATH_MAX];
768348Svikram 
769316Svikram 	/*
770316Svikram 	 * If we have not dealt with GRUB slice
771316Svikram 	 * we have nothing to do - just return.
772316Svikram 	 */
773316Svikram 	if (mntpt == NULL)
774316Svikram 		return;
775316Svikram 
776316Svikram 
777316Svikram 	/*
778316Svikram 	 * If we mounted the filesystem earlier in mount_grub_slice()
779316Svikram 	 * unmount it now.
780316Svikram 	 */
781316Svikram 	if (mnted) {
782348Svikram 		(void) snprintf(cmd, sizeof (cmd), "/sbin/umount %s",
783348Svikram 		    mntpt);
7845648Ssetje 		if (exec_cmd(cmd, NULL) != 0) {
785348Svikram 			bam_error(UMOUNT_FAILED, mntpt);
786316Svikram 		}
787316Svikram 		if (rmdir(mntpt) != 0) {
788316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
789316Svikram 		}
790316Svikram 	}
791621Svikram 
792316Svikram 	if (physlice)
793316Svikram 		free(physlice);
794621Svikram 	if (logslice)
795621Svikram 		free(logslice);
796621Svikram 	if (fs_type)
797621Svikram 		free(fs_type);
798621Svikram 
799316Svikram 	free(mntpt);
800316Svikram }
801316Svikram 
802621Svikram static char *
803621Svikram use_stubboot(void)
804621Svikram {
805621Svikram 	int mnted;
806621Svikram 	struct stat sb;
807621Svikram 	struct extmnttab mnt;
808621Svikram 	FILE *fp;
809621Svikram 	char cmd[PATH_MAX];
810621Svikram 
811621Svikram 	if (stat(STUBBOOT, &sb) != 0) {
812621Svikram 		bam_error(STUBBOOT_DIR_NOT_FOUND);
813621Svikram 		return (NULL);
814621Svikram 	}
815621Svikram 
816621Svikram 	/*
817621Svikram 	 * Check if stubboot is mounted. If not, mount it
818621Svikram 	 */
819621Svikram 	fp = fopen(MNTTAB, "r");
820621Svikram 	if (fp == NULL) {
821621Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
822621Svikram 		return (NULL);
823621Svikram 	}
824621Svikram 
825621Svikram 	resetmnttab(fp);
826621Svikram 
827621Svikram 	mnted = 0;
828621Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
829621Svikram 		if (strcmp(mnt.mnt_mountp, STUBBOOT) == 0) {
830621Svikram 			mnted = 1;
831621Svikram 			break;
832621Svikram 		}
833621Svikram 	}
834621Svikram 
835621Svikram 	(void) fclose(fp);
836621Svikram 
837621Svikram 	if (mnted)
838621Svikram 		return (STUBBOOT);
839621Svikram 
840621Svikram 	/*
841621Svikram 	 * Stubboot is not mounted, mount it now.
842621Svikram 	 * It should exist in /etc/vfstab
843621Svikram 	 */
844621Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount %s",
845621Svikram 	    STUBBOOT);
8465648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
847621Svikram 		bam_error(MOUNT_MNTPT_FAILED, STUBBOOT);
848621Svikram 		return (NULL);
849621Svikram 	}
850621Svikram 
851621Svikram 	return (STUBBOOT);
852621Svikram }
853621Svikram 
854621Svikram static void
855621Svikram disp_active_menu_locn(char *menu_path, char *logslice, char *fstype, int mnted)
856621Svikram {
857621Svikram 	/*
858621Svikram 	 * Check if we did a temp mount of an unmounted device.
859621Svikram 	 * If yes, print the block device and fstype for that device
860621Svikram 	 * else it is already mounted, so we print the path to the GRUB menu.
861621Svikram 	 */
862621Svikram 	if (mnted) {
863621Svikram 		bam_print(GRUB_MENU_DEVICE, logslice);
864621Svikram 		bam_print(GRUB_MENU_FSTYPE, fstype);
865621Svikram 	} else {
866621Svikram 		bam_print(GRUB_MENU_PATH, menu_path);
867621Svikram 	}
868621Svikram }
869621Svikram 
870621Svikram /*
871621Svikram  * NOTE: A single "/" is also considered a trailing slash and will
872621Svikram  * be deleted.
873621Svikram  */
874621Svikram static void
875621Svikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
876621Svikram {
877621Svikram 	size_t dstlen;
878621Svikram 
879621Svikram 	assert(src);
880621Svikram 	assert(dst);
881621Svikram 
882621Svikram 	(void) strlcpy(dst, src, dstsize);
883621Svikram 
884621Svikram 	dstlen = strlen(dst);
885621Svikram 	if (dst[dstlen - 1] == '/') {
886621Svikram 		dst[dstlen - 1] = '\0';
887621Svikram 	}
888621Svikram }
889621Svikram 
8900Sstevel@tonic-gate static error_t
8910Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
8920Sstevel@tonic-gate {
8930Sstevel@tonic-gate 	error_t ret;
8940Sstevel@tonic-gate 	char menu_path[PATH_MAX];
8953446Smrj 	char path[PATH_MAX];
8960Sstevel@tonic-gate 	menu_t *menu;
897621Svikram 	char *mntpt, *menu_root, *logslice, *fstype;
898316Svikram 	struct stat sb;
899316Svikram 	int mnted;	/* set if we did a mount */
9000Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
9015648Ssetje 	char *rootpath;
9025648Ssetje 
9035648Ssetje 	/*
9045648Ssetje 	 * Menu sub-command only applies to GRUB (i.e. x86)
9055648Ssetje 	 */
9065648Ssetje 	rootpath = (bam_alt_root) ? bam_root : "/";
9075648Ssetje 	if (!is_grub((const char *)rootpath)) {
9085648Ssetje 		bam_error(NOT_ON_SPARC);
9095648Ssetje 		return (BAM_ERROR);
9105648Ssetje 	}
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	/*
9130Sstevel@tonic-gate 	 * Check arguments
9140Sstevel@tonic-gate 	 */
9150Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
9160Sstevel@tonic-gate 	if (ret == BAM_ERROR) {
9170Sstevel@tonic-gate 		return (BAM_ERROR);
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate 
920316Svikram 	mntpt = NULL;
921316Svikram 	mnted = 0;
922621Svikram 	logslice = fstype = NULL;
923621Svikram 
924621Svikram 	/*
9253446Smrj 	 * Check for the menu.list file:
9263446Smrj 	 *
9273446Smrj 	 * 1. Check for a GRUB_slice file, be it on / or
9283446Smrj 	 *    on the user-provided alternate root.
9293446Smrj 	 * 2. Use the alternate root, if given.
9303446Smrj 	 * 3. Check /stubboot
9313446Smrj 	 * 4. Use /
932621Svikram 	 */
933621Svikram 	if (bam_alt_root) {
9343446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
9353446Smrj 		    GRUB_slice);
9363446Smrj 	} else {
9373446Smrj 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
9383446Smrj 	}
9393446Smrj 
9403446Smrj 	if (stat(path, &sb) == 0) {
941621Svikram 		mntpt = mount_grub_slice(&mnted, NULL, &logslice, &fstype);
942316Svikram 		menu_root = mntpt;
9433446Smrj 	} else if (bam_alt_root) {
9443446Smrj 		menu_root = bam_root;
945621Svikram 	} else if (stat(STUBBOOT, &sb) == 0) {
946621Svikram 		menu_root = use_stubboot();
947316Svikram 	} else {
948316Svikram 		menu_root = bam_root;
949316Svikram 	}
950316Svikram 
951621Svikram 	if (menu_root == NULL) {
952621Svikram 		bam_error(CANNOT_LOCATE_GRUB_MENU);
953621Svikram 		return (BAM_ERROR);
954621Svikram 	}
955621Svikram 
956621Svikram 	elide_trailing_slash(menu_root, menu_path, sizeof (menu_path));
957621Svikram 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
958621Svikram 
959621Svikram 	/*
960621Svikram 	 * If listing the menu, display the active menu
961621Svikram 	 * location
962621Svikram 	 */
963621Svikram 	if (strcmp(subcmd, "list_entry") == 0) {
964621Svikram 		disp_active_menu_locn(menu_path, logslice, fstype, mnted);
965621Svikram 	}
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	menu = menu_read(menu_path);
9680Sstevel@tonic-gate 	assert(menu);
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	/*
9710Sstevel@tonic-gate 	 * Special handling for setting timeout and default
9720Sstevel@tonic-gate 	 */
9730Sstevel@tonic-gate 	if (strcmp(subcmd, "set_option") == 0) {
9740Sstevel@tonic-gate 		if (largc != 1 || largv[0] == NULL) {
9750Sstevel@tonic-gate 			usage();
976621Svikram 			menu_free(menu);
977621Svikram 			umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
9780Sstevel@tonic-gate 			return (BAM_ERROR);
9790Sstevel@tonic-gate 		}
9800Sstevel@tonic-gate 		opt = largv[0];
9810Sstevel@tonic-gate 	} else if (largc != 0) {
9820Sstevel@tonic-gate 		usage();
983621Svikram 		menu_free(menu);
984621Svikram 		umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
9850Sstevel@tonic-gate 		return (BAM_ERROR);
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 
9883446Smrj 	ret = dboot_or_multiboot(bam_root);
9893446Smrj 	if (ret != BAM_SUCCESS)
9903446Smrj 		return (ret);
9913446Smrj 
9920Sstevel@tonic-gate 	/*
9930Sstevel@tonic-gate 	 * Once the sub-cmd handler has run
9940Sstevel@tonic-gate 	 * only the line field is guaranteed to have valid values
9950Sstevel@tonic-gate 	 */
9963446Smrj 	if ((strcmp(subcmd, "update_entry") == 0) ||
9973446Smrj 	    (strcmp(subcmd, "upgrade") == 0))
9980Sstevel@tonic-gate 		ret = f(menu, bam_root, opt);
9990Sstevel@tonic-gate 	else
10000Sstevel@tonic-gate 		ret = f(menu, menu_path, opt);
10010Sstevel@tonic-gate 	if (ret == BAM_WRITE) {
1002316Svikram 		ret = menu_write(menu_root, menu);
10030Sstevel@tonic-gate 	}
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	menu_free(menu);
10060Sstevel@tonic-gate 
1007621Svikram 	umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
1008316Svikram 
10090Sstevel@tonic-gate 	return (ret);
10100Sstevel@tonic-gate }
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate static error_t
10140Sstevel@tonic-gate bam_archive(
10150Sstevel@tonic-gate 	char *subcmd,
10160Sstevel@tonic-gate 	char *opt)
10170Sstevel@tonic-gate {
10180Sstevel@tonic-gate 	error_t ret;
10190Sstevel@tonic-gate 	error_t (*f)(char *root, char *opt);
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	/*
1022662Sszhou 	 * Add trailing / for archive subcommands
1023662Sszhou 	 */
1024662Sszhou 	if (rootbuf[strlen(rootbuf) - 1] != '/')
1025662Sszhou 		(void) strcat(rootbuf, "/");
1026662Sszhou 	bam_rootlen = strlen(rootbuf);
1027662Sszhou 
1028662Sszhou 	/*
10290Sstevel@tonic-gate 	 * Check arguments
10300Sstevel@tonic-gate 	 */
10310Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
10320Sstevel@tonic-gate 	if (ret != BAM_SUCCESS) {
10330Sstevel@tonic-gate 		return (BAM_ERROR);
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 
10363446Smrj 	ret = dboot_or_multiboot(rootbuf);
10373446Smrj 	if (ret != BAM_SUCCESS)
10383446Smrj 		return (ret);
10393446Smrj 
10400Sstevel@tonic-gate 	/*
10410Sstevel@tonic-gate 	 * Check archive not supported with update_all
10420Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
10430Sstevel@tonic-gate 	 * information for each BE.
10440Sstevel@tonic-gate 	 */
10450Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
10460Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
10470Sstevel@tonic-gate 		return (BAM_ERROR);
10480Sstevel@tonic-gate 	}
10490Sstevel@tonic-gate 
1050316Svikram 	if (strcmp(subcmd, "update_all") == 0)
1051316Svikram 		bam_update_all = 1;
1052316Svikram 
10535648Ssetje #if !defined(_OPB)
10544581Ssherrym 	ucode_install(bam_root);
10554581Ssherrym #endif
10564581Ssherrym 
1057316Svikram 	ret = f(bam_root, opt);
1058316Svikram 
1059316Svikram 	bam_update_all = 0;
1060316Svikram 
1061316Svikram 	return (ret);
10620Sstevel@tonic-gate }
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate /*PRINTFLIKE1*/
10653446Smrj void
10660Sstevel@tonic-gate bam_error(char *format, ...)
10670Sstevel@tonic-gate {
10680Sstevel@tonic-gate 	va_list ap;
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 	va_start(ap, format);
10710Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
10720Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
10730Sstevel@tonic-gate 	va_end(ap);
10740Sstevel@tonic-gate }
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate /*PRINTFLIKE1*/
10770Sstevel@tonic-gate static void
10780Sstevel@tonic-gate bam_print(char *format, ...)
10790Sstevel@tonic-gate {
10800Sstevel@tonic-gate 	va_list ap;
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 	va_start(ap, format);
10830Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
10840Sstevel@tonic-gate 	va_end(ap);
10850Sstevel@tonic-gate }
10860Sstevel@tonic-gate 
10873446Smrj /*PRINTFLIKE1*/
10883446Smrj void
10893446Smrj bam_print_stderr(char *format, ...)
10903446Smrj {
10913446Smrj 	va_list ap;
10923446Smrj 
10933446Smrj 	va_start(ap, format);
10943446Smrj 	(void) vfprintf(stderr, format, ap);
10953446Smrj 	va_end(ap);
10963446Smrj }
10973446Smrj 
10980Sstevel@tonic-gate static void
10990Sstevel@tonic-gate bam_exit(int excode)
11000Sstevel@tonic-gate {
11010Sstevel@tonic-gate 	bam_unlock();
11020Sstevel@tonic-gate 	exit(excode);
11030Sstevel@tonic-gate }
11040Sstevel@tonic-gate 
11050Sstevel@tonic-gate static void
11060Sstevel@tonic-gate bam_lock(void)
11070Sstevel@tonic-gate {
11080Sstevel@tonic-gate 	struct flock lock;
11090Sstevel@tonic-gate 	pid_t pid;
11100Sstevel@tonic-gate 
11110Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
11120Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11130Sstevel@tonic-gate 		/*
11140Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
11150Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
11160Sstevel@tonic-gate 		 * Proceed without the lock
11170Sstevel@tonic-gate 		 */
11180Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
11190Sstevel@tonic-gate 			bam_root_readonly = 1;
11200Sstevel@tonic-gate 			return;
11210Sstevel@tonic-gate 		}
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
11240Sstevel@tonic-gate 		bam_exit(1);
11250Sstevel@tonic-gate 	}
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
11280Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
11290Sstevel@tonic-gate 	lock.l_start = 0;
11300Sstevel@tonic-gate 	lock.l_len = 0;
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
11330Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
11340Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11350Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11360Sstevel@tonic-gate 			bam_lock_fd = -1;
11370Sstevel@tonic-gate 			bam_exit(1);
11380Sstevel@tonic-gate 		}
11390Sstevel@tonic-gate 		pid = 0;
11400Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
11410Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
11440Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
11450Sstevel@tonic-gate 		lock.l_start = 0;
11460Sstevel@tonic-gate 		lock.l_len = 0;
11470Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
11480Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11490Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11500Sstevel@tonic-gate 			bam_lock_fd = -1;
11510Sstevel@tonic-gate 			bam_exit(1);
11520Sstevel@tonic-gate 		}
11530Sstevel@tonic-gate 	}
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 	/* We own the lock now */
11560Sstevel@tonic-gate 	pid = getpid();
11570Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
11580Sstevel@tonic-gate }
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate static void
11610Sstevel@tonic-gate bam_unlock(void)
11620Sstevel@tonic-gate {
11630Sstevel@tonic-gate 	struct flock unlock;
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 	/*
11660Sstevel@tonic-gate 	 * NOP if we don't hold the lock
11670Sstevel@tonic-gate 	 */
11680Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11690Sstevel@tonic-gate 		return;
11700Sstevel@tonic-gate 	}
11710Sstevel@tonic-gate 
11720Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
11730Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
11740Sstevel@tonic-gate 	unlock.l_start = 0;
11750Sstevel@tonic-gate 	unlock.l_len = 0;
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
11780Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11790Sstevel@tonic-gate 	}
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
11820Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
11830Sstevel@tonic-gate 	}
11840Sstevel@tonic-gate 	bam_lock_fd = -1;
11850Sstevel@tonic-gate }
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate static error_t
11880Sstevel@tonic-gate list_archive(char *root, char *opt)
11890Sstevel@tonic-gate {
11900Sstevel@tonic-gate 	filelist_t flist;
11910Sstevel@tonic-gate 	filelist_t *flistp = &flist;
11920Sstevel@tonic-gate 	line_t *lp;
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 	assert(root);
11950Sstevel@tonic-gate 	assert(opt == NULL);
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
11980Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
11990Sstevel@tonic-gate 		return (BAM_ERROR);
12000Sstevel@tonic-gate 	}
12010Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
12020Sstevel@tonic-gate 
12030Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
12040Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
12050Sstevel@tonic-gate 	}
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	filelist_free(flistp);
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate 	return (BAM_SUCCESS);
12100Sstevel@tonic-gate }
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate /*
12130Sstevel@tonic-gate  * This routine writes a list of lines to a file.
12140Sstevel@tonic-gate  * The list is *not* freed
12150Sstevel@tonic-gate  */
12160Sstevel@tonic-gate static error_t
12170Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
12180Sstevel@tonic-gate {
12190Sstevel@tonic-gate 	char tmpfile[PATH_MAX];
12200Sstevel@tonic-gate 	char path[PATH_MAX];
12210Sstevel@tonic-gate 	FILE *fp;
12220Sstevel@tonic-gate 	int ret;
12230Sstevel@tonic-gate 	struct stat sb;
12240Sstevel@tonic-gate 	mode_t mode;
12250Sstevel@tonic-gate 	uid_t root_uid;
12260Sstevel@tonic-gate 	gid_t sys_gid;
12270Sstevel@tonic-gate 	struct passwd *pw;
12280Sstevel@tonic-gate 	struct group *gp;
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
12320Sstevel@tonic-gate 
12330Sstevel@tonic-gate 	if (start == NULL) {
12340Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
12350Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
12360Sstevel@tonic-gate 			if (unlink(path) != 0) {
12370Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
12380Sstevel@tonic-gate 				return (BAM_ERROR);
12390Sstevel@tonic-gate 			} else {
12400Sstevel@tonic-gate 				return (BAM_SUCCESS);
12410Sstevel@tonic-gate 			}
12420Sstevel@tonic-gate 		}
12430Sstevel@tonic-gate 	}
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	/*
12460Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
12470Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
12480Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
12490Sstevel@tonic-gate 	 */
12500Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
12510Sstevel@tonic-gate 		mode = sb.st_mode;
12520Sstevel@tonic-gate 		root_uid = sb.st_uid;
12530Sstevel@tonic-gate 		sys_gid = sb.st_gid;
12540Sstevel@tonic-gate 	} else {
12550Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
12560Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
12570Sstevel@tonic-gate 			root_uid = pw->pw_uid;
12580Sstevel@tonic-gate 		} else {
12590Sstevel@tonic-gate 			if (bam_verbose)
12600Sstevel@tonic-gate 				bam_error(CANT_FIND_USER,
12610Sstevel@tonic-gate 				    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
12620Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
12630Sstevel@tonic-gate 		}
12640Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
12650Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
12660Sstevel@tonic-gate 		} else {
12670Sstevel@tonic-gate 			if (bam_verbose)
12680Sstevel@tonic-gate 				bam_error(CANT_FIND_GROUP,
12690Sstevel@tonic-gate 				    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
12700Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
12710Sstevel@tonic-gate 		}
12720Sstevel@tonic-gate 	}
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	/* Truncate tmpfile first */
12770Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
12780Sstevel@tonic-gate 	if (fp == NULL) {
12790Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12800Sstevel@tonic-gate 		return (BAM_ERROR);
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 	ret = fclose(fp);
12830Sstevel@tonic-gate 	if (ret == EOF) {
12840Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12850Sstevel@tonic-gate 		return (BAM_ERROR);
12860Sstevel@tonic-gate 	}
12870Sstevel@tonic-gate 
12880Sstevel@tonic-gate 	/* Now open it in append mode */
12890Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
12900Sstevel@tonic-gate 	if (fp == NULL) {
12910Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12920Sstevel@tonic-gate 		return (BAM_ERROR);
12930Sstevel@tonic-gate 	}
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 	for (; start; start = start->next) {
12960Sstevel@tonic-gate 		ret = s_fputs(start->line, fp);
12970Sstevel@tonic-gate 		if (ret == EOF) {
12980Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
12990Sstevel@tonic-gate 			(void) fclose(fp);
13000Sstevel@tonic-gate 			return (BAM_ERROR);
13010Sstevel@tonic-gate 		}
13020Sstevel@tonic-gate 	}
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 	ret = fclose(fp);
13050Sstevel@tonic-gate 	if (ret == EOF) {
13060Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
13070Sstevel@tonic-gate 		return (BAM_ERROR);
13080Sstevel@tonic-gate 	}
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate 	/*
1311271Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1312271Sjg 	 * not supporting these operations - pcfs reports unsupported
1313271Sjg 	 * operations as EINVAL.
13140Sstevel@tonic-gate 	 */
13150Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1316271Sjg 	if (ret == -1 &&
1317271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13180Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
13190Sstevel@tonic-gate 		return (BAM_ERROR);
13200Sstevel@tonic-gate 	}
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1323271Sjg 	if (ret == -1 &&
1324271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13250Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
13260Sstevel@tonic-gate 		return (BAM_ERROR);
13270Sstevel@tonic-gate 	}
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 
13300Sstevel@tonic-gate 	/*
13310Sstevel@tonic-gate 	 * Do an atomic rename
13320Sstevel@tonic-gate 	 */
13330Sstevel@tonic-gate 	ret = rename(tmpfile, path);
13340Sstevel@tonic-gate 	if (ret != 0) {
13350Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
13360Sstevel@tonic-gate 		return (BAM_ERROR);
13370Sstevel@tonic-gate 	}
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate 	return (BAM_SUCCESS);
13400Sstevel@tonic-gate }
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate /*
13430Sstevel@tonic-gate  * This function should always return 0 - since we want
13440Sstevel@tonic-gate  * to create stat data for *all* files in the list.
13450Sstevel@tonic-gate  */
13460Sstevel@tonic-gate /*ARGSUSED*/
13470Sstevel@tonic-gate static int
13480Sstevel@tonic-gate cmpstat(
13490Sstevel@tonic-gate 	const char *file,
13500Sstevel@tonic-gate 	const struct stat *stat,
13510Sstevel@tonic-gate 	int flags,
13520Sstevel@tonic-gate 	struct FTW *ftw)
13530Sstevel@tonic-gate {
13540Sstevel@tonic-gate 	uint_t sz;
13550Sstevel@tonic-gate 	uint64_t *value;
13560Sstevel@tonic-gate 	uint64_t filestat[2];
13570Sstevel@tonic-gate 	int error;
13580Sstevel@tonic-gate 
13592334Ssetje 	struct safefile *safefilep;
13602334Ssetje 	FILE *fp;
13612334Ssetje 
13620Sstevel@tonic-gate 	/*
13630Sstevel@tonic-gate 	 * We only want regular files
13640Sstevel@tonic-gate 	 */
13650Sstevel@tonic-gate 	if (!S_ISREG(stat->st_mode))
13660Sstevel@tonic-gate 		return (0);
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 	/*
13690Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
13700Sstevel@tonic-gate 	 * but this is not fatal to update determination.
13710Sstevel@tonic-gate 	 */
13720Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
13730Sstevel@tonic-gate 		filestat[0] = stat->st_size;
13740Sstevel@tonic-gate 		filestat[1] = stat->st_mtime;
13750Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
13760Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
13770Sstevel@tonic-gate 		if (error)
13780Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
13790Sstevel@tonic-gate 	}
13800Sstevel@tonic-gate 
13810Sstevel@tonic-gate 	/*
13820Sstevel@tonic-gate 	 * The remaining steps are only required if we haven't made a
13830Sstevel@tonic-gate 	 * decision about update or if we are checking (-n)
13840Sstevel@tonic-gate 	 */
13850Sstevel@tonic-gate 	if (walk_arg.need_update && !bam_check)
13860Sstevel@tonic-gate 		return (0);
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate 	/*
1389*6319Sjg 	 * If we are invoked as part of system/filesystem/boot-archive, then
13902334Ssetje 	 * there are a number of things we should not worry about
13910Sstevel@tonic-gate 	 */
13922334Ssetje 	if (bam_smf_check) {
13932334Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
13942334Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
13952334Ssetje 			return (0);
13962334Ssetje 
13972334Ssetje 		/* read in list of safe files */
13982334Ssetje 		if (safefiles == NULL)
13992334Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
14002334Ssetje 				safefiles = s_calloc(1,
14012334Ssetje 				    sizeof (struct safefile));
14022334Ssetje 				safefilep = safefiles;
14032334Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
14042334Ssetje 				    MAXNAMELEN);
14052334Ssetje 				safefilep->next = NULL;
14062334Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
14072334Ssetje 				    MAXNAMELEN, fp) != NULL) {
14082334Ssetje 					safefilep->next = s_calloc(1,
14092334Ssetje 					    sizeof (struct safefile));
14102334Ssetje 					safefilep = safefilep->next;
14112334Ssetje 					safefilep->name = s_calloc(1,
14122334Ssetje 					    MAXPATHLEN + MAXNAMELEN);
14132334Ssetje 					safefilep->next = NULL;
14142334Ssetje 				}
14152334Ssetje 				(void) fclose(fp);
14162334Ssetje 			}
14172334Ssetje 	}
14180Sstevel@tonic-gate 
14190Sstevel@tonic-gate 	/*
14200Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
14210Sstevel@tonic-gate 	 */
14220Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
14230Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
14240Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
14250Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
14260Sstevel@tonic-gate 			return (0);
14270Sstevel@tonic-gate 		walk_arg.need_update = 1;
14280Sstevel@tonic-gate 		if (bam_verbose)
14290Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
14300Sstevel@tonic-gate 		return (0);
14310Sstevel@tonic-gate 	}
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 	/*
14340Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
14350Sstevel@tonic-gate 	 */
14360Sstevel@tonic-gate 	assert(sz == 2);
14370Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 	if (filestat[0] != stat->st_size ||
14400Sstevel@tonic-gate 	    filestat[1] != stat->st_mtime) {
14413615Ssetje 		if (bam_smf_check) {
14423615Ssetje 			safefilep = safefiles;
14433615Ssetje 			while (safefilep != NULL) {
14443615Ssetje 				if (strcmp(file + bam_rootlen,
14453615Ssetje 				    safefilep->name) == 0) {
14463615Ssetje 					(void) creat(NEED_UPDATE_FILE, 0644);
14473615Ssetje 					return (0);
14483615Ssetje 				}
14493615Ssetje 				safefilep = safefilep->next;
14503615Ssetje 			}
14513615Ssetje 		}
14520Sstevel@tonic-gate 		walk_arg.need_update = 1;
14530Sstevel@tonic-gate 		if (bam_verbose)
14540Sstevel@tonic-gate 			if (bam_smf_check)
14550Sstevel@tonic-gate 				bam_print("    %s\n", file);
14560Sstevel@tonic-gate 			else
14570Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
14580Sstevel@tonic-gate 	}
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate 	return (0);
14610Sstevel@tonic-gate }
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate /*
14640Sstevel@tonic-gate  * Check flags and presence of required files.
14650Sstevel@tonic-gate  * The force flag and/or absence of files should
14660Sstevel@tonic-gate  * trigger an update.
14670Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
14680Sstevel@tonic-gate  * (as -n should only produce parseable output.)
14690Sstevel@tonic-gate  */
14700Sstevel@tonic-gate static void
14710Sstevel@tonic-gate check_flags_and_files(char *root)
14720Sstevel@tonic-gate {
14730Sstevel@tonic-gate 	char path[PATH_MAX];
14740Sstevel@tonic-gate 	struct stat sb;
14750Sstevel@tonic-gate 
14760Sstevel@tonic-gate 	/*
14770Sstevel@tonic-gate 	 * if force, create archive unconditionally
14780Sstevel@tonic-gate 	 */
14790Sstevel@tonic-gate 	if (bam_force) {
14800Sstevel@tonic-gate 		walk_arg.need_update = 1;
14810Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
14820Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
14830Sstevel@tonic-gate 		return;
14840Sstevel@tonic-gate 	}
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate 	/*
14870Sstevel@tonic-gate 	 * If archive is missing, create archive
14880Sstevel@tonic-gate 	 */
1489*6319Sjg 	if (is_sun4u()) {
14905648Ssetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
1491*6319Sjg 		    SUN4U_ARCHIVE);
1492*6319Sjg 	} else if (is_sun4v()) {
14935648Ssetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
1494*6319Sjg 		    SUN4V_ARCHIVE);
1495*6319Sjg 	} else {
14965648Ssetje 		if (bam_direct == BAM_DIRECT_DBOOT) {
14975648Ssetje 			(void) snprintf(path, sizeof (path), "%s%s", root,
14985648Ssetje 			    DIRECT_BOOT_ARCHIVE_64);
14995648Ssetje 			if (stat(path, &sb) != 0) {
15005648Ssetje 				if (bam_verbose && !bam_check)
15015648Ssetje 					bam_print(UPDATE_ARCH_MISS, path);
15025648Ssetje 				walk_arg.need_update = 1;
15035648Ssetje 				return;
15045648Ssetje 			}
15055648Ssetje 		}
15065648Ssetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
15075648Ssetje 		    DIRECT_BOOT_ARCHIVE_32);
15085648Ssetje 	}
15095648Ssetje 
15100Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
15110Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
15120Sstevel@tonic-gate 			bam_print(UPDATE_ARCH_MISS, path);
15130Sstevel@tonic-gate 		walk_arg.need_update = 1;
15140Sstevel@tonic-gate 		return;
15150Sstevel@tonic-gate 	}
15160Sstevel@tonic-gate }
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate static error_t
15190Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
15200Sstevel@tonic-gate {
15210Sstevel@tonic-gate 	char path[PATH_MAX];
15220Sstevel@tonic-gate 	FILE *fp;
15230Sstevel@tonic-gate 	char buf[BAM_MAXLINE];
15240Sstevel@tonic-gate 
15250Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate 	fp = fopen(path, "r");
15280Sstevel@tonic-gate 	if (fp == NULL) {
15290Sstevel@tonic-gate 		if (bam_debug)
15300Sstevel@tonic-gate 			bam_error(FLIST_FAIL, path, strerror(errno));
15310Sstevel@tonic-gate 		return (BAM_ERROR);
15320Sstevel@tonic-gate 	}
15330Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1534316Svikram 		/* skip blank lines */
1535316Svikram 		if (strspn(buf, " \t") == strlen(buf))
1536316Svikram 			continue;
15370Sstevel@tonic-gate 		append_to_flist(flistp, buf);
15380Sstevel@tonic-gate 	}
15390Sstevel@tonic-gate 	if (fclose(fp) != 0) {
15400Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
15410Sstevel@tonic-gate 		return (BAM_ERROR);
15420Sstevel@tonic-gate 	}
15430Sstevel@tonic-gate 	return (BAM_SUCCESS);
15440Sstevel@tonic-gate }
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate static error_t
15470Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
15480Sstevel@tonic-gate {
15495648Ssetje 	char path[PATH_MAX];
15505648Ssetje 	char cmd[PATH_MAX];
15515648Ssetje 	struct stat sb;
15525648Ssetje 	int n, rval;
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 	/*
15575648Ssetje 	 * build and check path to extract_boot_filelist.ksh
15585648Ssetje 	 */
15595648Ssetje 	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
15605648Ssetje 	if (n >= sizeof (path)) {
15615648Ssetje 		bam_error(NO_FLIST);
15625648Ssetje 		return (BAM_ERROR);
15635648Ssetje 	}
15645648Ssetje 
15655648Ssetje 	/*
15665648Ssetje 	 * If extract_boot_filelist is present, exec it, otherwise read
15675648Ssetje 	 * the filelists directly, for compatibility with older images.
15680Sstevel@tonic-gate 	 */
15695648Ssetje 	if (stat(path, &sb) == 0) {
15705648Ssetje 		/*
15715648Ssetje 		 * build arguments to exec extract_boot_filelist.ksh
15725648Ssetje 		 */
1573*6319Sjg 		char *rootarg, *platarg;
1574*6319Sjg 		int platarglen = 1, rootarglen = 1;
1575*6319Sjg 		if (strlen(root) > 1)
1576*6319Sjg 			rootarglen += strlen(root) + strlen("-R ");
1577*6319Sjg 		if (bam_alt_platform)
1578*6319Sjg 			platarglen += strlen(bam_platform) + strlen("-p ");
1579*6319Sjg 		platarg = s_calloc(1, platarglen);
1580*6319Sjg 		rootarg = s_calloc(1, rootarglen);
1581*6319Sjg 		*platarg = 0;
1582*6319Sjg 		*rootarg = 0;
1583*6319Sjg 
15845648Ssetje 		if (strlen(root) > 1) {
1585*6319Sjg 			(void) snprintf(rootarg, rootarglen,
1586*6319Sjg 			    "-R %s", root);
15875648Ssetje 		}
1588*6319Sjg 		if (bam_alt_platform) {
1589*6319Sjg 			(void) snprintf(platarg, platarglen,
1590*6319Sjg 			    "-p %s", bam_platform);
1591*6319Sjg 		}
1592*6319Sjg 		n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
1593*6319Sjg 		    path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
1594*6319Sjg 		free(platarg);
1595*6319Sjg 		free(rootarg);
15965648Ssetje 		if (n >= sizeof (cmd)) {
15975648Ssetje 			bam_error(NO_FLIST);
15985648Ssetje 			return (BAM_ERROR);
15995648Ssetje 		}
16005648Ssetje 		if (exec_cmd(cmd, flistp) != 0) {
16015648Ssetje 			if (bam_debug)
16025648Ssetje 				bam_error(FLIST_FAIL, path, strerror(errno));
16035648Ssetje 			return (BAM_ERROR);
16045648Ssetje 		}
16055648Ssetje 	} else {
16065648Ssetje 		/*
16075648Ssetje 		 * Read current lists of files - only the first is mandatory
16085648Ssetje 		 */
16095648Ssetje 		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
16105648Ssetje 		if (rval != BAM_SUCCESS)
16115648Ssetje 			return (rval);
16125648Ssetje 		(void) read_one_list(root, flistp, ETC_FILE_LIST);
16135648Ssetje 	}
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate 	if (flistp->head == NULL) {
16160Sstevel@tonic-gate 		bam_error(NO_FLIST);
16170Sstevel@tonic-gate 		return (BAM_ERROR);
16180Sstevel@tonic-gate 	}
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate 	return (BAM_SUCCESS);
16210Sstevel@tonic-gate }
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate static void
16240Sstevel@tonic-gate getoldstat(char *root)
16250Sstevel@tonic-gate {
16260Sstevel@tonic-gate 	char path[PATH_MAX];
16270Sstevel@tonic-gate 	int fd, error;
16280Sstevel@tonic-gate 	struct stat sb;
16290Sstevel@tonic-gate 	char *ostat;
16300Sstevel@tonic-gate 
16310Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
16320Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
16330Sstevel@tonic-gate 	if (fd == -1) {
16340Sstevel@tonic-gate 		if (bam_verbose)
16350Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
16360Sstevel@tonic-gate 		walk_arg.need_update = 1;
16370Sstevel@tonic-gate 		return;
16380Sstevel@tonic-gate 	}
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
16410Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
16420Sstevel@tonic-gate 		(void) close(fd);
16430Sstevel@tonic-gate 		walk_arg.need_update = 1;
16440Sstevel@tonic-gate 		return;
16450Sstevel@tonic-gate 	}
16460Sstevel@tonic-gate 
16470Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
16500Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
16510Sstevel@tonic-gate 		(void) close(fd);
16520Sstevel@tonic-gate 		free(ostat);
16530Sstevel@tonic-gate 		walk_arg.need_update = 1;
16540Sstevel@tonic-gate 		return;
16550Sstevel@tonic-gate 	}
16560Sstevel@tonic-gate 
16570Sstevel@tonic-gate 	(void) close(fd);
16580Sstevel@tonic-gate 
16590Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
16600Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
16610Sstevel@tonic-gate 
16620Sstevel@tonic-gate 	free(ostat);
16630Sstevel@tonic-gate 
16640Sstevel@tonic-gate 	if (error) {
16650Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
16660Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
16670Sstevel@tonic-gate 		walk_arg.need_update = 1;
16680Sstevel@tonic-gate 		return;
16690Sstevel@tonic-gate 	}
16700Sstevel@tonic-gate }
16710Sstevel@tonic-gate 
16722583Svikram /*
16732583Svikram  * Checks if a file in the current (old) archive has
16742583Svikram  * been deleted from the root filesystem. This is needed for
16752583Svikram  * software like Trusted Extensions (TX) that switch early
16762583Svikram  * in boot based on presence/absence of a kernel module.
16772583Svikram  */
16782583Svikram static void
16792583Svikram check4stale(char *root)
16802583Svikram {
16812583Svikram 	nvpair_t	*nvp;
16822583Svikram 	nvlist_t	*nvlp;
16832583Svikram 	char 		*file;
16842583Svikram 	char		path[PATH_MAX];
16852583Svikram 	struct stat	sb;
16862583Svikram 
16872583Svikram 	/*
16882583Svikram 	 * Skip stale file check during smf check
16892583Svikram 	 */
16902583Svikram 	if (bam_smf_check)
16912583Svikram 		return;
16922583Svikram 
16932583Svikram 	/* Nothing to do if no old stats */
16942583Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
16952583Svikram 		return;
16962583Svikram 
16972583Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
16982583Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
16992583Svikram 		file = nvpair_name(nvp);
17002583Svikram 		if (file == NULL)
17012583Svikram 			continue;
17022583Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
17032583Svikram 		    root, file);
17042583Svikram 		if (stat(path, &sb) == -1) {
17052583Svikram 			walk_arg.need_update = 1;
17062583Svikram 			if (bam_verbose)
17072583Svikram 				bam_print(PARSEABLE_STALE_FILE, path);
17082583Svikram 		}
17092583Svikram 	}
17102583Svikram }
17112583Svikram 
17120Sstevel@tonic-gate static void
17130Sstevel@tonic-gate create_newstat(void)
17140Sstevel@tonic-gate {
17150Sstevel@tonic-gate 	int error;
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
17180Sstevel@tonic-gate 	if (error) {
17190Sstevel@tonic-gate 		/*
17200Sstevel@tonic-gate 		 * Not fatal - we can still create archive
17210Sstevel@tonic-gate 		 */
17220Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
17230Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
17240Sstevel@tonic-gate 	}
17250Sstevel@tonic-gate }
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate static void
17280Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
17290Sstevel@tonic-gate {
17300Sstevel@tonic-gate 	char path[PATH_MAX];
17310Sstevel@tonic-gate 	line_t *lp;
17320Sstevel@tonic-gate 
17330Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
17345648Ssetje 		/*
17355648Ssetje 		 * Don't follow symlinks.  A symlink must refer to
17365648Ssetje 		 * a file that would appear in the archive through
17375648Ssetje 		 * a direct reference.  This matches the archive
17385648Ssetje 		 * construction behavior.
17395648Ssetje 		 */
17400Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
17415648Ssetje 		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
17420Sstevel@tonic-gate 			/*
17430Sstevel@tonic-gate 			 * Some files may not exist.
17440Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
17450Sstevel@tonic-gate 			 * Emit verbose message only
17460Sstevel@tonic-gate 			 */
17470Sstevel@tonic-gate 			if (bam_verbose)
17480Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
17490Sstevel@tonic-gate 		}
17500Sstevel@tonic-gate 	}
17510Sstevel@tonic-gate }
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate static void
17540Sstevel@tonic-gate savenew(char *root)
17550Sstevel@tonic-gate {
17560Sstevel@tonic-gate 	char path[PATH_MAX];
17570Sstevel@tonic-gate 	char path2[PATH_MAX];
17580Sstevel@tonic-gate 	size_t sz;
17590Sstevel@tonic-gate 	char *nstat;
17600Sstevel@tonic-gate 	int fd, wrote, error;
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 	nstat = NULL;
17630Sstevel@tonic-gate 	sz = 0;
17640Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
17650Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
17660Sstevel@tonic-gate 	if (error) {
17670Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
17680Sstevel@tonic-gate 		return;
17690Sstevel@tonic-gate 	}
17700Sstevel@tonic-gate 
17710Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
17720Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
17730Sstevel@tonic-gate 	if (fd == -1) {
17740Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
17750Sstevel@tonic-gate 		free(nstat);
17760Sstevel@tonic-gate 		return;
17770Sstevel@tonic-gate 	}
17780Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
17790Sstevel@tonic-gate 	if (wrote != sz) {
17800Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
17810Sstevel@tonic-gate 		(void) close(fd);
17820Sstevel@tonic-gate 		free(nstat);
17830Sstevel@tonic-gate 		return;
17840Sstevel@tonic-gate 	}
17850Sstevel@tonic-gate 	(void) close(fd);
17860Sstevel@tonic-gate 	free(nstat);
17870Sstevel@tonic-gate 
17880Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
17890Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
17900Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
17910Sstevel@tonic-gate 	}
17920Sstevel@tonic-gate }
17930Sstevel@tonic-gate 
17940Sstevel@tonic-gate static void
17950Sstevel@tonic-gate clear_walk_args(void)
17960Sstevel@tonic-gate {
17970Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
17980Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
17990Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
18000Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
18010Sstevel@tonic-gate 	walk_arg.need_update = 0;
18020Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
18030Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
18040Sstevel@tonic-gate }
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate /*
18070Sstevel@tonic-gate  * Returns:
18080Sstevel@tonic-gate  *	0 - no update necessary
18090Sstevel@tonic-gate  *	1 - update required.
18100Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
18110Sstevel@tonic-gate  *
18120Sstevel@tonic-gate  * Special handling for check (-n):
18130Sstevel@tonic-gate  * ================================
18140Sstevel@tonic-gate  * The check (-n) option produces parseable output.
18150Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
18160Sstevel@tonic-gate  * to out of sync files.
18170Sstevel@tonic-gate  * All stderr messages are still printed though.
18180Sstevel@tonic-gate  *
18190Sstevel@tonic-gate  */
18200Sstevel@tonic-gate static int
18210Sstevel@tonic-gate update_required(char *root)
18220Sstevel@tonic-gate {
18230Sstevel@tonic-gate 	struct stat sb;
18240Sstevel@tonic-gate 	char path[PATH_MAX];
18250Sstevel@tonic-gate 	filelist_t flist;
18260Sstevel@tonic-gate 	filelist_t *flistp = &flist;
18270Sstevel@tonic-gate 	int need_update;
18280Sstevel@tonic-gate 
18290Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
18300Sstevel@tonic-gate 
18310Sstevel@tonic-gate 	walk_arg.need_update = 0;
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 	/*
18340Sstevel@tonic-gate 	 * Without consulting stat data, check if we need update
18350Sstevel@tonic-gate 	 */
18360Sstevel@tonic-gate 	check_flags_and_files(root);
18370Sstevel@tonic-gate 
18380Sstevel@tonic-gate 	/*
18390Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
18400Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
18410Sstevel@tonic-gate 	 */
18420Sstevel@tonic-gate 	if (bam_smf_check) {
18430Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
18440Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
18450Sstevel@tonic-gate 			return (0);
18460Sstevel@tonic-gate 	}
18470Sstevel@tonic-gate 
18480Sstevel@tonic-gate 	/*
18490Sstevel@tonic-gate 	 * consult stat data only if we haven't made a decision
18500Sstevel@tonic-gate 	 * about update. If checking (-n) however, we always
18510Sstevel@tonic-gate 	 * need stat data (since we want to compare old and new)
18520Sstevel@tonic-gate 	 */
18530Sstevel@tonic-gate 	if (!walk_arg.need_update || bam_check)
18540Sstevel@tonic-gate 		getoldstat(root);
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 	/*
18572583Svikram 	 * Check if the archive contains files that are no longer
18582583Svikram 	 * present on the root filesystem.
18592583Svikram 	 */
18602583Svikram 	if (!walk_arg.need_update || bam_check)
18612583Svikram 		check4stale(root);
18622583Svikram 
18632583Svikram 	/*
18640Sstevel@tonic-gate 	 * read list of files
18650Sstevel@tonic-gate 	 */
18660Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
18670Sstevel@tonic-gate 		clear_walk_args();
18680Sstevel@tonic-gate 		return (BAM_ERROR);
18690Sstevel@tonic-gate 	}
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 	/*
18740Sstevel@tonic-gate 	 * At this point either the update is required
18750Sstevel@tonic-gate 	 * or the decision is pending. In either case
18760Sstevel@tonic-gate 	 * we need to create new stat nvlist
18770Sstevel@tonic-gate 	 */
18780Sstevel@tonic-gate 	create_newstat();
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 	/*
18810Sstevel@tonic-gate 	 * This walk does 2 things:
18820Sstevel@tonic-gate 	 *  	- gets new stat data for every file
18830Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
18840Sstevel@tonic-gate 	 */
18850Sstevel@tonic-gate 	walk_list(root, &flist);
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate 	/* done with the file list */
18880Sstevel@tonic-gate 	filelist_free(flistp);
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	/*
18910Sstevel@tonic-gate 	 * if we didn't succeed in  creating new stat data above
18920Sstevel@tonic-gate 	 * just return result of update check so that archive is built.
18930Sstevel@tonic-gate 	 */
18940Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
18950Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
18960Sstevel@tonic-gate 		need_update = walk_arg.need_update;
18970Sstevel@tonic-gate 		clear_walk_args();
18980Sstevel@tonic-gate 		return (need_update ? 1 : 0);
18990Sstevel@tonic-gate 	}
19000Sstevel@tonic-gate 
19010Sstevel@tonic-gate 
19020Sstevel@tonic-gate 	/*
19030Sstevel@tonic-gate 	 * If no update required, discard newstat
19040Sstevel@tonic-gate 	 */
19050Sstevel@tonic-gate 	if (!walk_arg.need_update) {
19060Sstevel@tonic-gate 		clear_walk_args();
19070Sstevel@tonic-gate 		return (0);
19080Sstevel@tonic-gate 	}
19090Sstevel@tonic-gate 
19100Sstevel@tonic-gate 	return (1);
19110Sstevel@tonic-gate }
19120Sstevel@tonic-gate 
19130Sstevel@tonic-gate static error_t
19140Sstevel@tonic-gate create_ramdisk(char *root)
19150Sstevel@tonic-gate {
19160Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
19170Sstevel@tonic-gate 	size_t len;
19180Sstevel@tonic-gate 	struct stat sb;
19190Sstevel@tonic-gate 
19200Sstevel@tonic-gate 	/*
19210Sstevel@tonic-gate 	 * Setup command args for create_ramdisk.ksh
19220Sstevel@tonic-gate 	 */
19235648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
19240Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
19250Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
19260Sstevel@tonic-gate 		return (BAM_ERROR);
19270Sstevel@tonic-gate 	}
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
1930*6319Sjg 	if (bam_alt_platform)
1931*6319Sjg 		len += strlen(bam_platform) + strlen("-p ");
19320Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
19330Sstevel@tonic-gate 
1934*6319Sjg 	if (bam_alt_platform) {
1935*6319Sjg 		assert(strlen(root) > 1);
1936*6319Sjg 		(void) snprintf(cmdline, len, "%s -p %s -R %s",
1937*6319Sjg 		    path, bam_platform, root);
1938*6319Sjg 		/* chop off / at the end */
1939*6319Sjg 		cmdline[strlen(cmdline) - 1] = '\0';
1940*6319Sjg 	} else if (strlen(root) > 1) {
19410Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
19420Sstevel@tonic-gate 		/* chop off / at the end */
19430Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
19440Sstevel@tonic-gate 	} else
19450Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
19460Sstevel@tonic-gate 
19475648Ssetje 	if (exec_cmd(cmdline, NULL) != 0) {
19480Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
19490Sstevel@tonic-gate 		free(cmdline);
19500Sstevel@tonic-gate 		return (BAM_ERROR);
19510Sstevel@tonic-gate 	}
19520Sstevel@tonic-gate 	free(cmdline);
19530Sstevel@tonic-gate 
19540Sstevel@tonic-gate 	/*
19555648Ssetje 	 * The existence of the expected archives used to be
19565648Ssetje 	 * verified here. This check is done in create_ramdisk as
19575648Ssetje 	 * it needs to be in sync with the altroot operated upon.
19580Sstevel@tonic-gate 	 */
19590Sstevel@tonic-gate 
19600Sstevel@tonic-gate 	return (BAM_SUCCESS);
19610Sstevel@tonic-gate }
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate /*
19640Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
19650Sstevel@tonic-gate  * 1 - is miniroot
19660Sstevel@tonic-gate  * 0 - is not
19670Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
19680Sstevel@tonic-gate  */
19690Sstevel@tonic-gate static int
19700Sstevel@tonic-gate is_ramdisk(char *root)
19710Sstevel@tonic-gate {
19720Sstevel@tonic-gate 	struct extmnttab mnt;
19730Sstevel@tonic-gate 	FILE *fp;
19740Sstevel@tonic-gate 	int found;
1975316Svikram 	char mntpt[PATH_MAX];
1976316Svikram 	char *cp;
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate 	/*
19790Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
19800Sstevel@tonic-gate 	 * of dubious value:
1981316Svikram 	 *	- create boot_archive on a lofi-mounted boot_archive
19820Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
19830Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
19840Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
19850Sstevel@tonic-gate 	 * worth it.
19860Sstevel@tonic-gate 	 * The other two conditions are handled here
19870Sstevel@tonic-gate 	 */
19880Sstevel@tonic-gate 
19890Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
19900Sstevel@tonic-gate 	if (fp == NULL) {
19910Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
19920Sstevel@tonic-gate 		return (0);
19930Sstevel@tonic-gate 	}
19940Sstevel@tonic-gate 
19950Sstevel@tonic-gate 	resetmnttab(fp);
19960Sstevel@tonic-gate 
1997316Svikram 	/*
1998316Svikram 	 * Remove any trailing / from the mount point
1999316Svikram 	 */
2000316Svikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
2001316Svikram 	if (strcmp(root, "/") != 0) {
2002316Svikram 		cp = mntpt + strlen(mntpt) - 1;
2003316Svikram 		if (*cp == '/')
2004316Svikram 			*cp = '\0';
2005316Svikram 	}
20060Sstevel@tonic-gate 	found = 0;
20070Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
2008316Svikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
20090Sstevel@tonic-gate 			found = 1;
20100Sstevel@tonic-gate 			break;
20110Sstevel@tonic-gate 		}
20120Sstevel@tonic-gate 	}
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 	if (!found) {
20150Sstevel@tonic-gate 		if (bam_verbose)
2016316Svikram 			bam_error(NOT_IN_MNTTAB, mntpt);
20170Sstevel@tonic-gate 		(void) fclose(fp);
20180Sstevel@tonic-gate 		return (0);
20190Sstevel@tonic-gate 	}
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
20220Sstevel@tonic-gate 		if (bam_verbose)
20230Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
20240Sstevel@tonic-gate 		(void) fclose(fp);
20250Sstevel@tonic-gate 		return (1);
20260Sstevel@tonic-gate 	}
20270Sstevel@tonic-gate 
20280Sstevel@tonic-gate 	(void) fclose(fp);
20290Sstevel@tonic-gate 
20300Sstevel@tonic-gate 	return (0);
20310Sstevel@tonic-gate }
20320Sstevel@tonic-gate 
20330Sstevel@tonic-gate static int
20345648Ssetje is_boot_archive(char *root)
20350Sstevel@tonic-gate {
20360Sstevel@tonic-gate 	char path[PATH_MAX];
20370Sstevel@tonic-gate 	struct stat sb;
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate 	/*
20405648Ssetje 	 * We can't create an archive without the create_ramdisk script
20410Sstevel@tonic-gate 	 */
20425648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
20430Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
20440Sstevel@tonic-gate 		if (bam_verbose)
20450Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
20460Sstevel@tonic-gate 		return (0);
20470Sstevel@tonic-gate 	}
20480Sstevel@tonic-gate 
20495648Ssetje 	return (1);
20505648Ssetje }
20515648Ssetje 
20525648Ssetje /*
20535648Ssetje  * Need to call this for anything that operates on the GRUB menu
20545648Ssetje  */
20555648Ssetje int
20565648Ssetje is_grub(const char *root)
20575648Ssetje {
20585648Ssetje 	char path[PATH_MAX];
20595648Ssetje 	struct stat sb;
20605648Ssetje 
20610Sstevel@tonic-gate 	/*
20625648Ssetje 	 * GRUB_DIR is required to modify the menu
20630Sstevel@tonic-gate 	 */
20640Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR);
20650Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
20665648Ssetje 		if (bam_debug)
20670Sstevel@tonic-gate 			bam_print(DIR_MISS, path);
20680Sstevel@tonic-gate 		return (0);
20690Sstevel@tonic-gate 	}
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate 	return (1);
20720Sstevel@tonic-gate }
20730Sstevel@tonic-gate 
20740Sstevel@tonic-gate static int
20750Sstevel@tonic-gate is_readonly(char *root)
20760Sstevel@tonic-gate {
20770Sstevel@tonic-gate 	struct statvfs vfs;
20780Sstevel@tonic-gate 
20790Sstevel@tonic-gate 	/*
20800Sstevel@tonic-gate 	 * Check for RDONLY filesystem
20810Sstevel@tonic-gate 	 * When in doubt assume it is not readonly
20820Sstevel@tonic-gate 	 */
20830Sstevel@tonic-gate 	if (statvfs(root, &vfs) != 0) {
20840Sstevel@tonic-gate 		if (bam_verbose)
20850Sstevel@tonic-gate 			bam_error(STATVFS_FAIL, root, strerror(errno));
20860Sstevel@tonic-gate 		return (0);
20870Sstevel@tonic-gate 	}
20880Sstevel@tonic-gate 
20890Sstevel@tonic-gate 	if (vfs.f_flag & ST_RDONLY) {
20900Sstevel@tonic-gate 		return (1);
20910Sstevel@tonic-gate 	}
20920Sstevel@tonic-gate 
20930Sstevel@tonic-gate 	return (0);
20940Sstevel@tonic-gate }
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate static error_t
20970Sstevel@tonic-gate update_archive(char *root, char *opt)
20980Sstevel@tonic-gate {
20990Sstevel@tonic-gate 	error_t ret;
21000Sstevel@tonic-gate 
21010Sstevel@tonic-gate 	assert(root);
21020Sstevel@tonic-gate 	assert(opt == NULL);
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 	/*
21055648Ssetje 	 * root must belong to a boot archive based  OS,
21060Sstevel@tonic-gate 	 */
21075648Ssetje 	if (!is_boot_archive(root)) {
2108316Svikram 		/*
2109316Svikram 		 * Emit message only if not in context of update_all.
2110316Svikram 		 * If in update_all, emit only if verbose flag is set.
2111316Svikram 		 */
2112316Svikram 		if (!bam_update_all || bam_verbose)
2113316Svikram 			bam_print(NOT_GRUB_BOOT, root);
21140Sstevel@tonic-gate 		return (BAM_SUCCESS);
21150Sstevel@tonic-gate 	}
21160Sstevel@tonic-gate 
21170Sstevel@tonic-gate 	/*
2118662Sszhou 	 * If smf check is requested when / is writable (can happen
2119662Sszhou 	 * on first reboot following an upgrade because service
2120662Sszhou 	 * dependency is messed up), skip the check.
2121662Sszhou 	 */
2122662Sszhou 	if (bam_smf_check && !bam_root_readonly)
2123662Sszhou 		return (BAM_SUCCESS);
2124662Sszhou 
2125662Sszhou 	/*
2126662Sszhou 	 * root must be writable. This check applies to alternate
2127662Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
21280Sstevel@tonic-gate 	 * Note: statvfs() does not always report the truth
21290Sstevel@tonic-gate 	 */
2130756Ssetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
2131662Sszhou 		if (bam_verbose)
21320Sstevel@tonic-gate 			bam_print(RDONLY_FS, root);
21330Sstevel@tonic-gate 		return (BAM_SUCCESS);
21340Sstevel@tonic-gate 	}
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 	/*
21370Sstevel@tonic-gate 	 * Don't generate archive on ramdisk
21380Sstevel@tonic-gate 	 */
21390Sstevel@tonic-gate 	if (is_ramdisk(root)) {
21400Sstevel@tonic-gate 		if (bam_verbose)
21410Sstevel@tonic-gate 			bam_print(SKIP_RAMDISK);
21420Sstevel@tonic-gate 		return (BAM_SUCCESS);
21430Sstevel@tonic-gate 	}
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 	/*
21460Sstevel@tonic-gate 	 * Now check if updated is really needed
21470Sstevel@tonic-gate 	 */
21480Sstevel@tonic-gate 	ret = update_required(root);
21490Sstevel@tonic-gate 
21500Sstevel@tonic-gate 	/*
21510Sstevel@tonic-gate 	 * The check command (-n) is *not* a dry run
21520Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
21530Sstevel@tonic-gate 	 */
21540Sstevel@tonic-gate 	if (bam_check) {
21550Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
21560Sstevel@tonic-gate 	}
21570Sstevel@tonic-gate 
21580Sstevel@tonic-gate 	if (ret == 1) {
21590Sstevel@tonic-gate 		/* create the ramdisk */
21600Sstevel@tonic-gate 		ret = create_ramdisk(root);
21610Sstevel@tonic-gate 	}
21625648Ssetje 
21635648Ssetje 	/* if the archive is updated, save the new stat data */
21645648Ssetje 	if (ret == 0 && walk_arg.new_nvlp != NULL) {
21655648Ssetje 		savenew(root);
21665648Ssetje 	}
21675648Ssetje 
21685648Ssetje 	clear_walk_args();
21695648Ssetje 
21700Sstevel@tonic-gate 	return (ret);
21710Sstevel@tonic-gate }
21720Sstevel@tonic-gate 
2173316Svikram static void
21741746Svikram update_fdisk(void)
21751746Svikram {
21761746Svikram 	struct stat sb;
21771746Svikram 	char cmd[PATH_MAX];
21781746Svikram 	int ret1, ret2;
21791746Svikram 
21801746Svikram 	assert(stat(GRUB_fdisk, &sb) == 0);
21811746Svikram 	assert(stat(GRUB_fdisk_target, &sb) == 0);
21821746Svikram 
21831746Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`",
21841746Svikram 	    GRUB_fdisk, GRUB_fdisk_target);
21851746Svikram 
21861746Svikram 	bam_print(UPDATING_FDISK);
21875648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
21881746Svikram 		bam_error(FDISK_UPDATE_FAILED);
21891746Svikram 	}
21901746Svikram 
21911746Svikram 	/*
21921746Svikram 	 * We are done, remove the files.
21931746Svikram 	 */
21941746Svikram 	ret1 = unlink(GRUB_fdisk);
21951746Svikram 	ret2 = unlink(GRUB_fdisk_target);
21961746Svikram 	if (ret1 != 0 || ret2 != 0) {
21971746Svikram 		bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target);
21981746Svikram 	}
21991746Svikram }
22001746Svikram 
22011746Svikram static void
2202316Svikram restore_grub_slice(void)
2203316Svikram {
2204316Svikram 	struct stat sb;
2205316Svikram 	char *mntpt, *physlice;
2206316Svikram 	int mnted;	/* set if we did a mount */
2207316Svikram 	char menupath[PATH_MAX], cmd[PATH_MAX];
2208316Svikram 
2209316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
2210316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
2211316Svikram 		return;
2212316Svikram 	}
2213316Svikram 
2214316Svikram 	/*
2215316Svikram 	 * If we are doing an luactivate, don't attempt to restore GRUB or else
2216316Svikram 	 * we may not be able to get to DCA boot environments. Let luactivate
2217316Svikram 	 * handle GRUB/DCA installation
2218316Svikram 	 */
2219316Svikram 	if (stat(LU_ACTIVATE_FILE, &sb) == 0) {
2220316Svikram 		return;
2221316Svikram 	}
2222316Svikram 
2223316Svikram 	mnted = 0;
2224316Svikram 	physlice = NULL;
2225621Svikram 	mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL);
2226316Svikram 	if (mntpt == NULL) {
2227316Svikram 		bam_error(CANNOT_RESTORE_GRUB_SLICE);
2228316Svikram 		return;
2229316Svikram 	}
2230316Svikram 
2231316Svikram 	(void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU);
2232316Svikram 	if (stat(menupath, &sb) == 0) {
2233621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2234316Svikram 		return;
2235316Svikram 	}
2236316Svikram 
2237316Svikram 	/*
2238316Svikram 	 * The menu is missing - we need to do a restore
2239316Svikram 	 */
2240316Svikram 	bam_print(RESTORING_GRUB);
2241316Svikram 
2242316Svikram 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s %s",
2243316Svikram 	    INSTALLGRUB, STAGE1, STAGE2, physlice);
2244316Svikram 
22455648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
2246316Svikram 		bam_error(RESTORE_GRUB_FAILED);
2247621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2248316Svikram 		return;
2249316Svikram 	}
2250316Svikram 
2251316Svikram 	if (stat(GRUB_backup_menu, &sb) != 0) {
2252316Svikram 		bam_error(MISSING_BACKUP_MENU,
2253316Svikram 		    GRUB_backup_menu, strerror(errno));
2254621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2255316Svikram 		return;
2256316Svikram 	}
2257316Svikram 
2258316Svikram 	(void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s",
2259316Svikram 	    GRUB_backup_menu, menupath);
2260316Svikram 
22615648Ssetje 	if (exec_cmd(cmd, NULL) != 0) {
2262316Svikram 		bam_error(RESTORE_MENU_FAILED, menupath);
2263621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2264316Svikram 		return;
2265316Svikram 	}
2266316Svikram 
2267316Svikram 	/* Success */
2268621Svikram 	umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2269316Svikram }
2270316Svikram 
22710Sstevel@tonic-gate static error_t
22720Sstevel@tonic-gate update_all(char *root, char *opt)
22730Sstevel@tonic-gate {
22740Sstevel@tonic-gate 	struct extmnttab mnt;
22750Sstevel@tonic-gate 	struct stat sb;
22760Sstevel@tonic-gate 	FILE *fp;
22770Sstevel@tonic-gate 	char multibt[PATH_MAX];
22785648Ssetje 	char creatram[PATH_MAX];
22790Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
22801746Svikram 	int ret1, ret2;
22810Sstevel@tonic-gate 
2282621Svikram 	assert(root);
22830Sstevel@tonic-gate 	assert(opt == NULL);
22840Sstevel@tonic-gate 
2285621Svikram 	if (bam_rootlen != 1 || *root != '/') {
2286621Svikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
2287621Svikram 		bam_error(ALT_ROOT_INVALID, multibt);
2288621Svikram 		return (BAM_ERROR);
2289621Svikram 	}
2290621Svikram 
22910Sstevel@tonic-gate 	/*
22924493Snadkarni 	 * Check to see if we are in the midst of safemode patching
22934493Snadkarni 	 * If so skip building the archive for /. Instead build it
22944493Snadkarni 	 * against the latest bits obtained by creating a fresh lofs
22954493Snadkarni 	 * mount of root.
22960Sstevel@tonic-gate 	 */
22974493Snadkarni 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
22984493Snadkarni 		if (mkdir(LOFS_PATCH_MNT, 0755) == -1 &&
22994493Snadkarni 		    errno != EEXIST) {
23004493Snadkarni 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
23014493Snadkarni 			    strerror(errno));
23024493Snadkarni 			ret = BAM_ERROR;
23034493Snadkarni 			goto out;
23044493Snadkarni 		}
23054493Snadkarni 		(void) snprintf(multibt, sizeof (multibt),
23064493Snadkarni 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
23075648Ssetje 		if (exec_cmd(multibt, NULL) != 0) {
23084493Snadkarni 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
23094493Snadkarni 			ret = BAM_ERROR;
23104493Snadkarni 		}
23114493Snadkarni 		if (ret != BAM_ERROR) {
23124493Snadkarni 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
23134493Snadkarni 			    LOFS_PATCH_MNT);
23144493Snadkarni 			bam_rootlen = strlen(rootbuf);
23154493Snadkarni 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
23164493Snadkarni 				ret = BAM_ERROR;
23174550Snadkarni 			/*
23184550Snadkarni 			 * unmount the lofs mount since there could be
23194550Snadkarni 			 * multiple invocations of bootadm -a update_all
23204550Snadkarni 			 */
23214550Snadkarni 			(void) snprintf(multibt, sizeof (multibt),
23224550Snadkarni 			    "/sbin/umount %s", LOFS_PATCH_MNT);
23235648Ssetje 			if (exec_cmd(multibt, NULL) != 0) {
23244550Snadkarni 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
23254550Snadkarni 				ret = BAM_ERROR;
23264550Snadkarni 			}
23274493Snadkarni 		}
23284493Snadkarni 	} else {
23294493Snadkarni 		/*
23304493Snadkarni 		 * First update archive for current root
23314493Snadkarni 		 */
23324493Snadkarni 		if (update_archive(root, opt) != BAM_SUCCESS)
23334493Snadkarni 			ret = BAM_ERROR;
23344493Snadkarni 	}
23354493Snadkarni 
23364493Snadkarni 	if (ret == BAM_ERROR)
23374493Snadkarni 		goto out;
23380Sstevel@tonic-gate 
23390Sstevel@tonic-gate 	/*
23400Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
23410Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
23420Sstevel@tonic-gate 	 */
23430Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
23440Sstevel@tonic-gate 	if (fp == NULL) {
23450Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2346316Svikram 		ret = BAM_ERROR;
2347316Svikram 		goto out;
23480Sstevel@tonic-gate 	}
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate 	resetmnttab(fp);
23510Sstevel@tonic-gate 
23520Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
23530Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
23540Sstevel@tonic-gate 			continue;
23550Sstevel@tonic-gate 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
23560Sstevel@tonic-gate 			continue;
23570Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
23580Sstevel@tonic-gate 			continue;
23590Sstevel@tonic-gate 
23605648Ssetje 		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
23615648Ssetje 		    mnt.mnt_mountp, CREATE_RAMDISK);
23625648Ssetje 
23635648Ssetje 		if (stat(creatram, &sb) == -1)
23640Sstevel@tonic-gate 			continue;
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate 		/*
23670Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
23680Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
23690Sstevel@tonic-gate 		 */
23700Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
23710Sstevel@tonic-gate 		    mnt.mnt_mountp);
23720Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
23733446Smrj 
23743446Smrj 		/*
23753446Smrj 		 * It's possible that other mounts may be an alternate boot
23763446Smrj 		 * architecture, so check it again.
23773446Smrj 		 */
23783446Smrj 		if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) ||
23793446Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
23800Sstevel@tonic-gate 			ret = BAM_ERROR;
23810Sstevel@tonic-gate 	}
23820Sstevel@tonic-gate 
23830Sstevel@tonic-gate 	(void) fclose(fp);
23840Sstevel@tonic-gate 
2385316Svikram out:
2386316Svikram 	if (stat(GRUB_slice, &sb) == 0) {
2387316Svikram 		restore_grub_slice();
2388316Svikram 	}
2389316Svikram 
23901746Svikram 	/*
23911746Svikram 	 * Update fdisk table as we go down. Updating it when
23921746Svikram 	 * the system is running will confuse biosdev.
23931746Svikram 	 */
23941746Svikram 	ret1 = stat(GRUB_fdisk, &sb);
23951746Svikram 	ret2 = stat(GRUB_fdisk_target, &sb);
23961746Svikram 	if ((ret1 == 0) && (ret2 == 0)) {
23971746Svikram 		update_fdisk();
23981746Svikram 	} else if ((ret1 == 0) ^ (ret2 == 0)) {
23991746Svikram 		/*
24001746Svikram 		 * It is an error for one file to be
24011746Svikram 		 * present and the other absent.
24021746Svikram 		 * It is normal for both files to be
24031746Svikram 		 * absent - it indicates that no fdisk
24041746Svikram 		 * update is required.
24051746Svikram 		 */
24061746Svikram 		bam_error(MISSING_FDISK_FILE,
24071746Svikram 		    ret1 ? GRUB_fdisk : GRUB_fdisk_target);
24081746Svikram 		ret = BAM_ERROR;
24091746Svikram 	}
24101746Svikram 
24110Sstevel@tonic-gate 	return (ret);
24120Sstevel@tonic-gate }
24130Sstevel@tonic-gate 
24140Sstevel@tonic-gate static void
24150Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
24160Sstevel@tonic-gate {
24170Sstevel@tonic-gate 	if (mp->start == NULL) {
24180Sstevel@tonic-gate 		mp->start = lp;
24190Sstevel@tonic-gate 	} else {
24200Sstevel@tonic-gate 		mp->end->next = lp;
2421662Sszhou 		lp->prev = mp->end;
24220Sstevel@tonic-gate 	}
24230Sstevel@tonic-gate 	mp->end = lp;
24240Sstevel@tonic-gate }
24250Sstevel@tonic-gate 
2426662Sszhou static void
2427662Sszhou unlink_line(menu_t *mp, line_t *lp)
2428662Sszhou {
2429662Sszhou 	/* unlink from list */
2430662Sszhou 	if (lp->prev)
2431662Sszhou 		lp->prev->next = lp->next;
2432662Sszhou 	else
2433662Sszhou 		mp->start = lp->next;
2434662Sszhou 	if (lp->next)
2435662Sszhou 		lp->next->prev = lp->prev;
2436662Sszhou 	else
2437662Sszhou 		mp->end = lp->prev;
2438662Sszhou }
2439662Sszhou 
2440662Sszhou static entry_t *
2441662Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
2442662Sszhou {
2443662Sszhou 	entry_t *ent, *prev;
2444662Sszhou 
2445662Sszhou 	ent = s_calloc(1, sizeof (entry_t));
2446662Sszhou 	ent->start = start;
2447662Sszhou 	ent->end = end;
2448662Sszhou 
2449662Sszhou 	if (mp->entries == NULL) {
2450662Sszhou 		mp->entries = ent;
2451662Sszhou 		return (ent);
2452662Sszhou 	}
2453662Sszhou 
2454662Sszhou 	prev = mp->entries;
2455662Sszhou 	while (prev->next)
2456662Sszhou 		prev = prev-> next;
2457662Sszhou 	prev->next = ent;
2458662Sszhou 	ent->prev = prev;
2459662Sszhou 	return (ent);
2460662Sszhou }
2461662Sszhou 
2462662Sszhou static void
2463662Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
2464662Sszhou {
2465662Sszhou 	if (ent)
2466662Sszhou 		ent->end = lp;
2467662Sszhou }
2468662Sszhou 
24690Sstevel@tonic-gate /*
24705084Sjohnlev  * Check whether cmd matches the one indexed by which, and whether arg matches
24715084Sjohnlev  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
24725084Sjohnlev  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
24735084Sjohnlev  * strstr(), so it can be a partial match.
24745084Sjohnlev  */
24755084Sjohnlev static int
24765084Sjohnlev check_cmd(const char *cmd, const int which, const char *arg, const char *str)
24775084Sjohnlev {
24785084Sjohnlev 	if ((strcmp(cmd, menu_cmds[which]) != 0) &&
24795084Sjohnlev 	    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
24805084Sjohnlev 		return (0);
24815084Sjohnlev 	}
24825084Sjohnlev 	return (strstr(arg, str) != NULL);
24835084Sjohnlev }
24845084Sjohnlev 
24855084Sjohnlev /*
24860Sstevel@tonic-gate  * A line in menu.lst looks like
24870Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
24880Sstevel@tonic-gate  */
24890Sstevel@tonic-gate static void
24900Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
24910Sstevel@tonic-gate {
24920Sstevel@tonic-gate 	/*
24930Sstevel@tonic-gate 	 * save state across calls. This is so that
24940Sstevel@tonic-gate 	 * header gets the right entry# after title has
24950Sstevel@tonic-gate 	 * been processed
24960Sstevel@tonic-gate 	 */
2497662Sszhou 	static line_t *prev = NULL;
2498662Sszhou 	static entry_t *curr_ent = NULL;
24993446Smrj 	static int in_liveupgrade = 0;
25000Sstevel@tonic-gate 
25010Sstevel@tonic-gate 	line_t	*lp;
25020Sstevel@tonic-gate 	char *cmd, *sep, *arg;
25030Sstevel@tonic-gate 	char save, *cp, *line;
25040Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
25050Sstevel@tonic-gate 
25060Sstevel@tonic-gate 	if (str == NULL) {
25070Sstevel@tonic-gate 		return;
25080Sstevel@tonic-gate 	}
25090Sstevel@tonic-gate 
25100Sstevel@tonic-gate 	/*
25110Sstevel@tonic-gate 	 * First save a copy of the entire line.
25120Sstevel@tonic-gate 	 * We use this later to set the line field.
25130Sstevel@tonic-gate 	 */
25140Sstevel@tonic-gate 	line = s_strdup(str);
25150Sstevel@tonic-gate 
25160Sstevel@tonic-gate 	/* Eat up leading whitespace */
25170Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
25180Sstevel@tonic-gate 		str++;
25190Sstevel@tonic-gate 
25200Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
25210Sstevel@tonic-gate 		cmd = s_strdup("#");
25220Sstevel@tonic-gate 		sep = NULL;
25230Sstevel@tonic-gate 		arg = s_strdup(str + 1);
25240Sstevel@tonic-gate 		flag = BAM_COMMENT;
25253446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
25263446Smrj 			in_liveupgrade = 1;
25273446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
25283446Smrj 			in_liveupgrade = 0;
25293446Smrj 		}
25300Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
25310Sstevel@tonic-gate 		cmd = sep = arg = NULL;
25320Sstevel@tonic-gate 		flag = BAM_EMPTY;
25330Sstevel@tonic-gate 	} else {
25340Sstevel@tonic-gate 		/*
25350Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
25360Sstevel@tonic-gate 		 * However various development bits use '=' as a
25370Sstevel@tonic-gate 		 * separator. In addition, external users also
25380Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
25390Sstevel@tonic-gate 		 */
25400Sstevel@tonic-gate 		cp = str;
25410Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
25420Sstevel@tonic-gate 			if (*str == '\0') {
25430Sstevel@tonic-gate 				cmd = s_strdup(cp);
25440Sstevel@tonic-gate 				sep = arg = NULL;
25450Sstevel@tonic-gate 				break;
25460Sstevel@tonic-gate 			}
25470Sstevel@tonic-gate 			str++;
25480Sstevel@tonic-gate 		}
25490Sstevel@tonic-gate 
25500Sstevel@tonic-gate 		if (*str != '\0') {
25510Sstevel@tonic-gate 			save = *str;
25520Sstevel@tonic-gate 			*str = '\0';
25530Sstevel@tonic-gate 			cmd = s_strdup(cp);
25540Sstevel@tonic-gate 			*str = save;
25550Sstevel@tonic-gate 
25560Sstevel@tonic-gate 			str++;
25570Sstevel@tonic-gate 			save = *str;
25580Sstevel@tonic-gate 			*str = '\0';
25590Sstevel@tonic-gate 			sep = s_strdup(str - 1);
25600Sstevel@tonic-gate 			*str = save;
25610Sstevel@tonic-gate 
25620Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
25630Sstevel@tonic-gate 				str++;
25640Sstevel@tonic-gate 			if (*str == '\0')
25650Sstevel@tonic-gate 				arg = NULL;
25660Sstevel@tonic-gate 			else
25670Sstevel@tonic-gate 				arg = s_strdup(str);
25680Sstevel@tonic-gate 		}
25690Sstevel@tonic-gate 	}
25700Sstevel@tonic-gate 
25710Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
25720Sstevel@tonic-gate 
25730Sstevel@tonic-gate 	lp->cmd = cmd;
25740Sstevel@tonic-gate 	lp->sep = sep;
25750Sstevel@tonic-gate 	lp->arg = arg;
25760Sstevel@tonic-gate 	lp->line = line;
25770Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
25780Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
25790Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
25800Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
25810Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
25823446Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
25830Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
2584662Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
25853446Smrj 			curr_ent->flags = BAM_ENTRY_BOOTADM;
2586662Sszhou 		} else {
2587662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
25883446Smrj 			if (in_liveupgrade) {
25893446Smrj 				curr_ent->flags = BAM_ENTRY_LU;
25903446Smrj 			}
2591662Sszhou 		}
25923446Smrj 		curr_ent->entryNum = *entryNum;
25930Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
25940Sstevel@tonic-gate 		/*
25950Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
25960Sstevel@tonic-gate 		 * by the subsequent title
25970Sstevel@tonic-gate 		 */
25980Sstevel@tonic-gate 		lp->entryNum = *entryNum;
25990Sstevel@tonic-gate 		lp->flags = flag;
26000Sstevel@tonic-gate 	} else {
26010Sstevel@tonic-gate 		lp->entryNum = *entryNum;
26023446Smrj 
26033446Smrj 		if (*entryNum == ENTRY_INIT) {
26043446Smrj 			lp->flags = BAM_GLOBAL;
26053446Smrj 		} else {
26063446Smrj 			lp->flags = BAM_ENTRY;
26073446Smrj 
26083446Smrj 			if (cmd && arg) {
26093446Smrj 				/*
26103446Smrj 				 * We only compare for the length of "module"
26113446Smrj 				 * so that "module$" will also match.
26123446Smrj 				 */
26135084Sjohnlev 				if (check_cmd(cmd, MODULE_CMD, arg, MINIROOT))
26143446Smrj 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
26155084Sjohnlev 				else if (check_cmd(cmd, KERNEL_CMD, arg,
26165084Sjohnlev 				    "xen.gz"))
26175084Sjohnlev 					curr_ent->flags |= BAM_ENTRY_HV;
26183446Smrj 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
26193446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
26203446Smrj 				else if (strcmp(cmd,
26213446Smrj 				    menu_cmds[CHAINLOADER_CMD]) == 0)
26223446Smrj 					curr_ent->flags |=
26233446Smrj 					    BAM_ENTRY_CHAINLOADER;
26243446Smrj 			}
26253446Smrj 		}
26260Sstevel@tonic-gate 	}
26270Sstevel@tonic-gate 
2628662Sszhou 	/* record default, old default, and entry line ranges */
2629662Sszhou 	if (lp->flags == BAM_GLOBAL &&
2630662Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
2631662Sszhou 		mp->curdefault = lp;
2632662Sszhou 	} else if (lp->flags == BAM_COMMENT &&
2633662Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
2634662Sszhou 		mp->olddefault = lp;
26353446Smrj 	} else if (lp->flags == BAM_COMMENT &&
26363446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
26373446Smrj 		mp->old_rc_default = lp;
2638662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
26393446Smrj 	    (lp->flags == BAM_COMMENT &&
26403446Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
2641662Sszhou 		boot_entry_addline(curr_ent, lp);
2642662Sszhou 	}
26430Sstevel@tonic-gate 	append_line(mp, lp);
26440Sstevel@tonic-gate 
26450Sstevel@tonic-gate 	prev = lp;
26460Sstevel@tonic-gate }
26470Sstevel@tonic-gate 
2648621Svikram static void
2649621Svikram update_numbering(menu_t *mp)
2650621Svikram {
2651621Svikram 	int lineNum;
2652621Svikram 	int entryNum;
2653621Svikram 	int old_default_value;
2654621Svikram 	line_t *lp, *prev, *default_lp, *default_entry;
2655621Svikram 	char buf[PATH_MAX];
2656621Svikram 
2657621Svikram 	if (mp->start == NULL) {
2658621Svikram 		return;
2659621Svikram 	}
2660621Svikram 
2661621Svikram 	lineNum = LINE_INIT;
2662621Svikram 	entryNum = ENTRY_INIT;
2663621Svikram 	old_default_value = ENTRY_INIT;
2664621Svikram 	lp = default_lp = default_entry = NULL;
2665621Svikram 
2666621Svikram 	prev = NULL;
2667621Svikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
2668621Svikram 		lp->lineNum = ++lineNum;
2669621Svikram 
2670621Svikram 		/*
2671621Svikram 		 * Get the value of the default command
2672621Svikram 		 */
2673621Svikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
2674621Svikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
2675621Svikram 		    lp->arg) {
2676621Svikram 			old_default_value = atoi(lp->arg);
2677621Svikram 			default_lp = lp;
2678621Svikram 		}
2679621Svikram 
2680621Svikram 		/*
2681621Svikram 		 * If not boot entry, nothing else to fix for this
2682621Svikram 		 * entry
2683621Svikram 		 */
2684621Svikram 		if (lp->entryNum == ENTRY_INIT)
2685621Svikram 			continue;
2686621Svikram 
2687621Svikram 		/*
2688621Svikram 		 * Record the position of the default entry.
2689621Svikram 		 * The following works because global
2690621Svikram 		 * commands like default and timeout should precede
2691621Svikram 		 * actual boot entries, so old_default_value
2692621Svikram 		 * is already known (or default cmd is missing).
2693621Svikram 		 */
2694621Svikram 		if (default_entry == NULL &&
2695621Svikram 		    old_default_value != ENTRY_INIT &&
2696621Svikram 		    lp->entryNum == old_default_value) {
2697621Svikram 			default_entry = lp;
2698621Svikram 		}
2699621Svikram 
2700621Svikram 		/*
2701621Svikram 		 * Now fixup the entry number
2702621Svikram 		 */
2703621Svikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
2704621Svikram 			lp->entryNum = ++entryNum;
2705621Svikram 			/* fixup the bootadm header */
2706621Svikram 			if (prev && prev->flags == BAM_COMMENT &&
27073446Smrj 			    prev->arg &&
27083446Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2709621Svikram 				prev->entryNum = lp->entryNum;
2710621Svikram 			}
2711621Svikram 		} else {
2712621Svikram 			lp->entryNum = entryNum;
2713621Svikram 		}
2714621Svikram 	}
2715621Svikram 
2716621Svikram 	/*
2717621Svikram 	 * No default command in menu, simply return
2718621Svikram 	 */
2719621Svikram 	if (default_lp == NULL) {
2720621Svikram 		return;
2721621Svikram 	}
2722621Svikram 
2723621Svikram 	free(default_lp->arg);
2724621Svikram 	free(default_lp->line);
2725621Svikram 
2726621Svikram 	if (default_entry == NULL) {
2727621Svikram 		default_lp->arg = s_strdup("0");
2728621Svikram 	} else {
2729621Svikram 		(void) snprintf(buf, sizeof (buf), "%d",
2730621Svikram 		    default_entry->entryNum);
2731621Svikram 		default_lp->arg = s_strdup(buf);
2732621Svikram 	}
2733621Svikram 
2734621Svikram 	/*
2735621Svikram 	 * The following is required since only the line field gets
2736621Svikram 	 * written back to menu.lst
2737621Svikram 	 */
2738621Svikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
2739621Svikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
2740621Svikram 	default_lp->line = s_strdup(buf);
2741621Svikram }
2742621Svikram 
2743621Svikram 
27440Sstevel@tonic-gate static menu_t *
27450Sstevel@tonic-gate menu_read(char *menu_path)
27460Sstevel@tonic-gate {
27470Sstevel@tonic-gate 	FILE *fp;
27480Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
27490Sstevel@tonic-gate 	menu_t *mp;
27500Sstevel@tonic-gate 	int line, entry, len, n;
27510Sstevel@tonic-gate 
27520Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
27530Sstevel@tonic-gate 
27540Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
27550Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
27560Sstevel@tonic-gate 		return (mp);
27570Sstevel@tonic-gate 	}
27580Sstevel@tonic-gate 
27590Sstevel@tonic-gate 
27600Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
27610Sstevel@tonic-gate 	line = LINE_INIT;
27620Sstevel@tonic-gate 	entry = ENTRY_INIT;
27630Sstevel@tonic-gate 	cp = buf;
27640Sstevel@tonic-gate 	len = sizeof (buf);
27650Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
27660Sstevel@tonic-gate 		n = strlen(cp);
27670Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
27680Sstevel@tonic-gate 			len -= n - 1;
27690Sstevel@tonic-gate 			assert(len >= 2);
27700Sstevel@tonic-gate 			cp += n - 1;
27710Sstevel@tonic-gate 			continue;
27720Sstevel@tonic-gate 		}
27730Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
27740Sstevel@tonic-gate 		cp = buf;
27750Sstevel@tonic-gate 		len = sizeof (buf);
27760Sstevel@tonic-gate 	}
27770Sstevel@tonic-gate 
27780Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
27790Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
27800Sstevel@tonic-gate 	}
27810Sstevel@tonic-gate 
27820Sstevel@tonic-gate 	return (mp);
27830Sstevel@tonic-gate }
27840Sstevel@tonic-gate 
27850Sstevel@tonic-gate static error_t
27860Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
27870Sstevel@tonic-gate {
27880Sstevel@tonic-gate 	char *eq;
27890Sstevel@tonic-gate 	char *opt_dup;
27900Sstevel@tonic-gate 	int entryNum;
27910Sstevel@tonic-gate 
27920Sstevel@tonic-gate 	assert(mp);
27930Sstevel@tonic-gate 	assert(mp->start);
27940Sstevel@tonic-gate 	assert(opt);
27950Sstevel@tonic-gate 
27960Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
27970Sstevel@tonic-gate 
27980Sstevel@tonic-gate 	if (entry)
27990Sstevel@tonic-gate 		*entry = ENTRY_INIT;
28000Sstevel@tonic-gate 	if (title)
28010Sstevel@tonic-gate 		*title = NULL;
28020Sstevel@tonic-gate 
28030Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
28040Sstevel@tonic-gate 	if (eq == NULL) {
28050Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
28060Sstevel@tonic-gate 		free(opt_dup);
28070Sstevel@tonic-gate 		return (BAM_ERROR);
28080Sstevel@tonic-gate 	}
28090Sstevel@tonic-gate 
28100Sstevel@tonic-gate 	*eq = '\0';
28110Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
28120Sstevel@tonic-gate 		assert(mp->end);
28130Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
28140Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
28150Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
28160Sstevel@tonic-gate 			free(opt_dup);
28170Sstevel@tonic-gate 			return (BAM_ERROR);
28180Sstevel@tonic-gate 		}
28190Sstevel@tonic-gate 		*entry = entryNum;
28200Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
28210Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
28220Sstevel@tonic-gate 	} else {
28230Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
28240Sstevel@tonic-gate 		free(opt_dup);
28250Sstevel@tonic-gate 		return (BAM_ERROR);
28260Sstevel@tonic-gate 	}
28270Sstevel@tonic-gate 
28280Sstevel@tonic-gate 	free(opt_dup);
28290Sstevel@tonic-gate 	return (BAM_SUCCESS);
28300Sstevel@tonic-gate }
28310Sstevel@tonic-gate 
28320Sstevel@tonic-gate /*
28330Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
28340Sstevel@tonic-gate  * only title lines in file are printed.
28350Sstevel@tonic-gate  *
28360Sstevel@tonic-gate  * If invoked with a title or entry #, all
28370Sstevel@tonic-gate  * lines in *every* matching entry are listed
28380Sstevel@tonic-gate  */
28390Sstevel@tonic-gate static error_t
28400Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
28410Sstevel@tonic-gate {
28420Sstevel@tonic-gate 	line_t *lp;
28430Sstevel@tonic-gate 	int entry = ENTRY_INIT;
28440Sstevel@tonic-gate 	int found;
28450Sstevel@tonic-gate 	char *title = NULL;
28460Sstevel@tonic-gate 
28470Sstevel@tonic-gate 	assert(mp);
28480Sstevel@tonic-gate 	assert(menu_path);
28490Sstevel@tonic-gate 
28500Sstevel@tonic-gate 	if (mp->start == NULL) {
28510Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
28520Sstevel@tonic-gate 		return (BAM_ERROR);
28530Sstevel@tonic-gate 	}
28540Sstevel@tonic-gate 
28550Sstevel@tonic-gate 	if (opt != NULL) {
28560Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
28570Sstevel@tonic-gate 			return (BAM_ERROR);
28580Sstevel@tonic-gate 		}
28590Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
28600Sstevel@tonic-gate 	} else {
28610Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
28620Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
28630Sstevel@tonic-gate 	}
28640Sstevel@tonic-gate 
28650Sstevel@tonic-gate 	found = 0;
28660Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
28670Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
28680Sstevel@tonic-gate 			continue;
28690Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
28700Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
28710Sstevel@tonic-gate 			    lp->arg);
28720Sstevel@tonic-gate 			found = 1;
28730Sstevel@tonic-gate 			continue;
28740Sstevel@tonic-gate 		}
28750Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
28760Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
28770Sstevel@tonic-gate 			found = 1;
28780Sstevel@tonic-gate 			continue;
28790Sstevel@tonic-gate 		}
28800Sstevel@tonic-gate 
28810Sstevel@tonic-gate 		/*
28820Sstevel@tonic-gate 		 * We set the entry value here so that all lines
28830Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
28840Sstevel@tonic-gate 		 * title in other entries, all lines in those
28850Sstevel@tonic-gate 		 * entries get printed as well.
28860Sstevel@tonic-gate 		 */
28870Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
28880Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
28890Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
28900Sstevel@tonic-gate 			entry = lp->entryNum;
28910Sstevel@tonic-gate 			found = 1;
28920Sstevel@tonic-gate 			continue;
28930Sstevel@tonic-gate 		}
28940Sstevel@tonic-gate 	}
28950Sstevel@tonic-gate 
28960Sstevel@tonic-gate 	if (!found) {
28970Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
28980Sstevel@tonic-gate 		return (BAM_ERROR);
28990Sstevel@tonic-gate 	}
29000Sstevel@tonic-gate 
29010Sstevel@tonic-gate 	return (BAM_SUCCESS);
29020Sstevel@tonic-gate }
29030Sstevel@tonic-gate 
29045084Sjohnlev int
29050Sstevel@tonic-gate add_boot_entry(menu_t *mp,
29060Sstevel@tonic-gate 	char *title,
29070Sstevel@tonic-gate 	char *root,
29080Sstevel@tonic-gate 	char *kernel,
29095084Sjohnlev 	char *mod_kernel,
29100Sstevel@tonic-gate 	char *module)
29110Sstevel@tonic-gate {
29120Sstevel@tonic-gate 	int lineNum, entryNum;
29130Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
29143446Smrj 	menu_cmd_t k_cmd, m_cmd;
29150Sstevel@tonic-gate 
29160Sstevel@tonic-gate 	assert(mp);
29170Sstevel@tonic-gate 
29180Sstevel@tonic-gate 	if (title == NULL) {
2919656Sszhou 		title = "Solaris";	/* default to Solaris */
29200Sstevel@tonic-gate 	}
29210Sstevel@tonic-gate 	if (kernel == NULL) {
29220Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
29230Sstevel@tonic-gate 		return (BAM_ERROR);
29240Sstevel@tonic-gate 	}
29250Sstevel@tonic-gate 	if (module == NULL) {
29263446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
29273446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
29283446Smrj 			return (BAM_ERROR);
29293446Smrj 		}
29303446Smrj 
29313446Smrj 		/* Figure the commands out from the kernel line */
29323446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
29333446Smrj 			module = DIRECT_BOOT_ARCHIVE;
29343446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
29353446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
29363446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
29373446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
29383446Smrj 			k_cmd = KERNEL_CMD;
29393446Smrj 			m_cmd = MODULE_CMD;
29403446Smrj 		} else {
29413446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
29423446Smrj 			k_cmd = KERNEL_CMD;
29433446Smrj 			m_cmd = MODULE_CMD;
29443446Smrj 		}
29453446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
29463446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
29473446Smrj 		/*
29483446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
29493446Smrj 		 * command.  Otherwise, use "kernel".
29503446Smrj 		 */
29513446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
29523446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
29533446Smrj 	} else {
29543446Smrj 		k_cmd = KERNEL_CMD;
29553446Smrj 		m_cmd = MODULE_CMD;
29560Sstevel@tonic-gate 	}
29570Sstevel@tonic-gate 
29580Sstevel@tonic-gate 	if (mp->start) {
29590Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
29600Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
29610Sstevel@tonic-gate 	} else {
29620Sstevel@tonic-gate 		lineNum = LINE_INIT;
29630Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
29640Sstevel@tonic-gate 	}
29650Sstevel@tonic-gate 
29660Sstevel@tonic-gate 	/*
29670Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
29680Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
29690Sstevel@tonic-gate 	 */
29700Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
29713446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
2972662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29730Sstevel@tonic-gate 
29740Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29750Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
2976662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
2977662Sszhou 
2978662Sszhou 	if (root) {
2979662Sszhou 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2980662Sszhou 		    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
2981662Sszhou 		line_parser(mp, linebuf, &lineNum, &entryNum);
29820Sstevel@tonic-gate 	}
29830Sstevel@tonic-gate 
29840Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29853446Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
2986662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29870Sstevel@tonic-gate 
29885084Sjohnlev 	if (mod_kernel != NULL) {
29895084Sjohnlev 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29905084Sjohnlev 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
29915084Sjohnlev 		line_parser(mp, linebuf, &lineNum, &entryNum);
29925084Sjohnlev 	}
29935084Sjohnlev 
29940Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29953446Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
2996662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29970Sstevel@tonic-gate 
29980Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
29993446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
3000662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
30010Sstevel@tonic-gate 
30020Sstevel@tonic-gate 	return (entryNum);
30030Sstevel@tonic-gate }
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate static error_t
30060Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
30070Sstevel@tonic-gate {
3008662Sszhou 	line_t *lp, *freed;
3009662Sszhou 	entry_t *ent, *tmp;
30100Sstevel@tonic-gate 	int deleted;
30110Sstevel@tonic-gate 
30120Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
30130Sstevel@tonic-gate 
3014662Sszhou 	ent = mp->entries;
3015662Sszhou 	while (ent) {
3016662Sszhou 		lp = ent->start;
3017662Sszhou 		/* check entry number and make sure it's a bootadm entry */
3018662Sszhou 		if (lp->flags != BAM_COMMENT ||
30193446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
3020662Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
3021662Sszhou 			ent = ent->next;
30220Sstevel@tonic-gate 			continue;
30230Sstevel@tonic-gate 		}
30240Sstevel@tonic-gate 
3025662Sszhou 		/* free the entry content */
3026662Sszhou 		do {
3027662Sszhou 			freed = lp;
3028662Sszhou 			lp = lp->next;	/* prev stays the same */
3029662Sszhou 			unlink_line(mp, freed);
3030662Sszhou 			line_free(freed);
3031662Sszhou 		} while (freed != ent->end);
3032662Sszhou 
3033662Sszhou 		/* free the entry_t structure */
3034662Sszhou 		tmp = ent;
3035662Sszhou 		ent = ent->next;
3036662Sszhou 		if (tmp->prev)
3037662Sszhou 			tmp->prev->next = ent;
30380Sstevel@tonic-gate 		else
3039662Sszhou 			mp->entries = ent;
3040662Sszhou 		if (ent)
3041662Sszhou 			ent->prev = tmp->prev;
30420Sstevel@tonic-gate 		deleted = 1;
30430Sstevel@tonic-gate 	}
30440Sstevel@tonic-gate 
30450Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
30460Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
30470Sstevel@tonic-gate 		return (BAM_ERROR);
30480Sstevel@tonic-gate 	}
30490Sstevel@tonic-gate 
3050621Svikram 	/*
3051621Svikram 	 * Now that we have deleted an entry, update
3052621Svikram 	 * the entry numbering and the default cmd.
3053621Svikram 	 */
3054621Svikram 	update_numbering(mp);
3055621Svikram 
30560Sstevel@tonic-gate 	return (BAM_SUCCESS);
30570Sstevel@tonic-gate }
30580Sstevel@tonic-gate 
30590Sstevel@tonic-gate static error_t
30600Sstevel@tonic-gate delete_all_entries(menu_t *mp, char *menu_path, char *opt)
30610Sstevel@tonic-gate {
30620Sstevel@tonic-gate 	assert(mp);
30630Sstevel@tonic-gate 	assert(opt == NULL);
30640Sstevel@tonic-gate 
30650Sstevel@tonic-gate 	if (mp->start == NULL) {
30660Sstevel@tonic-gate 		bam_print(EMPTY_FILE, menu_path);
30670Sstevel@tonic-gate 		return (BAM_SUCCESS);
30680Sstevel@tonic-gate 	}
30690Sstevel@tonic-gate 
30700Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
30710Sstevel@tonic-gate 		return (BAM_ERROR);
30720Sstevel@tonic-gate 	}
30730Sstevel@tonic-gate 
30740Sstevel@tonic-gate 	return (BAM_WRITE);
30750Sstevel@tonic-gate }
30760Sstevel@tonic-gate 
30770Sstevel@tonic-gate static FILE *
3078662Sszhou open_diskmap(char *root)
30790Sstevel@tonic-gate {
30800Sstevel@tonic-gate 	FILE *fp;
30810Sstevel@tonic-gate 	char cmd[PATH_MAX];
30820Sstevel@tonic-gate 
30830Sstevel@tonic-gate 	/* make sure we have a map file */
30840Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
30850Sstevel@tonic-gate 	if (fp == NULL) {
30860Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
30875648Ssetje 		    "%s/%s > /dev/null", root, CREATE_DISKMAP);
30880Sstevel@tonic-gate 		(void) system(cmd);
30890Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
30900Sstevel@tonic-gate 	}
30910Sstevel@tonic-gate 	return (fp);
30920Sstevel@tonic-gate }
30930Sstevel@tonic-gate 
30940Sstevel@tonic-gate #define	SECTOR_SIZE	512
30950Sstevel@tonic-gate 
30960Sstevel@tonic-gate static int
30970Sstevel@tonic-gate get_partition(char *device)
30980Sstevel@tonic-gate {
30990Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
31000Sstevel@tonic-gate 	struct mboot *mboot;
31010Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
31020Sstevel@tonic-gate 	char *wholedisk, *slice;
31030Sstevel@tonic-gate 
31040Sstevel@tonic-gate 	/* form whole disk (p0) */
31050Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
31060Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
31070Sstevel@tonic-gate 	if (!is_pcfs)
31080Sstevel@tonic-gate 		*slice = '\0';
31090Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
31100Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
31110Sstevel@tonic-gate 	if (!is_pcfs)
31120Sstevel@tonic-gate 		*slice = 's';
31130Sstevel@tonic-gate 
31140Sstevel@tonic-gate 	/* read boot sector */
31150Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
31160Sstevel@tonic-gate 	free(wholedisk);
31170Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
31180Sstevel@tonic-gate 		return (partno);
31190Sstevel@tonic-gate 	}
31200Sstevel@tonic-gate 	(void) close(fd);
31210Sstevel@tonic-gate 
31220Sstevel@tonic-gate 	/* parse fdisk table */
31230Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
31240Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
31250Sstevel@tonic-gate 		struct ipart *part =
31260Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
31270Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
31280Sstevel@tonic-gate 			if (part->systid == 0xbe) {
31290Sstevel@tonic-gate 				partno = i;
31300Sstevel@tonic-gate 				break;
31310Sstevel@tonic-gate 			}
31320Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
31330Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
31340Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
31350Sstevel@tonic-gate 				partno = i;
31360Sstevel@tonic-gate 				break;
31370Sstevel@tonic-gate 			}
31380Sstevel@tonic-gate 		}
31390Sstevel@tonic-gate 	}
31400Sstevel@tonic-gate 	return (partno);
31410Sstevel@tonic-gate }
31420Sstevel@tonic-gate 
31430Sstevel@tonic-gate static char *
31440Sstevel@tonic-gate get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
31450Sstevel@tonic-gate {
31460Sstevel@tonic-gate 	char *grubdisk;	/* (hd#,#,#) */
31470Sstevel@tonic-gate 	char *slice;
31480Sstevel@tonic-gate 	char *grubhd;
31490Sstevel@tonic-gate 	int fdiskpart;
31500Sstevel@tonic-gate 	int found = 0;
31510Sstevel@tonic-gate 	char *devname, *ctdname = strstr(rootdev, "dsk/");
31520Sstevel@tonic-gate 	char linebuf[PATH_MAX];
31530Sstevel@tonic-gate 
31540Sstevel@tonic-gate 	if (ctdname == NULL)
31550Sstevel@tonic-gate 		return (NULL);
31560Sstevel@tonic-gate 
31570Sstevel@tonic-gate 	ctdname += strlen("dsk/");
31580Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
31590Sstevel@tonic-gate 	if (slice)
31600Sstevel@tonic-gate 		*slice = '\0';
31610Sstevel@tonic-gate 
31620Sstevel@tonic-gate 	rewind(fp);
31630Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
31640Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
31650Sstevel@tonic-gate 		if (grubhd)
31660Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
31670Sstevel@tonic-gate 		else
31680Sstevel@tonic-gate 			devname = NULL;
31690Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
31700Sstevel@tonic-gate 			found = 1;
31710Sstevel@tonic-gate 			break;
31720Sstevel@tonic-gate 		}
31730Sstevel@tonic-gate 	}
31740Sstevel@tonic-gate 
31750Sstevel@tonic-gate 	if (slice)
31760Sstevel@tonic-gate 		*slice = 's';
31770Sstevel@tonic-gate 
31780Sstevel@tonic-gate 	if (found == 0) {
31790Sstevel@tonic-gate 		if (bam_verbose)
31800Sstevel@tonic-gate 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
31810Sstevel@tonic-gate 		grubhd = "0";	/* assume disk 0 if can't match */
31820Sstevel@tonic-gate 	}
31830Sstevel@tonic-gate 
31840Sstevel@tonic-gate 	fdiskpart = get_partition(rootdev);
31850Sstevel@tonic-gate 	if (fdiskpart == -1)
31860Sstevel@tonic-gate 		return (NULL);
31870Sstevel@tonic-gate 
31880Sstevel@tonic-gate 	grubdisk = s_calloc(1, 10);
31890Sstevel@tonic-gate 	if (slice) {
31900Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
31910Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
31920Sstevel@tonic-gate 	} else
31930Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
31940Sstevel@tonic-gate 		    grubhd, fdiskpart);
31950Sstevel@tonic-gate 
31960Sstevel@tonic-gate 	/* if root not on bootdev, change GRUB disk to 0 */
31970Sstevel@tonic-gate 	if (!on_bootdev)
31980Sstevel@tonic-gate 		grubdisk[3] = '0';
31990Sstevel@tonic-gate 	return (grubdisk);
32000Sstevel@tonic-gate }
32010Sstevel@tonic-gate 
3202656Sszhou static char *
3203656Sszhou get_title(char *rootdir)
32040Sstevel@tonic-gate {
32050Sstevel@tonic-gate 	static char title[80];	/* from /etc/release */
3206662Sszhou 	char *cp = NULL, release[PATH_MAX];
32070Sstevel@tonic-gate 	FILE *fp;
32080Sstevel@tonic-gate 
32090Sstevel@tonic-gate 	/* open the /etc/release file */
32100Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
32110Sstevel@tonic-gate 
32120Sstevel@tonic-gate 	fp = fopen(release, "r");
32130Sstevel@tonic-gate 	if (fp == NULL)
3214656Sszhou 		return (NULL);
32150Sstevel@tonic-gate 
32160Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
32170Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
32180Sstevel@tonic-gate 		if (cp)
32190Sstevel@tonic-gate 			break;
32200Sstevel@tonic-gate 	}
32210Sstevel@tonic-gate 	(void) fclose(fp);
3222662Sszhou 	return (cp == NULL ? "Solaris" : cp);
32230Sstevel@tonic-gate }
32240Sstevel@tonic-gate 
32253446Smrj char *
32260Sstevel@tonic-gate get_special(char *mountp)
32270Sstevel@tonic-gate {
32280Sstevel@tonic-gate 	FILE *mntfp;
32290Sstevel@tonic-gate 	struct mnttab mp = {0}, mpref = {0};
32300Sstevel@tonic-gate 
32310Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
32320Sstevel@tonic-gate 	if (mntfp == NULL) {
32330Sstevel@tonic-gate 		return (0);
32340Sstevel@tonic-gate 	}
32350Sstevel@tonic-gate 
32360Sstevel@tonic-gate 	if (*mountp == '\0')
32370Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
32380Sstevel@tonic-gate 	else
32390Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
32400Sstevel@tonic-gate 	if (getmntany(mntfp, &mp, &mpref) != 0) {
32410Sstevel@tonic-gate 		(void) fclose(mntfp);
32420Sstevel@tonic-gate 		return (NULL);
32430Sstevel@tonic-gate 	}
32440Sstevel@tonic-gate 	(void) fclose(mntfp);
32450Sstevel@tonic-gate 
32460Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
32470Sstevel@tonic-gate }
32480Sstevel@tonic-gate 
32493446Smrj char *
32500Sstevel@tonic-gate os_to_grubdisk(char *osdisk, int on_bootdev)
32510Sstevel@tonic-gate {
32520Sstevel@tonic-gate 	FILE *fp;
32530Sstevel@tonic-gate 	char *grubdisk;
32540Sstevel@tonic-gate 
32550Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3256662Sszhou 	fp = open_diskmap("");
32570Sstevel@tonic-gate 	if (fp == NULL) {
32580Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdisk);
32590Sstevel@tonic-gate 		return (NULL);
32600Sstevel@tonic-gate 	}
32610Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
32620Sstevel@tonic-gate 	(void) fclose(fp);
32630Sstevel@tonic-gate 	return (grubdisk);
32640Sstevel@tonic-gate }
32650Sstevel@tonic-gate 
32660Sstevel@tonic-gate /*
32670Sstevel@tonic-gate  * Check if root is on the boot device
32680Sstevel@tonic-gate  * Return 0 (false) on error
32690Sstevel@tonic-gate  */
32700Sstevel@tonic-gate static int
32710Sstevel@tonic-gate menu_on_bootdev(char *menu_root, FILE *fp)
32720Sstevel@tonic-gate {
32730Sstevel@tonic-gate 	int ret;
32740Sstevel@tonic-gate 	char *grubhd, *bootp, *special;
32750Sstevel@tonic-gate 
32760Sstevel@tonic-gate 	special = get_special(menu_root);
32770Sstevel@tonic-gate 	if (special == NULL)
32780Sstevel@tonic-gate 		return (0);
32790Sstevel@tonic-gate 	bootp = strstr(special, "p0:boot");
32800Sstevel@tonic-gate 	if (bootp)
32810Sstevel@tonic-gate 		*bootp = '\0';
32820Sstevel@tonic-gate 	grubhd = get_grubdisk(special, fp, 1);
32830Sstevel@tonic-gate 	free(special);
32840Sstevel@tonic-gate 
32850Sstevel@tonic-gate 	if (grubhd == NULL)
32860Sstevel@tonic-gate 		return (0);
32870Sstevel@tonic-gate 	ret = grubhd[3] == '0';
32880Sstevel@tonic-gate 	free(grubhd);
32890Sstevel@tonic-gate 	return (ret);
32900Sstevel@tonic-gate }
32910Sstevel@tonic-gate 
3292662Sszhou /*
3293662Sszhou  * look for matching bootadm entry with specified parameters
3294662Sszhou  * Here are the rules (based on existing usage):
3295662Sszhou  * - If title is specified, match on title only
32965084Sjohnlev  * - Else, match on kernel, grubdisk and module.  Note that, if root_opt is
32975084Sjohnlev  *   non-zero, the absence of root line is considered a match.
3298662Sszhou  */
3299662Sszhou static entry_t *
33005084Sjohnlev find_boot_entry(menu_t *mp, char *title, char *kernel, char *root,
33015084Sjohnlev     char *module, int root_opt, int *entry_num)
3302662Sszhou {
3303662Sszhou 	int i;
3304662Sszhou 	line_t *lp;
3305662Sszhou 	entry_t *ent;
3306662Sszhou 
3307662Sszhou 	/* find matching entry */
3308662Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
3309662Sszhou 		lp = ent->start;
3310662Sszhou 
3311662Sszhou 		/* first line of entry must be bootadm comment */
3312662Sszhou 		lp = ent->start;
33133446Smrj 		if (lp->flags != BAM_COMMENT ||
33143446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
3315662Sszhou 			continue;
3316662Sszhou 		}
3317662Sszhou 
3318662Sszhou 		/* advance to title line */
3319662Sszhou 		lp = lp->next;
3320662Sszhou 		if (title) {
3321662Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
3322662Sszhou 			    strcmp(lp->arg, title) == 0)
3323662Sszhou 				break;
3324662Sszhou 			continue;	/* check title only */
3325662Sszhou 		}
3326662Sszhou 
3327662Sszhou 		lp = lp->next;	/* advance to root line */
33285084Sjohnlev 		if (lp == NULL) {
33295084Sjohnlev 			continue;
33305084Sjohnlev 		} else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3331662Sszhou 			/* root command found, match grub disk */
3332662Sszhou 			if (strcmp(lp->arg, root) != 0) {
3333662Sszhou 				continue;
3334662Sszhou 			}
3335662Sszhou 			lp = lp->next;	/* advance to kernel line */
3336662Sszhou 		} else {
3337662Sszhou 			/* no root command, see if root is optional */
3338662Sszhou 			if (root_opt == 0) {
3339662Sszhou 				continue;
3340662Sszhou 			}
3341662Sszhou 		}
3342662Sszhou 
3343662Sszhou 		if (lp == NULL || lp->next == NULL) {
3344662Sszhou 			continue;
3345662Sszhou 		}
3346662Sszhou 
33475084Sjohnlev 		if (kernel &&
33485084Sjohnlev 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
33495084Sjohnlev 			continue;
33505084Sjohnlev 		}
33515084Sjohnlev 
33523467Srscott 		/*
33535084Sjohnlev 		 * Check for matching module entry (failsafe or normal).
33545084Sjohnlev 		 * If it fails to match, we go around the loop again.
33555084Sjohnlev 		 * For xpv entries, there are two module lines, so we
33565084Sjohnlev 		 * do the check twice.
33573467Srscott 		 */
3358662Sszhou 		lp = lp->next;	/* advance to module line */
33595084Sjohnlev 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
33605084Sjohnlev 		    (((lp = lp->next) != NULL) &&
33615084Sjohnlev 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
33625084Sjohnlev 			/* match found */
33635084Sjohnlev 			break;
3364662Sszhou 		}
33655084Sjohnlev 	}
33665084Sjohnlev 
33675084Sjohnlev 	if (entry_num && ent) {
33685084Sjohnlev 		*entry_num = i;
33695084Sjohnlev 	}
3370662Sszhou 	return (ent);
3371662Sszhou }
3372662Sszhou 
3373662Sszhou static int
3374662Sszhou update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
33755084Sjohnlev     char *mod_kernel, char *module, int root_opt)
3376662Sszhou {
33773446Smrj 	int i, change_kernel = 0;
3378662Sszhou 	entry_t *ent;
3379662Sszhou 	line_t *lp;
3380662Sszhou 	char linebuf[BAM_MAXLINE];
3381662Sszhou 
3382662Sszhou 	/* note: don't match on title, it's updated on upgrade */
33835084Sjohnlev 	ent = find_boot_entry(mp, NULL, kernel, root, module, root_opt, &i);
33843446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
33853446Smrj 		/*
33863446Smrj 		 * We may be upgrading a kernel from multiboot to
33873446Smrj 		 * directboot.  Look for a multiboot entry.
33883446Smrj 		 */
33895084Sjohnlev 		ent = find_boot_entry(mp, NULL, "multiboot", root,
33905084Sjohnlev 		    MULTI_BOOT_ARCHIVE, root_opt, NULL);
33913446Smrj 		if (ent != NULL) {
33923446Smrj 			change_kernel = 1;
33933446Smrj 		}
33943446Smrj 	}
3395662Sszhou 	if (ent == NULL)
3396662Sszhou 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
33975084Sjohnlev 		    kernel, mod_kernel, module));
3398662Sszhou 
3399662Sszhou 	/* replace title of exiting entry and delete root line */
3400662Sszhou 	lp = ent->start;
3401662Sszhou 	lp = lp->next;	/* title line */
3402662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3403662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3404662Sszhou 	free(lp->arg);
3405662Sszhou 	free(lp->line);
3406662Sszhou 	lp->arg = s_strdup(title);
3407662Sszhou 	lp->line = s_strdup(linebuf);
3408662Sszhou 
3409662Sszhou 	lp = lp->next;	/* root line */
3410662Sszhou 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3411662Sszhou 		if (root_opt) {		/* root line not needed */
3412662Sszhou 			line_t *tmp = lp;
3413662Sszhou 			lp = lp->next;
3414662Sszhou 			unlink_line(mp, tmp);
3415662Sszhou 			line_free(tmp);
3416662Sszhou 		} else
3417662Sszhou 			lp = lp->next;
3418662Sszhou 	}
34193446Smrj 
34203446Smrj 	if (change_kernel) {
34213446Smrj 		/*
34223446Smrj 		 * We're upgrading from multiboot to directboot.
34233446Smrj 		 */
34243446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
34253446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
34263446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
34273446Smrj 			    kernel);
34283446Smrj 			free(lp->arg);
34293446Smrj 			free(lp->line);
34303446Smrj 			lp->arg = s_strdup(kernel);
34313446Smrj 			lp->line = s_strdup(linebuf);
34323446Smrj 			lp = lp->next;
34333446Smrj 		}
34343446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
34353446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
34363446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
34373446Smrj 			    module);
34383446Smrj 			free(lp->arg);
34393446Smrj 			free(lp->line);
34403446Smrj 			lp->arg = s_strdup(module);
34413446Smrj 			lp->line = s_strdup(linebuf);
34423446Smrj 			lp = lp->next;
34433446Smrj 		}
34443446Smrj 	}
3445662Sszhou 	return (i);
3446662Sszhou }
3447662Sszhou 
34480Sstevel@tonic-gate /*ARGSUSED*/
34490Sstevel@tonic-gate static error_t
34500Sstevel@tonic-gate update_entry(menu_t *mp, char *menu_root, char *opt)
34510Sstevel@tonic-gate {
34520Sstevel@tonic-gate 	FILE *fp;
34530Sstevel@tonic-gate 	int entry;
34543449Srscott 	char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL;
3455662Sszhou 	struct stat sbuf;
3456662Sszhou 	char failsafe[256];
34570Sstevel@tonic-gate 
34580Sstevel@tonic-gate 	assert(mp);
34590Sstevel@tonic-gate 	assert(opt);
34600Sstevel@tonic-gate 
34610Sstevel@tonic-gate 	osdev = strtok(opt, ",");
34620Sstevel@tonic-gate 	osroot = strtok(NULL, ",");
34630Sstevel@tonic-gate 	if (osroot == NULL)
34640Sstevel@tonic-gate 		osroot = menu_root;
34650Sstevel@tonic-gate 	title = get_title(osroot);
34660Sstevel@tonic-gate 
34670Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3468662Sszhou 	fp = open_diskmap(osroot);
34690Sstevel@tonic-gate 	if (fp == NULL) {
34700Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
34710Sstevel@tonic-gate 		return (BAM_ERROR);
34720Sstevel@tonic-gate 	}
34730Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
34740Sstevel@tonic-gate 	(void) fclose(fp);
34750Sstevel@tonic-gate 	if (grubdisk == NULL) {
34760Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
34770Sstevel@tonic-gate 		return (BAM_ERROR);
34780Sstevel@tonic-gate 	}
34790Sstevel@tonic-gate 
34800Sstevel@tonic-gate 	/* add the entry for normal Solaris */
34813446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
34823446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
34835084Sjohnlev 		    DIRECT_BOOT_KERNEL, NULL, DIRECT_BOOT_ARCHIVE,
34843446Smrj 		    osroot == menu_root);
34855084Sjohnlev 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
34865084Sjohnlev 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubdisk,
34875084Sjohnlev 			    XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE,
34885084Sjohnlev 			    osroot == menu_root);
34895084Sjohnlev 		}
34903446Smrj 	} else {
34915084Sjohnlev 		entry = update_boot_entry(mp, title, grubdisk, MULTI_BOOT,
34925084Sjohnlev 		    NULL, MULTI_BOOT_ARCHIVE, osroot == menu_root);
34935084Sjohnlev 	}
34945084Sjohnlev 
34955084Sjohnlev 	/*
34965084Sjohnlev 	 * Add the entry for failsafe archive.  On a bfu'd system, the
34975084Sjohnlev 	 * failsafe may be different than the installed kernel.
34985084Sjohnlev 	 */
34993446Smrj 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
35003446Smrj 	if (stat(failsafe, &sbuf) == 0) {
35013449Srscott 
35023449Srscott 		/* Figure out where the kernel line should point */
35033449Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
35043449Srscott 		    DIRECT_BOOT_FAILSAFE_KERNEL);
35053449Srscott 		if (stat(failsafe, &sbuf) == 0) {
35063449Srscott 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
35073449Srscott 		} else {
35083449Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
35093449Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
35103449Srscott 			if (stat(failsafe, &sbuf) == 0) {
35113449Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
35123449Srscott 			}
35133449Srscott 		}
35143449Srscott 		if (failsafe_kernel != NULL) {
35153449Srscott 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
35165084Sjohnlev 			    failsafe_kernel, NULL, MINIROOT,
35175084Sjohnlev 			    osroot == menu_root);
35183449Srscott 		}
35193446Smrj 	}
35200Sstevel@tonic-gate 	free(grubdisk);
35210Sstevel@tonic-gate 
35220Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
35230Sstevel@tonic-gate 		return (BAM_ERROR);
35240Sstevel@tonic-gate 	}
35250Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
35260Sstevel@tonic-gate 	return (BAM_WRITE);
35270Sstevel@tonic-gate }
35280Sstevel@tonic-gate 
3529316Svikram static char *
3530316Svikram read_grub_root(void)
3531316Svikram {
3532316Svikram 	FILE *fp;
3533316Svikram 	struct stat sb;
3534316Svikram 	char buf[BAM_MAXLINE];
3535316Svikram 	char *rootstr;
3536316Svikram 
3537316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3538316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3539316Svikram 		return (NULL);
3540316Svikram 	}
3541316Svikram 
3542316Svikram 	if (stat(GRUB_root, &sb) != 0) {
3543316Svikram 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3544316Svikram 		return (NULL);
3545316Svikram 	}
3546316Svikram 
3547316Svikram 	fp = fopen(GRUB_root, "r");
3548316Svikram 	if (fp == NULL) {
3549316Svikram 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3550316Svikram 		return (NULL);
3551316Svikram 	}
3552316Svikram 
3553316Svikram 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3554316Svikram 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3555316Svikram 		(void) fclose(fp);
3556316Svikram 		return (NULL);
3557316Svikram 	}
3558316Svikram 
3559316Svikram 	/*
3560316Svikram 	 * Copy buf here as check below may trash the buffer
3561316Svikram 	 */
3562316Svikram 	rootstr = s_strdup(buf);
3563316Svikram 
3564316Svikram 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3565316Svikram 		bam_error(BAD_ROOT_FILE, GRUB_root);
3566316Svikram 		free(rootstr);
3567316Svikram 		rootstr = NULL;
3568316Svikram 	}
3569316Svikram 
3570316Svikram 	(void) fclose(fp);
3571316Svikram 
3572316Svikram 	return (rootstr);
3573316Svikram }
3574316Svikram 
3575662Sszhou static void
35763446Smrj save_default_entry(menu_t *mp, const char *which)
3577662Sszhou {
3578662Sszhou 	int lineNum, entryNum;
3579662Sszhou 	int entry = 0;	/* default is 0 */
3580662Sszhou 	char linebuf[BAM_MAXLINE];
3581662Sszhou 	line_t *lp = mp->curdefault;
3582662Sszhou 
35833381Svikram 	if (mp->start) {
35843381Svikram 		lineNum = mp->end->lineNum;
35853381Svikram 		entryNum = mp->end->entryNum;
35863381Svikram 	} else {
35873381Svikram 		lineNum = LINE_INIT;
35883381Svikram 		entryNum = ENTRY_INIT;
35893381Svikram 	}
35903381Svikram 
3591662Sszhou 	if (lp)
3592662Sszhou 		entry = s_strtol(lp->arg);
3593662Sszhou 
35943446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
3595662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
3596662Sszhou }
3597662Sszhou 
3598662Sszhou static void
35993446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
3600662Sszhou {
3601662Sszhou 	int entry;
3602662Sszhou 	char *str;
3603662Sszhou 
3604662Sszhou 	if (lp == NULL)
3605662Sszhou 		return;		/* nothing to restore */
3606662Sszhou 
36073446Smrj 	str = lp->arg + strlen(which);
3608662Sszhou 	entry = s_strtol(str);
3609662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3610662Sszhou 
3611662Sszhou 	/* delete saved old default line */
3612662Sszhou 	unlink_line(mp, lp);
3613662Sszhou 	line_free(lp);
3614662Sszhou }
3615662Sszhou 
36160Sstevel@tonic-gate /*
36170Sstevel@tonic-gate  * This function is for supporting reboot with args.
36180Sstevel@tonic-gate  * The opt value can be:
36190Sstevel@tonic-gate  * NULL		delete temp entry, if present
36200Sstevel@tonic-gate  * entry=#	switches default entry to 1
36210Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
36220Sstevel@tonic-gate  *		and make it the default
36230Sstevel@tonic-gate  */
36240Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
36250Sstevel@tonic-gate 
3626662Sszhou /*ARGSUSED*/
36270Sstevel@tonic-gate static error_t
36280Sstevel@tonic-gate update_temp(menu_t *mp, char *menupath, char *opt)
36290Sstevel@tonic-gate {
36300Sstevel@tonic-gate 	int entry;
36314346Srscott 	char *grubdisk, *rootdev, *path, *opt_ptr;
36323446Smrj 	char kernbuf[BUFSIZ];
36333446Smrj 	char args_buf[BUFSIZ];
3634316Svikram 	struct stat sb;
36350Sstevel@tonic-gate 
36360Sstevel@tonic-gate 	assert(mp);
36370Sstevel@tonic-gate 
3638662Sszhou 	/* If no option, delete exiting reboot menu entry */
3639662Sszhou 	if (opt == NULL) {
3640662Sszhou 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
36415084Sjohnlev 		    NULL, 0, &entry);
3642662Sszhou 		if (ent == NULL)	/* not found is ok */
3643662Sszhou 			return (BAM_SUCCESS);
3644662Sszhou 		(void) do_delete(mp, entry);
36453446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
36463446Smrj 		mp->olddefault = NULL;
3647662Sszhou 		return (BAM_WRITE);
3648662Sszhou 	}
3649662Sszhou 
3650662Sszhou 	/* if entry= is specified, set the default entry */
3651662Sszhou 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
36520Sstevel@tonic-gate 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
36530Sstevel@tonic-gate 		/* this is entry=# option */
36540Sstevel@tonic-gate 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
36550Sstevel@tonic-gate 	}
36560Sstevel@tonic-gate 
36570Sstevel@tonic-gate 	/*
36580Sstevel@tonic-gate 	 * add a new menu entry base on opt and make it the default
36590Sstevel@tonic-gate 	 */
3660316Svikram 	grubdisk = NULL;
3661316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3662316Svikram 		/*
3663316Svikram 		 * 1. First get root disk name from mnttab
3664316Svikram 		 * 2. Translate disk name to grub name
3665316Svikram 		 * 3. Add the new menu entry
3666316Svikram 		 */
3667316Svikram 		rootdev = get_special("/");
3668316Svikram 		if (rootdev) {
3669316Svikram 			grubdisk = os_to_grubdisk(rootdev, 1);
3670316Svikram 			free(rootdev);
3671316Svikram 		}
3672316Svikram 	} else {
3673316Svikram 		/*
3674316Svikram 		 * This is an LU BE. The GRUB_root file
3675316Svikram 		 * contains entry for GRUB's "root" cmd.
3676316Svikram 		 */
3677316Svikram 		grubdisk = read_grub_root();
36780Sstevel@tonic-gate 	}
36790Sstevel@tonic-gate 	if (grubdisk == NULL) {
3680316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
36810Sstevel@tonic-gate 		return (BAM_ERROR);
36820Sstevel@tonic-gate 	}
36830Sstevel@tonic-gate 
36840Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
36853446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
36863446Smrj 		if (opt[0] == '-') {
36873446Smrj 			/* It's an option - first see if boot-file is set */
36883446Smrj 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
36893446Smrj 			    != BAM_SUCCESS)
36903446Smrj 				return (BAM_ERROR);
36913446Smrj 			if (kernbuf[0] == '\0')
36923446Smrj 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
36933446Smrj 				    BUFSIZ);
36943446Smrj 			(void) strlcat(kernbuf, " ", BUFSIZ);
36953446Smrj 			(void) strlcat(kernbuf, opt, BUFSIZ);
36963446Smrj 		} else if (opt[0] == '/') {
36974346Srscott 			/* It's a full path, so write it out. */
36983446Smrj 			(void) strlcpy(kernbuf, opt, BUFSIZ);
36994346Srscott 
37004346Srscott 			/*
37014346Srscott 			 * If someone runs:
37024346Srscott 			 *
37034346Srscott 			 *	# eeprom boot-args='-kd'
37044346Srscott 			 *	# reboot /platform/i86pc/kernel/unix
37054346Srscott 			 *
37064346Srscott 			 * we want to use the boot-args as part of the boot
37074346Srscott 			 * line.  On the other hand, if someone runs:
37084346Srscott 			 *
37094346Srscott 			 *	# reboot "/platform/i86pc/kernel/unix -kd"
37104346Srscott 			 *
37114346Srscott 			 * we don't need to mess with boot-args.  If there's
37124346Srscott 			 * no space in the options string, assume we're in the
37134346Srscott 			 * first case.
37144346Srscott 			 */
37154346Srscott 			if (strchr(opt, ' ') == NULL) {
37164346Srscott 				if (set_kernel(mp, ARGS_CMD, NULL, args_buf,
37174346Srscott 				    BUFSIZ) != BAM_SUCCESS)
37184346Srscott 					return (BAM_ERROR);
37194346Srscott 
37204346Srscott 				if (args_buf[0] != '\0') {
37214346Srscott 					(void) strlcat(kernbuf, " ", BUFSIZ);
37224346Srscott 					(void) strlcat(kernbuf, args_buf,
37234346Srscott 					    BUFSIZ);
37244346Srscott 				}
37254346Srscott 			}
37263446Smrj 		} else {
37274346Srscott 			/*
37284346Srscott 			 * It may be a partial path, or it may be a partial
37294346Srscott 			 * path followed by options.  Assume that only options
37304346Srscott 			 * follow a space.  If someone sends us a kernel path
37314346Srscott 			 * that includes a space, they deserve to be broken.
37324346Srscott 			 */
37334346Srscott 			opt_ptr = strchr(opt, ' ');
37344346Srscott 			if (opt_ptr != NULL) {
37354346Srscott 				*opt_ptr = '\0';
37364346Srscott 			}
37374346Srscott 
37383446Smrj 			path = expand_path(opt);
37393446Smrj 			if (path != NULL) {
37403446Smrj 				(void) strlcpy(kernbuf, path, BUFSIZ);
37413446Smrj 				free(path);
37424346Srscott 
37434346Srscott 				/*
37444346Srscott 				 * If there were options given, use those.
37454346Srscott 				 * Otherwise, copy over the default options.
37464346Srscott 				 */
37474346Srscott 				if (opt_ptr != NULL) {
37484346Srscott 					/* Restore the space in opt string */
37494346Srscott 					*opt_ptr = ' ';
37504346Srscott 					(void) strlcat(kernbuf, opt_ptr,
37514346Srscott 					    BUFSIZ);
37524346Srscott 				} else {
37533446Smrj 					if (set_kernel(mp, ARGS_CMD, NULL,
37543446Smrj 					    args_buf, BUFSIZ) != BAM_SUCCESS)
37553446Smrj 						return (BAM_ERROR);
37563446Smrj 
37573446Smrj 					if (args_buf[0] != '\0') {
37583446Smrj 						(void) strlcat(kernbuf, " ",
37593446Smrj 						    BUFSIZ);
37603446Smrj 						(void) strlcat(kernbuf,
37613446Smrj 						    args_buf, BUFSIZ);
37623446Smrj 					}
37633446Smrj 				}
37644346Srscott 			} else {
37654346Srscott 				bam_error(UNKNOWN_KERNEL, opt);
37664346Srscott 				bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
37674346Srscott 				return (BAM_ERROR);
37683446Smrj 			}
37693446Smrj 		}
37703446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
37715084Sjohnlev 		    NULL, NULL);
37723446Smrj 	} else {
37733446Smrj 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
37743446Smrj 		    MULTI_BOOT, opt);
37753446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
37765084Sjohnlev 		    NULL, MULTI_BOOT_ARCHIVE);
37773446Smrj 	}
37780Sstevel@tonic-gate 	free(grubdisk);
37790Sstevel@tonic-gate 
37800Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
3781316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
37820Sstevel@tonic-gate 		return (BAM_ERROR);
37830Sstevel@tonic-gate 	}
3784662Sszhou 
37853446Smrj 	save_default_entry(mp, BAM_OLDDEF);
37860Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
37870Sstevel@tonic-gate 	return (BAM_WRITE);
37880Sstevel@tonic-gate }
37890Sstevel@tonic-gate 
37900Sstevel@tonic-gate static error_t
37910Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
37920Sstevel@tonic-gate {
37930Sstevel@tonic-gate 	line_t *lp, *found, *last;
37940Sstevel@tonic-gate 	char *cp, *str;
37950Sstevel@tonic-gate 	char prefix[BAM_MAXLINE];
37960Sstevel@tonic-gate 	size_t len;
37970Sstevel@tonic-gate 
37980Sstevel@tonic-gate 	assert(mp);
37990Sstevel@tonic-gate 	assert(globalcmd);
38000Sstevel@tonic-gate 
3801316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3802316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3803316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3804316Svikram 			bam_error(INVALID_ENTRY, prefix);
3805316Svikram 			return (BAM_ERROR);
3806316Svikram 		}
3807316Svikram 	}
3808316Svikram 
38090Sstevel@tonic-gate 	found = last = NULL;
38100Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
38110Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
38120Sstevel@tonic-gate 			continue;
38130Sstevel@tonic-gate 
38140Sstevel@tonic-gate 		last = lp; /* track the last global found */
38150Sstevel@tonic-gate 
38160Sstevel@tonic-gate 		if (lp->cmd == NULL) {
38170Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
38180Sstevel@tonic-gate 			continue;
38190Sstevel@tonic-gate 		}
38200Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
38210Sstevel@tonic-gate 			continue;
38220Sstevel@tonic-gate 
38230Sstevel@tonic-gate 		if (found) {
38240Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
38250Sstevel@tonic-gate 		}
38260Sstevel@tonic-gate 		found = lp;
38270Sstevel@tonic-gate 	}
38280Sstevel@tonic-gate 
38290Sstevel@tonic-gate 	if (found == NULL) {
38300Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
38310Sstevel@tonic-gate 		if (last == NULL) {
38320Sstevel@tonic-gate 			lp->next = mp->start;
38330Sstevel@tonic-gate 			mp->start = lp;
38340Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
38350Sstevel@tonic-gate 		} else {
38360Sstevel@tonic-gate 			lp->next = last->next;
38370Sstevel@tonic-gate 			last->next = lp;
38380Sstevel@tonic-gate 			if (lp->next == NULL)
38390Sstevel@tonic-gate 				mp->end = lp;
38400Sstevel@tonic-gate 		}
38410Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
38420Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
38430Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
38440Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
38450Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
38460Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
38470Sstevel@tonic-gate 		return (BAM_WRITE);
38480Sstevel@tonic-gate 	}
38490Sstevel@tonic-gate 
38500Sstevel@tonic-gate 	/*
38510Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
38520Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
38530Sstevel@tonic-gate 	 * readability.
38540Sstevel@tonic-gate 	 */
38550Sstevel@tonic-gate 	str = found->line;
38560Sstevel@tonic-gate 	cp = prefix;
38570Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
38580Sstevel@tonic-gate 		*(cp++) = *(str++);
38590Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
38600Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
38610Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
38620Sstevel@tonic-gate 
38630Sstevel@tonic-gate 	free(found->line);
38640Sstevel@tonic-gate 	found->line = s_calloc(1, len);
38650Sstevel@tonic-gate 	(void) snprintf(found->line, len,
38664346Srscott 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
38670Sstevel@tonic-gate 
38680Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
38690Sstevel@tonic-gate }
38700Sstevel@tonic-gate 
38713446Smrj /*
38723446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
38734346Srscott  * expand it to a full unix path.  The calling function is expected to
38744346Srscott  * output a message if an error occurs and NULL is returned.
38753446Smrj  */
38763446Smrj static char *
38773446Smrj expand_path(const char *partial_path)
38783446Smrj {
38793446Smrj 	int new_path_len;
38803446Smrj 	char *new_path, new_path2[PATH_MAX];
38813446Smrj 	struct stat sb;
38823446Smrj 
38833446Smrj 	new_path_len = strlen(partial_path) + 64;
38843446Smrj 	new_path = s_calloc(1, new_path_len);
38853446Smrj 
38863446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
38873446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
38883446Smrj 	    partial_path);
38893446Smrj 	if (stat(new_path, &sb) == 0) {
38903446Smrj 		return (new_path);
38913446Smrj 	}
38923446Smrj 
38933446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
38943446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
38953446Smrj 		    DIRECT_BOOT_KERNEL);
38963446Smrj 		return (new_path);
38973446Smrj 	}
38983446Smrj 
38993446Smrj 	/*
39003446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
39013446Smrj 	 * see if we were just given a glom name.
39023446Smrj 	 */
39033446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
39043446Smrj 	    partial_path);
39053446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
39063446Smrj 	    partial_path);
39073446Smrj 	if (stat(new_path, &sb) == 0) {
39083446Smrj 		if (stat(new_path2, &sb) == 0) {
39093446Smrj 			/*
39103446Smrj 			 * We matched both, so we actually
39113446Smrj 			 * want to write the $ISADIR version.
39123446Smrj 			 */
39133446Smrj 			(void) snprintf(new_path, new_path_len,
39143446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
39153446Smrj 			    partial_path);
39163446Smrj 		}
39173446Smrj 		return (new_path);
39183446Smrj 	}
39193446Smrj 
39203446Smrj 	free(new_path);
39213446Smrj 	return (NULL);
39223446Smrj }
39233446Smrj 
39243446Smrj /*
39253446Smrj  * The kernel cmd and arg have been changed, so
39263446Smrj  * check whether the archive line needs to change.
39273446Smrj  */
39283446Smrj static void
39293446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
39303446Smrj {
39313446Smrj 	line_t *lp = entryp->start;
39323446Smrj 	char *new_archive;
39333446Smrj 	menu_cmd_t m_cmd;
39343446Smrj 
39353446Smrj 	for (; lp != NULL; lp = lp->next) {
39363446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
39373446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
39383446Smrj 			break;
39393446Smrj 		}
39403446Smrj 		if (lp == entryp->end)
39413446Smrj 			return;
39423446Smrj 	}
39433446Smrj 	if (lp == NULL)
39443446Smrj 		return;
39453446Smrj 
39463446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
39473446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
39483446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
39493446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
39503446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
39513446Smrj 		m_cmd = MODULE_CMD;
39523446Smrj 	} else {
39533446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
39543446Smrj 		m_cmd = MODULE_CMD;
39553446Smrj 	}
39563446Smrj 
39573446Smrj 	if (strcmp(lp->arg, new_archive) == 0)
39583446Smrj 		return;
39593446Smrj 
39603446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
39613446Smrj 		free(lp->cmd);
39623446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
39633446Smrj 	}
39643446Smrj 
39653446Smrj 	free(lp->arg);
39663446Smrj 	lp->arg = s_strdup(new_archive);
39673446Smrj 	update_line(lp);
39683446Smrj }
39693446Smrj 
39703446Smrj /*
39713446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
39723446Smrj  */
39733446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
39743446Smrj 
39753446Smrj /*
39763446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
39773446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
39783446Smrj  * string, reset the value to the default.  If path is a non-zero-length
39793446Smrj  * string, set the kernel or arguments.
39803446Smrj  */
39813446Smrj static error_t
39823446Smrj set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
39833446Smrj {
39843446Smrj 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
39853446Smrj 	entry_t *entryp;
39863446Smrj 	line_t *ptr, *kernelp;
39873446Smrj 	char *new_arg, *old_args, *space;
39883446Smrj 	char *grubdisk, *rootdev, *new_path;
39893446Smrj 	char old_space;
39903446Smrj 	size_t old_kernel_len, new_str_len;
39913446Smrj 	struct stat sb;
39923446Smrj 
39933446Smrj 	assert(bufsize > 0);
39943446Smrj 
39953446Smrj 	ptr = kernelp = NULL;
39963446Smrj 	new_arg = old_args = space = NULL;
39973446Smrj 	grubdisk = rootdev = new_path = NULL;
39983446Smrj 	buf[0] = '\0';
39993446Smrj 
40003446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
40013446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
40023446Smrj 		return (BAM_ERROR);
40033446Smrj 	}
40043446Smrj 
40053446Smrj 	/*
40063446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
40073446Smrj 	 * one, we don't want to mess with it.  Just print an error and
40083446Smrj 	 * return.
40093446Smrj 	 */
40103446Smrj 	if (mp->curdefault) {
40113446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
40123446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
40133446Smrj 			if (entryp->entryNum == entryNum)
40143446Smrj 				break;
40153446Smrj 		}
40163446Smrj 		if ((entryp != NULL) &&
40173446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
40183446Smrj 			bam_error(DEFAULT_NOT_BAM);
40193446Smrj 			return (BAM_ERROR);
40203446Smrj 		}
40213446Smrj 	}
40223446Smrj 
40233446Smrj 	entryNum = -1;
40245084Sjohnlev 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, 0,
40253446Smrj 	    &entryNum);
40263446Smrj 
40273446Smrj 	if (entryp != NULL) {
40283446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
40293446Smrj 		    ptr = ptr->next) {
40303446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
40313446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
40323446Smrj 				kernelp = ptr;
40333446Smrj 				break;
40343446Smrj 			}
40353446Smrj 		}
40363446Smrj 		if (kernelp == NULL) {
40373446Smrj 			bam_error(NO_KERNEL, entryNum);
40383446Smrj 			return (BAM_ERROR);
40393446Smrj 		}
40403446Smrj 
40413446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
40423446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
40433446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
40443446Smrj 			old_args++;
40453446Smrj 	}
40463446Smrj 
40473446Smrj 	if (path == NULL) {
40483446Smrj 		/* Simply report what was found */
40493446Smrj 		if (kernelp == NULL)
40503446Smrj 			return (BAM_SUCCESS);
40513446Smrj 
40523446Smrj 		if (optnum == ARGS_CMD) {
40533446Smrj 			if (old_args[0] != '\0')
40543446Smrj 				(void) strlcpy(buf, old_args, bufsize);
40553446Smrj 		} else {
40563446Smrj 			/*
40573446Smrj 			 * We need to print the kernel, so we just turn the
40583446Smrj 			 * first space into a '\0' and print the beginning.
40593446Smrj 			 * We don't print anything if it's the default kernel.
40603446Smrj 			 */
40613446Smrj 			old_space = *space;
40623446Smrj 			*space = '\0';
40633446Smrj 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
40643446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
40653446Smrj 			*space = old_space;
40663446Smrj 		}
40673446Smrj 		return (BAM_SUCCESS);
40683446Smrj 	}
40693446Smrj 
40703446Smrj 	/*
40713446Smrj 	 * First, check if we're resetting an entry to the default.
40723446Smrj 	 */
40733446Smrj 	if ((path[0] == '\0') ||
40743446Smrj 	    ((optnum == KERNEL_CMD) &&
40753446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
40763446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
40773446Smrj 			/* No previous entry, it's already the default */
40783446Smrj 			return (BAM_SUCCESS);
40793446Smrj 		}
40803446Smrj 
40813446Smrj 		/*
40823446Smrj 		 * Check if we can delete the entry.  If we're resetting the
40833446Smrj 		 * kernel command, and the args is already empty, or if we're
40843446Smrj 		 * resetting the args command, and the kernel is already the
40853446Smrj 		 * default, we can restore the old default and delete the entry.
40863446Smrj 		 */
40873446Smrj 		if (((optnum == KERNEL_CMD) &&
40883446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
40893446Smrj 		    ((optnum == ARGS_CMD) &&
40903446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
40913446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
40923446Smrj 			kernelp = NULL;
40933446Smrj 			(void) do_delete(mp, entryNum);
40943446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
40953446Smrj 			    mp->old_rc_default);
40963446Smrj 			mp->old_rc_default = NULL;
40973446Smrj 			rv = BAM_WRITE;
40983446Smrj 			goto done;
40993446Smrj 		}
41003446Smrj 
41013446Smrj 		if (optnum == KERNEL_CMD) {
41023446Smrj 			/*
41033446Smrj 			 * At this point, we've already checked that old_args
41043446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
41053446Smrj 			 * a space a the string termination character.
41063446Smrj 			 */
41073446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
41083446Smrj 			    strlen(old_args) + 2;
41093446Smrj 			new_arg = s_calloc(1, new_str_len);
41103446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
41113446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
41123446Smrj 			free(kernelp->arg);
41133446Smrj 			kernelp->arg = new_arg;
41143446Smrj 
41153446Smrj 			/*
41163446Smrj 			 * We have changed the kernel line, so we may need
41173446Smrj 			 * to update the archive line as well.
41183446Smrj 			 */
41193446Smrj 			set_archive_line(entryp, kernelp);
41203446Smrj 		} else {
41213446Smrj 			/*
41223446Smrj 			 * We're resetting the boot args to nothing, so
41233446Smrj 			 * we only need to copy the kernel.  We've already
41243446Smrj 			 * checked that the kernel is not the default.
41253446Smrj 			 */
41263446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
41273446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
41283446Smrj 			    kernelp->arg);
41293446Smrj 			free(kernelp->arg);
41303446Smrj 			kernelp->arg = new_arg;
41313446Smrj 		}
41323446Smrj 		rv = BAM_WRITE;
41333446Smrj 		goto done;
41343446Smrj 	}
41353446Smrj 
41363446Smrj 	/*
41373446Smrj 	 * Expand the kernel file to a full path, if necessary
41383446Smrj 	 */
41393446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
41403446Smrj 		new_path = expand_path(path);
41413446Smrj 		if (new_path == NULL) {
41424346Srscott 			bam_error(UNKNOWN_KERNEL, path);
41433446Smrj 			return (BAM_ERROR);
41443446Smrj 		}
41453446Smrj 		free_new_path = 1;
41463446Smrj 	} else {
41473446Smrj 		new_path = path;
41483446Smrj 		free_new_path = 0;
41493446Smrj 	}
41503446Smrj 
41513446Smrj 	/*
41523446Smrj 	 * At this point, we know we're setting a new value.  First, take care
41533446Smrj 	 * of the case where there was no previous entry.
41543446Smrj 	 */
41553446Smrj 	if (entryp == NULL) {
41563446Smrj 		/* Similar to code in update_temp */
41573446Smrj 		if (stat(GRUB_slice, &sb) != 0) {
41583446Smrj 			/*
41593446Smrj 			 * 1. First get root disk name from mnttab
41603446Smrj 			 * 2. Translate disk name to grub name
41613446Smrj 			 * 3. Add the new menu entry
41623446Smrj 			 */
41633446Smrj 			rootdev = get_special("/");
41643446Smrj 			if (rootdev) {
41653446Smrj 				grubdisk = os_to_grubdisk(rootdev, 1);
41663446Smrj 				free(rootdev);
41673446Smrj 			}
41683446Smrj 		} else {
41693446Smrj 			/*
41703446Smrj 			 * This is an LU BE. The GRUB_root file
41713446Smrj 			 * contains entry for GRUB's "root" cmd.
41723446Smrj 			 */
41733446Smrj 			grubdisk = read_grub_root();
41743446Smrj 		}
41753446Smrj 		if (grubdisk == NULL) {
41763446Smrj 			bam_error(REBOOT_WITH_ARGS_FAILED);
41773446Smrj 			rv = BAM_ERROR;
41783446Smrj 			goto done;
41793446Smrj 		}
41803446Smrj 		if (optnum == KERNEL_CMD) {
41813446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
41825084Sjohnlev 			    grubdisk, new_path, NULL, NULL);
41833446Smrj 		} else {
41843446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
41853446Smrj 			    strlen(path) + 8;
41863446Smrj 			new_arg = s_calloc(1, new_str_len);
41873446Smrj 
41883446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
41893446Smrj 			    DIRECT_BOOT_KERNEL, path);
41903446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
41915084Sjohnlev 			    grubdisk, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
41923446Smrj 		}
41933446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
41943446Smrj 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
41953446Smrj 		rv = BAM_WRITE;
41963446Smrj 		goto done;
41973446Smrj 	}
41983446Smrj 
41993446Smrj 	/*
42003446Smrj 	 * There was already an bootenv entry which we need to edit.
42013446Smrj 	 */
42023446Smrj 	if (optnum == KERNEL_CMD) {
42033446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
42043446Smrj 		new_arg = s_calloc(1, new_str_len);
42053446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
42063446Smrj 		    old_args);
42073446Smrj 		free(kernelp->arg);
42083446Smrj 		kernelp->arg = new_arg;
42093446Smrj 
42103446Smrj 		/*
42113446Smrj 		 * If we have changed the kernel line, we may need to update
42123446Smrj 		 * the archive line as well.
42133446Smrj 		 */
42143446Smrj 		set_archive_line(entryp, kernelp);
42153446Smrj 	} else {
42163446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
42173446Smrj 		new_arg = s_calloc(1, new_str_len);
42183446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
42193446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
42203446Smrj 		(void) strlcat(new_arg, path, new_str_len);
42213446Smrj 		free(kernelp->arg);
42223446Smrj 		kernelp->arg = new_arg;
42233446Smrj 	}
42243446Smrj 	rv = BAM_WRITE;
42253446Smrj 
42263446Smrj done:
42273446Smrj 	if ((rv == BAM_WRITE) && kernelp)
42283446Smrj 		update_line(kernelp);
42293446Smrj 	if (free_new_path)
42303446Smrj 		free(new_path);
42313446Smrj 	return (rv);
42323446Smrj }
42333446Smrj 
42340Sstevel@tonic-gate /*ARGSUSED*/
42350Sstevel@tonic-gate static error_t
42360Sstevel@tonic-gate set_option(menu_t *mp, char *menu_path, char *opt)
42370Sstevel@tonic-gate {
42380Sstevel@tonic-gate 	int optnum, optval;
42390Sstevel@tonic-gate 	char *val;
42403446Smrj 	char buf[BUFSIZ] = "";
42413446Smrj 	error_t rv;
42420Sstevel@tonic-gate 
42430Sstevel@tonic-gate 	assert(mp);
42440Sstevel@tonic-gate 	assert(opt);
42450Sstevel@tonic-gate 
42460Sstevel@tonic-gate 	val = strchr(opt, '=');
42473446Smrj 	if (val != NULL) {
42483446Smrj 		*val = '\0';
42490Sstevel@tonic-gate 	}
42500Sstevel@tonic-gate 
42510Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
42520Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
42530Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
42540Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
42553446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
42563446Smrj 		optnum = KERNEL_CMD;
42573446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
42583446Smrj 		optnum = ARGS_CMD;
42590Sstevel@tonic-gate 	} else {
42600Sstevel@tonic-gate 		bam_error(INVALID_ENTRY, opt);
42610Sstevel@tonic-gate 		return (BAM_ERROR);
42620Sstevel@tonic-gate 	}
42630Sstevel@tonic-gate 
42643446Smrj 	/*
42653446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
42663446Smrj 	 * others cause errors
42673446Smrj 	 */
42683446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
42693446Smrj 		bam_error(INVALID_ENTRY, opt);
42703446Smrj 		return (BAM_ERROR);
42713446Smrj 	} else if (val != NULL) {
42723446Smrj 		*val = '=';
42733446Smrj 	}
42743446Smrj 
42753446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
42763446Smrj 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
42773446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
42783446Smrj 			(void) printf("%s\n", buf);
42793446Smrj 		return (rv);
42803446Smrj 	} else {
42813446Smrj 		optval = s_strtol(val + 1);
42823446Smrj 		return (set_global(mp, menu_cmds[optnum], optval));
42833446Smrj 	}
42840Sstevel@tonic-gate }
42850Sstevel@tonic-gate 
42860Sstevel@tonic-gate /*
42870Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
42880Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
42890Sstevel@tonic-gate  */
42900Sstevel@tonic-gate static error_t
42910Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
42920Sstevel@tonic-gate {
42930Sstevel@tonic-gate 	line_t *lp;
42940Sstevel@tonic-gate 	char *arg;
42950Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
42960Sstevel@tonic-gate 
42970Sstevel@tonic-gate 	assert(mp);
42980Sstevel@tonic-gate 	assert(menu_path);
42990Sstevel@tonic-gate 	assert(globalcmd);
43000Sstevel@tonic-gate 
43010Sstevel@tonic-gate 	if (mp->start == NULL) {
43020Sstevel@tonic-gate 		if (!quiet)
43030Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
43040Sstevel@tonic-gate 		return (BAM_ERROR);
43050Sstevel@tonic-gate 	}
43060Sstevel@tonic-gate 
43070Sstevel@tonic-gate 	done = 0;
43080Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
43090Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
43100Sstevel@tonic-gate 			continue;
43110Sstevel@tonic-gate 
43120Sstevel@tonic-gate 		if (lp->cmd == NULL) {
43130Sstevel@tonic-gate 			if (!quiet)
43140Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
43150Sstevel@tonic-gate 			continue;
43160Sstevel@tonic-gate 		}
43170Sstevel@tonic-gate 
43180Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
43190Sstevel@tonic-gate 			continue;
43200Sstevel@tonic-gate 
43210Sstevel@tonic-gate 		/* Found global. Check for duplicates */
43220Sstevel@tonic-gate 		if (done && !quiet) {
43230Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
43240Sstevel@tonic-gate 			ret = BAM_ERROR;
43250Sstevel@tonic-gate 		}
43260Sstevel@tonic-gate 
43270Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
43280Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
43290Sstevel@tonic-gate 		done = 1;
43300Sstevel@tonic-gate 	}
43310Sstevel@tonic-gate 
43320Sstevel@tonic-gate 	if (!done && bam_verbose)
43330Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
43340Sstevel@tonic-gate 
43350Sstevel@tonic-gate 	return (ret);
43360Sstevel@tonic-gate }
43370Sstevel@tonic-gate 
43380Sstevel@tonic-gate static error_t
43390Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
43400Sstevel@tonic-gate {
43410Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
43420Sstevel@tonic-gate }
43430Sstevel@tonic-gate 
43440Sstevel@tonic-gate static void
43450Sstevel@tonic-gate line_free(line_t *lp)
43460Sstevel@tonic-gate {
43470Sstevel@tonic-gate 	if (lp == NULL)
43480Sstevel@tonic-gate 		return;
43490Sstevel@tonic-gate 
43500Sstevel@tonic-gate 	if (lp->cmd)
43510Sstevel@tonic-gate 		free(lp->cmd);
43520Sstevel@tonic-gate 	if (lp->sep)
43530Sstevel@tonic-gate 		free(lp->sep);
43540Sstevel@tonic-gate 	if (lp->arg)
43550Sstevel@tonic-gate 		free(lp->arg);
43560Sstevel@tonic-gate 	if (lp->line)
43570Sstevel@tonic-gate 		free(lp->line);
43580Sstevel@tonic-gate 	free(lp);
43590Sstevel@tonic-gate }
43600Sstevel@tonic-gate 
43610Sstevel@tonic-gate static void
43620Sstevel@tonic-gate linelist_free(line_t *start)
43630Sstevel@tonic-gate {
43640Sstevel@tonic-gate 	line_t *lp;
43650Sstevel@tonic-gate 
43660Sstevel@tonic-gate 	while (start) {
43670Sstevel@tonic-gate 		lp = start;
43680Sstevel@tonic-gate 		start = start->next;
43690Sstevel@tonic-gate 		line_free(lp);
43700Sstevel@tonic-gate 	}
43710Sstevel@tonic-gate }
43720Sstevel@tonic-gate 
43730Sstevel@tonic-gate static void
43740Sstevel@tonic-gate filelist_free(filelist_t *flistp)
43750Sstevel@tonic-gate {
43760Sstevel@tonic-gate 	linelist_free(flistp->head);
43770Sstevel@tonic-gate 	flistp->head = NULL;
43780Sstevel@tonic-gate 	flistp->tail = NULL;
43790Sstevel@tonic-gate }
43800Sstevel@tonic-gate 
43810Sstevel@tonic-gate static void
43820Sstevel@tonic-gate menu_free(menu_t *mp)
43830Sstevel@tonic-gate {
4384662Sszhou 	entry_t *ent, *tmp;
43850Sstevel@tonic-gate 	assert(mp);
43860Sstevel@tonic-gate 
43870Sstevel@tonic-gate 	if (mp->start)
43880Sstevel@tonic-gate 		linelist_free(mp->start);
4389662Sszhou 	ent = mp->entries;
4390662Sszhou 	while (ent) {
4391662Sszhou 		tmp = ent;
4392662Sszhou 		ent = tmp->next;
4393662Sszhou 		free(tmp);
4394662Sszhou 	}
4395662Sszhou 
43960Sstevel@tonic-gate 	free(mp);
43970Sstevel@tonic-gate }
43980Sstevel@tonic-gate 
43990Sstevel@tonic-gate /*
44000Sstevel@tonic-gate  * Utility routines
44010Sstevel@tonic-gate  */
44020Sstevel@tonic-gate 
44030Sstevel@tonic-gate 
44040Sstevel@tonic-gate /*
44050Sstevel@tonic-gate  * Returns 0 on success
44060Sstevel@tonic-gate  * Any other value indicates an error
44070Sstevel@tonic-gate  */
44080Sstevel@tonic-gate static int
44095648Ssetje exec_cmd(char *cmdline, filelist_t *flistp)
44100Sstevel@tonic-gate {
44110Sstevel@tonic-gate 	char buf[BUFSIZ];
44120Sstevel@tonic-gate 	int ret;
44130Sstevel@tonic-gate 	FILE *ptr;
44140Sstevel@tonic-gate 	sigset_t set;
44150Sstevel@tonic-gate 	void (*disp)(int);
44160Sstevel@tonic-gate 
44170Sstevel@tonic-gate 	/*
44180Sstevel@tonic-gate 	 * For security
44190Sstevel@tonic-gate 	 * - only absolute paths are allowed
44200Sstevel@tonic-gate 	 * - set IFS to space and tab
44210Sstevel@tonic-gate 	 */
44220Sstevel@tonic-gate 	if (*cmdline != '/') {
44230Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
44240Sstevel@tonic-gate 		return (-1);
44250Sstevel@tonic-gate 	}
44260Sstevel@tonic-gate 	(void) putenv("IFS= \t");
44270Sstevel@tonic-gate 
44280Sstevel@tonic-gate 	/*
44290Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
44300Sstevel@tonic-gate 	 * unblock it here
44310Sstevel@tonic-gate 	 */
44320Sstevel@tonic-gate 	(void) sigemptyset(&set);
44330Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
44340Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
44350Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
44360Sstevel@tonic-gate 		return (-1);
44370Sstevel@tonic-gate 	}
44380Sstevel@tonic-gate 
44390Sstevel@tonic-gate 	/*
44400Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
44410Sstevel@tonic-gate 	 */
44420Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
44430Sstevel@tonic-gate 	if (disp == SIG_ERR) {
44440Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
44450Sstevel@tonic-gate 		return (-1);
44460Sstevel@tonic-gate 	}
44470Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
44480Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
44490Sstevel@tonic-gate 		return (-1);
44500Sstevel@tonic-gate 	}
44510Sstevel@tonic-gate 
44520Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
44530Sstevel@tonic-gate 	if (ptr == NULL) {
44540Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
44550Sstevel@tonic-gate 		return (-1);
44560Sstevel@tonic-gate 	}
44570Sstevel@tonic-gate 
44580Sstevel@tonic-gate 	/*
44590Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
44600Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
44610Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
44620Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
44630Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
44640Sstevel@tonic-gate 	 * there is no reader process and the command dies with
44650Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
44660Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
44670Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
44680Sstevel@tonic-gate 	 * pclose().
44690Sstevel@tonic-gate 	 *
44700Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
44710Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
44720Sstevel@tonic-gate 	 * from the value returned by pclose()
44730Sstevel@tonic-gate 	 */
44745648Ssetje 	while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
44755648Ssetje 		if (flistp == NULL) {
44765648Ssetje 			/* s_fgets strips newlines, so insert them at the end */
44775648Ssetje 			bam_print(PRINT, buf);
44785648Ssetje 		} else {
44795648Ssetje 			append_to_flist(flistp, buf);
44800Sstevel@tonic-gate 		}
44810Sstevel@tonic-gate 	}
44820Sstevel@tonic-gate 
44830Sstevel@tonic-gate 	ret = pclose(ptr);
44840Sstevel@tonic-gate 	if (ret == -1) {
44850Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
44860Sstevel@tonic-gate 		return (-1);
44870Sstevel@tonic-gate 	}
44880Sstevel@tonic-gate 
44890Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
44900Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
44910Sstevel@tonic-gate 	} else {
44920Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
44930Sstevel@tonic-gate 		return (-1);
44940Sstevel@tonic-gate 	}
44950Sstevel@tonic-gate }
44960Sstevel@tonic-gate 
44970Sstevel@tonic-gate /*
44980Sstevel@tonic-gate  * Since this function returns -1 on error
44990Sstevel@tonic-gate  * it cannot be used to convert -1. However,
45000Sstevel@tonic-gate  * that is sufficient for what we need.
45010Sstevel@tonic-gate  */
45020Sstevel@tonic-gate static long
45030Sstevel@tonic-gate s_strtol(char *str)
45040Sstevel@tonic-gate {
45050Sstevel@tonic-gate 	long l;
45060Sstevel@tonic-gate 	char *res = NULL;
45070Sstevel@tonic-gate 
45080Sstevel@tonic-gate 	if (str == NULL) {
45090Sstevel@tonic-gate 		return (-1);
45100Sstevel@tonic-gate 	}
45110Sstevel@tonic-gate 
45120Sstevel@tonic-gate 	errno = 0;
45130Sstevel@tonic-gate 	l = strtol(str, &res, 10);
45140Sstevel@tonic-gate 	if (errno || *res != '\0') {
45150Sstevel@tonic-gate 		return (-1);
45160Sstevel@tonic-gate 	}
45170Sstevel@tonic-gate 
45180Sstevel@tonic-gate 	return (l);
45190Sstevel@tonic-gate }
45200Sstevel@tonic-gate 
45210Sstevel@tonic-gate /*
45220Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
45230Sstevel@tonic-gate  */
45240Sstevel@tonic-gate static int
45250Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
45260Sstevel@tonic-gate {
45270Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
45280Sstevel@tonic-gate 
45290Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
45300Sstevel@tonic-gate 	return (fputs(linebuf, fp));
45310Sstevel@tonic-gate }
45320Sstevel@tonic-gate 
45330Sstevel@tonic-gate /*
45340Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
45350Sstevel@tonic-gate  */
45363446Smrj char *
45370Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
45380Sstevel@tonic-gate {
45390Sstevel@tonic-gate 	int n;
45400Sstevel@tonic-gate 
45410Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
45420Sstevel@tonic-gate 	if (buf) {
45430Sstevel@tonic-gate 		n = strlen(buf);
45440Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
45450Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
45460Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
45470Sstevel@tonic-gate 	}
45480Sstevel@tonic-gate 
45490Sstevel@tonic-gate 	return (buf);
45500Sstevel@tonic-gate }
45510Sstevel@tonic-gate 
45523446Smrj void *
45530Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
45540Sstevel@tonic-gate {
45550Sstevel@tonic-gate 	void *ptr;
45560Sstevel@tonic-gate 
45570Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
45580Sstevel@tonic-gate 	if (ptr == NULL) {
45590Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
45600Sstevel@tonic-gate 		bam_exit(1);
45610Sstevel@tonic-gate 	}
45620Sstevel@tonic-gate 	return (ptr);
45630Sstevel@tonic-gate }
45640Sstevel@tonic-gate 
45653446Smrj void *
45663446Smrj s_realloc(void *ptr, size_t sz)
45673446Smrj {
45683446Smrj 	ptr = realloc(ptr, sz);
45693446Smrj 	if (ptr == NULL) {
45703446Smrj 		bam_error(NO_MEM, sz);
45713446Smrj 		bam_exit(1);
45723446Smrj 	}
45733446Smrj 	return (ptr);
45743446Smrj }
45753446Smrj 
45760Sstevel@tonic-gate static char *
45770Sstevel@tonic-gate s_strdup(char *str)
45780Sstevel@tonic-gate {
45790Sstevel@tonic-gate 	char *ptr;
45800Sstevel@tonic-gate 
45810Sstevel@tonic-gate 	if (str == NULL)
45820Sstevel@tonic-gate 		return (NULL);
45830Sstevel@tonic-gate 
45840Sstevel@tonic-gate 	ptr = strdup(str);
45850Sstevel@tonic-gate 	if (ptr == NULL) {
45860Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
45870Sstevel@tonic-gate 		bam_exit(1);
45880Sstevel@tonic-gate 	}
45890Sstevel@tonic-gate 	return (ptr);
45900Sstevel@tonic-gate }
45910Sstevel@tonic-gate 
45920Sstevel@tonic-gate /*
45930Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
45940Sstevel@tonic-gate  * Returns 0 otherwise
45950Sstevel@tonic-gate  */
45960Sstevel@tonic-gate static int
45970Sstevel@tonic-gate is_amd64(void)
45980Sstevel@tonic-gate {
45990Sstevel@tonic-gate 	static int amd64 = -1;
46000Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
46010Sstevel@tonic-gate 
46020Sstevel@tonic-gate 	if (amd64 != -1)
46030Sstevel@tonic-gate 		return (amd64);
46040Sstevel@tonic-gate 
4605*6319Sjg 	if (bam_alt_platform) {
4606*6319Sjg 		if (strcmp(bam_platform, "i86pc") == 0) {
4607*6319Sjg 			amd64 = 1;		/* diskless server */
4608*6319Sjg 		}
4609*6319Sjg 	} else {
4610*6319Sjg 		if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
4611*6319Sjg 		    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
4612*6319Sjg 			amd64 = 1;
4613*6319Sjg 		} else if (strstr(isabuf, "i386") == NULL) {
4614*6319Sjg 			amd64 = 1;		/* diskless server */
4615*6319Sjg 		}
4616*6319Sjg 	}
4617*6319Sjg 	if (amd64 == -1)
46180Sstevel@tonic-gate 		amd64 = 0;
46190Sstevel@tonic-gate 
46200Sstevel@tonic-gate 	return (amd64);
46210Sstevel@tonic-gate }
46220Sstevel@tonic-gate 
46235648Ssetje static int
46245648Ssetje is_sun4u(void)
46255648Ssetje {
4626*6319Sjg 	static int sun4u = -1;
46275648Ssetje 	char mbuf[257];	/* from sysinfo(2) manpage */
46285648Ssetje 
4629*6319Sjg 	if (sun4u != -1)
4630*6319Sjg 		return (sun4u);
4631*6319Sjg 
4632*6319Sjg 	if (bam_alt_platform) {
4633*6319Sjg 		if (strcmp(bam_platform, "sun4u") == 0) {
4634*6319Sjg 			sun4u = 1;
4635*6319Sjg 		}
4636*6319Sjg 	} else {
4637*6319Sjg 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 &&
4638*6319Sjg 		    strncmp(mbuf, "sun4u", strlen("sun4u")) == 0) {
4639*6319Sjg 			sun4u = 1;
4640*6319Sjg 		}
4641*6319Sjg 	}
4642*6319Sjg 	if (sun4u == -1)
4643*6319Sjg 		sun4u = 0;
46445648Ssetje 
46455648Ssetje 	return (sun4u);
46465648Ssetje }
46475648Ssetje 
46485648Ssetje static int
46495648Ssetje is_sun4v(void)
46505648Ssetje {
4651*6319Sjg 	static int sun4v = -1;
46525648Ssetje 	char mbuf[257];	/* from sysinfo(2) manpage */
46535648Ssetje 
4654*6319Sjg 	if (sun4v != -1)
4655*6319Sjg 		return (sun4v);
4656*6319Sjg 
4657*6319Sjg 	if (bam_alt_platform) {
4658*6319Sjg 		if (strcmp(bam_platform, "sun4v") == 0) {
4659*6319Sjg 			sun4v = 1;
4660*6319Sjg 		}
4661*6319Sjg 	} else {
4662*6319Sjg 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 &&
4663*6319Sjg 		    strncmp(mbuf, "sun4v", strlen("sun4v")) == 0) {
4664*6319Sjg 			sun4v = 1;
4665*6319Sjg 		}
4666*6319Sjg 	}
4667*6319Sjg 	if (sun4v == -1)
4668*6319Sjg 		sun4v = 0;
46695648Ssetje 
46705648Ssetje 	return (sun4v);
46715648Ssetje }
46725648Ssetje 
46735648Ssetje 
46740Sstevel@tonic-gate static void
46750Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
46760Sstevel@tonic-gate {
46770Sstevel@tonic-gate 	line_t *lp;
46780Sstevel@tonic-gate 
46790Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
46800Sstevel@tonic-gate 	lp->line = s_strdup(s);
46810Sstevel@tonic-gate 	if (flistp->head == NULL)
46820Sstevel@tonic-gate 		flistp->head = lp;
46830Sstevel@tonic-gate 	else
46840Sstevel@tonic-gate 		flistp->tail->next = lp;
46850Sstevel@tonic-gate 	flistp->tail = lp;
46860Sstevel@tonic-gate }
46874581Ssherrym 
46885648Ssetje #if !defined(_OPB)
46894581Ssherrym 
46904581Ssherrym UCODE_VENDORS;
46914581Ssherrym 
46924581Ssherrym /*ARGSUSED*/
46934581Ssherrym static void
46944581Ssherrym ucode_install(char *root)
46954581Ssherrym {
46964581Ssherrym 	int i;
46974581Ssherrym 
46984581Ssherrym 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
46994581Ssherrym 		int cmd_len = PATH_MAX + 256;
47004581Ssherrym 		char cmd[PATH_MAX + 256];
47014581Ssherrym 		char file[PATH_MAX];
47024581Ssherrym 		char timestamp[PATH_MAX];
47034581Ssherrym 		struct stat fstatus, tstatus;
47044581Ssherrym 		struct utimbuf u_times;
47054581Ssherrym 
47064581Ssherrym 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt",
47074581Ssherrym 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr);
47084581Ssherrym 
47094581Ssherrym 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
47104581Ssherrym 			continue;
47114581Ssherrym 
47124581Ssherrym 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
47134581Ssherrym 
47144581Ssherrym 		if (stat(timestamp, &tstatus) == 0 &&
47154581Ssherrym 		    fstatus.st_mtime <= tstatus.st_mtime)
47164581Ssherrym 			continue;
47174581Ssherrym 
47184581Ssherrym 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
47194581Ssherrym 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
47204581Ssherrym 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
47214581Ssherrym 		if (system(cmd) != 0)
47224581Ssherrym 			return;
47234581Ssherrym 
47244581Ssherrym 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
47254581Ssherrym 			return;
47264581Ssherrym 
47274581Ssherrym 		u_times.actime = fstatus.st_atime;
47284581Ssherrym 		u_times.modtime = fstatus.st_mtime;
47294581Ssherrym 		(void) utime(timestamp, &u_times);
47304581Ssherrym 	}
47314581Ssherrym }
47324581Ssherrym #endif
4733