xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 3446:5903aece022d)
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"
66*3446Smrj #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  */
134*3446Smrj 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 */
140*3446Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1410Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
142*3446Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1430Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1440Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
145*3446Smrj 	"chainloader",	/* CHAINLOADER_CMD */
146*3446Smrj 	"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 */
169*3446Smrj int bam_verbose;
170*3446Smrj 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);
222*3446Smrj static error_t set_kernel(menu_t *mp, menu_cmd_t optnum, char *path,
223*3446Smrj     char *buf, size_t bufsize);
224*3446Smrj 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 */
245*3446Smrj 	"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 
269*3446Smrj 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*3446Smrj 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:xX")) != -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];
886*3446Smrj 	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 	/*
906*3446Smrj 	 * Check for the menu.list file:
907*3446Smrj 	 *
908*3446Smrj 	 * 1. Check for a GRUB_slice file, be it on / or
909*3446Smrj 	 *    on the user-provided alternate root.
910*3446Smrj 	 * 2. Use the alternate root, if given.
911*3446Smrj 	 * 3. Check /stubboot
912*3446Smrj 	 * 4. Use /
913621Svikram 	 */
914621Svikram 	if (bam_alt_root) {
915*3446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
916*3446Smrj 		    GRUB_slice);
917*3446Smrj 	} else {
918*3446Smrj 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
919*3446Smrj 	}
920*3446Smrj 
921*3446Smrj 	if (stat(path, &sb) == 0) {
922621Svikram 		mntpt = mount_grub_slice(&mnted, NULL, &logslice, &fstype);
923316Svikram 		menu_root = mntpt;
924*3446Smrj 	} else if (bam_alt_root) {
925*3446Smrj 		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 
969*3446Smrj 	ret = dboot_or_multiboot(bam_root);
970*3446Smrj 	if (ret != BAM_SUCCESS)
971*3446Smrj 		return (ret);
972*3446Smrj 
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 	 */
977*3446Smrj 	if ((strcmp(subcmd, "update_entry") == 0) ||
978*3446Smrj 	    (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 
1027*3446Smrj 	ret = dboot_or_multiboot(rootbuf);
1028*3446Smrj 	if (ret != BAM_SUCCESS)
1029*3446Smrj 		return (ret);
1030*3446Smrj 
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*/
1052*3446Smrj 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 
1074*3446Smrj /*PRINTFLIKE1*/
1075*3446Smrj void
1076*3446Smrj bam_print_stderr(char *format, ...)
1077*3446Smrj {
1078*3446Smrj 	va_list ap;
1079*3446Smrj 
1080*3446Smrj 	va_start(ap, format);
1081*3446Smrj 	(void) vfprintf(stderr, format, ap);
1082*3446Smrj 	va_end(ap);
1083*3446Smrj }
1084*3446Smrj 
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 	 */
1475*3446Smrj 	(void) snprintf(path, sizeof (path), "%s%s", root,
1476*3446Smrj 	    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 	}
1483*3446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
1484*3446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", root,
1485*3446Smrj 		    DIRECT_BOOT_ARCHIVE_64);
1486*3446Smrj 		if (stat(path, &sb) != 0) {
1487*3446Smrj 			if (bam_verbose && !bam_check)
1488*3446Smrj 				bam_print(UPDATE_ARCH_MISS, path);
1489*3446Smrj 			walk_arg.need_update = 1;
1490*3446Smrj 			return;
1491*3446Smrj 		}
1492*3446Smrj 	}
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 	 */
1876*3446Smrj 	(void) snprintf(path, sizeof (path), "%s%s", root,
1877*3446Smrj 	    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 	}
1882*3446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
1883*3446Smrj 		(void) snprintf(path, sizeof (path), "%s%s", root,
1884*3446Smrj 		    DIRECT_BOOT_ARCHIVE_64);
1885*3446Smrj 		if (stat(path, &sb) != 0) {
1886*3446Smrj 			bam_error(ARCHIVE_NOT_CREATED, path);
1887*3446Smrj 			return (BAM_ERROR);
1888*3446Smrj 		}
1889*3446Smrj 	}
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);
2242*3446Smrj 
2243*3446Smrj 		/*
2244*3446Smrj 		 * It's possible that other mounts may be an alternate boot
2245*3446Smrj 		 * architecture, so check it again.
2246*3446Smrj 		 */
2247*3446Smrj 		if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) ||
2248*3446Smrj 		    (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;
2352*3446Smrj 	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;
2378*3446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
2379*3446Smrj 			in_liveupgrade = 1;
2380*3446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
2381*3446Smrj 			in_liveupgrade = 0;
2382*3446Smrj 		}
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 &&
2435*3446Smrj 		    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);
2438*3446Smrj 			curr_ent->flags = BAM_ENTRY_BOOTADM;
2439662Sszhou 		} else {
2440662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
2441*3446Smrj 			if (in_liveupgrade) {
2442*3446Smrj 				curr_ent->flags = BAM_ENTRY_LU;
2443*3446Smrj 			}
2444662Sszhou 		}
2445*3446Smrj 		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;
2455*3446Smrj 
2456*3446Smrj 		if (*entryNum == ENTRY_INIT) {
2457*3446Smrj 			lp->flags = BAM_GLOBAL;
2458*3446Smrj 		} else {
2459*3446Smrj 			lp->flags = BAM_ENTRY;
2460*3446Smrj 
2461*3446Smrj 			if (cmd && arg) {
2462*3446Smrj 				/*
2463*3446Smrj 				 * We only compare for the length of "module"
2464*3446Smrj 				 * so that "module$" will also match.
2465*3446Smrj 				 */
2466*3446Smrj 				if ((strncmp(cmd, menu_cmds[MODULE_CMD],
2467*3446Smrj 				    strlen(menu_cmds[MODULE_CMD])) == 0) &&
2468*3446Smrj 				    (strcmp(arg, MINIROOT) == 0))
2469*3446Smrj 					curr_ent->flags |= BAM_ENTRY_MINIROOT;
2470*3446Smrj 				else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0)
2471*3446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
2472*3446Smrj 				else if (strcmp(cmd,
2473*3446Smrj 				    menu_cmds[CHAINLOADER_CMD]) == 0)
2474*3446Smrj 					curr_ent->flags |=
2475*3446Smrj 					    BAM_ENTRY_CHAINLOADER;
2476*3446Smrj 			}
2477*3446Smrj 		}
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;
2487*3446Smrj 	} else if (lp->flags == BAM_COMMENT &&
2488*3446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
2489*3446Smrj 		mp->old_rc_default = lp;
2490662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
2491*3446Smrj 	    (lp->flags == BAM_COMMENT &&
2492*3446Smrj 	    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 &&
2559*3446Smrj 			    prev->arg &&
2560*3446Smrj 			    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];
2765*3446Smrj 	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) {
2777*3446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
2778*3446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
2779*3446Smrj 			return (BAM_ERROR);
2780*3446Smrj 		}
2781*3446Smrj 
2782*3446Smrj 		/* Figure the commands out from the kernel line */
2783*3446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
2784*3446Smrj 			module = DIRECT_BOOT_ARCHIVE;
2785*3446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
2786*3446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
2787*3446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
2788*3446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
2789*3446Smrj 			k_cmd = KERNEL_CMD;
2790*3446Smrj 			m_cmd = MODULE_CMD;
2791*3446Smrj 		} else {
2792*3446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
2793*3446Smrj 			k_cmd = KERNEL_CMD;
2794*3446Smrj 			m_cmd = MODULE_CMD;
2795*3446Smrj 		}
2796*3446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
2797*3446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
2798*3446Smrj 		/*
2799*3446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
2800*3446Smrj 		 * command.  Otherwise, use "kernel".
2801*3446Smrj 		 */
2802*3446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
2803*3446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
2804*3446Smrj 	} else {
2805*3446Smrj 		k_cmd = KERNEL_CMD;
2806*3446Smrj 		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",
2822*3446Smrj 	    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",
2836*3446Smrj 	    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",
2840*3446Smrj 	    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",
2844*3446Smrj 	    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 ||
2864*3446Smrj 		    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 
3070*3446Smrj 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 
3094*3446Smrj 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;
3159*3446Smrj 		if (lp->flags != BAM_COMMENT ||
3160*3446Smrj 		    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 
3191662Sszhou 		/* check for matching module entry (failsafe or normal) */
3192662Sszhou 		lp = lp->next;	/* advance to module line */
3193*3446Smrj 		if ((strncmp(lp->cmd, menu_cmds[MODULE_CMD],
3194*3446Smrj 		    strlen(menu_cmds[MODULE_CMD])) != 0) ||
3195*3446Smrj 		    (strcmp(lp->arg, module) != 0)) {
3196662Sszhou 			continue;
3197662Sszhou 		}
3198662Sszhou 		break;	/* match found */
3199662Sszhou 	}
3200662Sszhou 
3201662Sszhou 	*entry_num = i;
3202662Sszhou 	return (ent);
3203662Sszhou }
3204662Sszhou 
3205662Sszhou static int
3206662Sszhou update_boot_entry(menu_t *mp, char *title, char *root, char *kernel,
3207662Sszhou     char *module, int root_opt)
3208662Sszhou {
3209*3446Smrj 	int i, change_kernel = 0;
3210662Sszhou 	entry_t *ent;
3211662Sszhou 	line_t *lp;
3212662Sszhou 	char linebuf[BAM_MAXLINE];
3213662Sszhou 
3214662Sszhou 	/* note: don't match on title, it's updated on upgrade */
3215662Sszhou 	ent = find_boot_entry(mp, NULL, root, module, root_opt, &i);
3216*3446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
3217*3446Smrj 		/*
3218*3446Smrj 		 * We may be upgrading a kernel from multiboot to
3219*3446Smrj 		 * directboot.  Look for a multiboot entry.
3220*3446Smrj 		 */
3221*3446Smrj 		ent = find_boot_entry(mp, NULL, root, MULTI_BOOT_ARCHIVE,
3222*3446Smrj 		    root_opt, &i);
3223*3446Smrj 		if (ent != NULL) {
3224*3446Smrj 			change_kernel = 1;
3225*3446Smrj 		}
3226*3446Smrj 	}
3227662Sszhou 	if (ent == NULL)
3228662Sszhou 		return (add_boot_entry(mp, title, root_opt ? NULL : root,
3229662Sszhou 		    kernel, module));
3230662Sszhou 
3231662Sszhou 	/* replace title of exiting entry and delete root line */
3232662Sszhou 	lp = ent->start;
3233662Sszhou 	lp = lp->next;	/* title line */
3234662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3235662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3236662Sszhou 	free(lp->arg);
3237662Sszhou 	free(lp->line);
3238662Sszhou 	lp->arg = s_strdup(title);
3239662Sszhou 	lp->line = s_strdup(linebuf);
3240662Sszhou 
3241662Sszhou 	lp = lp->next;	/* root line */
3242662Sszhou 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
3243662Sszhou 		if (root_opt) {		/* root line not needed */
3244662Sszhou 			line_t *tmp = lp;
3245662Sszhou 			lp = lp->next;
3246662Sszhou 			unlink_line(mp, tmp);
3247662Sszhou 			line_free(tmp);
3248662Sszhou 		} else
3249662Sszhou 			lp = lp->next;
3250662Sszhou 	}
3251*3446Smrj 
3252*3446Smrj 	if (change_kernel) {
3253*3446Smrj 		/*
3254*3446Smrj 		 * We're upgrading from multiboot to directboot.
3255*3446Smrj 		 */
3256*3446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
3257*3446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3258*3446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
3259*3446Smrj 			    kernel);
3260*3446Smrj 			free(lp->arg);
3261*3446Smrj 			free(lp->line);
3262*3446Smrj 			lp->arg = s_strdup(kernel);
3263*3446Smrj 			lp->line = s_strdup(linebuf);
3264*3446Smrj 			lp = lp->next;
3265*3446Smrj 		}
3266*3446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
3267*3446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
3268*3446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
3269*3446Smrj 			    module);
3270*3446Smrj 			free(lp->arg);
3271*3446Smrj 			free(lp->line);
3272*3446Smrj 			lp->arg = s_strdup(module);
3273*3446Smrj 			lp->line = s_strdup(linebuf);
3274*3446Smrj 			lp = lp->next;
3275*3446Smrj 		}
3276*3446Smrj 	}
3277662Sszhou 	return (i);
3278662Sszhou }
3279662Sszhou 
32800Sstevel@tonic-gate /*ARGSUSED*/
32810Sstevel@tonic-gate static error_t
32820Sstevel@tonic-gate update_entry(menu_t *mp, char *menu_root, char *opt)
32830Sstevel@tonic-gate {
32840Sstevel@tonic-gate 	FILE *fp;
32850Sstevel@tonic-gate 	int entry;
32860Sstevel@tonic-gate 	char *grubdisk, *title, *osdev, *osroot;
3287662Sszhou 	struct stat sbuf;
3288662Sszhou 	char failsafe[256];
32890Sstevel@tonic-gate 
32900Sstevel@tonic-gate 	assert(mp);
32910Sstevel@tonic-gate 	assert(opt);
32920Sstevel@tonic-gate 
32930Sstevel@tonic-gate 	osdev = strtok(opt, ",");
32940Sstevel@tonic-gate 	osroot = strtok(NULL, ",");
32950Sstevel@tonic-gate 	if (osroot == NULL)
32960Sstevel@tonic-gate 		osroot = menu_root;
32970Sstevel@tonic-gate 	title = get_title(osroot);
32980Sstevel@tonic-gate 
32990Sstevel@tonic-gate 	/* translate /dev/dsk name to grub disk name */
3300662Sszhou 	fp = open_diskmap(osroot);
33010Sstevel@tonic-gate 	if (fp == NULL) {
33020Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
33030Sstevel@tonic-gate 		return (BAM_ERROR);
33040Sstevel@tonic-gate 	}
33050Sstevel@tonic-gate 	grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp));
33060Sstevel@tonic-gate 	(void) fclose(fp);
33070Sstevel@tonic-gate 	if (grubdisk == NULL) {
33080Sstevel@tonic-gate 		bam_error(DISKMAP_FAIL, osdev);
33090Sstevel@tonic-gate 		return (BAM_ERROR);
33100Sstevel@tonic-gate 	}
33110Sstevel@tonic-gate 
33120Sstevel@tonic-gate 	/* add the entry for normal Solaris */
3313*3446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
3314*3446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
3315*3446Smrj 		    DIRECT_BOOT_KERNEL, DIRECT_BOOT_ARCHIVE,
3316*3446Smrj 		    osroot == menu_root);
3317*3446Smrj 	} else {
3318*3446Smrj 		entry = update_boot_entry(mp, title, grubdisk,
3319*3446Smrj 		    MULTI_BOOT, MULTI_BOOT_ARCHIVE,
3320*3446Smrj 		    osroot == menu_root);
3321*3446Smrj 	}
33220Sstevel@tonic-gate 
33230Sstevel@tonic-gate 	/* add the entry for failsafe archive */
3324*3446Smrj 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT);
3325*3446Smrj 	if (stat(failsafe, &sbuf) == 0) {
3326*3446Smrj 		(void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk,
3327*3446Smrj 		    (bam_direct == BAM_DIRECT_DBOOT) ?
3328*3446Smrj 		    DIRECT_BOOT_FAILSAFE_LINE : MULTI_BOOT_FAILSAFE_LINE,
3329*3446Smrj 		    MINIROOT, osroot == menu_root);
3330*3446Smrj 	}
33310Sstevel@tonic-gate 	free(grubdisk);
33320Sstevel@tonic-gate 
33330Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
33340Sstevel@tonic-gate 		return (BAM_ERROR);
33350Sstevel@tonic-gate 	}
33360Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
33370Sstevel@tonic-gate 	return (BAM_WRITE);
33380Sstevel@tonic-gate }
33390Sstevel@tonic-gate 
3340316Svikram static char *
3341316Svikram read_grub_root(void)
3342316Svikram {
3343316Svikram 	FILE *fp;
3344316Svikram 	struct stat sb;
3345316Svikram 	char buf[BAM_MAXLINE];
3346316Svikram 	char *rootstr;
3347316Svikram 
3348316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3349316Svikram 		bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno));
3350316Svikram 		return (NULL);
3351316Svikram 	}
3352316Svikram 
3353316Svikram 	if (stat(GRUB_root, &sb) != 0) {
3354316Svikram 		bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno));
3355316Svikram 		return (NULL);
3356316Svikram 	}
3357316Svikram 
3358316Svikram 	fp = fopen(GRUB_root, "r");
3359316Svikram 	if (fp == NULL) {
3360316Svikram 		bam_error(OPEN_FAIL, GRUB_root, strerror(errno));
3361316Svikram 		return (NULL);
3362316Svikram 	}
3363316Svikram 
3364316Svikram 	if (s_fgets(buf, sizeof (buf), fp) == NULL) {
3365316Svikram 		bam_error(EMPTY_FILE, GRUB_root, strerror(errno));
3366316Svikram 		(void) fclose(fp);
3367316Svikram 		return (NULL);
3368316Svikram 	}
3369316Svikram 
3370316Svikram 	/*
3371316Svikram 	 * Copy buf here as check below may trash the buffer
3372316Svikram 	 */
3373316Svikram 	rootstr = s_strdup(buf);
3374316Svikram 
3375316Svikram 	if (s_fgets(buf, sizeof (buf), fp) != NULL) {
3376316Svikram 		bam_error(BAD_ROOT_FILE, GRUB_root);
3377316Svikram 		free(rootstr);
3378316Svikram 		rootstr = NULL;
3379316Svikram 	}
3380316Svikram 
3381316Svikram 	(void) fclose(fp);
3382316Svikram 
3383316Svikram 	return (rootstr);
3384316Svikram }
3385316Svikram 
3386662Sszhou static void
3387*3446Smrj save_default_entry(menu_t *mp, const char *which)
3388662Sszhou {
3389662Sszhou 	int lineNum, entryNum;
3390662Sszhou 	int entry = 0;	/* default is 0 */
3391662Sszhou 	char linebuf[BAM_MAXLINE];
3392662Sszhou 	line_t *lp = mp->curdefault;
3393662Sszhou 
33943381Svikram 	if (mp->start) {
33953381Svikram 		lineNum = mp->end->lineNum;
33963381Svikram 		entryNum = mp->end->entryNum;
33973381Svikram 	} else {
33983381Svikram 		lineNum = LINE_INIT;
33993381Svikram 		entryNum = ENTRY_INIT;
34003381Svikram 	}
34013381Svikram 
3402662Sszhou 	if (lp)
3403662Sszhou 		entry = s_strtol(lp->arg);
3404662Sszhou 
3405*3446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
3406662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
3407662Sszhou }
3408662Sszhou 
3409662Sszhou static void
3410*3446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
3411662Sszhou {
3412662Sszhou 	int entry;
3413662Sszhou 	char *str;
3414662Sszhou 
3415662Sszhou 	if (lp == NULL)
3416662Sszhou 		return;		/* nothing to restore */
3417662Sszhou 
3418*3446Smrj 	str = lp->arg + strlen(which);
3419662Sszhou 	entry = s_strtol(str);
3420662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
3421662Sszhou 
3422662Sszhou 	/* delete saved old default line */
3423662Sszhou 	unlink_line(mp, lp);
3424662Sszhou 	line_free(lp);
3425662Sszhou }
3426662Sszhou 
34270Sstevel@tonic-gate /*
34280Sstevel@tonic-gate  * This function is for supporting reboot with args.
34290Sstevel@tonic-gate  * The opt value can be:
34300Sstevel@tonic-gate  * NULL		delete temp entry, if present
34310Sstevel@tonic-gate  * entry=#	switches default entry to 1
34320Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
34330Sstevel@tonic-gate  *		and make it the default
34340Sstevel@tonic-gate  */
34350Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
34360Sstevel@tonic-gate 
3437662Sszhou /*ARGSUSED*/
34380Sstevel@tonic-gate static error_t
34390Sstevel@tonic-gate update_temp(menu_t *mp, char *menupath, char *opt)
34400Sstevel@tonic-gate {
34410Sstevel@tonic-gate 	int entry;
3442*3446Smrj 	char *grubdisk, *rootdev, *path;
3443*3446Smrj 	char kernbuf[BUFSIZ];
3444*3446Smrj 	char args_buf[BUFSIZ];
3445316Svikram 	struct stat sb;
34460Sstevel@tonic-gate 
34470Sstevel@tonic-gate 	assert(mp);
34480Sstevel@tonic-gate 
3449662Sszhou 	/* If no option, delete exiting reboot menu entry */
3450662Sszhou 	if (opt == NULL) {
3451662Sszhou 		entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
3452662Sszhou 		    0, &entry);
3453662Sszhou 		if (ent == NULL)	/* not found is ok */
3454662Sszhou 			return (BAM_SUCCESS);
3455662Sszhou 		(void) do_delete(mp, entry);
3456*3446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
3457*3446Smrj 		mp->olddefault = NULL;
3458662Sszhou 		return (BAM_WRITE);
3459662Sszhou 	}
3460662Sszhou 
3461662Sszhou 	/* if entry= is specified, set the default entry */
3462662Sszhou 	if (strncmp(opt, "entry=", strlen("entry=")) == 0 &&
34630Sstevel@tonic-gate 	    selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
34640Sstevel@tonic-gate 		/* this is entry=# option */
34650Sstevel@tonic-gate 		return (set_global(mp, menu_cmds[DEFAULT_CMD], entry));
34660Sstevel@tonic-gate 	}
34670Sstevel@tonic-gate 
34680Sstevel@tonic-gate 	/*
34690Sstevel@tonic-gate 	 * add a new menu entry base on opt and make it the default
34700Sstevel@tonic-gate 	 */
3471316Svikram 	grubdisk = NULL;
3472316Svikram 	if (stat(GRUB_slice, &sb) != 0) {
3473316Svikram 		/*
3474316Svikram 		 * 1. First get root disk name from mnttab
3475316Svikram 		 * 2. Translate disk name to grub name
3476316Svikram 		 * 3. Add the new menu entry
3477316Svikram 		 */
3478316Svikram 		rootdev = get_special("/");
3479316Svikram 		if (rootdev) {
3480316Svikram 			grubdisk = os_to_grubdisk(rootdev, 1);
3481316Svikram 			free(rootdev);
3482316Svikram 		}
3483316Svikram 	} else {
3484316Svikram 		/*
3485316Svikram 		 * This is an LU BE. The GRUB_root file
3486316Svikram 		 * contains entry for GRUB's "root" cmd.
3487316Svikram 		 */
3488316Svikram 		grubdisk = read_grub_root();
34890Sstevel@tonic-gate 	}
34900Sstevel@tonic-gate 	if (grubdisk == NULL) {
3491316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
34920Sstevel@tonic-gate 		return (BAM_ERROR);
34930Sstevel@tonic-gate 	}
34940Sstevel@tonic-gate 
34950Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
3496*3446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
3497*3446Smrj 		if (opt[0] == '-') {
3498*3446Smrj 			/* It's an option - first see if boot-file is set */
3499*3446Smrj 			if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ)
3500*3446Smrj 			    != BAM_SUCCESS)
3501*3446Smrj 				return (BAM_ERROR);
3502*3446Smrj 			if (kernbuf[0] == '\0')
3503*3446Smrj 				(void) strncpy(kernbuf, DIRECT_BOOT_KERNEL,
3504*3446Smrj 				    BUFSIZ);
3505*3446Smrj 			(void) strlcat(kernbuf, " ", BUFSIZ);
3506*3446Smrj 			(void) strlcat(kernbuf, opt, BUFSIZ);
3507*3446Smrj 		} else if (opt[0] == '/') {
3508*3446Smrj 			/* It's a full path - write it out and go home */
3509*3446Smrj 			(void) strlcpy(kernbuf, opt, BUFSIZ);
3510*3446Smrj 		} else {
3511*3446Smrj 			path = expand_path(opt);
3512*3446Smrj 			if (path != NULL) {
3513*3446Smrj 				(void) strlcpy(kernbuf, path, BUFSIZ);
3514*3446Smrj 				free(path);
3515*3446Smrj 				if (strcmp(opt, "kmdb") == 0) {
3516*3446Smrj 					if (set_kernel(mp, ARGS_CMD, NULL,
3517*3446Smrj 					    args_buf, BUFSIZ) != BAM_SUCCESS)
3518*3446Smrj 						return (BAM_ERROR);
3519*3446Smrj 
3520*3446Smrj 					if (args_buf[0] != '\0') {
3521*3446Smrj 						(void) strlcat(kernbuf, " ",
3522*3446Smrj 						    BUFSIZ);
3523*3446Smrj 						(void) strlcat(kernbuf,
3524*3446Smrj 						    args_buf, BUFSIZ);
3525*3446Smrj 					}
3526*3446Smrj 				}
3527*3446Smrj 			}
3528*3446Smrj 		}
3529*3446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3530*3446Smrj 		    NULL);
3531*3446Smrj 	} else {
3532*3446Smrj 		(void) snprintf(kernbuf, sizeof (kernbuf), "%s %s",
3533*3446Smrj 		    MULTI_BOOT, opt);
3534*3446Smrj 		entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf,
3535*3446Smrj 		    MULTI_BOOT_ARCHIVE);
3536*3446Smrj 	}
35370Sstevel@tonic-gate 	free(grubdisk);
35380Sstevel@tonic-gate 
35390Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
3540316Svikram 		bam_error(REBOOT_WITH_ARGS_FAILED);
35410Sstevel@tonic-gate 		return (BAM_ERROR);
35420Sstevel@tonic-gate 	}
3543662Sszhou 
3544*3446Smrj 	save_default_entry(mp, BAM_OLDDEF);
35450Sstevel@tonic-gate 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
35460Sstevel@tonic-gate 	return (BAM_WRITE);
35470Sstevel@tonic-gate }
35480Sstevel@tonic-gate 
35490Sstevel@tonic-gate static error_t
35500Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
35510Sstevel@tonic-gate {
35520Sstevel@tonic-gate 	line_t *lp, *found, *last;
35530Sstevel@tonic-gate 	char *cp, *str;
35540Sstevel@tonic-gate 	char prefix[BAM_MAXLINE];
35550Sstevel@tonic-gate 	size_t len;
35560Sstevel@tonic-gate 
35570Sstevel@tonic-gate 	assert(mp);
35580Sstevel@tonic-gate 	assert(globalcmd);
35590Sstevel@tonic-gate 
3560316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
3561316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
3562316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
3563316Svikram 			bam_error(INVALID_ENTRY, prefix);
3564316Svikram 			return (BAM_ERROR);
3565316Svikram 		}
3566316Svikram 	}
3567316Svikram 
35680Sstevel@tonic-gate 	found = last = NULL;
35690Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
35700Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
35710Sstevel@tonic-gate 			continue;
35720Sstevel@tonic-gate 
35730Sstevel@tonic-gate 		last = lp; /* track the last global found */
35740Sstevel@tonic-gate 
35750Sstevel@tonic-gate 		if (lp->cmd == NULL) {
35760Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
35770Sstevel@tonic-gate 			continue;
35780Sstevel@tonic-gate 		}
35790Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
35800Sstevel@tonic-gate 			continue;
35810Sstevel@tonic-gate 
35820Sstevel@tonic-gate 		if (found) {
35830Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
35840Sstevel@tonic-gate 		}
35850Sstevel@tonic-gate 		found = lp;
35860Sstevel@tonic-gate 	}
35870Sstevel@tonic-gate 
35880Sstevel@tonic-gate 	if (found == NULL) {
35890Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
35900Sstevel@tonic-gate 		if (last == NULL) {
35910Sstevel@tonic-gate 			lp->next = mp->start;
35920Sstevel@tonic-gate 			mp->start = lp;
35930Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
35940Sstevel@tonic-gate 		} else {
35950Sstevel@tonic-gate 			lp->next = last->next;
35960Sstevel@tonic-gate 			last->next = lp;
35970Sstevel@tonic-gate 			if (lp->next == NULL)
35980Sstevel@tonic-gate 				mp->end = lp;
35990Sstevel@tonic-gate 		}
36000Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
36010Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
36020Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
36030Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
36040Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
36050Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
36060Sstevel@tonic-gate 		return (BAM_WRITE);
36070Sstevel@tonic-gate 	}
36080Sstevel@tonic-gate 
36090Sstevel@tonic-gate 	/*
36100Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
36110Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
36120Sstevel@tonic-gate 	 * readability.
36130Sstevel@tonic-gate 	 */
36140Sstevel@tonic-gate 	str = found->line;
36150Sstevel@tonic-gate 	cp = prefix;
36160Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
36170Sstevel@tonic-gate 		*(cp++) = *(str++);
36180Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
36190Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
36200Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
36210Sstevel@tonic-gate 
36220Sstevel@tonic-gate 	free(found->line);
36230Sstevel@tonic-gate 	found->line = s_calloc(1, len);
36240Sstevel@tonic-gate 	(void) snprintf(found->line, len,
36250Sstevel@tonic-gate 		"%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
36260Sstevel@tonic-gate 
36270Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
36280Sstevel@tonic-gate }
36290Sstevel@tonic-gate 
3630*3446Smrj /*
3631*3446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
3632*3446Smrj  * expand it to a full unix path.
3633*3446Smrj  */
3634*3446Smrj static char *
3635*3446Smrj expand_path(const char *partial_path)
3636*3446Smrj {
3637*3446Smrj 	int new_path_len;
3638*3446Smrj 	char *new_path, new_path2[PATH_MAX];
3639*3446Smrj 	struct stat sb;
3640*3446Smrj 
3641*3446Smrj 	new_path_len = strlen(partial_path) + 64;
3642*3446Smrj 	new_path = s_calloc(1, new_path_len);
3643*3446Smrj 
3644*3446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
3645*3446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
3646*3446Smrj 	    partial_path);
3647*3446Smrj 	if (stat(new_path, &sb) == 0) {
3648*3446Smrj 		return (new_path);
3649*3446Smrj 	}
3650*3446Smrj 
3651*3446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
3652*3446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
3653*3446Smrj 		    DIRECT_BOOT_KERNEL);
3654*3446Smrj 		return (new_path);
3655*3446Smrj 	}
3656*3446Smrj 
3657*3446Smrj 	/*
3658*3446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
3659*3446Smrj 	 * see if we were just given a glom name.
3660*3446Smrj 	 */
3661*3446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
3662*3446Smrj 	    partial_path);
3663*3446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
3664*3446Smrj 	    partial_path);
3665*3446Smrj 	if (stat(new_path, &sb) == 0) {
3666*3446Smrj 		if (stat(new_path2, &sb) == 0) {
3667*3446Smrj 			/*
3668*3446Smrj 			 * We matched both, so we actually
3669*3446Smrj 			 * want to write the $ISADIR version.
3670*3446Smrj 			 */
3671*3446Smrj 			(void) snprintf(new_path, new_path_len,
3672*3446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
3673*3446Smrj 			    partial_path);
3674*3446Smrj 		}
3675*3446Smrj 		return (new_path);
3676*3446Smrj 	}
3677*3446Smrj 
3678*3446Smrj 	bam_error(UNKNOWN_KERNEL, partial_path);
3679*3446Smrj 	free(new_path);
3680*3446Smrj 	return (NULL);
3681*3446Smrj }
3682*3446Smrj 
3683*3446Smrj /*
3684*3446Smrj  * The kernel cmd and arg have been changed, so
3685*3446Smrj  * check whether the archive line needs to change.
3686*3446Smrj  */
3687*3446Smrj static void
3688*3446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
3689*3446Smrj {
3690*3446Smrj 	line_t *lp = entryp->start;
3691*3446Smrj 	char *new_archive;
3692*3446Smrj 	menu_cmd_t m_cmd;
3693*3446Smrj 
3694*3446Smrj 	for (; lp != NULL; lp = lp->next) {
3695*3446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
3696*3446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
3697*3446Smrj 			break;
3698*3446Smrj 		}
3699*3446Smrj 		if (lp == entryp->end)
3700*3446Smrj 			return;
3701*3446Smrj 	}
3702*3446Smrj 	if (lp == NULL)
3703*3446Smrj 		return;
3704*3446Smrj 
3705*3446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
3706*3446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
3707*3446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
3708*3446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
3709*3446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
3710*3446Smrj 		m_cmd = MODULE_CMD;
3711*3446Smrj 	} else {
3712*3446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
3713*3446Smrj 		m_cmd = MODULE_CMD;
3714*3446Smrj 	}
3715*3446Smrj 
3716*3446Smrj 	if (strcmp(lp->arg, new_archive) == 0)
3717*3446Smrj 		return;
3718*3446Smrj 
3719*3446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
3720*3446Smrj 		free(lp->cmd);
3721*3446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
3722*3446Smrj 	}
3723*3446Smrj 
3724*3446Smrj 	free(lp->arg);
3725*3446Smrj 	lp->arg = s_strdup(new_archive);
3726*3446Smrj 	update_line(lp);
3727*3446Smrj }
3728*3446Smrj 
3729*3446Smrj /*
3730*3446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
3731*3446Smrj  */
3732*3446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
3733*3446Smrj 
3734*3446Smrj /*
3735*3446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
3736*3446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
3737*3446Smrj  * string, reset the value to the default.  If path is a non-zero-length
3738*3446Smrj  * string, set the kernel or arguments.
3739*3446Smrj  */
3740*3446Smrj static error_t
3741*3446Smrj set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
3742*3446Smrj {
3743*3446Smrj 	int entryNum, rv = BAM_SUCCESS, free_new_path = 0;
3744*3446Smrj 	entry_t *entryp;
3745*3446Smrj 	line_t *ptr, *kernelp;
3746*3446Smrj 	char *new_arg, *old_args, *space;
3747*3446Smrj 	char *grubdisk, *rootdev, *new_path;
3748*3446Smrj 	char old_space;
3749*3446Smrj 	size_t old_kernel_len, new_str_len;
3750*3446Smrj 	struct stat sb;
3751*3446Smrj 
3752*3446Smrj 	assert(bufsize > 0);
3753*3446Smrj 
3754*3446Smrj 	ptr = kernelp = NULL;
3755*3446Smrj 	new_arg = old_args = space = NULL;
3756*3446Smrj 	grubdisk = rootdev = new_path = NULL;
3757*3446Smrj 	buf[0] = '\0';
3758*3446Smrj 
3759*3446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
3760*3446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
3761*3446Smrj 		return (BAM_ERROR);
3762*3446Smrj 	}
3763*3446Smrj 
3764*3446Smrj 	/*
3765*3446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
3766*3446Smrj 	 * one, we don't want to mess with it.  Just print an error and
3767*3446Smrj 	 * return.
3768*3446Smrj 	 */
3769*3446Smrj 	if (mp->curdefault) {
3770*3446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
3771*3446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
3772*3446Smrj 			if (entryp->entryNum == entryNum)
3773*3446Smrj 				break;
3774*3446Smrj 		}
3775*3446Smrj 		if ((entryp != NULL) &&
3776*3446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
3777*3446Smrj 			bam_error(DEFAULT_NOT_BAM);
3778*3446Smrj 			return (BAM_ERROR);
3779*3446Smrj 		}
3780*3446Smrj 	}
3781*3446Smrj 
3782*3446Smrj 	entryNum = -1;
3783*3446Smrj 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, 0,
3784*3446Smrj 	    &entryNum);
3785*3446Smrj 
3786*3446Smrj 	if (entryp != NULL) {
3787*3446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
3788*3446Smrj 		    ptr = ptr->next) {
3789*3446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
3790*3446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
3791*3446Smrj 				kernelp = ptr;
3792*3446Smrj 				break;
3793*3446Smrj 			}
3794*3446Smrj 		}
3795*3446Smrj 		if (kernelp == NULL) {
3796*3446Smrj 			bam_error(NO_KERNEL, entryNum);
3797*3446Smrj 			return (BAM_ERROR);
3798*3446Smrj 		}
3799*3446Smrj 
3800*3446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
3801*3446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
3802*3446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
3803*3446Smrj 			old_args++;
3804*3446Smrj 	}
3805*3446Smrj 
3806*3446Smrj 	if (path == NULL) {
3807*3446Smrj 		/* Simply report what was found */
3808*3446Smrj 		if (kernelp == NULL)
3809*3446Smrj 			return (BAM_SUCCESS);
3810*3446Smrj 
3811*3446Smrj 		if (optnum == ARGS_CMD) {
3812*3446Smrj 			if (old_args[0] != '\0')
3813*3446Smrj 				(void) strlcpy(buf, old_args, bufsize);
3814*3446Smrj 		} else {
3815*3446Smrj 			/*
3816*3446Smrj 			 * We need to print the kernel, so we just turn the
3817*3446Smrj 			 * first space into a '\0' and print the beginning.
3818*3446Smrj 			 * We don't print anything if it's the default kernel.
3819*3446Smrj 			 */
3820*3446Smrj 			old_space = *space;
3821*3446Smrj 			*space = '\0';
3822*3446Smrj 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0)
3823*3446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
3824*3446Smrj 			*space = old_space;
3825*3446Smrj 		}
3826*3446Smrj 		return (BAM_SUCCESS);
3827*3446Smrj 	}
3828*3446Smrj 
3829*3446Smrj 	/*
3830*3446Smrj 	 * First, check if we're resetting an entry to the default.
3831*3446Smrj 	 */
3832*3446Smrj 	if ((path[0] == '\0') ||
3833*3446Smrj 	    ((optnum == KERNEL_CMD) &&
3834*3446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
3835*3446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
3836*3446Smrj 			/* No previous entry, it's already the default */
3837*3446Smrj 			return (BAM_SUCCESS);
3838*3446Smrj 		}
3839*3446Smrj 
3840*3446Smrj 		/*
3841*3446Smrj 		 * Check if we can delete the entry.  If we're resetting the
3842*3446Smrj 		 * kernel command, and the args is already empty, or if we're
3843*3446Smrj 		 * resetting the args command, and the kernel is already the
3844*3446Smrj 		 * default, we can restore the old default and delete the entry.
3845*3446Smrj 		 */
3846*3446Smrj 		if (((optnum == KERNEL_CMD) &&
3847*3446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
3848*3446Smrj 		    ((optnum == ARGS_CMD) &&
3849*3446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
3850*3446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
3851*3446Smrj 			kernelp = NULL;
3852*3446Smrj 			(void) do_delete(mp, entryNum);
3853*3446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
3854*3446Smrj 			    mp->old_rc_default);
3855*3446Smrj 			mp->old_rc_default = NULL;
3856*3446Smrj 			rv = BAM_WRITE;
3857*3446Smrj 			goto done;
3858*3446Smrj 		}
3859*3446Smrj 
3860*3446Smrj 		if (optnum == KERNEL_CMD) {
3861*3446Smrj 			/*
3862*3446Smrj 			 * At this point, we've already checked that old_args
3863*3446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
3864*3446Smrj 			 * a space a the string termination character.
3865*3446Smrj 			 */
3866*3446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
3867*3446Smrj 			    strlen(old_args) + 2;
3868*3446Smrj 			new_arg = s_calloc(1, new_str_len);
3869*3446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
3870*3446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
3871*3446Smrj 			free(kernelp->arg);
3872*3446Smrj 			kernelp->arg = new_arg;
3873*3446Smrj 
3874*3446Smrj 			/*
3875*3446Smrj 			 * We have changed the kernel line, so we may need
3876*3446Smrj 			 * to update the archive line as well.
3877*3446Smrj 			 */
3878*3446Smrj 			set_archive_line(entryp, kernelp);
3879*3446Smrj 		} else {
3880*3446Smrj 			/*
3881*3446Smrj 			 * We're resetting the boot args to nothing, so
3882*3446Smrj 			 * we only need to copy the kernel.  We've already
3883*3446Smrj 			 * checked that the kernel is not the default.
3884*3446Smrj 			 */
3885*3446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
3886*3446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
3887*3446Smrj 			    kernelp->arg);
3888*3446Smrj 			free(kernelp->arg);
3889*3446Smrj 			kernelp->arg = new_arg;
3890*3446Smrj 		}
3891*3446Smrj 		rv = BAM_WRITE;
3892*3446Smrj 		goto done;
3893*3446Smrj 	}
3894*3446Smrj 
3895*3446Smrj 	/*
3896*3446Smrj 	 * Expand the kernel file to a full path, if necessary
3897*3446Smrj 	 */
3898*3446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
3899*3446Smrj 		new_path = expand_path(path);
3900*3446Smrj 		if (new_path == NULL) {
3901*3446Smrj 			return (BAM_ERROR);
3902*3446Smrj 		}
3903*3446Smrj 		free_new_path = 1;
3904*3446Smrj 	} else {
3905*3446Smrj 		new_path = path;
3906*3446Smrj 		free_new_path = 0;
3907*3446Smrj 	}
3908*3446Smrj 
3909*3446Smrj 	/*
3910*3446Smrj 	 * At this point, we know we're setting a new value.  First, take care
3911*3446Smrj 	 * of the case where there was no previous entry.
3912*3446Smrj 	 */
3913*3446Smrj 	if (entryp == NULL) {
3914*3446Smrj 		/* Similar to code in update_temp */
3915*3446Smrj 		if (stat(GRUB_slice, &sb) != 0) {
3916*3446Smrj 			/*
3917*3446Smrj 			 * 1. First get root disk name from mnttab
3918*3446Smrj 			 * 2. Translate disk name to grub name
3919*3446Smrj 			 * 3. Add the new menu entry
3920*3446Smrj 			 */
3921*3446Smrj 			rootdev = get_special("/");
3922*3446Smrj 			if (rootdev) {
3923*3446Smrj 				grubdisk = os_to_grubdisk(rootdev, 1);
3924*3446Smrj 				free(rootdev);
3925*3446Smrj 			}
3926*3446Smrj 		} else {
3927*3446Smrj 			/*
3928*3446Smrj 			 * This is an LU BE. The GRUB_root file
3929*3446Smrj 			 * contains entry for GRUB's "root" cmd.
3930*3446Smrj 			 */
3931*3446Smrj 			grubdisk = read_grub_root();
3932*3446Smrj 		}
3933*3446Smrj 		if (grubdisk == NULL) {
3934*3446Smrj 			bam_error(REBOOT_WITH_ARGS_FAILED);
3935*3446Smrj 			rv = BAM_ERROR;
3936*3446Smrj 			goto done;
3937*3446Smrj 		}
3938*3446Smrj 		if (optnum == KERNEL_CMD) {
3939*3446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
3940*3446Smrj 			    grubdisk, new_path, NULL);
3941*3446Smrj 		} else {
3942*3446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
3943*3446Smrj 			    strlen(path) + 8;
3944*3446Smrj 			new_arg = s_calloc(1, new_str_len);
3945*3446Smrj 
3946*3446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
3947*3446Smrj 			    DIRECT_BOOT_KERNEL, path);
3948*3446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
3949*3446Smrj 			    grubdisk, new_arg, DIRECT_BOOT_ARCHIVE);
3950*3446Smrj 		}
3951*3446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
3952*3446Smrj 		(void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
3953*3446Smrj 		rv = BAM_WRITE;
3954*3446Smrj 		goto done;
3955*3446Smrj 	}
3956*3446Smrj 
3957*3446Smrj 	/*
3958*3446Smrj 	 * There was already an bootenv entry which we need to edit.
3959*3446Smrj 	 */
3960*3446Smrj 	if (optnum == KERNEL_CMD) {
3961*3446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
3962*3446Smrj 		new_arg = s_calloc(1, new_str_len);
3963*3446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
3964*3446Smrj 		    old_args);
3965*3446Smrj 		free(kernelp->arg);
3966*3446Smrj 		kernelp->arg = new_arg;
3967*3446Smrj 
3968*3446Smrj 		/*
3969*3446Smrj 		 * If we have changed the kernel line, we may need to update
3970*3446Smrj 		 * the archive line as well.
3971*3446Smrj 		 */
3972*3446Smrj 		set_archive_line(entryp, kernelp);
3973*3446Smrj 	} else {
3974*3446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
3975*3446Smrj 		new_arg = s_calloc(1, new_str_len);
3976*3446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
3977*3446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
3978*3446Smrj 		(void) strlcat(new_arg, path, new_str_len);
3979*3446Smrj 		free(kernelp->arg);
3980*3446Smrj 		kernelp->arg = new_arg;
3981*3446Smrj 	}
3982*3446Smrj 	rv = BAM_WRITE;
3983*3446Smrj 
3984*3446Smrj done:
3985*3446Smrj 	if ((rv == BAM_WRITE) && kernelp)
3986*3446Smrj 		update_line(kernelp);
3987*3446Smrj 	if (free_new_path)
3988*3446Smrj 		free(new_path);
3989*3446Smrj 	return (rv);
3990*3446Smrj }
3991*3446Smrj 
39920Sstevel@tonic-gate /*ARGSUSED*/
39930Sstevel@tonic-gate static error_t
39940Sstevel@tonic-gate set_option(menu_t *mp, char *menu_path, char *opt)
39950Sstevel@tonic-gate {
39960Sstevel@tonic-gate 	int optnum, optval;
39970Sstevel@tonic-gate 	char *val;
3998*3446Smrj 	char buf[BUFSIZ] = "";
3999*3446Smrj 	error_t rv;
40000Sstevel@tonic-gate 
40010Sstevel@tonic-gate 	assert(mp);
40020Sstevel@tonic-gate 	assert(opt);
40030Sstevel@tonic-gate 
40040Sstevel@tonic-gate 	val = strchr(opt, '=');
4005*3446Smrj 	if (val != NULL) {
4006*3446Smrj 		*val = '\0';
40070Sstevel@tonic-gate 	}
40080Sstevel@tonic-gate 
40090Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
40100Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
40110Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
40120Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
4013*3446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
4014*3446Smrj 		optnum = KERNEL_CMD;
4015*3446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
4016*3446Smrj 		optnum = ARGS_CMD;
40170Sstevel@tonic-gate 	} else {
40180Sstevel@tonic-gate 		bam_error(INVALID_ENTRY, opt);
40190Sstevel@tonic-gate 		return (BAM_ERROR);
40200Sstevel@tonic-gate 	}
40210Sstevel@tonic-gate 
4022*3446Smrj 	/*
4023*3446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
4024*3446Smrj 	 * others cause errors
4025*3446Smrj 	 */
4026*3446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
4027*3446Smrj 		bam_error(INVALID_ENTRY, opt);
4028*3446Smrj 		return (BAM_ERROR);
4029*3446Smrj 	} else if (val != NULL) {
4030*3446Smrj 		*val = '=';
4031*3446Smrj 	}
4032*3446Smrj 
4033*3446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
4034*3446Smrj 		rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ);
4035*3446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
4036*3446Smrj 			(void) printf("%s\n", buf);
4037*3446Smrj 		return (rv);
4038*3446Smrj 	} else {
4039*3446Smrj 		optval = s_strtol(val + 1);
4040*3446Smrj 		return (set_global(mp, menu_cmds[optnum], optval));
4041*3446Smrj 	}
40420Sstevel@tonic-gate }
40430Sstevel@tonic-gate 
40440Sstevel@tonic-gate /*
40450Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
40460Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
40470Sstevel@tonic-gate  */
40480Sstevel@tonic-gate static error_t
40490Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
40500Sstevel@tonic-gate {
40510Sstevel@tonic-gate 	line_t *lp;
40520Sstevel@tonic-gate 	char *arg;
40530Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
40540Sstevel@tonic-gate 
40550Sstevel@tonic-gate 	assert(mp);
40560Sstevel@tonic-gate 	assert(menu_path);
40570Sstevel@tonic-gate 	assert(globalcmd);
40580Sstevel@tonic-gate 
40590Sstevel@tonic-gate 	if (mp->start == NULL) {
40600Sstevel@tonic-gate 		if (!quiet)
40610Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
40620Sstevel@tonic-gate 		return (BAM_ERROR);
40630Sstevel@tonic-gate 	}
40640Sstevel@tonic-gate 
40650Sstevel@tonic-gate 	done = 0;
40660Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
40670Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
40680Sstevel@tonic-gate 			continue;
40690Sstevel@tonic-gate 
40700Sstevel@tonic-gate 		if (lp->cmd == NULL) {
40710Sstevel@tonic-gate 			if (!quiet)
40720Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
40730Sstevel@tonic-gate 			continue;
40740Sstevel@tonic-gate 		}
40750Sstevel@tonic-gate 
40760Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
40770Sstevel@tonic-gate 			continue;
40780Sstevel@tonic-gate 
40790Sstevel@tonic-gate 		/* Found global. Check for duplicates */
40800Sstevel@tonic-gate 		if (done && !quiet) {
40810Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
40820Sstevel@tonic-gate 			ret = BAM_ERROR;
40830Sstevel@tonic-gate 		}
40840Sstevel@tonic-gate 
40850Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
40860Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
40870Sstevel@tonic-gate 		done = 1;
40880Sstevel@tonic-gate 	}
40890Sstevel@tonic-gate 
40900Sstevel@tonic-gate 	if (!done && bam_verbose)
40910Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
40920Sstevel@tonic-gate 
40930Sstevel@tonic-gate 	return (ret);
40940Sstevel@tonic-gate }
40950Sstevel@tonic-gate 
40960Sstevel@tonic-gate static error_t
40970Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
40980Sstevel@tonic-gate {
40990Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
41000Sstevel@tonic-gate }
41010Sstevel@tonic-gate 
41020Sstevel@tonic-gate static void
41030Sstevel@tonic-gate line_free(line_t *lp)
41040Sstevel@tonic-gate {
41050Sstevel@tonic-gate 	if (lp == NULL)
41060Sstevel@tonic-gate 		return;
41070Sstevel@tonic-gate 
41080Sstevel@tonic-gate 	if (lp->cmd)
41090Sstevel@tonic-gate 		free(lp->cmd);
41100Sstevel@tonic-gate 	if (lp->sep)
41110Sstevel@tonic-gate 		free(lp->sep);
41120Sstevel@tonic-gate 	if (lp->arg)
41130Sstevel@tonic-gate 		free(lp->arg);
41140Sstevel@tonic-gate 	if (lp->line)
41150Sstevel@tonic-gate 		free(lp->line);
41160Sstevel@tonic-gate 	free(lp);
41170Sstevel@tonic-gate }
41180Sstevel@tonic-gate 
41190Sstevel@tonic-gate static void
41200Sstevel@tonic-gate linelist_free(line_t *start)
41210Sstevel@tonic-gate {
41220Sstevel@tonic-gate 	line_t *lp;
41230Sstevel@tonic-gate 
41240Sstevel@tonic-gate 	while (start) {
41250Sstevel@tonic-gate 		lp = start;
41260Sstevel@tonic-gate 		start = start->next;
41270Sstevel@tonic-gate 		line_free(lp);
41280Sstevel@tonic-gate 	}
41290Sstevel@tonic-gate }
41300Sstevel@tonic-gate 
41310Sstevel@tonic-gate static void
41320Sstevel@tonic-gate filelist_free(filelist_t *flistp)
41330Sstevel@tonic-gate {
41340Sstevel@tonic-gate 	linelist_free(flistp->head);
41350Sstevel@tonic-gate 	flistp->head = NULL;
41360Sstevel@tonic-gate 	flistp->tail = NULL;
41370Sstevel@tonic-gate }
41380Sstevel@tonic-gate 
41390Sstevel@tonic-gate static void
41400Sstevel@tonic-gate menu_free(menu_t *mp)
41410Sstevel@tonic-gate {
4142662Sszhou 	entry_t *ent, *tmp;
41430Sstevel@tonic-gate 	assert(mp);
41440Sstevel@tonic-gate 
41450Sstevel@tonic-gate 	if (mp->start)
41460Sstevel@tonic-gate 		linelist_free(mp->start);
4147662Sszhou 	ent = mp->entries;
4148662Sszhou 	while (ent) {
4149662Sszhou 		tmp = ent;
4150662Sszhou 		ent = tmp->next;
4151662Sszhou 		free(tmp);
4152662Sszhou 	}
4153662Sszhou 
41540Sstevel@tonic-gate 	free(mp);
41550Sstevel@tonic-gate }
41560Sstevel@tonic-gate 
41570Sstevel@tonic-gate /*
41580Sstevel@tonic-gate  * Utility routines
41590Sstevel@tonic-gate  */
41600Sstevel@tonic-gate 
41610Sstevel@tonic-gate 
41620Sstevel@tonic-gate /*
41630Sstevel@tonic-gate  * Returns 0 on success
41640Sstevel@tonic-gate  * Any other value indicates an error
41650Sstevel@tonic-gate  */
41660Sstevel@tonic-gate static int
41670Sstevel@tonic-gate exec_cmd(char *cmdline, char *output, int64_t osize)
41680Sstevel@tonic-gate {
41690Sstevel@tonic-gate 	char buf[BUFSIZ];
41700Sstevel@tonic-gate 	int ret;
41710Sstevel@tonic-gate 	FILE *ptr;
41720Sstevel@tonic-gate 	size_t len;
41730Sstevel@tonic-gate 	sigset_t set;
41740Sstevel@tonic-gate 	void (*disp)(int);
41750Sstevel@tonic-gate 
41760Sstevel@tonic-gate 	/*
41770Sstevel@tonic-gate 	 * For security
41780Sstevel@tonic-gate 	 * - only absolute paths are allowed
41790Sstevel@tonic-gate 	 * - set IFS to space and tab
41800Sstevel@tonic-gate 	 */
41810Sstevel@tonic-gate 	if (*cmdline != '/') {
41820Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
41830Sstevel@tonic-gate 		return (-1);
41840Sstevel@tonic-gate 	}
41850Sstevel@tonic-gate 	(void) putenv("IFS= \t");
41860Sstevel@tonic-gate 
41870Sstevel@tonic-gate 	/*
41880Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
41890Sstevel@tonic-gate 	 * unblock it here
41900Sstevel@tonic-gate 	 */
41910Sstevel@tonic-gate 	(void) sigemptyset(&set);
41920Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
41930Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
41940Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
41950Sstevel@tonic-gate 		return (-1);
41960Sstevel@tonic-gate 	}
41970Sstevel@tonic-gate 
41980Sstevel@tonic-gate 	/*
41990Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
42000Sstevel@tonic-gate 	 */
42010Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
42020Sstevel@tonic-gate 	if (disp == SIG_ERR) {
42030Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
42040Sstevel@tonic-gate 		return (-1);
42050Sstevel@tonic-gate 	}
42060Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
42070Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
42080Sstevel@tonic-gate 		return (-1);
42090Sstevel@tonic-gate 	}
42100Sstevel@tonic-gate 
42110Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
42120Sstevel@tonic-gate 	if (ptr == NULL) {
42130Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
42140Sstevel@tonic-gate 		return (-1);
42150Sstevel@tonic-gate 	}
42160Sstevel@tonic-gate 
42170Sstevel@tonic-gate 	/*
42180Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
42190Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
42200Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
42210Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
42220Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
42230Sstevel@tonic-gate 	 * there is no reader process and the command dies with
42240Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
42250Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
42260Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
42270Sstevel@tonic-gate 	 * pclose().
42280Sstevel@tonic-gate 	 *
42290Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
42300Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
42310Sstevel@tonic-gate 	 * from the value returned by pclose()
42320Sstevel@tonic-gate 	 */
42330Sstevel@tonic-gate 	while (fgets(buf, sizeof (buf), ptr) != NULL) {
42340Sstevel@tonic-gate 		/* if (bam_verbose)  XXX */
42350Sstevel@tonic-gate 			bam_print(PRINT_NO_NEWLINE, buf);
42360Sstevel@tonic-gate 		if (output && osize > 0) {
42370Sstevel@tonic-gate 			(void) snprintf(output, osize, "%s", buf);
42380Sstevel@tonic-gate 			len = strlen(buf);
42390Sstevel@tonic-gate 			output += len;
42400Sstevel@tonic-gate 			osize -= len;
42410Sstevel@tonic-gate 		}
42420Sstevel@tonic-gate 	}
42430Sstevel@tonic-gate 
42440Sstevel@tonic-gate 	ret = pclose(ptr);
42450Sstevel@tonic-gate 	if (ret == -1) {
42460Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
42470Sstevel@tonic-gate 		return (-1);
42480Sstevel@tonic-gate 	}
42490Sstevel@tonic-gate 
42500Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
42510Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
42520Sstevel@tonic-gate 	} else {
42530Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
42540Sstevel@tonic-gate 		return (-1);
42550Sstevel@tonic-gate 	}
42560Sstevel@tonic-gate }
42570Sstevel@tonic-gate 
42580Sstevel@tonic-gate /*
42590Sstevel@tonic-gate  * Since this function returns -1 on error
42600Sstevel@tonic-gate  * it cannot be used to convert -1. However,
42610Sstevel@tonic-gate  * that is sufficient for what we need.
42620Sstevel@tonic-gate  */
42630Sstevel@tonic-gate static long
42640Sstevel@tonic-gate s_strtol(char *str)
42650Sstevel@tonic-gate {
42660Sstevel@tonic-gate 	long l;
42670Sstevel@tonic-gate 	char *res = NULL;
42680Sstevel@tonic-gate 
42690Sstevel@tonic-gate 	if (str == NULL) {
42700Sstevel@tonic-gate 		return (-1);
42710Sstevel@tonic-gate 	}
42720Sstevel@tonic-gate 
42730Sstevel@tonic-gate 	errno = 0;
42740Sstevel@tonic-gate 	l = strtol(str, &res, 10);
42750Sstevel@tonic-gate 	if (errno || *res != '\0') {
42760Sstevel@tonic-gate 		return (-1);
42770Sstevel@tonic-gate 	}
42780Sstevel@tonic-gate 
42790Sstevel@tonic-gate 	return (l);
42800Sstevel@tonic-gate }
42810Sstevel@tonic-gate 
42820Sstevel@tonic-gate /*
42830Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
42840Sstevel@tonic-gate  */
42850Sstevel@tonic-gate static int
42860Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
42870Sstevel@tonic-gate {
42880Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
42890Sstevel@tonic-gate 
42900Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
42910Sstevel@tonic-gate 	return (fputs(linebuf, fp));
42920Sstevel@tonic-gate }
42930Sstevel@tonic-gate 
42940Sstevel@tonic-gate /*
42950Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
42960Sstevel@tonic-gate  */
4297*3446Smrj char *
42980Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
42990Sstevel@tonic-gate {
43000Sstevel@tonic-gate 	int n;
43010Sstevel@tonic-gate 
43020Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
43030Sstevel@tonic-gate 	if (buf) {
43040Sstevel@tonic-gate 		n = strlen(buf);
43050Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
43060Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
43070Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
43080Sstevel@tonic-gate 	}
43090Sstevel@tonic-gate 
43100Sstevel@tonic-gate 	return (buf);
43110Sstevel@tonic-gate }
43120Sstevel@tonic-gate 
4313*3446Smrj void *
43140Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
43150Sstevel@tonic-gate {
43160Sstevel@tonic-gate 	void *ptr;
43170Sstevel@tonic-gate 
43180Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
43190Sstevel@tonic-gate 	if (ptr == NULL) {
43200Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
43210Sstevel@tonic-gate 		bam_exit(1);
43220Sstevel@tonic-gate 	}
43230Sstevel@tonic-gate 	return (ptr);
43240Sstevel@tonic-gate }
43250Sstevel@tonic-gate 
4326*3446Smrj void *
4327*3446Smrj s_realloc(void *ptr, size_t sz)
4328*3446Smrj {
4329*3446Smrj 	ptr = realloc(ptr, sz);
4330*3446Smrj 	if (ptr == NULL) {
4331*3446Smrj 		bam_error(NO_MEM, sz);
4332*3446Smrj 		bam_exit(1);
4333*3446Smrj 	}
4334*3446Smrj 	return (ptr);
4335*3446Smrj }
4336*3446Smrj 
43370Sstevel@tonic-gate static char *
43380Sstevel@tonic-gate s_strdup(char *str)
43390Sstevel@tonic-gate {
43400Sstevel@tonic-gate 	char *ptr;
43410Sstevel@tonic-gate 
43420Sstevel@tonic-gate 	if (str == NULL)
43430Sstevel@tonic-gate 		return (NULL);
43440Sstevel@tonic-gate 
43450Sstevel@tonic-gate 	ptr = strdup(str);
43460Sstevel@tonic-gate 	if (ptr == NULL) {
43470Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
43480Sstevel@tonic-gate 		bam_exit(1);
43490Sstevel@tonic-gate 	}
43500Sstevel@tonic-gate 	return (ptr);
43510Sstevel@tonic-gate }
43520Sstevel@tonic-gate 
43530Sstevel@tonic-gate /*
43540Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
43550Sstevel@tonic-gate  * Returns 0 otherwise
43560Sstevel@tonic-gate  */
43570Sstevel@tonic-gate static int
43580Sstevel@tonic-gate is_amd64(void)
43590Sstevel@tonic-gate {
43600Sstevel@tonic-gate 	static int amd64 = -1;
43610Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
43620Sstevel@tonic-gate 
43630Sstevel@tonic-gate 	if (amd64 != -1)
43640Sstevel@tonic-gate 		return (amd64);
43650Sstevel@tonic-gate 
43660Sstevel@tonic-gate 	if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
43670Sstevel@tonic-gate 	    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0)
43680Sstevel@tonic-gate 		amd64 = 1;
43690Sstevel@tonic-gate 	else if (strstr(isabuf, "i386") == NULL)
43700Sstevel@tonic-gate 		amd64 = 1;		/* diskless server */
43710Sstevel@tonic-gate 	else
43720Sstevel@tonic-gate 		amd64 = 0;
43730Sstevel@tonic-gate 
43740Sstevel@tonic-gate 	return (amd64);
43750Sstevel@tonic-gate }
43760Sstevel@tonic-gate 
43770Sstevel@tonic-gate static void
43780Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
43790Sstevel@tonic-gate {
43800Sstevel@tonic-gate 	line_t *lp;
43810Sstevel@tonic-gate 
43820Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
43830Sstevel@tonic-gate 	lp->line = s_strdup(s);
43840Sstevel@tonic-gate 	if (flistp->head == NULL)
43850Sstevel@tonic-gate 		flistp->head = lp;
43860Sstevel@tonic-gate 	else
43870Sstevel@tonic-gate 		flistp->tail->next = lp;
43880Sstevel@tonic-gate 	flistp->tail = lp;
43890Sstevel@tonic-gate }
4390