xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 4581:b6104e41b06c)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51746Svikram  * Common Development and Distribution License (the "License").
61746Svikram  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
223381Svikram  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * bootadm(1M) is a new utility for managing bootability of
300Sstevel@tonic-gate  * Solaris *Newboot* environments. It has two primary tasks:
310Sstevel@tonic-gate  * 	- Allow end users to manage bootability of Newboot Solaris instances
320Sstevel@tonic-gate  *	- Provide services to other subsystems in Solaris (primarily Install)
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /* Headers */
360Sstevel@tonic-gate #include <stdio.h>
370Sstevel@tonic-gate #include <errno.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <string.h>
400Sstevel@tonic-gate #include <unistd.h>
410Sstevel@tonic-gate #include <sys/types.h>
420Sstevel@tonic-gate #include <sys/stat.h>
430Sstevel@tonic-gate #include <stdarg.h>
440Sstevel@tonic-gate #include <limits.h>
450Sstevel@tonic-gate #include <signal.h>
460Sstevel@tonic-gate #include <sys/wait.h>
470Sstevel@tonic-gate #include <sys/mnttab.h>
480Sstevel@tonic-gate #include <sys/statvfs.h>
490Sstevel@tonic-gate #include <libnvpair.h>
500Sstevel@tonic-gate #include <ftw.h>
510Sstevel@tonic-gate #include <fcntl.h>
520Sstevel@tonic-gate #include <strings.h>
53*4581Ssherrym #include <utime.h>
540Sstevel@tonic-gate #include <sys/systeminfo.h>
550Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
562334Ssetje #include <sys/param.h>
57*4581Ssherrym #if defined(__i386)
58*4581Ssherrym #include <sys/ucode.h>
59*4581Ssherrym #endif
600Sstevel@tonic-gate 
610Sstevel@tonic-gate #include <pwd.h>
620Sstevel@tonic-gate #include <grp.h>
630Sstevel@tonic-gate #include <device_info.h>
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #include <locale.h>
660Sstevel@tonic-gate 
670Sstevel@tonic-gate #include <assert.h>
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #include "message.h"
703446Smrj #include "bootadm.h"
710Sstevel@tonic-gate 
720Sstevel@tonic-gate #ifndef TEXT_DOMAIN
730Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
740Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /* Type definitions */
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /* Primary subcmds */
790Sstevel@tonic-gate typedef enum {
800Sstevel@tonic-gate 	BAM_MENU = 3,
810Sstevel@tonic-gate 	BAM_ARCHIVE
820Sstevel@tonic-gate } subcmd_t;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate typedef enum {
850Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
860Sstevel@tonic-gate     OPT_REQ,		/* option required */
870Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
880Sstevel@tonic-gate } option_t;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate typedef struct {
910Sstevel@tonic-gate 	char	*subcmd;
920Sstevel@tonic-gate 	option_t option;
930Sstevel@tonic-gate 	error_t (*handler)();
942115Svikram 	int	unpriv;			/* is this an unprivileged command */
950Sstevel@tonic-gate } subcmd_defn_t;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
980Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
990Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
1020Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
1030Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1040Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
105621Svikram #define	STUBBOOT		"/stubboot"
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate /* lock related */
1080Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1090Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate #define	CREATE_RAMDISK		"/boot/solaris/bin/create_ramdisk"
1120Sstevel@tonic-gate #define	CREATE_DISKMAP		"/boot/solaris/bin/create_diskmap"
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;
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate /* function prototypes */
2040Sstevel@tonic-gate static void parse_args_internal(int argc, char *argv[]);
2050Sstevel@tonic-gate static void parse_args(int argc, char *argv[]);
2060Sstevel@tonic-gate static error_t bam_menu(char *subcmd, char *opt, int argc, char *argv[]);
2070Sstevel@tonic-gate static error_t bam_archive(char *subcmd, char *opt);
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate static void bam_print(char *format, ...);
2100Sstevel@tonic-gate static void bam_exit(int excode);
2110Sstevel@tonic-gate static void bam_lock(void);
2120Sstevel@tonic-gate static void bam_unlock(void);
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate static int exec_cmd(char *cmdline, char *output, int64_t osize);
2150Sstevel@tonic-gate static error_t read_globals(menu_t *mp, char *menu_path,
2160Sstevel@tonic-gate     char *globalcmd, int quiet);
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate static menu_t *menu_read(char *menu_path);
2190Sstevel@tonic-gate static error_t menu_write(char *root, menu_t *mp);
2200Sstevel@tonic-gate static void linelist_free(line_t *start);
2210Sstevel@tonic-gate static void menu_free(menu_t *mp);
2220Sstevel@tonic-gate static void line_free(line_t *lp);
2230Sstevel@tonic-gate static void filelist_free(filelist_t *flistp);
2240Sstevel@tonic-gate static error_t list2file(char *root, char *tmp,
2250Sstevel@tonic-gate     char *final, line_t *start);
2260Sstevel@tonic-gate static error_t list_entry(menu_t *mp, char *menu_path, char *opt);
2270Sstevel@tonic-gate static error_t delete_all_entries(menu_t *mp, char *menu_path, char *opt);
2280Sstevel@tonic-gate static error_t update_entry(menu_t *mp, char *root, char *opt);
2290Sstevel@tonic-gate static error_t update_temp(menu_t *mp, char *root, char *opt);
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate static error_t update_archive(char *root, char *opt);
2320Sstevel@tonic-gate static error_t list_archive(char *root, char *opt);
2330Sstevel@tonic-gate static error_t update_all(char *root, char *opt);
2340Sstevel@tonic-gate static error_t read_list(char *root, filelist_t  *flistp);
2350Sstevel@tonic-gate static error_t set_global(menu_t *mp, char *globalcmd, int val);
2360Sstevel@tonic-gate static error_t set_option(menu_t *mp, char *globalcmd, char *opt);
2373446Smrj static error_t set_kernel(menu_t *mp, menu_cmd_t optnum, char *path,
2383446Smrj     char *buf, size_t bufsize);
2393446Smrj static char *expand_path(const char *partial_path);
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate static long s_strtol(char *str);
2420Sstevel@tonic-gate static int s_fputs(char *str, FILE *fp);
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate static char *s_strdup(char *str);
2450Sstevel@tonic-gate static int is_readonly(char *);
2460Sstevel@tonic-gate static int is_amd64(void);
2470Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate #if defined(__sparc)
2500Sstevel@tonic-gate static void sparc_abort(void);
2510Sstevel@tonic-gate #endif
2520Sstevel@tonic-gate 
253*4581Ssherrym #if defined(__i386)
254*4581Ssherrym static void ucode_install();
255*4581Ssherrym #endif
256*4581Ssherrym 
2570Sstevel@tonic-gate /* Menu related sub commands */
2580Sstevel@tonic-gate static subcmd_defn_t menu_subcmds[] = {
2592115Svikram 	"set_option",		OPT_OPTIONAL,	set_option, 0,	/* PUB */
2602115Svikram 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
2612115Svikram 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
2622115Svikram 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
2632115Svikram 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
2643446Smrj 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
2652115Svikram 	NULL,			0,		NULL, 0	/* must be last */
2660Sstevel@tonic-gate };
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate /* Archive related sub commands */
2690Sstevel@tonic-gate static subcmd_defn_t arch_subcmds[] = {
2702115Svikram 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
2712115Svikram 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
2722115Svikram 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
2732115Svikram 	NULL,			0,		NULL, 0	/* must be last */
2740Sstevel@tonic-gate };
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate static struct {
2770Sstevel@tonic-gate 	nvlist_t *new_nvlp;
2780Sstevel@tonic-gate 	nvlist_t *old_nvlp;
2790Sstevel@tonic-gate 	int need_update;
2800Sstevel@tonic-gate } walk_arg;
2810Sstevel@tonic-gate 
2822334Ssetje 
2832345Ssetje struct safefile {
2842334Ssetje 	char *name;
2852334Ssetje 	struct safefile *next;
2862334Ssetje };
2872334Ssetje 
2883446Smrj static struct safefile *safefiles = NULL;
2892334Ssetje #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
2902334Ssetje 
2910Sstevel@tonic-gate static void
2920Sstevel@tonic-gate usage(void)
2930Sstevel@tonic-gate {
2940Sstevel@tonic-gate 	(void) fprintf(stderr, "USAGE:\n");
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	/* archive usage */
2980Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s update-archive [-vn] [-R altroot]\n",
2990Sstevel@tonic-gate 	    prog);
3000Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-archive [-R altroot]\n", prog);
3010Sstevel@tonic-gate #ifndef __sparc
3020Sstevel@tonic-gate 	/* x86 only */
3030Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
3040Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
3050Sstevel@tonic-gate #endif
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate int
3090Sstevel@tonic-gate main(int argc, char *argv[])
3100Sstevel@tonic-gate {
3110Sstevel@tonic-gate 	error_t ret;
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
3140Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
3170Sstevel@tonic-gate 		prog = argv[0];
3180Sstevel@tonic-gate 	} else {
3190Sstevel@tonic-gate 		prog++;
3200Sstevel@tonic-gate 	}
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	/*
3240Sstevel@tonic-gate 	 * Don't depend on caller's umask
3250Sstevel@tonic-gate 	 */
3260Sstevel@tonic-gate 	(void) umask(0022);
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	parse_args(argc, argv);
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate #if defined(__sparc)
3310Sstevel@tonic-gate 	/*
3320Sstevel@tonic-gate 	 * There are only two valid invocations of bootadm
3330Sstevel@tonic-gate 	 * on SPARC:
3340Sstevel@tonic-gate 	 *
3350Sstevel@tonic-gate 	 *	- SPARC diskless server creating boot_archive for i386 clients
3360Sstevel@tonic-gate 	 *	- archive creation call during reboot of a SPARC system
3370Sstevel@tonic-gate 	 *
3380Sstevel@tonic-gate 	 *	The latter should be a NOP
3390Sstevel@tonic-gate 	 */
3400Sstevel@tonic-gate 	if (bam_cmd != BAM_ARCHIVE) {
3410Sstevel@tonic-gate 		sparc_abort();
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate #endif
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	switch (bam_cmd) {
3460Sstevel@tonic-gate 		case BAM_MENU:
3470Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
3480Sstevel@tonic-gate 			break;
3490Sstevel@tonic-gate 		case BAM_ARCHIVE:
3500Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
3510Sstevel@tonic-gate 			break;
3520Sstevel@tonic-gate 		default:
3530Sstevel@tonic-gate 			usage();
3540Sstevel@tonic-gate 			bam_exit(1);
3550Sstevel@tonic-gate 	}
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
3580Sstevel@tonic-gate 		bam_exit(1);
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	bam_unlock();
3610Sstevel@tonic-gate 	return (0);
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate #if defined(__sparc)
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate static void
3670Sstevel@tonic-gate sparc_abort(void)
3680Sstevel@tonic-gate {
3690Sstevel@tonic-gate 	bam_error(NOT_ON_SPARC);
3700Sstevel@tonic-gate 	bam_exit(1);
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate #endif
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate /*
3760Sstevel@tonic-gate  * Equivalence of public and internal commands:
3770Sstevel@tonic-gate  *	update-archive  -- -a update
3780Sstevel@tonic-gate  *	list-archive	-- -a list
3790Sstevel@tonic-gate  *	set-menu	-- -m set_option
3800Sstevel@tonic-gate  *	list-menu	-- -m list_entry
3810Sstevel@tonic-gate  *	update-menu	-- -m update_entry
3820Sstevel@tonic-gate  */
3830Sstevel@tonic-gate static struct cmd_map {
3840Sstevel@tonic-gate 	char *bam_cmdname;
3850Sstevel@tonic-gate 	int bam_cmd;
3860Sstevel@tonic-gate 	char *bam_subcmd;
3870Sstevel@tonic-gate } cmd_map[] = {
3880Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
3890Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
3900Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
3910Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
3920Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
3930Sstevel@tonic-gate 	{ NULL,			0,		NULL}
3940Sstevel@tonic-gate };
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate /*
3970Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
3980Sstevel@tonic-gate  */
3990Sstevel@tonic-gate static void
4000Sstevel@tonic-gate parse_args(int argc, char *argv[])
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 	/* command conforming to the final spec */
4050Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
4060Sstevel@tonic-gate 		/*
4070Sstevel@tonic-gate 		 * Map commands to internal table.
4080Sstevel@tonic-gate 		 */
4090Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
4100Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
4110Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
4120Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
4130Sstevel@tonic-gate 				break;
4140Sstevel@tonic-gate 			}
4150Sstevel@tonic-gate 			cmp++;
4160Sstevel@tonic-gate 		}
4170Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
4180Sstevel@tonic-gate 			usage();
4190Sstevel@tonic-gate 			bam_exit(1);
4200Sstevel@tonic-gate 		}
4210Sstevel@tonic-gate 		argc--;
4220Sstevel@tonic-gate 		argv++;
4230Sstevel@tonic-gate 	}
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	parse_args_internal(argc, argv);
4260Sstevel@tonic-gate }
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate /*
4290Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
4300Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
4310Sstevel@tonic-gate  *	-a update	-- update-archive
4320Sstevel@tonic-gate  *	-a list		-- list-archive
4330Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
4340Sstevel@tonic-gate  *	-m update_entry	-- update-menu
4350Sstevel@tonic-gate  *	-m list_entry	-- list-menu
4360Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
4370Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
4380Sstevel@tonic-gate  */
4390Sstevel@tonic-gate static void
4400Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
4410Sstevel@tonic-gate {
4420Sstevel@tonic-gate 	int c, error;
4430Sstevel@tonic-gate 	extern char *optarg;
4440Sstevel@tonic-gate 	extern int optind, opterr;
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	/* Suppress error message from getopt */
4470Sstevel@tonic-gate 	opterr = 0;
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	error = 0;
4503467Srscott 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:")) != -1) {
4510Sstevel@tonic-gate 		switch (c) {
4520Sstevel@tonic-gate 		case 'a':
4530Sstevel@tonic-gate 			if (bam_cmd) {
4540Sstevel@tonic-gate 				error = 1;
4550Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4560Sstevel@tonic-gate 			}
4570Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
4580Sstevel@tonic-gate 			bam_subcmd = optarg;
4590Sstevel@tonic-gate 			break;
4600Sstevel@tonic-gate 		case 'd':
4610Sstevel@tonic-gate 			if (bam_debug) {
4620Sstevel@tonic-gate 				error = 1;
4630Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4640Sstevel@tonic-gate 			}
4650Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
4660Sstevel@tonic-gate 			break;
4670Sstevel@tonic-gate 		case 'f':
4680Sstevel@tonic-gate 			if (bam_force) {
4690Sstevel@tonic-gate 				error = 1;
4700Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4710Sstevel@tonic-gate 			}
4720Sstevel@tonic-gate 			bam_force = 1;
4730Sstevel@tonic-gate 			break;
4740Sstevel@tonic-gate 		case 'm':
4750Sstevel@tonic-gate 			if (bam_cmd) {
4760Sstevel@tonic-gate 				error = 1;
4770Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4780Sstevel@tonic-gate 			}
4790Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
4800Sstevel@tonic-gate 			bam_subcmd = optarg;
4810Sstevel@tonic-gate 			break;
4820Sstevel@tonic-gate 		case 'n':
4830Sstevel@tonic-gate 			if (bam_check) {
4840Sstevel@tonic-gate 				error = 1;
4850Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4860Sstevel@tonic-gate 			}
4870Sstevel@tonic-gate 			bam_check = 1;
4880Sstevel@tonic-gate 			break;
4890Sstevel@tonic-gate 		case 'o':
4900Sstevel@tonic-gate 			if (bam_opt) {
4910Sstevel@tonic-gate 				error = 1;
4920Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4930Sstevel@tonic-gate 			}
4940Sstevel@tonic-gate 			bam_opt = optarg;
4950Sstevel@tonic-gate 			break;
4960Sstevel@tonic-gate 		case 'v':
4970Sstevel@tonic-gate 			if (bam_verbose) {
4980Sstevel@tonic-gate 				error = 1;
4990Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
5000Sstevel@tonic-gate 			}
5010Sstevel@tonic-gate 			bam_verbose = 1;
5020Sstevel@tonic-gate 			break;
503662Sszhou 		case 'C':
504662Sszhou 			bam_smf_check = 1;
505662Sszhou 			break;
5060Sstevel@tonic-gate 		case 'R':
5070Sstevel@tonic-gate 			if (bam_root) {
5080Sstevel@tonic-gate 				error = 1;
5090Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
5100Sstevel@tonic-gate 				break;
5110Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
5120Sstevel@tonic-gate 				error = 1;
5130Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
5140Sstevel@tonic-gate 				    strerror(errno));
5150Sstevel@tonic-gate 				break;
5160Sstevel@tonic-gate 			}
517621Svikram 			bam_alt_root = 1;
5180Sstevel@tonic-gate 			bam_root = rootbuf;
5190Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
5200Sstevel@tonic-gate 			break;
5210Sstevel@tonic-gate 		case '?':
5220Sstevel@tonic-gate 			error = 1;
5230Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
5240Sstevel@tonic-gate 			break;
5250Sstevel@tonic-gate 		default :
5260Sstevel@tonic-gate 			error = 1;
5270Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
5280Sstevel@tonic-gate 			break;
5290Sstevel@tonic-gate 		}
5300Sstevel@tonic-gate 	}
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 	/*
5330Sstevel@tonic-gate 	 * A command option must be specfied
5340Sstevel@tonic-gate 	 */
5350Sstevel@tonic-gate 	if (!bam_cmd) {
5360Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
5370Sstevel@tonic-gate 			usage();
5380Sstevel@tonic-gate 			bam_exit(0);
5390Sstevel@tonic-gate 		}
5400Sstevel@tonic-gate 		bam_error(NEED_CMD);
5410Sstevel@tonic-gate 		error = 1;
5420Sstevel@tonic-gate 	}
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	if (error) {
5450Sstevel@tonic-gate 		usage();
5460Sstevel@tonic-gate 		bam_exit(1);
5470Sstevel@tonic-gate 	}
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	if (optind > argc) {
5500Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
5510Sstevel@tonic-gate 		bam_exit(1);
5520Sstevel@tonic-gate 	} else if (optind < argc) {
5530Sstevel@tonic-gate 		bam_argv = &argv[optind];
5540Sstevel@tonic-gate 		bam_argc = argc - optind;
5550Sstevel@tonic-gate 	}
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	/*
5580Sstevel@tonic-gate 	 * -n implies verbose mode
5590Sstevel@tonic-gate 	 */
5600Sstevel@tonic-gate 	if (bam_check)
5610Sstevel@tonic-gate 		bam_verbose = 1;
5620Sstevel@tonic-gate }
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate static error_t
5650Sstevel@tonic-gate check_subcmd_and_options(
5660Sstevel@tonic-gate 	char *subcmd,
5670Sstevel@tonic-gate 	char *opt,
5680Sstevel@tonic-gate 	subcmd_defn_t *table,
5690Sstevel@tonic-gate 	error_t (**fp)())
5700Sstevel@tonic-gate {
5710Sstevel@tonic-gate 	int i;
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	if (subcmd == NULL) {
5740Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
5750Sstevel@tonic-gate 		return (BAM_ERROR);
5760Sstevel@tonic-gate 	}
5770Sstevel@tonic-gate 
5782115Svikram 	if (bam_argc != 0 || bam_argv) {
5792115Svikram 		if (strcmp(subcmd, "set_option") != 0 || bam_argc != 1) {
5802115Svikram 			bam_error(TRAILING_ARGS);
5812115Svikram 			usage();
5822115Svikram 			return (BAM_ERROR);
5832115Svikram 		}
5842115Svikram 	}
5852115Svikram 
5860Sstevel@tonic-gate 	if (bam_root == NULL) {
5870Sstevel@tonic-gate 		bam_root = rootbuf;
5880Sstevel@tonic-gate 		bam_rootlen = 1;
5890Sstevel@tonic-gate 	}
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	/* verify that subcmd is valid */
5920Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
5930Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
5940Sstevel@tonic-gate 			break;
5950Sstevel@tonic-gate 	}
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
5980Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
5990Sstevel@tonic-gate 		return (BAM_ERROR);
6000Sstevel@tonic-gate 	}
6010Sstevel@tonic-gate 
6022115Svikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
6032115Svikram 		bam_error(MUST_BE_ROOT);
6042115Svikram 		return (BAM_ERROR);
6052115Svikram 	}
6062115Svikram 
6072115Svikram 	/*
6082115Svikram 	 * Currently only privileged commands need a lock
6092115Svikram 	 */
6102115Svikram 	if (table[i].unpriv == 0)
6112115Svikram 		bam_lock();
6122115Svikram 
6130Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
6140Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
6150Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
6160Sstevel@tonic-gate 			if (opt)
6170Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
6180Sstevel@tonic-gate 			else
6190Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
6200Sstevel@tonic-gate 			return (BAM_ERROR);
6210Sstevel@tonic-gate 		}
6220Sstevel@tonic-gate 	}
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	*fp = table[i].handler;
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	return (BAM_SUCCESS);
6270Sstevel@tonic-gate }
6280Sstevel@tonic-gate 
629316Svikram 
630316Svikram static char *
631621Svikram mount_grub_slice(int *mnted, char **physlice, char **logslice, char **fs_type)
632316Svikram {
633316Svikram 	struct extmnttab mnt;
634316Svikram 	struct stat sb;
635316Svikram 	char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32];
636348Svikram 	char cmd[PATH_MAX];
637316Svikram 	char *mntpt;
638316Svikram 	int p, l, f;
639316Svikram 	FILE *fp;
640316Svikram 
641316Svikram 	assert(mnted);
642316Svikram 	*mnted = 0;
643316Svikram 
644316Svikram 	/*
645621Svikram 	 * physlice, logslice, fs_type  args may be NULL
646316Svikram 	 */
647316Svikram 	if (physlice)
648316Svikram 		*physlice = NULL;
649621Svikram 	if (logslice)
650621Svikram 		*logslice = NULL;
651621Svikram 	if (fs_type)
652621Svikram 		*fs_type = NULL;
653316Svikram 
654316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
655316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
656316Svikram 		return (NULL);
657316Svikram 	}
658316Svikram 
659316Svikram 	fp = fopen(GRUB_slice, "r");
660316Svikram 	if (fp == NULL) {
661316Svikram 		bam_error(OPEN_FAIL, GRUB_slice, strerror(errno));
662316Svikram 		return (NULL);
663316Svikram 	}
664316Svikram 
665316Svikram 	dev[0] = fstype[0] = phys[0] = '\0';
666316Svikram 	p = sizeof ("PHYS_SLICE=") - 1;
667316Svikram 	l = sizeof ("LOG_SLICE=") - 1;
668316Svikram 	f = sizeof ("LOG_FSTYP=") - 1;
669316Svikram 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
670316Svikram 		if (strncmp(buf, "PHYS_SLICE=", p) == 0) {
671316Svikram 			(void) strlcpy(phys, buf + p, sizeof (phys));
672316Svikram 			continue;
673316Svikram 		}
674316Svikram 		if (strncmp(buf, "LOG_SLICE=", l) == 0) {
675316Svikram 			(void) strlcpy(dev, buf + l, sizeof (dev));
676316Svikram 			continue;
677316Svikram 		}
678316Svikram 		if (strncmp(buf, "LOG_FSTYP=", f) == 0) {
679316Svikram 			(void) strlcpy(fstype, buf + f, sizeof (fstype));
680316Svikram 			continue;
681316Svikram 		}
682316Svikram 	}
683316Svikram 	(void) fclose(fp);
684316Svikram 
685316Svikram 	if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') {
686316Svikram 		bam_error(BAD_SLICE_FILE, GRUB_slice);
687316Svikram 		return (NULL);
688316Svikram 	}
689316Svikram 
690316Svikram 	if (physlice) {
691316Svikram 		*physlice = s_strdup(phys);
692316Svikram 	}
693621Svikram 	if (logslice) {
694621Svikram 		*logslice = s_strdup(dev);
695621Svikram 	}
696621Svikram 	if (fs_type) {
697621Svikram 		*fs_type = s_strdup(fstype);
698621Svikram 	}
699316Svikram 
700316Svikram 	/*
701316Svikram 	 * Check if the slice is already mounted
702316Svikram 	 */
703316Svikram 	fp = fopen(MNTTAB, "r");
704316Svikram 	if (fp == NULL) {
705316Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
706621Svikram 		goto error;
707316Svikram 	}
708316Svikram 
709316Svikram 	resetmnttab(fp);
710316Svikram 
711316Svikram 	mntpt = NULL;
712316Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
713316Svikram 		if (strcmp(mnt.mnt_special, dev) == 0) {
714316Svikram 			mntpt = s_strdup(mnt.mnt_mountp);
715316Svikram 			break;
716316Svikram 		}
717316Svikram 	}
718316Svikram 
719316Svikram 	(void) fclose(fp);
720316Svikram 
721316Svikram 	if (mntpt) {
722316Svikram 		return (mntpt);
723316Svikram 	}
724316Svikram 
725316Svikram 
726316Svikram 	/*
727316Svikram 	 * GRUB slice is not mounted, we need to mount it now.
728316Svikram 	 * First create the mountpoint
729316Svikram 	 */
730316Svikram 	mntpt = s_calloc(1, PATH_MAX);
731316Svikram 	(void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid());
732316Svikram 	if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) {
733316Svikram 		bam_error(MKDIR_FAILED, mntpt, strerror(errno));
734316Svikram 		free(mntpt);
735621Svikram 		goto error;
736316Svikram 	}
737316Svikram 
738348Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount -F %s %s %s",
739348Svikram 	    fstype, dev, mntpt);
740348Svikram 
741348Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
742621Svikram 		bam_error(MOUNT_FAILED, dev, fstype);
743316Svikram 		if (rmdir(mntpt) != 0) {
744316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
745316Svikram 		}
746316Svikram 		free(mntpt);
747621Svikram 		goto error;
748316Svikram 	}
749316Svikram 
750316Svikram 	*mnted = 1;
751316Svikram 	return (mntpt);
752621Svikram 
753621Svikram error:
754621Svikram 	if (physlice) {
755621Svikram 		free(*physlice);
756621Svikram 		*physlice = NULL;
757621Svikram 	}
758621Svikram 	if (logslice) {
759621Svikram 		free(*logslice);
760621Svikram 		*logslice = NULL;
761621Svikram 	}
762621Svikram 	if (fs_type) {
763621Svikram 		free(*fs_type);
764621Svikram 		*fs_type = NULL;
765621Svikram 	}
766621Svikram 	return (NULL);
767316Svikram }
768316Svikram 
769316Svikram static void
770621Svikram umount_grub_slice(
771621Svikram 	int mnted,
772621Svikram 	char *mntpt,
773621Svikram 	char *physlice,
774621Svikram 	char *logslice,
775621Svikram 	char *fs_type)
776316Svikram {
777348Svikram 	char cmd[PATH_MAX];
778348Svikram 
779316Svikram 	/*
780316Svikram 	 * If we have not dealt with GRUB slice
781316Svikram 	 * we have nothing to do - just return.
782316Svikram 	 */
783316Svikram 	if (mntpt == NULL)
784316Svikram 		return;
785316Svikram 
786316Svikram 
787316Svikram 	/*
788316Svikram 	 * If we mounted the filesystem earlier in mount_grub_slice()
789316Svikram 	 * unmount it now.
790316Svikram 	 */
791316Svikram 	if (mnted) {
792348Svikram 		(void) snprintf(cmd, sizeof (cmd), "/sbin/umount %s",
793348Svikram 		    mntpt);
794348Svikram 		if (exec_cmd(cmd, NULL, 0) != 0) {
795348Svikram 			bam_error(UMOUNT_FAILED, mntpt);
796316Svikram 		}
797316Svikram 		if (rmdir(mntpt) != 0) {
798316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
799316Svikram 		}
800316Svikram 	}
801621Svikram 
802316Svikram 	if (physlice)
803316Svikram 		free(physlice);
804621Svikram 	if (logslice)
805621Svikram 		free(logslice);
806621Svikram 	if (fs_type)
807621Svikram 		free(fs_type);
808621Svikram 
809316Svikram 	free(mntpt);
810316Svikram }
811316Svikram 
812621Svikram static char *
813621Svikram use_stubboot(void)
814621Svikram {
815621Svikram 	int mnted;
816621Svikram 	struct stat sb;
817621Svikram 	struct extmnttab mnt;
818621Svikram 	FILE *fp;
819621Svikram 	char cmd[PATH_MAX];
820621Svikram 
821621Svikram 	if (stat(STUBBOOT, &sb) != 0) {
822621Svikram 		bam_error(STUBBOOT_DIR_NOT_FOUND);
823621Svikram 		return (NULL);
824621Svikram 	}
825621Svikram 
826621Svikram 	/*
827621Svikram 	 * Check if stubboot is mounted. If not, mount it
828621Svikram 	 */
829621Svikram 	fp = fopen(MNTTAB, "r");
830621Svikram 	if (fp == NULL) {
831621Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
832621Svikram 		return (NULL);
833621Svikram 	}
834621Svikram 
835621Svikram 	resetmnttab(fp);
836621Svikram 
837621Svikram 	mnted = 0;
838621Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
839621Svikram 		if (strcmp(mnt.mnt_mountp, STUBBOOT) == 0) {
840621Svikram 			mnted = 1;
841621Svikram 			break;
842621Svikram 		}
843621Svikram 	}
844621Svikram 
845621Svikram 	(void) fclose(fp);
846621Svikram 
847621Svikram 	if (mnted)
848621Svikram 		return (STUBBOOT);
849621Svikram 
850621Svikram 	/*
851621Svikram 	 * Stubboot is not mounted, mount it now.
852621Svikram 	 * It should exist in /etc/vfstab
853621Svikram 	 */
854621Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount %s",
855621Svikram 	    STUBBOOT);
856621Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
857621Svikram 		bam_error(MOUNT_MNTPT_FAILED, STUBBOOT);
858621Svikram 		return (NULL);
859621Svikram 	}
860621Svikram 
861621Svikram 	return (STUBBOOT);
862621Svikram }
863621Svikram 
864621Svikram static void
865621Svikram disp_active_menu_locn(char *menu_path, char *logslice, char *fstype, int mnted)
866621Svikram {
867621Svikram 	/*
868621Svikram 	 * Check if we did a temp mount of an unmounted device.
869621Svikram 	 * If yes, print the block device and fstype for that device
870621Svikram 	 * else it is already mounted, so we print the path to the GRUB menu.
871621Svikram 	 */
872621Svikram 	if (mnted) {
873621Svikram 		bam_print(GRUB_MENU_DEVICE, logslice);
874621Svikram 		bam_print(GRUB_MENU_FSTYPE, fstype);
875621Svikram 	} else {
876621Svikram 		bam_print(GRUB_MENU_PATH, menu_path);
877621Svikram 	}
878621Svikram }
879621Svikram 
880621Svikram /*
881621Svikram  * NOTE: A single "/" is also considered a trailing slash and will
882621Svikram  * be deleted.
883621Svikram  */
884621Svikram static void
885621Svikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
886621Svikram {
887621Svikram 	size_t dstlen;
888621Svikram 
889621Svikram 	assert(src);
890621Svikram 	assert(dst);
891621Svikram 
892621Svikram 	(void) strlcpy(dst, src, dstsize);
893621Svikram 
894621Svikram 	dstlen = strlen(dst);
895621Svikram 	if (dst[dstlen - 1] == '/') {
896621Svikram 		dst[dstlen - 1] = '\0';
897621Svikram 	}
898621Svikram }
899621Svikram 
9000Sstevel@tonic-gate static error_t
9010Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
9020Sstevel@tonic-gate {
9030Sstevel@tonic-gate 	error_t ret;
9040Sstevel@tonic-gate 	char menu_path[PATH_MAX];
9053446Smrj 	char path[PATH_MAX];
9060Sstevel@tonic-gate 	menu_t *menu;
907621Svikram 	char *mntpt, *menu_root, *logslice, *fstype;
908316Svikram 	struct stat sb;
909316Svikram 	int mnted;	/* set if we did a mount */
9100Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
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 
10360Sstevel@tonic-gate #if defined(__sparc)
10370Sstevel@tonic-gate 	/*
10380Sstevel@tonic-gate 	 * A NOP if called on SPARC during reboot
10390Sstevel@tonic-gate 	 */
10400Sstevel@tonic-gate 	if (strcmp(subcmd, "update_all") == 0)
10410Sstevel@tonic-gate 		return (BAM_SUCCESS);
10420Sstevel@tonic-gate 	else if (strcmp(subcmd, "update") != 0)
10430Sstevel@tonic-gate 		sparc_abort();
10440Sstevel@tonic-gate #endif
10450Sstevel@tonic-gate 
10463446Smrj 	ret = dboot_or_multiboot(rootbuf);
10473446Smrj 	if (ret != BAM_SUCCESS)
10483446Smrj 		return (ret);
10493446Smrj 
10500Sstevel@tonic-gate 	/*
10510Sstevel@tonic-gate 	 * Check archive not supported with update_all
10520Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
10530Sstevel@tonic-gate 	 * information for each BE.
10540Sstevel@tonic-gate 	 */
10550Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
10560Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
10570Sstevel@tonic-gate 		return (BAM_ERROR);
10580Sstevel@tonic-gate 	}
10590Sstevel@tonic-gate 
1060316Svikram 	if (strcmp(subcmd, "update_all") == 0)
1061316Svikram 		bam_update_all = 1;
1062316Svikram 
1063*4581Ssherrym #if defined(__i386)
1064*4581Ssherrym 	ucode_install(bam_root);
1065*4581Ssherrym #endif
1066*4581Ssherrym 
1067316Svikram 	ret = f(bam_root, opt);
1068316Svikram 
1069316Svikram 	bam_update_all = 0;
1070316Svikram 
1071316Svikram 	return (ret);
10720Sstevel@tonic-gate }
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate /*PRINTFLIKE1*/
10753446Smrj void
10760Sstevel@tonic-gate bam_error(char *format, ...)
10770Sstevel@tonic-gate {
10780Sstevel@tonic-gate 	va_list ap;
10790Sstevel@tonic-gate 
10800Sstevel@tonic-gate 	va_start(ap, format);
10810Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
10820Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
10830Sstevel@tonic-gate 	va_end(ap);
10840Sstevel@tonic-gate }
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate /*PRINTFLIKE1*/
10870Sstevel@tonic-gate static void
10880Sstevel@tonic-gate bam_print(char *format, ...)
10890Sstevel@tonic-gate {
10900Sstevel@tonic-gate 	va_list ap;
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 	va_start(ap, format);
10930Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
10940Sstevel@tonic-gate 	va_end(ap);
10950Sstevel@tonic-gate }
10960Sstevel@tonic-gate 
10973446Smrj /*PRINTFLIKE1*/
10983446Smrj void
10993446Smrj bam_print_stderr(char *format, ...)
11003446Smrj {
11013446Smrj 	va_list ap;
11023446Smrj 
11033446Smrj 	va_start(ap, format);
11043446Smrj 	(void) vfprintf(stderr, format, ap);
11053446Smrj 	va_end(ap);
11063446Smrj }
11073446Smrj 
11080Sstevel@tonic-gate static void
11090Sstevel@tonic-gate bam_exit(int excode)
11100Sstevel@tonic-gate {
11110Sstevel@tonic-gate 	bam_unlock();
11120Sstevel@tonic-gate 	exit(excode);
11130Sstevel@tonic-gate }
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate static void
11160Sstevel@tonic-gate bam_lock(void)
11170Sstevel@tonic-gate {
11180Sstevel@tonic-gate 	struct flock lock;
11190Sstevel@tonic-gate 	pid_t pid;
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
11220Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11230Sstevel@tonic-gate 		/*
11240Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
11250Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
11260Sstevel@tonic-gate 		 * Proceed without the lock
11270Sstevel@tonic-gate 		 */
11280Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
11290Sstevel@tonic-gate 			bam_root_readonly = 1;
11300Sstevel@tonic-gate 			return;
11310Sstevel@tonic-gate 		}
11320Sstevel@tonic-gate 
11330Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
11340Sstevel@tonic-gate 		bam_exit(1);
11350Sstevel@tonic-gate 	}
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
11380Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
11390Sstevel@tonic-gate 	lock.l_start = 0;
11400Sstevel@tonic-gate 	lock.l_len = 0;
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
11430Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
11440Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11450Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11460Sstevel@tonic-gate 			bam_lock_fd = -1;
11470Sstevel@tonic-gate 			bam_exit(1);
11480Sstevel@tonic-gate 		}
11490Sstevel@tonic-gate 		pid = 0;
11500Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
11510Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
11540Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
11550Sstevel@tonic-gate 		lock.l_start = 0;
11560Sstevel@tonic-gate 		lock.l_len = 0;
11570Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
11580Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11590Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11600Sstevel@tonic-gate 			bam_lock_fd = -1;
11610Sstevel@tonic-gate 			bam_exit(1);
11620Sstevel@tonic-gate 		}
11630Sstevel@tonic-gate 	}
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 	/* We own the lock now */
11660Sstevel@tonic-gate 	pid = getpid();
11670Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
11680Sstevel@tonic-gate }
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate static void
11710Sstevel@tonic-gate bam_unlock(void)
11720Sstevel@tonic-gate {
11730Sstevel@tonic-gate 	struct flock unlock;
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 	/*
11760Sstevel@tonic-gate 	 * NOP if we don't hold the lock
11770Sstevel@tonic-gate 	 */
11780Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11790Sstevel@tonic-gate 		return;
11800Sstevel@tonic-gate 	}
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
11830Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
11840Sstevel@tonic-gate 	unlock.l_start = 0;
11850Sstevel@tonic-gate 	unlock.l_len = 0;
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
11880Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11890Sstevel@tonic-gate 	}
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
11920Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
11930Sstevel@tonic-gate 	}
11940Sstevel@tonic-gate 	bam_lock_fd = -1;
11950Sstevel@tonic-gate }
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate static error_t
11980Sstevel@tonic-gate list_archive(char *root, char *opt)
11990Sstevel@tonic-gate {
12000Sstevel@tonic-gate 	filelist_t flist;
12010Sstevel@tonic-gate 	filelist_t *flistp = &flist;
12020Sstevel@tonic-gate 	line_t *lp;
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	assert(root);
12050Sstevel@tonic-gate 	assert(opt == NULL);
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
12080Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
12090Sstevel@tonic-gate 		return (BAM_ERROR);
12100Sstevel@tonic-gate 	}
12110Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
12140Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
12150Sstevel@tonic-gate 	}
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 	filelist_free(flistp);
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate 	return (BAM_SUCCESS);
12200Sstevel@tonic-gate }
12210Sstevel@tonic-gate 
12220Sstevel@tonic-gate /*
12230Sstevel@tonic-gate  * This routine writes a list of lines to a file.
12240Sstevel@tonic-gate  * The list is *not* freed
12250Sstevel@tonic-gate  */
12260Sstevel@tonic-gate static error_t
12270Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
12280Sstevel@tonic-gate {
12290Sstevel@tonic-gate 	char tmpfile[PATH_MAX];
12300Sstevel@tonic-gate 	char path[PATH_MAX];
12310Sstevel@tonic-gate 	FILE *fp;
12320Sstevel@tonic-gate 	int ret;
12330Sstevel@tonic-gate 	struct stat sb;
12340Sstevel@tonic-gate 	mode_t mode;
12350Sstevel@tonic-gate 	uid_t root_uid;
12360Sstevel@tonic-gate 	gid_t sys_gid;
12370Sstevel@tonic-gate 	struct passwd *pw;
12380Sstevel@tonic-gate 	struct group *gp;
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	if (start == NULL) {
12440Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
12450Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
12460Sstevel@tonic-gate 			if (unlink(path) != 0) {
12470Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
12480Sstevel@tonic-gate 				return (BAM_ERROR);
12490Sstevel@tonic-gate 			} else {
12500Sstevel@tonic-gate 				return (BAM_SUCCESS);
12510Sstevel@tonic-gate 			}
12520Sstevel@tonic-gate 		}
12530Sstevel@tonic-gate 	}
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate 	/*
12560Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
12570Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
12580Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
12590Sstevel@tonic-gate 	 */
12600Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
12610Sstevel@tonic-gate 		mode = sb.st_mode;
12620Sstevel@tonic-gate 		root_uid = sb.st_uid;
12630Sstevel@tonic-gate 		sys_gid = sb.st_gid;
12640Sstevel@tonic-gate 	} else {
12650Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
12660Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
12670Sstevel@tonic-gate 			root_uid = pw->pw_uid;
12680Sstevel@tonic-gate 		} else {
12690Sstevel@tonic-gate 			if (bam_verbose)
12700Sstevel@tonic-gate 				bam_error(CANT_FIND_USER,
12710Sstevel@tonic-gate 				    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
12720Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
12730Sstevel@tonic-gate 		}
12740Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
12750Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
12760Sstevel@tonic-gate 		} else {
12770Sstevel@tonic-gate 			if (bam_verbose)
12780Sstevel@tonic-gate 				bam_error(CANT_FIND_GROUP,
12790Sstevel@tonic-gate 				    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
12800Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
12810Sstevel@tonic-gate 		}
12820Sstevel@tonic-gate 	}
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
12850Sstevel@tonic-gate 
12860Sstevel@tonic-gate 	/* Truncate tmpfile first */
12870Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
12880Sstevel@tonic-gate 	if (fp == NULL) {
12890Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12900Sstevel@tonic-gate 		return (BAM_ERROR);
12910Sstevel@tonic-gate 	}
12920Sstevel@tonic-gate 	ret = fclose(fp);
12930Sstevel@tonic-gate 	if (ret == EOF) {
12940Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12950Sstevel@tonic-gate 		return (BAM_ERROR);
12960Sstevel@tonic-gate 	}
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 	/* Now open it in append mode */
12990Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
13000Sstevel@tonic-gate 	if (fp == NULL) {
13010Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
13020Sstevel@tonic-gate 		return (BAM_ERROR);
13030Sstevel@tonic-gate 	}
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 	for (; start; start = start->next) {
13060Sstevel@tonic-gate 		ret = s_fputs(start->line, fp);
13070Sstevel@tonic-gate 		if (ret == EOF) {
13080Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
13090Sstevel@tonic-gate 			(void) fclose(fp);
13100Sstevel@tonic-gate 			return (BAM_ERROR);
13110Sstevel@tonic-gate 		}
13120Sstevel@tonic-gate 	}
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate 	ret = fclose(fp);
13150Sstevel@tonic-gate 	if (ret == EOF) {
13160Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
13170Sstevel@tonic-gate 		return (BAM_ERROR);
13180Sstevel@tonic-gate 	}
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	/*
1321271Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1322271Sjg 	 * not supporting these operations - pcfs reports unsupported
1323271Sjg 	 * operations as EINVAL.
13240Sstevel@tonic-gate 	 */
13250Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1326271Sjg 	if (ret == -1 &&
1327271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13280Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
13290Sstevel@tonic-gate 		return (BAM_ERROR);
13300Sstevel@tonic-gate 	}
13310Sstevel@tonic-gate 
13320Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1333271Sjg 	if (ret == -1 &&
1334271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13350Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
13360Sstevel@tonic-gate 		return (BAM_ERROR);
13370Sstevel@tonic-gate 	}
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate 	/*
13410Sstevel@tonic-gate 	 * Do an atomic rename
13420Sstevel@tonic-gate 	 */
13430Sstevel@tonic-gate 	ret = rename(tmpfile, path);
13440Sstevel@tonic-gate 	if (ret != 0) {
13450Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
13460Sstevel@tonic-gate 		return (BAM_ERROR);
13470Sstevel@tonic-gate 	}
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate 	return (BAM_SUCCESS);
13500Sstevel@tonic-gate }
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate /*
13530Sstevel@tonic-gate  * This function should always return 0 - since we want
13540Sstevel@tonic-gate  * to create stat data for *all* files in the list.
13550Sstevel@tonic-gate  */
13560Sstevel@tonic-gate /*ARGSUSED*/
13570Sstevel@tonic-gate static int
13580Sstevel@tonic-gate cmpstat(
13590Sstevel@tonic-gate 	const char *file,
13600Sstevel@tonic-gate 	const struct stat *stat,
13610Sstevel@tonic-gate 	int flags,
13620Sstevel@tonic-gate 	struct FTW *ftw)
13630Sstevel@tonic-gate {
13640Sstevel@tonic-gate 	uint_t sz;
13650Sstevel@tonic-gate 	uint64_t *value;
13660Sstevel@tonic-gate 	uint64_t filestat[2];
13670Sstevel@tonic-gate 	int error;
13680Sstevel@tonic-gate 
13692334Ssetje 	struct safefile *safefilep;
13702334Ssetje 	FILE *fp;
13712334Ssetje 
13720Sstevel@tonic-gate 	/*
13730Sstevel@tonic-gate 	 * We only want regular files
13740Sstevel@tonic-gate 	 */
13750Sstevel@tonic-gate 	if (!S_ISREG(stat->st_mode))
13760Sstevel@tonic-gate 		return (0);
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	/*
13790Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
13800Sstevel@tonic-gate 	 * but this is not fatal to update determination.
13810Sstevel@tonic-gate 	 */
13820Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
13830Sstevel@tonic-gate 		filestat[0] = stat->st_size;
13840Sstevel@tonic-gate 		filestat[1] = stat->st_mtime;
13850Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
13860Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
13870Sstevel@tonic-gate 		if (error)
13880Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
13890Sstevel@tonic-gate 	}
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 	/*
13920Sstevel@tonic-gate 	 * The remaining steps are only required if we haven't made a
13930Sstevel@tonic-gate 	 * decision about update or if we are checking (-n)
13940Sstevel@tonic-gate 	 */
13950Sstevel@tonic-gate 	if (walk_arg.need_update && !bam_check)
13960Sstevel@tonic-gate 		return (0);
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate 	/*
13992334Ssetje 	 * If we are invoked as part of system/filesyste/boot-archive, then
14002334Ssetje 	 * there are a number of things we should not worry about
14010Sstevel@tonic-gate 	 */
14022334Ssetje 	if (bam_smf_check) {
14032334Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
14042334Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
14052334Ssetje 			return (0);
14062334Ssetje 
14072334Ssetje 		/* read in list of safe files */
14082334Ssetje 		if (safefiles == NULL)
14092334Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
14102334Ssetje 				safefiles = s_calloc(1,
14112334Ssetje 				    sizeof (struct safefile));
14122334Ssetje 				safefilep = safefiles;
14132334Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
14142334Ssetje 				    MAXNAMELEN);
14152334Ssetje 				safefilep->next = NULL;
14162334Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
14172334Ssetje 				    MAXNAMELEN, fp) != NULL) {
14182334Ssetje 					safefilep->next = s_calloc(1,
14192334Ssetje 					    sizeof (struct safefile));
14202334Ssetje 					safefilep = safefilep->next;
14212334Ssetje 					safefilep->name = s_calloc(1,
14222334Ssetje 					    MAXPATHLEN + MAXNAMELEN);
14232334Ssetje 					safefilep->next = NULL;
14242334Ssetje 				}
14252334Ssetje 				(void) fclose(fp);
14262334Ssetje 			}
14272334Ssetje 	}
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 	/*
14300Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
14310Sstevel@tonic-gate 	 */
14320Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
14330Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
14340Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
14350Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
14360Sstevel@tonic-gate 			return (0);
14370Sstevel@tonic-gate 		walk_arg.need_update = 1;
14380Sstevel@tonic-gate 		if (bam_verbose)
14390Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
14400Sstevel@tonic-gate 		return (0);
14410Sstevel@tonic-gate 	}
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	/*
14440Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
14450Sstevel@tonic-gate 	 */
14460Sstevel@tonic-gate 	assert(sz == 2);
14470Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate 	if (filestat[0] != stat->st_size ||
14500Sstevel@tonic-gate 	    filestat[1] != stat->st_mtime) {
14513615Ssetje 		if (bam_smf_check) {
14523615Ssetje 			safefilep = safefiles;
14533615Ssetje 			while (safefilep != NULL) {
14543615Ssetje 				if (strcmp(file + bam_rootlen,
14553615Ssetje 				    safefilep->name) == 0) {
14563615Ssetje 					(void) creat(NEED_UPDATE_FILE, 0644);
14573615Ssetje 					return (0);
14583615Ssetje 				}
14593615Ssetje 				safefilep = safefilep->next;
14603615Ssetje 			}
14613615Ssetje 		}
14620Sstevel@tonic-gate 		walk_arg.need_update = 1;
14630Sstevel@tonic-gate 		if (bam_verbose)
14640Sstevel@tonic-gate 			if (bam_smf_check)
14650Sstevel@tonic-gate 				bam_print("    %s\n", file);
14660Sstevel@tonic-gate 			else
14670Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
14680Sstevel@tonic-gate 	}
14690Sstevel@tonic-gate 
14700Sstevel@tonic-gate 	return (0);
14710Sstevel@tonic-gate }
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate /*
14740Sstevel@tonic-gate  * Check flags and presence of required files.
14750Sstevel@tonic-gate  * The force flag and/or absence of files should
14760Sstevel@tonic-gate  * trigger an update.
14770Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
14780Sstevel@tonic-gate  * (as -n should only produce parseable output.)
14790Sstevel@tonic-gate  */
14800Sstevel@tonic-gate static void
14810Sstevel@tonic-gate check_flags_and_files(char *root)
14820Sstevel@tonic-gate {
14830Sstevel@tonic-gate 	char path[PATH_MAX];
14840Sstevel@tonic-gate 	struct stat sb;
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate 	/*
14870Sstevel@tonic-gate 	 * if force, create archive unconditionally
14880Sstevel@tonic-gate 	 */
14890Sstevel@tonic-gate 	if (bam_force) {
14900Sstevel@tonic-gate 		walk_arg.need_update = 1;
14910Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
14920Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
14930Sstevel@tonic-gate 		return;
14940Sstevel@tonic-gate 	}
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 	/*
14970Sstevel@tonic-gate 	 * If archive is missing, create archive
14980Sstevel@tonic-gate 	 */
14993446Smrj 	(void) snprintf(path, sizeof (path), "%s%s", root,
15003446Smrj 	    DIRECT_BOOT_ARCHIVE_32);
15010Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
15020Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
15030Sstevel@tonic-gate 			bam_print(UPDATE_ARCH_MISS, path);
15040Sstevel@tonic-gate 		walk_arg.need_update = 1;
15050Sstevel@tonic-gate 		return;
15060Sstevel@tonic-gate 	}
15073446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
15083446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", root,
15093446Smrj 		    DIRECT_BOOT_ARCHIVE_64);
15103446Smrj 		if (stat(path, &sb) != 0) {
15113446Smrj 			if (bam_verbose && !bam_check)
15123446Smrj 				bam_print(UPDATE_ARCH_MISS, path);
15133446Smrj 			walk_arg.need_update = 1;
15143446Smrj 			return;
15153446Smrj 		}
15163446Smrj 	}
15170Sstevel@tonic-gate }
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate static error_t
15200Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
15210Sstevel@tonic-gate {
15220Sstevel@tonic-gate 	char path[PATH_MAX];
15230Sstevel@tonic-gate 	FILE *fp;
15240Sstevel@tonic-gate 	char buf[BAM_MAXLINE];
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate 	fp = fopen(path, "r");
15290Sstevel@tonic-gate 	if (fp == NULL) {
15300Sstevel@tonic-gate 		if (bam_debug)
15310Sstevel@tonic-gate 			bam_error(FLIST_FAIL, path, strerror(errno));
15320Sstevel@tonic-gate 		return (BAM_ERROR);
15330Sstevel@tonic-gate 	}
15340Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1535316Svikram 		/* skip blank lines */
1536316Svikram 		if (strspn(buf, " \t") == strlen(buf))
1537316Svikram 			continue;
15380Sstevel@tonic-gate 		append_to_flist(flistp, buf);
15390Sstevel@tonic-gate 	}
15400Sstevel@tonic-gate 	if (fclose(fp) != 0) {
15410Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
15420Sstevel@tonic-gate 		return (BAM_ERROR);
15430Sstevel@tonic-gate 	}
15440Sstevel@tonic-gate 	return (BAM_SUCCESS);
15450Sstevel@tonic-gate }
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate static error_t
15480Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
15490Sstevel@tonic-gate {
15500Sstevel@tonic-gate 	int rval;
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	/*
15550Sstevel@tonic-gate 	 * Read current lists of files - only the first is mandatory
15560Sstevel@tonic-gate 	 */
15570Sstevel@tonic-gate 	rval = read_one_list(root, flistp, BOOT_FILE_LIST);
15580Sstevel@tonic-gate 	if (rval != BAM_SUCCESS)
15590Sstevel@tonic-gate 		return (rval);
15600Sstevel@tonic-gate 	(void) read_one_list(root, flistp, ETC_FILE_LIST);
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 	if (flistp->head == NULL) {
15630Sstevel@tonic-gate 		bam_error(NO_FLIST);
15640Sstevel@tonic-gate 		return (BAM_ERROR);
15650Sstevel@tonic-gate 	}
15660Sstevel@tonic-gate 
15670Sstevel@tonic-gate 	return (BAM_SUCCESS);
15680Sstevel@tonic-gate }
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate static void
15710Sstevel@tonic-gate getoldstat(char *root)
15720Sstevel@tonic-gate {
15730Sstevel@tonic-gate 	char path[PATH_MAX];
15740Sstevel@tonic-gate 	int fd, error;
15750Sstevel@tonic-gate 	struct stat sb;
15760Sstevel@tonic-gate 	char *ostat;
15770Sstevel@tonic-gate 
15780Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
15790Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
15800Sstevel@tonic-gate 	if (fd == -1) {
15810Sstevel@tonic-gate 		if (bam_verbose)
15820Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
15830Sstevel@tonic-gate 		walk_arg.need_update = 1;
15840Sstevel@tonic-gate 		return;
15850Sstevel@tonic-gate 	}
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
15880Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
15890Sstevel@tonic-gate 		(void) close(fd);
15900Sstevel@tonic-gate 		walk_arg.need_update = 1;
15910Sstevel@tonic-gate 		return;
15920Sstevel@tonic-gate 	}
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
15950Sstevel@tonic-gate 
15960Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
15970Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
15980Sstevel@tonic-gate 		(void) close(fd);
15990Sstevel@tonic-gate 		free(ostat);
16000Sstevel@tonic-gate 		walk_arg.need_update = 1;
16010Sstevel@tonic-gate 		return;
16020Sstevel@tonic-gate 	}
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 	(void) close(fd);
16050Sstevel@tonic-gate 
16060Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
16070Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 	free(ostat);
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 	if (error) {
16120Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
16130Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
16140Sstevel@tonic-gate 		walk_arg.need_update = 1;
16150Sstevel@tonic-gate 		return;
16160Sstevel@tonic-gate 	}
16170Sstevel@tonic-gate }
16180Sstevel@tonic-gate 
16192583Svikram /*
16202583Svikram  * Checks if a file in the current (old) archive has
16212583Svikram  * been deleted from the root filesystem. This is needed for
16222583Svikram  * software like Trusted Extensions (TX) that switch early
16232583Svikram  * in boot based on presence/absence of a kernel module.
16242583Svikram  */
16252583Svikram static void
16262583Svikram check4stale(char *root)
16272583Svikram {
16282583Svikram 	nvpair_t	*nvp;
16292583Svikram 	nvlist_t	*nvlp;
16302583Svikram 	char 		*file;
16312583Svikram 	char		path[PATH_MAX];
16322583Svikram 	struct stat	sb;
16332583Svikram 
16342583Svikram 	/*
16352583Svikram 	 * Skip stale file check during smf check
16362583Svikram 	 */
16372583Svikram 	if (bam_smf_check)
16382583Svikram 		return;
16392583Svikram 
16402583Svikram 	/* Nothing to do if no old stats */
16412583Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
16422583Svikram 		return;
16432583Svikram 
16442583Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
16452583Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
16462583Svikram 		file = nvpair_name(nvp);
16472583Svikram 		if (file == NULL)
16482583Svikram 			continue;
16492583Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
16502583Svikram 		    root, file);
16512583Svikram 		if (stat(path, &sb) == -1) {
16522583Svikram 			walk_arg.need_update = 1;
16532583Svikram 			if (bam_verbose)
16542583Svikram 				bam_print(PARSEABLE_STALE_FILE, path);
16552583Svikram 		}
16562583Svikram 	}
16572583Svikram }
16582583Svikram 
16590Sstevel@tonic-gate static void
16600Sstevel@tonic-gate create_newstat(void)
16610Sstevel@tonic-gate {
16620Sstevel@tonic-gate 	int error;
16630Sstevel@tonic-gate 
16640Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
16650Sstevel@tonic-gate 	if (error) {
16660Sstevel@tonic-gate 		/*
16670Sstevel@tonic-gate 		 * Not fatal - we can still create archive
16680Sstevel@tonic-gate 		 */
16690Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
16700Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
16710Sstevel@tonic-gate 	}
16720Sstevel@tonic-gate }
16730Sstevel@tonic-gate 
16740Sstevel@tonic-gate static void
16750Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
16760Sstevel@tonic-gate {
16770Sstevel@tonic-gate 	char path[PATH_MAX];
16780Sstevel@tonic-gate 	line_t *lp;
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
16810Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
16820Sstevel@tonic-gate 		/* XXX shouldn't we use FTW_MOUNT ? */
16830Sstevel@tonic-gate 		if (nftw(path, cmpstat, 20, 0) == -1) {
16840Sstevel@tonic-gate 			/*
16850Sstevel@tonic-gate 			 * Some files may not exist.
16860Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
16870Sstevel@tonic-gate 			 * Emit verbose message only
16880Sstevel@tonic-gate 			 */
16890Sstevel@tonic-gate 			if (bam_verbose)
16900Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
16910Sstevel@tonic-gate 		}
16920Sstevel@tonic-gate 	}
16930Sstevel@tonic-gate }
16940Sstevel@tonic-gate 
16950Sstevel@tonic-gate static void
16960Sstevel@tonic-gate savenew(char *root)
16970Sstevel@tonic-gate {
16980Sstevel@tonic-gate 	char path[PATH_MAX];
16990Sstevel@tonic-gate 	char path2[PATH_MAX];
17000Sstevel@tonic-gate 	size_t sz;
17010Sstevel@tonic-gate 	char *nstat;
17020Sstevel@tonic-gate 	int fd, wrote, error;
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate 	nstat = NULL;
17050Sstevel@tonic-gate 	sz = 0;
17060Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
17070Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
17080Sstevel@tonic-gate 	if (error) {
17090Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
17100Sstevel@tonic-gate 		return;
17110Sstevel@tonic-gate 	}
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
17140Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
17150Sstevel@tonic-gate 	if (fd == -1) {
17160Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
17170Sstevel@tonic-gate 		free(nstat);
17180Sstevel@tonic-gate 		return;
17190Sstevel@tonic-gate 	}
17200Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
17210Sstevel@tonic-gate 	if (wrote != sz) {
17220Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
17230Sstevel@tonic-gate 		(void) close(fd);
17240Sstevel@tonic-gate 		free(nstat);
17250Sstevel@tonic-gate 		return;
17260Sstevel@tonic-gate 	}
17270Sstevel@tonic-gate 	(void) close(fd);
17280Sstevel@tonic-gate 	free(nstat);
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
17310Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
17320Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
17330Sstevel@tonic-gate 	}
17340Sstevel@tonic-gate }
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate static void
17370Sstevel@tonic-gate clear_walk_args(void)
17380Sstevel@tonic-gate {
17390Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
17400Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
17410Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
17420Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
17430Sstevel@tonic-gate 	walk_arg.need_update = 0;
17440Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
17450Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
17460Sstevel@tonic-gate }
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate /*
17490Sstevel@tonic-gate  * Returns:
17500Sstevel@tonic-gate  *	0 - no update necessary
17510Sstevel@tonic-gate  *	1 - update required.
17520Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
17530Sstevel@tonic-gate  *
17540Sstevel@tonic-gate  * Special handling for check (-n):
17550Sstevel@tonic-gate  * ================================
17560Sstevel@tonic-gate  * The check (-n) option produces parseable output.
17570Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
17580Sstevel@tonic-gate  * to out of sync files.
17590Sstevel@tonic-gate  * All stderr messages are still printed though.
17600Sstevel@tonic-gate  *
17610Sstevel@tonic-gate  */
17620Sstevel@tonic-gate static int
17630Sstevel@tonic-gate update_required(char *root)
17640Sstevel@tonic-gate {
17650Sstevel@tonic-gate 	struct stat sb;
17660Sstevel@tonic-gate 	char path[PATH_MAX];
17670Sstevel@tonic-gate 	filelist_t flist;
17680Sstevel@tonic-gate 	filelist_t *flistp = &flist;
17690Sstevel@tonic-gate 	int need_update;
17700Sstevel@tonic-gate 
17710Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	walk_arg.need_update = 0;
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 	/*
17760Sstevel@tonic-gate 	 * Without consulting stat data, check if we need update
17770Sstevel@tonic-gate 	 */
17780Sstevel@tonic-gate 	check_flags_and_files(root);
17790Sstevel@tonic-gate 
17800Sstevel@tonic-gate 	/*
17810Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
17820Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
17830Sstevel@tonic-gate 	 */
17840Sstevel@tonic-gate 	if (bam_smf_check) {
17850Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
17860Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
17870Sstevel@tonic-gate 			return (0);
17880Sstevel@tonic-gate 	}
17890Sstevel@tonic-gate 
17900Sstevel@tonic-gate 	/*
17910Sstevel@tonic-gate 	 * consult stat data only if we haven't made a decision
17920Sstevel@tonic-gate 	 * about update. If checking (-n) however, we always
17930Sstevel@tonic-gate 	 * need stat data (since we want to compare old and new)
17940Sstevel@tonic-gate 	 */
17950Sstevel@tonic-gate 	if (!walk_arg.need_update || bam_check)
17960Sstevel@tonic-gate 		getoldstat(root);
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate 	/*
17992583Svikram 	 * Check if the archive contains files that are no longer
18002583Svikram 	 * present on the root filesystem.
18012583Svikram 	 */
18022583Svikram 	if (!walk_arg.need_update || bam_check)
18032583Svikram 		check4stale(root);
18042583Svikram 
18052583Svikram 	/*
18060Sstevel@tonic-gate 	 * read list of files
18070Sstevel@tonic-gate 	 */
18080Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
18090Sstevel@tonic-gate 		clear_walk_args();
18100Sstevel@tonic-gate 		return (BAM_ERROR);
18110Sstevel@tonic-gate 	}
18120Sstevel@tonic-gate 
18130Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 	/*
18160Sstevel@tonic-gate 	 * At this point either the update is required
18170Sstevel@tonic-gate 	 * or the decision is pending. In either case
18180Sstevel@tonic-gate 	 * we need to create new stat nvlist
18190Sstevel@tonic-gate 	 */
18200Sstevel@tonic-gate 	create_newstat();
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate 	/*
18230Sstevel@tonic-gate 	 * This walk does 2 things:
18240Sstevel@tonic-gate 	 *  	- gets new stat data for every file
18250Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
18260Sstevel@tonic-gate 	 */
18270Sstevel@tonic-gate 	walk_list(root, &flist);
18280Sstevel@tonic-gate 
18290Sstevel@tonic-gate 	/* done with the file list */
18300Sstevel@tonic-gate 	filelist_free(flistp);
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 	/*
18330Sstevel@tonic-gate 	 * if we didn't succeed in  creating new stat data above
18340Sstevel@tonic-gate 	 * just return result of update check so that archive is built.
18350Sstevel@tonic-gate 	 */
18360Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
18370Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
18380Sstevel@tonic-gate 		need_update = walk_arg.need_update;
18390Sstevel@tonic-gate 		clear_walk_args();
18400Sstevel@tonic-gate 		return (need_update ? 1 : 0);
18410Sstevel@tonic-gate 	}
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 
18440Sstevel@tonic-gate 	/*
18450Sstevel@tonic-gate 	 * If no update required, discard newstat
18460Sstevel@tonic-gate 	 */
18470Sstevel@tonic-gate 	if (!walk_arg.need_update) {
18480Sstevel@tonic-gate 		clear_walk_args();
18490Sstevel@tonic-gate 		return (0);
18500Sstevel@tonic-gate 	}
18510Sstevel@tonic-gate 
18520Sstevel@tonic-gate 	/*
18530Sstevel@tonic-gate 	 * At this point we need an update - so save new stat data
18540Sstevel@tonic-gate 	 * However, if only checking (-n), don't save new stat data.
18550Sstevel@tonic-gate 	 */
18560Sstevel@tonic-gate 	if (!bam_check)
18570Sstevel@tonic-gate 		savenew(root);
18580Sstevel@tonic-gate 
18590Sstevel@tonic-gate 	clear_walk_args();
18600Sstevel@tonic-gate 
18610Sstevel@tonic-gate 	return (1);
18620Sstevel@tonic-gate }
18630Sstevel@tonic-gate 
18640Sstevel@tonic-gate static error_t
18650Sstevel@tonic-gate create_ramdisk(char *root)
18660Sstevel@tonic-gate {
18670Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
18680Sstevel@tonic-gate 	size_t len;
18690Sstevel@tonic-gate 	struct stat sb;
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 	/*
18720Sstevel@tonic-gate 	 * Setup command args for create_ramdisk.ksh
18730Sstevel@tonic-gate 	 */
18740Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, CREATE_RAMDISK);
18750Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
18760Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
18770Sstevel@tonic-gate 		return (BAM_ERROR);
18780Sstevel@tonic-gate 	}
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
18810Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate 	if (strlen(root) > 1) {
18840Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
18850Sstevel@tonic-gate 		/* chop off / at the end */
18860Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
18870Sstevel@tonic-gate 	} else
18880Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	if (exec_cmd(cmdline, NULL, 0) != 0) {
18910Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
18920Sstevel@tonic-gate 		free(cmdline);
18930Sstevel@tonic-gate 		return (BAM_ERROR);
18940Sstevel@tonic-gate 	}
18950Sstevel@tonic-gate 	free(cmdline);
18960Sstevel@tonic-gate 
18970Sstevel@tonic-gate 	/*
18980Sstevel@tonic-gate 	 * Verify that the archive has been created
18990Sstevel@tonic-gate 	 */
19003446Smrj 	(void) snprintf(path, sizeof (path), "%s%s", root,
19013446Smrj 	    DIRECT_BOOT_ARCHIVE_32);
19020Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
19030Sstevel@tonic-gate 		bam_error(ARCHIVE_NOT_CREATED, path);
19040Sstevel@tonic-gate 		return (BAM_ERROR);
19050Sstevel@tonic-gate 	}
19063446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
19073446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", root,
19083446Smrj 		    DIRECT_BOOT_ARCHIVE_64);
19093446Smrj 		if (stat(path, &sb) != 0) {
19103446Smrj 			bam_error(ARCHIVE_NOT_CREATED, path);
19113446Smrj 			return (BAM_ERROR);
19123446Smrj 		}
19133446Smrj 	}
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate 	return (BAM_SUCCESS);
19160Sstevel@tonic-gate }
19170Sstevel@tonic-gate 
19180Sstevel@tonic-gate /*
19190Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
19200Sstevel@tonic-gate  * 1 - is miniroot
19210Sstevel@tonic-gate  * 0 - is not
19220Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
19230Sstevel@tonic-gate  */
19240Sstevel@tonic-gate static int
19250Sstevel@tonic-gate is_ramdisk(char *root)
19260Sstevel@tonic-gate {
19270Sstevel@tonic-gate 	struct extmnttab mnt;
19280Sstevel@tonic-gate 	FILE *fp;
19290Sstevel@tonic-gate 	int found;
1930316Svikram 	char mntpt[PATH_MAX];
1931316Svikram 	char *cp;
19320Sstevel@tonic-gate 
19330Sstevel@tonic-gate 	/*
19340Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
19350Sstevel@tonic-gate 	 * of dubious value:
1936316Svikram 	 *	- create boot_archive on a lofi-mounted boot_archive
19370Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
19380Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
19390Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
19400Sstevel@tonic-gate 	 * worth it.
19410Sstevel@tonic-gate 	 * The other two conditions are handled here
19420Sstevel@tonic-gate 	 */
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
19450Sstevel@tonic-gate 	if (fp == NULL) {
19460Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
19470Sstevel@tonic-gate 		return (0);
19480Sstevel@tonic-gate 	}
19490Sstevel@tonic-gate 
19500Sstevel@tonic-gate 	resetmnttab(fp);
19510Sstevel@tonic-gate 
1952316Svikram 	/*
1953316Svikram 	 * Remove any trailing / from the mount point
1954316Svikram 	 */
1955316Svikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
1956316Svikram 	if (strcmp(root, "/") != 0) {
1957316Svikram 		cp = mntpt + strlen(mntpt) - 1;
1958316Svikram 		if (*cp == '/')
1959316Svikram 			*cp = '\0';
1960316Svikram 	}
19610Sstevel@tonic-gate 	found = 0;
19620Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1963316Svikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
19640Sstevel@tonic-gate 			found = 1;
19650Sstevel@tonic-gate 			break;
19660Sstevel@tonic-gate 		}
19670Sstevel@tonic-gate 	}
19680Sstevel@tonic-gate 
19690Sstevel@tonic-gate 	if (!found) {
19700Sstevel@tonic-gate 		if (bam_verbose)
1971316Svikram 			bam_error(NOT_IN_MNTTAB, mntpt);
19720Sstevel@tonic-gate 		(void) fclose(fp);
19730Sstevel@tonic-gate 		return (0);
19740Sstevel@tonic-gate 	}
19750Sstevel@tonic-gate 
19760Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
19770Sstevel@tonic-gate 		if (bam_verbose)
19780Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
19790Sstevel@tonic-gate 		(void) fclose(fp);
19800Sstevel@tonic-gate 		return (1);
19810Sstevel@tonic-gate 	}
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 	(void) fclose(fp);
19840Sstevel@tonic-gate 
19850Sstevel@tonic-gate 	return (0);
19860Sstevel@tonic-gate }
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate static int
19890Sstevel@tonic-gate is_newboot(char *root)
19900Sstevel@tonic-gate {
19910Sstevel@tonic-gate 	char path[PATH_MAX];
19920Sstevel@tonic-gate 	struct stat sb;
19930Sstevel@tonic-gate 
19940Sstevel@tonic-gate 	/*
19950Sstevel@tonic-gate 	 * We can't boot without MULTI_BOOT
19960Sstevel@tonic-gate 	 */
19970Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, MULTI_BOOT);
19980Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
19990Sstevel@tonic-gate 		if (bam_verbose)
20000Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
20010Sstevel@tonic-gate 		return (0);
20020Sstevel@tonic-gate 	}
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 	/*
20050Sstevel@tonic-gate 	 * We can't generate archive without GRUB_DIR
20060Sstevel@tonic-gate 	 */
20070Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR);
20080Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
20090Sstevel@tonic-gate 		if (bam_verbose)
20100Sstevel@tonic-gate 			bam_print(DIR_MISS, path);
20110Sstevel@tonic-gate 		return (0);
20120Sstevel@tonic-gate 	}
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 	return (1);
20150Sstevel@tonic-gate }
20160Sstevel@tonic-gate 
20170Sstevel@tonic-gate static int
20180Sstevel@tonic-gate is_readonly(char *root)
20190Sstevel@tonic-gate {
20200Sstevel@tonic-gate 	struct statvfs vfs;
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate 	/*
20230Sstevel@tonic-gate 	 * Check for RDONLY filesystem
20240Sstevel@tonic-gate 	 * When in doubt assume it is not readonly
20250Sstevel@tonic-gate 	 */
20260Sstevel@tonic-gate 	if (statvfs(root, &vfs) != 0) {
20270Sstevel@tonic-gate 		if (bam_verbose)
20280Sstevel@tonic-gate 			bam_error(STATVFS_FAIL, root, strerror(errno));
20290Sstevel@tonic-gate 		return (0);
20300Sstevel@tonic-gate 	}
20310Sstevel@tonic-gate 
20320Sstevel@tonic-gate 	if (vfs.f_flag & ST_RDONLY) {
20330Sstevel@tonic-gate 		return (1);
20340Sstevel@tonic-gate 	}
20350Sstevel@tonic-gate 
20360Sstevel@tonic-gate 	return (0);
20370Sstevel@tonic-gate }
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate static error_t
20400Sstevel@tonic-gate update_archive(char *root, char *opt)
20410Sstevel@tonic-gate {
20420Sstevel@tonic-gate 	error_t ret;
20430Sstevel@tonic-gate 
20440Sstevel@tonic-gate 	assert(root);
20450Sstevel@tonic-gate 	assert(opt == NULL);
20460Sstevel@tonic-gate 
20470Sstevel@tonic-gate 	/*
2048316Svikram 	 * root must belong to a GRUB boot OS,
20490Sstevel@tonic-gate 	 * don't care on sparc except for diskless clients
20500Sstevel@tonic-gate 	 */
20510Sstevel@tonic-gate 	if (!is_newboot(root)) {
2052316Svikram 		/*
2053316Svikram 		 * Emit message only if not in context of update_all.
2054316Svikram 		 * If in update_all, emit only if verbose flag is set.
2055316Svikram 		 */
2056316Svikram 		if (!bam_update_all || bam_verbose)
2057316Svikram 			bam_print(NOT_GRUB_BOOT, root);
20580Sstevel@tonic-gate 		return (BAM_SUCCESS);
20590Sstevel@tonic-gate 	}
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate 	/*
2062662Sszhou 	 * If smf check is requested when / is writable (can happen
2063662Sszhou 	 * on first reboot following an upgrade because service
2064662Sszhou 	 * dependency is messed up), skip the check.
2065662Sszhou 	 */
2066662Sszhou 	if (bam_smf_check && !bam_root_readonly)
2067662Sszhou 		return (BAM_SUCCESS);
2068662Sszhou 
2069662Sszhou 	/*
2070662Sszhou 	 * root must be writable. This check applies to alternate
2071662Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
20720Sstevel@tonic-gate 	 * Note: statvfs() does not always report the truth
20730Sstevel@tonic-gate 	 */
2074756Ssetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
2075662Sszhou 		if (bam_verbose)
20760Sstevel@tonic-gate 			bam_print(RDONLY_FS, root);
20770Sstevel@tonic-gate 		return (BAM_SUCCESS);
20780Sstevel@tonic-gate 	}
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	/*
20810Sstevel@tonic-gate 	 * Don't generate archive on ramdisk
20820Sstevel@tonic-gate 	 */
20830Sstevel@tonic-gate 	if (is_ramdisk(root)) {
20840Sstevel@tonic-gate 		if (bam_verbose)
20850Sstevel@tonic-gate 			bam_print(SKIP_RAMDISK);
20860Sstevel@tonic-gate 		return (BAM_SUCCESS);
20870Sstevel@tonic-gate 	}
20880Sstevel@tonic-gate 
20890Sstevel@tonic-gate 	/*
20900Sstevel@tonic-gate 	 * Now check if updated is really needed
20910Sstevel@tonic-gate 	 */
20920Sstevel@tonic-gate 	ret = update_required(root);
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate 	/*
20950Sstevel@tonic-gate 	 * The check command (-n) is *not* a dry run
20960Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
20970Sstevel@tonic-gate 	 */
20980Sstevel@tonic-gate 	if (bam_check) {
20990Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
21000Sstevel@tonic-gate 	}
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 	if (ret == 1) {
21030Sstevel@tonic-gate 		/* create the ramdisk */
21040Sstevel@tonic-gate 		ret = create_ramdisk(root);
21050Sstevel@tonic-gate 	}
21060Sstevel@tonic-gate 	return (ret);
21070Sstevel@tonic-gate }
21080Sstevel@tonic-gate 
2109316Svikram static void
21101746Svikram update_fdisk(void)
21111746Svikram {
21121746Svikram 	struct stat sb;
21131746Svikram 	char cmd[PATH_MAX];
21141746Svikram 	int ret1, ret2;
21151746Svikram 
21161746Svikram 	assert(stat(GRUB_fdisk, &sb) == 0);
21171746Svikram 	assert(stat(GRUB_fdisk_target, &sb) == 0);
21181746Svikram 
21191746Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`",
21201746Svikram 	    GRUB_fdisk, GRUB_fdisk_target);
21211746Svikram 
21221746Svikram 	bam_print(UPDATING_FDISK);
21231746Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
21241746Svikram 		bam_error(FDISK_UPDATE_FAILED);
21251746Svikram 	}
21261746Svikram 
21271746Svikram 	/*
21281746Svikram 	 * We are done, remove the files.
21291746Svikram 	 */
21301746Svikram 	ret1 = unlink(GRUB_fdisk);
21311746Svikram 	ret2 = unlink(GRUB_fdisk_target);
21321746Svikram 	if (ret1 != 0 || ret2 != 0) {
21331746Svikram 		bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target);
21341746Svikram 	}
21351746Svikram }
21361746Svikram 
21371746Svikram static void
2138316Svikram restore_grub_slice(void)
2139316Svikram {
2140316Svikram 	struct stat sb;
2141316Svikram 	char *mntpt, *physlice;
2142316Svikram 	int mnted;	/* set if we did a mount */
2143316Svikram 	char menupath[PATH_MAX], cmd[PATH_MAX];
2144316Svikram 
2145316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
2146316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
2147316Svikram 		return;
2148316Svikram 	}
2149316Svikram 
2150316Svikram 	/*
2151316Svikram 	 * If we are doing an luactivate, don't attempt to restore GRUB or else
2152316Svikram 	 * we may not be able to get to DCA boot environments. Let luactivate
2153316Svikram 	 * handle GRUB/DCA installation
2154316Svikram 	 */
2155316Svikram 	if (stat(LU_ACTIVATE_FILE, &sb) == 0) {
2156316Svikram 		return;
2157316Svikram 	}
2158316Svikram 
2159316Svikram 	mnted = 0;
2160316Svikram 	physlice = NULL;
2161621Svikram 	mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL);
2162316Svikram 	if (mntpt == NULL) {
2163316Svikram 		bam_error(CANNOT_RESTORE_GRUB_SLICE);
2164316Svikram 		return;
2165316Svikram 	}
2166316Svikram 
2167316Svikram 	(void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU);
2168316Svikram 	if (stat(menupath, &sb) == 0) {
2169621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2170316Svikram 		return;
2171316Svikram 	}
2172316Svikram 
2173316Svikram 	/*
2174316Svikram 	 * The menu is missing - we need to do a restore
2175316Svikram 	 */
2176316Svikram 	bam_print(RESTORING_GRUB);
2177316Svikram 
2178316Svikram 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s %s",
2179316Svikram 	    INSTALLGRUB, STAGE1, STAGE2, physlice);
2180316Svikram 
2181316Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
2182316Svikram 		bam_error(RESTORE_GRUB_FAILED);
2183621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2184316Svikram 		return;
2185316Svikram 	}
2186316Svikram 
2187316Svikram 	if (stat(GRUB_backup_menu, &sb) != 0) {
2188316Svikram 		bam_error(MISSING_BACKUP_MENU,
2189316Svikram 		    GRUB_backup_menu, strerror(errno));
2190621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2191316Svikram 		return;
2192316Svikram 	}
2193316Svikram 
2194316Svikram 	(void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s",
2195316Svikram 	    GRUB_backup_menu, menupath);
2196316Svikram 
2197316Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
2198316Svikram 		bam_error(RESTORE_MENU_FAILED, menupath);
2199621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2200316Svikram 		return;
2201316Svikram 	}
2202316Svikram 
2203316Svikram 	/* Success */
2204621Svikram 	umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2205316Svikram }
2206316Svikram 
22070Sstevel@tonic-gate static error_t
22080Sstevel@tonic-gate update_all(char *root, char *opt)
22090Sstevel@tonic-gate {
22100Sstevel@tonic-gate 	struct extmnttab mnt;
22110Sstevel@tonic-gate 	struct stat sb;
22120Sstevel@tonic-gate 	FILE *fp;
22130Sstevel@tonic-gate 	char multibt[PATH_MAX];
22140Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
22151746Svikram 	int ret1, ret2;
22160Sstevel@tonic-gate 
2217621Svikram 	assert(root);
22180Sstevel@tonic-gate 	assert(opt == NULL);
22190Sstevel@tonic-gate 
2220621Svikram 	if (bam_rootlen != 1 || *root != '/') {
2221621Svikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
2222621Svikram 		bam_error(ALT_ROOT_INVALID, multibt);
2223621Svikram 		return (BAM_ERROR);
2224621Svikram 	}
2225621Svikram 
22260Sstevel@tonic-gate 	/*
22274493Snadkarni 	 * Check to see if we are in the midst of safemode patching
22284493Snadkarni 	 * If so skip building the archive for /. Instead build it
22294493Snadkarni 	 * against the latest bits obtained by creating a fresh lofs
22304493Snadkarni 	 * mount of root.
22310Sstevel@tonic-gate 	 */
22324493Snadkarni 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
22334493Snadkarni 		if (mkdir(LOFS_PATCH_MNT, 0755) == -1 &&
22344493Snadkarni 		    errno != EEXIST) {
22354493Snadkarni 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
22364493Snadkarni 			    strerror(errno));
22374493Snadkarni 			ret = BAM_ERROR;
22384493Snadkarni 			goto out;
22394493Snadkarni 		}
22404493Snadkarni 		(void) snprintf(multibt, sizeof (multibt),
22414493Snadkarni 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
22424493Snadkarni 		if (exec_cmd(multibt, NULL, 0) != 0) {
22434493Snadkarni 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
22444493Snadkarni 			ret = BAM_ERROR;
22454493Snadkarni 		}
22464493Snadkarni 		if (ret != BAM_ERROR) {
22474493Snadkarni 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
22484493Snadkarni 			    LOFS_PATCH_MNT);
22494493Snadkarni 			bam_rootlen = strlen(rootbuf);
22504493Snadkarni 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
22514493Snadkarni 				ret = BAM_ERROR;
22524550Snadkarni 			/*
22534550Snadkarni 			 * unmount the lofs mount since there could be
22544550Snadkarni 			 * multiple invocations of bootadm -a update_all
22554550Snadkarni 			 */
22564550Snadkarni 			(void) snprintf(multibt, sizeof (multibt),
22574550Snadkarni 			    "/sbin/umount %s", LOFS_PATCH_MNT);
22584550Snadkarni 			if (exec_cmd(multibt, NULL, 0) != 0) {
22594550Snadkarni 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
22604550Snadkarni 				ret = BAM_ERROR;
22614550Snadkarni 			}
22624493Snadkarni 		}
22634493Snadkarni 	} else {
22644493Snadkarni 		/*
22654493Snadkarni 		 * First update archive for current root
22664493Snadkarni 		 */
22674493Snadkarni 		if (update_archive(root, opt) != BAM_SUCCESS)
22684493Snadkarni 			ret = BAM_ERROR;
22694493Snadkarni 	}
22704493Snadkarni 
22714493Snadkarni 	if (ret == BAM_ERROR)
22724493Snadkarni 		goto out;
22730Sstevel@tonic-gate 
22740Sstevel@tonic-gate 	/*
22750Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
22760Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
22770Sstevel@tonic-gate 	 */
22780Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
22790Sstevel@tonic-gate 	if (fp == NULL) {
22800Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2281316Svikram 		ret = BAM_ERROR;
2282316Svikram 		goto out;
22830Sstevel@tonic-gate 	}
22840Sstevel@tonic-gate 
22850Sstevel@tonic-gate 	resetmnttab(fp);
22860Sstevel@tonic-gate 
22870Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
22880Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
22890Sstevel@tonic-gate 			continue;
22900Sstevel@tonic-gate 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
22910Sstevel@tonic-gate 			continue;
22920Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
22930Sstevel@tonic-gate 			continue;
22940Sstevel@tonic-gate 
22950Sstevel@tonic-gate 		(void) snprintf(multibt, sizeof (multibt), "%s%s",
22960Sstevel@tonic-gate 		    mnt.mnt_mountp, MULTI_BOOT);
22970Sstevel@tonic-gate 
22980Sstevel@tonic-gate 		if (stat(multibt, &sb) == -1)
22990Sstevel@tonic-gate 			continue;
23000Sstevel@tonic-gate 
23010Sstevel@tonic-gate 		/*
23020Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
23030Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
23040Sstevel@tonic-gate 		 */
23050Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
23060Sstevel@tonic-gate 		    mnt.mnt_mountp);
23070Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
23083446Smrj 
23093446Smrj 		/*
23103446Smrj 		 * It's possible that other mounts may be an alternate boot
23113446Smrj 		 * architecture, so check it again.
23123446Smrj 		 */
23133446Smrj 		if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) ||
23143446Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
23150Sstevel@tonic-gate 			ret = BAM_ERROR;
23160Sstevel@tonic-gate 	}
23170Sstevel@tonic-gate 
23180Sstevel@tonic-gate 	(void) fclose(fp);
23190Sstevel@tonic-gate 
2320316Svikram out:
2321316Svikram 	if (stat(GRUB_slice, &sb) == 0) {
2322316Svikram 		restore_grub_slice();
2323316Svikram 	}
2324316Svikram 
23251746Svikram 	/*
23261746Svikram 	 * Update fdisk table as we go down. Updating it when
23271746Svikram 	 * the system is running will confuse biosdev.
23281746Svikram 	 */
23291746Svikram 	ret1 = stat(GRUB_fdisk, &sb);
23301746Svikram 	ret2 = stat(GRUB_fdisk_target, &sb);
23311746Svikram 	if ((ret1 == 0) && (ret2 == 0)) {
23321746Svikram 		update_fdisk();
23331746Svikram 	} else if ((ret1 == 0) ^ (ret2 == 0)) {
23341746Svikram 		/*
23351746Svikram 		 * It is an error for one file to be
23361746Svikram 		 * present and the other absent.
23371746Svikram 		 * It is normal for both files to be
23381746Svikram 		 * absent - it indicates that no fdisk
23391746Svikram 		 * update is required.
23401746Svikram 		 */
23411746Svikram 		bam_error(MISSING_FDISK_FILE,
23421746Svikram 		    ret1 ? GRUB_fdisk : GRUB_fdisk_target);
23431746Svikram 		ret = BAM_ERROR;
23441746Svikram 	}
23451746Svikram 
23460Sstevel@tonic-gate 	return (ret);
23470Sstevel@tonic-gate }
23480Sstevel@tonic-gate 
23490Sstevel@tonic-gate static void
23500Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
23510Sstevel@tonic-gate {
23520Sstevel@tonic-gate 	if (mp->start == NULL) {
23530Sstevel@tonic-gate 		mp->start = lp;
23540Sstevel@tonic-gate 	} else {
23550Sstevel@tonic-gate 		mp->end->next = lp;
2356662Sszhou 		lp->prev = mp->end;
23570Sstevel@tonic-gate 	}
23580Sstevel@tonic-gate 	mp->end = lp;
23590Sstevel@tonic-gate }
23600Sstevel@tonic-gate 
2361662Sszhou static void
2362662Sszhou unlink_line(menu_t *mp, line_t *lp)
2363662Sszhou {
2364662Sszhou 	/* unlink from list */
2365662Sszhou 	if (lp->prev)
2366662Sszhou 		lp->prev->next = lp->next;
2367662Sszhou 	else
2368662Sszhou 		mp->start = lp->next;
2369662Sszhou 	if (lp->next)
2370662Sszhou 		lp->next->prev = lp->prev;
2371662Sszhou 	else
2372662Sszhou 		mp->end = lp->prev;
2373662Sszhou }
2374662Sszhou 
2375662Sszhou static entry_t *
2376662Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
2377662Sszhou {
2378662Sszhou 	entry_t *ent, *prev;
2379662Sszhou 
2380662Sszhou 	ent = s_calloc(1, sizeof (entry_t));
2381662Sszhou 	ent->start = start;
2382662Sszhou 	ent->end = end;
2383662Sszhou 
2384662Sszhou 	if (mp->entries == NULL) {
2385662Sszhou 		mp->entries = ent;
2386662Sszhou 		return (ent);
2387662Sszhou 	}
2388662Sszhou 
2389662Sszhou 	prev = mp->entries;
2390662Sszhou 	while (prev->next)
2391662Sszhou 		prev = prev-> next;
2392662Sszhou 	prev->next = ent;
2393662Sszhou 	ent->prev = prev;
2394662Sszhou 	return (ent);
2395662Sszhou }
2396662Sszhou 
2397662Sszhou static void
2398662Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
2399662Sszhou {
2400662Sszhou 	if (ent)
2401662Sszhou 		ent->end = lp;
2402662Sszhou }
2403662Sszhou 
24040Sstevel@tonic-gate /*
24050Sstevel@tonic-gate  * A line in menu.lst looks like
24060Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
24070Sstevel@tonic-gate  */
24080Sstevel@tonic-gate static void
24090Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
24100Sstevel@tonic-gate {
24110Sstevel@tonic-gate 	/*
24120Sstevel@tonic-gate 	 * save state across calls. This is so that
24130Sstevel@tonic-gate 	 * header gets the right entry# after title has
24140Sstevel@tonic-gate 	 * been processed
24150Sstevel@tonic-gate 	 */
2416662Sszhou 	static line_t *prev = NULL;
2417662Sszhou 	static entry_t *curr_ent = NULL;
24183446Smrj 	static int in_liveupgrade = 0;
24190Sstevel@tonic-gate 
24200Sstevel@tonic-gate 	line_t	*lp;
24210Sstevel@tonic-gate 	char *cmd, *sep, *arg;
24220Sstevel@tonic-gate 	char save, *cp, *line;
24230Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
24240Sstevel@tonic-gate 
24250Sstevel@tonic-gate 	if (str == NULL) {
24260Sstevel@tonic-gate 		return;
24270Sstevel@tonic-gate 	}
24280Sstevel@tonic-gate 
24290Sstevel@tonic-gate 	/*
24300Sstevel@tonic-gate 	 * First save a copy of the entire line.
24310Sstevel@tonic-gate 	 * We use this later to set the line field.
24320Sstevel@tonic-gate 	 */
24330Sstevel@tonic-gate 	line = s_strdup(str);
24340Sstevel@tonic-gate 
24350Sstevel@tonic-gate 	/* Eat up leading whitespace */
24360Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
24370Sstevel@tonic-gate 		str++;
24380Sstevel@tonic-gate 
24390Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
24400Sstevel@tonic-gate 		cmd = s_strdup("#");
24410Sstevel@tonic-gate 		sep = NULL;
24420Sstevel@tonic-gate 		arg = s_strdup(str + 1);
24430Sstevel@tonic-gate 		flag = BAM_COMMENT;
24443446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
24453446Smrj 			in_liveupgrade = 1;
24463446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
24473446Smrj 			in_liveupgrade = 0;
24483446Smrj 		}
24490Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
24500Sstevel@tonic-gate 		cmd = sep = arg = NULL;
24510Sstevel@tonic-gate 		flag = BAM_EMPTY;
24520Sstevel@tonic-gate 	} else {
24530Sstevel@tonic-gate 		/*
24540Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
24550Sstevel@tonic-gate 		 * However various development bits use '=' as a
24560Sstevel@tonic-gate 		 * separator. In addition, external users also
24570Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
24580Sstevel@tonic-gate 		 */
24590Sstevel@tonic-gate 		cp = str;
24600Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
24610Sstevel@tonic-gate 			if (*str == '\0') {
24620Sstevel@tonic-gate 				cmd = s_strdup(cp);
24630Sstevel@tonic-gate 				sep = arg = NULL;
24640Sstevel@tonic-gate 				break;
24650Sstevel@tonic-gate 			}
24660Sstevel@tonic-gate 			str++;
24670Sstevel@tonic-gate 		}
24680Sstevel@tonic-gate 
24690Sstevel@tonic-gate 		if (*str != '\0') {
24700Sstevel@tonic-gate 			save = *str;
24710Sstevel@tonic-gate 			*str = '\0';
24720Sstevel@tonic-gate 			cmd = s_strdup(cp);
24730Sstevel@tonic-gate 			*str = save;
24740Sstevel@tonic-gate 
24750Sstevel@tonic-gate 			str++;
24760Sstevel@tonic-gate 			save = *str;
24770Sstevel@tonic-gate 			*str = '\0';
24780Sstevel@tonic-gate 			sep = s_strdup(str - 1);
24790Sstevel@tonic-gate 			*str = save;
24800Sstevel@tonic-gate 
24810Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
24820Sstevel@tonic-gate 				str++;
24830Sstevel@tonic-gate 			if (*str == '\0')
24840Sstevel@tonic-gate 				arg = NULL;
24850Sstevel@tonic-gate 			else
24860Sstevel@tonic-gate 				arg = s_strdup(str);
24870Sstevel@tonic-gate 		}
24880Sstevel@tonic-gate 	}
24890Sstevel@tonic-gate 
24900Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
24910Sstevel@tonic-gate 
24920Sstevel@tonic-gate 	lp->cmd = cmd;
24930Sstevel@tonic-gate 	lp->sep = sep;
24940Sstevel@tonic-gate 	lp->arg = arg;
24950Sstevel@tonic-gate 	lp->line = line;
24960Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
24970Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
24980Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
24990Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
25000Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
25013446Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
25020Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
2503662Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
25043446Smrj 			curr_ent->flags = BAM_ENTRY_BOOTADM;
2505662Sszhou 		} else {
2506662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
25073446Smrj 			if (in_liveupgrade) {
25083446Smrj 				curr_ent->flags = BAM_ENTRY_LU;
25093446Smrj 			}
2510662Sszhou 		}
25113446Smrj 		curr_ent->entryNum = *entryNum;
25120Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
25130Sstevel@tonic-gate 		/*
25140Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
25150Sstevel@tonic-gate 		 * by the subsequent title
25160Sstevel@tonic-gate 		 */
25170Sstevel@tonic-gate 		lp->entryNum = *entryNum;
25180Sstevel@tonic-gate 		lp->flags = flag;
25190Sstevel@tonic-gate 	} else {
25200Sstevel@tonic-gate 		lp->entryNum = *entryNum;
25213446Smrj 
25223446Smrj 		if (*entryNum == ENTRY_INIT) {
25233446Smrj 			lp->flags = BAM_GLOBAL;
25243446Smrj 		} else {
25253446Smrj 			lp->flags = BAM_ENTRY;
25263446Smrj 
25273446Smrj 			if (cmd && arg) {
25283446Smrj 				/*
25293446Smrj 				 * We only compare for the length of "module"
25303446Smrj 				 * so that "module$" will also match.
25313446Smrj 				 */
25323446Smrj 				if ((strncmp(cmd, menu_cmds[MODULE_CMD],
25333446Smrj 				    strlen(menu_cmds[MODULE_CMD])) == 0) &&
25343446Smrj 				    (strcmp(arg, MINIROOT) == 0))
25353446Smrj 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
25363446Smrj 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
25373446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
25383446Smrj 				else if (strcmp(cmd,
25393446Smrj 				    menu_cmds[CHAINLOADER_CMD]) == 0)
25403446Smrj 					curr_ent->flags |=
25413446Smrj 					    BAM_ENTRY_CHAINLOADER;
25423446Smrj 			}
25433446Smrj 		}
25440Sstevel@tonic-gate 	}
25450Sstevel@tonic-gate 
2546662Sszhou 	/* record default, old default, and entry line ranges */
2547662Sszhou 	if (lp->flags == BAM_GLOBAL &&
2548662Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
2549662Sszhou 		mp->curdefault = lp;
2550662Sszhou 	} else if (lp->flags == BAM_COMMENT &&
2551662Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
2552662Sszhou 		mp->olddefault = lp;
25533446Smrj 	} else if (lp->flags == BAM_COMMENT &&
25543446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
25553446Smrj 		mp->old_rc_default = lp;
2556662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
25573446Smrj 	    (lp->flags == BAM_COMMENT &&
25583446Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
2559662Sszhou 		boot_entry_addline(curr_ent, lp);
2560662Sszhou 	}
25610Sstevel@tonic-gate 	append_line(mp, lp);
25620Sstevel@tonic-gate 
25630Sstevel@tonic-gate 	prev = lp;
25640Sstevel@tonic-gate }
25650Sstevel@tonic-gate 
2566621Svikram static void
2567621Svikram update_numbering(menu_t *mp)
2568621Svikram {
2569621Svikram 	int lineNum;
2570621Svikram 	int entryNum;
2571621Svikram 	int old_default_value;
2572621Svikram 	line_t *lp, *prev, *default_lp, *default_entry;
2573621Svikram 	char buf[PATH_MAX];
2574621Svikram 
2575621Svikram 	if (mp->start == NULL) {
2576621Svikram 		return;
2577621Svikram 	}
2578621Svikram 
2579621Svikram 	lineNum = LINE_INIT;
2580621Svikram 	entryNum = ENTRY_INIT;
2581621Svikram 	old_default_value = ENTRY_INIT;
2582621Svikram 	lp = default_lp = default_entry = NULL;
2583621Svikram 
2584621Svikram 	prev = NULL;
2585621Svikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
2586621Svikram 		lp->lineNum = ++lineNum;
2587621Svikram 
2588621Svikram 		/*
2589621Svikram 		 * Get the value of the default command
2590621Svikram 		 */
2591621Svikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
2592621Svikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
2593621Svikram 		    lp->arg) {
2594621Svikram 			old_default_value = atoi(lp->arg);
2595621Svikram 			default_lp = lp;
2596621Svikram 		}
2597621Svikram 
2598621Svikram 		/*
2599621Svikram 		 * If not boot entry, nothing else to fix for this
2600621Svikram 		 * entry
2601621Svikram 		 */
2602621Svikram 		if (lp->entryNum == ENTRY_INIT)
2603621Svikram 			continue;
2604621Svikram 
2605621Svikram 		/*
2606621Svikram 		 * Record the position of the default entry.
2607621Svikram 		 * The following works because global
2608621Svikram 		 * commands like default and timeout should precede
2609621Svikram 		 * actual boot entries, so old_default_value
2610621Svikram 		 * is already known (or default cmd is missing).
2611621Svikram 		 */
2612621Svikram 		if (default_entry == NULL &&
2613621Svikram 		    old_default_value != ENTRY_INIT &&
2614621Svikram 		    lp->entryNum == old_default_value) {
2615621Svikram 			default_entry = lp;
2616621Svikram 		}
2617621Svikram 
2618621Svikram 		/*
2619621Svikram 		 * Now fixup the entry number
2620621Svikram 		 */
2621621Svikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
2622621Svikram 			lp->entryNum = ++entryNum;
2623621Svikram 			/* fixup the bootadm header */
2624621Svikram 			if (prev && prev->flags == BAM_COMMENT &&
26253446Smrj 			    prev->arg &&
26263446Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2627621Svikram 				prev->entryNum = lp->entryNum;
2628621Svikram 			}
2629621Svikram 		} else {
2630621Svikram 			lp->entryNum = entryNum;
2631621Svikram 		}
2632621Svikram 	}
2633621Svikram 
2634621Svikram 	/*
2635621Svikram 	 * No default command in menu, simply return
2636621Svikram 	 */
2637621Svikram 	if (default_lp == NULL) {
2638621Svikram 		return;
2639621Svikram 	}
2640621Svikram 
2641621Svikram 	free(default_lp->arg);
2642621Svikram 	free(default_lp->line);
2643621Svikram 
2644621Svikram 	if (default_entry == NULL) {
2645621Svikram 		default_lp->arg = s_strdup("0");
2646621Svikram 	} else {
2647621Svikram 		(void) snprintf(buf, sizeof (buf), "%d",
2648621Svikram 		    default_entry->entryNum);
2649621Svikram 		default_lp->arg = s_strdup(buf);
2650621Svikram 	}
2651621Svikram 
2652621Svikram 	/*
2653621Svikram 	 * The following is required since only the line field gets
2654621Svikram 	 * written back to menu.lst
2655621Svikram 	 */
2656621Svikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
2657621Svikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
2658621Svikram 	default_lp->line = s_strdup(buf);
2659621Svikram }
2660621Svikram 
2661621Svikram 
26620Sstevel@tonic-gate static menu_t *
26630Sstevel@tonic-gate menu_read(char *menu_path)
26640Sstevel@tonic-gate {
26650Sstevel@tonic-gate 	FILE *fp;
26660Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
26670Sstevel@tonic-gate 	menu_t *mp;
26680Sstevel@tonic-gate 	int line, entry, len, n;
26690Sstevel@tonic-gate 
26700Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
26710Sstevel@tonic-gate 
26720Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
26730Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
26740Sstevel@tonic-gate 		return (mp);
26750Sstevel@tonic-gate 	}
26760Sstevel@tonic-gate 
26770Sstevel@tonic-gate 
26780Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
26790Sstevel@tonic-gate 	line = LINE_INIT;
26800Sstevel@tonic-gate 	entry = ENTRY_INIT;
26810Sstevel@tonic-gate 	cp = buf;
26820Sstevel@tonic-gate 	len = sizeof (buf);
26830Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
26840Sstevel@tonic-gate 		n = strlen(cp);
26850Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
26860Sstevel@tonic-gate 			len -= n - 1;
26870Sstevel@tonic-gate 			assert(len >= 2);
26880Sstevel@tonic-gate 			cp += n - 1;
26890Sstevel@tonic-gate 			continue;
26900Sstevel@tonic-gate 		}
26910Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
26920Sstevel@tonic-gate 		cp = buf;
26930Sstevel@tonic-gate 		len = sizeof (buf);
26940Sstevel@tonic-gate 	}
26950Sstevel@tonic-gate 
26960Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
26970Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
26980Sstevel@tonic-gate 	}
26990Sstevel@tonic-gate 
27000Sstevel@tonic-gate 	return (mp);
27010Sstevel@tonic-gate }
27020Sstevel@tonic-gate 
27030Sstevel@tonic-gate static error_t
27040Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
27050Sstevel@tonic-gate {
27060Sstevel@tonic-gate 	char *eq;
27070Sstevel@tonic-gate 	char *opt_dup;
27080Sstevel@tonic-gate 	int entryNum;
27090Sstevel@tonic-gate 
27100Sstevel@tonic-gate 	assert(mp);
27110Sstevel@tonic-gate 	assert(mp->start);
27120Sstevel@tonic-gate 	assert(opt);
27130Sstevel@tonic-gate 
27140Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
27150Sstevel@tonic-gate 
27160Sstevel@tonic-gate 	if (entry)
27170Sstevel@tonic-gate 		*entry = ENTRY_INIT;
27180Sstevel@tonic-gate 	if (title)
27190Sstevel@tonic-gate 		*title = NULL;
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
27220Sstevel@tonic-gate 	if (eq == NULL) {
27230Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
27240Sstevel@tonic-gate 		free(opt_dup);
27250Sstevel@tonic-gate 		return (BAM_ERROR);
27260Sstevel@tonic-gate 	}
27270Sstevel@tonic-gate 
27280Sstevel@tonic-gate 	*eq = '\0';
27290Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
27300Sstevel@tonic-gate 		assert(mp->end);
27310Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
27320Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
27330Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
27340Sstevel@tonic-gate 			free(opt_dup);
27350Sstevel@tonic-gate 			return (BAM_ERROR);
27360Sstevel@tonic-gate 		}
27370Sstevel@tonic-gate 		*entry = entryNum;
27380Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
27390Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
27400Sstevel@tonic-gate 	} else {
27410Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
27420Sstevel@tonic-gate 		free(opt_dup);
27430Sstevel@tonic-gate 		return (BAM_ERROR);
27440Sstevel@tonic-gate 	}
27450Sstevel@tonic-gate 
27460Sstevel@tonic-gate 	free(opt_dup);
27470Sstevel@tonic-gate 	return (BAM_SUCCESS);
27480Sstevel@tonic-gate }
27490Sstevel@tonic-gate 
27500Sstevel@tonic-gate /*
27510Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
27520Sstevel@tonic-gate  * only title lines in file are printed.
27530Sstevel@tonic-gate  *
27540Sstevel@tonic-gate  * If invoked with a title or entry #, all
27550Sstevel@tonic-gate  * lines in *every* matching entry are listed
27560Sstevel@tonic-gate  */
27570Sstevel@tonic-gate static error_t
27580Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
27590Sstevel@tonic-gate {
27600Sstevel@tonic-gate 	line_t *lp;
27610Sstevel@tonic-gate 	int entry = ENTRY_INIT;
27620Sstevel@tonic-gate 	int found;
27630Sstevel@tonic-gate 	char *title = NULL;
27640Sstevel@tonic-gate 
27650Sstevel@tonic-gate 	assert(mp);
27660Sstevel@tonic-gate 	assert(menu_path);
27670Sstevel@tonic-gate 
27680Sstevel@tonic-gate 	if (mp->start == NULL) {
27690Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
27700Sstevel@tonic-gate 		return (BAM_ERROR);
27710Sstevel@tonic-gate 	}
27720Sstevel@tonic-gate 
27730Sstevel@tonic-gate 	if (opt != NULL) {
27740Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
27750Sstevel@tonic-gate 			return (BAM_ERROR);
27760Sstevel@tonic-gate 		}
27770Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
27780Sstevel@tonic-gate 	} else {
27790Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
27800Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
27810Sstevel@tonic-gate 	}
27820Sstevel@tonic-gate 
27830Sstevel@tonic-gate 	found = 0;
27840Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
27850Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
27860Sstevel@tonic-gate 			continue;
27870Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
27880Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
27890Sstevel@tonic-gate 			    lp->arg);
27900Sstevel@tonic-gate 			found = 1;
27910Sstevel@tonic-gate 			continue;
27920Sstevel@tonic-gate 		}
27930Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
27940Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
27950Sstevel@tonic-gate 			found = 1;
27960Sstevel@tonic-gate 			continue;
27970Sstevel@tonic-gate 		}
27980Sstevel@tonic-gate 
27990Sstevel@tonic-gate 		/*
28000Sstevel@tonic-gate 		 * We set the entry value here so that all lines
28010Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
28020Sstevel@tonic-gate 		 * title in other entries, all lines in those
28030Sstevel@tonic-gate 		 * entries get printed as well.
28040Sstevel@tonic-gate 		 */
28050Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
28060Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
28070Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
28080Sstevel@tonic-gate 			entry = lp->entryNum;
28090Sstevel@tonic-gate 			found = 1;
28100Sstevel@tonic-gate 			continue;
28110Sstevel@tonic-gate 		}
28120Sstevel@tonic-gate 	}
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 	if (!found) {
28150Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
28160Sstevel@tonic-gate 		return (BAM_ERROR);
28170Sstevel@tonic-gate 	}
28180Sstevel@tonic-gate 
28190Sstevel@tonic-gate 	return (BAM_SUCCESS);
28200Sstevel@tonic-gate }
28210Sstevel@tonic-gate 
28220Sstevel@tonic-gate static int
28230Sstevel@tonic-gate add_boot_entry(menu_t *mp,
28240Sstevel@tonic-gate 	char *title,
28250Sstevel@tonic-gate 	char *root,
28260Sstevel@tonic-gate 	char *kernel,
28270Sstevel@tonic-gate 	char *module)
28280Sstevel@tonic-gate {
28290Sstevel@tonic-gate 	int lineNum, entryNum;
28300Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
28313446Smrj 	menu_cmd_t k_cmd, m_cmd;
28320Sstevel@tonic-gate 
28330Sstevel@tonic-gate 	assert(mp);
28340Sstevel@tonic-gate 
28350Sstevel@tonic-gate 	if (title == NULL) {
2836656Sszhou 		title = "Solaris";	/* default to Solaris */
28370Sstevel@tonic-gate 	}
28380Sstevel@tonic-gate 	if (kernel == NULL) {
28390Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
28400Sstevel@tonic-gate 		return (BAM_ERROR);
28410Sstevel@tonic-gate 	}
28420Sstevel@tonic-gate 	if (module == NULL) {
28433446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
28443446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
28453446Smrj 			return (BAM_ERROR);
28463446Smrj 		}
28473446Smrj 
28483446Smrj 		/* Figure the commands out from the kernel line */
28493446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
28503446Smrj 			module = DIRECT_BOOT_ARCHIVE;
28513446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
28523446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
28533446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
28543446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
28553446Smrj 			k_cmd = KERNEL_CMD;
28563446Smrj 			m_cmd = MODULE_CMD;
28573446Smrj 		} else {
28583446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
28593446Smrj 			k_cmd = KERNEL_CMD;
28603446Smrj 			m_cmd = MODULE_CMD;
28613446Smrj 		}
28623446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
28633446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
28643446Smrj 		/*
28653446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
28663446Smrj 		 * command.  Otherwise, use "kernel".
28673446Smrj 		 */
28683446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
28693446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
28703446Smrj 	} else {
28713446Smrj 		k_cmd = KERNEL_CMD;
28723446Smrj 		m_cmd = MODULE_CMD;
28730Sstevel@tonic-gate 	}
28740Sstevel@tonic-gate 
28750Sstevel@tonic-gate 	if (mp->start) {
28760Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
28770Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
28780Sstevel@tonic-gate 	} else {
28790Sstevel@tonic-gate 		lineNum = LINE_INIT;
28800Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
28810Sstevel@tonic-gate 	}
28820Sstevel@tonic-gate 
28830Sstevel@tonic-gate 	/*
28840Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
28850Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
28860Sstevel@tonic-gate 	 */
28870Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
28883446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
2889662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
28900Sstevel@tonic-gate 
28910Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
28920Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
2893662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
2894662Sszhou 
2895662Sszhou 	if (root) {
2896662Sszhou 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2897662Sszhou 		    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
2898662Sszhou 		line_parser(mp, linebuf, &lineNum, &entryNum);
28990Sstevel@tonic-gate 	}
29000Sstevel@tonic-gate 
29010Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29023446Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
2903662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29040Sstevel@tonic-gate 
29050Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
29063446Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
2907662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29080Sstevel@tonic-gate 
29090Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
29103446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
2911662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
29120Sstevel@tonic-gate 
29130Sstevel@tonic-gate 	return (entryNum);
29140Sstevel@tonic-gate }
29150Sstevel@tonic-gate 
29160Sstevel@tonic-gate static error_t
29170Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
29180Sstevel@tonic-gate {
2919662Sszhou 	line_t *lp, *freed;
2920662Sszhou 	entry_t *ent, *tmp;
29210Sstevel@tonic-gate 	int deleted;
29220Sstevel@tonic-gate 
29230Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
29240Sstevel@tonic-gate 
2925662Sszhou 	ent = mp->entries;
2926662Sszhou 	while (ent) {
2927662Sszhou 		lp = ent->start;
2928662Sszhou 		/* check entry number and make sure it's a bootadm entry */
2929662Sszhou 		if (lp->flags != BAM_COMMENT ||
29303446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
2931662Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
2932662Sszhou 			ent = ent->next;
29330Sstevel@tonic-gate 			continue;
29340Sstevel@tonic-gate 		}
29350Sstevel@tonic-gate 
2936662Sszhou 		/* free the entry content */
2937662Sszhou 		do {
2938662Sszhou 			freed = lp;
2939662Sszhou 			lp = lp->next;	/* prev stays the same */
2940662Sszhou 			unlink_line(mp, freed);
2941662Sszhou 			line_free(freed);
2942662Sszhou 		} while (freed != ent->end);
2943662Sszhou 
2944662Sszhou 		/* free the entry_t structure */
2945662Sszhou 		tmp = ent;
2946662Sszhou 		ent = ent->next;
2947662Sszhou 		if (tmp->prev)
2948662Sszhou 			tmp->prev->next = ent;
29490Sstevel@tonic-gate 		else
2950662Sszhou 			mp->entries = ent;
2951662Sszhou 		if (ent)
2952662Sszhou 			ent->prev = tmp->prev;
29530Sstevel@tonic-gate 		deleted = 1;
29540Sstevel@tonic-gate 	}
29550Sstevel@tonic-gate 
29560Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
29570Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
29580Sstevel@tonic-gate 		return (BAM_ERROR);
29590Sstevel@tonic-gate 	}
29600Sstevel@tonic-gate 
2961621Svikram 	/*
2962621Svikram 	 * Now that we have deleted an entry, update
2963621Svikram 	 * the entry numbering and the default cmd.
2964621Svikram 	 */
2965621Svikram 	update_numbering(mp);
2966621Svikram 
29670Sstevel@tonic-gate 	return (BAM_SUCCESS);
29680Sstevel@tonic-gate }
29690Sstevel@tonic-gate 
29700Sstevel@tonic-gate static error_t
29710Sstevel@tonic-gate delete_all_entries(menu_t *mp, char *menu_path, char *opt)
29720Sstevel@tonic-gate {
29730Sstevel@tonic-gate 	assert(mp);
29740Sstevel@tonic-gate 	assert(opt == NULL);
29750Sstevel@tonic-gate 
29760Sstevel@tonic-gate 	if (mp->start == NULL) {
29770Sstevel@tonic-gate 		bam_print(EMPTY_FILE, menu_path);
29780Sstevel@tonic-gate 		return (BAM_SUCCESS);
29790Sstevel@tonic-gate 	}
29800Sstevel@tonic-gate 
29810Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
29820Sstevel@tonic-gate 		return (BAM_ERROR);
29830Sstevel@tonic-gate 	}
29840Sstevel@tonic-gate 
29850Sstevel@tonic-gate 	return (BAM_WRITE);
29860Sstevel@tonic-gate }
29870Sstevel@tonic-gate 
29880Sstevel@tonic-gate static FILE *
2989662Sszhou open_diskmap(char *root)
29900Sstevel@tonic-gate {
29910Sstevel@tonic-gate 	FILE *fp;
29920Sstevel@tonic-gate 	char cmd[PATH_MAX];
29930Sstevel@tonic-gate 
29940Sstevel@tonic-gate 	/* make sure we have a map file */
29950Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
29960Sstevel@tonic-gate 	if (fp == NULL) {
29970Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
2998662Sszhou 		    "%s%s > /dev/null", root, CREATE_DISKMAP);
29990Sstevel@tonic-gate 		(void) system(cmd);
30000Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
30010Sstevel@tonic-gate 	}
30020Sstevel@tonic-gate 	return (fp);
30030Sstevel@tonic-gate }
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate #define	SECTOR_SIZE	512
30060Sstevel@tonic-gate 
30070Sstevel@tonic-gate static int
30080Sstevel@tonic-gate get_partition(char *device)
30090Sstevel@tonic-gate {
30100Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
30110Sstevel@tonic-gate 	struct mboot *mboot;
30120Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
30130Sstevel@tonic-gate 	char *wholedisk, *slice;
30140Sstevel@tonic-gate 
30150Sstevel@tonic-gate 	/* form whole disk (p0) */
30160Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
30170Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
30180Sstevel@tonic-gate 	if (!is_pcfs)
30190Sstevel@tonic-gate 		*slice = '\0';
30200Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
30210Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
30220Sstevel@tonic-gate 	if (!is_pcfs)
30230Sstevel@tonic-gate 		*slice = 's';
30240Sstevel@tonic-gate 
30250Sstevel@tonic-gate 	/* read boot sector */
30260Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
30270Sstevel@tonic-gate 	free(wholedisk);
30280Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
30290Sstevel@tonic-gate 		return (partno);
30300Sstevel@tonic-gate 	}
30310Sstevel@tonic-gate 	(void) close(fd);
30320Sstevel@tonic-gate 
30330Sstevel@tonic-gate 	/* parse fdisk table */
30340Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
30350Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
30360Sstevel@tonic-gate 		struct ipart *part =
30370Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
30380Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
30390Sstevel@tonic-gate 			if (part->systid == 0xbe) {
30400Sstevel@tonic-gate 				partno = i;
30410Sstevel@tonic-gate 				break;
30420Sstevel@tonic-gate 			}
30430Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
30440Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
30450Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
30460Sstevel@tonic-gate 				partno = i;
30470Sstevel@tonic-gate 				break;
30480Sstevel@tonic-gate 			}
30490Sstevel@tonic-gate 		}
30500Sstevel@tonic-gate 	}
30510Sstevel@tonic-gate 	return (partno);
30520Sstevel@tonic-gate }
30530Sstevel@tonic-gate 
30540Sstevel@tonic-gate static char *
30550Sstevel@tonic-gate get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
30560Sstevel@tonic-gate {
30570Sstevel@tonic-gate 	char *grubdisk;	/* (hd#,#,#) */
30580Sstevel@tonic-gate 	char *slice;
30590Sstevel@tonic-gate 	char *grubhd;
30600Sstevel@tonic-gate 	int fdiskpart;
30610Sstevel@tonic-gate 	int found = 0;
30620Sstevel@tonic-gate 	char *devname, *ctdname = strstr(rootdev, "dsk/");
30630Sstevel@tonic-gate 	char linebuf[PATH_MAX];
30640Sstevel@tonic-gate 
30650Sstevel@tonic-gate 	if (ctdname == NULL)
30660Sstevel@tonic-gate 		return (NULL);
30670Sstevel@tonic-gate 
30680Sstevel@tonic-gate 	ctdname += strlen("dsk/");
30690Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
30700Sstevel@tonic-gate 	if (slice)
30710Sstevel@tonic-gate 		*slice = '\0';
30720Sstevel@tonic-gate 
30730Sstevel@tonic-gate 	rewind(fp);
30740Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
30750Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
30760Sstevel@tonic-gate 		if (grubhd)
30770Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
30780Sstevel@tonic-gate 		else
30790Sstevel@tonic-gate 			devname = NULL;
30800Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
30810Sstevel@tonic-gate 			found = 1;
30820Sstevel@tonic-gate 			break;
30830Sstevel@tonic-gate 		}
30840Sstevel@tonic-gate 	}
30850Sstevel@tonic-gate 
30860Sstevel@tonic-gate 	if (slice)
30870Sstevel@tonic-gate 		*slice = 's';
30880Sstevel@tonic-gate 
30890Sstevel@tonic-gate 	if (found == 0) {
30900Sstevel@tonic-gate 		if (bam_verbose)
30910Sstevel@tonic-gate 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
30920Sstevel@tonic-gate 		grubhd = "0";	/* assume disk 0 if can't match */
30930Sstevel@tonic-gate 	}
30940Sstevel@tonic-gate 
30950Sstevel@tonic-gate 	fdiskpart = get_partition(rootdev);
30960Sstevel@tonic-gate 	if (fdiskpart == -1)
30970Sstevel@tonic-gate 		return (NULL);
30980Sstevel@tonic-gate 
30990Sstevel@tonic-gate 	grubdisk = s_calloc(1, 10);
31000Sstevel@tonic-gate 	if (slice) {
31010Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
31020Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
31030Sstevel@tonic-gate 	} else
31040Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
31050Sstevel@tonic-gate 		    grubhd, fdiskpart);
31060Sstevel@tonic-gate 
31070Sstevel@tonic-gate 	/* if root not on bootdev, change GRUB disk to 0 */
31080Sstevel@tonic-gate 	if (!on_bootdev)
31090Sstevel@tonic-gate 		grubdisk[3] = '0';
31100Sstevel@tonic-gate 	return (grubdisk);
31110Sstevel@tonic-gate }
31120Sstevel@tonic-gate 
3113656Sszhou static char *
3114656Sszhou get_title(char *rootdir)
31150Sstevel@tonic-gate {
31160Sstevel@tonic-gate 	static char title[80];	/* from /etc/release */
3117662Sszhou 	char *cp = NULL, release[PATH_MAX];
31180Sstevel@tonic-gate 	FILE *fp;
31190Sstevel@tonic-gate 
31200Sstevel@tonic-gate 	/* open the /etc/release file */
31210Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
31220Sstevel@tonic-gate 
31230Sstevel@tonic-gate 	fp = fopen(release, "r");
31240Sstevel@tonic-gate 	if (fp == NULL)
3125656Sszhou 		return (NULL);
31260Sstevel@tonic-gate 
31270Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
31280Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
31290Sstevel@tonic-gate 		if (cp)
31300Sstevel@tonic-gate 			break;
31310Sstevel@tonic-gate 	}
31320Sstevel@tonic-gate 	(void) fclose(fp);
3133662Sszhou 	return (cp == NULL ? "Solaris" : cp);
31340Sstevel@tonic-gate }
31350Sstevel@tonic-gate 
31363446Smrj char *
31370Sstevel@tonic-gate get_special(char *mountp)
31380Sstevel@tonic-gate {
31390Sstevel@tonic-gate 	FILE *mntfp;
31400Sstevel@tonic-gate 	struct mnttab mp = {0}, mpref = {0};
31410Sstevel@tonic-gate 
31420Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
31430Sstevel@tonic-gate 	if (mntfp == NULL) {
31440Sstevel@tonic-gate 		return (0);
31450Sstevel@tonic-gate 	}
31460Sstevel@tonic-gate 
31470Sstevel@tonic-gate 	if (*mountp == '\0')
31480Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
31490Sstevel@tonic-gate 	else
31500Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
31510Sstevel@tonic-gate 	if (getmntany(mntfp, &mp, &mpref) != 0) {
31520Sstevel@tonic-gate 		(void) fclose(mntfp);
31530Sstevel@tonic-gate 		return (NULL);
31540Sstevel@tonic-gate 	}
31550Sstevel@tonic-gate 	(void) fclose(mntfp);
31560Sstevel@tonic-gate 
31570Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
31580Sstevel@tonic-gate }
31590Sstevel@tonic-gate 
31603446Smrj char *
31610Sstevel@tonic-gate os_to_grubdisk(char *osdisk, int on_bootdev)
31620Sstevel@tonic-gate {
31630Sstevel@tonic-gate 	FILE *fp;
31640Sstevel@tonic-gate 	char *grubdisk;
31650Sstevel@tonic-gate 
31660Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3167662Sszhou 	fp = open_diskmap("");
31680Sstevel@tonic-gate 	if (fp == NULL) {
31690Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdisk);
31700Sstevel@tonic-gate 		return (NULL);
31710Sstevel@tonic-gate 	}
31720Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
31730Sstevel@tonic-gate 	(void) fclose(fp);
31740Sstevel@tonic-gate 	return (grubdisk);
31750Sstevel@tonic-gate }
31760Sstevel@tonic-gate 
31770Sstevel@tonic-gate /*
31780Sstevel@tonic-gate  * Check if root is on the boot device
31790Sstevel@tonic-gate  * Return 0 (false) on error
31800Sstevel@tonic-gate  */
31810Sstevel@tonic-gate static int
31820Sstevel@tonic-gate menu_on_bootdev(char *menu_root, FILE *fp)
31830Sstevel@tonic-gate {
31840Sstevel@tonic-gate 	int ret;
31850Sstevel@tonic-gate 	char *grubhd, *bootp, *special;
31860Sstevel@tonic-gate 
31870Sstevel@tonic-gate 	special = get_special(menu_root);
31880Sstevel@tonic-gate 	if (special == NULL)
31890Sstevel@tonic-gate 		return (0);
31900Sstevel@tonic-gate 	bootp = strstr(special, "p0:boot");
31910Sstevel@tonic-gate 	if (bootp)
31920Sstevel@tonic-gate 		*bootp = '\0';
31930Sstevel@tonic-gate 	grubhd = get_grubdisk(special, fp, 1);
31940Sstevel@tonic-gate 	free(special);
31950Sstevel@tonic-gate 
31960Sstevel@tonic-gate 	if (grubhd == NULL)
31970Sstevel@tonic-gate 		return (0);
31980Sstevel@tonic-gate 	ret = grubhd[3] == '0';
31990Sstevel@tonic-gate 	free(grubhd);
32000Sstevel@tonic-gate 	return (ret);
32010Sstevel@tonic-gate }
32020Sstevel@tonic-gate 
3203662Sszhou /*
3204662Sszhou  * look for matching bootadm entry with specified parameters
3205662Sszhou  * Here are the rules (based on existing usage):
3206662Sszhou  * - If title is specified, match on title only
3207662Sszhou  * - Else, match on grubdisk and module (don't care about kernel line).
3208662Sszhou  *   note that, if root_opt is non-zero, the absence of root line is
3209662Sszhou  *   considered a match.
3210662Sszhou  */
3211662Sszhou static entry_t *
3212662Sszhou find_boot_entry(menu_t *mp, char *title, char *root, char *module,
3213662Sszhou     int root_opt, int *entry_num)
3214662Sszhou {
3215662Sszhou 	int i;
3216662Sszhou 	line_t *lp;
3217662Sszhou 	entry_t *ent;
3218662Sszhou 
3219662Sszhou 	/* find matching entry */
3220662Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
3221662Sszhou 		lp = ent->start;
3222662Sszhou 
3223662Sszhou 		/* first line of entry must be bootadm comment */
3224662Sszhou 		lp = ent->start;
32253446Smrj 		if (lp->flags != BAM_COMMENT ||
32263446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
3227662Sszhou 			continue;
3228662Sszhou 		}
3229662Sszhou 
3230662Sszhou 		/* advance to title line */
3231662Sszhou 		lp = lp->next;
3232662Sszhou 		if (title) {
3233662Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
3234662Sszhou 			    strcmp(lp->arg, title) == 0)
3235662Sszhou 				break;
3236662Sszhou 			continue;	/* check title only */
3237662Sszhou 		}
3238662Sszhou 
3239662Sszhou 		lp = lp->next;	/* advance to root line */
3240662Sszhou 		if (lp == NULL || strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3241662Sszhou 			/* root command found, match grub disk */
3242662Sszhou 			if (strcmp(lp->arg, root) != 0) {
3243662Sszhou 				continue;
3244662Sszhou 			}
3245662Sszhou 			lp = lp->next;	/* advance to kernel line */
3246662Sszhou 		} else {
3247662Sszhou 			/* no root command, see if root is optional */
3248662Sszhou 			if (root_opt == 0) {
3249662Sszhou 				continue;
3250662Sszhou 			}
3251662Sszhou 		}
3252662Sszhou 
3253662Sszhou 		if (lp == NULL || lp->next == NULL) {
3254662Sszhou 			continue;
3255662Sszhou 		}
3256662Sszhou 
32573467Srscott 		/*
32583467Srscott 		 * Check for matching module entry (failsafe or normal).  We
32593467Srscott 		 * use a strncmp to match "module" or "module$", since we
32603467Srscott 		 * don't know which one it should be.  If it fails to match,
32613467Srscott 		 * we go around the loop again.
32623467Srscott 		 */
3263662Sszhou 		lp = lp->next;	/* advance to module line */
32643446Smrj 		if ((strncmp(lp->cmd, menu_cmds[MODULE_CMD],
32653446Smrj 		    strlen(menu_cmds[MODULE_CMD])) != 0) ||
32663446Smrj 		    (strcmp(lp->arg, module) != 0)) {
3267662Sszhou 			continue;
3268662Sszhou 		}
3269662Sszhou 		break;	/* match found */
3270662Sszhou 	}
3271662Sszhou 
3272662Sszhou 	*entry_num = i;
3273662Sszhou 	return (ent);
3274662Sszhou }
3275662Sszhou 
3276662Sszhou static int
3277662Sszhou update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
3278662Sszhou     char *module, int root_opt)
3279662Sszhou {
32803446Smrj 	int i, change_kernel = 0;
3281662Sszhou 	entry_t *ent;
3282662Sszhou 	line_t *lp;
3283662Sszhou 	char linebuf[BAM_MAXLINE];
3284662Sszhou 
3285662Sszhou 	/* note: don't match on title, it's updated on upgrade */
3286662Sszhou 	ent = find_boot_entry(mp, NULL, root, module, root_opt, &i);
32873446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
32883446Smrj 		/*
32893446Smrj 		 * We may be upgrading a kernel from multiboot to
32903446Smrj 		 * directboot.  Look for a multiboot entry.
32913446Smrj 		 */
32923446Smrj 		ent = find_boot_entry(mp, NULL, root, MULTI_BOOT_ARCHIVE,
32933446Smrj 		    root_opt, &i);
32943446Smrj 		if (ent != NULL) {
32953446Smrj 			change_kernel = 1;
32963446Smrj 		}
32973446Smrj 	}
3298662Sszhou 	if (ent == NULL)
3299662Sszhou 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
3300662Sszhou 		    kernel, module));
3301662Sszhou 
3302662Sszhou 	/* replace title of exiting entry and delete root line */
3303662Sszhou 	lp = ent->start;
3304662Sszhou 	lp = lp->next;	/* title line */
3305662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3306662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3307662Sszhou 	free(lp->arg);
3308662Sszhou 	free(lp->line);
3309662Sszhou 	lp->arg = s_strdup(title);
3310662Sszhou 	lp->line = s_strdup(linebuf);
3311662Sszhou 
3312662Sszhou 	lp = lp->next;	/* root line */
3313662Sszhou 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3314662Sszhou 		if (root_opt) {		/* root line not needed */
3315662Sszhou 			line_t *tmp = lp;
3316662Sszhou 			lp = lp->next;
3317662Sszhou 			unlink_line(mp, tmp);
3318662Sszhou 			line_free(tmp);
3319662Sszhou 		} else
3320662Sszhou 			lp = lp->next;
3321662Sszhou 	}
33223446Smrj 
33233446Smrj 	if (change_kernel) {
33243446Smrj 		/*
33253446Smrj 		 * We're upgrading from multiboot to directboot.
33263446Smrj 		 */
33273446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
33283446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
33293446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
33303446Smrj 			    kernel);
33313446Smrj 			free(lp->arg);
33323446Smrj 			free(lp->line);
33333446Smrj 			lp->arg = s_strdup(kernel);
33343446Smrj 			lp->line = s_strdup(linebuf);
33353446Smrj 			lp = lp->next;
33363446Smrj 		}
33373446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
33383446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
33393446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
33403446Smrj 			    module);
33413446Smrj 			free(lp->arg);
33423446Smrj 			free(lp->line);
33433446Smrj 			lp->arg = s_strdup(module);
33443446Smrj 			lp->line = s_strdup(linebuf);
33453446Smrj 			lp = lp->next;
33463446Smrj 		}
33473446Smrj 	}
3348662Sszhou 	return (i);
3349662Sszhou }
3350662Sszhou 
33510Sstevel@tonic-gate /*ARGSUSED*/
33520Sstevel@tonic-gate static error_t
33530Sstevel@tonic-gate update_entry(menu_t *mp, char *menu_root, char *opt)
33540Sstevel@tonic-gate {
33550Sstevel@tonic-gate 	FILE *fp;
33560Sstevel@tonic-gate 	int entry;
33573449Srscott 	char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL;
3358662Sszhou 	struct stat sbuf;
3359662Sszhou 	char failsafe[256];
33600Sstevel@tonic-gate 
33610Sstevel@tonic-gate 	assert(mp);
33620Sstevel@tonic-gate 	assert(opt);
33630Sstevel@tonic-gate 
33640Sstevel@tonic-gate 	osdev = strtok(opt, ",");
33650Sstevel@tonic-gate 	osroot = strtok(NULL, ",");
33660Sstevel@tonic-gate 	if (osroot == NULL)
33670Sstevel@tonic-gate 		osroot = menu_root;
33680Sstevel@tonic-gate 	title = get_title(osroot);
33690Sstevel@tonic-gate 
33700Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3371662Sszhou 	fp = open_diskmap(osroot);
33720Sstevel@tonic-gate 	if (fp == NULL) {
33730Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
33740Sstevel@tonic-gate 		return (BAM_ERROR);
33750Sstevel@tonic-gate 	}
33760Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
33770Sstevel@tonic-gate 	(void) fclose(fp);
33780Sstevel@tonic-gate 	if (grubdisk == NULL) {
33790Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
33800Sstevel@tonic-gate 		return (BAM_ERROR);
33810Sstevel@tonic-gate 	}
33820Sstevel@tonic-gate 
33830Sstevel@tonic-gate 	/* add the entry for normal Solaris */
33843446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
33853446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
33863446Smrj 		    DIRECT_BOOT_KERNEL, DIRECT_BOOT_ARCHIVE,
33873446Smrj 		    osroot == menu_root);
33883446Smrj 	} else {
33893446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
33903446Smrj 		    MULTI_BOOT, MULTI_BOOT_ARCHIVE,
33913446Smrj 		    osroot == menu_root);
33923446Smrj 	}
33930Sstevel@tonic-gate 
33940Sstevel@tonic-gate 	/* add the entry for failsafe archive */
33953446Smrj 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
33963446Smrj 	if (stat(failsafe, &sbuf) == 0) {
33973449Srscott 
33983449Srscott 		/* Figure out where the kernel line should point */
33993449Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
34003449Srscott 		    DIRECT_BOOT_FAILSAFE_KERNEL);
34013449Srscott 		if (stat(failsafe, &sbuf) == 0) {
34023449Srscott 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
34033449Srscott 		} else {
34043449Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
34053449Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
34063449Srscott 			if (stat(failsafe, &sbuf) == 0) {
34073449Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
34083449Srscott 			}
34093449Srscott 		}
34103449Srscott 		if (failsafe_kernel != NULL) {
34113449Srscott 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
34123449Srscott 			    failsafe_kernel, MINIROOT, osroot == menu_root);
34133449Srscott 		} else {
34143449Srscott 			bam_error(NO_FAILSAFE_KERNEL);
34153449Srscott 		}
34163446Smrj 	}
34170Sstevel@tonic-gate 	free(grubdisk);
34180Sstevel@tonic-gate 
34190Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
34200Sstevel@tonic-gate 		return (BAM_ERROR);
34210Sstevel@tonic-gate 	}
34220Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
34230Sstevel@tonic-gate 	return (BAM_WRITE);
34240Sstevel@tonic-gate }
34250Sstevel@tonic-gate 
3426316Svikram static char *
3427316Svikram read_grub_root(void)
3428316Svikram {
3429316Svikram 	FILE *fp;
3430316Svikram 	struct stat sb;
3431316Svikram 	char buf[BAM_MAXLINE];
3432316Svikram 	char *rootstr;
3433316Svikram 
3434316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3435316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3436316Svikram 		return (NULL);
3437316Svikram 	}
3438316Svikram 
3439316Svikram 	if (stat(GRUB_root, &sb) != 0) {
3440316Svikram 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3441316Svikram 		return (NULL);
3442316Svikram 	}
3443316Svikram 
3444316Svikram 	fp = fopen(GRUB_root, "r");
3445316Svikram 	if (fp == NULL) {
3446316Svikram 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3447316Svikram 		return (NULL);
3448316Svikram 	}
3449316Svikram 
3450316Svikram 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3451316Svikram 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3452316Svikram 		(void) fclose(fp);
3453316Svikram 		return (NULL);
3454316Svikram 	}
3455316Svikram 
3456316Svikram 	/*
3457316Svikram 	 * Copy buf here as check below may trash the buffer
3458316Svikram 	 */
3459316Svikram 	rootstr = s_strdup(buf);
3460316Svikram 
3461316Svikram 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3462316Svikram 		bam_error(BAD_ROOT_FILE, GRUB_root);
3463316Svikram 		free(rootstr);
3464316Svikram 		rootstr = NULL;
3465316Svikram 	}
3466316Svikram 
3467316Svikram 	(void) fclose(fp);
3468316Svikram 
3469316Svikram 	return (rootstr);
3470316Svikram }
3471316Svikram 
3472662Sszhou static void
34733446Smrj save_default_entry(menu_t *mp, const char *which)
3474662Sszhou {
3475662Sszhou 	int lineNum, entryNum;
3476662Sszhou 	int entry = 0;	/* default is 0 */
3477662Sszhou 	char linebuf[BAM_MAXLINE];
3478662Sszhou 	line_t *lp = mp->curdefault;
3479662Sszhou 
34803381Svikram 	if (mp->start) {
34813381Svikram 		lineNum = mp->end->lineNum;
34823381Svikram 		entryNum = mp->end->entryNum;
34833381Svikram 	} else {
34843381Svikram 		lineNum = LINE_INIT;
34853381Svikram 		entryNum = ENTRY_INIT;
34863381Svikram 	}
34873381Svikram 
3488662Sszhou 	if (lp)
3489662Sszhou 		entry = s_strtol(lp->arg);
3490662Sszhou 
34913446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
3492662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
3493662Sszhou }
3494662Sszhou 
3495662Sszhou static void
34963446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
3497662Sszhou {
3498662Sszhou 	int entry;
3499662Sszhou 	char *str;
3500662Sszhou 
3501662Sszhou 	if (lp == NULL)
3502662Sszhou 		return;		/* nothing to restore */
3503662Sszhou 
35043446Smrj 	str = lp->arg + strlen(which);
3505662Sszhou 	entry = s_strtol(str);
3506662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3507662Sszhou 
3508662Sszhou 	/* delete saved old default line */
3509662Sszhou 	unlink_line(mp, lp);
3510662Sszhou 	line_free(lp);
3511662Sszhou }
3512662Sszhou 
35130Sstevel@tonic-gate /*
35140Sstevel@tonic-gate  * This function is for supporting reboot with args.
35150Sstevel@tonic-gate  * The opt value can be:
35160Sstevel@tonic-gate  * NULL		delete temp entry, if present
35170Sstevel@tonic-gate  * entry=#	switches default entry to 1
35180Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
35190Sstevel@tonic-gate  *		and make it the default
35200Sstevel@tonic-gate  */
35210Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
35220Sstevel@tonic-gate 
3523662Sszhou /*ARGSUSED*/
35240Sstevel@tonic-gate static error_t
35250Sstevel@tonic-gate update_temp(menu_t *mp, char *menupath, char *opt)
35260Sstevel@tonic-gate {
35270Sstevel@tonic-gate 	int entry;
35284346Srscott 	char *grubdisk, *rootdev, *path, *opt_ptr;
35293446Smrj 	char kernbuf[BUFSIZ];
35303446Smrj 	char args_buf[BUFSIZ];
3531316Svikram 	struct stat sb;
35320Sstevel@tonic-gate 
35330Sstevel@tonic-gate 	assert(mp);
35340Sstevel@tonic-gate 
3535662Sszhou 	/* If no option, delete exiting reboot menu entry */
3536662Sszhou 	if (opt == NULL) {
3537662Sszhou 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
3538662Sszhou 		    0, &entry);
3539662Sszhou 		if (ent == NULL)	/* not found is ok */
3540662Sszhou 			return (BAM_SUCCESS);
3541662Sszhou 		(void) do_delete(mp, entry);
35423446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
35433446Smrj 		mp->olddefault = NULL;
3544662Sszhou 		return (BAM_WRITE);
3545662Sszhou 	}
3546662Sszhou 
3547662Sszhou 	/* if entry= is specified, set the default entry */
3548662Sszhou 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
35490Sstevel@tonic-gate 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
35500Sstevel@tonic-gate 		/* this is entry=# option */
35510Sstevel@tonic-gate 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
35520Sstevel@tonic-gate 	}
35530Sstevel@tonic-gate 
35540Sstevel@tonic-gate 	/*
35550Sstevel@tonic-gate 	 * add a new menu entry base on opt and make it the default
35560Sstevel@tonic-gate 	 */
3557316Svikram 	grubdisk = NULL;
3558316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3559316Svikram 		/*
3560316Svikram 		 * 1. First get root disk name from mnttab
3561316Svikram 		 * 2. Translate disk name to grub name
3562316Svikram 		 * 3. Add the new menu entry
3563316Svikram 		 */
3564316Svikram 		rootdev = get_special("/");
3565316Svikram 		if (rootdev) {
3566316Svikram 			grubdisk = os_to_grubdisk(rootdev, 1);
3567316Svikram 			free(rootdev);
3568316Svikram 		}
3569316Svikram 	} else {
3570316Svikram 		/*
3571316Svikram 		 * This is an LU BE. The GRUB_root file
3572316Svikram 		 * contains entry for GRUB's "root" cmd.
3573316Svikram 		 */
3574316Svikram 		grubdisk = read_grub_root();
35750Sstevel@tonic-gate 	}
35760Sstevel@tonic-gate 	if (grubdisk == NULL) {
3577316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
35780Sstevel@tonic-gate 		return (BAM_ERROR);
35790Sstevel@tonic-gate 	}
35800Sstevel@tonic-gate 
35810Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
35823446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
35833446Smrj 		if (opt[0] == '-') {
35843446Smrj 			/* It's an option - first see if boot-file is set */
35853446Smrj 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
35863446Smrj 			    != BAM_SUCCESS)
35873446Smrj 				return (BAM_ERROR);
35883446Smrj 			if (kernbuf[0] == '\0')
35893446Smrj 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
35903446Smrj 				    BUFSIZ);
35913446Smrj 			(void) strlcat(kernbuf, " ", BUFSIZ);
35923446Smrj 			(void) strlcat(kernbuf, opt, BUFSIZ);
35933446Smrj 		} else if (opt[0] == '/') {
35944346Srscott 			/* It's a full path, so write it out. */
35953446Smrj 			(void) strlcpy(kernbuf, opt, BUFSIZ);
35964346Srscott 
35974346Srscott 			/*
35984346Srscott 			 * If someone runs:
35994346Srscott 			 *
36004346Srscott 			 *	# eeprom boot-args='-kd'
36014346Srscott 			 *	# reboot /platform/i86pc/kernel/unix
36024346Srscott 			 *
36034346Srscott 			 * we want to use the boot-args as part of the boot
36044346Srscott 			 * line.  On the other hand, if someone runs:
36054346Srscott 			 *
36064346Srscott 			 *	# reboot "/platform/i86pc/kernel/unix -kd"
36074346Srscott 			 *
36084346Srscott 			 * we don't need to mess with boot-args.  If there's
36094346Srscott 			 * no space in the options string, assume we're in the
36104346Srscott 			 * first case.
36114346Srscott 			 */
36124346Srscott 			if (strchr(opt, ' ') == NULL) {
36134346Srscott 				if (set_kernel(mp, ARGS_CMD, NULL, args_buf,
36144346Srscott 				    BUFSIZ) != BAM_SUCCESS)
36154346Srscott 					return (BAM_ERROR);
36164346Srscott 
36174346Srscott 				if (args_buf[0] != '\0') {
36184346Srscott 					(void) strlcat(kernbuf, " ", BUFSIZ);
36194346Srscott 					(void) strlcat(kernbuf, args_buf,
36204346Srscott 					    BUFSIZ);
36214346Srscott 				}
36224346Srscott 			}
36233446Smrj 		} else {
36244346Srscott 			/*
36254346Srscott 			 * It may be a partial path, or it may be a partial
36264346Srscott 			 * path followed by options.  Assume that only options
36274346Srscott 			 * follow a space.  If someone sends us a kernel path
36284346Srscott 			 * that includes a space, they deserve to be broken.
36294346Srscott 			 */
36304346Srscott 			opt_ptr = strchr(opt, ' ');
36314346Srscott 			if (opt_ptr != NULL) {
36324346Srscott 				*opt_ptr = '\0';
36334346Srscott 			}
36344346Srscott 
36353446Smrj 			path = expand_path(opt);
36363446Smrj 			if (path != NULL) {
36373446Smrj 				(void) strlcpy(kernbuf, path, BUFSIZ);
36383446Smrj 				free(path);
36394346Srscott 
36404346Srscott 				/*
36414346Srscott 				 * If there were options given, use those.
36424346Srscott 				 * Otherwise, copy over the default options.
36434346Srscott 				 */
36444346Srscott 				if (opt_ptr != NULL) {
36454346Srscott 					/* Restore the space in opt string */
36464346Srscott 					*opt_ptr = ' ';
36474346Srscott 					(void) strlcat(kernbuf, opt_ptr,
36484346Srscott 					    BUFSIZ);
36494346Srscott 				} else {
36503446Smrj 					if (set_kernel(mp, ARGS_CMD, NULL,
36513446Smrj 					    args_buf, BUFSIZ) != BAM_SUCCESS)
36523446Smrj 						return (BAM_ERROR);
36533446Smrj 
36543446Smrj 					if (args_buf[0] != '\0') {
36553446Smrj 						(void) strlcat(kernbuf, " ",
36563446Smrj 						    BUFSIZ);
36573446Smrj 						(void) strlcat(kernbuf,
36583446Smrj 						    args_buf, BUFSIZ);
36593446Smrj 					}
36603446Smrj 				}
36614346Srscott 			} else {
36624346Srscott 				bam_error(UNKNOWN_KERNEL, opt);
36634346Srscott 				bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
36644346Srscott 				return (BAM_ERROR);
36653446Smrj 			}
36663446Smrj 		}
36673446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
36683446Smrj 		    NULL);
36693446Smrj 	} else {
36703446Smrj 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
36713446Smrj 		    MULTI_BOOT, opt);
36723446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
36733446Smrj 		    MULTI_BOOT_ARCHIVE);
36743446Smrj 	}
36750Sstevel@tonic-gate 	free(grubdisk);
36760Sstevel@tonic-gate 
36770Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
3678316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
36790Sstevel@tonic-gate 		return (BAM_ERROR);
36800Sstevel@tonic-gate 	}
3681662Sszhou 
36823446Smrj 	save_default_entry(mp, BAM_OLDDEF);
36830Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
36840Sstevel@tonic-gate 	return (BAM_WRITE);
36850Sstevel@tonic-gate }
36860Sstevel@tonic-gate 
36870Sstevel@tonic-gate static error_t
36880Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
36890Sstevel@tonic-gate {
36900Sstevel@tonic-gate 	line_t *lp, *found, *last;
36910Sstevel@tonic-gate 	char *cp, *str;
36920Sstevel@tonic-gate 	char prefix[BAM_MAXLINE];
36930Sstevel@tonic-gate 	size_t len;
36940Sstevel@tonic-gate 
36950Sstevel@tonic-gate 	assert(mp);
36960Sstevel@tonic-gate 	assert(globalcmd);
36970Sstevel@tonic-gate 
3698316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3699316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3700316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3701316Svikram 			bam_error(INVALID_ENTRY, prefix);
3702316Svikram 			return (BAM_ERROR);
3703316Svikram 		}
3704316Svikram 	}
3705316Svikram 
37060Sstevel@tonic-gate 	found = last = NULL;
37070Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
37080Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
37090Sstevel@tonic-gate 			continue;
37100Sstevel@tonic-gate 
37110Sstevel@tonic-gate 		last = lp; /* track the last global found */
37120Sstevel@tonic-gate 
37130Sstevel@tonic-gate 		if (lp->cmd == NULL) {
37140Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
37150Sstevel@tonic-gate 			continue;
37160Sstevel@tonic-gate 		}
37170Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
37180Sstevel@tonic-gate 			continue;
37190Sstevel@tonic-gate 
37200Sstevel@tonic-gate 		if (found) {
37210Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
37220Sstevel@tonic-gate 		}
37230Sstevel@tonic-gate 		found = lp;
37240Sstevel@tonic-gate 	}
37250Sstevel@tonic-gate 
37260Sstevel@tonic-gate 	if (found == NULL) {
37270Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
37280Sstevel@tonic-gate 		if (last == NULL) {
37290Sstevel@tonic-gate 			lp->next = mp->start;
37300Sstevel@tonic-gate 			mp->start = lp;
37310Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
37320Sstevel@tonic-gate 		} else {
37330Sstevel@tonic-gate 			lp->next = last->next;
37340Sstevel@tonic-gate 			last->next = lp;
37350Sstevel@tonic-gate 			if (lp->next == NULL)
37360Sstevel@tonic-gate 				mp->end = lp;
37370Sstevel@tonic-gate 		}
37380Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
37390Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
37400Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
37410Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
37420Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
37430Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
37440Sstevel@tonic-gate 		return (BAM_WRITE);
37450Sstevel@tonic-gate 	}
37460Sstevel@tonic-gate 
37470Sstevel@tonic-gate 	/*
37480Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
37490Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
37500Sstevel@tonic-gate 	 * readability.
37510Sstevel@tonic-gate 	 */
37520Sstevel@tonic-gate 	str = found->line;
37530Sstevel@tonic-gate 	cp = prefix;
37540Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
37550Sstevel@tonic-gate 		*(cp++) = *(str++);
37560Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
37570Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
37580Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
37590Sstevel@tonic-gate 
37600Sstevel@tonic-gate 	free(found->line);
37610Sstevel@tonic-gate 	found->line = s_calloc(1, len);
37620Sstevel@tonic-gate 	(void) snprintf(found->line, len,
37634346Srscott 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
37640Sstevel@tonic-gate 
37650Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
37660Sstevel@tonic-gate }
37670Sstevel@tonic-gate 
37683446Smrj /*
37693446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
37704346Srscott  * expand it to a full unix path.  The calling function is expected to
37714346Srscott  * output a message if an error occurs and NULL is returned.
37723446Smrj  */
37733446Smrj static char *
37743446Smrj expand_path(const char *partial_path)
37753446Smrj {
37763446Smrj 	int new_path_len;
37773446Smrj 	char *new_path, new_path2[PATH_MAX];
37783446Smrj 	struct stat sb;
37793446Smrj 
37803446Smrj 	new_path_len = strlen(partial_path) + 64;
37813446Smrj 	new_path = s_calloc(1, new_path_len);
37823446Smrj 
37833446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
37843446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
37853446Smrj 	    partial_path);
37863446Smrj 	if (stat(new_path, &sb) == 0) {
37873446Smrj 		return (new_path);
37883446Smrj 	}
37893446Smrj 
37903446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
37913446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
37923446Smrj 		    DIRECT_BOOT_KERNEL);
37933446Smrj 		return (new_path);
37943446Smrj 	}
37953446Smrj 
37963446Smrj 	/*
37973446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
37983446Smrj 	 * see if we were just given a glom name.
37993446Smrj 	 */
38003446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
38013446Smrj 	    partial_path);
38023446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
38033446Smrj 	    partial_path);
38043446Smrj 	if (stat(new_path, &sb) == 0) {
38053446Smrj 		if (stat(new_path2, &sb) == 0) {
38063446Smrj 			/*
38073446Smrj 			 * We matched both, so we actually
38083446Smrj 			 * want to write the $ISADIR version.
38093446Smrj 			 */
38103446Smrj 			(void) snprintf(new_path, new_path_len,
38113446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
38123446Smrj 			    partial_path);
38133446Smrj 		}
38143446Smrj 		return (new_path);
38153446Smrj 	}
38163446Smrj 
38173446Smrj 	free(new_path);
38183446Smrj 	return (NULL);
38193446Smrj }
38203446Smrj 
38213446Smrj /*
38223446Smrj  * The kernel cmd and arg have been changed, so
38233446Smrj  * check whether the archive line needs to change.
38243446Smrj  */
38253446Smrj static void
38263446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
38273446Smrj {
38283446Smrj 	line_t *lp = entryp->start;
38293446Smrj 	char *new_archive;
38303446Smrj 	menu_cmd_t m_cmd;
38313446Smrj 
38323446Smrj 	for (; lp != NULL; lp = lp->next) {
38333446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
38343446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
38353446Smrj 			break;
38363446Smrj 		}
38373446Smrj 		if (lp == entryp->end)
38383446Smrj 			return;
38393446Smrj 	}
38403446Smrj 	if (lp == NULL)
38413446Smrj 		return;
38423446Smrj 
38433446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
38443446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
38453446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
38463446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
38473446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
38483446Smrj 		m_cmd = MODULE_CMD;
38493446Smrj 	} else {
38503446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
38513446Smrj 		m_cmd = MODULE_CMD;
38523446Smrj 	}
38533446Smrj 
38543446Smrj 	if (strcmp(lp->arg, new_archive) == 0)
38553446Smrj 		return;
38563446Smrj 
38573446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
38583446Smrj 		free(lp->cmd);
38593446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
38603446Smrj 	}
38613446Smrj 
38623446Smrj 	free(lp->arg);
38633446Smrj 	lp->arg = s_strdup(new_archive);
38643446Smrj 	update_line(lp);
38653446Smrj }
38663446Smrj 
38673446Smrj /*
38683446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
38693446Smrj  */
38703446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
38713446Smrj 
38723446Smrj /*
38733446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
38743446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
38753446Smrj  * string, reset the value to the default.  If path is a non-zero-length
38763446Smrj  * string, set the kernel or arguments.
38773446Smrj  */
38783446Smrj static error_t
38793446Smrj set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
38803446Smrj {
38813446Smrj 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
38823446Smrj 	entry_t *entryp;
38833446Smrj 	line_t *ptr, *kernelp;
38843446Smrj 	char *new_arg, *old_args, *space;
38853446Smrj 	char *grubdisk, *rootdev, *new_path;
38863446Smrj 	char old_space;
38873446Smrj 	size_t old_kernel_len, new_str_len;
38883446Smrj 	struct stat sb;
38893446Smrj 
38903446Smrj 	assert(bufsize > 0);
38913446Smrj 
38923446Smrj 	ptr = kernelp = NULL;
38933446Smrj 	new_arg = old_args = space = NULL;
38943446Smrj 	grubdisk = rootdev = new_path = NULL;
38953446Smrj 	buf[0] = '\0';
38963446Smrj 
38973446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
38983446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
38993446Smrj 		return (BAM_ERROR);
39003446Smrj 	}
39013446Smrj 
39023446Smrj 	/*
39033446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
39043446Smrj 	 * one, we don't want to mess with it.  Just print an error and
39053446Smrj 	 * return.
39063446Smrj 	 */
39073446Smrj 	if (mp->curdefault) {
39083446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
39093446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
39103446Smrj 			if (entryp->entryNum == entryNum)
39113446Smrj 				break;
39123446Smrj 		}
39133446Smrj 		if ((entryp != NULL) &&
39143446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
39153446Smrj 			bam_error(DEFAULT_NOT_BAM);
39163446Smrj 			return (BAM_ERROR);
39173446Smrj 		}
39183446Smrj 	}
39193446Smrj 
39203446Smrj 	entryNum = -1;
39213446Smrj 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, 0,
39223446Smrj 	    &entryNum);
39233446Smrj 
39243446Smrj 	if (entryp != NULL) {
39253446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
39263446Smrj 		    ptr = ptr->next) {
39273446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
39283446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
39293446Smrj 				kernelp = ptr;
39303446Smrj 				break;
39313446Smrj 			}
39323446Smrj 		}
39333446Smrj 		if (kernelp == NULL) {
39343446Smrj 			bam_error(NO_KERNEL, entryNum);
39353446Smrj 			return (BAM_ERROR);
39363446Smrj 		}
39373446Smrj 
39383446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
39393446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
39403446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
39413446Smrj 			old_args++;
39423446Smrj 	}
39433446Smrj 
39443446Smrj 	if (path == NULL) {
39453446Smrj 		/* Simply report what was found */
39463446Smrj 		if (kernelp == NULL)
39473446Smrj 			return (BAM_SUCCESS);
39483446Smrj 
39493446Smrj 		if (optnum == ARGS_CMD) {
39503446Smrj 			if (old_args[0] != '\0')
39513446Smrj 				(void) strlcpy(buf, old_args, bufsize);
39523446Smrj 		} else {
39533446Smrj 			/*
39543446Smrj 			 * We need to print the kernel, so we just turn the
39553446Smrj 			 * first space into a '\0' and print the beginning.
39563446Smrj 			 * We don't print anything if it's the default kernel.
39573446Smrj 			 */
39583446Smrj 			old_space = *space;
39593446Smrj 			*space = '\0';
39603446Smrj 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
39613446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
39623446Smrj 			*space = old_space;
39633446Smrj 		}
39643446Smrj 		return (BAM_SUCCESS);
39653446Smrj 	}
39663446Smrj 
39673446Smrj 	/*
39683446Smrj 	 * First, check if we're resetting an entry to the default.
39693446Smrj 	 */
39703446Smrj 	if ((path[0] == '\0') ||
39713446Smrj 	    ((optnum == KERNEL_CMD) &&
39723446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
39733446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
39743446Smrj 			/* No previous entry, it's already the default */
39753446Smrj 			return (BAM_SUCCESS);
39763446Smrj 		}
39773446Smrj 
39783446Smrj 		/*
39793446Smrj 		 * Check if we can delete the entry.  If we're resetting the
39803446Smrj 		 * kernel command, and the args is already empty, or if we're
39813446Smrj 		 * resetting the args command, and the kernel is already the
39823446Smrj 		 * default, we can restore the old default and delete the entry.
39833446Smrj 		 */
39843446Smrj 		if (((optnum == KERNEL_CMD) &&
39853446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
39863446Smrj 		    ((optnum == ARGS_CMD) &&
39873446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
39883446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
39893446Smrj 			kernelp = NULL;
39903446Smrj 			(void) do_delete(mp, entryNum);
39913446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
39923446Smrj 			    mp->old_rc_default);
39933446Smrj 			mp->old_rc_default = NULL;
39943446Smrj 			rv = BAM_WRITE;
39953446Smrj 			goto done;
39963446Smrj 		}
39973446Smrj 
39983446Smrj 		if (optnum == KERNEL_CMD) {
39993446Smrj 			/*
40003446Smrj 			 * At this point, we've already checked that old_args
40013446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
40023446Smrj 			 * a space a the string termination character.
40033446Smrj 			 */
40043446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
40053446Smrj 			    strlen(old_args) + 2;
40063446Smrj 			new_arg = s_calloc(1, new_str_len);
40073446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
40083446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
40093446Smrj 			free(kernelp->arg);
40103446Smrj 			kernelp->arg = new_arg;
40113446Smrj 
40123446Smrj 			/*
40133446Smrj 			 * We have changed the kernel line, so we may need
40143446Smrj 			 * to update the archive line as well.
40153446Smrj 			 */
40163446Smrj 			set_archive_line(entryp, kernelp);
40173446Smrj 		} else {
40183446Smrj 			/*
40193446Smrj 			 * We're resetting the boot args to nothing, so
40203446Smrj 			 * we only need to copy the kernel.  We've already
40213446Smrj 			 * checked that the kernel is not the default.
40223446Smrj 			 */
40233446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
40243446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
40253446Smrj 			    kernelp->arg);
40263446Smrj 			free(kernelp->arg);
40273446Smrj 			kernelp->arg = new_arg;
40283446Smrj 		}
40293446Smrj 		rv = BAM_WRITE;
40303446Smrj 		goto done;
40313446Smrj 	}
40323446Smrj 
40333446Smrj 	/*
40343446Smrj 	 * Expand the kernel file to a full path, if necessary
40353446Smrj 	 */
40363446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
40373446Smrj 		new_path = expand_path(path);
40383446Smrj 		if (new_path == NULL) {
40394346Srscott 			bam_error(UNKNOWN_KERNEL, path);
40403446Smrj 			return (BAM_ERROR);
40413446Smrj 		}
40423446Smrj 		free_new_path = 1;
40433446Smrj 	} else {
40443446Smrj 		new_path = path;
40453446Smrj 		free_new_path = 0;
40463446Smrj 	}
40473446Smrj 
40483446Smrj 	/*
40493446Smrj 	 * At this point, we know we're setting a new value.  First, take care
40503446Smrj 	 * of the case where there was no previous entry.
40513446Smrj 	 */
40523446Smrj 	if (entryp == NULL) {
40533446Smrj 		/* Similar to code in update_temp */
40543446Smrj 		if (stat(GRUB_slice, &sb) != 0) {
40553446Smrj 			/*
40563446Smrj 			 * 1. First get root disk name from mnttab
40573446Smrj 			 * 2. Translate disk name to grub name
40583446Smrj 			 * 3. Add the new menu entry
40593446Smrj 			 */
40603446Smrj 			rootdev = get_special("/");
40613446Smrj 			if (rootdev) {
40623446Smrj 				grubdisk = os_to_grubdisk(rootdev, 1);
40633446Smrj 				free(rootdev);
40643446Smrj 			}
40653446Smrj 		} else {
40663446Smrj 			/*
40673446Smrj 			 * This is an LU BE. The GRUB_root file
40683446Smrj 			 * contains entry for GRUB's "root" cmd.
40693446Smrj 			 */
40703446Smrj 			grubdisk = read_grub_root();
40713446Smrj 		}
40723446Smrj 		if (grubdisk == NULL) {
40733446Smrj 			bam_error(REBOOT_WITH_ARGS_FAILED);
40743446Smrj 			rv = BAM_ERROR;
40753446Smrj 			goto done;
40763446Smrj 		}
40773446Smrj 		if (optnum == KERNEL_CMD) {
40783446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
40793446Smrj 			    grubdisk, new_path, NULL);
40803446Smrj 		} else {
40813446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
40823446Smrj 			    strlen(path) + 8;
40833446Smrj 			new_arg = s_calloc(1, new_str_len);
40843446Smrj 
40853446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
40863446Smrj 			    DIRECT_BOOT_KERNEL, path);
40873446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
40883446Smrj 			    grubdisk, new_arg, DIRECT_BOOT_ARCHIVE);
40893446Smrj 		}
40903446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
40913446Smrj 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
40923446Smrj 		rv = BAM_WRITE;
40933446Smrj 		goto done;
40943446Smrj 	}
40953446Smrj 
40963446Smrj 	/*
40973446Smrj 	 * There was already an bootenv entry which we need to edit.
40983446Smrj 	 */
40993446Smrj 	if (optnum == KERNEL_CMD) {
41003446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
41013446Smrj 		new_arg = s_calloc(1, new_str_len);
41023446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
41033446Smrj 		    old_args);
41043446Smrj 		free(kernelp->arg);
41053446Smrj 		kernelp->arg = new_arg;
41063446Smrj 
41073446Smrj 		/*
41083446Smrj 		 * If we have changed the kernel line, we may need to update
41093446Smrj 		 * the archive line as well.
41103446Smrj 		 */
41113446Smrj 		set_archive_line(entryp, kernelp);
41123446Smrj 	} else {
41133446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
41143446Smrj 		new_arg = s_calloc(1, new_str_len);
41153446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
41163446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
41173446Smrj 		(void) strlcat(new_arg, path, new_str_len);
41183446Smrj 		free(kernelp->arg);
41193446Smrj 		kernelp->arg = new_arg;
41203446Smrj 	}
41213446Smrj 	rv = BAM_WRITE;
41223446Smrj 
41233446Smrj done:
41243446Smrj 	if ((rv == BAM_WRITE) && kernelp)
41253446Smrj 		update_line(kernelp);
41263446Smrj 	if (free_new_path)
41273446Smrj 		free(new_path);
41283446Smrj 	return (rv);
41293446Smrj }
41303446Smrj 
41310Sstevel@tonic-gate /*ARGSUSED*/
41320Sstevel@tonic-gate static error_t
41330Sstevel@tonic-gate set_option(menu_t *mp, char *menu_path, char *opt)
41340Sstevel@tonic-gate {
41350Sstevel@tonic-gate 	int optnum, optval;
41360Sstevel@tonic-gate 	char *val;
41373446Smrj 	char buf[BUFSIZ] = "";
41383446Smrj 	error_t rv;
41390Sstevel@tonic-gate 
41400Sstevel@tonic-gate 	assert(mp);
41410Sstevel@tonic-gate 	assert(opt);
41420Sstevel@tonic-gate 
41430Sstevel@tonic-gate 	val = strchr(opt, '=');
41443446Smrj 	if (val != NULL) {
41453446Smrj 		*val = '\0';
41460Sstevel@tonic-gate 	}
41470Sstevel@tonic-gate 
41480Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
41490Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
41500Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
41510Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
41523446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
41533446Smrj 		optnum = KERNEL_CMD;
41543446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
41553446Smrj 		optnum = ARGS_CMD;
41560Sstevel@tonic-gate 	} else {
41570Sstevel@tonic-gate 		bam_error(INVALID_ENTRY, opt);
41580Sstevel@tonic-gate 		return (BAM_ERROR);
41590Sstevel@tonic-gate 	}
41600Sstevel@tonic-gate 
41613446Smrj 	/*
41623446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
41633446Smrj 	 * others cause errors
41643446Smrj 	 */
41653446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
41663446Smrj 		bam_error(INVALID_ENTRY, opt);
41673446Smrj 		return (BAM_ERROR);
41683446Smrj 	} else if (val != NULL) {
41693446Smrj 		*val = '=';
41703446Smrj 	}
41713446Smrj 
41723446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
41733446Smrj 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
41743446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
41753446Smrj 			(void) printf("%s\n", buf);
41763446Smrj 		return (rv);
41773446Smrj 	} else {
41783446Smrj 		optval = s_strtol(val + 1);
41793446Smrj 		return (set_global(mp, menu_cmds[optnum], optval));
41803446Smrj 	}
41810Sstevel@tonic-gate }
41820Sstevel@tonic-gate 
41830Sstevel@tonic-gate /*
41840Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
41850Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
41860Sstevel@tonic-gate  */
41870Sstevel@tonic-gate static error_t
41880Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
41890Sstevel@tonic-gate {
41900Sstevel@tonic-gate 	line_t *lp;
41910Sstevel@tonic-gate 	char *arg;
41920Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
41930Sstevel@tonic-gate 
41940Sstevel@tonic-gate 	assert(mp);
41950Sstevel@tonic-gate 	assert(menu_path);
41960Sstevel@tonic-gate 	assert(globalcmd);
41970Sstevel@tonic-gate 
41980Sstevel@tonic-gate 	if (mp->start == NULL) {
41990Sstevel@tonic-gate 		if (!quiet)
42000Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
42010Sstevel@tonic-gate 		return (BAM_ERROR);
42020Sstevel@tonic-gate 	}
42030Sstevel@tonic-gate 
42040Sstevel@tonic-gate 	done = 0;
42050Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
42060Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
42070Sstevel@tonic-gate 			continue;
42080Sstevel@tonic-gate 
42090Sstevel@tonic-gate 		if (lp->cmd == NULL) {
42100Sstevel@tonic-gate 			if (!quiet)
42110Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
42120Sstevel@tonic-gate 			continue;
42130Sstevel@tonic-gate 		}
42140Sstevel@tonic-gate 
42150Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
42160Sstevel@tonic-gate 			continue;
42170Sstevel@tonic-gate 
42180Sstevel@tonic-gate 		/* Found global. Check for duplicates */
42190Sstevel@tonic-gate 		if (done && !quiet) {
42200Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
42210Sstevel@tonic-gate 			ret = BAM_ERROR;
42220Sstevel@tonic-gate 		}
42230Sstevel@tonic-gate 
42240Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
42250Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
42260Sstevel@tonic-gate 		done = 1;
42270Sstevel@tonic-gate 	}
42280Sstevel@tonic-gate 
42290Sstevel@tonic-gate 	if (!done && bam_verbose)
42300Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
42310Sstevel@tonic-gate 
42320Sstevel@tonic-gate 	return (ret);
42330Sstevel@tonic-gate }
42340Sstevel@tonic-gate 
42350Sstevel@tonic-gate static error_t
42360Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
42370Sstevel@tonic-gate {
42380Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
42390Sstevel@tonic-gate }
42400Sstevel@tonic-gate 
42410Sstevel@tonic-gate static void
42420Sstevel@tonic-gate line_free(line_t *lp)
42430Sstevel@tonic-gate {
42440Sstevel@tonic-gate 	if (lp == NULL)
42450Sstevel@tonic-gate 		return;
42460Sstevel@tonic-gate 
42470Sstevel@tonic-gate 	if (lp->cmd)
42480Sstevel@tonic-gate 		free(lp->cmd);
42490Sstevel@tonic-gate 	if (lp->sep)
42500Sstevel@tonic-gate 		free(lp->sep);
42510Sstevel@tonic-gate 	if (lp->arg)
42520Sstevel@tonic-gate 		free(lp->arg);
42530Sstevel@tonic-gate 	if (lp->line)
42540Sstevel@tonic-gate 		free(lp->line);
42550Sstevel@tonic-gate 	free(lp);
42560Sstevel@tonic-gate }
42570Sstevel@tonic-gate 
42580Sstevel@tonic-gate static void
42590Sstevel@tonic-gate linelist_free(line_t *start)
42600Sstevel@tonic-gate {
42610Sstevel@tonic-gate 	line_t *lp;
42620Sstevel@tonic-gate 
42630Sstevel@tonic-gate 	while (start) {
42640Sstevel@tonic-gate 		lp = start;
42650Sstevel@tonic-gate 		start = start->next;
42660Sstevel@tonic-gate 		line_free(lp);
42670Sstevel@tonic-gate 	}
42680Sstevel@tonic-gate }
42690Sstevel@tonic-gate 
42700Sstevel@tonic-gate static void
42710Sstevel@tonic-gate filelist_free(filelist_t *flistp)
42720Sstevel@tonic-gate {
42730Sstevel@tonic-gate 	linelist_free(flistp->head);
42740Sstevel@tonic-gate 	flistp->head = NULL;
42750Sstevel@tonic-gate 	flistp->tail = NULL;
42760Sstevel@tonic-gate }
42770Sstevel@tonic-gate 
42780Sstevel@tonic-gate static void
42790Sstevel@tonic-gate menu_free(menu_t *mp)
42800Sstevel@tonic-gate {
4281662Sszhou 	entry_t *ent, *tmp;
42820Sstevel@tonic-gate 	assert(mp);
42830Sstevel@tonic-gate 
42840Sstevel@tonic-gate 	if (mp->start)
42850Sstevel@tonic-gate 		linelist_free(mp->start);
4286662Sszhou 	ent = mp->entries;
4287662Sszhou 	while (ent) {
4288662Sszhou 		tmp = ent;
4289662Sszhou 		ent = tmp->next;
4290662Sszhou 		free(tmp);
4291662Sszhou 	}
4292662Sszhou 
42930Sstevel@tonic-gate 	free(mp);
42940Sstevel@tonic-gate }
42950Sstevel@tonic-gate 
42960Sstevel@tonic-gate /*
42970Sstevel@tonic-gate  * Utility routines
42980Sstevel@tonic-gate  */
42990Sstevel@tonic-gate 
43000Sstevel@tonic-gate 
43010Sstevel@tonic-gate /*
43020Sstevel@tonic-gate  * Returns 0 on success
43030Sstevel@tonic-gate  * Any other value indicates an error
43040Sstevel@tonic-gate  */
43050Sstevel@tonic-gate static int
43060Sstevel@tonic-gate exec_cmd(char *cmdline, char *output, int64_t osize)
43070Sstevel@tonic-gate {
43080Sstevel@tonic-gate 	char buf[BUFSIZ];
43090Sstevel@tonic-gate 	int ret;
43100Sstevel@tonic-gate 	FILE *ptr;
43110Sstevel@tonic-gate 	size_t len;
43120Sstevel@tonic-gate 	sigset_t set;
43130Sstevel@tonic-gate 	void (*disp)(int);
43140Sstevel@tonic-gate 
43150Sstevel@tonic-gate 	/*
43160Sstevel@tonic-gate 	 * For security
43170Sstevel@tonic-gate 	 * - only absolute paths are allowed
43180Sstevel@tonic-gate 	 * - set IFS to space and tab
43190Sstevel@tonic-gate 	 */
43200Sstevel@tonic-gate 	if (*cmdline != '/') {
43210Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
43220Sstevel@tonic-gate 		return (-1);
43230Sstevel@tonic-gate 	}
43240Sstevel@tonic-gate 	(void) putenv("IFS= \t");
43250Sstevel@tonic-gate 
43260Sstevel@tonic-gate 	/*
43270Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
43280Sstevel@tonic-gate 	 * unblock it here
43290Sstevel@tonic-gate 	 */
43300Sstevel@tonic-gate 	(void) sigemptyset(&set);
43310Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
43320Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
43330Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
43340Sstevel@tonic-gate 		return (-1);
43350Sstevel@tonic-gate 	}
43360Sstevel@tonic-gate 
43370Sstevel@tonic-gate 	/*
43380Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
43390Sstevel@tonic-gate 	 */
43400Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
43410Sstevel@tonic-gate 	if (disp == SIG_ERR) {
43420Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
43430Sstevel@tonic-gate 		return (-1);
43440Sstevel@tonic-gate 	}
43450Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
43460Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
43470Sstevel@tonic-gate 		return (-1);
43480Sstevel@tonic-gate 	}
43490Sstevel@tonic-gate 
43500Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
43510Sstevel@tonic-gate 	if (ptr == NULL) {
43520Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
43530Sstevel@tonic-gate 		return (-1);
43540Sstevel@tonic-gate 	}
43550Sstevel@tonic-gate 
43560Sstevel@tonic-gate 	/*
43570Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
43580Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
43590Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
43600Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
43610Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
43620Sstevel@tonic-gate 	 * there is no reader process and the command dies with
43630Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
43640Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
43650Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
43660Sstevel@tonic-gate 	 * pclose().
43670Sstevel@tonic-gate 	 *
43680Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
43690Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
43700Sstevel@tonic-gate 	 * from the value returned by pclose()
43710Sstevel@tonic-gate 	 */
43720Sstevel@tonic-gate 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
43730Sstevel@tonic-gate 		/* if (bam_verbose)  XXX */
43740Sstevel@tonic-gate 			bam_print(PRINT_NO_NEWLINE, buf);
43750Sstevel@tonic-gate 		if (output && osize > 0) {
43760Sstevel@tonic-gate 			(void) snprintf(output, osize, "%s", buf);
43770Sstevel@tonic-gate 			len = strlen(buf);
43780Sstevel@tonic-gate 			output += len;
43790Sstevel@tonic-gate 			osize -= len;
43800Sstevel@tonic-gate 		}
43810Sstevel@tonic-gate 	}
43820Sstevel@tonic-gate 
43830Sstevel@tonic-gate 	ret = pclose(ptr);
43840Sstevel@tonic-gate 	if (ret == -1) {
43850Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
43860Sstevel@tonic-gate 		return (-1);
43870Sstevel@tonic-gate 	}
43880Sstevel@tonic-gate 
43890Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
43900Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
43910Sstevel@tonic-gate 	} else {
43920Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
43930Sstevel@tonic-gate 		return (-1);
43940Sstevel@tonic-gate 	}
43950Sstevel@tonic-gate }
43960Sstevel@tonic-gate 
43970Sstevel@tonic-gate /*
43980Sstevel@tonic-gate  * Since this function returns -1 on error
43990Sstevel@tonic-gate  * it cannot be used to convert -1. However,
44000Sstevel@tonic-gate  * that is sufficient for what we need.
44010Sstevel@tonic-gate  */
44020Sstevel@tonic-gate static long
44030Sstevel@tonic-gate s_strtol(char *str)
44040Sstevel@tonic-gate {
44050Sstevel@tonic-gate 	long l;
44060Sstevel@tonic-gate 	char *res = NULL;
44070Sstevel@tonic-gate 
44080Sstevel@tonic-gate 	if (str == NULL) {
44090Sstevel@tonic-gate 		return (-1);
44100Sstevel@tonic-gate 	}
44110Sstevel@tonic-gate 
44120Sstevel@tonic-gate 	errno = 0;
44130Sstevel@tonic-gate 	l = strtol(str, &res, 10);
44140Sstevel@tonic-gate 	if (errno || *res != '\0') {
44150Sstevel@tonic-gate 		return (-1);
44160Sstevel@tonic-gate 	}
44170Sstevel@tonic-gate 
44180Sstevel@tonic-gate 	return (l);
44190Sstevel@tonic-gate }
44200Sstevel@tonic-gate 
44210Sstevel@tonic-gate /*
44220Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
44230Sstevel@tonic-gate  */
44240Sstevel@tonic-gate static int
44250Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
44260Sstevel@tonic-gate {
44270Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
44280Sstevel@tonic-gate 
44290Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
44300Sstevel@tonic-gate 	return (fputs(linebuf, fp));
44310Sstevel@tonic-gate }
44320Sstevel@tonic-gate 
44330Sstevel@tonic-gate /*
44340Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
44350Sstevel@tonic-gate  */
44363446Smrj char *
44370Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
44380Sstevel@tonic-gate {
44390Sstevel@tonic-gate 	int n;
44400Sstevel@tonic-gate 
44410Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
44420Sstevel@tonic-gate 	if (buf) {
44430Sstevel@tonic-gate 		n = strlen(buf);
44440Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
44450Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
44460Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
44470Sstevel@tonic-gate 	}
44480Sstevel@tonic-gate 
44490Sstevel@tonic-gate 	return (buf);
44500Sstevel@tonic-gate }
44510Sstevel@tonic-gate 
44523446Smrj void *
44530Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
44540Sstevel@tonic-gate {
44550Sstevel@tonic-gate 	void *ptr;
44560Sstevel@tonic-gate 
44570Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
44580Sstevel@tonic-gate 	if (ptr == NULL) {
44590Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
44600Sstevel@tonic-gate 		bam_exit(1);
44610Sstevel@tonic-gate 	}
44620Sstevel@tonic-gate 	return (ptr);
44630Sstevel@tonic-gate }
44640Sstevel@tonic-gate 
44653446Smrj void *
44663446Smrj s_realloc(void *ptr, size_t sz)
44673446Smrj {
44683446Smrj 	ptr = realloc(ptr, sz);
44693446Smrj 	if (ptr == NULL) {
44703446Smrj 		bam_error(NO_MEM, sz);
44713446Smrj 		bam_exit(1);
44723446Smrj 	}
44733446Smrj 	return (ptr);
44743446Smrj }
44753446Smrj 
44760Sstevel@tonic-gate static char *
44770Sstevel@tonic-gate s_strdup(char *str)
44780Sstevel@tonic-gate {
44790Sstevel@tonic-gate 	char *ptr;
44800Sstevel@tonic-gate 
44810Sstevel@tonic-gate 	if (str == NULL)
44820Sstevel@tonic-gate 		return (NULL);
44830Sstevel@tonic-gate 
44840Sstevel@tonic-gate 	ptr = strdup(str);
44850Sstevel@tonic-gate 	if (ptr == NULL) {
44860Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
44870Sstevel@tonic-gate 		bam_exit(1);
44880Sstevel@tonic-gate 	}
44890Sstevel@tonic-gate 	return (ptr);
44900Sstevel@tonic-gate }
44910Sstevel@tonic-gate 
44920Sstevel@tonic-gate /*
44930Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
44940Sstevel@tonic-gate  * Returns 0 otherwise
44950Sstevel@tonic-gate  */
44960Sstevel@tonic-gate static int
44970Sstevel@tonic-gate is_amd64(void)
44980Sstevel@tonic-gate {
44990Sstevel@tonic-gate 	static int amd64 = -1;
45000Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
45010Sstevel@tonic-gate 
45020Sstevel@tonic-gate 	if (amd64 != -1)
45030Sstevel@tonic-gate 		return (amd64);
45040Sstevel@tonic-gate 
45050Sstevel@tonic-gate 	if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
45060Sstevel@tonic-gate 	    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0)
45070Sstevel@tonic-gate 		amd64 = 1;
45080Sstevel@tonic-gate 	else if (strstr(isabuf, "i386") == NULL)
45090Sstevel@tonic-gate 		amd64 = 1;		/* diskless server */
45100Sstevel@tonic-gate 	else
45110Sstevel@tonic-gate 		amd64 = 0;
45120Sstevel@tonic-gate 
45130Sstevel@tonic-gate 	return (amd64);
45140Sstevel@tonic-gate }
45150Sstevel@tonic-gate 
45160Sstevel@tonic-gate static void
45170Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
45180Sstevel@tonic-gate {
45190Sstevel@tonic-gate 	line_t *lp;
45200Sstevel@tonic-gate 
45210Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
45220Sstevel@tonic-gate 	lp->line = s_strdup(s);
45230Sstevel@tonic-gate 	if (flistp->head == NULL)
45240Sstevel@tonic-gate 		flistp->head = lp;
45250Sstevel@tonic-gate 	else
45260Sstevel@tonic-gate 		flistp->tail->next = lp;
45270Sstevel@tonic-gate 	flistp->tail = lp;
45280Sstevel@tonic-gate }
4529*4581Ssherrym 
4530*4581Ssherrym #if defined(__i386)
4531*4581Ssherrym 
4532*4581Ssherrym UCODE_VENDORS;
4533*4581Ssherrym 
4534*4581Ssherrym /*ARGSUSED*/
4535*4581Ssherrym static void
4536*4581Ssherrym ucode_install(char *root)
4537*4581Ssherrym {
4538*4581Ssherrym 	int i;
4539*4581Ssherrym 
4540*4581Ssherrym 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
4541*4581Ssherrym 		int cmd_len = PATH_MAX + 256;
4542*4581Ssherrym 		char cmd[PATH_MAX + 256];
4543*4581Ssherrym 		char file[PATH_MAX];
4544*4581Ssherrym 		char timestamp[PATH_MAX];
4545*4581Ssherrym 		struct stat fstatus, tstatus;
4546*4581Ssherrym 		struct utimbuf u_times;
4547*4581Ssherrym 
4548*4581Ssherrym 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt",
4549*4581Ssherrym 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr);
4550*4581Ssherrym 
4551*4581Ssherrym 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
4552*4581Ssherrym 			continue;
4553*4581Ssherrym 
4554*4581Ssherrym 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
4555*4581Ssherrym 
4556*4581Ssherrym 		if (stat(timestamp, &tstatus) == 0 &&
4557*4581Ssherrym 		    fstatus.st_mtime <= tstatus.st_mtime)
4558*4581Ssherrym 			continue;
4559*4581Ssherrym 
4560*4581Ssherrym 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
4561*4581Ssherrym 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
4562*4581Ssherrym 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
4563*4581Ssherrym 		if (system(cmd) != 0)
4564*4581Ssherrym 			return;
4565*4581Ssherrym 
4566*4581Ssherrym 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
4567*4581Ssherrym 			return;
4568*4581Ssherrym 
4569*4581Ssherrym 		u_times.actime = fstatus.st_atime;
4570*4581Ssherrym 		u_times.modtime = fstatus.st_mtime;
4571*4581Ssherrym 		(void) utime(timestamp, &u_times);
4572*4581Ssherrym 	}
4573*4581Ssherrym }
4574*4581Ssherrym #endif
4575