xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 3467:8cf06bcbb424)
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>
530Sstevel@tonic-gate #include <sys/systeminfo.h>
540Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
552334Ssetje #include <sys/param.h>
560Sstevel@tonic-gate 
570Sstevel@tonic-gate #include <pwd.h>
580Sstevel@tonic-gate #include <grp.h>
590Sstevel@tonic-gate #include <device_info.h>
600Sstevel@tonic-gate 
610Sstevel@tonic-gate #include <locale.h>
620Sstevel@tonic-gate 
630Sstevel@tonic-gate #include <assert.h>
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #include "message.h"
663446Smrj #include "bootadm.h"
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #ifndef TEXT_DOMAIN
690Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
700Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
710Sstevel@tonic-gate 
720Sstevel@tonic-gate /* Type definitions */
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /* Primary subcmds */
750Sstevel@tonic-gate typedef enum {
760Sstevel@tonic-gate 	BAM_MENU = 3,
770Sstevel@tonic-gate 	BAM_ARCHIVE
780Sstevel@tonic-gate } subcmd_t;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate typedef enum {
810Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
820Sstevel@tonic-gate     OPT_REQ,		/* option required */
830Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
840Sstevel@tonic-gate } option_t;
850Sstevel@tonic-gate 
860Sstevel@tonic-gate typedef struct {
870Sstevel@tonic-gate 	char	*subcmd;
880Sstevel@tonic-gate 	option_t option;
890Sstevel@tonic-gate 	error_t (*handler)();
902115Svikram 	int	unpriv;			/* is this an unprivileged command */
910Sstevel@tonic-gate } subcmd_defn_t;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
940Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
950Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
960Sstevel@tonic-gate 
970Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
980Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
990Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1000Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
101621Svikram #define	STUBBOOT		"/stubboot"
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate /* lock related */
1040Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1050Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate #define	CREATE_RAMDISK		"/boot/solaris/bin/create_ramdisk"
1080Sstevel@tonic-gate #define	CREATE_DISKMAP		"/boot/solaris/bin/create_diskmap"
1090Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
1100Sstevel@tonic-gate 
111316Svikram #define	GRUB_slice		"/etc/lu/GRUB_slice"
112316Svikram #define	GRUB_root		"/etc/lu/GRUB_root"
113316Svikram #define	GRUB_backup_menu	"/etc/lu/GRUB_backup_menu"
114316Svikram #define	GRUB_slice_mntpt	"/tmp/GRUB_slice_mntpt"
115316Svikram #define	LU_ACTIVATE_FILE	"/etc/lu/DelayUpdate/activate.sh"
1161746Svikram #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
1171746Svikram #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
118316Svikram 
119316Svikram #define	INSTALLGRUB		"/sbin/installgrub"
120316Svikram #define	STAGE1			"/boot/grub/stage1"
121316Svikram #define	STAGE2			"/boot/grub/stage2"
122316Svikram 
1230Sstevel@tonic-gate /*
1240Sstevel@tonic-gate  * Default file attributes
1250Sstevel@tonic-gate  */
1260Sstevel@tonic-gate #define	DEFAULT_DEV_MODE	0644	/* default permissions */
1270Sstevel@tonic-gate #define	DEFAULT_DEV_UID		0	/* user root */
1280Sstevel@tonic-gate #define	DEFAULT_DEV_GID		3	/* group sys */
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate /*
1310Sstevel@tonic-gate  * Menu related
1320Sstevel@tonic-gate  * menu_cmd_t and menu_cmds must be kept in sync
1330Sstevel@tonic-gate  */
1343446Smrj char *menu_cmds[] = {
1350Sstevel@tonic-gate 	"default",	/* DEFAULT_CMD */
1360Sstevel@tonic-gate 	"timeout",	/* TIMEOUT_CMD */
1370Sstevel@tonic-gate 	"title",	/* TITLE_CMD */
1380Sstevel@tonic-gate 	"root",		/* ROOT_CMD */
1390Sstevel@tonic-gate 	"kernel",	/* KERNEL_CMD */
1403446Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1410Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
1423446Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1430Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1440Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
1453446Smrj 	"chainloader",	/* CHAINLOADER_CMD */
1463446Smrj 	"args",		/* ARGS_CMD */
1470Sstevel@tonic-gate 	NULL
1480Sstevel@tonic-gate };
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate #define	OPT_ENTRY_NUM	"entry"
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate /*
1530Sstevel@tonic-gate  * archive related
1540Sstevel@tonic-gate  */
1550Sstevel@tonic-gate typedef struct {
1560Sstevel@tonic-gate 	line_t *head;
1570Sstevel@tonic-gate 	line_t *tail;
1580Sstevel@tonic-gate } filelist_t;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
1610Sstevel@tonic-gate #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
1640Sstevel@tonic-gate #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
1650Sstevel@tonic-gate #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
1660Sstevel@tonic-gate #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate /* Globals */
1693446Smrj int bam_verbose;
1703446Smrj int bam_force;
1710Sstevel@tonic-gate static char *prog;
1720Sstevel@tonic-gate static subcmd_t bam_cmd;
1730Sstevel@tonic-gate static char *bam_root;
1740Sstevel@tonic-gate static int bam_rootlen;
1750Sstevel@tonic-gate static int bam_root_readonly;
176621Svikram static int bam_alt_root;
1770Sstevel@tonic-gate static char *bam_subcmd;
1780Sstevel@tonic-gate static char *bam_opt;
1790Sstevel@tonic-gate static int bam_debug;
1800Sstevel@tonic-gate static char **bam_argv;
1810Sstevel@tonic-gate static int bam_argc;
1820Sstevel@tonic-gate static int bam_check;
1830Sstevel@tonic-gate static int bam_smf_check;
1840Sstevel@tonic-gate static int bam_lock_fd = -1;
1850Sstevel@tonic-gate static char rootbuf[PATH_MAX] = "/";
186316Svikram static int bam_update_all;
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate /* function prototypes */
1890Sstevel@tonic-gate static void parse_args_internal(int argc, char *argv[]);
1900Sstevel@tonic-gate static void parse_args(int argc, char *argv[]);
1910Sstevel@tonic-gate static error_t bam_menu(char *subcmd, char *opt, int argc, char *argv[]);
1920Sstevel@tonic-gate static error_t bam_archive(char *subcmd, char *opt);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate static void bam_print(char *format, ...);
1950Sstevel@tonic-gate static void bam_exit(int excode);
1960Sstevel@tonic-gate static void bam_lock(void);
1970Sstevel@tonic-gate static void bam_unlock(void);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate static int exec_cmd(char *cmdline, char *output, int64_t osize);
2000Sstevel@tonic-gate static error_t read_globals(menu_t *mp, char *menu_path,
2010Sstevel@tonic-gate     char *globalcmd, int quiet);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate static menu_t *menu_read(char *menu_path);
2040Sstevel@tonic-gate static error_t menu_write(char *root, menu_t *mp);
2050Sstevel@tonic-gate static void linelist_free(line_t *start);
2060Sstevel@tonic-gate static void menu_free(menu_t *mp);
2070Sstevel@tonic-gate static void line_free(line_t *lp);
2080Sstevel@tonic-gate static void filelist_free(filelist_t *flistp);
2090Sstevel@tonic-gate static error_t list2file(char *root, char *tmp,
2100Sstevel@tonic-gate     char *final, line_t *start);
2110Sstevel@tonic-gate static error_t list_entry(menu_t *mp, char *menu_path, char *opt);
2120Sstevel@tonic-gate static error_t delete_all_entries(menu_t *mp, char *menu_path, char *opt);
2130Sstevel@tonic-gate static error_t update_entry(menu_t *mp, char *root, char *opt);
2140Sstevel@tonic-gate static error_t update_temp(menu_t *mp, char *root, char *opt);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate static error_t update_archive(char *root, char *opt);
2170Sstevel@tonic-gate static error_t list_archive(char *root, char *opt);
2180Sstevel@tonic-gate static error_t update_all(char *root, char *opt);
2190Sstevel@tonic-gate static error_t read_list(char *root, filelist_t  *flistp);
2200Sstevel@tonic-gate static error_t set_global(menu_t *mp, char *globalcmd, int val);
2210Sstevel@tonic-gate static error_t set_option(menu_t *mp, char *globalcmd, char *opt);
2223446Smrj static error_t set_kernel(menu_t *mp, menu_cmd_t optnum, char *path,
2233446Smrj     char *buf, size_t bufsize);
2243446Smrj static char *expand_path(const char *partial_path);
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate static long s_strtol(char *str);
2270Sstevel@tonic-gate static int s_fputs(char *str, FILE *fp);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate static char *s_strdup(char *str);
2300Sstevel@tonic-gate static int is_readonly(char *);
2310Sstevel@tonic-gate static int is_amd64(void);
2320Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate #if defined(__sparc)
2350Sstevel@tonic-gate static void sparc_abort(void);
2360Sstevel@tonic-gate #endif
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate /* Menu related sub commands */
2390Sstevel@tonic-gate static subcmd_defn_t menu_subcmds[] = {
2402115Svikram 	"set_option",		OPT_OPTIONAL,	set_option, 0,	/* PUB */
2412115Svikram 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
2422115Svikram 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
2432115Svikram 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
2442115Svikram 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
2453446Smrj 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
2462115Svikram 	NULL,			0,		NULL, 0	/* must be last */
2470Sstevel@tonic-gate };
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate /* Archive related sub commands */
2500Sstevel@tonic-gate static subcmd_defn_t arch_subcmds[] = {
2512115Svikram 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
2522115Svikram 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
2532115Svikram 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
2542115Svikram 	NULL,			0,		NULL, 0	/* must be last */
2550Sstevel@tonic-gate };
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate static struct {
2580Sstevel@tonic-gate 	nvlist_t *new_nvlp;
2590Sstevel@tonic-gate 	nvlist_t *old_nvlp;
2600Sstevel@tonic-gate 	int need_update;
2610Sstevel@tonic-gate } walk_arg;
2620Sstevel@tonic-gate 
2632334Ssetje 
2642345Ssetje struct safefile {
2652334Ssetje 	char *name;
2662334Ssetje 	struct safefile *next;
2672334Ssetje };
2682334Ssetje 
2693446Smrj static struct safefile *safefiles = NULL;
2702334Ssetje #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
2712334Ssetje 
2720Sstevel@tonic-gate static void
2730Sstevel@tonic-gate usage(void)
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate 	(void) fprintf(stderr, "USAGE:\n");
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	/* archive usage */
2790Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s update-archive [-vn] [-R altroot]\n",
2800Sstevel@tonic-gate 	    prog);
2810Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-archive [-R altroot]\n", prog);
2820Sstevel@tonic-gate #ifndef __sparc
2830Sstevel@tonic-gate 	/* x86 only */
2840Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
2850Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
2860Sstevel@tonic-gate #endif
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate int
2900Sstevel@tonic-gate main(int argc, char *argv[])
2910Sstevel@tonic-gate {
2920Sstevel@tonic-gate 	error_t ret;
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
2950Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
2980Sstevel@tonic-gate 		prog = argv[0];
2990Sstevel@tonic-gate 	} else {
3000Sstevel@tonic-gate 		prog++;
3010Sstevel@tonic-gate 	}
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	/*
3050Sstevel@tonic-gate 	 * Don't depend on caller's umask
3060Sstevel@tonic-gate 	 */
3070Sstevel@tonic-gate 	(void) umask(0022);
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	parse_args(argc, argv);
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate #if defined(__sparc)
3120Sstevel@tonic-gate 	/*
3130Sstevel@tonic-gate 	 * There are only two valid invocations of bootadm
3140Sstevel@tonic-gate 	 * on SPARC:
3150Sstevel@tonic-gate 	 *
3160Sstevel@tonic-gate 	 *	- SPARC diskless server creating boot_archive for i386 clients
3170Sstevel@tonic-gate 	 *	- archive creation call during reboot of a SPARC system
3180Sstevel@tonic-gate 	 *
3190Sstevel@tonic-gate 	 *	The latter should be a NOP
3200Sstevel@tonic-gate 	 */
3210Sstevel@tonic-gate 	if (bam_cmd != BAM_ARCHIVE) {
3220Sstevel@tonic-gate 		sparc_abort();
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate #endif
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	switch (bam_cmd) {
3270Sstevel@tonic-gate 		case BAM_MENU:
3280Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
3290Sstevel@tonic-gate 			break;
3300Sstevel@tonic-gate 		case BAM_ARCHIVE:
3310Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
3320Sstevel@tonic-gate 			break;
3330Sstevel@tonic-gate 		default:
3340Sstevel@tonic-gate 			usage();
3350Sstevel@tonic-gate 			bam_exit(1);
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
3390Sstevel@tonic-gate 		bam_exit(1);
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	bam_unlock();
3420Sstevel@tonic-gate 	return (0);
3430Sstevel@tonic-gate }
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate #if defined(__sparc)
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate static void
3480Sstevel@tonic-gate sparc_abort(void)
3490Sstevel@tonic-gate {
3500Sstevel@tonic-gate 	bam_error(NOT_ON_SPARC);
3510Sstevel@tonic-gate 	bam_exit(1);
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate #endif
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate /*
3570Sstevel@tonic-gate  * Equivalence of public and internal commands:
3580Sstevel@tonic-gate  *	update-archive  -- -a update
3590Sstevel@tonic-gate  *	list-archive	-- -a list
3600Sstevel@tonic-gate  *	set-menu	-- -m set_option
3610Sstevel@tonic-gate  *	list-menu	-- -m list_entry
3620Sstevel@tonic-gate  *	update-menu	-- -m update_entry
3630Sstevel@tonic-gate  */
3640Sstevel@tonic-gate static struct cmd_map {
3650Sstevel@tonic-gate 	char *bam_cmdname;
3660Sstevel@tonic-gate 	int bam_cmd;
3670Sstevel@tonic-gate 	char *bam_subcmd;
3680Sstevel@tonic-gate } cmd_map[] = {
3690Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
3700Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
3710Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
3720Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
3730Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
3740Sstevel@tonic-gate 	{ NULL,			0,		NULL}
3750Sstevel@tonic-gate };
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate /*
3780Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
3790Sstevel@tonic-gate  */
3800Sstevel@tonic-gate static void
3810Sstevel@tonic-gate parse_args(int argc, char *argv[])
3820Sstevel@tonic-gate {
3830Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	/* command conforming to the final spec */
3860Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
3870Sstevel@tonic-gate 		/*
3880Sstevel@tonic-gate 		 * Map commands to internal table.
3890Sstevel@tonic-gate 		 */
3900Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
3910Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
3920Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
3930Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
3940Sstevel@tonic-gate 				break;
3950Sstevel@tonic-gate 			}
3960Sstevel@tonic-gate 			cmp++;
3970Sstevel@tonic-gate 		}
3980Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
3990Sstevel@tonic-gate 			usage();
4000Sstevel@tonic-gate 			bam_exit(1);
4010Sstevel@tonic-gate 		}
4020Sstevel@tonic-gate 		argc--;
4030Sstevel@tonic-gate 		argv++;
4040Sstevel@tonic-gate 	}
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	parse_args_internal(argc, argv);
4070Sstevel@tonic-gate }
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate /*
4100Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
4110Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
4120Sstevel@tonic-gate  *	-a update	-- update-archive
4130Sstevel@tonic-gate  *	-a list		-- list-archive
4140Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
4150Sstevel@tonic-gate  *	-m update_entry	-- update-menu
4160Sstevel@tonic-gate  *	-m list_entry	-- list-menu
4170Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
4180Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
4190Sstevel@tonic-gate  */
4200Sstevel@tonic-gate static void
4210Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
4220Sstevel@tonic-gate {
4230Sstevel@tonic-gate 	int c, error;
4240Sstevel@tonic-gate 	extern char *optarg;
4250Sstevel@tonic-gate 	extern int optind, opterr;
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	/* Suppress error message from getopt */
4280Sstevel@tonic-gate 	opterr = 0;
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	error = 0;
431*3467Srscott 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:")) != -1) {
4320Sstevel@tonic-gate 		switch (c) {
4330Sstevel@tonic-gate 		case 'a':
4340Sstevel@tonic-gate 			if (bam_cmd) {
4350Sstevel@tonic-gate 				error = 1;
4360Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4370Sstevel@tonic-gate 			}
4380Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
4390Sstevel@tonic-gate 			bam_subcmd = optarg;
4400Sstevel@tonic-gate 			break;
4410Sstevel@tonic-gate 		case 'd':
4420Sstevel@tonic-gate 			if (bam_debug) {
4430Sstevel@tonic-gate 				error = 1;
4440Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4450Sstevel@tonic-gate 			}
4460Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
4470Sstevel@tonic-gate 			break;
4480Sstevel@tonic-gate 		case 'f':
4490Sstevel@tonic-gate 			if (bam_force) {
4500Sstevel@tonic-gate 				error = 1;
4510Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4520Sstevel@tonic-gate 			}
4530Sstevel@tonic-gate 			bam_force = 1;
4540Sstevel@tonic-gate 			break;
4550Sstevel@tonic-gate 		case 'm':
4560Sstevel@tonic-gate 			if (bam_cmd) {
4570Sstevel@tonic-gate 				error = 1;
4580Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4590Sstevel@tonic-gate 			}
4600Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
4610Sstevel@tonic-gate 			bam_subcmd = optarg;
4620Sstevel@tonic-gate 			break;
4630Sstevel@tonic-gate 		case 'n':
4640Sstevel@tonic-gate 			if (bam_check) {
4650Sstevel@tonic-gate 				error = 1;
4660Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4670Sstevel@tonic-gate 			}
4680Sstevel@tonic-gate 			bam_check = 1;
4690Sstevel@tonic-gate 			break;
4700Sstevel@tonic-gate 		case 'o':
4710Sstevel@tonic-gate 			if (bam_opt) {
4720Sstevel@tonic-gate 				error = 1;
4730Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4740Sstevel@tonic-gate 			}
4750Sstevel@tonic-gate 			bam_opt = optarg;
4760Sstevel@tonic-gate 			break;
4770Sstevel@tonic-gate 		case 'v':
4780Sstevel@tonic-gate 			if (bam_verbose) {
4790Sstevel@tonic-gate 				error = 1;
4800Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4810Sstevel@tonic-gate 			}
4820Sstevel@tonic-gate 			bam_verbose = 1;
4830Sstevel@tonic-gate 			break;
484662Sszhou 		case 'C':
485662Sszhou 			bam_smf_check = 1;
486662Sszhou 			break;
4870Sstevel@tonic-gate 		case 'R':
4880Sstevel@tonic-gate 			if (bam_root) {
4890Sstevel@tonic-gate 				error = 1;
4900Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4910Sstevel@tonic-gate 				break;
4920Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
4930Sstevel@tonic-gate 				error = 1;
4940Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
4950Sstevel@tonic-gate 				    strerror(errno));
4960Sstevel@tonic-gate 				break;
4970Sstevel@tonic-gate 			}
498621Svikram 			bam_alt_root = 1;
4990Sstevel@tonic-gate 			bam_root = rootbuf;
5000Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
5010Sstevel@tonic-gate 			break;
5020Sstevel@tonic-gate 		case '?':
5030Sstevel@tonic-gate 			error = 1;
5040Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
5050Sstevel@tonic-gate 			break;
5060Sstevel@tonic-gate 		default :
5070Sstevel@tonic-gate 			error = 1;
5080Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
5090Sstevel@tonic-gate 			break;
5100Sstevel@tonic-gate 		}
5110Sstevel@tonic-gate 	}
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	/*
5140Sstevel@tonic-gate 	 * A command option must be specfied
5150Sstevel@tonic-gate 	 */
5160Sstevel@tonic-gate 	if (!bam_cmd) {
5170Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
5180Sstevel@tonic-gate 			usage();
5190Sstevel@tonic-gate 			bam_exit(0);
5200Sstevel@tonic-gate 		}
5210Sstevel@tonic-gate 		bam_error(NEED_CMD);
5220Sstevel@tonic-gate 		error = 1;
5230Sstevel@tonic-gate 	}
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	if (error) {
5260Sstevel@tonic-gate 		usage();
5270Sstevel@tonic-gate 		bam_exit(1);
5280Sstevel@tonic-gate 	}
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	if (optind > argc) {
5310Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
5320Sstevel@tonic-gate 		bam_exit(1);
5330Sstevel@tonic-gate 	} else if (optind < argc) {
5340Sstevel@tonic-gate 		bam_argv = &argv[optind];
5350Sstevel@tonic-gate 		bam_argc = argc - optind;
5360Sstevel@tonic-gate 	}
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	/*
5390Sstevel@tonic-gate 	 * -n implies verbose mode
5400Sstevel@tonic-gate 	 */
5410Sstevel@tonic-gate 	if (bam_check)
5420Sstevel@tonic-gate 		bam_verbose = 1;
5430Sstevel@tonic-gate }
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate static error_t
5460Sstevel@tonic-gate check_subcmd_and_options(
5470Sstevel@tonic-gate 	char *subcmd,
5480Sstevel@tonic-gate 	char *opt,
5490Sstevel@tonic-gate 	subcmd_defn_t *table,
5500Sstevel@tonic-gate 	error_t (**fp)())
5510Sstevel@tonic-gate {
5520Sstevel@tonic-gate 	int i;
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	if (subcmd == NULL) {
5550Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
5560Sstevel@tonic-gate 		return (BAM_ERROR);
5570Sstevel@tonic-gate 	}
5580Sstevel@tonic-gate 
5592115Svikram 	if (bam_argc != 0 || bam_argv) {
5602115Svikram 		if (strcmp(subcmd, "set_option") != 0 || bam_argc != 1) {
5612115Svikram 			bam_error(TRAILING_ARGS);
5622115Svikram 			usage();
5632115Svikram 			return (BAM_ERROR);
5642115Svikram 		}
5652115Svikram 	}
5662115Svikram 
5670Sstevel@tonic-gate 	if (bam_root == NULL) {
5680Sstevel@tonic-gate 		bam_root = rootbuf;
5690Sstevel@tonic-gate 		bam_rootlen = 1;
5700Sstevel@tonic-gate 	}
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 	/* verify that subcmd is valid */
5730Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
5740Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
5750Sstevel@tonic-gate 			break;
5760Sstevel@tonic-gate 	}
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
5790Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
5800Sstevel@tonic-gate 		return (BAM_ERROR);
5810Sstevel@tonic-gate 	}
5820Sstevel@tonic-gate 
5832115Svikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
5842115Svikram 		bam_error(MUST_BE_ROOT);
5852115Svikram 		return (BAM_ERROR);
5862115Svikram 	}
5872115Svikram 
5882115Svikram 	/*
5892115Svikram 	 * Currently only privileged commands need a lock
5902115Svikram 	 */
5912115Svikram 	if (table[i].unpriv == 0)
5922115Svikram 		bam_lock();
5932115Svikram 
5940Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
5950Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
5960Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
5970Sstevel@tonic-gate 			if (opt)
5980Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
5990Sstevel@tonic-gate 			else
6000Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
6010Sstevel@tonic-gate 			return (BAM_ERROR);
6020Sstevel@tonic-gate 		}
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	*fp = table[i].handler;
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	return (BAM_SUCCESS);
6080Sstevel@tonic-gate }
6090Sstevel@tonic-gate 
610316Svikram 
611316Svikram static char *
612621Svikram mount_grub_slice(int *mnted, char **physlice, char **logslice, char **fs_type)
613316Svikram {
614316Svikram 	struct extmnttab mnt;
615316Svikram 	struct stat sb;
616316Svikram 	char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32];
617348Svikram 	char cmd[PATH_MAX];
618316Svikram 	char *mntpt;
619316Svikram 	int p, l, f;
620316Svikram 	FILE *fp;
621316Svikram 
622316Svikram 	assert(mnted);
623316Svikram 	*mnted = 0;
624316Svikram 
625316Svikram 	/*
626621Svikram 	 * physlice, logslice, fs_type  args may be NULL
627316Svikram 	 */
628316Svikram 	if (physlice)
629316Svikram 		*physlice = NULL;
630621Svikram 	if (logslice)
631621Svikram 		*logslice = NULL;
632621Svikram 	if (fs_type)
633621Svikram 		*fs_type = NULL;
634316Svikram 
635316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
636316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
637316Svikram 		return (NULL);
638316Svikram 	}
639316Svikram 
640316Svikram 	fp = fopen(GRUB_slice, "r");
641316Svikram 	if (fp == NULL) {
642316Svikram 		bam_error(OPEN_FAIL, GRUB_slice, strerror(errno));
643316Svikram 		return (NULL);
644316Svikram 	}
645316Svikram 
646316Svikram 	dev[0] = fstype[0] = phys[0] = '\0';
647316Svikram 	p = sizeof ("PHYS_SLICE=") - 1;
648316Svikram 	l = sizeof ("LOG_SLICE=") - 1;
649316Svikram 	f = sizeof ("LOG_FSTYP=") - 1;
650316Svikram 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
651316Svikram 		if (strncmp(buf, "PHYS_SLICE=", p) == 0) {
652316Svikram 			(void) strlcpy(phys, buf + p, sizeof (phys));
653316Svikram 			continue;
654316Svikram 		}
655316Svikram 		if (strncmp(buf, "LOG_SLICE=", l) == 0) {
656316Svikram 			(void) strlcpy(dev, buf + l, sizeof (dev));
657316Svikram 			continue;
658316Svikram 		}
659316Svikram 		if (strncmp(buf, "LOG_FSTYP=", f) == 0) {
660316Svikram 			(void) strlcpy(fstype, buf + f, sizeof (fstype));
661316Svikram 			continue;
662316Svikram 		}
663316Svikram 	}
664316Svikram 	(void) fclose(fp);
665316Svikram 
666316Svikram 	if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') {
667316Svikram 		bam_error(BAD_SLICE_FILE, GRUB_slice);
668316Svikram 		return (NULL);
669316Svikram 	}
670316Svikram 
671316Svikram 	if (physlice) {
672316Svikram 		*physlice = s_strdup(phys);
673316Svikram 	}
674621Svikram 	if (logslice) {
675621Svikram 		*logslice = s_strdup(dev);
676621Svikram 	}
677621Svikram 	if (fs_type) {
678621Svikram 		*fs_type = s_strdup(fstype);
679621Svikram 	}
680316Svikram 
681316Svikram 	/*
682316Svikram 	 * Check if the slice is already mounted
683316Svikram 	 */
684316Svikram 	fp = fopen(MNTTAB, "r");
685316Svikram 	if (fp == NULL) {
686316Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
687621Svikram 		goto error;
688316Svikram 	}
689316Svikram 
690316Svikram 	resetmnttab(fp);
691316Svikram 
692316Svikram 	mntpt = NULL;
693316Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
694316Svikram 		if (strcmp(mnt.mnt_special, dev) == 0) {
695316Svikram 			mntpt = s_strdup(mnt.mnt_mountp);
696316Svikram 			break;
697316Svikram 		}
698316Svikram 	}
699316Svikram 
700316Svikram 	(void) fclose(fp);
701316Svikram 
702316Svikram 	if (mntpt) {
703316Svikram 		return (mntpt);
704316Svikram 	}
705316Svikram 
706316Svikram 
707316Svikram 	/*
708316Svikram 	 * GRUB slice is not mounted, we need to mount it now.
709316Svikram 	 * First create the mountpoint
710316Svikram 	 */
711316Svikram 	mntpt = s_calloc(1, PATH_MAX);
712316Svikram 	(void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid());
713316Svikram 	if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) {
714316Svikram 		bam_error(MKDIR_FAILED, mntpt, strerror(errno));
715316Svikram 		free(mntpt);
716621Svikram 		goto error;
717316Svikram 	}
718316Svikram 
719348Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount -F %s %s %s",
720348Svikram 	    fstype, dev, mntpt);
721348Svikram 
722348Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
723621Svikram 		bam_error(MOUNT_FAILED, dev, fstype);
724316Svikram 		if (rmdir(mntpt) != 0) {
725316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
726316Svikram 		}
727316Svikram 		free(mntpt);
728621Svikram 		goto error;
729316Svikram 	}
730316Svikram 
731316Svikram 	*mnted = 1;
732316Svikram 	return (mntpt);
733621Svikram 
734621Svikram error:
735621Svikram 	if (physlice) {
736621Svikram 		free(*physlice);
737621Svikram 		*physlice = NULL;
738621Svikram 	}
739621Svikram 	if (logslice) {
740621Svikram 		free(*logslice);
741621Svikram 		*logslice = NULL;
742621Svikram 	}
743621Svikram 	if (fs_type) {
744621Svikram 		free(*fs_type);
745621Svikram 		*fs_type = NULL;
746621Svikram 	}
747621Svikram 	return (NULL);
748316Svikram }
749316Svikram 
750316Svikram static void
751621Svikram umount_grub_slice(
752621Svikram 	int mnted,
753621Svikram 	char *mntpt,
754621Svikram 	char *physlice,
755621Svikram 	char *logslice,
756621Svikram 	char *fs_type)
757316Svikram {
758348Svikram 	char cmd[PATH_MAX];
759348Svikram 
760316Svikram 	/*
761316Svikram 	 * If we have not dealt with GRUB slice
762316Svikram 	 * we have nothing to do - just return.
763316Svikram 	 */
764316Svikram 	if (mntpt == NULL)
765316Svikram 		return;
766316Svikram 
767316Svikram 
768316Svikram 	/*
769316Svikram 	 * If we mounted the filesystem earlier in mount_grub_slice()
770316Svikram 	 * unmount it now.
771316Svikram 	 */
772316Svikram 	if (mnted) {
773348Svikram 		(void) snprintf(cmd, sizeof (cmd), "/sbin/umount %s",
774348Svikram 		    mntpt);
775348Svikram 		if (exec_cmd(cmd, NULL, 0) != 0) {
776348Svikram 			bam_error(UMOUNT_FAILED, mntpt);
777316Svikram 		}
778316Svikram 		if (rmdir(mntpt) != 0) {
779316Svikram 			bam_error(RMDIR_FAILED, mntpt, strerror(errno));
780316Svikram 		}
781316Svikram 	}
782621Svikram 
783316Svikram 	if (physlice)
784316Svikram 		free(physlice);
785621Svikram 	if (logslice)
786621Svikram 		free(logslice);
787621Svikram 	if (fs_type)
788621Svikram 		free(fs_type);
789621Svikram 
790316Svikram 	free(mntpt);
791316Svikram }
792316Svikram 
793621Svikram static char *
794621Svikram use_stubboot(void)
795621Svikram {
796621Svikram 	int mnted;
797621Svikram 	struct stat sb;
798621Svikram 	struct extmnttab mnt;
799621Svikram 	FILE *fp;
800621Svikram 	char cmd[PATH_MAX];
801621Svikram 
802621Svikram 	if (stat(STUBBOOT, &sb) != 0) {
803621Svikram 		bam_error(STUBBOOT_DIR_NOT_FOUND);
804621Svikram 		return (NULL);
805621Svikram 	}
806621Svikram 
807621Svikram 	/*
808621Svikram 	 * Check if stubboot is mounted. If not, mount it
809621Svikram 	 */
810621Svikram 	fp = fopen(MNTTAB, "r");
811621Svikram 	if (fp == NULL) {
812621Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
813621Svikram 		return (NULL);
814621Svikram 	}
815621Svikram 
816621Svikram 	resetmnttab(fp);
817621Svikram 
818621Svikram 	mnted = 0;
819621Svikram 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
820621Svikram 		if (strcmp(mnt.mnt_mountp, STUBBOOT) == 0) {
821621Svikram 			mnted = 1;
822621Svikram 			break;
823621Svikram 		}
824621Svikram 	}
825621Svikram 
826621Svikram 	(void) fclose(fp);
827621Svikram 
828621Svikram 	if (mnted)
829621Svikram 		return (STUBBOOT);
830621Svikram 
831621Svikram 	/*
832621Svikram 	 * Stubboot is not mounted, mount it now.
833621Svikram 	 * It should exist in /etc/vfstab
834621Svikram 	 */
835621Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/mount %s",
836621Svikram 	    STUBBOOT);
837621Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
838621Svikram 		bam_error(MOUNT_MNTPT_FAILED, STUBBOOT);
839621Svikram 		return (NULL);
840621Svikram 	}
841621Svikram 
842621Svikram 	return (STUBBOOT);
843621Svikram }
844621Svikram 
845621Svikram static void
846621Svikram disp_active_menu_locn(char *menu_path, char *logslice, char *fstype, int mnted)
847621Svikram {
848621Svikram 	/*
849621Svikram 	 * Check if we did a temp mount of an unmounted device.
850621Svikram 	 * If yes, print the block device and fstype for that device
851621Svikram 	 * else it is already mounted, so we print the path to the GRUB menu.
852621Svikram 	 */
853621Svikram 	if (mnted) {
854621Svikram 		bam_print(GRUB_MENU_DEVICE, logslice);
855621Svikram 		bam_print(GRUB_MENU_FSTYPE, fstype);
856621Svikram 	} else {
857621Svikram 		bam_print(GRUB_MENU_PATH, menu_path);
858621Svikram 	}
859621Svikram }
860621Svikram 
861621Svikram /*
862621Svikram  * NOTE: A single "/" is also considered a trailing slash and will
863621Svikram  * be deleted.
864621Svikram  */
865621Svikram static void
866621Svikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
867621Svikram {
868621Svikram 	size_t dstlen;
869621Svikram 
870621Svikram 	assert(src);
871621Svikram 	assert(dst);
872621Svikram 
873621Svikram 	(void) strlcpy(dst, src, dstsize);
874621Svikram 
875621Svikram 	dstlen = strlen(dst);
876621Svikram 	if (dst[dstlen - 1] == '/') {
877621Svikram 		dst[dstlen - 1] = '\0';
878621Svikram 	}
879621Svikram }
880621Svikram 
8810Sstevel@tonic-gate static error_t
8820Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
8830Sstevel@tonic-gate {
8840Sstevel@tonic-gate 	error_t ret;
8850Sstevel@tonic-gate 	char menu_path[PATH_MAX];
8863446Smrj 	char path[PATH_MAX];
8870Sstevel@tonic-gate 	menu_t *menu;
888621Svikram 	char *mntpt, *menu_root, *logslice, *fstype;
889316Svikram 	struct stat sb;
890316Svikram 	int mnted;	/* set if we did a mount */
8910Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	/*
8940Sstevel@tonic-gate 	 * Check arguments
8950Sstevel@tonic-gate 	 */
8960Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
8970Sstevel@tonic-gate 	if (ret == BAM_ERROR) {
8980Sstevel@tonic-gate 		return (BAM_ERROR);
8990Sstevel@tonic-gate 	}
9000Sstevel@tonic-gate 
901316Svikram 	mntpt = NULL;
902316Svikram 	mnted = 0;
903621Svikram 	logslice = fstype = NULL;
904621Svikram 
905621Svikram 	/*
9063446Smrj 	 * Check for the menu.list file:
9073446Smrj 	 *
9083446Smrj 	 * 1. Check for a GRUB_slice file, be it on / or
9093446Smrj 	 *    on the user-provided alternate root.
9103446Smrj 	 * 2. Use the alternate root, if given.
9113446Smrj 	 * 3. Check /stubboot
9123446Smrj 	 * 4. Use /
913621Svikram 	 */
914621Svikram 	if (bam_alt_root) {
9153446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
9163446Smrj 		    GRUB_slice);
9173446Smrj 	} else {
9183446Smrj 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
9193446Smrj 	}
9203446Smrj 
9213446Smrj 	if (stat(path, &sb) == 0) {
922621Svikram 		mntpt = mount_grub_slice(&mnted, NULL, &logslice, &fstype);
923316Svikram 		menu_root = mntpt;
9243446Smrj 	} else if (bam_alt_root) {
9253446Smrj 		menu_root = bam_root;
926621Svikram 	} else if (stat(STUBBOOT, &sb) == 0) {
927621Svikram 		menu_root = use_stubboot();
928316Svikram 	} else {
929316Svikram 		menu_root = bam_root;
930316Svikram 	}
931316Svikram 
932621Svikram 	if (menu_root == NULL) {
933621Svikram 		bam_error(CANNOT_LOCATE_GRUB_MENU);
934621Svikram 		return (BAM_ERROR);
935621Svikram 	}
936621Svikram 
937621Svikram 	elide_trailing_slash(menu_root, menu_path, sizeof (menu_path));
938621Svikram 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
939621Svikram 
940621Svikram 	/*
941621Svikram 	 * If listing the menu, display the active menu
942621Svikram 	 * location
943621Svikram 	 */
944621Svikram 	if (strcmp(subcmd, "list_entry") == 0) {
945621Svikram 		disp_active_menu_locn(menu_path, logslice, fstype, mnted);
946621Svikram 	}
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	menu = menu_read(menu_path);
9490Sstevel@tonic-gate 	assert(menu);
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 	/*
9520Sstevel@tonic-gate 	 * Special handling for setting timeout and default
9530Sstevel@tonic-gate 	 */
9540Sstevel@tonic-gate 	if (strcmp(subcmd, "set_option") == 0) {
9550Sstevel@tonic-gate 		if (largc != 1 || largv[0] == NULL) {
9560Sstevel@tonic-gate 			usage();
957621Svikram 			menu_free(menu);
958621Svikram 			umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
9590Sstevel@tonic-gate 			return (BAM_ERROR);
9600Sstevel@tonic-gate 		}
9610Sstevel@tonic-gate 		opt = largv[0];
9620Sstevel@tonic-gate 	} else if (largc != 0) {
9630Sstevel@tonic-gate 		usage();
964621Svikram 		menu_free(menu);
965621Svikram 		umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
9660Sstevel@tonic-gate 		return (BAM_ERROR);
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 
9693446Smrj 	ret = dboot_or_multiboot(bam_root);
9703446Smrj 	if (ret != BAM_SUCCESS)
9713446Smrj 		return (ret);
9723446Smrj 
9730Sstevel@tonic-gate 	/*
9740Sstevel@tonic-gate 	 * Once the sub-cmd handler has run
9750Sstevel@tonic-gate 	 * only the line field is guaranteed to have valid values
9760Sstevel@tonic-gate 	 */
9773446Smrj 	if ((strcmp(subcmd, "update_entry") == 0) ||
9783446Smrj 	    (strcmp(subcmd, "upgrade") == 0))
9790Sstevel@tonic-gate 		ret = f(menu, bam_root, opt);
9800Sstevel@tonic-gate 	else
9810Sstevel@tonic-gate 		ret = f(menu, menu_path, opt);
9820Sstevel@tonic-gate 	if (ret == BAM_WRITE) {
983316Svikram 		ret = menu_write(menu_root, menu);
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 	menu_free(menu);
9870Sstevel@tonic-gate 
988621Svikram 	umount_grub_slice(mnted, mntpt, NULL, logslice, fstype);
989316Svikram 
9900Sstevel@tonic-gate 	return (ret);
9910Sstevel@tonic-gate }
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate static error_t
9950Sstevel@tonic-gate bam_archive(
9960Sstevel@tonic-gate 	char *subcmd,
9970Sstevel@tonic-gate 	char *opt)
9980Sstevel@tonic-gate {
9990Sstevel@tonic-gate 	error_t ret;
10000Sstevel@tonic-gate 	error_t (*f)(char *root, char *opt);
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	/*
1003662Sszhou 	 * Add trailing / for archive subcommands
1004662Sszhou 	 */
1005662Sszhou 	if (rootbuf[strlen(rootbuf) - 1] != '/')
1006662Sszhou 		(void) strcat(rootbuf, "/");
1007662Sszhou 	bam_rootlen = strlen(rootbuf);
1008662Sszhou 
1009662Sszhou 	/*
10100Sstevel@tonic-gate 	 * Check arguments
10110Sstevel@tonic-gate 	 */
10120Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
10130Sstevel@tonic-gate 	if (ret != BAM_SUCCESS) {
10140Sstevel@tonic-gate 		return (BAM_ERROR);
10150Sstevel@tonic-gate 	}
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate #if defined(__sparc)
10180Sstevel@tonic-gate 	/*
10190Sstevel@tonic-gate 	 * A NOP if called on SPARC during reboot
10200Sstevel@tonic-gate 	 */
10210Sstevel@tonic-gate 	if (strcmp(subcmd, "update_all") == 0)
10220Sstevel@tonic-gate 		return (BAM_SUCCESS);
10230Sstevel@tonic-gate 	else if (strcmp(subcmd, "update") != 0)
10240Sstevel@tonic-gate 		sparc_abort();
10250Sstevel@tonic-gate #endif
10260Sstevel@tonic-gate 
10273446Smrj 	ret = dboot_or_multiboot(rootbuf);
10283446Smrj 	if (ret != BAM_SUCCESS)
10293446Smrj 		return (ret);
10303446Smrj 
10310Sstevel@tonic-gate 	/*
10320Sstevel@tonic-gate 	 * Check archive not supported with update_all
10330Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
10340Sstevel@tonic-gate 	 * information for each BE.
10350Sstevel@tonic-gate 	 */
10360Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
10370Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
10380Sstevel@tonic-gate 		return (BAM_ERROR);
10390Sstevel@tonic-gate 	}
10400Sstevel@tonic-gate 
1041316Svikram 	if (strcmp(subcmd, "update_all") == 0)
1042316Svikram 		bam_update_all = 1;
1043316Svikram 
1044316Svikram 	ret = f(bam_root, opt);
1045316Svikram 
1046316Svikram 	bam_update_all = 0;
1047316Svikram 
1048316Svikram 	return (ret);
10490Sstevel@tonic-gate }
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate /*PRINTFLIKE1*/
10523446Smrj void
10530Sstevel@tonic-gate bam_error(char *format, ...)
10540Sstevel@tonic-gate {
10550Sstevel@tonic-gate 	va_list ap;
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 	va_start(ap, format);
10580Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
10590Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
10600Sstevel@tonic-gate 	va_end(ap);
10610Sstevel@tonic-gate }
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate /*PRINTFLIKE1*/
10640Sstevel@tonic-gate static void
10650Sstevel@tonic-gate bam_print(char *format, ...)
10660Sstevel@tonic-gate {
10670Sstevel@tonic-gate 	va_list ap;
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	va_start(ap, format);
10700Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
10710Sstevel@tonic-gate 	va_end(ap);
10720Sstevel@tonic-gate }
10730Sstevel@tonic-gate 
10743446Smrj /*PRINTFLIKE1*/
10753446Smrj void
10763446Smrj bam_print_stderr(char *format, ...)
10773446Smrj {
10783446Smrj 	va_list ap;
10793446Smrj 
10803446Smrj 	va_start(ap, format);
10813446Smrj 	(void) vfprintf(stderr, format, ap);
10823446Smrj 	va_end(ap);
10833446Smrj }
10843446Smrj 
10850Sstevel@tonic-gate static void
10860Sstevel@tonic-gate bam_exit(int excode)
10870Sstevel@tonic-gate {
10880Sstevel@tonic-gate 	bam_unlock();
10890Sstevel@tonic-gate 	exit(excode);
10900Sstevel@tonic-gate }
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate static void
10930Sstevel@tonic-gate bam_lock(void)
10940Sstevel@tonic-gate {
10950Sstevel@tonic-gate 	struct flock lock;
10960Sstevel@tonic-gate 	pid_t pid;
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
10990Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11000Sstevel@tonic-gate 		/*
11010Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
11020Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
11030Sstevel@tonic-gate 		 * Proceed without the lock
11040Sstevel@tonic-gate 		 */
11050Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
11060Sstevel@tonic-gate 			bam_root_readonly = 1;
11070Sstevel@tonic-gate 			return;
11080Sstevel@tonic-gate 		}
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
11110Sstevel@tonic-gate 		bam_exit(1);
11120Sstevel@tonic-gate 	}
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
11150Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
11160Sstevel@tonic-gate 	lock.l_start = 0;
11170Sstevel@tonic-gate 	lock.l_len = 0;
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
11200Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
11210Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11220Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11230Sstevel@tonic-gate 			bam_lock_fd = -1;
11240Sstevel@tonic-gate 			bam_exit(1);
11250Sstevel@tonic-gate 		}
11260Sstevel@tonic-gate 		pid = 0;
11270Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
11280Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
11310Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
11320Sstevel@tonic-gate 		lock.l_start = 0;
11330Sstevel@tonic-gate 		lock.l_len = 0;
11340Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
11350Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11360Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11370Sstevel@tonic-gate 			bam_lock_fd = -1;
11380Sstevel@tonic-gate 			bam_exit(1);
11390Sstevel@tonic-gate 		}
11400Sstevel@tonic-gate 	}
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	/* We own the lock now */
11430Sstevel@tonic-gate 	pid = getpid();
11440Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
11450Sstevel@tonic-gate }
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate static void
11480Sstevel@tonic-gate bam_unlock(void)
11490Sstevel@tonic-gate {
11500Sstevel@tonic-gate 	struct flock unlock;
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 	/*
11530Sstevel@tonic-gate 	 * NOP if we don't hold the lock
11540Sstevel@tonic-gate 	 */
11550Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11560Sstevel@tonic-gate 		return;
11570Sstevel@tonic-gate 	}
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
11600Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
11610Sstevel@tonic-gate 	unlock.l_start = 0;
11620Sstevel@tonic-gate 	unlock.l_len = 0;
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
11650Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11660Sstevel@tonic-gate 	}
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
11690Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
11700Sstevel@tonic-gate 	}
11710Sstevel@tonic-gate 	bam_lock_fd = -1;
11720Sstevel@tonic-gate }
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate static error_t
11750Sstevel@tonic-gate list_archive(char *root, char *opt)
11760Sstevel@tonic-gate {
11770Sstevel@tonic-gate 	filelist_t flist;
11780Sstevel@tonic-gate 	filelist_t *flistp = &flist;
11790Sstevel@tonic-gate 	line_t *lp;
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 	assert(root);
11820Sstevel@tonic-gate 	assert(opt == NULL);
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
11850Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
11860Sstevel@tonic-gate 		return (BAM_ERROR);
11870Sstevel@tonic-gate 	}
11880Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
11890Sstevel@tonic-gate 
11900Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
11910Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
11920Sstevel@tonic-gate 	}
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 	filelist_free(flistp);
11950Sstevel@tonic-gate 
11960Sstevel@tonic-gate 	return (BAM_SUCCESS);
11970Sstevel@tonic-gate }
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate /*
12000Sstevel@tonic-gate  * This routine writes a list of lines to a file.
12010Sstevel@tonic-gate  * The list is *not* freed
12020Sstevel@tonic-gate  */
12030Sstevel@tonic-gate static error_t
12040Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
12050Sstevel@tonic-gate {
12060Sstevel@tonic-gate 	char tmpfile[PATH_MAX];
12070Sstevel@tonic-gate 	char path[PATH_MAX];
12080Sstevel@tonic-gate 	FILE *fp;
12090Sstevel@tonic-gate 	int ret;
12100Sstevel@tonic-gate 	struct stat sb;
12110Sstevel@tonic-gate 	mode_t mode;
12120Sstevel@tonic-gate 	uid_t root_uid;
12130Sstevel@tonic-gate 	gid_t sys_gid;
12140Sstevel@tonic-gate 	struct passwd *pw;
12150Sstevel@tonic-gate 	struct group *gp;
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 	if (start == NULL) {
12210Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
12220Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
12230Sstevel@tonic-gate 			if (unlink(path) != 0) {
12240Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
12250Sstevel@tonic-gate 				return (BAM_ERROR);
12260Sstevel@tonic-gate 			} else {
12270Sstevel@tonic-gate 				return (BAM_SUCCESS);
12280Sstevel@tonic-gate 			}
12290Sstevel@tonic-gate 		}
12300Sstevel@tonic-gate 	}
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 	/*
12330Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
12340Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
12350Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
12360Sstevel@tonic-gate 	 */
12370Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
12380Sstevel@tonic-gate 		mode = sb.st_mode;
12390Sstevel@tonic-gate 		root_uid = sb.st_uid;
12400Sstevel@tonic-gate 		sys_gid = sb.st_gid;
12410Sstevel@tonic-gate 	} else {
12420Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
12430Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
12440Sstevel@tonic-gate 			root_uid = pw->pw_uid;
12450Sstevel@tonic-gate 		} else {
12460Sstevel@tonic-gate 			if (bam_verbose)
12470Sstevel@tonic-gate 				bam_error(CANT_FIND_USER,
12480Sstevel@tonic-gate 				    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
12490Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
12500Sstevel@tonic-gate 		}
12510Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
12520Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
12530Sstevel@tonic-gate 		} else {
12540Sstevel@tonic-gate 			if (bam_verbose)
12550Sstevel@tonic-gate 				bam_error(CANT_FIND_GROUP,
12560Sstevel@tonic-gate 				    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
12570Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
12580Sstevel@tonic-gate 		}
12590Sstevel@tonic-gate 	}
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	/* Truncate tmpfile first */
12640Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
12650Sstevel@tonic-gate 	if (fp == NULL) {
12660Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12670Sstevel@tonic-gate 		return (BAM_ERROR);
12680Sstevel@tonic-gate 	}
12690Sstevel@tonic-gate 	ret = fclose(fp);
12700Sstevel@tonic-gate 	if (ret == EOF) {
12710Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12720Sstevel@tonic-gate 		return (BAM_ERROR);
12730Sstevel@tonic-gate 	}
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 	/* Now open it in append mode */
12760Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
12770Sstevel@tonic-gate 	if (fp == NULL) {
12780Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12790Sstevel@tonic-gate 		return (BAM_ERROR);
12800Sstevel@tonic-gate 	}
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 	for (; start; start = start->next) {
12830Sstevel@tonic-gate 		ret = s_fputs(start->line, fp);
12840Sstevel@tonic-gate 		if (ret == EOF) {
12850Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
12860Sstevel@tonic-gate 			(void) fclose(fp);
12870Sstevel@tonic-gate 			return (BAM_ERROR);
12880Sstevel@tonic-gate 		}
12890Sstevel@tonic-gate 	}
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	ret = fclose(fp);
12920Sstevel@tonic-gate 	if (ret == EOF) {
12930Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12940Sstevel@tonic-gate 		return (BAM_ERROR);
12950Sstevel@tonic-gate 	}
12960Sstevel@tonic-gate 
12970Sstevel@tonic-gate 	/*
1298271Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1299271Sjg 	 * not supporting these operations - pcfs reports unsupported
1300271Sjg 	 * operations as EINVAL.
13010Sstevel@tonic-gate 	 */
13020Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1303271Sjg 	if (ret == -1 &&
1304271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13050Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
13060Sstevel@tonic-gate 		return (BAM_ERROR);
13070Sstevel@tonic-gate 	}
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1310271Sjg 	if (ret == -1 &&
1311271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13120Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
13130Sstevel@tonic-gate 		return (BAM_ERROR);
13140Sstevel@tonic-gate 	}
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate 	/*
13180Sstevel@tonic-gate 	 * Do an atomic rename
13190Sstevel@tonic-gate 	 */
13200Sstevel@tonic-gate 	ret = rename(tmpfile, path);
13210Sstevel@tonic-gate 	if (ret != 0) {
13220Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
13230Sstevel@tonic-gate 		return (BAM_ERROR);
13240Sstevel@tonic-gate 	}
13250Sstevel@tonic-gate 
13260Sstevel@tonic-gate 	return (BAM_SUCCESS);
13270Sstevel@tonic-gate }
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate /*
13300Sstevel@tonic-gate  * This function should always return 0 - since we want
13310Sstevel@tonic-gate  * to create stat data for *all* files in the list.
13320Sstevel@tonic-gate  */
13330Sstevel@tonic-gate /*ARGSUSED*/
13340Sstevel@tonic-gate static int
13350Sstevel@tonic-gate cmpstat(
13360Sstevel@tonic-gate 	const char *file,
13370Sstevel@tonic-gate 	const struct stat *stat,
13380Sstevel@tonic-gate 	int flags,
13390Sstevel@tonic-gate 	struct FTW *ftw)
13400Sstevel@tonic-gate {
13410Sstevel@tonic-gate 	uint_t sz;
13420Sstevel@tonic-gate 	uint64_t *value;
13430Sstevel@tonic-gate 	uint64_t filestat[2];
13440Sstevel@tonic-gate 	int error;
13450Sstevel@tonic-gate 
13462334Ssetje 	struct safefile *safefilep;
13472334Ssetje 	FILE *fp;
13482334Ssetje 
13490Sstevel@tonic-gate 	/*
13500Sstevel@tonic-gate 	 * We only want regular files
13510Sstevel@tonic-gate 	 */
13520Sstevel@tonic-gate 	if (!S_ISREG(stat->st_mode))
13530Sstevel@tonic-gate 		return (0);
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 	/*
13560Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
13570Sstevel@tonic-gate 	 * but this is not fatal to update determination.
13580Sstevel@tonic-gate 	 */
13590Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
13600Sstevel@tonic-gate 		filestat[0] = stat->st_size;
13610Sstevel@tonic-gate 		filestat[1] = stat->st_mtime;
13620Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
13630Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
13640Sstevel@tonic-gate 		if (error)
13650Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
13660Sstevel@tonic-gate 	}
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 	/*
13690Sstevel@tonic-gate 	 * The remaining steps are only required if we haven't made a
13700Sstevel@tonic-gate 	 * decision about update or if we are checking (-n)
13710Sstevel@tonic-gate 	 */
13720Sstevel@tonic-gate 	if (walk_arg.need_update && !bam_check)
13730Sstevel@tonic-gate 		return (0);
13740Sstevel@tonic-gate 
13750Sstevel@tonic-gate 	/*
13762334Ssetje 	 * If we are invoked as part of system/filesyste/boot-archive, then
13772334Ssetje 	 * there are a number of things we should not worry about
13780Sstevel@tonic-gate 	 */
13792334Ssetje 	if (bam_smf_check) {
13802334Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
13812334Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
13822334Ssetje 			return (0);
13832334Ssetje 
13842334Ssetje 		/* read in list of safe files */
13852334Ssetje 		if (safefiles == NULL)
13862334Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
13872334Ssetje 				safefiles = s_calloc(1,
13882334Ssetje 				    sizeof (struct safefile));
13892334Ssetje 				safefilep = safefiles;
13902334Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
13912334Ssetje 				    MAXNAMELEN);
13922334Ssetje 				safefilep->next = NULL;
13932334Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
13942334Ssetje 				    MAXNAMELEN, fp) != NULL) {
13952334Ssetje 					safefilep->next = s_calloc(1,
13962334Ssetje 					    sizeof (struct safefile));
13972334Ssetje 					safefilep = safefilep->next;
13982334Ssetje 					safefilep->name = s_calloc(1,
13992334Ssetje 					    MAXPATHLEN + MAXNAMELEN);
14002334Ssetje 					safefilep->next = NULL;
14012334Ssetje 				}
14022334Ssetje 				(void) fclose(fp);
14032334Ssetje 			}
14042334Ssetje 
14052334Ssetje 		safefilep = safefiles;
14062334Ssetje 		while (safefilep->next != NULL)
14072334Ssetje 			if (strcmp(file, safefilep->name) != 0) {
14082334Ssetje 				fp = fopen(NEED_UPDATE_FILE, "w");
14092334Ssetje 				if (fclose(fp) != 0)
14102334Ssetje 					bam_error(CLOSE_FAIL, NEED_UPDATE_FILE,
14112334Ssetje 						strerror(errno));
14122334Ssetje 				return (0);
14132334Ssetje 			}
14142334Ssetje 	}
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 	/*
14170Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
14180Sstevel@tonic-gate 	 */
14190Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
14200Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
14210Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
14220Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
14230Sstevel@tonic-gate 			return (0);
14240Sstevel@tonic-gate 		walk_arg.need_update = 1;
14250Sstevel@tonic-gate 		if (bam_verbose)
14260Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
14270Sstevel@tonic-gate 		return (0);
14280Sstevel@tonic-gate 	}
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	/*
14310Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
14320Sstevel@tonic-gate 	 */
14330Sstevel@tonic-gate 	assert(sz == 2);
14340Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
14350Sstevel@tonic-gate 
14360Sstevel@tonic-gate 	if (filestat[0] != stat->st_size ||
14370Sstevel@tonic-gate 	    filestat[1] != stat->st_mtime) {
14380Sstevel@tonic-gate 		walk_arg.need_update = 1;
14390Sstevel@tonic-gate 		if (bam_verbose)
14400Sstevel@tonic-gate 			if (bam_smf_check)
14410Sstevel@tonic-gate 				bam_print("    %s\n", file);
14420Sstevel@tonic-gate 			else
14430Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
14440Sstevel@tonic-gate 	}
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 	return (0);
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate /*
14500Sstevel@tonic-gate  * Check flags and presence of required files.
14510Sstevel@tonic-gate  * The force flag and/or absence of files should
14520Sstevel@tonic-gate  * trigger an update.
14530Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
14540Sstevel@tonic-gate  * (as -n should only produce parseable output.)
14550Sstevel@tonic-gate  */
14560Sstevel@tonic-gate static void
14570Sstevel@tonic-gate check_flags_and_files(char *root)
14580Sstevel@tonic-gate {
14590Sstevel@tonic-gate 	char path[PATH_MAX];
14600Sstevel@tonic-gate 	struct stat sb;
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	/*
14630Sstevel@tonic-gate 	 * if force, create archive unconditionally
14640Sstevel@tonic-gate 	 */
14650Sstevel@tonic-gate 	if (bam_force) {
14660Sstevel@tonic-gate 		walk_arg.need_update = 1;
14670Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
14680Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
14690Sstevel@tonic-gate 		return;
14700Sstevel@tonic-gate 	}
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 	/*
14730Sstevel@tonic-gate 	 * If archive is missing, create archive
14740Sstevel@tonic-gate 	 */
14753446Smrj 	(void) snprintf(path, sizeof (path), "%s%s", root,
14763446Smrj 	    DIRECT_BOOT_ARCHIVE_32);
14770Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
14780Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
14790Sstevel@tonic-gate 			bam_print(UPDATE_ARCH_MISS, path);
14800Sstevel@tonic-gate 		walk_arg.need_update = 1;
14810Sstevel@tonic-gate 		return;
14820Sstevel@tonic-gate 	}
14833446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
14843446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", root,
14853446Smrj 		    DIRECT_BOOT_ARCHIVE_64);
14863446Smrj 		if (stat(path, &sb) != 0) {
14873446Smrj 			if (bam_verbose && !bam_check)
14883446Smrj 				bam_print(UPDATE_ARCH_MISS, path);
14893446Smrj 			walk_arg.need_update = 1;
14903446Smrj 			return;
14913446Smrj 		}
14923446Smrj 	}
14930Sstevel@tonic-gate }
14940Sstevel@tonic-gate 
14950Sstevel@tonic-gate static error_t
14960Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
14970Sstevel@tonic-gate {
14980Sstevel@tonic-gate 	char path[PATH_MAX];
14990Sstevel@tonic-gate 	FILE *fp;
15000Sstevel@tonic-gate 	char buf[BAM_MAXLINE];
15010Sstevel@tonic-gate 
15020Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate 	fp = fopen(path, "r");
15050Sstevel@tonic-gate 	if (fp == NULL) {
15060Sstevel@tonic-gate 		if (bam_debug)
15070Sstevel@tonic-gate 			bam_error(FLIST_FAIL, path, strerror(errno));
15080Sstevel@tonic-gate 		return (BAM_ERROR);
15090Sstevel@tonic-gate 	}
15100Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1511316Svikram 		/* skip blank lines */
1512316Svikram 		if (strspn(buf, " \t") == strlen(buf))
1513316Svikram 			continue;
15140Sstevel@tonic-gate 		append_to_flist(flistp, buf);
15150Sstevel@tonic-gate 	}
15160Sstevel@tonic-gate 	if (fclose(fp) != 0) {
15170Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
15180Sstevel@tonic-gate 		return (BAM_ERROR);
15190Sstevel@tonic-gate 	}
15200Sstevel@tonic-gate 	return (BAM_SUCCESS);
15210Sstevel@tonic-gate }
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate static error_t
15240Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
15250Sstevel@tonic-gate {
15260Sstevel@tonic-gate 	int rval;
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
15290Sstevel@tonic-gate 
15300Sstevel@tonic-gate 	/*
15310Sstevel@tonic-gate 	 * Read current lists of files - only the first is mandatory
15320Sstevel@tonic-gate 	 */
15330Sstevel@tonic-gate 	rval = read_one_list(root, flistp, BOOT_FILE_LIST);
15340Sstevel@tonic-gate 	if (rval != BAM_SUCCESS)
15350Sstevel@tonic-gate 		return (rval);
15360Sstevel@tonic-gate 	(void) read_one_list(root, flistp, ETC_FILE_LIST);
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate 	if (flistp->head == NULL) {
15390Sstevel@tonic-gate 		bam_error(NO_FLIST);
15400Sstevel@tonic-gate 		return (BAM_ERROR);
15410Sstevel@tonic-gate 	}
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 	return (BAM_SUCCESS);
15440Sstevel@tonic-gate }
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate static void
15470Sstevel@tonic-gate getoldstat(char *root)
15480Sstevel@tonic-gate {
15490Sstevel@tonic-gate 	char path[PATH_MAX];
15500Sstevel@tonic-gate 	int fd, error;
15510Sstevel@tonic-gate 	struct stat sb;
15520Sstevel@tonic-gate 	char *ostat;
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
15550Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
15560Sstevel@tonic-gate 	if (fd == -1) {
15570Sstevel@tonic-gate 		if (bam_verbose)
15580Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
15590Sstevel@tonic-gate 		walk_arg.need_update = 1;
15600Sstevel@tonic-gate 		return;
15610Sstevel@tonic-gate 	}
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
15640Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
15650Sstevel@tonic-gate 		(void) close(fd);
15660Sstevel@tonic-gate 		walk_arg.need_update = 1;
15670Sstevel@tonic-gate 		return;
15680Sstevel@tonic-gate 	}
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
15710Sstevel@tonic-gate 
15720Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
15730Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
15740Sstevel@tonic-gate 		(void) close(fd);
15750Sstevel@tonic-gate 		free(ostat);
15760Sstevel@tonic-gate 		walk_arg.need_update = 1;
15770Sstevel@tonic-gate 		return;
15780Sstevel@tonic-gate 	}
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate 	(void) close(fd);
15810Sstevel@tonic-gate 
15820Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
15830Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
15840Sstevel@tonic-gate 
15850Sstevel@tonic-gate 	free(ostat);
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate 	if (error) {
15880Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
15890Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
15900Sstevel@tonic-gate 		walk_arg.need_update = 1;
15910Sstevel@tonic-gate 		return;
15920Sstevel@tonic-gate 	}
15930Sstevel@tonic-gate }
15940Sstevel@tonic-gate 
15952583Svikram /*
15962583Svikram  * Checks if a file in the current (old) archive has
15972583Svikram  * been deleted from the root filesystem. This is needed for
15982583Svikram  * software like Trusted Extensions (TX) that switch early
15992583Svikram  * in boot based on presence/absence of a kernel module.
16002583Svikram  */
16012583Svikram static void
16022583Svikram check4stale(char *root)
16032583Svikram {
16042583Svikram 	nvpair_t	*nvp;
16052583Svikram 	nvlist_t	*nvlp;
16062583Svikram 	char 		*file;
16072583Svikram 	char		path[PATH_MAX];
16082583Svikram 	struct stat	sb;
16092583Svikram 
16102583Svikram 	/*
16112583Svikram 	 * Skip stale file check during smf check
16122583Svikram 	 */
16132583Svikram 	if (bam_smf_check)
16142583Svikram 		return;
16152583Svikram 
16162583Svikram 	/* Nothing to do if no old stats */
16172583Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
16182583Svikram 		return;
16192583Svikram 
16202583Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
16212583Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
16222583Svikram 		file = nvpair_name(nvp);
16232583Svikram 		if (file == NULL)
16242583Svikram 			continue;
16252583Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
16262583Svikram 		    root, file);
16272583Svikram 		if (stat(path, &sb) == -1) {
16282583Svikram 			walk_arg.need_update = 1;
16292583Svikram 			if (bam_verbose)
16302583Svikram 				bam_print(PARSEABLE_STALE_FILE, path);
16312583Svikram 		}
16322583Svikram 	}
16332583Svikram }
16342583Svikram 
16350Sstevel@tonic-gate static void
16360Sstevel@tonic-gate create_newstat(void)
16370Sstevel@tonic-gate {
16380Sstevel@tonic-gate 	int error;
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
16410Sstevel@tonic-gate 	if (error) {
16420Sstevel@tonic-gate 		/*
16430Sstevel@tonic-gate 		 * Not fatal - we can still create archive
16440Sstevel@tonic-gate 		 */
16450Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
16460Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
16470Sstevel@tonic-gate 	}
16480Sstevel@tonic-gate }
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate static void
16510Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
16520Sstevel@tonic-gate {
16530Sstevel@tonic-gate 	char path[PATH_MAX];
16540Sstevel@tonic-gate 	line_t *lp;
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
16570Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
16580Sstevel@tonic-gate 		/* XXX shouldn't we use FTW_MOUNT ? */
16590Sstevel@tonic-gate 		if (nftw(path, cmpstat, 20, 0) == -1) {
16600Sstevel@tonic-gate 			/*
16610Sstevel@tonic-gate 			 * Some files may not exist.
16620Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
16630Sstevel@tonic-gate 			 * Emit verbose message only
16640Sstevel@tonic-gate 			 */
16650Sstevel@tonic-gate 			if (bam_verbose)
16660Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
16670Sstevel@tonic-gate 		}
16680Sstevel@tonic-gate 	}
16690Sstevel@tonic-gate }
16700Sstevel@tonic-gate 
16710Sstevel@tonic-gate static void
16720Sstevel@tonic-gate savenew(char *root)
16730Sstevel@tonic-gate {
16740Sstevel@tonic-gate 	char path[PATH_MAX];
16750Sstevel@tonic-gate 	char path2[PATH_MAX];
16760Sstevel@tonic-gate 	size_t sz;
16770Sstevel@tonic-gate 	char *nstat;
16780Sstevel@tonic-gate 	int fd, wrote, error;
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 	nstat = NULL;
16810Sstevel@tonic-gate 	sz = 0;
16820Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
16830Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
16840Sstevel@tonic-gate 	if (error) {
16850Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
16860Sstevel@tonic-gate 		return;
16870Sstevel@tonic-gate 	}
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
16900Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
16910Sstevel@tonic-gate 	if (fd == -1) {
16920Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
16930Sstevel@tonic-gate 		free(nstat);
16940Sstevel@tonic-gate 		return;
16950Sstevel@tonic-gate 	}
16960Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
16970Sstevel@tonic-gate 	if (wrote != sz) {
16980Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
16990Sstevel@tonic-gate 		(void) close(fd);
17000Sstevel@tonic-gate 		free(nstat);
17010Sstevel@tonic-gate 		return;
17020Sstevel@tonic-gate 	}
17030Sstevel@tonic-gate 	(void) close(fd);
17040Sstevel@tonic-gate 	free(nstat);
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
17070Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
17080Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
17090Sstevel@tonic-gate 	}
17100Sstevel@tonic-gate }
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate static void
17130Sstevel@tonic-gate clear_walk_args(void)
17140Sstevel@tonic-gate {
17150Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
17160Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
17170Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
17180Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
17190Sstevel@tonic-gate 	walk_arg.need_update = 0;
17200Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
17210Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
17220Sstevel@tonic-gate }
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate /*
17250Sstevel@tonic-gate  * Returns:
17260Sstevel@tonic-gate  *	0 - no update necessary
17270Sstevel@tonic-gate  *	1 - update required.
17280Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
17290Sstevel@tonic-gate  *
17300Sstevel@tonic-gate  * Special handling for check (-n):
17310Sstevel@tonic-gate  * ================================
17320Sstevel@tonic-gate  * The check (-n) option produces parseable output.
17330Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
17340Sstevel@tonic-gate  * to out of sync files.
17350Sstevel@tonic-gate  * All stderr messages are still printed though.
17360Sstevel@tonic-gate  *
17370Sstevel@tonic-gate  */
17380Sstevel@tonic-gate static int
17390Sstevel@tonic-gate update_required(char *root)
17400Sstevel@tonic-gate {
17410Sstevel@tonic-gate 	struct stat sb;
17420Sstevel@tonic-gate 	char path[PATH_MAX];
17430Sstevel@tonic-gate 	filelist_t flist;
17440Sstevel@tonic-gate 	filelist_t *flistp = &flist;
17450Sstevel@tonic-gate 	int need_update;
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 	walk_arg.need_update = 0;
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 	/*
17520Sstevel@tonic-gate 	 * Without consulting stat data, check if we need update
17530Sstevel@tonic-gate 	 */
17540Sstevel@tonic-gate 	check_flags_and_files(root);
17550Sstevel@tonic-gate 
17560Sstevel@tonic-gate 	/*
17570Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
17580Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
17590Sstevel@tonic-gate 	 */
17600Sstevel@tonic-gate 	if (bam_smf_check) {
17610Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
17620Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
17630Sstevel@tonic-gate 			return (0);
17640Sstevel@tonic-gate 	}
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate 	/*
17670Sstevel@tonic-gate 	 * consult stat data only if we haven't made a decision
17680Sstevel@tonic-gate 	 * about update. If checking (-n) however, we always
17690Sstevel@tonic-gate 	 * need stat data (since we want to compare old and new)
17700Sstevel@tonic-gate 	 */
17710Sstevel@tonic-gate 	if (!walk_arg.need_update || bam_check)
17720Sstevel@tonic-gate 		getoldstat(root);
17730Sstevel@tonic-gate 
17740Sstevel@tonic-gate 	/*
17752583Svikram 	 * Check if the archive contains files that are no longer
17762583Svikram 	 * present on the root filesystem.
17772583Svikram 	 */
17782583Svikram 	if (!walk_arg.need_update || bam_check)
17792583Svikram 		check4stale(root);
17802583Svikram 
17812583Svikram 	/*
17820Sstevel@tonic-gate 	 * read list of files
17830Sstevel@tonic-gate 	 */
17840Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
17850Sstevel@tonic-gate 		clear_walk_args();
17860Sstevel@tonic-gate 		return (BAM_ERROR);
17870Sstevel@tonic-gate 	}
17880Sstevel@tonic-gate 
17890Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate 	/*
17920Sstevel@tonic-gate 	 * At this point either the update is required
17930Sstevel@tonic-gate 	 * or the decision is pending. In either case
17940Sstevel@tonic-gate 	 * we need to create new stat nvlist
17950Sstevel@tonic-gate 	 */
17960Sstevel@tonic-gate 	create_newstat();
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate 	/*
17990Sstevel@tonic-gate 	 * This walk does 2 things:
18000Sstevel@tonic-gate 	 *  	- gets new stat data for every file
18010Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
18020Sstevel@tonic-gate 	 */
18030Sstevel@tonic-gate 	walk_list(root, &flist);
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate 	/* done with the file list */
18060Sstevel@tonic-gate 	filelist_free(flistp);
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate 	/*
18090Sstevel@tonic-gate 	 * if we didn't succeed in  creating new stat data above
18100Sstevel@tonic-gate 	 * just return result of update check so that archive is built.
18110Sstevel@tonic-gate 	 */
18120Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
18130Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
18140Sstevel@tonic-gate 		need_update = walk_arg.need_update;
18150Sstevel@tonic-gate 		clear_walk_args();
18160Sstevel@tonic-gate 		return (need_update ? 1 : 0);
18170Sstevel@tonic-gate 	}
18180Sstevel@tonic-gate 
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate 	/*
18210Sstevel@tonic-gate 	 * If no update required, discard newstat
18220Sstevel@tonic-gate 	 */
18230Sstevel@tonic-gate 	if (!walk_arg.need_update) {
18240Sstevel@tonic-gate 		clear_walk_args();
18250Sstevel@tonic-gate 		return (0);
18260Sstevel@tonic-gate 	}
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate 	/*
18290Sstevel@tonic-gate 	 * At this point we need an update - so save new stat data
18300Sstevel@tonic-gate 	 * However, if only checking (-n), don't save new stat data.
18310Sstevel@tonic-gate 	 */
18320Sstevel@tonic-gate 	if (!bam_check)
18330Sstevel@tonic-gate 		savenew(root);
18340Sstevel@tonic-gate 
18350Sstevel@tonic-gate 	clear_walk_args();
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate 	return (1);
18380Sstevel@tonic-gate }
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate static error_t
18410Sstevel@tonic-gate create_ramdisk(char *root)
18420Sstevel@tonic-gate {
18430Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
18440Sstevel@tonic-gate 	size_t len;
18450Sstevel@tonic-gate 	struct stat sb;
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	/*
18480Sstevel@tonic-gate 	 * Setup command args for create_ramdisk.ksh
18490Sstevel@tonic-gate 	 */
18500Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, CREATE_RAMDISK);
18510Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
18520Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
18530Sstevel@tonic-gate 		return (BAM_ERROR);
18540Sstevel@tonic-gate 	}
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
18570Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
18580Sstevel@tonic-gate 
18590Sstevel@tonic-gate 	if (strlen(root) > 1) {
18600Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
18610Sstevel@tonic-gate 		/* chop off / at the end */
18620Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
18630Sstevel@tonic-gate 	} else
18640Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
18650Sstevel@tonic-gate 
18660Sstevel@tonic-gate 	if (exec_cmd(cmdline, NULL, 0) != 0) {
18670Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
18680Sstevel@tonic-gate 		free(cmdline);
18690Sstevel@tonic-gate 		return (BAM_ERROR);
18700Sstevel@tonic-gate 	}
18710Sstevel@tonic-gate 	free(cmdline);
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 	/*
18740Sstevel@tonic-gate 	 * Verify that the archive has been created
18750Sstevel@tonic-gate 	 */
18763446Smrj 	(void) snprintf(path, sizeof (path), "%s%s", root,
18773446Smrj 	    DIRECT_BOOT_ARCHIVE_32);
18780Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
18790Sstevel@tonic-gate 		bam_error(ARCHIVE_NOT_CREATED, path);
18800Sstevel@tonic-gate 		return (BAM_ERROR);
18810Sstevel@tonic-gate 	}
18823446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
18833446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", root,
18843446Smrj 		    DIRECT_BOOT_ARCHIVE_64);
18853446Smrj 		if (stat(path, &sb) != 0) {
18863446Smrj 			bam_error(ARCHIVE_NOT_CREATED, path);
18873446Smrj 			return (BAM_ERROR);
18883446Smrj 		}
18893446Smrj 	}
18900Sstevel@tonic-gate 
18910Sstevel@tonic-gate 	return (BAM_SUCCESS);
18920Sstevel@tonic-gate }
18930Sstevel@tonic-gate 
18940Sstevel@tonic-gate /*
18950Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
18960Sstevel@tonic-gate  * 1 - is miniroot
18970Sstevel@tonic-gate  * 0 - is not
18980Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
18990Sstevel@tonic-gate  */
19000Sstevel@tonic-gate static int
19010Sstevel@tonic-gate is_ramdisk(char *root)
19020Sstevel@tonic-gate {
19030Sstevel@tonic-gate 	struct extmnttab mnt;
19040Sstevel@tonic-gate 	FILE *fp;
19050Sstevel@tonic-gate 	int found;
1906316Svikram 	char mntpt[PATH_MAX];
1907316Svikram 	char *cp;
19080Sstevel@tonic-gate 
19090Sstevel@tonic-gate 	/*
19100Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
19110Sstevel@tonic-gate 	 * of dubious value:
1912316Svikram 	 *	- create boot_archive on a lofi-mounted boot_archive
19130Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
19140Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
19150Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
19160Sstevel@tonic-gate 	 * worth it.
19170Sstevel@tonic-gate 	 * The other two conditions are handled here
19180Sstevel@tonic-gate 	 */
19190Sstevel@tonic-gate 
19200Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
19210Sstevel@tonic-gate 	if (fp == NULL) {
19220Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
19230Sstevel@tonic-gate 		return (0);
19240Sstevel@tonic-gate 	}
19250Sstevel@tonic-gate 
19260Sstevel@tonic-gate 	resetmnttab(fp);
19270Sstevel@tonic-gate 
1928316Svikram 	/*
1929316Svikram 	 * Remove any trailing / from the mount point
1930316Svikram 	 */
1931316Svikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
1932316Svikram 	if (strcmp(root, "/") != 0) {
1933316Svikram 		cp = mntpt + strlen(mntpt) - 1;
1934316Svikram 		if (*cp == '/')
1935316Svikram 			*cp = '\0';
1936316Svikram 	}
19370Sstevel@tonic-gate 	found = 0;
19380Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1939316Svikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
19400Sstevel@tonic-gate 			found = 1;
19410Sstevel@tonic-gate 			break;
19420Sstevel@tonic-gate 		}
19430Sstevel@tonic-gate 	}
19440Sstevel@tonic-gate 
19450Sstevel@tonic-gate 	if (!found) {
19460Sstevel@tonic-gate 		if (bam_verbose)
1947316Svikram 			bam_error(NOT_IN_MNTTAB, mntpt);
19480Sstevel@tonic-gate 		(void) fclose(fp);
19490Sstevel@tonic-gate 		return (0);
19500Sstevel@tonic-gate 	}
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
19530Sstevel@tonic-gate 		if (bam_verbose)
19540Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
19550Sstevel@tonic-gate 		(void) fclose(fp);
19560Sstevel@tonic-gate 		return (1);
19570Sstevel@tonic-gate 	}
19580Sstevel@tonic-gate 
19590Sstevel@tonic-gate 	(void) fclose(fp);
19600Sstevel@tonic-gate 
19610Sstevel@tonic-gate 	return (0);
19620Sstevel@tonic-gate }
19630Sstevel@tonic-gate 
19640Sstevel@tonic-gate static int
19650Sstevel@tonic-gate is_newboot(char *root)
19660Sstevel@tonic-gate {
19670Sstevel@tonic-gate 	char path[PATH_MAX];
19680Sstevel@tonic-gate 	struct stat sb;
19690Sstevel@tonic-gate 
19700Sstevel@tonic-gate 	/*
19710Sstevel@tonic-gate 	 * We can't boot without MULTI_BOOT
19720Sstevel@tonic-gate 	 */
19730Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, MULTI_BOOT);
19740Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
19750Sstevel@tonic-gate 		if (bam_verbose)
19760Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
19770Sstevel@tonic-gate 		return (0);
19780Sstevel@tonic-gate 	}
19790Sstevel@tonic-gate 
19800Sstevel@tonic-gate 	/*
19810Sstevel@tonic-gate 	 * We can't generate archive without GRUB_DIR
19820Sstevel@tonic-gate 	 */
19830Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR);
19840Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
19850Sstevel@tonic-gate 		if (bam_verbose)
19860Sstevel@tonic-gate 			bam_print(DIR_MISS, path);
19870Sstevel@tonic-gate 		return (0);
19880Sstevel@tonic-gate 	}
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate 	return (1);
19910Sstevel@tonic-gate }
19920Sstevel@tonic-gate 
19930Sstevel@tonic-gate static int
19940Sstevel@tonic-gate is_readonly(char *root)
19950Sstevel@tonic-gate {
19960Sstevel@tonic-gate 	struct statvfs vfs;
19970Sstevel@tonic-gate 
19980Sstevel@tonic-gate 	/*
19990Sstevel@tonic-gate 	 * Check for RDONLY filesystem
20000Sstevel@tonic-gate 	 * When in doubt assume it is not readonly
20010Sstevel@tonic-gate 	 */
20020Sstevel@tonic-gate 	if (statvfs(root, &vfs) != 0) {
20030Sstevel@tonic-gate 		if (bam_verbose)
20040Sstevel@tonic-gate 			bam_error(STATVFS_FAIL, root, strerror(errno));
20050Sstevel@tonic-gate 		return (0);
20060Sstevel@tonic-gate 	}
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate 	if (vfs.f_flag & ST_RDONLY) {
20090Sstevel@tonic-gate 		return (1);
20100Sstevel@tonic-gate 	}
20110Sstevel@tonic-gate 
20120Sstevel@tonic-gate 	return (0);
20130Sstevel@tonic-gate }
20140Sstevel@tonic-gate 
20150Sstevel@tonic-gate static error_t
20160Sstevel@tonic-gate update_archive(char *root, char *opt)
20170Sstevel@tonic-gate {
20180Sstevel@tonic-gate 	error_t ret;
20190Sstevel@tonic-gate 
20200Sstevel@tonic-gate 	assert(root);
20210Sstevel@tonic-gate 	assert(opt == NULL);
20220Sstevel@tonic-gate 
20230Sstevel@tonic-gate 	/*
2024316Svikram 	 * root must belong to a GRUB boot OS,
20250Sstevel@tonic-gate 	 * don't care on sparc except for diskless clients
20260Sstevel@tonic-gate 	 */
20270Sstevel@tonic-gate 	if (!is_newboot(root)) {
2028316Svikram 		/*
2029316Svikram 		 * Emit message only if not in context of update_all.
2030316Svikram 		 * If in update_all, emit only if verbose flag is set.
2031316Svikram 		 */
2032316Svikram 		if (!bam_update_all || bam_verbose)
2033316Svikram 			bam_print(NOT_GRUB_BOOT, root);
20340Sstevel@tonic-gate 		return (BAM_SUCCESS);
20350Sstevel@tonic-gate 	}
20360Sstevel@tonic-gate 
20370Sstevel@tonic-gate 	/*
2038662Sszhou 	 * If smf check is requested when / is writable (can happen
2039662Sszhou 	 * on first reboot following an upgrade because service
2040662Sszhou 	 * dependency is messed up), skip the check.
2041662Sszhou 	 */
2042662Sszhou 	if (bam_smf_check && !bam_root_readonly)
2043662Sszhou 		return (BAM_SUCCESS);
2044662Sszhou 
2045662Sszhou 	/*
2046662Sszhou 	 * root must be writable. This check applies to alternate
2047662Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
20480Sstevel@tonic-gate 	 * Note: statvfs() does not always report the truth
20490Sstevel@tonic-gate 	 */
2050756Ssetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
2051662Sszhou 		if (bam_verbose)
20520Sstevel@tonic-gate 			bam_print(RDONLY_FS, root);
20530Sstevel@tonic-gate 		return (BAM_SUCCESS);
20540Sstevel@tonic-gate 	}
20550Sstevel@tonic-gate 
20560Sstevel@tonic-gate 	/*
20570Sstevel@tonic-gate 	 * Don't generate archive on ramdisk
20580Sstevel@tonic-gate 	 */
20590Sstevel@tonic-gate 	if (is_ramdisk(root)) {
20600Sstevel@tonic-gate 		if (bam_verbose)
20610Sstevel@tonic-gate 			bam_print(SKIP_RAMDISK);
20620Sstevel@tonic-gate 		return (BAM_SUCCESS);
20630Sstevel@tonic-gate 	}
20640Sstevel@tonic-gate 
20650Sstevel@tonic-gate 	/*
20660Sstevel@tonic-gate 	 * Now check if updated is really needed
20670Sstevel@tonic-gate 	 */
20680Sstevel@tonic-gate 	ret = update_required(root);
20690Sstevel@tonic-gate 
20700Sstevel@tonic-gate 	/*
20710Sstevel@tonic-gate 	 * The check command (-n) is *not* a dry run
20720Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
20730Sstevel@tonic-gate 	 */
20740Sstevel@tonic-gate 	if (bam_check) {
20750Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
20760Sstevel@tonic-gate 	}
20770Sstevel@tonic-gate 
20780Sstevel@tonic-gate 	if (ret == 1) {
20790Sstevel@tonic-gate 		/* create the ramdisk */
20800Sstevel@tonic-gate 		ret = create_ramdisk(root);
20810Sstevel@tonic-gate 	}
20820Sstevel@tonic-gate 	return (ret);
20830Sstevel@tonic-gate }
20840Sstevel@tonic-gate 
2085316Svikram static void
20861746Svikram update_fdisk(void)
20871746Svikram {
20881746Svikram 	struct stat sb;
20891746Svikram 	char cmd[PATH_MAX];
20901746Svikram 	int ret1, ret2;
20911746Svikram 
20921746Svikram 	assert(stat(GRUB_fdisk, &sb) == 0);
20931746Svikram 	assert(stat(GRUB_fdisk_target, &sb) == 0);
20941746Svikram 
20951746Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`",
20961746Svikram 	    GRUB_fdisk, GRUB_fdisk_target);
20971746Svikram 
20981746Svikram 	bam_print(UPDATING_FDISK);
20991746Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
21001746Svikram 		bam_error(FDISK_UPDATE_FAILED);
21011746Svikram 	}
21021746Svikram 
21031746Svikram 	/*
21041746Svikram 	 * We are done, remove the files.
21051746Svikram 	 */
21061746Svikram 	ret1 = unlink(GRUB_fdisk);
21071746Svikram 	ret2 = unlink(GRUB_fdisk_target);
21081746Svikram 	if (ret1 != 0 || ret2 != 0) {
21091746Svikram 		bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target);
21101746Svikram 	}
21111746Svikram }
21121746Svikram 
21131746Svikram static void
2114316Svikram restore_grub_slice(void)
2115316Svikram {
2116316Svikram 	struct stat sb;
2117316Svikram 	char *mntpt, *physlice;
2118316Svikram 	int mnted;	/* set if we did a mount */
2119316Svikram 	char menupath[PATH_MAX], cmd[PATH_MAX];
2120316Svikram 
2121316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
2122316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
2123316Svikram 		return;
2124316Svikram 	}
2125316Svikram 
2126316Svikram 	/*
2127316Svikram 	 * If we are doing an luactivate, don't attempt to restore GRUB or else
2128316Svikram 	 * we may not be able to get to DCA boot environments. Let luactivate
2129316Svikram 	 * handle GRUB/DCA installation
2130316Svikram 	 */
2131316Svikram 	if (stat(LU_ACTIVATE_FILE, &sb) == 0) {
2132316Svikram 		return;
2133316Svikram 	}
2134316Svikram 
2135316Svikram 	mnted = 0;
2136316Svikram 	physlice = NULL;
2137621Svikram 	mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL);
2138316Svikram 	if (mntpt == NULL) {
2139316Svikram 		bam_error(CANNOT_RESTORE_GRUB_SLICE);
2140316Svikram 		return;
2141316Svikram 	}
2142316Svikram 
2143316Svikram 	(void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU);
2144316Svikram 	if (stat(menupath, &sb) == 0) {
2145621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2146316Svikram 		return;
2147316Svikram 	}
2148316Svikram 
2149316Svikram 	/*
2150316Svikram 	 * The menu is missing - we need to do a restore
2151316Svikram 	 */
2152316Svikram 	bam_print(RESTORING_GRUB);
2153316Svikram 
2154316Svikram 	(void) snprintf(cmd, sizeof (cmd), "%s %s %s %s",
2155316Svikram 	    INSTALLGRUB, STAGE1, STAGE2, physlice);
2156316Svikram 
2157316Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
2158316Svikram 		bam_error(RESTORE_GRUB_FAILED);
2159621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2160316Svikram 		return;
2161316Svikram 	}
2162316Svikram 
2163316Svikram 	if (stat(GRUB_backup_menu, &sb) != 0) {
2164316Svikram 		bam_error(MISSING_BACKUP_MENU,
2165316Svikram 		    GRUB_backup_menu, strerror(errno));
2166621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2167316Svikram 		return;
2168316Svikram 	}
2169316Svikram 
2170316Svikram 	(void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s",
2171316Svikram 	    GRUB_backup_menu, menupath);
2172316Svikram 
2173316Svikram 	if (exec_cmd(cmd, NULL, 0) != 0) {
2174316Svikram 		bam_error(RESTORE_MENU_FAILED, menupath);
2175621Svikram 		umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2176316Svikram 		return;
2177316Svikram 	}
2178316Svikram 
2179316Svikram 	/* Success */
2180621Svikram 	umount_grub_slice(mnted, mntpt, physlice, NULL, NULL);
2181316Svikram }
2182316Svikram 
21830Sstevel@tonic-gate static error_t
21840Sstevel@tonic-gate update_all(char *root, char *opt)
21850Sstevel@tonic-gate {
21860Sstevel@tonic-gate 	struct extmnttab mnt;
21870Sstevel@tonic-gate 	struct stat sb;
21880Sstevel@tonic-gate 	FILE *fp;
21890Sstevel@tonic-gate 	char multibt[PATH_MAX];
21900Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
21911746Svikram 	int ret1, ret2;
21920Sstevel@tonic-gate 
2193621Svikram 	assert(root);
21940Sstevel@tonic-gate 	assert(opt == NULL);
21950Sstevel@tonic-gate 
2196621Svikram 	if (bam_rootlen != 1 || *root != '/') {
2197621Svikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
2198621Svikram 		bam_error(ALT_ROOT_INVALID, multibt);
2199621Svikram 		return (BAM_ERROR);
2200621Svikram 	}
2201621Svikram 
22020Sstevel@tonic-gate 	/*
22030Sstevel@tonic-gate 	 * First update archive for current root
22040Sstevel@tonic-gate 	 */
22050Sstevel@tonic-gate 	if (update_archive(root, opt) != BAM_SUCCESS)
22060Sstevel@tonic-gate 		ret = BAM_ERROR;
22070Sstevel@tonic-gate 
22080Sstevel@tonic-gate 	/*
22090Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
22100Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
22110Sstevel@tonic-gate 	 */
22120Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
22130Sstevel@tonic-gate 	if (fp == NULL) {
22140Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2215316Svikram 		ret = BAM_ERROR;
2216316Svikram 		goto out;
22170Sstevel@tonic-gate 	}
22180Sstevel@tonic-gate 
22190Sstevel@tonic-gate 	resetmnttab(fp);
22200Sstevel@tonic-gate 
22210Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
22220Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
22230Sstevel@tonic-gate 			continue;
22240Sstevel@tonic-gate 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
22250Sstevel@tonic-gate 			continue;
22260Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
22270Sstevel@tonic-gate 			continue;
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 		(void) snprintf(multibt, sizeof (multibt), "%s%s",
22300Sstevel@tonic-gate 		    mnt.mnt_mountp, MULTI_BOOT);
22310Sstevel@tonic-gate 
22320Sstevel@tonic-gate 		if (stat(multibt, &sb) == -1)
22330Sstevel@tonic-gate 			continue;
22340Sstevel@tonic-gate 
22350Sstevel@tonic-gate 		/*
22360Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
22370Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
22380Sstevel@tonic-gate 		 */
22390Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
22400Sstevel@tonic-gate 		    mnt.mnt_mountp);
22410Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
22423446Smrj 
22433446Smrj 		/*
22443446Smrj 		 * It's possible that other mounts may be an alternate boot
22453446Smrj 		 * architecture, so check it again.
22463446Smrj 		 */
22473446Smrj 		if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) ||
22483446Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
22490Sstevel@tonic-gate 			ret = BAM_ERROR;
22500Sstevel@tonic-gate 	}
22510Sstevel@tonic-gate 
22520Sstevel@tonic-gate 	(void) fclose(fp);
22530Sstevel@tonic-gate 
2254316Svikram out:
2255316Svikram 	if (stat(GRUB_slice, &sb) == 0) {
2256316Svikram 		restore_grub_slice();
2257316Svikram 	}
2258316Svikram 
22591746Svikram 	/*
22601746Svikram 	 * Update fdisk table as we go down. Updating it when
22611746Svikram 	 * the system is running will confuse biosdev.
22621746Svikram 	 */
22631746Svikram 	ret1 = stat(GRUB_fdisk, &sb);
22641746Svikram 	ret2 = stat(GRUB_fdisk_target, &sb);
22651746Svikram 	if ((ret1 == 0) && (ret2 == 0)) {
22661746Svikram 		update_fdisk();
22671746Svikram 	} else if ((ret1 == 0) ^ (ret2 == 0)) {
22681746Svikram 		/*
22691746Svikram 		 * It is an error for one file to be
22701746Svikram 		 * present and the other absent.
22711746Svikram 		 * It is normal for both files to be
22721746Svikram 		 * absent - it indicates that no fdisk
22731746Svikram 		 * update is required.
22741746Svikram 		 */
22751746Svikram 		bam_error(MISSING_FDISK_FILE,
22761746Svikram 		    ret1 ? GRUB_fdisk : GRUB_fdisk_target);
22771746Svikram 		ret = BAM_ERROR;
22781746Svikram 	}
22791746Svikram 
22800Sstevel@tonic-gate 	return (ret);
22810Sstevel@tonic-gate }
22820Sstevel@tonic-gate 
22830Sstevel@tonic-gate static void
22840Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
22850Sstevel@tonic-gate {
22860Sstevel@tonic-gate 	if (mp->start == NULL) {
22870Sstevel@tonic-gate 		mp->start = lp;
22880Sstevel@tonic-gate 	} else {
22890Sstevel@tonic-gate 		mp->end->next = lp;
2290662Sszhou 		lp->prev = mp->end;
22910Sstevel@tonic-gate 	}
22920Sstevel@tonic-gate 	mp->end = lp;
22930Sstevel@tonic-gate }
22940Sstevel@tonic-gate 
2295662Sszhou static void
2296662Sszhou unlink_line(menu_t *mp, line_t *lp)
2297662Sszhou {
2298662Sszhou 	/* unlink from list */
2299662Sszhou 	if (lp->prev)
2300662Sszhou 		lp->prev->next = lp->next;
2301662Sszhou 	else
2302662Sszhou 		mp->start = lp->next;
2303662Sszhou 	if (lp->next)
2304662Sszhou 		lp->next->prev = lp->prev;
2305662Sszhou 	else
2306662Sszhou 		mp->end = lp->prev;
2307662Sszhou }
2308662Sszhou 
2309662Sszhou static entry_t *
2310662Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
2311662Sszhou {
2312662Sszhou 	entry_t *ent, *prev;
2313662Sszhou 
2314662Sszhou 	ent = s_calloc(1, sizeof (entry_t));
2315662Sszhou 	ent->start = start;
2316662Sszhou 	ent->end = end;
2317662Sszhou 
2318662Sszhou 	if (mp->entries == NULL) {
2319662Sszhou 		mp->entries = ent;
2320662Sszhou 		return (ent);
2321662Sszhou 	}
2322662Sszhou 
2323662Sszhou 	prev = mp->entries;
2324662Sszhou 	while (prev->next)
2325662Sszhou 		prev = prev-> next;
2326662Sszhou 	prev->next = ent;
2327662Sszhou 	ent->prev = prev;
2328662Sszhou 	return (ent);
2329662Sszhou }
2330662Sszhou 
2331662Sszhou static void
2332662Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
2333662Sszhou {
2334662Sszhou 	if (ent)
2335662Sszhou 		ent->end = lp;
2336662Sszhou }
2337662Sszhou 
23380Sstevel@tonic-gate /*
23390Sstevel@tonic-gate  * A line in menu.lst looks like
23400Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
23410Sstevel@tonic-gate  */
23420Sstevel@tonic-gate static void
23430Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
23440Sstevel@tonic-gate {
23450Sstevel@tonic-gate 	/*
23460Sstevel@tonic-gate 	 * save state across calls. This is so that
23470Sstevel@tonic-gate 	 * header gets the right entry# after title has
23480Sstevel@tonic-gate 	 * been processed
23490Sstevel@tonic-gate 	 */
2350662Sszhou 	static line_t *prev = NULL;
2351662Sszhou 	static entry_t *curr_ent = NULL;
23523446Smrj 	static int in_liveupgrade = 0;
23530Sstevel@tonic-gate 
23540Sstevel@tonic-gate 	line_t	*lp;
23550Sstevel@tonic-gate 	char *cmd, *sep, *arg;
23560Sstevel@tonic-gate 	char save, *cp, *line;
23570Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
23580Sstevel@tonic-gate 
23590Sstevel@tonic-gate 	if (str == NULL) {
23600Sstevel@tonic-gate 		return;
23610Sstevel@tonic-gate 	}
23620Sstevel@tonic-gate 
23630Sstevel@tonic-gate 	/*
23640Sstevel@tonic-gate 	 * First save a copy of the entire line.
23650Sstevel@tonic-gate 	 * We use this later to set the line field.
23660Sstevel@tonic-gate 	 */
23670Sstevel@tonic-gate 	line = s_strdup(str);
23680Sstevel@tonic-gate 
23690Sstevel@tonic-gate 	/* Eat up leading whitespace */
23700Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
23710Sstevel@tonic-gate 		str++;
23720Sstevel@tonic-gate 
23730Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
23740Sstevel@tonic-gate 		cmd = s_strdup("#");
23750Sstevel@tonic-gate 		sep = NULL;
23760Sstevel@tonic-gate 		arg = s_strdup(str + 1);
23770Sstevel@tonic-gate 		flag = BAM_COMMENT;
23783446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
23793446Smrj 			in_liveupgrade = 1;
23803446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
23813446Smrj 			in_liveupgrade = 0;
23823446Smrj 		}
23830Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
23840Sstevel@tonic-gate 		cmd = sep = arg = NULL;
23850Sstevel@tonic-gate 		flag = BAM_EMPTY;
23860Sstevel@tonic-gate 	} else {
23870Sstevel@tonic-gate 		/*
23880Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
23890Sstevel@tonic-gate 		 * However various development bits use '=' as a
23900Sstevel@tonic-gate 		 * separator. In addition, external users also
23910Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
23920Sstevel@tonic-gate 		 */
23930Sstevel@tonic-gate 		cp = str;
23940Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
23950Sstevel@tonic-gate 			if (*str == '\0') {
23960Sstevel@tonic-gate 				cmd = s_strdup(cp);
23970Sstevel@tonic-gate 				sep = arg = NULL;
23980Sstevel@tonic-gate 				break;
23990Sstevel@tonic-gate 			}
24000Sstevel@tonic-gate 			str++;
24010Sstevel@tonic-gate 		}
24020Sstevel@tonic-gate 
24030Sstevel@tonic-gate 		if (*str != '\0') {
24040Sstevel@tonic-gate 			save = *str;
24050Sstevel@tonic-gate 			*str = '\0';
24060Sstevel@tonic-gate 			cmd = s_strdup(cp);
24070Sstevel@tonic-gate 			*str = save;
24080Sstevel@tonic-gate 
24090Sstevel@tonic-gate 			str++;
24100Sstevel@tonic-gate 			save = *str;
24110Sstevel@tonic-gate 			*str = '\0';
24120Sstevel@tonic-gate 			sep = s_strdup(str - 1);
24130Sstevel@tonic-gate 			*str = save;
24140Sstevel@tonic-gate 
24150Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
24160Sstevel@tonic-gate 				str++;
24170Sstevel@tonic-gate 			if (*str == '\0')
24180Sstevel@tonic-gate 				arg = NULL;
24190Sstevel@tonic-gate 			else
24200Sstevel@tonic-gate 				arg = s_strdup(str);
24210Sstevel@tonic-gate 		}
24220Sstevel@tonic-gate 	}
24230Sstevel@tonic-gate 
24240Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
24250Sstevel@tonic-gate 
24260Sstevel@tonic-gate 	lp->cmd = cmd;
24270Sstevel@tonic-gate 	lp->sep = sep;
24280Sstevel@tonic-gate 	lp->arg = arg;
24290Sstevel@tonic-gate 	lp->line = line;
24300Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
24310Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
24320Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
24330Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
24340Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
24353446Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
24360Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
2437662Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
24383446Smrj 			curr_ent->flags = BAM_ENTRY_BOOTADM;
2439662Sszhou 		} else {
2440662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
24413446Smrj 			if (in_liveupgrade) {
24423446Smrj 				curr_ent->flags = BAM_ENTRY_LU;
24433446Smrj 			}
2444662Sszhou 		}
24453446Smrj 		curr_ent->entryNum = *entryNum;
24460Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
24470Sstevel@tonic-gate 		/*
24480Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
24490Sstevel@tonic-gate 		 * by the subsequent title
24500Sstevel@tonic-gate 		 */
24510Sstevel@tonic-gate 		lp->entryNum = *entryNum;
24520Sstevel@tonic-gate 		lp->flags = flag;
24530Sstevel@tonic-gate 	} else {
24540Sstevel@tonic-gate 		lp->entryNum = *entryNum;
24553446Smrj 
24563446Smrj 		if (*entryNum == ENTRY_INIT) {
24573446Smrj 			lp->flags = BAM_GLOBAL;
24583446Smrj 		} else {
24593446Smrj 			lp->flags = BAM_ENTRY;
24603446Smrj 
24613446Smrj 			if (cmd && arg) {
24623446Smrj 				/*
24633446Smrj 				 * We only compare for the length of "module"
24643446Smrj 				 * so that "module$" will also match.
24653446Smrj 				 */
24663446Smrj 				if ((strncmp(cmd, menu_cmds[MODULE_CMD],
24673446Smrj 				    strlen(menu_cmds[MODULE_CMD])) == 0) &&
24683446Smrj 				    (strcmp(arg, MINIROOT) == 0))
24693446Smrj 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
24703446Smrj 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
24713446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
24723446Smrj 				else if (strcmp(cmd,
24733446Smrj 				    menu_cmds[CHAINLOADER_CMD]) == 0)
24743446Smrj 					curr_ent->flags |=
24753446Smrj 					    BAM_ENTRY_CHAINLOADER;
24763446Smrj 			}
24773446Smrj 		}
24780Sstevel@tonic-gate 	}
24790Sstevel@tonic-gate 
2480662Sszhou 	/* record default, old default, and entry line ranges */
2481662Sszhou 	if (lp->flags == BAM_GLOBAL &&
2482662Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
2483662Sszhou 		mp->curdefault = lp;
2484662Sszhou 	} else if (lp->flags == BAM_COMMENT &&
2485662Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
2486662Sszhou 		mp->olddefault = lp;
24873446Smrj 	} else if (lp->flags == BAM_COMMENT &&
24883446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
24893446Smrj 		mp->old_rc_default = lp;
2490662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
24913446Smrj 	    (lp->flags == BAM_COMMENT &&
24923446Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
2493662Sszhou 		boot_entry_addline(curr_ent, lp);
2494662Sszhou 	}
24950Sstevel@tonic-gate 	append_line(mp, lp);
24960Sstevel@tonic-gate 
24970Sstevel@tonic-gate 	prev = lp;
24980Sstevel@tonic-gate }
24990Sstevel@tonic-gate 
2500621Svikram static void
2501621Svikram update_numbering(menu_t *mp)
2502621Svikram {
2503621Svikram 	int lineNum;
2504621Svikram 	int entryNum;
2505621Svikram 	int old_default_value;
2506621Svikram 	line_t *lp, *prev, *default_lp, *default_entry;
2507621Svikram 	char buf[PATH_MAX];
2508621Svikram 
2509621Svikram 	if (mp->start == NULL) {
2510621Svikram 		return;
2511621Svikram 	}
2512621Svikram 
2513621Svikram 	lineNum = LINE_INIT;
2514621Svikram 	entryNum = ENTRY_INIT;
2515621Svikram 	old_default_value = ENTRY_INIT;
2516621Svikram 	lp = default_lp = default_entry = NULL;
2517621Svikram 
2518621Svikram 	prev = NULL;
2519621Svikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
2520621Svikram 		lp->lineNum = ++lineNum;
2521621Svikram 
2522621Svikram 		/*
2523621Svikram 		 * Get the value of the default command
2524621Svikram 		 */
2525621Svikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
2526621Svikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
2527621Svikram 		    lp->arg) {
2528621Svikram 			old_default_value = atoi(lp->arg);
2529621Svikram 			default_lp = lp;
2530621Svikram 		}
2531621Svikram 
2532621Svikram 		/*
2533621Svikram 		 * If not boot entry, nothing else to fix for this
2534621Svikram 		 * entry
2535621Svikram 		 */
2536621Svikram 		if (lp->entryNum == ENTRY_INIT)
2537621Svikram 			continue;
2538621Svikram 
2539621Svikram 		/*
2540621Svikram 		 * Record the position of the default entry.
2541621Svikram 		 * The following works because global
2542621Svikram 		 * commands like default and timeout should precede
2543621Svikram 		 * actual boot entries, so old_default_value
2544621Svikram 		 * is already known (or default cmd is missing).
2545621Svikram 		 */
2546621Svikram 		if (default_entry == NULL &&
2547621Svikram 		    old_default_value != ENTRY_INIT &&
2548621Svikram 		    lp->entryNum == old_default_value) {
2549621Svikram 			default_entry = lp;
2550621Svikram 		}
2551621Svikram 
2552621Svikram 		/*
2553621Svikram 		 * Now fixup the entry number
2554621Svikram 		 */
2555621Svikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
2556621Svikram 			lp->entryNum = ++entryNum;
2557621Svikram 			/* fixup the bootadm header */
2558621Svikram 			if (prev && prev->flags == BAM_COMMENT &&
25593446Smrj 			    prev->arg &&
25603446Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2561621Svikram 				prev->entryNum = lp->entryNum;
2562621Svikram 			}
2563621Svikram 		} else {
2564621Svikram 			lp->entryNum = entryNum;
2565621Svikram 		}
2566621Svikram 	}
2567621Svikram 
2568621Svikram 	/*
2569621Svikram 	 * No default command in menu, simply return
2570621Svikram 	 */
2571621Svikram 	if (default_lp == NULL) {
2572621Svikram 		return;
2573621Svikram 	}
2574621Svikram 
2575621Svikram 	free(default_lp->arg);
2576621Svikram 	free(default_lp->line);
2577621Svikram 
2578621Svikram 	if (default_entry == NULL) {
2579621Svikram 		default_lp->arg = s_strdup("0");
2580621Svikram 	} else {
2581621Svikram 		(void) snprintf(buf, sizeof (buf), "%d",
2582621Svikram 		    default_entry->entryNum);
2583621Svikram 		default_lp->arg = s_strdup(buf);
2584621Svikram 	}
2585621Svikram 
2586621Svikram 	/*
2587621Svikram 	 * The following is required since only the line field gets
2588621Svikram 	 * written back to menu.lst
2589621Svikram 	 */
2590621Svikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
2591621Svikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
2592621Svikram 	default_lp->line = s_strdup(buf);
2593621Svikram }
2594621Svikram 
2595621Svikram 
25960Sstevel@tonic-gate static menu_t *
25970Sstevel@tonic-gate menu_read(char *menu_path)
25980Sstevel@tonic-gate {
25990Sstevel@tonic-gate 	FILE *fp;
26000Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
26010Sstevel@tonic-gate 	menu_t *mp;
26020Sstevel@tonic-gate 	int line, entry, len, n;
26030Sstevel@tonic-gate 
26040Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
26050Sstevel@tonic-gate 
26060Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
26070Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
26080Sstevel@tonic-gate 		return (mp);
26090Sstevel@tonic-gate 	}
26100Sstevel@tonic-gate 
26110Sstevel@tonic-gate 
26120Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
26130Sstevel@tonic-gate 	line = LINE_INIT;
26140Sstevel@tonic-gate 	entry = ENTRY_INIT;
26150Sstevel@tonic-gate 	cp = buf;
26160Sstevel@tonic-gate 	len = sizeof (buf);
26170Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
26180Sstevel@tonic-gate 		n = strlen(cp);
26190Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
26200Sstevel@tonic-gate 			len -= n - 1;
26210Sstevel@tonic-gate 			assert(len >= 2);
26220Sstevel@tonic-gate 			cp += n - 1;
26230Sstevel@tonic-gate 			continue;
26240Sstevel@tonic-gate 		}
26250Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
26260Sstevel@tonic-gate 		cp = buf;
26270Sstevel@tonic-gate 		len = sizeof (buf);
26280Sstevel@tonic-gate 	}
26290Sstevel@tonic-gate 
26300Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
26310Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
26320Sstevel@tonic-gate 	}
26330Sstevel@tonic-gate 
26340Sstevel@tonic-gate 	return (mp);
26350Sstevel@tonic-gate }
26360Sstevel@tonic-gate 
26370Sstevel@tonic-gate static error_t
26380Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
26390Sstevel@tonic-gate {
26400Sstevel@tonic-gate 	char *eq;
26410Sstevel@tonic-gate 	char *opt_dup;
26420Sstevel@tonic-gate 	int entryNum;
26430Sstevel@tonic-gate 
26440Sstevel@tonic-gate 	assert(mp);
26450Sstevel@tonic-gate 	assert(mp->start);
26460Sstevel@tonic-gate 	assert(opt);
26470Sstevel@tonic-gate 
26480Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
26490Sstevel@tonic-gate 
26500Sstevel@tonic-gate 	if (entry)
26510Sstevel@tonic-gate 		*entry = ENTRY_INIT;
26520Sstevel@tonic-gate 	if (title)
26530Sstevel@tonic-gate 		*title = NULL;
26540Sstevel@tonic-gate 
26550Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
26560Sstevel@tonic-gate 	if (eq == NULL) {
26570Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
26580Sstevel@tonic-gate 		free(opt_dup);
26590Sstevel@tonic-gate 		return (BAM_ERROR);
26600Sstevel@tonic-gate 	}
26610Sstevel@tonic-gate 
26620Sstevel@tonic-gate 	*eq = '\0';
26630Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
26640Sstevel@tonic-gate 		assert(mp->end);
26650Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
26660Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
26670Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
26680Sstevel@tonic-gate 			free(opt_dup);
26690Sstevel@tonic-gate 			return (BAM_ERROR);
26700Sstevel@tonic-gate 		}
26710Sstevel@tonic-gate 		*entry = entryNum;
26720Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
26730Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
26740Sstevel@tonic-gate 	} else {
26750Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
26760Sstevel@tonic-gate 		free(opt_dup);
26770Sstevel@tonic-gate 		return (BAM_ERROR);
26780Sstevel@tonic-gate 	}
26790Sstevel@tonic-gate 
26800Sstevel@tonic-gate 	free(opt_dup);
26810Sstevel@tonic-gate 	return (BAM_SUCCESS);
26820Sstevel@tonic-gate }
26830Sstevel@tonic-gate 
26840Sstevel@tonic-gate /*
26850Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
26860Sstevel@tonic-gate  * only title lines in file are printed.
26870Sstevel@tonic-gate  *
26880Sstevel@tonic-gate  * If invoked with a title or entry #, all
26890Sstevel@tonic-gate  * lines in *every* matching entry are listed
26900Sstevel@tonic-gate  */
26910Sstevel@tonic-gate static error_t
26920Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
26930Sstevel@tonic-gate {
26940Sstevel@tonic-gate 	line_t *lp;
26950Sstevel@tonic-gate 	int entry = ENTRY_INIT;
26960Sstevel@tonic-gate 	int found;
26970Sstevel@tonic-gate 	char *title = NULL;
26980Sstevel@tonic-gate 
26990Sstevel@tonic-gate 	assert(mp);
27000Sstevel@tonic-gate 	assert(menu_path);
27010Sstevel@tonic-gate 
27020Sstevel@tonic-gate 	if (mp->start == NULL) {
27030Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
27040Sstevel@tonic-gate 		return (BAM_ERROR);
27050Sstevel@tonic-gate 	}
27060Sstevel@tonic-gate 
27070Sstevel@tonic-gate 	if (opt != NULL) {
27080Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
27090Sstevel@tonic-gate 			return (BAM_ERROR);
27100Sstevel@tonic-gate 		}
27110Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
27120Sstevel@tonic-gate 	} else {
27130Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
27140Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
27150Sstevel@tonic-gate 	}
27160Sstevel@tonic-gate 
27170Sstevel@tonic-gate 	found = 0;
27180Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
27190Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
27200Sstevel@tonic-gate 			continue;
27210Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
27220Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
27230Sstevel@tonic-gate 			    lp->arg);
27240Sstevel@tonic-gate 			found = 1;
27250Sstevel@tonic-gate 			continue;
27260Sstevel@tonic-gate 		}
27270Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
27280Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
27290Sstevel@tonic-gate 			found = 1;
27300Sstevel@tonic-gate 			continue;
27310Sstevel@tonic-gate 		}
27320Sstevel@tonic-gate 
27330Sstevel@tonic-gate 		/*
27340Sstevel@tonic-gate 		 * We set the entry value here so that all lines
27350Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
27360Sstevel@tonic-gate 		 * title in other entries, all lines in those
27370Sstevel@tonic-gate 		 * entries get printed as well.
27380Sstevel@tonic-gate 		 */
27390Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
27400Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
27410Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
27420Sstevel@tonic-gate 			entry = lp->entryNum;
27430Sstevel@tonic-gate 			found = 1;
27440Sstevel@tonic-gate 			continue;
27450Sstevel@tonic-gate 		}
27460Sstevel@tonic-gate 	}
27470Sstevel@tonic-gate 
27480Sstevel@tonic-gate 	if (!found) {
27490Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
27500Sstevel@tonic-gate 		return (BAM_ERROR);
27510Sstevel@tonic-gate 	}
27520Sstevel@tonic-gate 
27530Sstevel@tonic-gate 	return (BAM_SUCCESS);
27540Sstevel@tonic-gate }
27550Sstevel@tonic-gate 
27560Sstevel@tonic-gate static int
27570Sstevel@tonic-gate add_boot_entry(menu_t *mp,
27580Sstevel@tonic-gate 	char *title,
27590Sstevel@tonic-gate 	char *root,
27600Sstevel@tonic-gate 	char *kernel,
27610Sstevel@tonic-gate 	char *module)
27620Sstevel@tonic-gate {
27630Sstevel@tonic-gate 	int lineNum, entryNum;
27640Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
27653446Smrj 	menu_cmd_t k_cmd, m_cmd;
27660Sstevel@tonic-gate 
27670Sstevel@tonic-gate 	assert(mp);
27680Sstevel@tonic-gate 
27690Sstevel@tonic-gate 	if (title == NULL) {
2770656Sszhou 		title = "Solaris";	/* default to Solaris */
27710Sstevel@tonic-gate 	}
27720Sstevel@tonic-gate 	if (kernel == NULL) {
27730Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
27740Sstevel@tonic-gate 		return (BAM_ERROR);
27750Sstevel@tonic-gate 	}
27760Sstevel@tonic-gate 	if (module == NULL) {
27773446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
27783446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
27793446Smrj 			return (BAM_ERROR);
27803446Smrj 		}
27813446Smrj 
27823446Smrj 		/* Figure the commands out from the kernel line */
27833446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
27843446Smrj 			module = DIRECT_BOOT_ARCHIVE;
27853446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
27863446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
27873446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
27883446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
27893446Smrj 			k_cmd = KERNEL_CMD;
27903446Smrj 			m_cmd = MODULE_CMD;
27913446Smrj 		} else {
27923446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
27933446Smrj 			k_cmd = KERNEL_CMD;
27943446Smrj 			m_cmd = MODULE_CMD;
27953446Smrj 		}
27963446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
27973446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
27983446Smrj 		/*
27993446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
28003446Smrj 		 * command.  Otherwise, use "kernel".
28013446Smrj 		 */
28023446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
28033446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
28043446Smrj 	} else {
28053446Smrj 		k_cmd = KERNEL_CMD;
28063446Smrj 		m_cmd = MODULE_CMD;
28070Sstevel@tonic-gate 	}
28080Sstevel@tonic-gate 
28090Sstevel@tonic-gate 	if (mp->start) {
28100Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
28110Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
28120Sstevel@tonic-gate 	} else {
28130Sstevel@tonic-gate 		lineNum = LINE_INIT;
28140Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
28150Sstevel@tonic-gate 	}
28160Sstevel@tonic-gate 
28170Sstevel@tonic-gate 	/*
28180Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
28190Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
28200Sstevel@tonic-gate 	 */
28210Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
28223446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
2823662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
28240Sstevel@tonic-gate 
28250Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
28260Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
2827662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
2828662Sszhou 
2829662Sszhou 	if (root) {
2830662Sszhou 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
2831662Sszhou 		    menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root);
2832662Sszhou 		line_parser(mp, linebuf, &lineNum, &entryNum);
28330Sstevel@tonic-gate 	}
28340Sstevel@tonic-gate 
28350Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
28363446Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
2837662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
28380Sstevel@tonic-gate 
28390Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
28403446Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
2841662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
28443446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
2845662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
28460Sstevel@tonic-gate 
28470Sstevel@tonic-gate 	return (entryNum);
28480Sstevel@tonic-gate }
28490Sstevel@tonic-gate 
28500Sstevel@tonic-gate static error_t
28510Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
28520Sstevel@tonic-gate {
2853662Sszhou 	line_t *lp, *freed;
2854662Sszhou 	entry_t *ent, *tmp;
28550Sstevel@tonic-gate 	int deleted;
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
28580Sstevel@tonic-gate 
2859662Sszhou 	ent = mp->entries;
2860662Sszhou 	while (ent) {
2861662Sszhou 		lp = ent->start;
2862662Sszhou 		/* check entry number and make sure it's a bootadm entry */
2863662Sszhou 		if (lp->flags != BAM_COMMENT ||
28643446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
2865662Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
2866662Sszhou 			ent = ent->next;
28670Sstevel@tonic-gate 			continue;
28680Sstevel@tonic-gate 		}
28690Sstevel@tonic-gate 
2870662Sszhou 		/* free the entry content */
2871662Sszhou 		do {
2872662Sszhou 			freed = lp;
2873662Sszhou 			lp = lp->next;	/* prev stays the same */
2874662Sszhou 			unlink_line(mp, freed);
2875662Sszhou 			line_free(freed);
2876662Sszhou 		} while (freed != ent->end);
2877662Sszhou 
2878662Sszhou 		/* free the entry_t structure */
2879662Sszhou 		tmp = ent;
2880662Sszhou 		ent = ent->next;
2881662Sszhou 		if (tmp->prev)
2882662Sszhou 			tmp->prev->next = ent;
28830Sstevel@tonic-gate 		else
2884662Sszhou 			mp->entries = ent;
2885662Sszhou 		if (ent)
2886662Sszhou 			ent->prev = tmp->prev;
28870Sstevel@tonic-gate 		deleted = 1;
28880Sstevel@tonic-gate 	}
28890Sstevel@tonic-gate 
28900Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
28910Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
28920Sstevel@tonic-gate 		return (BAM_ERROR);
28930Sstevel@tonic-gate 	}
28940Sstevel@tonic-gate 
2895621Svikram 	/*
2896621Svikram 	 * Now that we have deleted an entry, update
2897621Svikram 	 * the entry numbering and the default cmd.
2898621Svikram 	 */
2899621Svikram 	update_numbering(mp);
2900621Svikram 
29010Sstevel@tonic-gate 	return (BAM_SUCCESS);
29020Sstevel@tonic-gate }
29030Sstevel@tonic-gate 
29040Sstevel@tonic-gate static error_t
29050Sstevel@tonic-gate delete_all_entries(menu_t *mp, char *menu_path, char *opt)
29060Sstevel@tonic-gate {
29070Sstevel@tonic-gate 	assert(mp);
29080Sstevel@tonic-gate 	assert(opt == NULL);
29090Sstevel@tonic-gate 
29100Sstevel@tonic-gate 	if (mp->start == NULL) {
29110Sstevel@tonic-gate 		bam_print(EMPTY_FILE, menu_path);
29120Sstevel@tonic-gate 		return (BAM_SUCCESS);
29130Sstevel@tonic-gate 	}
29140Sstevel@tonic-gate 
29150Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
29160Sstevel@tonic-gate 		return (BAM_ERROR);
29170Sstevel@tonic-gate 	}
29180Sstevel@tonic-gate 
29190Sstevel@tonic-gate 	return (BAM_WRITE);
29200Sstevel@tonic-gate }
29210Sstevel@tonic-gate 
29220Sstevel@tonic-gate static FILE *
2923662Sszhou open_diskmap(char *root)
29240Sstevel@tonic-gate {
29250Sstevel@tonic-gate 	FILE *fp;
29260Sstevel@tonic-gate 	char cmd[PATH_MAX];
29270Sstevel@tonic-gate 
29280Sstevel@tonic-gate 	/* make sure we have a map file */
29290Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
29300Sstevel@tonic-gate 	if (fp == NULL) {
29310Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
2932662Sszhou 		    "%s%s > /dev/null", root, CREATE_DISKMAP);
29330Sstevel@tonic-gate 		(void) system(cmd);
29340Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
29350Sstevel@tonic-gate 	}
29360Sstevel@tonic-gate 	return (fp);
29370Sstevel@tonic-gate }
29380Sstevel@tonic-gate 
29390Sstevel@tonic-gate #define	SECTOR_SIZE	512
29400Sstevel@tonic-gate 
29410Sstevel@tonic-gate static int
29420Sstevel@tonic-gate get_partition(char *device)
29430Sstevel@tonic-gate {
29440Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
29450Sstevel@tonic-gate 	struct mboot *mboot;
29460Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
29470Sstevel@tonic-gate 	char *wholedisk, *slice;
29480Sstevel@tonic-gate 
29490Sstevel@tonic-gate 	/* form whole disk (p0) */
29500Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
29510Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
29520Sstevel@tonic-gate 	if (!is_pcfs)
29530Sstevel@tonic-gate 		*slice = '\0';
29540Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
29550Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
29560Sstevel@tonic-gate 	if (!is_pcfs)
29570Sstevel@tonic-gate 		*slice = 's';
29580Sstevel@tonic-gate 
29590Sstevel@tonic-gate 	/* read boot sector */
29600Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
29610Sstevel@tonic-gate 	free(wholedisk);
29620Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
29630Sstevel@tonic-gate 		return (partno);
29640Sstevel@tonic-gate 	}
29650Sstevel@tonic-gate 	(void) close(fd);
29660Sstevel@tonic-gate 
29670Sstevel@tonic-gate 	/* parse fdisk table */
29680Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
29690Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
29700Sstevel@tonic-gate 		struct ipart *part =
29710Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
29720Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
29730Sstevel@tonic-gate 			if (part->systid == 0xbe) {
29740Sstevel@tonic-gate 				partno = i;
29750Sstevel@tonic-gate 				break;
29760Sstevel@tonic-gate 			}
29770Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
29780Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
29790Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
29800Sstevel@tonic-gate 				partno = i;
29810Sstevel@tonic-gate 				break;
29820Sstevel@tonic-gate 			}
29830Sstevel@tonic-gate 		}
29840Sstevel@tonic-gate 	}
29850Sstevel@tonic-gate 	return (partno);
29860Sstevel@tonic-gate }
29870Sstevel@tonic-gate 
29880Sstevel@tonic-gate static char *
29890Sstevel@tonic-gate get_grubdisk(char *rootdev, FILE *fp, int on_bootdev)
29900Sstevel@tonic-gate {
29910Sstevel@tonic-gate 	char *grubdisk;	/* (hd#,#,#) */
29920Sstevel@tonic-gate 	char *slice;
29930Sstevel@tonic-gate 	char *grubhd;
29940Sstevel@tonic-gate 	int fdiskpart;
29950Sstevel@tonic-gate 	int found = 0;
29960Sstevel@tonic-gate 	char *devname, *ctdname = strstr(rootdev, "dsk/");
29970Sstevel@tonic-gate 	char linebuf[PATH_MAX];
29980Sstevel@tonic-gate 
29990Sstevel@tonic-gate 	if (ctdname == NULL)
30000Sstevel@tonic-gate 		return (NULL);
30010Sstevel@tonic-gate 
30020Sstevel@tonic-gate 	ctdname += strlen("dsk/");
30030Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
30040Sstevel@tonic-gate 	if (slice)
30050Sstevel@tonic-gate 		*slice = '\0';
30060Sstevel@tonic-gate 
30070Sstevel@tonic-gate 	rewind(fp);
30080Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
30090Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
30100Sstevel@tonic-gate 		if (grubhd)
30110Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
30120Sstevel@tonic-gate 		else
30130Sstevel@tonic-gate 			devname = NULL;
30140Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
30150Sstevel@tonic-gate 			found = 1;
30160Sstevel@tonic-gate 			break;
30170Sstevel@tonic-gate 		}
30180Sstevel@tonic-gate 	}
30190Sstevel@tonic-gate 
30200Sstevel@tonic-gate 	if (slice)
30210Sstevel@tonic-gate 		*slice = 's';
30220Sstevel@tonic-gate 
30230Sstevel@tonic-gate 	if (found == 0) {
30240Sstevel@tonic-gate 		if (bam_verbose)
30250Sstevel@tonic-gate 			bam_print(DISKMAP_FAIL_NONFATAL, rootdev);
30260Sstevel@tonic-gate 		grubhd = "0";	/* assume disk 0 if can't match */
30270Sstevel@tonic-gate 	}
30280Sstevel@tonic-gate 
30290Sstevel@tonic-gate 	fdiskpart = get_partition(rootdev);
30300Sstevel@tonic-gate 	if (fdiskpart == -1)
30310Sstevel@tonic-gate 		return (NULL);
30320Sstevel@tonic-gate 
30330Sstevel@tonic-gate 	grubdisk = s_calloc(1, 10);
30340Sstevel@tonic-gate 	if (slice) {
30350Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d,%c)",
30360Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
30370Sstevel@tonic-gate 	} else
30380Sstevel@tonic-gate 		(void) snprintf(grubdisk, 10, "(hd%s,%d)",
30390Sstevel@tonic-gate 		    grubhd, fdiskpart);
30400Sstevel@tonic-gate 
30410Sstevel@tonic-gate 	/* if root not on bootdev, change GRUB disk to 0 */
30420Sstevel@tonic-gate 	if (!on_bootdev)
30430Sstevel@tonic-gate 		grubdisk[3] = '0';
30440Sstevel@tonic-gate 	return (grubdisk);
30450Sstevel@tonic-gate }
30460Sstevel@tonic-gate 
3047656Sszhou static char *
3048656Sszhou get_title(char *rootdir)
30490Sstevel@tonic-gate {
30500Sstevel@tonic-gate 	static char title[80];	/* from /etc/release */
3051662Sszhou 	char *cp = NULL, release[PATH_MAX];
30520Sstevel@tonic-gate 	FILE *fp;
30530Sstevel@tonic-gate 
30540Sstevel@tonic-gate 	/* open the /etc/release file */
30550Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
30560Sstevel@tonic-gate 
30570Sstevel@tonic-gate 	fp = fopen(release, "r");
30580Sstevel@tonic-gate 	if (fp == NULL)
3059656Sszhou 		return (NULL);
30600Sstevel@tonic-gate 
30610Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
30620Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
30630Sstevel@tonic-gate 		if (cp)
30640Sstevel@tonic-gate 			break;
30650Sstevel@tonic-gate 	}
30660Sstevel@tonic-gate 	(void) fclose(fp);
3067662Sszhou 	return (cp == NULL ? "Solaris" : cp);
30680Sstevel@tonic-gate }
30690Sstevel@tonic-gate 
30703446Smrj char *
30710Sstevel@tonic-gate get_special(char *mountp)
30720Sstevel@tonic-gate {
30730Sstevel@tonic-gate 	FILE *mntfp;
30740Sstevel@tonic-gate 	struct mnttab mp = {0}, mpref = {0};
30750Sstevel@tonic-gate 
30760Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
30770Sstevel@tonic-gate 	if (mntfp == NULL) {
30780Sstevel@tonic-gate 		return (0);
30790Sstevel@tonic-gate 	}
30800Sstevel@tonic-gate 
30810Sstevel@tonic-gate 	if (*mountp == '\0')
30820Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
30830Sstevel@tonic-gate 	else
30840Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
30850Sstevel@tonic-gate 	if (getmntany(mntfp, &mp, &mpref) != 0) {
30860Sstevel@tonic-gate 		(void) fclose(mntfp);
30870Sstevel@tonic-gate 		return (NULL);
30880Sstevel@tonic-gate 	}
30890Sstevel@tonic-gate 	(void) fclose(mntfp);
30900Sstevel@tonic-gate 
30910Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
30920Sstevel@tonic-gate }
30930Sstevel@tonic-gate 
30943446Smrj char *
30950Sstevel@tonic-gate os_to_grubdisk(char *osdisk, int on_bootdev)
30960Sstevel@tonic-gate {
30970Sstevel@tonic-gate 	FILE *fp;
30980Sstevel@tonic-gate 	char *grubdisk;
30990Sstevel@tonic-gate 
31000Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3101662Sszhou 	fp = open_diskmap("");
31020Sstevel@tonic-gate 	if (fp == NULL) {
31030Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdisk);
31040Sstevel@tonic-gate 		return (NULL);
31050Sstevel@tonic-gate 	}
31060Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdisk, fp, on_bootdev);
31070Sstevel@tonic-gate 	(void) fclose(fp);
31080Sstevel@tonic-gate 	return (grubdisk);
31090Sstevel@tonic-gate }
31100Sstevel@tonic-gate 
31110Sstevel@tonic-gate /*
31120Sstevel@tonic-gate  * Check if root is on the boot device
31130Sstevel@tonic-gate  * Return 0 (false) on error
31140Sstevel@tonic-gate  */
31150Sstevel@tonic-gate static int
31160Sstevel@tonic-gate menu_on_bootdev(char *menu_root, FILE *fp)
31170Sstevel@tonic-gate {
31180Sstevel@tonic-gate 	int ret;
31190Sstevel@tonic-gate 	char *grubhd, *bootp, *special;
31200Sstevel@tonic-gate 
31210Sstevel@tonic-gate 	special = get_special(menu_root);
31220Sstevel@tonic-gate 	if (special == NULL)
31230Sstevel@tonic-gate 		return (0);
31240Sstevel@tonic-gate 	bootp = strstr(special, "p0:boot");
31250Sstevel@tonic-gate 	if (bootp)
31260Sstevel@tonic-gate 		*bootp = '\0';
31270Sstevel@tonic-gate 	grubhd = get_grubdisk(special, fp, 1);
31280Sstevel@tonic-gate 	free(special);
31290Sstevel@tonic-gate 
31300Sstevel@tonic-gate 	if (grubhd == NULL)
31310Sstevel@tonic-gate 		return (0);
31320Sstevel@tonic-gate 	ret = grubhd[3] == '0';
31330Sstevel@tonic-gate 	free(grubhd);
31340Sstevel@tonic-gate 	return (ret);
31350Sstevel@tonic-gate }
31360Sstevel@tonic-gate 
3137662Sszhou /*
3138662Sszhou  * look for matching bootadm entry with specified parameters
3139662Sszhou  * Here are the rules (based on existing usage):
3140662Sszhou  * - If title is specified, match on title only
3141662Sszhou  * - Else, match on grubdisk and module (don't care about kernel line).
3142662Sszhou  *   note that, if root_opt is non-zero, the absence of root line is
3143662Sszhou  *   considered a match.
3144662Sszhou  */
3145662Sszhou static entry_t *
3146662Sszhou find_boot_entry(menu_t *mp, char *title, char *root, char *module,
3147662Sszhou     int root_opt, int *entry_num)
3148662Sszhou {
3149662Sszhou 	int i;
3150662Sszhou 	line_t *lp;
3151662Sszhou 	entry_t *ent;
3152662Sszhou 
3153662Sszhou 	/* find matching entry */
3154662Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
3155662Sszhou 		lp = ent->start;
3156662Sszhou 
3157662Sszhou 		/* first line of entry must be bootadm comment */
3158662Sszhou 		lp = ent->start;
31593446Smrj 		if (lp->flags != BAM_COMMENT ||
31603446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
3161662Sszhou 			continue;
3162662Sszhou 		}
3163662Sszhou 
3164662Sszhou 		/* advance to title line */
3165662Sszhou 		lp = lp->next;
3166662Sszhou 		if (title) {
3167662Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
3168662Sszhou 			    strcmp(lp->arg, title) == 0)
3169662Sszhou 				break;
3170662Sszhou 			continue;	/* check title only */
3171662Sszhou 		}
3172662Sszhou 
3173662Sszhou 		lp = lp->next;	/* advance to root line */
3174662Sszhou 		if (lp == NULL || strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3175662Sszhou 			/* root command found, match grub disk */
3176662Sszhou 			if (strcmp(lp->arg, root) != 0) {
3177662Sszhou 				continue;
3178662Sszhou 			}
3179662Sszhou 			lp = lp->next;	/* advance to kernel line */
3180662Sszhou 		} else {
3181662Sszhou 			/* no root command, see if root is optional */
3182662Sszhou 			if (root_opt == 0) {
3183662Sszhou 				continue;
3184662Sszhou 			}
3185662Sszhou 		}
3186662Sszhou 
3187662Sszhou 		if (lp == NULL || lp->next == NULL) {
3188662Sszhou 			continue;
3189662Sszhou 		}
3190662Sszhou 
3191*3467Srscott 		/*
3192*3467Srscott 		 * Check for matching module entry (failsafe or normal).  We
3193*3467Srscott 		 * use a strncmp to match "module" or "module$", since we
3194*3467Srscott 		 * don't know which one it should be.  If it fails to match,
3195*3467Srscott 		 * we go around the loop again.
3196*3467Srscott 		 */
3197662Sszhou 		lp = lp->next;	/* advance to module line */
31983446Smrj 		if ((strncmp(lp->cmd, menu_cmds[MODULE_CMD],
31993446Smrj 		    strlen(menu_cmds[MODULE_CMD])) != 0) ||
32003446Smrj 		    (strcmp(lp->arg, module) != 0)) {
3201662Sszhou 			continue;
3202662Sszhou 		}
3203662Sszhou 		break;	/* match found */
3204662Sszhou 	}
3205662Sszhou 
3206662Sszhou 	*entry_num = i;
3207662Sszhou 	return (ent);
3208662Sszhou }
3209662Sszhou 
3210662Sszhou static int
3211662Sszhou update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
3212662Sszhou     char *module, int root_opt)
3213662Sszhou {
32143446Smrj 	int i, change_kernel = 0;
3215662Sszhou 	entry_t *ent;
3216662Sszhou 	line_t *lp;
3217662Sszhou 	char linebuf[BAM_MAXLINE];
3218662Sszhou 
3219662Sszhou 	/* note: don't match on title, it's updated on upgrade */
3220662Sszhou 	ent = find_boot_entry(mp, NULL, root, module, root_opt, &i);
32213446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
32223446Smrj 		/*
32233446Smrj 		 * We may be upgrading a kernel from multiboot to
32243446Smrj 		 * directboot.  Look for a multiboot entry.
32253446Smrj 		 */
32263446Smrj 		ent = find_boot_entry(mp, NULL, root, MULTI_BOOT_ARCHIVE,
32273446Smrj 		    root_opt, &i);
32283446Smrj 		if (ent != NULL) {
32293446Smrj 			change_kernel = 1;
32303446Smrj 		}
32313446Smrj 	}
3232662Sszhou 	if (ent == NULL)
3233662Sszhou 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
3234662Sszhou 		    kernel, module));
3235662Sszhou 
3236662Sszhou 	/* replace title of exiting entry and delete root line */
3237662Sszhou 	lp = ent->start;
3238662Sszhou 	lp = lp->next;	/* title line */
3239662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3240662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3241662Sszhou 	free(lp->arg);
3242662Sszhou 	free(lp->line);
3243662Sszhou 	lp->arg = s_strdup(title);
3244662Sszhou 	lp->line = s_strdup(linebuf);
3245662Sszhou 
3246662Sszhou 	lp = lp->next;	/* root line */
3247662Sszhou 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3248662Sszhou 		if (root_opt) {		/* root line not needed */
3249662Sszhou 			line_t *tmp = lp;
3250662Sszhou 			lp = lp->next;
3251662Sszhou 			unlink_line(mp, tmp);
3252662Sszhou 			line_free(tmp);
3253662Sszhou 		} else
3254662Sszhou 			lp = lp->next;
3255662Sszhou 	}
32563446Smrj 
32573446Smrj 	if (change_kernel) {
32583446Smrj 		/*
32593446Smrj 		 * We're upgrading from multiboot to directboot.
32603446Smrj 		 */
32613446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
32623446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
32633446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
32643446Smrj 			    kernel);
32653446Smrj 			free(lp->arg);
32663446Smrj 			free(lp->line);
32673446Smrj 			lp->arg = s_strdup(kernel);
32683446Smrj 			lp->line = s_strdup(linebuf);
32693446Smrj 			lp = lp->next;
32703446Smrj 		}
32713446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
32723446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
32733446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
32743446Smrj 			    module);
32753446Smrj 			free(lp->arg);
32763446Smrj 			free(lp->line);
32773446Smrj 			lp->arg = s_strdup(module);
32783446Smrj 			lp->line = s_strdup(linebuf);
32793446Smrj 			lp = lp->next;
32803446Smrj 		}
32813446Smrj 	}
3282662Sszhou 	return (i);
3283662Sszhou }
3284662Sszhou 
32850Sstevel@tonic-gate /*ARGSUSED*/
32860Sstevel@tonic-gate static error_t
32870Sstevel@tonic-gate update_entry(menu_t *mp, char *menu_root, char *opt)
32880Sstevel@tonic-gate {
32890Sstevel@tonic-gate 	FILE *fp;
32900Sstevel@tonic-gate 	int entry;
32913449Srscott 	char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL;
3292662Sszhou 	struct stat sbuf;
3293662Sszhou 	char failsafe[256];
32940Sstevel@tonic-gate 
32950Sstevel@tonic-gate 	assert(mp);
32960Sstevel@tonic-gate 	assert(opt);
32970Sstevel@tonic-gate 
32980Sstevel@tonic-gate 	osdev = strtok(opt, ",");
32990Sstevel@tonic-gate 	osroot = strtok(NULL, ",");
33000Sstevel@tonic-gate 	if (osroot == NULL)
33010Sstevel@tonic-gate 		osroot = menu_root;
33020Sstevel@tonic-gate 	title = get_title(osroot);
33030Sstevel@tonic-gate 
33040Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3305662Sszhou 	fp = open_diskmap(osroot);
33060Sstevel@tonic-gate 	if (fp == NULL) {
33070Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
33080Sstevel@tonic-gate 		return (BAM_ERROR);
33090Sstevel@tonic-gate 	}
33100Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
33110Sstevel@tonic-gate 	(void) fclose(fp);
33120Sstevel@tonic-gate 	if (grubdisk == NULL) {
33130Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
33140Sstevel@tonic-gate 		return (BAM_ERROR);
33150Sstevel@tonic-gate 	}
33160Sstevel@tonic-gate 
33170Sstevel@tonic-gate 	/* add the entry for normal Solaris */
33183446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
33193446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
33203446Smrj 		    DIRECT_BOOT_KERNEL, DIRECT_BOOT_ARCHIVE,
33213446Smrj 		    osroot == menu_root);
33223446Smrj 	} else {
33233446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
33243446Smrj 		    MULTI_BOOT, MULTI_BOOT_ARCHIVE,
33253446Smrj 		    osroot == menu_root);
33263446Smrj 	}
33270Sstevel@tonic-gate 
33280Sstevel@tonic-gate 	/* add the entry for failsafe archive */
33293446Smrj 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
33303446Smrj 	if (stat(failsafe, &sbuf) == 0) {
33313449Srscott 
33323449Srscott 		/* Figure out where the kernel line should point */
33333449Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
33343449Srscott 		    DIRECT_BOOT_FAILSAFE_KERNEL);
33353449Srscott 		if (stat(failsafe, &sbuf) == 0) {
33363449Srscott 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
33373449Srscott 		} else {
33383449Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
33393449Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
33403449Srscott 			if (stat(failsafe, &sbuf) == 0) {
33413449Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
33423449Srscott 			}
33433449Srscott 		}
33443449Srscott 		if (failsafe_kernel != NULL) {
33453449Srscott 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
33463449Srscott 			    failsafe_kernel, MINIROOT, osroot == menu_root);
33473449Srscott 		} else {
33483449Srscott 			bam_error(NO_FAILSAFE_KERNEL);
33493449Srscott 		}
33503446Smrj 	}
33510Sstevel@tonic-gate 	free(grubdisk);
33520Sstevel@tonic-gate 
33530Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
33540Sstevel@tonic-gate 		return (BAM_ERROR);
33550Sstevel@tonic-gate 	}
33560Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
33570Sstevel@tonic-gate 	return (BAM_WRITE);
33580Sstevel@tonic-gate }
33590Sstevel@tonic-gate 
3360316Svikram static char *
3361316Svikram read_grub_root(void)
3362316Svikram {
3363316Svikram 	FILE *fp;
3364316Svikram 	struct stat sb;
3365316Svikram 	char buf[BAM_MAXLINE];
3366316Svikram 	char *rootstr;
3367316Svikram 
3368316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3369316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3370316Svikram 		return (NULL);
3371316Svikram 	}
3372316Svikram 
3373316Svikram 	if (stat(GRUB_root, &sb) != 0) {
3374316Svikram 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3375316Svikram 		return (NULL);
3376316Svikram 	}
3377316Svikram 
3378316Svikram 	fp = fopen(GRUB_root, "r");
3379316Svikram 	if (fp == NULL) {
3380316Svikram 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3381316Svikram 		return (NULL);
3382316Svikram 	}
3383316Svikram 
3384316Svikram 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3385316Svikram 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3386316Svikram 		(void) fclose(fp);
3387316Svikram 		return (NULL);
3388316Svikram 	}
3389316Svikram 
3390316Svikram 	/*
3391316Svikram 	 * Copy buf here as check below may trash the buffer
3392316Svikram 	 */
3393316Svikram 	rootstr = s_strdup(buf);
3394316Svikram 
3395316Svikram 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3396316Svikram 		bam_error(BAD_ROOT_FILE, GRUB_root);
3397316Svikram 		free(rootstr);
3398316Svikram 		rootstr = NULL;
3399316Svikram 	}
3400316Svikram 
3401316Svikram 	(void) fclose(fp);
3402316Svikram 
3403316Svikram 	return (rootstr);
3404316Svikram }
3405316Svikram 
3406662Sszhou static void
34073446Smrj save_default_entry(menu_t *mp, const char *which)
3408662Sszhou {
3409662Sszhou 	int lineNum, entryNum;
3410662Sszhou 	int entry = 0;	/* default is 0 */
3411662Sszhou 	char linebuf[BAM_MAXLINE];
3412662Sszhou 	line_t *lp = mp->curdefault;
3413662Sszhou 
34143381Svikram 	if (mp->start) {
34153381Svikram 		lineNum = mp->end->lineNum;
34163381Svikram 		entryNum = mp->end->entryNum;
34173381Svikram 	} else {
34183381Svikram 		lineNum = LINE_INIT;
34193381Svikram 		entryNum = ENTRY_INIT;
34203381Svikram 	}
34213381Svikram 
3422662Sszhou 	if (lp)
3423662Sszhou 		entry = s_strtol(lp->arg);
3424662Sszhou 
34253446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
3426662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
3427662Sszhou }
3428662Sszhou 
3429662Sszhou static void
34303446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
3431662Sszhou {
3432662Sszhou 	int entry;
3433662Sszhou 	char *str;
3434662Sszhou 
3435662Sszhou 	if (lp == NULL)
3436662Sszhou 		return;		/* nothing to restore */
3437662Sszhou 
34383446Smrj 	str = lp->arg + strlen(which);
3439662Sszhou 	entry = s_strtol(str);
3440662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3441662Sszhou 
3442662Sszhou 	/* delete saved old default line */
3443662Sszhou 	unlink_line(mp, lp);
3444662Sszhou 	line_free(lp);
3445662Sszhou }
3446662Sszhou 
34470Sstevel@tonic-gate /*
34480Sstevel@tonic-gate  * This function is for supporting reboot with args.
34490Sstevel@tonic-gate  * The opt value can be:
34500Sstevel@tonic-gate  * NULL		delete temp entry, if present
34510Sstevel@tonic-gate  * entry=#	switches default entry to 1
34520Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
34530Sstevel@tonic-gate  *		and make it the default
34540Sstevel@tonic-gate  */
34550Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
34560Sstevel@tonic-gate 
3457662Sszhou /*ARGSUSED*/
34580Sstevel@tonic-gate static error_t
34590Sstevel@tonic-gate update_temp(menu_t *mp, char *menupath, char *opt)
34600Sstevel@tonic-gate {
34610Sstevel@tonic-gate 	int entry;
34623446Smrj 	char *grubdisk, *rootdev, *path;
34633446Smrj 	char kernbuf[BUFSIZ];
34643446Smrj 	char args_buf[BUFSIZ];
3465316Svikram 	struct stat sb;
34660Sstevel@tonic-gate 
34670Sstevel@tonic-gate 	assert(mp);
34680Sstevel@tonic-gate 
3469662Sszhou 	/* If no option, delete exiting reboot menu entry */
3470662Sszhou 	if (opt == NULL) {
3471662Sszhou 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
3472662Sszhou 		    0, &entry);
3473662Sszhou 		if (ent == NULL)	/* not found is ok */
3474662Sszhou 			return (BAM_SUCCESS);
3475662Sszhou 		(void) do_delete(mp, entry);
34763446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
34773446Smrj 		mp->olddefault = NULL;
3478662Sszhou 		return (BAM_WRITE);
3479662Sszhou 	}
3480662Sszhou 
3481662Sszhou 	/* if entry= is specified, set the default entry */
3482662Sszhou 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
34830Sstevel@tonic-gate 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
34840Sstevel@tonic-gate 		/* this is entry=# option */
34850Sstevel@tonic-gate 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
34860Sstevel@tonic-gate 	}
34870Sstevel@tonic-gate 
34880Sstevel@tonic-gate 	/*
34890Sstevel@tonic-gate 	 * add a new menu entry base on opt and make it the default
34900Sstevel@tonic-gate 	 */
3491316Svikram 	grubdisk = NULL;
3492316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3493316Svikram 		/*
3494316Svikram 		 * 1. First get root disk name from mnttab
3495316Svikram 		 * 2. Translate disk name to grub name
3496316Svikram 		 * 3. Add the new menu entry
3497316Svikram 		 */
3498316Svikram 		rootdev = get_special("/");
3499316Svikram 		if (rootdev) {
3500316Svikram 			grubdisk = os_to_grubdisk(rootdev, 1);
3501316Svikram 			free(rootdev);
3502316Svikram 		}
3503316Svikram 	} else {
3504316Svikram 		/*
3505316Svikram 		 * This is an LU BE. The GRUB_root file
3506316Svikram 		 * contains entry for GRUB's "root" cmd.
3507316Svikram 		 */
3508316Svikram 		grubdisk = read_grub_root();
35090Sstevel@tonic-gate 	}
35100Sstevel@tonic-gate 	if (grubdisk == NULL) {
3511316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
35120Sstevel@tonic-gate 		return (BAM_ERROR);
35130Sstevel@tonic-gate 	}
35140Sstevel@tonic-gate 
35150Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
35163446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
35173446Smrj 		if (opt[0] == '-') {
35183446Smrj 			/* It's an option - first see if boot-file is set */
35193446Smrj 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
35203446Smrj 			    != BAM_SUCCESS)
35213446Smrj 				return (BAM_ERROR);
35223446Smrj 			if (kernbuf[0] == '\0')
35233446Smrj 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
35243446Smrj 				    BUFSIZ);
35253446Smrj 			(void) strlcat(kernbuf, " ", BUFSIZ);
35263446Smrj 			(void) strlcat(kernbuf, opt, BUFSIZ);
35273446Smrj 		} else if (opt[0] == '/') {
35283446Smrj 			/* It's a full path - write it out and go home */
35293446Smrj 			(void) strlcpy(kernbuf, opt, BUFSIZ);
35303446Smrj 		} else {
35313446Smrj 			path = expand_path(opt);
35323446Smrj 			if (path != NULL) {
35333446Smrj 				(void) strlcpy(kernbuf, path, BUFSIZ);
35343446Smrj 				free(path);
35353446Smrj 				if (strcmp(opt, "kmdb") == 0) {
35363446Smrj 					if (set_kernel(mp, ARGS_CMD, NULL,
35373446Smrj 					    args_buf, BUFSIZ) != BAM_SUCCESS)
35383446Smrj 						return (BAM_ERROR);
35393446Smrj 
35403446Smrj 					if (args_buf[0] != '\0') {
35413446Smrj 						(void) strlcat(kernbuf, " ",
35423446Smrj 						    BUFSIZ);
35433446Smrj 						(void) strlcat(kernbuf,
35443446Smrj 						    args_buf, BUFSIZ);
35453446Smrj 					}
35463446Smrj 				}
35473446Smrj 			}
35483446Smrj 		}
35493446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
35503446Smrj 		    NULL);
35513446Smrj 	} else {
35523446Smrj 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
35533446Smrj 		    MULTI_BOOT, opt);
35543446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
35553446Smrj 		    MULTI_BOOT_ARCHIVE);
35563446Smrj 	}
35570Sstevel@tonic-gate 	free(grubdisk);
35580Sstevel@tonic-gate 
35590Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
3560316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
35610Sstevel@tonic-gate 		return (BAM_ERROR);
35620Sstevel@tonic-gate 	}
3563662Sszhou 
35643446Smrj 	save_default_entry(mp, BAM_OLDDEF);
35650Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
35660Sstevel@tonic-gate 	return (BAM_WRITE);
35670Sstevel@tonic-gate }
35680Sstevel@tonic-gate 
35690Sstevel@tonic-gate static error_t
35700Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
35710Sstevel@tonic-gate {
35720Sstevel@tonic-gate 	line_t *lp, *found, *last;
35730Sstevel@tonic-gate 	char *cp, *str;
35740Sstevel@tonic-gate 	char prefix[BAM_MAXLINE];
35750Sstevel@tonic-gate 	size_t len;
35760Sstevel@tonic-gate 
35770Sstevel@tonic-gate 	assert(mp);
35780Sstevel@tonic-gate 	assert(globalcmd);
35790Sstevel@tonic-gate 
3580316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3581316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3582316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3583316Svikram 			bam_error(INVALID_ENTRY, prefix);
3584316Svikram 			return (BAM_ERROR);
3585316Svikram 		}
3586316Svikram 	}
3587316Svikram 
35880Sstevel@tonic-gate 	found = last = NULL;
35890Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
35900Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
35910Sstevel@tonic-gate 			continue;
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate 		last = lp; /* track the last global found */
35940Sstevel@tonic-gate 
35950Sstevel@tonic-gate 		if (lp->cmd == NULL) {
35960Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
35970Sstevel@tonic-gate 			continue;
35980Sstevel@tonic-gate 		}
35990Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
36000Sstevel@tonic-gate 			continue;
36010Sstevel@tonic-gate 
36020Sstevel@tonic-gate 		if (found) {
36030Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
36040Sstevel@tonic-gate 		}
36050Sstevel@tonic-gate 		found = lp;
36060Sstevel@tonic-gate 	}
36070Sstevel@tonic-gate 
36080Sstevel@tonic-gate 	if (found == NULL) {
36090Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
36100Sstevel@tonic-gate 		if (last == NULL) {
36110Sstevel@tonic-gate 			lp->next = mp->start;
36120Sstevel@tonic-gate 			mp->start = lp;
36130Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
36140Sstevel@tonic-gate 		} else {
36150Sstevel@tonic-gate 			lp->next = last->next;
36160Sstevel@tonic-gate 			last->next = lp;
36170Sstevel@tonic-gate 			if (lp->next == NULL)
36180Sstevel@tonic-gate 				mp->end = lp;
36190Sstevel@tonic-gate 		}
36200Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
36210Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
36220Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
36230Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
36240Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
36250Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
36260Sstevel@tonic-gate 		return (BAM_WRITE);
36270Sstevel@tonic-gate 	}
36280Sstevel@tonic-gate 
36290Sstevel@tonic-gate 	/*
36300Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
36310Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
36320Sstevel@tonic-gate 	 * readability.
36330Sstevel@tonic-gate 	 */
36340Sstevel@tonic-gate 	str = found->line;
36350Sstevel@tonic-gate 	cp = prefix;
36360Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
36370Sstevel@tonic-gate 		*(cp++) = *(str++);
36380Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
36390Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
36400Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
36410Sstevel@tonic-gate 
36420Sstevel@tonic-gate 	free(found->line);
36430Sstevel@tonic-gate 	found->line = s_calloc(1, len);
36440Sstevel@tonic-gate 	(void) snprintf(found->line, len,
36450Sstevel@tonic-gate 		"%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
36460Sstevel@tonic-gate 
36470Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
36480Sstevel@tonic-gate }
36490Sstevel@tonic-gate 
36503446Smrj /*
36513446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
36523446Smrj  * expand it to a full unix path.
36533446Smrj  */
36543446Smrj static char *
36553446Smrj expand_path(const char *partial_path)
36563446Smrj {
36573446Smrj 	int new_path_len;
36583446Smrj 	char *new_path, new_path2[PATH_MAX];
36593446Smrj 	struct stat sb;
36603446Smrj 
36613446Smrj 	new_path_len = strlen(partial_path) + 64;
36623446Smrj 	new_path = s_calloc(1, new_path_len);
36633446Smrj 
36643446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
36653446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
36663446Smrj 	    partial_path);
36673446Smrj 	if (stat(new_path, &sb) == 0) {
36683446Smrj 		return (new_path);
36693446Smrj 	}
36703446Smrj 
36713446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
36723446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
36733446Smrj 		    DIRECT_BOOT_KERNEL);
36743446Smrj 		return (new_path);
36753446Smrj 	}
36763446Smrj 
36773446Smrj 	/*
36783446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
36793446Smrj 	 * see if we were just given a glom name.
36803446Smrj 	 */
36813446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
36823446Smrj 	    partial_path);
36833446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
36843446Smrj 	    partial_path);
36853446Smrj 	if (stat(new_path, &sb) == 0) {
36863446Smrj 		if (stat(new_path2, &sb) == 0) {
36873446Smrj 			/*
36883446Smrj 			 * We matched both, so we actually
36893446Smrj 			 * want to write the $ISADIR version.
36903446Smrj 			 */
36913446Smrj 			(void) snprintf(new_path, new_path_len,
36923446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
36933446Smrj 			    partial_path);
36943446Smrj 		}
36953446Smrj 		return (new_path);
36963446Smrj 	}
36973446Smrj 
36983446Smrj 	bam_error(UNKNOWN_KERNEL, partial_path);
36993446Smrj 	free(new_path);
37003446Smrj 	return (NULL);
37013446Smrj }
37023446Smrj 
37033446Smrj /*
37043446Smrj  * The kernel cmd and arg have been changed, so
37053446Smrj  * check whether the archive line needs to change.
37063446Smrj  */
37073446Smrj static void
37083446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
37093446Smrj {
37103446Smrj 	line_t *lp = entryp->start;
37113446Smrj 	char *new_archive;
37123446Smrj 	menu_cmd_t m_cmd;
37133446Smrj 
37143446Smrj 	for (; lp != NULL; lp = lp->next) {
37153446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
37163446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
37173446Smrj 			break;
37183446Smrj 		}
37193446Smrj 		if (lp == entryp->end)
37203446Smrj 			return;
37213446Smrj 	}
37223446Smrj 	if (lp == NULL)
37233446Smrj 		return;
37243446Smrj 
37253446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
37263446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
37273446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
37283446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
37293446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
37303446Smrj 		m_cmd = MODULE_CMD;
37313446Smrj 	} else {
37323446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
37333446Smrj 		m_cmd = MODULE_CMD;
37343446Smrj 	}
37353446Smrj 
37363446Smrj 	if (strcmp(lp->arg, new_archive) == 0)
37373446Smrj 		return;
37383446Smrj 
37393446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
37403446Smrj 		free(lp->cmd);
37413446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
37423446Smrj 	}
37433446Smrj 
37443446Smrj 	free(lp->arg);
37453446Smrj 	lp->arg = s_strdup(new_archive);
37463446Smrj 	update_line(lp);
37473446Smrj }
37483446Smrj 
37493446Smrj /*
37503446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
37513446Smrj  */
37523446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
37533446Smrj 
37543446Smrj /*
37553446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
37563446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
37573446Smrj  * string, reset the value to the default.  If path is a non-zero-length
37583446Smrj  * string, set the kernel or arguments.
37593446Smrj  */
37603446Smrj static error_t
37613446Smrj set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
37623446Smrj {
37633446Smrj 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
37643446Smrj 	entry_t *entryp;
37653446Smrj 	line_t *ptr, *kernelp;
37663446Smrj 	char *new_arg, *old_args, *space;
37673446Smrj 	char *grubdisk, *rootdev, *new_path;
37683446Smrj 	char old_space;
37693446Smrj 	size_t old_kernel_len, new_str_len;
37703446Smrj 	struct stat sb;
37713446Smrj 
37723446Smrj 	assert(bufsize > 0);
37733446Smrj 
37743446Smrj 	ptr = kernelp = NULL;
37753446Smrj 	new_arg = old_args = space = NULL;
37763446Smrj 	grubdisk = rootdev = new_path = NULL;
37773446Smrj 	buf[0] = '\0';
37783446Smrj 
37793446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
37803446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
37813446Smrj 		return (BAM_ERROR);
37823446Smrj 	}
37833446Smrj 
37843446Smrj 	/*
37853446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
37863446Smrj 	 * one, we don't want to mess with it.  Just print an error and
37873446Smrj 	 * return.
37883446Smrj 	 */
37893446Smrj 	if (mp->curdefault) {
37903446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
37913446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
37923446Smrj 			if (entryp->entryNum == entryNum)
37933446Smrj 				break;
37943446Smrj 		}
37953446Smrj 		if ((entryp != NULL) &&
37963446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
37973446Smrj 			bam_error(DEFAULT_NOT_BAM);
37983446Smrj 			return (BAM_ERROR);
37993446Smrj 		}
38003446Smrj 	}
38013446Smrj 
38023446Smrj 	entryNum = -1;
38033446Smrj 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, 0,
38043446Smrj 	    &entryNum);
38053446Smrj 
38063446Smrj 	if (entryp != NULL) {
38073446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
38083446Smrj 		    ptr = ptr->next) {
38093446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
38103446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
38113446Smrj 				kernelp = ptr;
38123446Smrj 				break;
38133446Smrj 			}
38143446Smrj 		}
38153446Smrj 		if (kernelp == NULL) {
38163446Smrj 			bam_error(NO_KERNEL, entryNum);
38173446Smrj 			return (BAM_ERROR);
38183446Smrj 		}
38193446Smrj 
38203446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
38213446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
38223446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
38233446Smrj 			old_args++;
38243446Smrj 	}
38253446Smrj 
38263446Smrj 	if (path == NULL) {
38273446Smrj 		/* Simply report what was found */
38283446Smrj 		if (kernelp == NULL)
38293446Smrj 			return (BAM_SUCCESS);
38303446Smrj 
38313446Smrj 		if (optnum == ARGS_CMD) {
38323446Smrj 			if (old_args[0] != '\0')
38333446Smrj 				(void) strlcpy(buf, old_args, bufsize);
38343446Smrj 		} else {
38353446Smrj 			/*
38363446Smrj 			 * We need to print the kernel, so we just turn the
38373446Smrj 			 * first space into a '\0' and print the beginning.
38383446Smrj 			 * We don't print anything if it's the default kernel.
38393446Smrj 			 */
38403446Smrj 			old_space = *space;
38413446Smrj 			*space = '\0';
38423446Smrj 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
38433446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
38443446Smrj 			*space = old_space;
38453446Smrj 		}
38463446Smrj 		return (BAM_SUCCESS);
38473446Smrj 	}
38483446Smrj 
38493446Smrj 	/*
38503446Smrj 	 * First, check if we're resetting an entry to the default.
38513446Smrj 	 */
38523446Smrj 	if ((path[0] == '\0') ||
38533446Smrj 	    ((optnum == KERNEL_CMD) &&
38543446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
38553446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
38563446Smrj 			/* No previous entry, it's already the default */
38573446Smrj 			return (BAM_SUCCESS);
38583446Smrj 		}
38593446Smrj 
38603446Smrj 		/*
38613446Smrj 		 * Check if we can delete the entry.  If we're resetting the
38623446Smrj 		 * kernel command, and the args is already empty, or if we're
38633446Smrj 		 * resetting the args command, and the kernel is already the
38643446Smrj 		 * default, we can restore the old default and delete the entry.
38653446Smrj 		 */
38663446Smrj 		if (((optnum == KERNEL_CMD) &&
38673446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
38683446Smrj 		    ((optnum == ARGS_CMD) &&
38693446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
38703446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
38713446Smrj 			kernelp = NULL;
38723446Smrj 			(void) do_delete(mp, entryNum);
38733446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
38743446Smrj 			    mp->old_rc_default);
38753446Smrj 			mp->old_rc_default = NULL;
38763446Smrj 			rv = BAM_WRITE;
38773446Smrj 			goto done;
38783446Smrj 		}
38793446Smrj 
38803446Smrj 		if (optnum == KERNEL_CMD) {
38813446Smrj 			/*
38823446Smrj 			 * At this point, we've already checked that old_args
38833446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
38843446Smrj 			 * a space a the string termination character.
38853446Smrj 			 */
38863446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
38873446Smrj 			    strlen(old_args) + 2;
38883446Smrj 			new_arg = s_calloc(1, new_str_len);
38893446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
38903446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
38913446Smrj 			free(kernelp->arg);
38923446Smrj 			kernelp->arg = new_arg;
38933446Smrj 
38943446Smrj 			/*
38953446Smrj 			 * We have changed the kernel line, so we may need
38963446Smrj 			 * to update the archive line as well.
38973446Smrj 			 */
38983446Smrj 			set_archive_line(entryp, kernelp);
38993446Smrj 		} else {
39003446Smrj 			/*
39013446Smrj 			 * We're resetting the boot args to nothing, so
39023446Smrj 			 * we only need to copy the kernel.  We've already
39033446Smrj 			 * checked that the kernel is not the default.
39043446Smrj 			 */
39053446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
39063446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
39073446Smrj 			    kernelp->arg);
39083446Smrj 			free(kernelp->arg);
39093446Smrj 			kernelp->arg = new_arg;
39103446Smrj 		}
39113446Smrj 		rv = BAM_WRITE;
39123446Smrj 		goto done;
39133446Smrj 	}
39143446Smrj 
39153446Smrj 	/*
39163446Smrj 	 * Expand the kernel file to a full path, if necessary
39173446Smrj 	 */
39183446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
39193446Smrj 		new_path = expand_path(path);
39203446Smrj 		if (new_path == NULL) {
39213446Smrj 			return (BAM_ERROR);
39223446Smrj 		}
39233446Smrj 		free_new_path = 1;
39243446Smrj 	} else {
39253446Smrj 		new_path = path;
39263446Smrj 		free_new_path = 0;
39273446Smrj 	}
39283446Smrj 
39293446Smrj 	/*
39303446Smrj 	 * At this point, we know we're setting a new value.  First, take care
39313446Smrj 	 * of the case where there was no previous entry.
39323446Smrj 	 */
39333446Smrj 	if (entryp == NULL) {
39343446Smrj 		/* Similar to code in update_temp */
39353446Smrj 		if (stat(GRUB_slice, &sb) != 0) {
39363446Smrj 			/*
39373446Smrj 			 * 1. First get root disk name from mnttab
39383446Smrj 			 * 2. Translate disk name to grub name
39393446Smrj 			 * 3. Add the new menu entry
39403446Smrj 			 */
39413446Smrj 			rootdev = get_special("/");
39423446Smrj 			if (rootdev) {
39433446Smrj 				grubdisk = os_to_grubdisk(rootdev, 1);
39443446Smrj 				free(rootdev);
39453446Smrj 			}
39463446Smrj 		} else {
39473446Smrj 			/*
39483446Smrj 			 * This is an LU BE. The GRUB_root file
39493446Smrj 			 * contains entry for GRUB's "root" cmd.
39503446Smrj 			 */
39513446Smrj 			grubdisk = read_grub_root();
39523446Smrj 		}
39533446Smrj 		if (grubdisk == NULL) {
39543446Smrj 			bam_error(REBOOT_WITH_ARGS_FAILED);
39553446Smrj 			rv = BAM_ERROR;
39563446Smrj 			goto done;
39573446Smrj 		}
39583446Smrj 		if (optnum == KERNEL_CMD) {
39593446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
39603446Smrj 			    grubdisk, new_path, NULL);
39613446Smrj 		} else {
39623446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
39633446Smrj 			    strlen(path) + 8;
39643446Smrj 			new_arg = s_calloc(1, new_str_len);
39653446Smrj 
39663446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
39673446Smrj 			    DIRECT_BOOT_KERNEL, path);
39683446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
39693446Smrj 			    grubdisk, new_arg, DIRECT_BOOT_ARCHIVE);
39703446Smrj 		}
39713446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
39723446Smrj 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
39733446Smrj 		rv = BAM_WRITE;
39743446Smrj 		goto done;
39753446Smrj 	}
39763446Smrj 
39773446Smrj 	/*
39783446Smrj 	 * There was already an bootenv entry which we need to edit.
39793446Smrj 	 */
39803446Smrj 	if (optnum == KERNEL_CMD) {
39813446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
39823446Smrj 		new_arg = s_calloc(1, new_str_len);
39833446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
39843446Smrj 		    old_args);
39853446Smrj 		free(kernelp->arg);
39863446Smrj 		kernelp->arg = new_arg;
39873446Smrj 
39883446Smrj 		/*
39893446Smrj 		 * If we have changed the kernel line, we may need to update
39903446Smrj 		 * the archive line as well.
39913446Smrj 		 */
39923446Smrj 		set_archive_line(entryp, kernelp);
39933446Smrj 	} else {
39943446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
39953446Smrj 		new_arg = s_calloc(1, new_str_len);
39963446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
39973446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
39983446Smrj 		(void) strlcat(new_arg, path, new_str_len);
39993446Smrj 		free(kernelp->arg);
40003446Smrj 		kernelp->arg = new_arg;
40013446Smrj 	}
40023446Smrj 	rv = BAM_WRITE;
40033446Smrj 
40043446Smrj done:
40053446Smrj 	if ((rv == BAM_WRITE) && kernelp)
40063446Smrj 		update_line(kernelp);
40073446Smrj 	if (free_new_path)
40083446Smrj 		free(new_path);
40093446Smrj 	return (rv);
40103446Smrj }
40113446Smrj 
40120Sstevel@tonic-gate /*ARGSUSED*/
40130Sstevel@tonic-gate static error_t
40140Sstevel@tonic-gate set_option(menu_t *mp, char *menu_path, char *opt)
40150Sstevel@tonic-gate {
40160Sstevel@tonic-gate 	int optnum, optval;
40170Sstevel@tonic-gate 	char *val;
40183446Smrj 	char buf[BUFSIZ] = "";
40193446Smrj 	error_t rv;
40200Sstevel@tonic-gate 
40210Sstevel@tonic-gate 	assert(mp);
40220Sstevel@tonic-gate 	assert(opt);
40230Sstevel@tonic-gate 
40240Sstevel@tonic-gate 	val = strchr(opt, '=');
40253446Smrj 	if (val != NULL) {
40263446Smrj 		*val = '\0';
40270Sstevel@tonic-gate 	}
40280Sstevel@tonic-gate 
40290Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
40300Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
40310Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
40320Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
40333446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
40343446Smrj 		optnum = KERNEL_CMD;
40353446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
40363446Smrj 		optnum = ARGS_CMD;
40370Sstevel@tonic-gate 	} else {
40380Sstevel@tonic-gate 		bam_error(INVALID_ENTRY, opt);
40390Sstevel@tonic-gate 		return (BAM_ERROR);
40400Sstevel@tonic-gate 	}
40410Sstevel@tonic-gate 
40423446Smrj 	/*
40433446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
40443446Smrj 	 * others cause errors
40453446Smrj 	 */
40463446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
40473446Smrj 		bam_error(INVALID_ENTRY, opt);
40483446Smrj 		return (BAM_ERROR);
40493446Smrj 	} else if (val != NULL) {
40503446Smrj 		*val = '=';
40513446Smrj 	}
40523446Smrj 
40533446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
40543446Smrj 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
40553446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
40563446Smrj 			(void) printf("%s\n", buf);
40573446Smrj 		return (rv);
40583446Smrj 	} else {
40593446Smrj 		optval = s_strtol(val + 1);
40603446Smrj 		return (set_global(mp, menu_cmds[optnum], optval));
40613446Smrj 	}
40620Sstevel@tonic-gate }
40630Sstevel@tonic-gate 
40640Sstevel@tonic-gate /*
40650Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
40660Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
40670Sstevel@tonic-gate  */
40680Sstevel@tonic-gate static error_t
40690Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
40700Sstevel@tonic-gate {
40710Sstevel@tonic-gate 	line_t *lp;
40720Sstevel@tonic-gate 	char *arg;
40730Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
40740Sstevel@tonic-gate 
40750Sstevel@tonic-gate 	assert(mp);
40760Sstevel@tonic-gate 	assert(menu_path);
40770Sstevel@tonic-gate 	assert(globalcmd);
40780Sstevel@tonic-gate 
40790Sstevel@tonic-gate 	if (mp->start == NULL) {
40800Sstevel@tonic-gate 		if (!quiet)
40810Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
40820Sstevel@tonic-gate 		return (BAM_ERROR);
40830Sstevel@tonic-gate 	}
40840Sstevel@tonic-gate 
40850Sstevel@tonic-gate 	done = 0;
40860Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
40870Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
40880Sstevel@tonic-gate 			continue;
40890Sstevel@tonic-gate 
40900Sstevel@tonic-gate 		if (lp->cmd == NULL) {
40910Sstevel@tonic-gate 			if (!quiet)
40920Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
40930Sstevel@tonic-gate 			continue;
40940Sstevel@tonic-gate 		}
40950Sstevel@tonic-gate 
40960Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
40970Sstevel@tonic-gate 			continue;
40980Sstevel@tonic-gate 
40990Sstevel@tonic-gate 		/* Found global. Check for duplicates */
41000Sstevel@tonic-gate 		if (done && !quiet) {
41010Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
41020Sstevel@tonic-gate 			ret = BAM_ERROR;
41030Sstevel@tonic-gate 		}
41040Sstevel@tonic-gate 
41050Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
41060Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
41070Sstevel@tonic-gate 		done = 1;
41080Sstevel@tonic-gate 	}
41090Sstevel@tonic-gate 
41100Sstevel@tonic-gate 	if (!done && bam_verbose)
41110Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
41120Sstevel@tonic-gate 
41130Sstevel@tonic-gate 	return (ret);
41140Sstevel@tonic-gate }
41150Sstevel@tonic-gate 
41160Sstevel@tonic-gate static error_t
41170Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
41180Sstevel@tonic-gate {
41190Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
41200Sstevel@tonic-gate }
41210Sstevel@tonic-gate 
41220Sstevel@tonic-gate static void
41230Sstevel@tonic-gate line_free(line_t *lp)
41240Sstevel@tonic-gate {
41250Sstevel@tonic-gate 	if (lp == NULL)
41260Sstevel@tonic-gate 		return;
41270Sstevel@tonic-gate 
41280Sstevel@tonic-gate 	if (lp->cmd)
41290Sstevel@tonic-gate 		free(lp->cmd);
41300Sstevel@tonic-gate 	if (lp->sep)
41310Sstevel@tonic-gate 		free(lp->sep);
41320Sstevel@tonic-gate 	if (lp->arg)
41330Sstevel@tonic-gate 		free(lp->arg);
41340Sstevel@tonic-gate 	if (lp->line)
41350Sstevel@tonic-gate 		free(lp->line);
41360Sstevel@tonic-gate 	free(lp);
41370Sstevel@tonic-gate }
41380Sstevel@tonic-gate 
41390Sstevel@tonic-gate static void
41400Sstevel@tonic-gate linelist_free(line_t *start)
41410Sstevel@tonic-gate {
41420Sstevel@tonic-gate 	line_t *lp;
41430Sstevel@tonic-gate 
41440Sstevel@tonic-gate 	while (start) {
41450Sstevel@tonic-gate 		lp = start;
41460Sstevel@tonic-gate 		start = start->next;
41470Sstevel@tonic-gate 		line_free(lp);
41480Sstevel@tonic-gate 	}
41490Sstevel@tonic-gate }
41500Sstevel@tonic-gate 
41510Sstevel@tonic-gate static void
41520Sstevel@tonic-gate filelist_free(filelist_t *flistp)
41530Sstevel@tonic-gate {
41540Sstevel@tonic-gate 	linelist_free(flistp->head);
41550Sstevel@tonic-gate 	flistp->head = NULL;
41560Sstevel@tonic-gate 	flistp->tail = NULL;
41570Sstevel@tonic-gate }
41580Sstevel@tonic-gate 
41590Sstevel@tonic-gate static void
41600Sstevel@tonic-gate menu_free(menu_t *mp)
41610Sstevel@tonic-gate {
4162662Sszhou 	entry_t *ent, *tmp;
41630Sstevel@tonic-gate 	assert(mp);
41640Sstevel@tonic-gate 
41650Sstevel@tonic-gate 	if (mp->start)
41660Sstevel@tonic-gate 		linelist_free(mp->start);
4167662Sszhou 	ent = mp->entries;
4168662Sszhou 	while (ent) {
4169662Sszhou 		tmp = ent;
4170662Sszhou 		ent = tmp->next;
4171662Sszhou 		free(tmp);
4172662Sszhou 	}
4173662Sszhou 
41740Sstevel@tonic-gate 	free(mp);
41750Sstevel@tonic-gate }
41760Sstevel@tonic-gate 
41770Sstevel@tonic-gate /*
41780Sstevel@tonic-gate  * Utility routines
41790Sstevel@tonic-gate  */
41800Sstevel@tonic-gate 
41810Sstevel@tonic-gate 
41820Sstevel@tonic-gate /*
41830Sstevel@tonic-gate  * Returns 0 on success
41840Sstevel@tonic-gate  * Any other value indicates an error
41850Sstevel@tonic-gate  */
41860Sstevel@tonic-gate static int
41870Sstevel@tonic-gate exec_cmd(char *cmdline, char *output, int64_t osize)
41880Sstevel@tonic-gate {
41890Sstevel@tonic-gate 	char buf[BUFSIZ];
41900Sstevel@tonic-gate 	int ret;
41910Sstevel@tonic-gate 	FILE *ptr;
41920Sstevel@tonic-gate 	size_t len;
41930Sstevel@tonic-gate 	sigset_t set;
41940Sstevel@tonic-gate 	void (*disp)(int);
41950Sstevel@tonic-gate 
41960Sstevel@tonic-gate 	/*
41970Sstevel@tonic-gate 	 * For security
41980Sstevel@tonic-gate 	 * - only absolute paths are allowed
41990Sstevel@tonic-gate 	 * - set IFS to space and tab
42000Sstevel@tonic-gate 	 */
42010Sstevel@tonic-gate 	if (*cmdline != '/') {
42020Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
42030Sstevel@tonic-gate 		return (-1);
42040Sstevel@tonic-gate 	}
42050Sstevel@tonic-gate 	(void) putenv("IFS= \t");
42060Sstevel@tonic-gate 
42070Sstevel@tonic-gate 	/*
42080Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
42090Sstevel@tonic-gate 	 * unblock it here
42100Sstevel@tonic-gate 	 */
42110Sstevel@tonic-gate 	(void) sigemptyset(&set);
42120Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
42130Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
42140Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
42150Sstevel@tonic-gate 		return (-1);
42160Sstevel@tonic-gate 	}
42170Sstevel@tonic-gate 
42180Sstevel@tonic-gate 	/*
42190Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
42200Sstevel@tonic-gate 	 */
42210Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
42220Sstevel@tonic-gate 	if (disp == SIG_ERR) {
42230Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
42240Sstevel@tonic-gate 		return (-1);
42250Sstevel@tonic-gate 	}
42260Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
42270Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
42280Sstevel@tonic-gate 		return (-1);
42290Sstevel@tonic-gate 	}
42300Sstevel@tonic-gate 
42310Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
42320Sstevel@tonic-gate 	if (ptr == NULL) {
42330Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
42340Sstevel@tonic-gate 		return (-1);
42350Sstevel@tonic-gate 	}
42360Sstevel@tonic-gate 
42370Sstevel@tonic-gate 	/*
42380Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
42390Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
42400Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
42410Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
42420Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
42430Sstevel@tonic-gate 	 * there is no reader process and the command dies with
42440Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
42450Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
42460Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
42470Sstevel@tonic-gate 	 * pclose().
42480Sstevel@tonic-gate 	 *
42490Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
42500Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
42510Sstevel@tonic-gate 	 * from the value returned by pclose()
42520Sstevel@tonic-gate 	 */
42530Sstevel@tonic-gate 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
42540Sstevel@tonic-gate 		/* if (bam_verbose)  XXX */
42550Sstevel@tonic-gate 			bam_print(PRINT_NO_NEWLINE, buf);
42560Sstevel@tonic-gate 		if (output && osize > 0) {
42570Sstevel@tonic-gate 			(void) snprintf(output, osize, "%s", buf);
42580Sstevel@tonic-gate 			len = strlen(buf);
42590Sstevel@tonic-gate 			output += len;
42600Sstevel@tonic-gate 			osize -= len;
42610Sstevel@tonic-gate 		}
42620Sstevel@tonic-gate 	}
42630Sstevel@tonic-gate 
42640Sstevel@tonic-gate 	ret = pclose(ptr);
42650Sstevel@tonic-gate 	if (ret == -1) {
42660Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
42670Sstevel@tonic-gate 		return (-1);
42680Sstevel@tonic-gate 	}
42690Sstevel@tonic-gate 
42700Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
42710Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
42720Sstevel@tonic-gate 	} else {
42730Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
42740Sstevel@tonic-gate 		return (-1);
42750Sstevel@tonic-gate 	}
42760Sstevel@tonic-gate }
42770Sstevel@tonic-gate 
42780Sstevel@tonic-gate /*
42790Sstevel@tonic-gate  * Since this function returns -1 on error
42800Sstevel@tonic-gate  * it cannot be used to convert -1. However,
42810Sstevel@tonic-gate  * that is sufficient for what we need.
42820Sstevel@tonic-gate  */
42830Sstevel@tonic-gate static long
42840Sstevel@tonic-gate s_strtol(char *str)
42850Sstevel@tonic-gate {
42860Sstevel@tonic-gate 	long l;
42870Sstevel@tonic-gate 	char *res = NULL;
42880Sstevel@tonic-gate 
42890Sstevel@tonic-gate 	if (str == NULL) {
42900Sstevel@tonic-gate 		return (-1);
42910Sstevel@tonic-gate 	}
42920Sstevel@tonic-gate 
42930Sstevel@tonic-gate 	errno = 0;
42940Sstevel@tonic-gate 	l = strtol(str, &res, 10);
42950Sstevel@tonic-gate 	if (errno || *res != '\0') {
42960Sstevel@tonic-gate 		return (-1);
42970Sstevel@tonic-gate 	}
42980Sstevel@tonic-gate 
42990Sstevel@tonic-gate 	return (l);
43000Sstevel@tonic-gate }
43010Sstevel@tonic-gate 
43020Sstevel@tonic-gate /*
43030Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
43040Sstevel@tonic-gate  */
43050Sstevel@tonic-gate static int
43060Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
43070Sstevel@tonic-gate {
43080Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
43090Sstevel@tonic-gate 
43100Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
43110Sstevel@tonic-gate 	return (fputs(linebuf, fp));
43120Sstevel@tonic-gate }
43130Sstevel@tonic-gate 
43140Sstevel@tonic-gate /*
43150Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
43160Sstevel@tonic-gate  */
43173446Smrj char *
43180Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
43190Sstevel@tonic-gate {
43200Sstevel@tonic-gate 	int n;
43210Sstevel@tonic-gate 
43220Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
43230Sstevel@tonic-gate 	if (buf) {
43240Sstevel@tonic-gate 		n = strlen(buf);
43250Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
43260Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
43270Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
43280Sstevel@tonic-gate 	}
43290Sstevel@tonic-gate 
43300Sstevel@tonic-gate 	return (buf);
43310Sstevel@tonic-gate }
43320Sstevel@tonic-gate 
43333446Smrj void *
43340Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
43350Sstevel@tonic-gate {
43360Sstevel@tonic-gate 	void *ptr;
43370Sstevel@tonic-gate 
43380Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
43390Sstevel@tonic-gate 	if (ptr == NULL) {
43400Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
43410Sstevel@tonic-gate 		bam_exit(1);
43420Sstevel@tonic-gate 	}
43430Sstevel@tonic-gate 	return (ptr);
43440Sstevel@tonic-gate }
43450Sstevel@tonic-gate 
43463446Smrj void *
43473446Smrj s_realloc(void *ptr, size_t sz)
43483446Smrj {
43493446Smrj 	ptr = realloc(ptr, sz);
43503446Smrj 	if (ptr == NULL) {
43513446Smrj 		bam_error(NO_MEM, sz);
43523446Smrj 		bam_exit(1);
43533446Smrj 	}
43543446Smrj 	return (ptr);
43553446Smrj }
43563446Smrj 
43570Sstevel@tonic-gate static char *
43580Sstevel@tonic-gate s_strdup(char *str)
43590Sstevel@tonic-gate {
43600Sstevel@tonic-gate 	char *ptr;
43610Sstevel@tonic-gate 
43620Sstevel@tonic-gate 	if (str == NULL)
43630Sstevel@tonic-gate 		return (NULL);
43640Sstevel@tonic-gate 
43650Sstevel@tonic-gate 	ptr = strdup(str);
43660Sstevel@tonic-gate 	if (ptr == NULL) {
43670Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
43680Sstevel@tonic-gate 		bam_exit(1);
43690Sstevel@tonic-gate 	}
43700Sstevel@tonic-gate 	return (ptr);
43710Sstevel@tonic-gate }
43720Sstevel@tonic-gate 
43730Sstevel@tonic-gate /*
43740Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
43750Sstevel@tonic-gate  * Returns 0 otherwise
43760Sstevel@tonic-gate  */
43770Sstevel@tonic-gate static int
43780Sstevel@tonic-gate is_amd64(void)
43790Sstevel@tonic-gate {
43800Sstevel@tonic-gate 	static int amd64 = -1;
43810Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
43820Sstevel@tonic-gate 
43830Sstevel@tonic-gate 	if (amd64 != -1)
43840Sstevel@tonic-gate 		return (amd64);
43850Sstevel@tonic-gate 
43860Sstevel@tonic-gate 	if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
43870Sstevel@tonic-gate 	    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0)
43880Sstevel@tonic-gate 		amd64 = 1;
43890Sstevel@tonic-gate 	else if (strstr(isabuf, "i386") == NULL)
43900Sstevel@tonic-gate 		amd64 = 1;		/* diskless server */
43910Sstevel@tonic-gate 	else
43920Sstevel@tonic-gate 		amd64 = 0;
43930Sstevel@tonic-gate 
43940Sstevel@tonic-gate 	return (amd64);
43950Sstevel@tonic-gate }
43960Sstevel@tonic-gate 
43970Sstevel@tonic-gate static void
43980Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
43990Sstevel@tonic-gate {
44000Sstevel@tonic-gate 	line_t *lp;
44010Sstevel@tonic-gate 
44020Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
44030Sstevel@tonic-gate 	lp->line = s_strdup(s);
44040Sstevel@tonic-gate 	if (flistp->head == NULL)
44050Sstevel@tonic-gate 		flistp->head = lp;
44060Sstevel@tonic-gate 	else
44070Sstevel@tonic-gate 		flistp->tail->next = lp;
44080Sstevel@tonic-gate 	flistp->tail = lp;
44090Sstevel@tonic-gate }
4410