xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 7111:56d507d78bcd)
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 /*
226319Sjg  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * bootadm(1M) is a new utility for managing bootability of
300Sstevel@tonic-gate  * Solaris *Newboot* environments. It has two primary tasks:
310Sstevel@tonic-gate  * 	- Allow end users to manage bootability of Newboot Solaris instances
320Sstevel@tonic-gate  *	- Provide services to other subsystems in Solaris (primarily Install)
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /* Headers */
360Sstevel@tonic-gate #include <stdio.h>
370Sstevel@tonic-gate #include <errno.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <string.h>
400Sstevel@tonic-gate #include <unistd.h>
410Sstevel@tonic-gate #include <sys/types.h>
420Sstevel@tonic-gate #include <sys/stat.h>
430Sstevel@tonic-gate #include <stdarg.h>
440Sstevel@tonic-gate #include <limits.h>
450Sstevel@tonic-gate #include <signal.h>
460Sstevel@tonic-gate #include <sys/wait.h>
470Sstevel@tonic-gate #include <sys/mnttab.h>
480Sstevel@tonic-gate #include <sys/statvfs.h>
490Sstevel@tonic-gate #include <libnvpair.h>
500Sstevel@tonic-gate #include <ftw.h>
510Sstevel@tonic-gate #include <fcntl.h>
520Sstevel@tonic-gate #include <strings.h>
534581Ssherrym #include <utime.h>
540Sstevel@tonic-gate #include <sys/systeminfo.h>
550Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
562334Ssetje #include <sys/param.h>
576448Svikram #include <dirent.h>
586448Svikram #include <ctype.h>
596448Svikram #include <libgen.h>
606423Sgw25295 #include <sys/sysmacros.h>
616694Svikram #include <libscf.h>
625648Ssetje 
635648Ssetje #if !defined(_OPB)
644581Ssherrym #include <sys/ucode.h>
654581Ssherrym #endif
660Sstevel@tonic-gate 
670Sstevel@tonic-gate #include <pwd.h>
680Sstevel@tonic-gate #include <grp.h>
690Sstevel@tonic-gate #include <device_info.h>
706448Svikram #include <sys/vtoc.h>
716448Svikram #include <sys/efi_partition.h>
726448Svikram 
730Sstevel@tonic-gate #include <locale.h>
740Sstevel@tonic-gate 
750Sstevel@tonic-gate #include "message.h"
763446Smrj #include "bootadm.h"
770Sstevel@tonic-gate 
780Sstevel@tonic-gate #ifndef TEXT_DOMAIN
790Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
800Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
810Sstevel@tonic-gate 
820Sstevel@tonic-gate /* Type definitions */
830Sstevel@tonic-gate 
840Sstevel@tonic-gate /* Primary subcmds */
850Sstevel@tonic-gate typedef enum {
860Sstevel@tonic-gate 	BAM_MENU = 3,
870Sstevel@tonic-gate 	BAM_ARCHIVE
880Sstevel@tonic-gate } subcmd_t;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate typedef enum {
910Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
920Sstevel@tonic-gate     OPT_REQ,		/* option required */
930Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
940Sstevel@tonic-gate } option_t;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate typedef struct {
970Sstevel@tonic-gate 	char	*subcmd;
980Sstevel@tonic-gate 	option_t option;
990Sstevel@tonic-gate 	error_t (*handler)();
1002115Svikram 	int	unpriv;			/* is this an unprivileged command */
1010Sstevel@tonic-gate } subcmd_defn_t;
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
1040Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
1050Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
1086694Svikram #define	GRUB_STAGE2		GRUB_DIR "/stage2"
1090Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
1100Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1116694Svikram #define	GRUB_BACKUP_MENU	"/etc/lu/GRUB_backup_menu"
1120Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
113621Svikram #define	STUBBOOT		"/stubboot"
1146448Svikram #define	MULTIBOOT		"/platform/i86pc/multiboot"
1156448Svikram #define	GRUBSIGN_DIR		"/boot/grub/bootsign"
1166448Svikram #define	GRUBSIGN_BACKUP		"/etc/bootsign"
1176448Svikram #define	GRUBSIGN_UFS_PREFIX	"rootfs"
1186448Svikram #define	GRUBSIGN_ZFS_PREFIX	"pool_"
1196448Svikram #define	GRUBSIGN_LU_PREFIX	"BE_"
1206448Svikram #define	UFS_SIGNATURE_LIST	"/var/run/grub_ufs_signatures"
1216448Svikram #define	ZFS_LEGACY_MNTPT	"/tmp/bootadm_mnt_zfs_legacy"
1226448Svikram 
1236448Svikram #define	BOOTADM_RDONLY_TEST	"BOOTADM_RDONLY_TEST"
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate /* lock related */
1260Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1270Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1280Sstevel@tonic-gate 
1295648Ssetje #define	CREATE_RAMDISK		"boot/solaris/bin/create_ramdisk"
1305648Ssetje #define	CREATE_DISKMAP		"boot/solaris/bin/create_diskmap"
1315648Ssetje #define	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
1320Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
1330Sstevel@tonic-gate 
134316Svikram #define	GRUB_slice		"/etc/lu/GRUB_slice"
135316Svikram #define	GRUB_root		"/etc/lu/GRUB_root"
1361746Svikram #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
1371746Svikram #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
1386694Svikram #define	FINDROOT_INSTALLGRUB	"/etc/lu/installgrub.findroot"
1396694Svikram #define	LULIB			"/usr/lib/lu/lulib"
1406694Svikram #define	LULIB_PROPAGATE_FILE	"lulib_propagate_file"
1416694Svikram #define	CKSUM			"/usr/bin/cksum"
1426694Svikram #define	LU_MENU_CKSUM		"/etc/lu/menu.cksum"
1436694Svikram #define	BOOTADM			"/sbin/bootadm"
144316Svikram 
145316Svikram #define	INSTALLGRUB		"/sbin/installgrub"
146316Svikram #define	STAGE1			"/boot/grub/stage1"
147316Svikram #define	STAGE2			"/boot/grub/stage2"
148316Svikram 
1496448Svikram typedef enum zfs_mnted {
1506448Svikram 	ZFS_MNT_ERROR = -1,
1516448Svikram 	LEGACY_MOUNTED = 1,
1526448Svikram 	LEGACY_ALREADY,
1536448Svikram 	ZFS_MOUNTED,
1546448Svikram 	ZFS_ALREADY
1556448Svikram } zfs_mnted_t;
1566448Svikram 
1576448Svikram 
1586448Svikram 
1596448Svikram 
1600Sstevel@tonic-gate /*
1614493Snadkarni  * The following two defines are used to detect and create the correct
1624493Snadkarni  * boot archive  when safemode patching is underway.  LOFS_PATCH_FILE is a
1634493Snadkarni  * contracted private interface between bootadm and the install
1644493Snadkarni  * consolidation.  It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE
1654493Snadkarni  * is applied.
1664493Snadkarni  */
1674493Snadkarni 
1684493Snadkarni #define	LOFS_PATCH_FILE		"/var/run/.patch_loopback_mode"
1694493Snadkarni #define	LOFS_PATCH_MNT		"/var/run/.patch_root_loopbackmnt"
1704493Snadkarni 
1714493Snadkarni /*
1720Sstevel@tonic-gate  * Default file attributes
1730Sstevel@tonic-gate  */
1740Sstevel@tonic-gate #define	DEFAULT_DEV_MODE	0644	/* default permissions */
1750Sstevel@tonic-gate #define	DEFAULT_DEV_UID		0	/* user root */
1760Sstevel@tonic-gate #define	DEFAULT_DEV_GID		3	/* group sys */
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate /*
1790Sstevel@tonic-gate  * Menu related
1800Sstevel@tonic-gate  * menu_cmd_t and menu_cmds must be kept in sync
1810Sstevel@tonic-gate  */
1823446Smrj char *menu_cmds[] = {
1830Sstevel@tonic-gate 	"default",	/* DEFAULT_CMD */
1840Sstevel@tonic-gate 	"timeout",	/* TIMEOUT_CMD */
1850Sstevel@tonic-gate 	"title",	/* TITLE_CMD */
1860Sstevel@tonic-gate 	"root",		/* ROOT_CMD */
1870Sstevel@tonic-gate 	"kernel",	/* KERNEL_CMD */
1883446Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1890Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
1903446Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1910Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1920Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
1933446Smrj 	"chainloader",	/* CHAINLOADER_CMD */
1943446Smrj 	"args",		/* ARGS_CMD */
1956448Svikram 	"findroot",	/* FINDROOT_CMD */
1960Sstevel@tonic-gate 	NULL
1970Sstevel@tonic-gate };
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate #define	OPT_ENTRY_NUM	"entry"
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate /*
2026448Svikram  * exec_cmd related
2030Sstevel@tonic-gate  */
2040Sstevel@tonic-gate typedef struct {
2050Sstevel@tonic-gate 	line_t *head;
2060Sstevel@tonic-gate 	line_t *tail;
2070Sstevel@tonic-gate } filelist_t;
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
2100Sstevel@tonic-gate #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
2130Sstevel@tonic-gate #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
2140Sstevel@tonic-gate #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
2150Sstevel@tonic-gate #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate /* Globals */
2183446Smrj int bam_verbose;
2193446Smrj int bam_force;
2206448Svikram int bam_debug;
2210Sstevel@tonic-gate static char *prog;
2220Sstevel@tonic-gate static subcmd_t bam_cmd;
2230Sstevel@tonic-gate static char *bam_root;
2240Sstevel@tonic-gate static int bam_rootlen;
2250Sstevel@tonic-gate static int bam_root_readonly;
226621Svikram static int bam_alt_root;
2270Sstevel@tonic-gate static char *bam_subcmd;
2280Sstevel@tonic-gate static char *bam_opt;
2290Sstevel@tonic-gate static char **bam_argv;
2300Sstevel@tonic-gate static int bam_argc;
2310Sstevel@tonic-gate static int bam_check;
2320Sstevel@tonic-gate static int bam_smf_check;
2330Sstevel@tonic-gate static int bam_lock_fd = -1;
2346423Sgw25295 static int bam_zfs;
2350Sstevel@tonic-gate static char rootbuf[PATH_MAX] = "/";
236316Svikram static int bam_update_all;
2376319Sjg static int bam_alt_platform;
2386319Sjg static char *bam_platform;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate /* function prototypes */
2415648Ssetje static void parse_args_internal(int, char *[]);
2425648Ssetje static void parse_args(int, char *argv[]);
2435648Ssetje static error_t bam_menu(char *, char *, int, char *[]);
2445648Ssetje static error_t bam_archive(char *, char *);
2455648Ssetje 
2465648Ssetje static void bam_exit(int);
2470Sstevel@tonic-gate static void bam_lock(void);
2480Sstevel@tonic-gate static void bam_unlock(void);
2490Sstevel@tonic-gate 
2505648Ssetje static int exec_cmd(char *, filelist_t *);
2515648Ssetje static error_t read_globals(menu_t *, char *, char *, int);
2526448Svikram static int menu_on_bootdisk(char *os_root, char *menu_root);
2535648Ssetje static menu_t *menu_read(char *);
2545648Ssetje static error_t menu_write(char *, menu_t *);
2555648Ssetje static void linelist_free(line_t *);
2565648Ssetje static void menu_free(menu_t *);
2575648Ssetje static void filelist_free(filelist_t *);
2585648Ssetje static error_t list2file(char *, char *, char *, line_t *);
2595648Ssetje static error_t list_entry(menu_t *, char *, char *);
2605648Ssetje static error_t delete_all_entries(menu_t *, char *, char *);
2616448Svikram static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
2626448Svikram static error_t update_temp(menu_t *mp, char *dummy, char *opt);
2635648Ssetje 
2645648Ssetje static error_t update_archive(char *, char *);
2655648Ssetje static error_t list_archive(char *, char *);
2665648Ssetje static error_t update_all(char *, char *);
2675648Ssetje static error_t read_list(char *, filelist_t *);
2685648Ssetje static error_t set_global(menu_t *, char *, int);
2695648Ssetje static error_t set_option(menu_t *, char *, char *);
2705648Ssetje static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
2716448Svikram static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
2725648Ssetje static char *expand_path(const char *);
2735648Ssetje 
2745648Ssetje static long s_strtol(char *);
2755648Ssetje static int s_fputs(char *, FILE *);
2765648Ssetje 
2776448Svikram static int is_zfs(char *root);
2786448Svikram static int is_ufs(char *root);
2796448Svikram static int is_pcfs(char *root);
2800Sstevel@tonic-gate static int is_amd64(void);
2816582Ssetje static char *get_machine(void);
2820Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2836448Svikram static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted);
2846448Svikram static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt);
2856448Svikram static int ufs_add_to_sign_list(char *sign);
2866694Svikram static error_t synchronize_BE_menu(void);
2870Sstevel@tonic-gate 
2885648Ssetje #if !defined(_OPB)
2894581Ssherrym static void ucode_install();
2904581Ssherrym #endif
2914581Ssherrym 
2920Sstevel@tonic-gate /* Menu related sub commands */
2930Sstevel@tonic-gate static subcmd_defn_t menu_subcmds[] = {
2946448Svikram 	"set_option",		OPT_ABSENT,	set_option, 0,	/* PUB */
2952115Svikram 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
2962115Svikram 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
2972115Svikram 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
2982115Svikram 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
2993446Smrj 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
3002115Svikram 	NULL,			0,		NULL, 0	/* must be last */
3010Sstevel@tonic-gate };
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate /* Archive related sub commands */
3040Sstevel@tonic-gate static subcmd_defn_t arch_subcmds[] = {
3052115Svikram 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
3062115Svikram 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
3072115Svikram 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
3082115Svikram 	NULL,			0,		NULL, 0	/* must be last */
3090Sstevel@tonic-gate };
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate static struct {
3120Sstevel@tonic-gate 	nvlist_t *new_nvlp;
3130Sstevel@tonic-gate 	nvlist_t *old_nvlp;
3140Sstevel@tonic-gate 	int need_update;
3150Sstevel@tonic-gate } walk_arg;
3160Sstevel@tonic-gate 
3172334Ssetje 
3182345Ssetje struct safefile {
3192334Ssetje 	char *name;
3202334Ssetje 	struct safefile *next;
3212334Ssetje };
3222334Ssetje 
3233446Smrj static struct safefile *safefiles = NULL;
3242334Ssetje #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
3252334Ssetje 
3260Sstevel@tonic-gate static void
3270Sstevel@tonic-gate usage(void)
3280Sstevel@tonic-gate {
3290Sstevel@tonic-gate 	(void) fprintf(stderr, "USAGE:\n");
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	/* archive usage */
3336319Sjg 	(void) fprintf(stderr,
3346319Sjg 	    "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog);
3356319Sjg 	(void) fprintf(stderr,
3366319Sjg 	    "\t%s list-archive [-R altroot [-p platform>]]\n", prog);
3375648Ssetje #if !defined(_OPB)
3380Sstevel@tonic-gate 	/* x86 only */
3390Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
3400Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
3410Sstevel@tonic-gate #endif
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate int
3450Sstevel@tonic-gate main(int argc, char *argv[])
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate 	error_t ret;
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
3500Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
3530Sstevel@tonic-gate 		prog = argv[0];
3540Sstevel@tonic-gate 	} else {
3550Sstevel@tonic-gate 		prog++;
3560Sstevel@tonic-gate 	}
3570Sstevel@tonic-gate 
3586448Svikram 	INJECT_ERROR1("ASSERT_ON", assert(0))
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	/*
3610Sstevel@tonic-gate 	 * Don't depend on caller's umask
3620Sstevel@tonic-gate 	 */
3630Sstevel@tonic-gate 	(void) umask(0022);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	parse_args(argc, argv);
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	switch (bam_cmd) {
3680Sstevel@tonic-gate 		case BAM_MENU:
3690Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
3700Sstevel@tonic-gate 			break;
3710Sstevel@tonic-gate 		case BAM_ARCHIVE:
3720Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
3730Sstevel@tonic-gate 			break;
3740Sstevel@tonic-gate 		default:
3750Sstevel@tonic-gate 			usage();
3760Sstevel@tonic-gate 			bam_exit(1);
3770Sstevel@tonic-gate 	}
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
3800Sstevel@tonic-gate 		bam_exit(1);
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	bam_unlock();
3830Sstevel@tonic-gate 	return (0);
3840Sstevel@tonic-gate }
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate /*
3870Sstevel@tonic-gate  * Equivalence of public and internal commands:
3880Sstevel@tonic-gate  *	update-archive  -- -a update
3890Sstevel@tonic-gate  *	list-archive	-- -a list
3900Sstevel@tonic-gate  *	set-menu	-- -m set_option
3910Sstevel@tonic-gate  *	list-menu	-- -m list_entry
3920Sstevel@tonic-gate  *	update-menu	-- -m update_entry
3930Sstevel@tonic-gate  */
3940Sstevel@tonic-gate static struct cmd_map {
3950Sstevel@tonic-gate 	char *bam_cmdname;
3960Sstevel@tonic-gate 	int bam_cmd;
3970Sstevel@tonic-gate 	char *bam_subcmd;
3980Sstevel@tonic-gate } cmd_map[] = {
3990Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
4000Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
4010Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
4020Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
4030Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
4040Sstevel@tonic-gate 	{ NULL,			0,		NULL}
4050Sstevel@tonic-gate };
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate /*
4080Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
4090Sstevel@tonic-gate  */
4100Sstevel@tonic-gate static void
4110Sstevel@tonic-gate parse_args(int argc, char *argv[])
4120Sstevel@tonic-gate {
4130Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	/* command conforming to the final spec */
4160Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
4170Sstevel@tonic-gate 		/*
4180Sstevel@tonic-gate 		 * Map commands to internal table.
4190Sstevel@tonic-gate 		 */
4200Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
4210Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
4220Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
4230Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
4240Sstevel@tonic-gate 				break;
4250Sstevel@tonic-gate 			}
4260Sstevel@tonic-gate 			cmp++;
4270Sstevel@tonic-gate 		}
4280Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
4290Sstevel@tonic-gate 			usage();
4300Sstevel@tonic-gate 			bam_exit(1);
4310Sstevel@tonic-gate 		}
4320Sstevel@tonic-gate 		argc--;
4330Sstevel@tonic-gate 		argv++;
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	parse_args_internal(argc, argv);
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate /*
4400Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
4410Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
4420Sstevel@tonic-gate  *	-a update	-- update-archive
4430Sstevel@tonic-gate  *	-a list		-- list-archive
4440Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
4450Sstevel@tonic-gate  *	-m update_entry	-- update-menu
4460Sstevel@tonic-gate  *	-m list_entry	-- list-menu
4470Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
4480Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
4490Sstevel@tonic-gate  */
4500Sstevel@tonic-gate static void
4510Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
4520Sstevel@tonic-gate {
4530Sstevel@tonic-gate 	int c, error;
4540Sstevel@tonic-gate 	extern char *optarg;
4550Sstevel@tonic-gate 	extern int optind, opterr;
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	/* Suppress error message from getopt */
4580Sstevel@tonic-gate 	opterr = 0;
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	error = 0;
4616423Sgw25295 	while ((c = getopt(argc, argv, "a:d:fm:no:vCR:p:Z")) != -1) {
4620Sstevel@tonic-gate 		switch (c) {
4630Sstevel@tonic-gate 		case 'a':
4640Sstevel@tonic-gate 			if (bam_cmd) {
4650Sstevel@tonic-gate 				error = 1;
4660Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4670Sstevel@tonic-gate 			}
4680Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
4690Sstevel@tonic-gate 			bam_subcmd = optarg;
4700Sstevel@tonic-gate 			break;
4710Sstevel@tonic-gate 		case 'd':
4720Sstevel@tonic-gate 			if (bam_debug) {
4730Sstevel@tonic-gate 				error = 1;
4740Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4750Sstevel@tonic-gate 			}
4760Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
4770Sstevel@tonic-gate 			break;
4780Sstevel@tonic-gate 		case 'f':
4790Sstevel@tonic-gate 			if (bam_force) {
4800Sstevel@tonic-gate 				error = 1;
4810Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4820Sstevel@tonic-gate 			}
4830Sstevel@tonic-gate 			bam_force = 1;
4840Sstevel@tonic-gate 			break;
4850Sstevel@tonic-gate 		case 'm':
4860Sstevel@tonic-gate 			if (bam_cmd) {
4870Sstevel@tonic-gate 				error = 1;
4880Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
4890Sstevel@tonic-gate 			}
4900Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
4910Sstevel@tonic-gate 			bam_subcmd = optarg;
4920Sstevel@tonic-gate 			break;
4930Sstevel@tonic-gate 		case 'n':
4940Sstevel@tonic-gate 			if (bam_check) {
4950Sstevel@tonic-gate 				error = 1;
4960Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
4970Sstevel@tonic-gate 			}
4980Sstevel@tonic-gate 			bam_check = 1;
4990Sstevel@tonic-gate 			break;
5000Sstevel@tonic-gate 		case 'o':
5010Sstevel@tonic-gate 			if (bam_opt) {
5020Sstevel@tonic-gate 				error = 1;
5030Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
5040Sstevel@tonic-gate 			}
5050Sstevel@tonic-gate 			bam_opt = optarg;
5060Sstevel@tonic-gate 			break;
5070Sstevel@tonic-gate 		case 'v':
5080Sstevel@tonic-gate 			if (bam_verbose) {
5090Sstevel@tonic-gate 				error = 1;
5100Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
5110Sstevel@tonic-gate 			}
5120Sstevel@tonic-gate 			bam_verbose = 1;
5130Sstevel@tonic-gate 			break;
514662Sszhou 		case 'C':
515662Sszhou 			bam_smf_check = 1;
516662Sszhou 			break;
5170Sstevel@tonic-gate 		case 'R':
5180Sstevel@tonic-gate 			if (bam_root) {
5190Sstevel@tonic-gate 				error = 1;
5200Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
5210Sstevel@tonic-gate 				break;
5220Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
5230Sstevel@tonic-gate 				error = 1;
5240Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
5250Sstevel@tonic-gate 				    strerror(errno));
5260Sstevel@tonic-gate 				break;
5270Sstevel@tonic-gate 			}
528621Svikram 			bam_alt_root = 1;
5290Sstevel@tonic-gate 			bam_root = rootbuf;
5300Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
5310Sstevel@tonic-gate 			break;
5326319Sjg 		case 'p':
5336319Sjg 			bam_alt_platform = 1;
5346319Sjg 			bam_platform = optarg;
5356319Sjg 			if ((strcmp(bam_platform, "i86pc") != 0) &&
5366319Sjg 			    (strcmp(bam_platform, "sun4u") != 0) &&
5376319Sjg 			    (strcmp(bam_platform, "sun4v") != 0)) {
5386319Sjg 				error = 1;
5396319Sjg 				bam_error(INVALID_PLAT, bam_platform);
5406319Sjg 			}
5416319Sjg 			break;
5426423Sgw25295 		case 'Z':
5436423Sgw25295 			bam_zfs = 1;
5446423Sgw25295 			break;
5450Sstevel@tonic-gate 		case '?':
5460Sstevel@tonic-gate 			error = 1;
5470Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
5480Sstevel@tonic-gate 			break;
5490Sstevel@tonic-gate 		default :
5500Sstevel@tonic-gate 			error = 1;
5510Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
5520Sstevel@tonic-gate 			break;
5530Sstevel@tonic-gate 		}
5540Sstevel@tonic-gate 	}
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	/*
5576319Sjg 	 * An alternate platform requires an alternate root
5586319Sjg 	 */
5596319Sjg 	if (bam_alt_platform && bam_alt_root == 0) {
5606319Sjg 		usage();
5616319Sjg 		bam_exit(0);
5626319Sjg 	}
5636319Sjg 
5646319Sjg 	/*
5650Sstevel@tonic-gate 	 * A command option must be specfied
5660Sstevel@tonic-gate 	 */
5670Sstevel@tonic-gate 	if (!bam_cmd) {
5680Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
5690Sstevel@tonic-gate 			usage();
5700Sstevel@tonic-gate 			bam_exit(0);
5710Sstevel@tonic-gate 		}
5720Sstevel@tonic-gate 		bam_error(NEED_CMD);
5730Sstevel@tonic-gate 		error = 1;
5740Sstevel@tonic-gate 	}
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 	if (error) {
5770Sstevel@tonic-gate 		usage();
5780Sstevel@tonic-gate 		bam_exit(1);
5790Sstevel@tonic-gate 	}
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	if (optind > argc) {
5820Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
5830Sstevel@tonic-gate 		bam_exit(1);
5840Sstevel@tonic-gate 	} else if (optind < argc) {
5850Sstevel@tonic-gate 		bam_argv = &argv[optind];
5860Sstevel@tonic-gate 		bam_argc = argc - optind;
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	/*
5900Sstevel@tonic-gate 	 * -n implies verbose mode
5910Sstevel@tonic-gate 	 */
5920Sstevel@tonic-gate 	if (bam_check)
5930Sstevel@tonic-gate 		bam_verbose = 1;
5940Sstevel@tonic-gate }
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate static error_t
5970Sstevel@tonic-gate check_subcmd_and_options(
5980Sstevel@tonic-gate 	char *subcmd,
5990Sstevel@tonic-gate 	char *opt,
6000Sstevel@tonic-gate 	subcmd_defn_t *table,
6010Sstevel@tonic-gate 	error_t (**fp)())
6020Sstevel@tonic-gate {
6030Sstevel@tonic-gate 	int i;
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	if (subcmd == NULL) {
6060Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
6070Sstevel@tonic-gate 		return (BAM_ERROR);
6080Sstevel@tonic-gate 	}
6090Sstevel@tonic-gate 
6106448Svikram 	if (strcmp(subcmd, "set_option") == 0) {
6116448Svikram 		if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
6126448Svikram 			bam_error(MISSING_ARG);
6136448Svikram 			usage();
6146448Svikram 			return (BAM_ERROR);
6156448Svikram 		} else if (bam_argc > 1 || bam_argv[1] != NULL) {
6162115Svikram 			bam_error(TRAILING_ARGS);
6172115Svikram 			usage();
6182115Svikram 			return (BAM_ERROR);
6192115Svikram 		}
6206448Svikram 	} else if (bam_argc || bam_argv) {
6216448Svikram 		bam_error(TRAILING_ARGS);
6226448Svikram 		usage();
6236448Svikram 		return (BAM_ERROR);
6242115Svikram 	}
6252115Svikram 
6260Sstevel@tonic-gate 	if (bam_root == NULL) {
6270Sstevel@tonic-gate 		bam_root = rootbuf;
6280Sstevel@tonic-gate 		bam_rootlen = 1;
6290Sstevel@tonic-gate 	}
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	/* verify that subcmd is valid */
6320Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
6330Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
6340Sstevel@tonic-gate 			break;
6350Sstevel@tonic-gate 	}
6360Sstevel@tonic-gate 
6370Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
6380Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
6390Sstevel@tonic-gate 		return (BAM_ERROR);
6400Sstevel@tonic-gate 	}
6410Sstevel@tonic-gate 
6422115Svikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
6432115Svikram 		bam_error(MUST_BE_ROOT);
6442115Svikram 		return (BAM_ERROR);
6452115Svikram 	}
6462115Svikram 
6472115Svikram 	/*
6482115Svikram 	 * Currently only privileged commands need a lock
6492115Svikram 	 */
6502115Svikram 	if (table[i].unpriv == 0)
6512115Svikram 		bam_lock();
6522115Svikram 
6530Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
6540Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
6550Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
6560Sstevel@tonic-gate 			if (opt)
6570Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
6580Sstevel@tonic-gate 			else
6590Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
6600Sstevel@tonic-gate 			return (BAM_ERROR);
6610Sstevel@tonic-gate 		}
6620Sstevel@tonic-gate 	}
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	*fp = table[i].handler;
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	return (BAM_SUCCESS);
6670Sstevel@tonic-gate }
6680Sstevel@tonic-gate 
669621Svikram /*
670621Svikram  * NOTE: A single "/" is also considered a trailing slash and will
671621Svikram  * be deleted.
672621Svikram  */
673621Svikram static void
674621Svikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
675621Svikram {
676621Svikram 	size_t dstlen;
677621Svikram 
678621Svikram 	assert(src);
679621Svikram 	assert(dst);
680621Svikram 
681621Svikram 	(void) strlcpy(dst, src, dstsize);
682621Svikram 
683621Svikram 	dstlen = strlen(dst);
684621Svikram 	if (dst[dstlen - 1] == '/') {
685621Svikram 		dst[dstlen - 1] = '\0';
686621Svikram 	}
687621Svikram }
688621Svikram 
6890Sstevel@tonic-gate static error_t
6900Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
6910Sstevel@tonic-gate {
6926448Svikram 	error_t			ret;
6936448Svikram 	char			menu_path[PATH_MAX];
6946448Svikram 	char			clean_menu_root[PATH_MAX];
6956448Svikram 	char			path[PATH_MAX];
6966448Svikram 	menu_t			*menu;
6976448Svikram 	char			menu_root[PATH_MAX];
6986448Svikram 	struct stat		sb;
6990Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
7006448Svikram 	char			*special;
7016448Svikram 	char			*pool = NULL;
7026448Svikram 	zfs_mnted_t		zmnted;
7036448Svikram 	char			*zmntpt;
7046448Svikram 	char			*osdev;
7056448Svikram 	char			*osroot;
7066448Svikram 	const char		*fcn = "bam_menu()";
7075648Ssetje 
7085648Ssetje 	/*
7095648Ssetje 	 * Menu sub-command only applies to GRUB (i.e. x86)
7105648Ssetje 	 */
7116448Svikram 	if (!is_grub(bam_alt_root ? bam_root : "/")) {
7126448Svikram 		bam_error(NOT_GRUB_BOOT);
7135648Ssetje 		return (BAM_ERROR);
7145648Ssetje 	}
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 	/*
7170Sstevel@tonic-gate 	 * Check arguments
7180Sstevel@tonic-gate 	 */
7190Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
7200Sstevel@tonic-gate 	if (ret == BAM_ERROR) {
7210Sstevel@tonic-gate 		return (BAM_ERROR);
7220Sstevel@tonic-gate 	}
7230Sstevel@tonic-gate 
7246448Svikram 	assert(bam_root);
7256448Svikram 
7266448Svikram 	(void) strlcpy(menu_root, bam_root, sizeof (menu_root));
7276448Svikram 	osdev = osroot = NULL;
7286448Svikram 
7296448Svikram 	if (strcmp(subcmd, "update_entry") == 0) {
7306448Svikram 		assert(opt);
7316448Svikram 
7326448Svikram 		osdev = strtok(opt, ",");
7336448Svikram 		assert(osdev);
7346448Svikram 		osroot = strtok(NULL, ",");
7356448Svikram 		if (osroot) {
7366448Svikram 			/* fixup bam_root so that it points at osroot */
7376448Svikram 			if (realpath(osroot, rootbuf) == NULL) {
7386448Svikram 				bam_error(CANT_RESOLVE, osroot,
7396448Svikram 				    strerror(errno));
7406448Svikram 				return (BAM_ERROR);
7416448Svikram 			}
7426448Svikram 			bam_alt_root = 1;
7436448Svikram 			bam_root  = rootbuf;
7446448Svikram 			bam_rootlen = strlen(rootbuf);
7456448Svikram 		}
746621Svikram 	}
747621Svikram 
7486423Sgw25295 	/*
7496448Svikram 	 * We support menu on PCFS (under certain conditions), but
7506448Svikram 	 * not the OS root
7516423Sgw25295 	 */
7526448Svikram 	if (is_pcfs(bam_root)) {
7536448Svikram 		bam_error(PCFS_ROOT_NOTSUP, bam_root);
7546448Svikram 		return (BAM_ERROR);
7556448Svikram 	}
7566448Svikram 
7576448Svikram 	if (stat(menu_root, &sb) == -1) {
7586448Svikram 		bam_error(CANNOT_LOCATE_GRUB_MENU);
7596448Svikram 		return (BAM_ERROR);
7606448Svikram 	}
7616448Svikram 
7626448Svikram 	BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root));
7636423Sgw25295 
7646423Sgw25295 	/*
7656448Svikram 	 * We no longer use the GRUB slice file. If it exists, then
7666448Svikram 	 * the user is doing something that is unsupported (such as
7676448Svikram 	 * standard upgrading an old Live Upgrade BE). If that
7686448Svikram 	 * happens, mimic existing behavior i.e. pretend that it is
7696448Svikram 	 * not a BE. Emit a warning though.
7706423Sgw25295 	 */
7716448Svikram 	if (bam_alt_root) {
7726448Svikram 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
7736448Svikram 		    GRUB_slice);
7746448Svikram 	} else {
7756448Svikram 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
7766448Svikram 	}
7776448Svikram 
7786448Svikram 	if (stat(path, &sb) == 0)
7796448Svikram 		bam_error(GRUB_SLICE_FILE_EXISTS, path);
7806448Svikram 
7816448Svikram 	if (is_zfs(menu_root)) {
7826448Svikram 		assert(strcmp(menu_root, bam_root) == 0);
7836448Svikram 		special = get_special(menu_root);
7846448Svikram 		INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
7856448Svikram 		if (special == NULL) {
7866448Svikram 			bam_error(CANT_FIND_SPECIAL, menu_root);
7876448Svikram 			return (BAM_ERROR);
7886448Svikram 		}
7896448Svikram 		pool = strtok(special, "/");
7906448Svikram 		INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
7916448Svikram 		if (pool == NULL) {
7926448Svikram 			free(special);
7936448Svikram 			bam_error(CANT_FIND_POOL, menu_root);
7946448Svikram 			return (BAM_ERROR);
7956448Svikram 		}
7966448Svikram 		BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool));
7976448Svikram 
7986448Svikram 		zmntpt = mount_top_dataset(pool, &zmnted);
7996448Svikram 		INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
8006448Svikram 		if (zmntpt == NULL) {
8016448Svikram 			bam_error(CANT_MOUNT_POOL_DATASET, pool);
8026448Svikram 			free(special);
8036448Svikram 			return (BAM_ERROR);
8046448Svikram 		}
8056448Svikram 		BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt));
8066448Svikram 
8076448Svikram 		(void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
8086448Svikram 		BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root));
8096448Svikram 	}
8106448Svikram 
8116448Svikram 	elide_trailing_slash(menu_root, clean_menu_root,
8126448Svikram 	    sizeof (clean_menu_root));
8136448Svikram 
8146448Svikram 	BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root));
8156448Svikram 
8166448Svikram 	(void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
817621Svikram 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
818621Svikram 
8196448Svikram 	BAM_DPRINTF((D_MENU_PATH, fcn, menu_path));
8206448Svikram 
821621Svikram 	/*
8226448Svikram 	 * If listing the menu, display the menu location
823621Svikram 	 */
824621Svikram 	if (strcmp(subcmd, "list_entry") == 0) {
8256448Svikram 		bam_print(GRUB_MENU_PATH, menu_path);
8266448Svikram 	}
8276448Svikram 
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	menu = menu_read(menu_path);
8300Sstevel@tonic-gate 	assert(menu);
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	/*
8336448Svikram 	 * We already checked the following case in
8346448Svikram 	 * check_subcmd_and_suboptions() above. Complete the
8356448Svikram 	 * final step now.
8360Sstevel@tonic-gate 	 */
8370Sstevel@tonic-gate 	if (strcmp(subcmd, "set_option") == 0) {
8386448Svikram 		assert(largc == 1 && largv[0] && largv[1] == NULL);
8390Sstevel@tonic-gate 		opt = largv[0];
8406448Svikram 	} else {
8416448Svikram 		assert(largc == 0 && largv == NULL);
8426448Svikram 	}
8436448Svikram 
8446448Svikram 	ret = get_boot_cap(bam_root);
8456448Svikram 	if (ret != BAM_SUCCESS) {
8466448Svikram 		BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
8476448Svikram 		goto out;
8486448Svikram 	}
8493446Smrj 
8500Sstevel@tonic-gate 	/*
8510Sstevel@tonic-gate 	 * Once the sub-cmd handler has run
8520Sstevel@tonic-gate 	 * only the line field is guaranteed to have valid values
8530Sstevel@tonic-gate 	 */
8546448Svikram 	if (strcmp(subcmd, "update_entry") == 0)
8556448Svikram 		ret = f(menu, menu_root, osdev);
8566448Svikram 	else if (strcmp(subcmd, "upgrade") == 0)
8576448Svikram 		ret = f(menu, bam_root, menu_root);
8586448Svikram 	else if (strcmp(subcmd, "list_entry") == 0)
8596448Svikram 		ret = f(menu, menu_path, opt);
8600Sstevel@tonic-gate 	else
8616448Svikram 		ret = f(menu, NULL, opt);
8626448Svikram 
8630Sstevel@tonic-gate 	if (ret == BAM_WRITE) {
8646448Svikram 		BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root));
8656448Svikram 		ret = menu_write(clean_menu_root, menu);
8666448Svikram 	}
8676448Svikram 
8686448Svikram out:
8696448Svikram 	INJECT_ERROR1("POOL_SET", pool = "/pooldata");
8706448Svikram 	assert((is_zfs(menu_root)) ^ (pool == NULL));
8716448Svikram 	if (pool) {
8726448Svikram 		(void) umount_top_dataset(pool, zmnted, zmntpt);
8736448Svikram 		free(special);
8746448Svikram 	}
8750Sstevel@tonic-gate 	menu_free(menu);
8760Sstevel@tonic-gate 	return (ret);
8770Sstevel@tonic-gate }
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate static error_t
8810Sstevel@tonic-gate bam_archive(
8820Sstevel@tonic-gate 	char *subcmd,
8830Sstevel@tonic-gate 	char *opt)
8840Sstevel@tonic-gate {
8856448Svikram 	error_t			ret;
8866448Svikram 	error_t			(*f)(char *root, char *opt);
8876448Svikram 	const char		*fcn = "bam_archive()";
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 	/*
890662Sszhou 	 * Add trailing / for archive subcommands
891662Sszhou 	 */
892662Sszhou 	if (rootbuf[strlen(rootbuf) - 1] != '/')
893662Sszhou 		(void) strcat(rootbuf, "/");
894662Sszhou 	bam_rootlen = strlen(rootbuf);
895662Sszhou 
896662Sszhou 	/*
8970Sstevel@tonic-gate 	 * Check arguments
8980Sstevel@tonic-gate 	 */
8990Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
9000Sstevel@tonic-gate 	if (ret != BAM_SUCCESS) {
9010Sstevel@tonic-gate 		return (BAM_ERROR);
9020Sstevel@tonic-gate 	}
9030Sstevel@tonic-gate 
9046448Svikram 	ret = get_boot_cap(rootbuf);
9056448Svikram 	if (ret != BAM_SUCCESS) {
9066448Svikram 		BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
9073446Smrj 		return (ret);
9086448Svikram 	}
9093446Smrj 
9100Sstevel@tonic-gate 	/*
9110Sstevel@tonic-gate 	 * Check archive not supported with update_all
9120Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
9130Sstevel@tonic-gate 	 * information for each BE.
9140Sstevel@tonic-gate 	 */
9150Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
9160Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
9170Sstevel@tonic-gate 		return (BAM_ERROR);
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate 
920316Svikram 	if (strcmp(subcmd, "update_all") == 0)
921316Svikram 		bam_update_all = 1;
922316Svikram 
9235648Ssetje #if !defined(_OPB)
9244581Ssherrym 	ucode_install(bam_root);
9254581Ssherrym #endif
9264581Ssherrym 
927316Svikram 	ret = f(bam_root, opt);
928316Svikram 
929316Svikram 	bam_update_all = 0;
930316Svikram 
931316Svikram 	return (ret);
9320Sstevel@tonic-gate }
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate /*PRINTFLIKE1*/
9353446Smrj void
9360Sstevel@tonic-gate bam_error(char *format, ...)
9370Sstevel@tonic-gate {
9380Sstevel@tonic-gate 	va_list ap;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	va_start(ap, format);
9410Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
9420Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
9430Sstevel@tonic-gate 	va_end(ap);
9440Sstevel@tonic-gate }
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate /*PRINTFLIKE1*/
9476448Svikram void
9486448Svikram bam_derror(char *format, ...)
9496448Svikram {
9506448Svikram 	va_list ap;
9516448Svikram 
9526448Svikram 	assert(bam_debug);
9536448Svikram 
9546448Svikram 	va_start(ap, format);
9556448Svikram 	(void) fprintf(stderr, "DEBUG: ");
9566448Svikram 	(void) vfprintf(stderr, format, ap);
9576448Svikram 	va_end(ap);
9586448Svikram }
9596448Svikram 
9606448Svikram /*PRINTFLIKE1*/
9616448Svikram void
9620Sstevel@tonic-gate bam_print(char *format, ...)
9630Sstevel@tonic-gate {
9640Sstevel@tonic-gate 	va_list ap;
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	va_start(ap, format);
9670Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
9680Sstevel@tonic-gate 	va_end(ap);
9690Sstevel@tonic-gate }
9700Sstevel@tonic-gate 
9713446Smrj /*PRINTFLIKE1*/
9723446Smrj void
9733446Smrj bam_print_stderr(char *format, ...)
9743446Smrj {
9753446Smrj 	va_list ap;
9763446Smrj 
9773446Smrj 	va_start(ap, format);
9783446Smrj 	(void) vfprintf(stderr, format, ap);
9793446Smrj 	va_end(ap);
9803446Smrj }
9813446Smrj 
9820Sstevel@tonic-gate static void
9830Sstevel@tonic-gate bam_exit(int excode)
9840Sstevel@tonic-gate {
9850Sstevel@tonic-gate 	bam_unlock();
9860Sstevel@tonic-gate 	exit(excode);
9870Sstevel@tonic-gate }
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate static void
9900Sstevel@tonic-gate bam_lock(void)
9910Sstevel@tonic-gate {
9920Sstevel@tonic-gate 	struct flock lock;
9930Sstevel@tonic-gate 	pid_t pid;
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
9960Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
9970Sstevel@tonic-gate 		/*
9980Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
9990Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
10000Sstevel@tonic-gate 		 * Proceed without the lock
10010Sstevel@tonic-gate 		 */
10020Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
10030Sstevel@tonic-gate 			bam_root_readonly = 1;
10040Sstevel@tonic-gate 			return;
10050Sstevel@tonic-gate 		}
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
10080Sstevel@tonic-gate 		bam_exit(1);
10090Sstevel@tonic-gate 	}
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
10120Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
10130Sstevel@tonic-gate 	lock.l_start = 0;
10140Sstevel@tonic-gate 	lock.l_len = 0;
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
10170Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
10180Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
10190Sstevel@tonic-gate 			(void) close(bam_lock_fd);
10200Sstevel@tonic-gate 			bam_lock_fd = -1;
10210Sstevel@tonic-gate 			bam_exit(1);
10220Sstevel@tonic-gate 		}
10230Sstevel@tonic-gate 		pid = 0;
10240Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
10250Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
10280Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
10290Sstevel@tonic-gate 		lock.l_start = 0;
10300Sstevel@tonic-gate 		lock.l_len = 0;
10310Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
10320Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
10330Sstevel@tonic-gate 			(void) close(bam_lock_fd);
10340Sstevel@tonic-gate 			bam_lock_fd = -1;
10350Sstevel@tonic-gate 			bam_exit(1);
10360Sstevel@tonic-gate 		}
10370Sstevel@tonic-gate 	}
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	/* We own the lock now */
10400Sstevel@tonic-gate 	pid = getpid();
10410Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
10420Sstevel@tonic-gate }
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate static void
10450Sstevel@tonic-gate bam_unlock(void)
10460Sstevel@tonic-gate {
10470Sstevel@tonic-gate 	struct flock unlock;
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	/*
10500Sstevel@tonic-gate 	 * NOP if we don't hold the lock
10510Sstevel@tonic-gate 	 */
10520Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
10530Sstevel@tonic-gate 		return;
10540Sstevel@tonic-gate 	}
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
10570Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
10580Sstevel@tonic-gate 	unlock.l_start = 0;
10590Sstevel@tonic-gate 	unlock.l_len = 0;
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
10620Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
10630Sstevel@tonic-gate 	}
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
10660Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
10670Sstevel@tonic-gate 	}
10680Sstevel@tonic-gate 	bam_lock_fd = -1;
10690Sstevel@tonic-gate }
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate static error_t
10720Sstevel@tonic-gate list_archive(char *root, char *opt)
10730Sstevel@tonic-gate {
10740Sstevel@tonic-gate 	filelist_t flist;
10750Sstevel@tonic-gate 	filelist_t *flistp = &flist;
10760Sstevel@tonic-gate 	line_t *lp;
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	assert(root);
10790Sstevel@tonic-gate 	assert(opt == NULL);
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
10820Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
10830Sstevel@tonic-gate 		return (BAM_ERROR);
10840Sstevel@tonic-gate 	}
10850Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
10860Sstevel@tonic-gate 
10870Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
10880Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
10890Sstevel@tonic-gate 	}
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	filelist_free(flistp);
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	return (BAM_SUCCESS);
10940Sstevel@tonic-gate }
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate /*
10970Sstevel@tonic-gate  * This routine writes a list of lines to a file.
10980Sstevel@tonic-gate  * The list is *not* freed
10990Sstevel@tonic-gate  */
11000Sstevel@tonic-gate static error_t
11010Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
11020Sstevel@tonic-gate {
11036448Svikram 	char		tmpfile[PATH_MAX];
11046448Svikram 	char		path[PATH_MAX];
11056448Svikram 	FILE		*fp;
11066448Svikram 	int		ret;
11076448Svikram 	struct stat	sb;
11086448Svikram 	mode_t		mode;
11096448Svikram 	uid_t		root_uid;
11106448Svikram 	gid_t		sys_gid;
11116448Svikram 	struct passwd	*pw;
11126448Svikram 	struct group	*gp;
11136448Svikram 	const char	*fcn = "list2file()";
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	if (start == NULL) {
11186448Svikram 		/* Empty GRUB menu */
11190Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
11200Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
11210Sstevel@tonic-gate 			if (unlink(path) != 0) {
11220Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
11230Sstevel@tonic-gate 				return (BAM_ERROR);
11240Sstevel@tonic-gate 			} else {
11250Sstevel@tonic-gate 				return (BAM_SUCCESS);
11260Sstevel@tonic-gate 			}
11270Sstevel@tonic-gate 		}
11286448Svikram 		return (BAM_SUCCESS);
11290Sstevel@tonic-gate 	}
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	/*
11320Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
11330Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
11340Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
11350Sstevel@tonic-gate 	 */
11360Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
11370Sstevel@tonic-gate 		mode = sb.st_mode;
11380Sstevel@tonic-gate 		root_uid = sb.st_uid;
11390Sstevel@tonic-gate 		sys_gid = sb.st_gid;
11400Sstevel@tonic-gate 	} else {
11410Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
11420Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
11430Sstevel@tonic-gate 			root_uid = pw->pw_uid;
11440Sstevel@tonic-gate 		} else {
11456448Svikram 			bam_error(CANT_FIND_USER,
11466448Svikram 			    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
11470Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
11480Sstevel@tonic-gate 		}
11490Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
11500Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
11510Sstevel@tonic-gate 		} else {
11526448Svikram 			bam_error(CANT_FIND_GROUP,
11536448Svikram 			    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
11540Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
11550Sstevel@tonic-gate 		}
11560Sstevel@tonic-gate 	}
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate 	/* Truncate tmpfile first */
11610Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
11620Sstevel@tonic-gate 	if (fp == NULL) {
11630Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
11640Sstevel@tonic-gate 		return (BAM_ERROR);
11650Sstevel@tonic-gate 	}
11666694Svikram 	ret = fclose(fp);
11676694Svikram 	INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
11686694Svikram 	if (ret == EOF) {
11690Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
11700Sstevel@tonic-gate 		return (BAM_ERROR);
11710Sstevel@tonic-gate 	}
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 	/* Now open it in append mode */
11740Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
11750Sstevel@tonic-gate 	if (fp == NULL) {
11760Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
11770Sstevel@tonic-gate 		return (BAM_ERROR);
11780Sstevel@tonic-gate 	}
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 	for (; start; start = start->next) {
11816694Svikram 		ret = s_fputs(start->line, fp);
11826694Svikram 		INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
11836694Svikram 		if (ret == EOF) {
11840Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
11850Sstevel@tonic-gate 			(void) fclose(fp);
11860Sstevel@tonic-gate 			return (BAM_ERROR);
11870Sstevel@tonic-gate 		}
11880Sstevel@tonic-gate 	}
11890Sstevel@tonic-gate 
11906694Svikram 	ret = fclose(fp);
11916694Svikram 	INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
11926694Svikram 	if (ret == EOF) {
11930Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
11940Sstevel@tonic-gate 		return (BAM_ERROR);
11950Sstevel@tonic-gate 	}
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate 	/*
1198271Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1199271Sjg 	 * not supporting these operations - pcfs reports unsupported
1200271Sjg 	 * operations as EINVAL.
12010Sstevel@tonic-gate 	 */
12020Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1203271Sjg 	if (ret == -1 &&
1204271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
12050Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
12060Sstevel@tonic-gate 		return (BAM_ERROR);
12070Sstevel@tonic-gate 	}
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1210271Sjg 	if (ret == -1 &&
1211271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
12120Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
12130Sstevel@tonic-gate 		return (BAM_ERROR);
12140Sstevel@tonic-gate 	}
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 	/*
12180Sstevel@tonic-gate 	 * Do an atomic rename
12190Sstevel@tonic-gate 	 */
12200Sstevel@tonic-gate 	ret = rename(tmpfile, path);
12216694Svikram 	INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
12220Sstevel@tonic-gate 	if (ret != 0) {
12230Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
12240Sstevel@tonic-gate 		return (BAM_ERROR);
12250Sstevel@tonic-gate 	}
12260Sstevel@tonic-gate 
12276448Svikram 	BAM_DPRINTF((D_WROTE_FILE, fcn, path));
12280Sstevel@tonic-gate 	return (BAM_SUCCESS);
12290Sstevel@tonic-gate }
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate /*
12320Sstevel@tonic-gate  * This function should always return 0 - since we want
12330Sstevel@tonic-gate  * to create stat data for *all* files in the list.
12340Sstevel@tonic-gate  */
12350Sstevel@tonic-gate /*ARGSUSED*/
12360Sstevel@tonic-gate static int
12370Sstevel@tonic-gate cmpstat(
12380Sstevel@tonic-gate 	const char *file,
12390Sstevel@tonic-gate 	const struct stat *stat,
12400Sstevel@tonic-gate 	int flags,
12410Sstevel@tonic-gate 	struct FTW *ftw)
12420Sstevel@tonic-gate {
12430Sstevel@tonic-gate 	uint_t sz;
12440Sstevel@tonic-gate 	uint64_t *value;
12450Sstevel@tonic-gate 	uint64_t filestat[2];
12460Sstevel@tonic-gate 	int error;
12470Sstevel@tonic-gate 
12482334Ssetje 	struct safefile *safefilep;
12492334Ssetje 	FILE *fp;
12502334Ssetje 
12510Sstevel@tonic-gate 	/*
12520Sstevel@tonic-gate 	 * We only want regular files
12530Sstevel@tonic-gate 	 */
12540Sstevel@tonic-gate 	if (!S_ISREG(stat->st_mode))
12550Sstevel@tonic-gate 		return (0);
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	/*
12580Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
12590Sstevel@tonic-gate 	 * but this is not fatal to update determination.
12600Sstevel@tonic-gate 	 */
12610Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
12620Sstevel@tonic-gate 		filestat[0] = stat->st_size;
12630Sstevel@tonic-gate 		filestat[1] = stat->st_mtime;
12640Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
12650Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
12660Sstevel@tonic-gate 		if (error)
12670Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
12680Sstevel@tonic-gate 	}
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate 	/*
12710Sstevel@tonic-gate 	 * The remaining steps are only required if we haven't made a
12720Sstevel@tonic-gate 	 * decision about update or if we are checking (-n)
12730Sstevel@tonic-gate 	 */
12740Sstevel@tonic-gate 	if (walk_arg.need_update && !bam_check)
12750Sstevel@tonic-gate 		return (0);
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate 	/*
12786319Sjg 	 * If we are invoked as part of system/filesystem/boot-archive, then
12792334Ssetje 	 * there are a number of things we should not worry about
12800Sstevel@tonic-gate 	 */
12812334Ssetje 	if (bam_smf_check) {
12822334Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
12832334Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
12842334Ssetje 			return (0);
12852334Ssetje 
12862334Ssetje 		/* read in list of safe files */
12872334Ssetje 		if (safefiles == NULL)
12882334Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
12892334Ssetje 				safefiles = s_calloc(1,
12902334Ssetje 				    sizeof (struct safefile));
12912334Ssetje 				safefilep = safefiles;
12922334Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
12932334Ssetje 				    MAXNAMELEN);
12942334Ssetje 				safefilep->next = NULL;
12952334Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
12962334Ssetje 				    MAXNAMELEN, fp) != NULL) {
12972334Ssetje 					safefilep->next = s_calloc(1,
12982334Ssetje 					    sizeof (struct safefile));
12992334Ssetje 					safefilep = safefilep->next;
13002334Ssetje 					safefilep->name = s_calloc(1,
13012334Ssetje 					    MAXPATHLEN + MAXNAMELEN);
13022334Ssetje 					safefilep->next = NULL;
13032334Ssetje 				}
13042334Ssetje 				(void) fclose(fp);
13052334Ssetje 			}
13062334Ssetje 	}
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 	/*
13090Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
13100Sstevel@tonic-gate 	 */
13110Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
13120Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
13130Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
13140Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
13150Sstevel@tonic-gate 			return (0);
13160Sstevel@tonic-gate 		walk_arg.need_update = 1;
13170Sstevel@tonic-gate 		if (bam_verbose)
13180Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
13190Sstevel@tonic-gate 		return (0);
13200Sstevel@tonic-gate 	}
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 	/*
13230Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
13240Sstevel@tonic-gate 	 */
13250Sstevel@tonic-gate 	assert(sz == 2);
13260Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
13270Sstevel@tonic-gate 
13280Sstevel@tonic-gate 	if (filestat[0] != stat->st_size ||
13290Sstevel@tonic-gate 	    filestat[1] != stat->st_mtime) {
13303615Ssetje 		if (bam_smf_check) {
13313615Ssetje 			safefilep = safefiles;
13323615Ssetje 			while (safefilep != NULL) {
13333615Ssetje 				if (strcmp(file + bam_rootlen,
13343615Ssetje 				    safefilep->name) == 0) {
13353615Ssetje 					(void) creat(NEED_UPDATE_FILE, 0644);
13363615Ssetje 					return (0);
13373615Ssetje 				}
13383615Ssetje 				safefilep = safefilep->next;
13393615Ssetje 			}
13403615Ssetje 		}
13410Sstevel@tonic-gate 		walk_arg.need_update = 1;
13420Sstevel@tonic-gate 		if (bam_verbose)
13430Sstevel@tonic-gate 			if (bam_smf_check)
13440Sstevel@tonic-gate 				bam_print("    %s\n", file);
13450Sstevel@tonic-gate 			else
13460Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
13470Sstevel@tonic-gate 	}
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate 	return (0);
13500Sstevel@tonic-gate }
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate /*
13530Sstevel@tonic-gate  * Check flags and presence of required files.
13540Sstevel@tonic-gate  * The force flag and/or absence of files should
13550Sstevel@tonic-gate  * trigger an update.
13560Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
13570Sstevel@tonic-gate  * (as -n should only produce parseable output.)
13580Sstevel@tonic-gate  */
13590Sstevel@tonic-gate static void
13600Sstevel@tonic-gate check_flags_and_files(char *root)
13610Sstevel@tonic-gate {
13620Sstevel@tonic-gate 	char path[PATH_MAX];
13630Sstevel@tonic-gate 	struct stat sb;
13640Sstevel@tonic-gate 
13650Sstevel@tonic-gate 	/*
13660Sstevel@tonic-gate 	 * if force, create archive unconditionally
13670Sstevel@tonic-gate 	 */
13680Sstevel@tonic-gate 	if (bam_force) {
13690Sstevel@tonic-gate 		walk_arg.need_update = 1;
13700Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
13710Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
13720Sstevel@tonic-gate 		return;
13730Sstevel@tonic-gate 	}
13740Sstevel@tonic-gate 
13750Sstevel@tonic-gate 	/*
13760Sstevel@tonic-gate 	 * If archive is missing, create archive
13770Sstevel@tonic-gate 	 */
13786582Ssetje 	if (is_sparc()) {
13796582Ssetje 		(void) snprintf(path, sizeof (path), "%s%s%s%s", root,
13806582Ssetje 		    ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
13816319Sjg 	} else {
13825648Ssetje 		if (bam_direct == BAM_DIRECT_DBOOT) {
13835648Ssetje 			(void) snprintf(path, sizeof (path), "%s%s", root,
13845648Ssetje 			    DIRECT_BOOT_ARCHIVE_64);
13855648Ssetje 			if (stat(path, &sb) != 0) {
13865648Ssetje 				if (bam_verbose && !bam_check)
13875648Ssetje 					bam_print(UPDATE_ARCH_MISS, path);
13885648Ssetje 				walk_arg.need_update = 1;
13895648Ssetje 				return;
13905648Ssetje 			}
13915648Ssetje 		}
13925648Ssetje 		(void) snprintf(path, sizeof (path), "%s%s", root,
13935648Ssetje 		    DIRECT_BOOT_ARCHIVE_32);
13945648Ssetje 	}
13955648Ssetje 
13960Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
13970Sstevel@tonic-gate 		if (bam_verbose && !bam_check)
13980Sstevel@tonic-gate 			bam_print(UPDATE_ARCH_MISS, path);
13990Sstevel@tonic-gate 		walk_arg.need_update = 1;
14000Sstevel@tonic-gate 		return;
14010Sstevel@tonic-gate 	}
14020Sstevel@tonic-gate }
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate static error_t
14050Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
14060Sstevel@tonic-gate {
14070Sstevel@tonic-gate 	char path[PATH_MAX];
14080Sstevel@tonic-gate 	FILE *fp;
14090Sstevel@tonic-gate 	char buf[BAM_MAXLINE];
14106448Svikram 	const char *fcn = "read_one_list()";
14110Sstevel@tonic-gate 
14120Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 	fp = fopen(path, "r");
14150Sstevel@tonic-gate 	if (fp == NULL) {
14166448Svikram 		BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
14170Sstevel@tonic-gate 		return (BAM_ERROR);
14180Sstevel@tonic-gate 	}
14190Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
1420316Svikram 		/* skip blank lines */
1421316Svikram 		if (strspn(buf, " \t") == strlen(buf))
1422316Svikram 			continue;
14230Sstevel@tonic-gate 		append_to_flist(flistp, buf);
14240Sstevel@tonic-gate 	}
14250Sstevel@tonic-gate 	if (fclose(fp) != 0) {
14260Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
14270Sstevel@tonic-gate 		return (BAM_ERROR);
14280Sstevel@tonic-gate 	}
14290Sstevel@tonic-gate 	return (BAM_SUCCESS);
14300Sstevel@tonic-gate }
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate static error_t
14330Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
14340Sstevel@tonic-gate {
14355648Ssetje 	char path[PATH_MAX];
14365648Ssetje 	char cmd[PATH_MAX];
14375648Ssetje 	struct stat sb;
14385648Ssetje 	int n, rval;
14396448Svikram 	const char *fcn = "read_list()";
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	/*
14445648Ssetje 	 * build and check path to extract_boot_filelist.ksh
14455648Ssetje 	 */
14465648Ssetje 	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
14475648Ssetje 	if (n >= sizeof (path)) {
14485648Ssetje 		bam_error(NO_FLIST);
14495648Ssetje 		return (BAM_ERROR);
14505648Ssetje 	}
14515648Ssetje 
14525648Ssetje 	/*
14535648Ssetje 	 * If extract_boot_filelist is present, exec it, otherwise read
14545648Ssetje 	 * the filelists directly, for compatibility with older images.
14550Sstevel@tonic-gate 	 */
14565648Ssetje 	if (stat(path, &sb) == 0) {
14575648Ssetje 		/*
14585648Ssetje 		 * build arguments to exec extract_boot_filelist.ksh
14595648Ssetje 		 */
14606319Sjg 		char *rootarg, *platarg;
14616319Sjg 		int platarglen = 1, rootarglen = 1;
14626319Sjg 		if (strlen(root) > 1)
14636319Sjg 			rootarglen += strlen(root) + strlen("-R ");
14646319Sjg 		if (bam_alt_platform)
14656319Sjg 			platarglen += strlen(bam_platform) + strlen("-p ");
14666319Sjg 		platarg = s_calloc(1, platarglen);
14676319Sjg 		rootarg = s_calloc(1, rootarglen);
14686319Sjg 		*platarg = 0;
14696319Sjg 		*rootarg = 0;
14706319Sjg 
14715648Ssetje 		if (strlen(root) > 1) {
14726319Sjg 			(void) snprintf(rootarg, rootarglen,
14736319Sjg 			    "-R %s", root);
14745648Ssetje 		}
14756319Sjg 		if (bam_alt_platform) {
14766319Sjg 			(void) snprintf(platarg, platarglen,
14776319Sjg 			    "-p %s", bam_platform);
14786319Sjg 		}
14796319Sjg 		n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
14806319Sjg 		    path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
14816319Sjg 		free(platarg);
14826319Sjg 		free(rootarg);
14835648Ssetje 		if (n >= sizeof (cmd)) {
14845648Ssetje 			bam_error(NO_FLIST);
14855648Ssetje 			return (BAM_ERROR);
14865648Ssetje 		}
14875648Ssetje 		if (exec_cmd(cmd, flistp) != 0) {
14886448Svikram 			BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
14895648Ssetje 			return (BAM_ERROR);
14905648Ssetje 		}
14915648Ssetje 	} else {
14925648Ssetje 		/*
14935648Ssetje 		 * Read current lists of files - only the first is mandatory
14945648Ssetje 		 */
14955648Ssetje 		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
14965648Ssetje 		if (rval != BAM_SUCCESS)
14975648Ssetje 			return (rval);
14985648Ssetje 		(void) read_one_list(root, flistp, ETC_FILE_LIST);
14995648Ssetje 	}
15000Sstevel@tonic-gate 
15010Sstevel@tonic-gate 	if (flistp->head == NULL) {
15020Sstevel@tonic-gate 		bam_error(NO_FLIST);
15030Sstevel@tonic-gate 		return (BAM_ERROR);
15040Sstevel@tonic-gate 	}
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate 	return (BAM_SUCCESS);
15070Sstevel@tonic-gate }
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate static void
15100Sstevel@tonic-gate getoldstat(char *root)
15110Sstevel@tonic-gate {
15120Sstevel@tonic-gate 	char path[PATH_MAX];
15130Sstevel@tonic-gate 	int fd, error;
15140Sstevel@tonic-gate 	struct stat sb;
15150Sstevel@tonic-gate 	char *ostat;
15160Sstevel@tonic-gate 
15170Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
15180Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
15190Sstevel@tonic-gate 	if (fd == -1) {
15200Sstevel@tonic-gate 		if (bam_verbose)
15210Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
15220Sstevel@tonic-gate 		walk_arg.need_update = 1;
15230Sstevel@tonic-gate 		return;
15240Sstevel@tonic-gate 	}
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
15270Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
15280Sstevel@tonic-gate 		(void) close(fd);
15290Sstevel@tonic-gate 		walk_arg.need_update = 1;
15300Sstevel@tonic-gate 		return;
15310Sstevel@tonic-gate 	}
15320Sstevel@tonic-gate 
15330Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
15360Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
15370Sstevel@tonic-gate 		(void) close(fd);
15380Sstevel@tonic-gate 		free(ostat);
15390Sstevel@tonic-gate 		walk_arg.need_update = 1;
15400Sstevel@tonic-gate 		return;
15410Sstevel@tonic-gate 	}
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 	(void) close(fd);
15440Sstevel@tonic-gate 
15450Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
15460Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
15470Sstevel@tonic-gate 
15480Sstevel@tonic-gate 	free(ostat);
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate 	if (error) {
15510Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
15520Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
15530Sstevel@tonic-gate 		walk_arg.need_update = 1;
15540Sstevel@tonic-gate 		return;
15550Sstevel@tonic-gate 	}
15560Sstevel@tonic-gate }
15570Sstevel@tonic-gate 
15582583Svikram /*
15592583Svikram  * Checks if a file in the current (old) archive has
15602583Svikram  * been deleted from the root filesystem. This is needed for
15612583Svikram  * software like Trusted Extensions (TX) that switch early
15622583Svikram  * in boot based on presence/absence of a kernel module.
15632583Svikram  */
15642583Svikram static void
15652583Svikram check4stale(char *root)
15662583Svikram {
15672583Svikram 	nvpair_t	*nvp;
15682583Svikram 	nvlist_t	*nvlp;
15692583Svikram 	char 		*file;
15702583Svikram 	char		path[PATH_MAX];
15712583Svikram 	struct stat	sb;
15722583Svikram 
15732583Svikram 	/*
15742583Svikram 	 * Skip stale file check during smf check
15752583Svikram 	 */
15762583Svikram 	if (bam_smf_check)
15772583Svikram 		return;
15782583Svikram 
15792583Svikram 	/* Nothing to do if no old stats */
15802583Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
15812583Svikram 		return;
15822583Svikram 
15832583Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
15842583Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
15852583Svikram 		file = nvpair_name(nvp);
15862583Svikram 		if (file == NULL)
15872583Svikram 			continue;
15882583Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
15892583Svikram 		    root, file);
15902583Svikram 		if (stat(path, &sb) == -1) {
15912583Svikram 			walk_arg.need_update = 1;
15922583Svikram 			if (bam_verbose)
15932583Svikram 				bam_print(PARSEABLE_STALE_FILE, path);
15942583Svikram 		}
15952583Svikram 	}
15962583Svikram }
15972583Svikram 
15980Sstevel@tonic-gate static void
15990Sstevel@tonic-gate create_newstat(void)
16000Sstevel@tonic-gate {
16010Sstevel@tonic-gate 	int error;
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
16040Sstevel@tonic-gate 	if (error) {
16050Sstevel@tonic-gate 		/*
16060Sstevel@tonic-gate 		 * Not fatal - we can still create archive
16070Sstevel@tonic-gate 		 */
16080Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
16090Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
16100Sstevel@tonic-gate 	}
16110Sstevel@tonic-gate }
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate static void
16140Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
16150Sstevel@tonic-gate {
16160Sstevel@tonic-gate 	char path[PATH_MAX];
16170Sstevel@tonic-gate 	line_t *lp;
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
16205648Ssetje 		/*
16215648Ssetje 		 * Don't follow symlinks.  A symlink must refer to
16225648Ssetje 		 * a file that would appear in the archive through
16235648Ssetje 		 * a direct reference.  This matches the archive
16245648Ssetje 		 * construction behavior.
16255648Ssetje 		 */
16260Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
16275648Ssetje 		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
16280Sstevel@tonic-gate 			/*
16290Sstevel@tonic-gate 			 * Some files may not exist.
16300Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
16310Sstevel@tonic-gate 			 * Emit verbose message only
16320Sstevel@tonic-gate 			 */
16330Sstevel@tonic-gate 			if (bam_verbose)
16340Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
16350Sstevel@tonic-gate 		}
16360Sstevel@tonic-gate 	}
16370Sstevel@tonic-gate }
16380Sstevel@tonic-gate 
16390Sstevel@tonic-gate static void
16400Sstevel@tonic-gate savenew(char *root)
16410Sstevel@tonic-gate {
16420Sstevel@tonic-gate 	char path[PATH_MAX];
16430Sstevel@tonic-gate 	char path2[PATH_MAX];
16440Sstevel@tonic-gate 	size_t sz;
16450Sstevel@tonic-gate 	char *nstat;
16460Sstevel@tonic-gate 	int fd, wrote, error;
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	nstat = NULL;
16490Sstevel@tonic-gate 	sz = 0;
16500Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
16510Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
16520Sstevel@tonic-gate 	if (error) {
16530Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
16540Sstevel@tonic-gate 		return;
16550Sstevel@tonic-gate 	}
16560Sstevel@tonic-gate 
16570Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
16580Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
16590Sstevel@tonic-gate 	if (fd == -1) {
16600Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
16610Sstevel@tonic-gate 		free(nstat);
16620Sstevel@tonic-gate 		return;
16630Sstevel@tonic-gate 	}
16640Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
16650Sstevel@tonic-gate 	if (wrote != sz) {
16660Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
16670Sstevel@tonic-gate 		(void) close(fd);
16680Sstevel@tonic-gate 		free(nstat);
16690Sstevel@tonic-gate 		return;
16700Sstevel@tonic-gate 	}
16710Sstevel@tonic-gate 	(void) close(fd);
16720Sstevel@tonic-gate 	free(nstat);
16730Sstevel@tonic-gate 
16740Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
16750Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
16760Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
16770Sstevel@tonic-gate 	}
16780Sstevel@tonic-gate }
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate static void
16810Sstevel@tonic-gate clear_walk_args(void)
16820Sstevel@tonic-gate {
16830Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
16840Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
16850Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
16860Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
16870Sstevel@tonic-gate 	walk_arg.need_update = 0;
16880Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
16890Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
16900Sstevel@tonic-gate }
16910Sstevel@tonic-gate 
16920Sstevel@tonic-gate /*
16930Sstevel@tonic-gate  * Returns:
16940Sstevel@tonic-gate  *	0 - no update necessary
16950Sstevel@tonic-gate  *	1 - update required.
16960Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
16970Sstevel@tonic-gate  *
16980Sstevel@tonic-gate  * Special handling for check (-n):
16990Sstevel@tonic-gate  * ================================
17000Sstevel@tonic-gate  * The check (-n) option produces parseable output.
17010Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
17020Sstevel@tonic-gate  * to out of sync files.
17030Sstevel@tonic-gate  * All stderr messages are still printed though.
17040Sstevel@tonic-gate  *
17050Sstevel@tonic-gate  */
17060Sstevel@tonic-gate static int
17070Sstevel@tonic-gate update_required(char *root)
17080Sstevel@tonic-gate {
17090Sstevel@tonic-gate 	struct stat sb;
17100Sstevel@tonic-gate 	char path[PATH_MAX];
17110Sstevel@tonic-gate 	filelist_t flist;
17120Sstevel@tonic-gate 	filelist_t *flistp = &flist;
17130Sstevel@tonic-gate 	int need_update;
17140Sstevel@tonic-gate 
17150Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	walk_arg.need_update = 0;
17180Sstevel@tonic-gate 
17190Sstevel@tonic-gate 	/*
17200Sstevel@tonic-gate 	 * Without consulting stat data, check if we need update
17210Sstevel@tonic-gate 	 */
17220Sstevel@tonic-gate 	check_flags_and_files(root);
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate 	/*
17250Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
17260Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
17270Sstevel@tonic-gate 	 */
17280Sstevel@tonic-gate 	if (bam_smf_check) {
17290Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
17300Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
17310Sstevel@tonic-gate 			return (0);
17320Sstevel@tonic-gate 	}
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 	/*
17350Sstevel@tonic-gate 	 * consult stat data only if we haven't made a decision
17360Sstevel@tonic-gate 	 * about update. If checking (-n) however, we always
17370Sstevel@tonic-gate 	 * need stat data (since we want to compare old and new)
17380Sstevel@tonic-gate 	 */
17390Sstevel@tonic-gate 	if (!walk_arg.need_update || bam_check)
17400Sstevel@tonic-gate 		getoldstat(root);
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 	/*
17432583Svikram 	 * Check if the archive contains files that are no longer
17442583Svikram 	 * present on the root filesystem.
17452583Svikram 	 */
17462583Svikram 	if (!walk_arg.need_update || bam_check)
17472583Svikram 		check4stale(root);
17482583Svikram 
17492583Svikram 	/*
17500Sstevel@tonic-gate 	 * read list of files
17510Sstevel@tonic-gate 	 */
17520Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
17530Sstevel@tonic-gate 		clear_walk_args();
17540Sstevel@tonic-gate 		return (BAM_ERROR);
17550Sstevel@tonic-gate 	}
17560Sstevel@tonic-gate 
17570Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 	/*
17600Sstevel@tonic-gate 	 * At this point either the update is required
17610Sstevel@tonic-gate 	 * or the decision is pending. In either case
17620Sstevel@tonic-gate 	 * we need to create new stat nvlist
17630Sstevel@tonic-gate 	 */
17640Sstevel@tonic-gate 	create_newstat();
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate 	/*
17670Sstevel@tonic-gate 	 * This walk does 2 things:
17680Sstevel@tonic-gate 	 *  	- gets new stat data for every file
17690Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
17700Sstevel@tonic-gate 	 */
17710Sstevel@tonic-gate 	walk_list(root, &flist);
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	/* done with the file list */
17740Sstevel@tonic-gate 	filelist_free(flistp);
17750Sstevel@tonic-gate 
17760Sstevel@tonic-gate 	/*
17770Sstevel@tonic-gate 	 * if we didn't succeed in  creating new stat data above
17780Sstevel@tonic-gate 	 * just return result of update check so that archive is built.
17790Sstevel@tonic-gate 	 */
17800Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
17810Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
17820Sstevel@tonic-gate 		need_update = walk_arg.need_update;
17830Sstevel@tonic-gate 		clear_walk_args();
17840Sstevel@tonic-gate 		return (need_update ? 1 : 0);
17850Sstevel@tonic-gate 	}
17860Sstevel@tonic-gate 
17870Sstevel@tonic-gate 
17880Sstevel@tonic-gate 	/*
17890Sstevel@tonic-gate 	 * If no update required, discard newstat
17900Sstevel@tonic-gate 	 */
17910Sstevel@tonic-gate 	if (!walk_arg.need_update) {
17920Sstevel@tonic-gate 		clear_walk_args();
17930Sstevel@tonic-gate 		return (0);
17940Sstevel@tonic-gate 	}
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate 	return (1);
17970Sstevel@tonic-gate }
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate static error_t
18000Sstevel@tonic-gate create_ramdisk(char *root)
18010Sstevel@tonic-gate {
18020Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
18030Sstevel@tonic-gate 	size_t len;
18040Sstevel@tonic-gate 	struct stat sb;
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate 	/*
18070Sstevel@tonic-gate 	 * Setup command args for create_ramdisk.ksh
18080Sstevel@tonic-gate 	 */
18095648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
18100Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
18110Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
18120Sstevel@tonic-gate 		return (BAM_ERROR);
18130Sstevel@tonic-gate 	}
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
18166319Sjg 	if (bam_alt_platform)
18176319Sjg 		len += strlen(bam_platform) + strlen("-p ");
18180Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
18190Sstevel@tonic-gate 
18206319Sjg 	if (bam_alt_platform) {
18216319Sjg 		assert(strlen(root) > 1);
18226319Sjg 		(void) snprintf(cmdline, len, "%s -p %s -R %s",
18236319Sjg 		    path, bam_platform, root);
18246319Sjg 		/* chop off / at the end */
18256319Sjg 		cmdline[strlen(cmdline) - 1] = '\0';
18266319Sjg 	} else if (strlen(root) > 1) {
18270Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
18280Sstevel@tonic-gate 		/* chop off / at the end */
18290Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
18300Sstevel@tonic-gate 	} else
18310Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
18320Sstevel@tonic-gate 
18335648Ssetje 	if (exec_cmd(cmdline, NULL) != 0) {
18340Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
18350Sstevel@tonic-gate 		free(cmdline);
18360Sstevel@tonic-gate 		return (BAM_ERROR);
18370Sstevel@tonic-gate 	}
18380Sstevel@tonic-gate 	free(cmdline);
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 	/*
18415648Ssetje 	 * The existence of the expected archives used to be
18425648Ssetje 	 * verified here. This check is done in create_ramdisk as
18435648Ssetje 	 * it needs to be in sync with the altroot operated upon.
18440Sstevel@tonic-gate 	 */
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate 	return (BAM_SUCCESS);
18470Sstevel@tonic-gate }
18480Sstevel@tonic-gate 
18490Sstevel@tonic-gate /*
18500Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
18510Sstevel@tonic-gate  * 1 - is miniroot
18520Sstevel@tonic-gate  * 0 - is not
18530Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
18540Sstevel@tonic-gate  */
18550Sstevel@tonic-gate static int
18560Sstevel@tonic-gate is_ramdisk(char *root)
18570Sstevel@tonic-gate {
18580Sstevel@tonic-gate 	struct extmnttab mnt;
18590Sstevel@tonic-gate 	FILE *fp;
18600Sstevel@tonic-gate 	int found;
1861316Svikram 	char mntpt[PATH_MAX];
1862316Svikram 	char *cp;
18630Sstevel@tonic-gate 
18640Sstevel@tonic-gate 	/*
18650Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
18660Sstevel@tonic-gate 	 * of dubious value:
1867316Svikram 	 *	- create boot_archive on a lofi-mounted boot_archive
18680Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
18690Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
18700Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
18710Sstevel@tonic-gate 	 * worth it.
18720Sstevel@tonic-gate 	 * The other two conditions are handled here
18730Sstevel@tonic-gate 	 */
18740Sstevel@tonic-gate 
18750Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
18760Sstevel@tonic-gate 	if (fp == NULL) {
18770Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
18780Sstevel@tonic-gate 		return (0);
18790Sstevel@tonic-gate 	}
18800Sstevel@tonic-gate 
18810Sstevel@tonic-gate 	resetmnttab(fp);
18820Sstevel@tonic-gate 
1883316Svikram 	/*
1884316Svikram 	 * Remove any trailing / from the mount point
1885316Svikram 	 */
1886316Svikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
1887316Svikram 	if (strcmp(root, "/") != 0) {
1888316Svikram 		cp = mntpt + strlen(mntpt) - 1;
1889316Svikram 		if (*cp == '/')
1890316Svikram 			*cp = '\0';
1891316Svikram 	}
18920Sstevel@tonic-gate 	found = 0;
18930Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1894316Svikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
18950Sstevel@tonic-gate 			found = 1;
18960Sstevel@tonic-gate 			break;
18970Sstevel@tonic-gate 		}
18980Sstevel@tonic-gate 	}
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate 	if (!found) {
19010Sstevel@tonic-gate 		if (bam_verbose)
1902316Svikram 			bam_error(NOT_IN_MNTTAB, mntpt);
19030Sstevel@tonic-gate 		(void) fclose(fp);
19040Sstevel@tonic-gate 		return (0);
19050Sstevel@tonic-gate 	}
19060Sstevel@tonic-gate 
19070Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
19080Sstevel@tonic-gate 		if (bam_verbose)
19090Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
19100Sstevel@tonic-gate 		(void) fclose(fp);
19110Sstevel@tonic-gate 		return (1);
19120Sstevel@tonic-gate 	}
19130Sstevel@tonic-gate 
19140Sstevel@tonic-gate 	(void) fclose(fp);
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate 	return (0);
19170Sstevel@tonic-gate }
19180Sstevel@tonic-gate 
19190Sstevel@tonic-gate static int
19205648Ssetje is_boot_archive(char *root)
19210Sstevel@tonic-gate {
19226448Svikram 	char		path[PATH_MAX];
19236448Svikram 	struct stat	sb;
19246448Svikram 	int		error;
19256448Svikram 	const char	*fcn = "is_boot_archive()";
19260Sstevel@tonic-gate 
19270Sstevel@tonic-gate 	/*
19285648Ssetje 	 * We can't create an archive without the create_ramdisk script
19290Sstevel@tonic-gate 	 */
19305648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
19316448Svikram 	error = stat(path, &sb);
19326448Svikram 	INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
19336448Svikram 	if (error == -1) {
19340Sstevel@tonic-gate 		if (bam_verbose)
19350Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
19366448Svikram 		BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
19370Sstevel@tonic-gate 		return (0);
19380Sstevel@tonic-gate 	}
19390Sstevel@tonic-gate 
19406448Svikram 	BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
19415648Ssetje 	return (1);
19425648Ssetje }
19435648Ssetje 
19445648Ssetje /*
19455648Ssetje  * Need to call this for anything that operates on the GRUB menu
19466694Svikram  * In the x86 live upgrade case the directory /boot/grub may be present
19476694Svikram  * even on pre-newboot BEs. The authoritative way to check for a GRUB target
19486694Svikram  * is to check for the presence of the stage2 binary which is present
19496694Svikram  * only on GRUB targets (even on x86 boot partitions). Checking for the
19506694Svikram  * presence of the multiboot binary is not correct as it is not present
19516694Svikram  * on x86 boot partitions.
19525648Ssetje  */
19535648Ssetje int
19545648Ssetje is_grub(const char *root)
19555648Ssetje {
19565648Ssetje 	char path[PATH_MAX];
19575648Ssetje 	struct stat sb;
19586448Svikram 	const char *fcn = "is_grub()";
19596448Svikram 
19606694Svikram 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
19610Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
19626448Svikram 		BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
19630Sstevel@tonic-gate 		return (0);
19640Sstevel@tonic-gate 	}
19650Sstevel@tonic-gate 
19660Sstevel@tonic-gate 	return (1);
19670Sstevel@tonic-gate }
19680Sstevel@tonic-gate 
19690Sstevel@tonic-gate static int
19706448Svikram is_zfs(char *root)
19716448Svikram {
19726448Svikram 	struct statvfs		vfs;
19736448Svikram 	int			ret;
19746448Svikram 	const char		*fcn = "is_zfs()";
19756448Svikram 
19766448Svikram 	ret = statvfs(root, &vfs);
19776448Svikram 	INJECT_ERROR1("STATVFS_ZFS", ret = 1);
19786448Svikram 	if (ret != 0) {
19796448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
19806448Svikram 		return (0);
19816448Svikram 	}
19826448Svikram 
19836448Svikram 	if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
19846448Svikram 		BAM_DPRINTF((D_IS_ZFS, fcn, root));
19856448Svikram 		return (1);
19866448Svikram 	} else {
19876448Svikram 		BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
19880Sstevel@tonic-gate 		return (0);
19890Sstevel@tonic-gate 	}
19906448Svikram }
19916448Svikram 
19926448Svikram static int
19936448Svikram is_ufs(char *root)
19946448Svikram {
19956448Svikram 	struct statvfs		vfs;
19966448Svikram 	int			ret;
19976448Svikram 	const char		*fcn = "is_ufs()";
19986448Svikram 
19996448Svikram 	ret = statvfs(root, &vfs);
20006448Svikram 	INJECT_ERROR1("STATVFS_UFS", ret = 1);
20016448Svikram 	if (ret != 0) {
20026448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
20036448Svikram 		return (0);
20046448Svikram 	}
20056448Svikram 
20066448Svikram 	if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
20076448Svikram 		BAM_DPRINTF((D_IS_UFS, fcn, root));
20080Sstevel@tonic-gate 		return (1);
20096448Svikram 	} else {
20106448Svikram 		BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
20116448Svikram 		return (0);
20126448Svikram 	}
20130Sstevel@tonic-gate }
20140Sstevel@tonic-gate 
20156423Sgw25295 static int
20166448Svikram is_pcfs(char *root)
20176448Svikram {
20186448Svikram 	struct statvfs		vfs;
20196448Svikram 	int			ret;
20206448Svikram 	const char		*fcn = "is_pcfs()";
20216448Svikram 
20226448Svikram 	ret = statvfs(root, &vfs);
20236448Svikram 	INJECT_ERROR1("STATVFS_PCFS", ret = 1);
20246448Svikram 	if (ret != 0) {
20256448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
20266423Sgw25295 		return (0);
20276423Sgw25295 	}
20286423Sgw25295 
20296448Svikram 	if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
20306448Svikram 		BAM_DPRINTF((D_IS_PCFS, fcn, root));
20316448Svikram 		return (1);
20326448Svikram 	} else {
20336448Svikram 		BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
20346423Sgw25295 		return (0);
20356448Svikram 	}
20366448Svikram }
20376448Svikram 
20386448Svikram static int
20396448Svikram is_readonly(char *root)
20406448Svikram {
20416448Svikram 	int		fd;
20426448Svikram 	int		error;
20436448Svikram 	char		testfile[PATH_MAX];
20446448Svikram 	const char	*fcn = "is_readonly()";
20456423Sgw25295 
20466423Sgw25295 	/*
20476448Svikram 	 * Using statvfs() to check for a read-only filesystem is not
20486448Svikram 	 * reliable. The only way to reliably test is to attempt to
20496448Svikram 	 * create a file
20506423Sgw25295 	 */
20516448Svikram 	(void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
20526448Svikram 	    root, BOOTADM_RDONLY_TEST, getpid());
20536448Svikram 
20546448Svikram 	(void) unlink(testfile);
20556448Svikram 
20566448Svikram 	errno = 0;
20576448Svikram 	fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
20586448Svikram 	error = errno;
20596448Svikram 	INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
20606448Svikram 	if (fd == -1 && error == EROFS) {
20616448Svikram 		BAM_DPRINTF((D_RDONLY_FS, fcn, root));
20626423Sgw25295 		return (1);
20636448Svikram 	} else if (fd == -1) {
20646448Svikram 		bam_error(RDONLY_TEST_ERROR, root, strerror(error));
20656448Svikram 	}
20666448Svikram 
20676448Svikram 	(void) close(fd);
20686448Svikram 	(void) unlink(testfile);
20696448Svikram 
20706448Svikram 	BAM_DPRINTF((D_RDWR_FS, fcn, root));
20716423Sgw25295 	return (0);
20726423Sgw25295 }
20736423Sgw25295 
20740Sstevel@tonic-gate static error_t
20750Sstevel@tonic-gate update_archive(char *root, char *opt)
20760Sstevel@tonic-gate {
20770Sstevel@tonic-gate 	error_t ret;
20780Sstevel@tonic-gate 
20790Sstevel@tonic-gate 	assert(root);
20800Sstevel@tonic-gate 	assert(opt == NULL);
20810Sstevel@tonic-gate 
20820Sstevel@tonic-gate 	/*
20836448Svikram 	 * root must belong to a boot archive based OS,
20840Sstevel@tonic-gate 	 */
20855648Ssetje 	if (!is_boot_archive(root)) {
2086316Svikram 		/*
2087316Svikram 		 * Emit message only if not in context of update_all.
2088316Svikram 		 * If in update_all, emit only if verbose flag is set.
2089316Svikram 		 */
2090316Svikram 		if (!bam_update_all || bam_verbose)
20916448Svikram 			bam_print(NOT_ARCHIVE_BOOT, root);
20920Sstevel@tonic-gate 		return (BAM_SUCCESS);
20930Sstevel@tonic-gate 	}
20940Sstevel@tonic-gate 
20950Sstevel@tonic-gate 	/*
2096662Sszhou 	 * If smf check is requested when / is writable (can happen
2097662Sszhou 	 * on first reboot following an upgrade because service
2098662Sszhou 	 * dependency is messed up), skip the check.
2099662Sszhou 	 */
2100662Sszhou 	if (bam_smf_check && !bam_root_readonly)
2101662Sszhou 		return (BAM_SUCCESS);
2102662Sszhou 
2103662Sszhou 	/*
2104662Sszhou 	 * root must be writable. This check applies to alternate
2105662Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
21060Sstevel@tonic-gate 	 */
2107756Ssetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
2108662Sszhou 		if (bam_verbose)
21090Sstevel@tonic-gate 			bam_print(RDONLY_FS, root);
21100Sstevel@tonic-gate 		return (BAM_SUCCESS);
21110Sstevel@tonic-gate 	}
21120Sstevel@tonic-gate 
21130Sstevel@tonic-gate 	/*
21140Sstevel@tonic-gate 	 * Don't generate archive on ramdisk
21150Sstevel@tonic-gate 	 */
21160Sstevel@tonic-gate 	if (is_ramdisk(root)) {
21170Sstevel@tonic-gate 		if (bam_verbose)
21180Sstevel@tonic-gate 			bam_print(SKIP_RAMDISK);
21190Sstevel@tonic-gate 		return (BAM_SUCCESS);
21200Sstevel@tonic-gate 	}
21210Sstevel@tonic-gate 
21220Sstevel@tonic-gate 	/*
21230Sstevel@tonic-gate 	 * Now check if updated is really needed
21240Sstevel@tonic-gate 	 */
21250Sstevel@tonic-gate 	ret = update_required(root);
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate 	/*
21280Sstevel@tonic-gate 	 * The check command (-n) is *not* a dry run
21290Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
21300Sstevel@tonic-gate 	 */
21310Sstevel@tonic-gate 	if (bam_check) {
21320Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
21330Sstevel@tonic-gate 	}
21340Sstevel@tonic-gate 
21350Sstevel@tonic-gate 	if (ret == 1) {
21360Sstevel@tonic-gate 		/* create the ramdisk */
21370Sstevel@tonic-gate 		ret = create_ramdisk(root);
21380Sstevel@tonic-gate 	}
21395648Ssetje 
21405648Ssetje 	/* if the archive is updated, save the new stat data */
21415648Ssetje 	if (ret == 0 && walk_arg.new_nvlp != NULL) {
21425648Ssetje 		savenew(root);
21435648Ssetje 	}
21445648Ssetje 
21455648Ssetje 	clear_walk_args();
21465648Ssetje 
21470Sstevel@tonic-gate 	return (ret);
21480Sstevel@tonic-gate }
21490Sstevel@tonic-gate 
21506694Svikram static error_t
21516694Svikram synchronize_BE_menu(void)
21526694Svikram {
21536694Svikram 	struct stat	sb;
21546694Svikram 	char		cmdline[PATH_MAX];
21556694Svikram 	char		cksum_line[PATH_MAX];
21566694Svikram 	filelist_t	flist = {0};
21576694Svikram 	char		*old_cksum_str;
21586694Svikram 	char		*old_size_str;
21596694Svikram 	char		*old_file;
21606694Svikram 	char		*curr_cksum_str;
21616694Svikram 	char		*curr_size_str;
21626694Svikram 	char		*curr_file;
21636694Svikram 	FILE		*cfp;
21646694Svikram 	int		found;
21656694Svikram 	int		ret;
21666694Svikram 	const char	*fcn = "synchronize_BE_menu()";
21676694Svikram 
21686694Svikram 	BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
21696694Svikram 
21706694Svikram 	/* Check if findroot enabled LU BE */
21716694Svikram 	if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
21726694Svikram 		BAM_DPRINTF((D_NOT_LU_BE, fcn));
21736694Svikram 		return (BAM_SUCCESS);
21746694Svikram 	}
21756694Svikram 
21766694Svikram 	if (stat(LU_MENU_CKSUM, &sb) != 0) {
21776694Svikram 		BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
21786694Svikram 		goto menu_sync;
21796694Svikram 	}
21806694Svikram 
21816694Svikram 	cfp = fopen(LU_MENU_CKSUM, "r");
21826694Svikram 	INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
21836694Svikram 	if (cfp == NULL) {
21846694Svikram 		bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
21856694Svikram 		goto menu_sync;
21866694Svikram 	}
21876694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
21886694Svikram 
21896694Svikram 	found = 0;
21906694Svikram 	while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
21916694Svikram 		INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
21926694Svikram 		if (found) {
21936694Svikram 			bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
21946694Svikram 			(void) fclose(cfp);
21956694Svikram 			goto menu_sync;
21966694Svikram 		}
21976694Svikram 		found = 1;
21986694Svikram 	}
21996694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
22006694Svikram 
22016694Svikram 
22026694Svikram 	old_cksum_str = strtok(cksum_line, " \t");
22036694Svikram 	old_size_str = strtok(NULL, " \t");
22046694Svikram 	old_file = strtok(NULL, " \t");
22056694Svikram 
22066694Svikram 	INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
22076694Svikram 	INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
22086694Svikram 	INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
22096694Svikram 	if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
22106694Svikram 		bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
22116694Svikram 		goto menu_sync;
22126694Svikram 	}
22136694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
22146694Svikram 
22156694Svikram 	/* Get checksum of current menu */
22166694Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s",
22176694Svikram 	    CKSUM, GRUB_MENU);
22186694Svikram 	ret = exec_cmd(cmdline, &flist);
22196694Svikram 	INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
22206694Svikram 	if (ret != 0) {
22216694Svikram 		bam_error(MENU_CKSUM_FAIL);
22226694Svikram 		return (BAM_ERROR);
22236694Svikram 	}
22246694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
22256694Svikram 
22266694Svikram 	INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
22276694Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
22286694Svikram 		bam_error(BAD_CKSUM);
22296694Svikram 		filelist_free(&flist);
22306694Svikram 		return (BAM_ERROR);
22316694Svikram 	}
22326694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
22336694Svikram 
22346694Svikram 	curr_cksum_str = strtok(flist.head->line, " \t");
22356694Svikram 	curr_size_str = strtok(NULL, " \t");
22366694Svikram 	curr_file = strtok(NULL, " \t");
22376694Svikram 
22386694Svikram 	INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
22396694Svikram 	INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
22406694Svikram 	INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
22416694Svikram 	if (curr_cksum_str == NULL || curr_size_str == NULL ||
22426694Svikram 	    curr_file == NULL) {
22436694Svikram 		bam_error(BAD_CKSUM_PARSE);
22446694Svikram 		filelist_free(&flist);
22456694Svikram 		return (BAM_ERROR);
22466694Svikram 	}
22476694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
22486694Svikram 
22496694Svikram 	if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
22506694Svikram 	    strcmp(old_size_str, curr_size_str) == 0 &&
22516694Svikram 	    strcmp(old_file, curr_file) == 0) {
22526694Svikram 		filelist_free(&flist);
22536694Svikram 		BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
22546694Svikram 		return (BAM_SUCCESS);
22556694Svikram 	}
22566694Svikram 
22576694Svikram 	filelist_free(&flist);
22586694Svikram 
22596694Svikram 	/* cksum doesn't match - the menu has changed */
22606694Svikram 	BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
22616694Svikram 
22626694Svikram menu_sync:
22636694Svikram 	bam_print(PROP_GRUB_MENU);
22646694Svikram 
22656694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
22667048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
22676694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
22686694Svikram 	ret = exec_cmd(cmdline, NULL);
22696694Svikram 	INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
22706694Svikram 	if (ret != 0) {
22716694Svikram 		bam_error(MENU_PROP_FAIL);
22726694Svikram 		return (BAM_ERROR);
22736694Svikram 	}
22746694Svikram 	BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
22756694Svikram 
22767048Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
22776694Svikram 	    GRUB_MENU, GRUB_BACKUP_MENU);
22786694Svikram 	ret = exec_cmd(cmdline, NULL);
22796694Svikram 	INJECT_ERROR1("CREATE_BACKUP", ret = 1);
22806694Svikram 	if (ret != 0) {
22816694Svikram 		bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
22826694Svikram 		return (BAM_ERROR);
22836694Svikram 	}
22846694Svikram 	BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
22856694Svikram 
22866694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
22877048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
22886694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
22896694Svikram 	ret = exec_cmd(cmdline, NULL);
22906694Svikram 	INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
22916694Svikram 	if (ret != 0) {
22926694Svikram 		bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
22936694Svikram 		return (BAM_ERROR);
22946694Svikram 	}
22956694Svikram 	BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
22966694Svikram 
22976694Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
22986694Svikram 	    CKSUM, GRUB_MENU, LU_MENU_CKSUM);
22996694Svikram 	ret = exec_cmd(cmdline, NULL);
23006694Svikram 	INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
23016694Svikram 	if (ret != 0) {
23026694Svikram 		bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
23036694Svikram 		return (BAM_ERROR);
23046694Svikram 	}
23056694Svikram 	BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
23066694Svikram 
23076694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
23087048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
23096694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
23106694Svikram 	ret = exec_cmd(cmdline, NULL);
23116694Svikram 	INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
23126694Svikram 	if (ret != 0) {
23136694Svikram 		bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
23146694Svikram 		return (BAM_ERROR);
23156694Svikram 	}
23166694Svikram 	BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
23176694Svikram 
23186694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
23197048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
23206694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, BOOTADM);
23216694Svikram 	ret = exec_cmd(cmdline, NULL);
23226694Svikram 	INJECT_ERROR1("PROPAGATE_BOOTADM_FILE", ret = 1);
23236694Svikram 	if (ret != 0) {
23246694Svikram 		bam_error(BOOTADM_PROP_FAIL, BOOTADM);
23256694Svikram 		return (BAM_ERROR);
23266694Svikram 	}
23276694Svikram 	BAM_DPRINTF((D_PROPAGATED_BOOTADM, fcn, BOOTADM));
23286694Svikram 
23296694Svikram 	return (BAM_SUCCESS);
23301746Svikram }
23311746Svikram 
23320Sstevel@tonic-gate static error_t
23330Sstevel@tonic-gate update_all(char *root, char *opt)
23340Sstevel@tonic-gate {
23350Sstevel@tonic-gate 	struct extmnttab mnt;
23360Sstevel@tonic-gate 	struct stat sb;
23370Sstevel@tonic-gate 	FILE *fp;
23380Sstevel@tonic-gate 	char multibt[PATH_MAX];
23395648Ssetje 	char creatram[PATH_MAX];
23400Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
23410Sstevel@tonic-gate 
2342621Svikram 	assert(root);
23430Sstevel@tonic-gate 	assert(opt == NULL);
23440Sstevel@tonic-gate 
2345621Svikram 	if (bam_rootlen != 1 || *root != '/') {
2346621Svikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
2347621Svikram 		bam_error(ALT_ROOT_INVALID, multibt);
2348621Svikram 		return (BAM_ERROR);
2349621Svikram 	}
2350621Svikram 
23510Sstevel@tonic-gate 	/*
23524493Snadkarni 	 * Check to see if we are in the midst of safemode patching
23534493Snadkarni 	 * If so skip building the archive for /. Instead build it
23544493Snadkarni 	 * against the latest bits obtained by creating a fresh lofs
23554493Snadkarni 	 * mount of root.
23560Sstevel@tonic-gate 	 */
23574493Snadkarni 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
23584493Snadkarni 		if (mkdir(LOFS_PATCH_MNT, 0755) == -1 &&
23594493Snadkarni 		    errno != EEXIST) {
23604493Snadkarni 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
23614493Snadkarni 			    strerror(errno));
23624493Snadkarni 			ret = BAM_ERROR;
23634493Snadkarni 			goto out;
23644493Snadkarni 		}
23654493Snadkarni 		(void) snprintf(multibt, sizeof (multibt),
23664493Snadkarni 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
23675648Ssetje 		if (exec_cmd(multibt, NULL) != 0) {
23684493Snadkarni 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
23694493Snadkarni 			ret = BAM_ERROR;
23704493Snadkarni 		}
23714493Snadkarni 		if (ret != BAM_ERROR) {
23724493Snadkarni 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
23734493Snadkarni 			    LOFS_PATCH_MNT);
23744493Snadkarni 			bam_rootlen = strlen(rootbuf);
23754493Snadkarni 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
23764493Snadkarni 				ret = BAM_ERROR;
23774550Snadkarni 			/*
23784550Snadkarni 			 * unmount the lofs mount since there could be
23794550Snadkarni 			 * multiple invocations of bootadm -a update_all
23804550Snadkarni 			 */
23814550Snadkarni 			(void) snprintf(multibt, sizeof (multibt),
23824550Snadkarni 			    "/sbin/umount %s", LOFS_PATCH_MNT);
23835648Ssetje 			if (exec_cmd(multibt, NULL) != 0) {
23844550Snadkarni 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
23854550Snadkarni 				ret = BAM_ERROR;
23864550Snadkarni 			}
23874493Snadkarni 		}
23884493Snadkarni 	} else {
23894493Snadkarni 		/*
23904493Snadkarni 		 * First update archive for current root
23914493Snadkarni 		 */
23924493Snadkarni 		if (update_archive(root, opt) != BAM_SUCCESS)
23934493Snadkarni 			ret = BAM_ERROR;
23944493Snadkarni 	}
23954493Snadkarni 
23964493Snadkarni 	if (ret == BAM_ERROR)
23974493Snadkarni 		goto out;
23980Sstevel@tonic-gate 
23990Sstevel@tonic-gate 	/*
24000Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
24010Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
24020Sstevel@tonic-gate 	 */
24030Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
24040Sstevel@tonic-gate 	if (fp == NULL) {
24050Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
2406316Svikram 		ret = BAM_ERROR;
2407316Svikram 		goto out;
24080Sstevel@tonic-gate 	}
24090Sstevel@tonic-gate 
24100Sstevel@tonic-gate 	resetmnttab(fp);
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
24130Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
24140Sstevel@tonic-gate 			continue;
24150Sstevel@tonic-gate 		if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)
24160Sstevel@tonic-gate 			continue;
24170Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
24180Sstevel@tonic-gate 			continue;
24190Sstevel@tonic-gate 
24205648Ssetje 		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
24215648Ssetje 		    mnt.mnt_mountp, CREATE_RAMDISK);
24225648Ssetje 
24235648Ssetje 		if (stat(creatram, &sb) == -1)
24240Sstevel@tonic-gate 			continue;
24250Sstevel@tonic-gate 
24260Sstevel@tonic-gate 		/*
24270Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
24280Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
24290Sstevel@tonic-gate 		 */
24300Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
24310Sstevel@tonic-gate 		    mnt.mnt_mountp);
24320Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
24333446Smrj 
24343446Smrj 		/*
24353446Smrj 		 * It's possible that other mounts may be an alternate boot
24363446Smrj 		 * architecture, so check it again.
24373446Smrj 		 */
24386448Svikram 		if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
24393446Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
24400Sstevel@tonic-gate 			ret = BAM_ERROR;
24410Sstevel@tonic-gate 	}
24420Sstevel@tonic-gate 
24430Sstevel@tonic-gate 	(void) fclose(fp);
24440Sstevel@tonic-gate 
2445316Svikram out:
24461746Svikram 	/*
24476694Svikram 	 * We no longer use biosdev for Live Upgrade. Hence
24486694Svikram 	 * there is no need to defer (to shutdown time) any fdisk
24496694Svikram 	 * updates
24501746Svikram 	 */
24516694Svikram 	if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
24526694Svikram 		bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
24536694Svikram 	}
24546694Svikram 
24556694Svikram 	/*
24566694Svikram 	 * If user has updated menu in current BE, propagate the
24576694Svikram 	 * updates to all BEs.
24586694Svikram 	 */
24596694Svikram 	if (synchronize_BE_menu() != BAM_SUCCESS)
24601746Svikram 		ret = BAM_ERROR;
24611746Svikram 
24620Sstevel@tonic-gate 	return (ret);
24630Sstevel@tonic-gate }
24640Sstevel@tonic-gate 
24650Sstevel@tonic-gate static void
24660Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
24670Sstevel@tonic-gate {
24680Sstevel@tonic-gate 	if (mp->start == NULL) {
24690Sstevel@tonic-gate 		mp->start = lp;
24700Sstevel@tonic-gate 	} else {
24710Sstevel@tonic-gate 		mp->end->next = lp;
2472662Sszhou 		lp->prev = mp->end;
24730Sstevel@tonic-gate 	}
24740Sstevel@tonic-gate 	mp->end = lp;
24750Sstevel@tonic-gate }
24760Sstevel@tonic-gate 
24776448Svikram void
2478662Sszhou unlink_line(menu_t *mp, line_t *lp)
2479662Sszhou {
2480662Sszhou 	/* unlink from list */
2481662Sszhou 	if (lp->prev)
2482662Sszhou 		lp->prev->next = lp->next;
2483662Sszhou 	else
2484662Sszhou 		mp->start = lp->next;
2485662Sszhou 	if (lp->next)
2486662Sszhou 		lp->next->prev = lp->prev;
2487662Sszhou 	else
2488662Sszhou 		mp->end = lp->prev;
2489662Sszhou }
2490662Sszhou 
2491662Sszhou static entry_t *
2492662Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
2493662Sszhou {
2494662Sszhou 	entry_t *ent, *prev;
24956448Svikram 	const char *fcn = "boot_entry_new()";
24966448Svikram 
24976448Svikram 	assert(mp);
24986448Svikram 	assert(start);
24996448Svikram 	assert(end);
2500662Sszhou 
2501662Sszhou 	ent = s_calloc(1, sizeof (entry_t));
25026448Svikram 	BAM_DPRINTF((D_ENTRY_NEW, fcn));
2503662Sszhou 	ent->start = start;
2504662Sszhou 	ent->end = end;
2505662Sszhou 
2506662Sszhou 	if (mp->entries == NULL) {
2507662Sszhou 		mp->entries = ent;
25086448Svikram 		BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
2509662Sszhou 		return (ent);
2510662Sszhou 	}
2511662Sszhou 
2512662Sszhou 	prev = mp->entries;
2513662Sszhou 	while (prev->next)
25146448Svikram 		prev = prev->next;
2515662Sszhou 	prev->next = ent;
2516662Sszhou 	ent->prev = prev;
25176448Svikram 	BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
2518662Sszhou 	return (ent);
2519662Sszhou }
2520662Sszhou 
2521662Sszhou static void
2522662Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
2523662Sszhou {
2524662Sszhou 	if (ent)
2525662Sszhou 		ent->end = lp;
2526662Sszhou }
2527662Sszhou 
25280Sstevel@tonic-gate /*
25295084Sjohnlev  * Check whether cmd matches the one indexed by which, and whether arg matches
25305084Sjohnlev  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
25315084Sjohnlev  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
25325084Sjohnlev  * strstr(), so it can be a partial match.
25335084Sjohnlev  */
25345084Sjohnlev static int
25355084Sjohnlev check_cmd(const char *cmd, const int which, const char *arg, const char *str)
25365084Sjohnlev {
25376448Svikram 	int			ret;
25386448Svikram 	const char		*fcn = "check_cmd()";
25396448Svikram 
25406448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
25416448Svikram 
25425084Sjohnlev 	if ((strcmp(cmd, menu_cmds[which]) != 0) &&
25435084Sjohnlev 	    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
25446448Svikram 		BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
25456448Svikram 		    fcn, cmd, menu_cmds[which]));
25465084Sjohnlev 		return (0);
25475084Sjohnlev 	}
25486448Svikram 	ret = (strstr(arg, str) != NULL);
25496448Svikram 
25506448Svikram 	if (ret) {
25516448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
25526448Svikram 	} else {
25536448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
25546448Svikram 	}
25556448Svikram 
25566448Svikram 	return (ret);
25576448Svikram }
25586448Svikram 
25596448Svikram static error_t
25606448Svikram kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
25616448Svikram {
25626448Svikram 	const char		*fcn  = "kernel_parser()";
25636448Svikram 
25646448Svikram 	assert(entry);
25656448Svikram 	assert(cmd);
25666448Svikram 	assert(arg);
25676448Svikram 
25686448Svikram 	if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
25696448Svikram 	    strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
25706448Svikram 		BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
25716448Svikram 		return (BAM_ERROR);
25726448Svikram 	}
25736448Svikram 
25746448Svikram 	if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
25756448Svikram 		BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
25766448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
25776448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_KERNEL,
25786448Svikram 	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
25796448Svikram 		BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
25806448Svikram 		entry->flags |= BAM_ENTRY_DBOOT;
25816448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_64,
25826448Svikram 	    sizeof (DIRECT_BOOT_64) - 1) == 0) {
25836448Svikram 		BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
25846448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
25856448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
25866448Svikram 	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
25876448Svikram 		BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
25886448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
25896448Svikram 	} else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
25906448Svikram 		BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
25916448Svikram 		entry->flags |= BAM_ENTRY_MULTIBOOT;
25926448Svikram 	} else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
25936448Svikram 	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
25946448Svikram 		BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
25956448Svikram 		entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
25966448Svikram 	} else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
25976448Svikram 		BAM_DPRINTF((D_SET_HV, fcn, arg));
25986448Svikram 		entry->flags |= BAM_ENTRY_HV;
25996448Svikram 	} else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
26006448Svikram 		BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
26016448Svikram 		return (BAM_ERROR);
26026448Svikram 	} else {
26036448Svikram 		BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
26046448Svikram 		bam_error(UNKNOWN_KERNEL_LINE, linenum);
26056448Svikram 		return (BAM_ERROR);
26066448Svikram 	}
26076448Svikram 
26086448Svikram 	return (BAM_SUCCESS);
26096448Svikram }
26106448Svikram 
26116448Svikram static error_t
26126448Svikram module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
26136448Svikram {
26146448Svikram 	const char		*fcn = "module_parser()";
26156448Svikram 
26166448Svikram 	assert(entry);
26176448Svikram 	assert(cmd);
26186448Svikram 	assert(arg);
26196448Svikram 
26206448Svikram 	if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
26216448Svikram 	    strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
26226448Svikram 		BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
26236448Svikram 		return (BAM_ERROR);
26246448Svikram 	}
26256448Svikram 
26266448Svikram 	if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
26276448Svikram 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
26286448Svikram 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
26296448Svikram 	    strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
26306448Svikram 	    strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
26316448Svikram 	    strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
26326448Svikram 	    strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
26336448Svikram 		BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
26346448Svikram 		return (BAM_SUCCESS);
26356448Svikram 	} else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
26366448Svikram 	    !(entry->flags & BAM_ENTRY_LU)) {
26376448Svikram 		/* don't emit warning for hand entries */
26386448Svikram 		BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
26396448Svikram 		return (BAM_ERROR);
26406448Svikram 	} else {
26416448Svikram 		BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
26426448Svikram 		bam_error(UNKNOWN_MODULE_LINE, linenum);
26436448Svikram 		return (BAM_ERROR);
26446448Svikram 	}
26455084Sjohnlev }
26465084Sjohnlev 
26475084Sjohnlev /*
26480Sstevel@tonic-gate  * A line in menu.lst looks like
26490Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
26500Sstevel@tonic-gate  */
26510Sstevel@tonic-gate static void
26520Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
26530Sstevel@tonic-gate {
26540Sstevel@tonic-gate 	/*
26550Sstevel@tonic-gate 	 * save state across calls. This is so that
26560Sstevel@tonic-gate 	 * header gets the right entry# after title has
26570Sstevel@tonic-gate 	 * been processed
26580Sstevel@tonic-gate 	 */
2659662Sszhou 	static line_t *prev = NULL;
2660662Sszhou 	static entry_t *curr_ent = NULL;
26613446Smrj 	static int in_liveupgrade = 0;
26620Sstevel@tonic-gate 
26630Sstevel@tonic-gate 	line_t	*lp;
26640Sstevel@tonic-gate 	char *cmd, *sep, *arg;
26650Sstevel@tonic-gate 	char save, *cp, *line;
26660Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
26676448Svikram 	const char *fcn = "line_parser()";
26680Sstevel@tonic-gate 
26690Sstevel@tonic-gate 	if (str == NULL) {
26700Sstevel@tonic-gate 		return;
26710Sstevel@tonic-gate 	}
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 	/*
26740Sstevel@tonic-gate 	 * First save a copy of the entire line.
26750Sstevel@tonic-gate 	 * We use this later to set the line field.
26760Sstevel@tonic-gate 	 */
26770Sstevel@tonic-gate 	line = s_strdup(str);
26780Sstevel@tonic-gate 
26790Sstevel@tonic-gate 	/* Eat up leading whitespace */
26800Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
26810Sstevel@tonic-gate 		str++;
26820Sstevel@tonic-gate 
26830Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
26840Sstevel@tonic-gate 		cmd = s_strdup("#");
26850Sstevel@tonic-gate 		sep = NULL;
26860Sstevel@tonic-gate 		arg = s_strdup(str + 1);
26870Sstevel@tonic-gate 		flag = BAM_COMMENT;
26883446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
26893446Smrj 			in_liveupgrade = 1;
26903446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
26913446Smrj 			in_liveupgrade = 0;
26923446Smrj 		}
26930Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
26940Sstevel@tonic-gate 		cmd = sep = arg = NULL;
26950Sstevel@tonic-gate 		flag = BAM_EMPTY;
26960Sstevel@tonic-gate 	} else {
26970Sstevel@tonic-gate 		/*
26980Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
26990Sstevel@tonic-gate 		 * However various development bits use '=' as a
27000Sstevel@tonic-gate 		 * separator. In addition, external users also
27010Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
27020Sstevel@tonic-gate 		 */
27030Sstevel@tonic-gate 		cp = str;
27040Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
27050Sstevel@tonic-gate 			if (*str == '\0') {
27060Sstevel@tonic-gate 				cmd = s_strdup(cp);
27070Sstevel@tonic-gate 				sep = arg = NULL;
27080Sstevel@tonic-gate 				break;
27090Sstevel@tonic-gate 			}
27100Sstevel@tonic-gate 			str++;
27110Sstevel@tonic-gate 		}
27120Sstevel@tonic-gate 
27130Sstevel@tonic-gate 		if (*str != '\0') {
27140Sstevel@tonic-gate 			save = *str;
27150Sstevel@tonic-gate 			*str = '\0';
27160Sstevel@tonic-gate 			cmd = s_strdup(cp);
27170Sstevel@tonic-gate 			*str = save;
27180Sstevel@tonic-gate 
27190Sstevel@tonic-gate 			str++;
27200Sstevel@tonic-gate 			save = *str;
27210Sstevel@tonic-gate 			*str = '\0';
27220Sstevel@tonic-gate 			sep = s_strdup(str - 1);
27230Sstevel@tonic-gate 			*str = save;
27240Sstevel@tonic-gate 
27250Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
27260Sstevel@tonic-gate 				str++;
27270Sstevel@tonic-gate 			if (*str == '\0')
27280Sstevel@tonic-gate 				arg = NULL;
27290Sstevel@tonic-gate 			else
27300Sstevel@tonic-gate 				arg = s_strdup(str);
27310Sstevel@tonic-gate 		}
27320Sstevel@tonic-gate 	}
27330Sstevel@tonic-gate 
27340Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
27350Sstevel@tonic-gate 
27360Sstevel@tonic-gate 	lp->cmd = cmd;
27370Sstevel@tonic-gate 	lp->sep = sep;
27380Sstevel@tonic-gate 	lp->arg = arg;
27390Sstevel@tonic-gate 	lp->line = line;
27400Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
27410Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
27420Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
27430Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
27440Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
27453446Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
27460Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
2747662Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
27486448Svikram 			curr_ent->flags |= BAM_ENTRY_BOOTADM;
27496448Svikram 			BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
2750662Sszhou 		} else {
2751662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
27523446Smrj 			if (in_liveupgrade) {
27536448Svikram 				curr_ent->flags |= BAM_ENTRY_LU;
27546448Svikram 				BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
27553446Smrj 			}
2756662Sszhou 		}
27573446Smrj 		curr_ent->entryNum = *entryNum;
27580Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
27590Sstevel@tonic-gate 		/*
27600Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
27610Sstevel@tonic-gate 		 * by the subsequent title
27620Sstevel@tonic-gate 		 */
27630Sstevel@tonic-gate 		lp->entryNum = *entryNum;
27640Sstevel@tonic-gate 		lp->flags = flag;
27650Sstevel@tonic-gate 	} else {
27660Sstevel@tonic-gate 		lp->entryNum = *entryNum;
27673446Smrj 
27683446Smrj 		if (*entryNum == ENTRY_INIT) {
27693446Smrj 			lp->flags = BAM_GLOBAL;
27703446Smrj 		} else {
27713446Smrj 			lp->flags = BAM_ENTRY;
27723446Smrj 
27733446Smrj 			if (cmd && arg) {
27746448Svikram 				if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
27756448Svikram 					BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
27763446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
27776448Svikram 				} else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
27786448Svikram 				    == 0) {
27796448Svikram 					BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
27806448Svikram 					    arg));
27816448Svikram 					curr_ent->flags |= BAM_ENTRY_FINDROOT;
27826448Svikram 				} else if (strcmp(cmd,
27836448Svikram 				    menu_cmds[CHAINLOADER_CMD]) == 0) {
27846448Svikram 					BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
27856448Svikram 					    arg));
27863446Smrj 					curr_ent->flags |=
27873446Smrj 					    BAM_ENTRY_CHAINLOADER;
27886448Svikram 				} else if (kernel_parser(curr_ent, cmd, arg,
27896448Svikram 				    lp->lineNum) != BAM_SUCCESS) {
27906448Svikram 					(void) module_parser(curr_ent, cmd,
27916448Svikram 					    arg, lp->lineNum);
27926448Svikram 				}
27933446Smrj 			}
27943446Smrj 		}
27950Sstevel@tonic-gate 	}
27960Sstevel@tonic-gate 
2797662Sszhou 	/* record default, old default, and entry line ranges */
2798662Sszhou 	if (lp->flags == BAM_GLOBAL &&
2799662Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
2800662Sszhou 		mp->curdefault = lp;
2801662Sszhou 	} else if (lp->flags == BAM_COMMENT &&
2802662Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
2803662Sszhou 		mp->olddefault = lp;
28043446Smrj 	} else if (lp->flags == BAM_COMMENT &&
28053446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
28063446Smrj 		mp->old_rc_default = lp;
2807662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
28083446Smrj 	    (lp->flags == BAM_COMMENT &&
28093446Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
2810662Sszhou 		boot_entry_addline(curr_ent, lp);
2811662Sszhou 	}
28120Sstevel@tonic-gate 	append_line(mp, lp);
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 	prev = lp;
28150Sstevel@tonic-gate }
28160Sstevel@tonic-gate 
28176448Svikram void
2818621Svikram update_numbering(menu_t *mp)
2819621Svikram {
2820621Svikram 	int lineNum;
2821621Svikram 	int entryNum;
2822621Svikram 	int old_default_value;
2823621Svikram 	line_t *lp, *prev, *default_lp, *default_entry;
2824621Svikram 	char buf[PATH_MAX];
2825621Svikram 
2826621Svikram 	if (mp->start == NULL) {
2827621Svikram 		return;
2828621Svikram 	}
2829621Svikram 
2830621Svikram 	lineNum = LINE_INIT;
2831621Svikram 	entryNum = ENTRY_INIT;
2832621Svikram 	old_default_value = ENTRY_INIT;
2833621Svikram 	lp = default_lp = default_entry = NULL;
2834621Svikram 
2835621Svikram 	prev = NULL;
2836621Svikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
2837621Svikram 		lp->lineNum = ++lineNum;
2838621Svikram 
2839621Svikram 		/*
2840621Svikram 		 * Get the value of the default command
2841621Svikram 		 */
2842621Svikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
2843621Svikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
2844621Svikram 		    lp->arg) {
2845621Svikram 			old_default_value = atoi(lp->arg);
2846621Svikram 			default_lp = lp;
2847621Svikram 		}
2848621Svikram 
2849621Svikram 		/*
28506448Svikram 		 * If not a booting entry, nothing else to fix for this
2851621Svikram 		 * entry
2852621Svikram 		 */
2853621Svikram 		if (lp->entryNum == ENTRY_INIT)
2854621Svikram 			continue;
2855621Svikram 
2856621Svikram 		/*
2857621Svikram 		 * Record the position of the default entry.
2858621Svikram 		 * The following works because global
2859621Svikram 		 * commands like default and timeout should precede
2860621Svikram 		 * actual boot entries, so old_default_value
2861621Svikram 		 * is already known (or default cmd is missing).
2862621Svikram 		 */
2863621Svikram 		if (default_entry == NULL &&
2864621Svikram 		    old_default_value != ENTRY_INIT &&
2865621Svikram 		    lp->entryNum == old_default_value) {
2866621Svikram 			default_entry = lp;
2867621Svikram 		}
2868621Svikram 
2869621Svikram 		/*
2870621Svikram 		 * Now fixup the entry number
2871621Svikram 		 */
2872621Svikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
2873621Svikram 			lp->entryNum = ++entryNum;
2874621Svikram 			/* fixup the bootadm header */
2875621Svikram 			if (prev && prev->flags == BAM_COMMENT &&
28763446Smrj 			    prev->arg &&
28773446Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
2878621Svikram 				prev->entryNum = lp->entryNum;
2879621Svikram 			}
2880621Svikram 		} else {
2881621Svikram 			lp->entryNum = entryNum;
2882621Svikram 		}
2883621Svikram 	}
2884621Svikram 
2885621Svikram 	/*
2886621Svikram 	 * No default command in menu, simply return
2887621Svikram 	 */
2888621Svikram 	if (default_lp == NULL) {
2889621Svikram 		return;
2890621Svikram 	}
2891621Svikram 
2892621Svikram 	free(default_lp->arg);
2893621Svikram 	free(default_lp->line);
2894621Svikram 
2895621Svikram 	if (default_entry == NULL) {
2896621Svikram 		default_lp->arg = s_strdup("0");
2897621Svikram 	} else {
2898621Svikram 		(void) snprintf(buf, sizeof (buf), "%d",
2899621Svikram 		    default_entry->entryNum);
2900621Svikram 		default_lp->arg = s_strdup(buf);
2901621Svikram 	}
2902621Svikram 
2903621Svikram 	/*
2904621Svikram 	 * The following is required since only the line field gets
2905621Svikram 	 * written back to menu.lst
2906621Svikram 	 */
2907621Svikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
2908621Svikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
2909621Svikram 	default_lp->line = s_strdup(buf);
2910621Svikram }
2911621Svikram 
2912621Svikram 
29130Sstevel@tonic-gate static menu_t *
29140Sstevel@tonic-gate menu_read(char *menu_path)
29150Sstevel@tonic-gate {
29160Sstevel@tonic-gate 	FILE *fp;
29170Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
29180Sstevel@tonic-gate 	menu_t *mp;
29190Sstevel@tonic-gate 	int line, entry, len, n;
29200Sstevel@tonic-gate 
29210Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
29220Sstevel@tonic-gate 
29230Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
29240Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
29250Sstevel@tonic-gate 		return (mp);
29260Sstevel@tonic-gate 	}
29270Sstevel@tonic-gate 
29280Sstevel@tonic-gate 
29290Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
29300Sstevel@tonic-gate 	line = LINE_INIT;
29310Sstevel@tonic-gate 	entry = ENTRY_INIT;
29320Sstevel@tonic-gate 	cp = buf;
29330Sstevel@tonic-gate 	len = sizeof (buf);
29340Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
29350Sstevel@tonic-gate 		n = strlen(cp);
29360Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
29370Sstevel@tonic-gate 			len -= n - 1;
29380Sstevel@tonic-gate 			assert(len >= 2);
29390Sstevel@tonic-gate 			cp += n - 1;
29400Sstevel@tonic-gate 			continue;
29410Sstevel@tonic-gate 		}
29420Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
29430Sstevel@tonic-gate 		cp = buf;
29440Sstevel@tonic-gate 		len = sizeof (buf);
29450Sstevel@tonic-gate 	}
29460Sstevel@tonic-gate 
29470Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
29480Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
29490Sstevel@tonic-gate 	}
29500Sstevel@tonic-gate 
29510Sstevel@tonic-gate 	return (mp);
29520Sstevel@tonic-gate }
29530Sstevel@tonic-gate 
29540Sstevel@tonic-gate static error_t
29550Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
29560Sstevel@tonic-gate {
29570Sstevel@tonic-gate 	char *eq;
29580Sstevel@tonic-gate 	char *opt_dup;
29590Sstevel@tonic-gate 	int entryNum;
29600Sstevel@tonic-gate 
29610Sstevel@tonic-gate 	assert(mp);
29620Sstevel@tonic-gate 	assert(mp->start);
29630Sstevel@tonic-gate 	assert(opt);
29640Sstevel@tonic-gate 
29650Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
29660Sstevel@tonic-gate 
29670Sstevel@tonic-gate 	if (entry)
29680Sstevel@tonic-gate 		*entry = ENTRY_INIT;
29690Sstevel@tonic-gate 	if (title)
29700Sstevel@tonic-gate 		*title = NULL;
29710Sstevel@tonic-gate 
29720Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
29730Sstevel@tonic-gate 	if (eq == NULL) {
29740Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
29750Sstevel@tonic-gate 		free(opt_dup);
29760Sstevel@tonic-gate 		return (BAM_ERROR);
29770Sstevel@tonic-gate 	}
29780Sstevel@tonic-gate 
29790Sstevel@tonic-gate 	*eq = '\0';
29800Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
29810Sstevel@tonic-gate 		assert(mp->end);
29820Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
29830Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
29840Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
29850Sstevel@tonic-gate 			free(opt_dup);
29860Sstevel@tonic-gate 			return (BAM_ERROR);
29870Sstevel@tonic-gate 		}
29880Sstevel@tonic-gate 		*entry = entryNum;
29890Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
29900Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
29910Sstevel@tonic-gate 	} else {
29920Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
29930Sstevel@tonic-gate 		free(opt_dup);
29940Sstevel@tonic-gate 		return (BAM_ERROR);
29950Sstevel@tonic-gate 	}
29960Sstevel@tonic-gate 
29970Sstevel@tonic-gate 	free(opt_dup);
29980Sstevel@tonic-gate 	return (BAM_SUCCESS);
29990Sstevel@tonic-gate }
30000Sstevel@tonic-gate 
30010Sstevel@tonic-gate /*
30020Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
30030Sstevel@tonic-gate  * only title lines in file are printed.
30040Sstevel@tonic-gate  *
30050Sstevel@tonic-gate  * If invoked with a title or entry #, all
30060Sstevel@tonic-gate  * lines in *every* matching entry are listed
30070Sstevel@tonic-gate  */
30080Sstevel@tonic-gate static error_t
30090Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
30100Sstevel@tonic-gate {
30110Sstevel@tonic-gate 	line_t *lp;
30120Sstevel@tonic-gate 	int entry = ENTRY_INIT;
30130Sstevel@tonic-gate 	int found;
30140Sstevel@tonic-gate 	char *title = NULL;
30150Sstevel@tonic-gate 
30160Sstevel@tonic-gate 	assert(mp);
30170Sstevel@tonic-gate 	assert(menu_path);
30180Sstevel@tonic-gate 
30196448Svikram 	/* opt is optional */
30206448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
30216448Svikram 	    opt ? opt : "<NULL>"));
30226448Svikram 
30230Sstevel@tonic-gate 	if (mp->start == NULL) {
30240Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
30250Sstevel@tonic-gate 		return (BAM_ERROR);
30260Sstevel@tonic-gate 	}
30270Sstevel@tonic-gate 
30280Sstevel@tonic-gate 	if (opt != NULL) {
30290Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
30300Sstevel@tonic-gate 			return (BAM_ERROR);
30310Sstevel@tonic-gate 		}
30320Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
30330Sstevel@tonic-gate 	} else {
30340Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
30350Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
30360Sstevel@tonic-gate 	}
30370Sstevel@tonic-gate 
30380Sstevel@tonic-gate 	found = 0;
30390Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
30400Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
30410Sstevel@tonic-gate 			continue;
30420Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
30430Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
30440Sstevel@tonic-gate 			    lp->arg);
30450Sstevel@tonic-gate 			found = 1;
30460Sstevel@tonic-gate 			continue;
30470Sstevel@tonic-gate 		}
30480Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
30490Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
30500Sstevel@tonic-gate 			found = 1;
30510Sstevel@tonic-gate 			continue;
30520Sstevel@tonic-gate 		}
30530Sstevel@tonic-gate 
30540Sstevel@tonic-gate 		/*
30550Sstevel@tonic-gate 		 * We set the entry value here so that all lines
30560Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
30570Sstevel@tonic-gate 		 * title in other entries, all lines in those
30580Sstevel@tonic-gate 		 * entries get printed as well.
30590Sstevel@tonic-gate 		 */
30600Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
30610Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
30620Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
30630Sstevel@tonic-gate 			entry = lp->entryNum;
30640Sstevel@tonic-gate 			found = 1;
30650Sstevel@tonic-gate 			continue;
30660Sstevel@tonic-gate 		}
30670Sstevel@tonic-gate 	}
30680Sstevel@tonic-gate 
30690Sstevel@tonic-gate 	if (!found) {
30700Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
30710Sstevel@tonic-gate 		return (BAM_ERROR);
30720Sstevel@tonic-gate 	}
30730Sstevel@tonic-gate 
30740Sstevel@tonic-gate 	return (BAM_SUCCESS);
30750Sstevel@tonic-gate }
30760Sstevel@tonic-gate 
30775084Sjohnlev int
30780Sstevel@tonic-gate add_boot_entry(menu_t *mp,
30790Sstevel@tonic-gate 	char *title,
30806448Svikram 	char *findroot,
30810Sstevel@tonic-gate 	char *kernel,
30825084Sjohnlev 	char *mod_kernel,
30830Sstevel@tonic-gate 	char *module)
30840Sstevel@tonic-gate {
30856448Svikram 	int		lineNum;
30866448Svikram 	int		entryNum;
30876448Svikram 	char		linebuf[BAM_MAXLINE];
30886448Svikram 	menu_cmd_t	k_cmd;
30896448Svikram 	menu_cmd_t	m_cmd;
30906448Svikram 	const char	*fcn = "add_boot_entry()";
30910Sstevel@tonic-gate 
30920Sstevel@tonic-gate 	assert(mp);
30930Sstevel@tonic-gate 
30946448Svikram 	INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
30956448Svikram 	if (findroot == NULL) {
30966448Svikram 		bam_error(NULL_FINDROOT);
30976448Svikram 		return (BAM_ERROR);
30986448Svikram 	}
30996448Svikram 
31000Sstevel@tonic-gate 	if (title == NULL) {
3101656Sszhou 		title = "Solaris";	/* default to Solaris */
31020Sstevel@tonic-gate 	}
31030Sstevel@tonic-gate 	if (kernel == NULL) {
31040Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
31050Sstevel@tonic-gate 		return (BAM_ERROR);
31060Sstevel@tonic-gate 	}
31070Sstevel@tonic-gate 	if (module == NULL) {
31083446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
31093446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
31103446Smrj 			return (BAM_ERROR);
31113446Smrj 		}
31123446Smrj 
31133446Smrj 		/* Figure the commands out from the kernel line */
31143446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
31153446Smrj 			module = DIRECT_BOOT_ARCHIVE;
31163446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
31173446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
31183446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
31193446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
31203446Smrj 			k_cmd = KERNEL_CMD;
31213446Smrj 			m_cmd = MODULE_CMD;
31223446Smrj 		} else {
31233446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
31243446Smrj 			k_cmd = KERNEL_CMD;
31253446Smrj 			m_cmd = MODULE_CMD;
31263446Smrj 		}
31273446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
31283446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
31293446Smrj 		/*
31303446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
31313446Smrj 		 * command.  Otherwise, use "kernel".
31323446Smrj 		 */
31333446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
31343446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
31353446Smrj 	} else {
31363446Smrj 		k_cmd = KERNEL_CMD;
31373446Smrj 		m_cmd = MODULE_CMD;
31380Sstevel@tonic-gate 	}
31390Sstevel@tonic-gate 
31400Sstevel@tonic-gate 	if (mp->start) {
31410Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
31420Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
31430Sstevel@tonic-gate 	} else {
31440Sstevel@tonic-gate 		lineNum = LINE_INIT;
31450Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
31460Sstevel@tonic-gate 	}
31470Sstevel@tonic-gate 
31480Sstevel@tonic-gate 	/*
31490Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
31500Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
31510Sstevel@tonic-gate 	 */
31520Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
31533446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
3154662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
31550Sstevel@tonic-gate 
31560Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
31570Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
3158662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
3159662Sszhou 
31606448Svikram 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
31616448Svikram 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
31626448Svikram 	line_parser(mp, linebuf, &lineNum, &entryNum);
31636448Svikram 	BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
31640Sstevel@tonic-gate 
31650Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
31663446Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
3167662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
31680Sstevel@tonic-gate 
31695084Sjohnlev 	if (mod_kernel != NULL) {
31705084Sjohnlev 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
31715084Sjohnlev 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
31725084Sjohnlev 		line_parser(mp, linebuf, &lineNum, &entryNum);
31735084Sjohnlev 	}
31745084Sjohnlev 
31750Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
31763446Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
3177662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
31780Sstevel@tonic-gate 
31790Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
31803446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
3181662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
31820Sstevel@tonic-gate 
31830Sstevel@tonic-gate 	return (entryNum);
31840Sstevel@tonic-gate }
31850Sstevel@tonic-gate 
31860Sstevel@tonic-gate static error_t
31870Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
31880Sstevel@tonic-gate {
31896448Svikram 	line_t		*lp;
31906448Svikram 	line_t		*freed;
31916448Svikram 	entry_t		*ent;
31926448Svikram 	entry_t		*tmp;
31936448Svikram 	int		deleted;
31946448Svikram 	const char	*fcn = "do_delete()";
31950Sstevel@tonic-gate 
31960Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
31970Sstevel@tonic-gate 
31986448Svikram 	tmp = NULL;
31996448Svikram 
3200662Sszhou 	ent = mp->entries;
3201662Sszhou 	while (ent) {
3202662Sszhou 		lp = ent->start;
3203662Sszhou 		/* check entry number and make sure it's a bootadm entry */
3204662Sszhou 		if (lp->flags != BAM_COMMENT ||
32053446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
3206662Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
3207662Sszhou 			ent = ent->next;
32080Sstevel@tonic-gate 			continue;
32090Sstevel@tonic-gate 		}
32100Sstevel@tonic-gate 
3211662Sszhou 		/* free the entry content */
3212662Sszhou 		do {
3213662Sszhou 			freed = lp;
3214662Sszhou 			lp = lp->next;	/* prev stays the same */
32156448Svikram 			BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
3216662Sszhou 			unlink_line(mp, freed);
3217662Sszhou 			line_free(freed);
3218662Sszhou 		} while (freed != ent->end);
3219662Sszhou 
3220662Sszhou 		/* free the entry_t structure */
32216448Svikram 		assert(tmp == NULL);
3222662Sszhou 		tmp = ent;
3223662Sszhou 		ent = ent->next;
3224662Sszhou 		if (tmp->prev)
3225662Sszhou 			tmp->prev->next = ent;
32260Sstevel@tonic-gate 		else
3227662Sszhou 			mp->entries = ent;
3228662Sszhou 		if (ent)
3229662Sszhou 			ent->prev = tmp->prev;
32306448Svikram 		BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
32316448Svikram 		free(tmp);
32326448Svikram 		tmp = NULL;
32330Sstevel@tonic-gate 		deleted = 1;
32340Sstevel@tonic-gate 	}
32350Sstevel@tonic-gate 
32366448Svikram 	assert(tmp == NULL);
32376448Svikram 
32380Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
32390Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
32400Sstevel@tonic-gate 		return (BAM_ERROR);
32410Sstevel@tonic-gate 	}
32420Sstevel@tonic-gate 
3243621Svikram 	/*
3244621Svikram 	 * Now that we have deleted an entry, update
3245621Svikram 	 * the entry numbering and the default cmd.
3246621Svikram 	 */
3247621Svikram 	update_numbering(mp);
3248621Svikram 
32490Sstevel@tonic-gate 	return (BAM_SUCCESS);
32500Sstevel@tonic-gate }
32510Sstevel@tonic-gate 
32520Sstevel@tonic-gate static error_t
32536448Svikram delete_all_entries(menu_t *mp, char *dummy, char *opt)
32540Sstevel@tonic-gate {
32550Sstevel@tonic-gate 	assert(mp);
32566448Svikram 	assert(dummy == NULL);
32570Sstevel@tonic-gate 	assert(opt == NULL);
32580Sstevel@tonic-gate 
32596448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
32606448Svikram 
32610Sstevel@tonic-gate 	if (mp->start == NULL) {
32626448Svikram 		bam_print(EMPTY_MENU);
32630Sstevel@tonic-gate 		return (BAM_SUCCESS);
32640Sstevel@tonic-gate 	}
32650Sstevel@tonic-gate 
32660Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
32670Sstevel@tonic-gate 		return (BAM_ERROR);
32680Sstevel@tonic-gate 	}
32690Sstevel@tonic-gate 
32700Sstevel@tonic-gate 	return (BAM_WRITE);
32710Sstevel@tonic-gate }
32720Sstevel@tonic-gate 
32730Sstevel@tonic-gate static FILE *
32746448Svikram create_diskmap(char *osroot)
32750Sstevel@tonic-gate {
32760Sstevel@tonic-gate 	FILE *fp;
32770Sstevel@tonic-gate 	char cmd[PATH_MAX];
32786448Svikram 	const char *fcn = "create_diskmap()";
32790Sstevel@tonic-gate 
32800Sstevel@tonic-gate 	/* make sure we have a map file */
32810Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
32820Sstevel@tonic-gate 	if (fp == NULL) {
32830Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
32846448Svikram 		    "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
32856448Svikram 		if (exec_cmd(cmd, NULL) != 0)
32866448Svikram 			return (NULL);
32870Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
32886448Svikram 		INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
32896448Svikram 		if (fp) {
32906448Svikram 			BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
32916448Svikram 		} else {
32926448Svikram 			BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
32936448Svikram 		}
32940Sstevel@tonic-gate 	}
32950Sstevel@tonic-gate 	return (fp);
32960Sstevel@tonic-gate }
32970Sstevel@tonic-gate 
32980Sstevel@tonic-gate #define	SECTOR_SIZE	512
32990Sstevel@tonic-gate 
33000Sstevel@tonic-gate static int
33010Sstevel@tonic-gate get_partition(char *device)
33020Sstevel@tonic-gate {
33030Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
33040Sstevel@tonic-gate 	struct mboot *mboot;
33050Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
33060Sstevel@tonic-gate 	char *wholedisk, *slice;
33070Sstevel@tonic-gate 
33080Sstevel@tonic-gate 	/* form whole disk (p0) */
33090Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
33100Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
33110Sstevel@tonic-gate 	if (!is_pcfs)
33120Sstevel@tonic-gate 		*slice = '\0';
33130Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
33140Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
33150Sstevel@tonic-gate 	if (!is_pcfs)
33160Sstevel@tonic-gate 		*slice = 's';
33170Sstevel@tonic-gate 
33180Sstevel@tonic-gate 	/* read boot sector */
33190Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
33200Sstevel@tonic-gate 	free(wholedisk);
33210Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
33220Sstevel@tonic-gate 		return (partno);
33230Sstevel@tonic-gate 	}
33240Sstevel@tonic-gate 	(void) close(fd);
33250Sstevel@tonic-gate 
33260Sstevel@tonic-gate 	/* parse fdisk table */
33270Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
33280Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
33290Sstevel@tonic-gate 		struct ipart *part =
33300Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
33310Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
33320Sstevel@tonic-gate 			if (part->systid == 0xbe) {
33330Sstevel@tonic-gate 				partno = i;
33340Sstevel@tonic-gate 				break;
33350Sstevel@tonic-gate 			}
33360Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
33370Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
33380Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
33390Sstevel@tonic-gate 				partno = i;
33400Sstevel@tonic-gate 				break;
33410Sstevel@tonic-gate 			}
33420Sstevel@tonic-gate 		}
33430Sstevel@tonic-gate 	}
33440Sstevel@tonic-gate 	return (partno);
33450Sstevel@tonic-gate }
33460Sstevel@tonic-gate 
33476448Svikram char *
33486448Svikram get_grubroot(char *osroot, char *osdev, char *menu_root)
33496448Svikram {
33506448Svikram 	char		*grubroot;	/* (hd#,#,#) */
33516448Svikram 	char		*slice;
33526448Svikram 	char		*grubhd;
33536448Svikram 	int		fdiskpart;
33546448Svikram 	int		found = 0;
33556448Svikram 	char		*devname;
33566448Svikram 	char		*ctdname = strstr(osdev, "dsk/");
33576448Svikram 	char		linebuf[PATH_MAX];
33586448Svikram 	FILE		*fp;
33596448Svikram 	const char	*fcn = "get_grubroot()";
33606448Svikram 
33616448Svikram 	INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
33626448Svikram 	if (ctdname == NULL) {
33636448Svikram 		bam_error(INVALID_DEV_DSK, osdev);
33640Sstevel@tonic-gate 		return (NULL);
33656448Svikram 	}
33666448Svikram 
33676448Svikram 	if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
33686448Svikram 		/* menu bears no resemblance to our reality */
33696448Svikram 		bam_error(CANNOT_GRUBROOT_BOOTDISK, fcn, osdev);
33706448Svikram 		return (NULL);
33716448Svikram 	}
33720Sstevel@tonic-gate 
33730Sstevel@tonic-gate 	ctdname += strlen("dsk/");
33740Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
33750Sstevel@tonic-gate 	if (slice)
33760Sstevel@tonic-gate 		*slice = '\0';
33770Sstevel@tonic-gate 
33786448Svikram 	fp = create_diskmap(osroot);
33796448Svikram 	if (fp == NULL) {
33806448Svikram 		bam_error(DISKMAP_FAIL, osroot);
33816448Svikram 		return (NULL);
33826448Svikram 	}
33836448Svikram 
33840Sstevel@tonic-gate 	rewind(fp);
33850Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
33860Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
33870Sstevel@tonic-gate 		if (grubhd)
33880Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
33890Sstevel@tonic-gate 		else
33900Sstevel@tonic-gate 			devname = NULL;
33910Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
33920Sstevel@tonic-gate 			found = 1;
33930Sstevel@tonic-gate 			break;
33940Sstevel@tonic-gate 		}
33950Sstevel@tonic-gate 	}
33960Sstevel@tonic-gate 
33970Sstevel@tonic-gate 	if (slice)
33980Sstevel@tonic-gate 		*slice = 's';
33990Sstevel@tonic-gate 
34006448Svikram 	(void) fclose(fp);
34016448Svikram 	fp = NULL;
34026448Svikram 
34036448Svikram 	INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
34040Sstevel@tonic-gate 	if (found == 0) {
34056448Svikram 		bam_error(BIOSDEV_FAIL, osdev);
34060Sstevel@tonic-gate 		return (NULL);
34076448Svikram 	}
34086448Svikram 
34096448Svikram 	fdiskpart = get_partition(osdev);
34106448Svikram 	INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1);
34116448Svikram 	if (fdiskpart == -1) {
34126448Svikram 		bam_error(FDISKPART_FAIL, osdev);
34136448Svikram 		return (NULL);
34146448Svikram 	}
34156448Svikram 
34166448Svikram 	grubroot = s_calloc(1, 10);
34170Sstevel@tonic-gate 	if (slice) {
34186448Svikram 		(void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
34190Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
34200Sstevel@tonic-gate 	} else
34216448Svikram 		(void) snprintf(grubroot, 10, "(hd%s,%d)",
34220Sstevel@tonic-gate 		    grubhd, fdiskpart);
34230Sstevel@tonic-gate 
34246448Svikram 	assert(fp == NULL);
34256448Svikram 	assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
34266448Svikram 	return (grubroot);
34276448Svikram }
34286448Svikram 
34296448Svikram static char *
34306448Svikram find_primary_common(char *mntpt, char *fstype)
34316448Svikram {
34326448Svikram 	char		signdir[PATH_MAX];
34336448Svikram 	char		tmpsign[MAXNAMELEN + 1];
34346448Svikram 	char		*lu;
34356448Svikram 	char		*ufs;
34366448Svikram 	char		*zfs;
34376448Svikram 	DIR		*dirp = NULL;
34386448Svikram 	struct dirent	*entp;
34396448Svikram 	struct stat	sb;
34406448Svikram 	const char	*fcn = "find_primary_common()";
34416448Svikram 
34426448Svikram 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
34436448Svikram 	    mntpt, GRUBSIGN_DIR);
34446448Svikram 
34456448Svikram 	if (stat(signdir, &sb) == -1) {
34466448Svikram 		BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
34476448Svikram 		return (NULL);
34486448Svikram 	}
34496448Svikram 
34506448Svikram 	dirp = opendir(signdir);
34516448Svikram 	INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
34526448Svikram 	if (dirp == NULL) {
34536448Svikram 		bam_error(OPENDIR_FAILED, signdir, strerror(errno));
34546448Svikram 		return (NULL);
34556448Svikram 	}
34566448Svikram 
34576448Svikram 	ufs = zfs = lu = NULL;
34586448Svikram 
34596448Svikram 	while (entp = readdir(dirp)) {
34606448Svikram 		if (strcmp(entp->d_name, ".") == 0 ||
34616448Svikram 		    strcmp(entp->d_name, "..") == 0)
34626448Svikram 			continue;
34636448Svikram 
34646448Svikram 		(void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
34656448Svikram 
34666448Svikram 		if (lu == NULL &&
34676448Svikram 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
34686448Svikram 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
34696448Svikram 			lu = s_strdup(tmpsign);
34706448Svikram 		}
34716448Svikram 
34726448Svikram 		if (ufs == NULL &&
34736448Svikram 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
34746448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
34756448Svikram 			ufs = s_strdup(tmpsign);
34766448Svikram 		}
34776448Svikram 
34786448Svikram 		if (zfs == NULL &&
34796448Svikram 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
34806448Svikram 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
34816448Svikram 			zfs = s_strdup(tmpsign);
34826448Svikram 		}
34836448Svikram 	}
34846448Svikram 
34856448Svikram 	BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
34866448Svikram 	    zfs ? zfs : "NULL",
34876448Svikram 	    ufs ? ufs : "NULL",
34886448Svikram 	    lu ? lu : "NULL"));
34896448Svikram 
34906448Svikram 	if (dirp) {
34916448Svikram 		(void) closedir(dirp);
34926448Svikram 		dirp = NULL;
34936448Svikram 	}
34946448Svikram 
34956448Svikram 	if (strcmp(fstype, "ufs") == 0 && zfs) {
34966448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
34976448Svikram 		free(zfs);
34986448Svikram 		zfs = NULL;
34996448Svikram 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
35006448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
35016448Svikram 		free(ufs);
35026448Svikram 		ufs = NULL;
35036448Svikram 	}
35046448Svikram 
35056448Svikram 	assert(dirp == NULL);
35066448Svikram 
35076448Svikram 	/* For now, we let Live Upgrade take care of its signature itself */
35086448Svikram 	if (lu) {
35096448Svikram 		BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
35106448Svikram 		free(lu);
35116448Svikram 		lu = NULL;
35126448Svikram 	}
35136448Svikram 
35146448Svikram 	return (zfs ? zfs : ufs);
35156448Svikram }
35166448Svikram 
35176448Svikram static char *
35186448Svikram find_backup_common(char *mntpt, char *fstype)
35196448Svikram {
35206448Svikram 	FILE		*bfp = NULL;
35216448Svikram 	char		tmpsign[MAXNAMELEN + 1];
35226448Svikram 	char		backup[PATH_MAX];
35236448Svikram 	char		*ufs;
35246448Svikram 	char		*zfs;
35256448Svikram 	char		*lu;
35266448Svikram 	int		error;
35276448Svikram 	const char	*fcn = "find_backup_common()";
35286448Svikram 
35296448Svikram 	/*
35306448Svikram 	 * We didn't find it in the primary directory.
35316448Svikram 	 * Look at the backup
35326448Svikram 	 */
35336448Svikram 	(void) snprintf(backup, sizeof (backup), "%s%s",
35346448Svikram 	    mntpt, GRUBSIGN_BACKUP);
35356448Svikram 
35366448Svikram 	bfp = fopen(backup, "r");
35376448Svikram 	if (bfp == NULL) {
35386448Svikram 		error = errno;
35396448Svikram 		if (bam_verbose) {
35406448Svikram 			bam_error(OPEN_FAIL, backup, strerror(error));
35416448Svikram 		}
35426448Svikram 		BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
35436448Svikram 		return (NULL);
35446448Svikram 	}
35456448Svikram 
35466448Svikram 	ufs = zfs = lu = NULL;
35476448Svikram 
35486448Svikram 	while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
35496448Svikram 
35506448Svikram 		if (lu == NULL &&
35516448Svikram 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
35526448Svikram 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
35536448Svikram 			lu = s_strdup(tmpsign);
35546448Svikram 		}
35556448Svikram 
35566448Svikram 		if (ufs == NULL &&
35576448Svikram 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
35586448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
35596448Svikram 			ufs = s_strdup(tmpsign);
35606448Svikram 		}
35616448Svikram 
35626448Svikram 		if (zfs == NULL &&
35636448Svikram 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
35646448Svikram 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
35656448Svikram 			zfs = s_strdup(tmpsign);
35666448Svikram 		}
35676448Svikram 	}
35686448Svikram 
35696448Svikram 	BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
35706448Svikram 	    zfs ? zfs : "NULL",
35716448Svikram 	    ufs ? ufs : "NULL",
35726448Svikram 	    lu ? lu : "NULL"));
35736448Svikram 
35746448Svikram 	if (bfp) {
35756448Svikram 		(void) fclose(bfp);
35766448Svikram 		bfp = NULL;
35776448Svikram 	}
35786448Svikram 
35796448Svikram 	if (strcmp(fstype, "ufs") == 0 && zfs) {
35806448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
35816448Svikram 		free(zfs);
35826448Svikram 		zfs = NULL;
35836448Svikram 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
35846448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
35856448Svikram 		free(ufs);
35866448Svikram 		ufs = NULL;
35876448Svikram 	}
35886448Svikram 
35896448Svikram 	assert(bfp == NULL);
35906448Svikram 
35916448Svikram 	/* For now, we let Live Upgrade take care of its signature itself */
35926448Svikram 	if (lu) {
35936448Svikram 		BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
35946448Svikram 		free(lu);
35956448Svikram 		lu = NULL;
35966448Svikram 	}
35976448Svikram 
35986448Svikram 	return (zfs ? zfs : ufs);
35996448Svikram }
36006448Svikram 
36016448Svikram static char *
36026448Svikram find_ufs_existing(char *osroot)
36036448Svikram {
36046448Svikram 	char		*sign;
36056448Svikram 	const char	*fcn = "find_ufs_existing()";
36066448Svikram 
36076448Svikram 	sign = find_primary_common(osroot, "ufs");
36086448Svikram 	if (sign == NULL) {
36096448Svikram 		sign = find_backup_common(osroot, "ufs");
36106448Svikram 		BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
36116448Svikram 	} else {
36126448Svikram 		BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
36136448Svikram 	}
36146448Svikram 
36156448Svikram 	return (sign);
36166448Svikram }
36176448Svikram 
36186448Svikram char *
36196448Svikram get_mountpoint(char *special, char *fstype)
36206448Svikram {
36216448Svikram 	FILE		*mntfp;
36226448Svikram 	struct mnttab	mp = {0};
36236448Svikram 	struct mnttab	mpref = {0};
36246448Svikram 	int		error;
36256448Svikram 	int		ret;
36266448Svikram 	const char	*fcn = "get_mountpoint()";
36276448Svikram 
36286448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
36296448Svikram 
36306448Svikram 	mntfp = fopen(MNTTAB, "r");
36316448Svikram 	error = errno;
36326448Svikram 	INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
36336448Svikram 	if (mntfp == NULL) {
36346448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
36356448Svikram 		return (NULL);
36366448Svikram 	}
36376448Svikram 
36386448Svikram 	mpref.mnt_special = special;
36396448Svikram 	mpref.mnt_fstype = fstype;
36406448Svikram 
36416448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
36426448Svikram 	INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
36436448Svikram 	if (ret != 0) {
36446448Svikram 		(void) fclose(mntfp);
36456448Svikram 		BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
36466448Svikram 		return (NULL);
36476448Svikram 	}
36486448Svikram 	(void) fclose(mntfp);
36496448Svikram 
36506448Svikram 	assert(mp.mnt_mountp);
36516448Svikram 
36526448Svikram 	BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
36536448Svikram 
36546448Svikram 	return (s_strdup(mp.mnt_mountp));
36556448Svikram }
36566448Svikram 
36576448Svikram /*
36586448Svikram  * Mounts a "legacy" top dataset (if needed)
36596448Svikram  * Returns:	The mountpoint of the legacy top dataset or NULL on error
36606448Svikram  * 		mnted returns one of the above values defined for zfs_mnted_t
36616448Svikram  */
36626448Svikram static char *
36636448Svikram mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
36646448Svikram {
36656448Svikram 	char		cmd[PATH_MAX];
36666448Svikram 	char		tmpmnt[PATH_MAX];
36676448Svikram 	filelist_t	flist = {0};
36686448Svikram 	char		*is_mounted;
36696448Svikram 	struct stat	sb;
36706448Svikram 	int		ret;
36716448Svikram 	const char	*fcn = "mount_legacy_dataset()";
36726448Svikram 
36736448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
36746448Svikram 
36756448Svikram 	*mnted = ZFS_MNT_ERROR;
36766448Svikram 
36776448Svikram 	(void) snprintf(cmd, sizeof (cmd),
36786448Svikram 	    "/sbin/zfs get -Ho value mounted %s",
36796448Svikram 	    pool);
36806448Svikram 
36816448Svikram 	ret = exec_cmd(cmd, &flist);
36826448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
36836448Svikram 	if (ret != 0) {
36846448Svikram 		bam_error(ZFS_MNTED_FAILED, pool);
36856448Svikram 		return (NULL);
36866448Svikram 	}
36876448Svikram 
36886448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
36896448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
36906448Svikram 		bam_error(BAD_ZFS_MNTED, pool);
36916448Svikram 		filelist_free(&flist);
36926448Svikram 		return (NULL);
36936448Svikram 	}
36946448Svikram 
36956448Svikram 	is_mounted = strtok(flist.head->line, " \t\n");
36966448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
36976448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
36986448Svikram 	if (strcmp(is_mounted, "no") != 0) {
36996448Svikram 		filelist_free(&flist);
37006448Svikram 		*mnted = LEGACY_ALREADY;
37016448Svikram 		/* get_mountpoint returns a strdup'ed string */
37026448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
37036448Svikram 		return (get_mountpoint(pool, "zfs"));
37046448Svikram 	}
37056448Svikram 
37066448Svikram 	filelist_free(&flist);
37076448Svikram 
37086448Svikram 	/*
37096448Svikram 	 * legacy top dataset is not mounted. Mount it now
37106448Svikram 	 * First create a mountpoint.
37116448Svikram 	 */
37126448Svikram 	(void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
37136448Svikram 	    ZFS_LEGACY_MNTPT, getpid());
37146448Svikram 
37156448Svikram 	ret = stat(tmpmnt, &sb);
37166448Svikram 	if (ret == -1) {
37176448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
37186448Svikram 		ret = mkdirp(tmpmnt, 0755);
37196448Svikram 		INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
37206448Svikram 		if (ret == -1) {
37216448Svikram 			bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
37226448Svikram 			return (NULL);
37236448Svikram 		}
37246448Svikram 	} else {
37256448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
37266448Svikram 	}
37276448Svikram 
37286448Svikram 	(void) snprintf(cmd, sizeof (cmd),
37296448Svikram 	    "/sbin/mount -F zfs %s %s",
37306448Svikram 	    pool, tmpmnt);
37316448Svikram 
37326448Svikram 	ret = exec_cmd(cmd, NULL);
37336448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
37346448Svikram 	if (ret != 0) {
37356448Svikram 		bam_error(ZFS_MOUNT_FAILED, pool);
37366448Svikram 		(void) rmdir(tmpmnt);
37376448Svikram 		return (NULL);
37386448Svikram 	}
37396448Svikram 
37406448Svikram 	*mnted = LEGACY_MOUNTED;
37416448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
37426448Svikram 	return (s_strdup(tmpmnt));
37436448Svikram }
37446448Svikram 
37456448Svikram /*
37466448Svikram  * Mounts the top dataset (if needed)
37476448Svikram  * Returns:	The mountpoint of the top dataset or NULL on error
37486448Svikram  * 		mnted returns one of the above values defined for zfs_mnted_t
37496448Svikram  */
37506448Svikram static char *
37516448Svikram mount_top_dataset(char *pool, zfs_mnted_t *mnted)
37526448Svikram {
37536448Svikram 	char		cmd[PATH_MAX];
37546448Svikram 	filelist_t	flist = {0};
37556448Svikram 	char		*is_mounted;
37566448Svikram 	char		*mntpt;
37576448Svikram 	char		*zmntpt;
37586448Svikram 	int		ret;
37596448Svikram 	const char	*fcn = "mount_top_dataset()";
37606448Svikram 
37616448Svikram 	*mnted = ZFS_MNT_ERROR;
37626448Svikram 
37636448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
37646448Svikram 
37656448Svikram 	/*
37666448Svikram 	 * First check if the top dataset is a "legacy" dataset
37676448Svikram 	 */
37686448Svikram 	(void) snprintf(cmd, sizeof (cmd),
37696448Svikram 	    "/sbin/zfs get -Ho value mountpoint %s",
37706448Svikram 	    pool);
37716448Svikram 	ret = exec_cmd(cmd, &flist);
37726448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
37736448Svikram 	if (ret != 0) {
37746448Svikram 		bam_error(ZFS_MNTPT_FAILED, pool);
37756448Svikram 		return (NULL);
37766448Svikram 	}
37776448Svikram 
37786448Svikram 	if (flist.head && (flist.head == flist.tail)) {
37796448Svikram 		char *legacy = strtok(flist.head->line, " \t\n");
37806448Svikram 		if (legacy && strcmp(legacy, "legacy") == 0) {
37816448Svikram 			filelist_free(&flist);
37826448Svikram 			BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
37836448Svikram 			return (mount_legacy_dataset(pool, mnted));
37846448Svikram 		}
37856448Svikram 	}
37866448Svikram 
37876448Svikram 	filelist_free(&flist);
37886448Svikram 
37896448Svikram 	BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
37906448Svikram 
37916448Svikram 	(void) snprintf(cmd, sizeof (cmd),
37926448Svikram 	    "/sbin/zfs get -Ho value mounted %s",
37936448Svikram 	    pool);
37946448Svikram 
37956448Svikram 	ret = exec_cmd(cmd, &flist);
37966448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
37976448Svikram 	if (ret != 0) {
37986448Svikram 		bam_error(ZFS_MNTED_FAILED, pool);
37996448Svikram 		return (NULL);
38006448Svikram 	}
38016448Svikram 
38026448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
38036448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
38046448Svikram 		bam_error(BAD_ZFS_MNTED, pool);
38056448Svikram 		filelist_free(&flist);
38066448Svikram 		return (NULL);
38076448Svikram 	}
38086448Svikram 
38096448Svikram 	is_mounted = strtok(flist.head->line, " \t\n");
38106448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
38116448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
38126448Svikram 	if (strcmp(is_mounted, "no") != 0) {
38136448Svikram 		filelist_free(&flist);
38146448Svikram 		*mnted = ZFS_ALREADY;
38156448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
38166448Svikram 		goto mounted;
38176448Svikram 	}
38186448Svikram 
38196448Svikram 	filelist_free(&flist);
38206448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
38216448Svikram 
38226448Svikram 	/* top dataset is not mounted. Mount it now */
38236448Svikram 	(void) snprintf(cmd, sizeof (cmd),
38246448Svikram 	    "/sbin/zfs mount %s", pool);
38256448Svikram 	ret = exec_cmd(cmd, NULL);
38266448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
38276448Svikram 	if (ret != 0) {
38286448Svikram 		bam_error(ZFS_MOUNT_FAILED, pool);
38296448Svikram 		return (NULL);
38306448Svikram 	}
38316448Svikram 	*mnted = ZFS_MOUNTED;
38326448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
38336448Svikram 	/*FALLTHRU*/
38346448Svikram mounted:
38356448Svikram 	/*
38366448Svikram 	 * Now get the mountpoint
38376448Svikram 	 */
38386448Svikram 	(void) snprintf(cmd, sizeof (cmd),
38396448Svikram 	    "/sbin/zfs get -Ho value mountpoint %s",
38406448Svikram 	    pool);
38416448Svikram 
38426448Svikram 	ret = exec_cmd(cmd, &flist);
38436448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
38446448Svikram 	if (ret != 0) {
38456448Svikram 		bam_error(ZFS_MNTPT_FAILED, pool);
38466448Svikram 		goto error;
38476448Svikram 	}
38486448Svikram 
38496448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
38506448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
38516448Svikram 		bam_error(NULL_ZFS_MNTPT, pool);
38526448Svikram 		goto error;
38536448Svikram 	}
38546448Svikram 
38556448Svikram 	mntpt = strtok(flist.head->line, " \t\n");
38566448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
38576448Svikram 	if (*mntpt != '/') {
38586448Svikram 		bam_error(BAD_ZFS_MNTPT, pool, mntpt);
38596448Svikram 		goto error;
38606448Svikram 	}
38616448Svikram 	zmntpt = s_strdup(mntpt);
38626448Svikram 
38636448Svikram 	filelist_free(&flist);
38646448Svikram 
38656448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
38666448Svikram 
38676448Svikram 	return (zmntpt);
38686448Svikram 
38696448Svikram error:
38706448Svikram 	filelist_free(&flist);
38716448Svikram 	(void) umount_top_dataset(pool, *mnted, NULL);
38726448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
38736448Svikram 	return (NULL);
38746448Svikram }
38756448Svikram 
38766448Svikram static int
38776448Svikram umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
38786448Svikram {
38796448Svikram 	char		cmd[PATH_MAX];
38806448Svikram 	int		ret;
38816448Svikram 	const char	*fcn = "umount_top_dataset()";
38826448Svikram 
38836448Svikram 	INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
38846448Svikram 	switch (mnted) {
38856448Svikram 	case LEGACY_ALREADY:
38866448Svikram 	case ZFS_ALREADY:
38876448Svikram 		/* nothing to do */
38886448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
38896448Svikram 		    mntpt ? mntpt : "NULL"));
38906448Svikram 		free(mntpt);
38916448Svikram 		return (BAM_SUCCESS);
38926448Svikram 	case LEGACY_MOUNTED:
38936448Svikram 		(void) snprintf(cmd, sizeof (cmd),
38946448Svikram 		    "/sbin/umount %s", pool);
38956448Svikram 		ret = exec_cmd(cmd, NULL);
38966448Svikram 		INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
38976448Svikram 		if (ret != 0) {
38986448Svikram 			bam_error(UMOUNT_FAILED, pool);
38996448Svikram 			free(mntpt);
39006448Svikram 			return (BAM_ERROR);
39016448Svikram 		}
39026448Svikram 		if (mntpt)
39036448Svikram 			(void) rmdir(mntpt);
39046448Svikram 		free(mntpt);
39056448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
39066448Svikram 		return (BAM_SUCCESS);
39076448Svikram 	case ZFS_MOUNTED:
39086448Svikram 		free(mntpt);
39096448Svikram 		(void) snprintf(cmd, sizeof (cmd),
39106448Svikram 		    "/sbin/zfs unmount %s", pool);
39116448Svikram 		ret = exec_cmd(cmd, NULL);
39126448Svikram 		INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
39136448Svikram 		if (ret != 0) {
39146448Svikram 			bam_error(UMOUNT_FAILED, pool);
39156448Svikram 			return (BAM_ERROR);
39166448Svikram 		}
39176448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
39186448Svikram 		return (BAM_SUCCESS);
39196448Svikram 	default:
39206448Svikram 		bam_error(INT_BAD_MNTSTATE, pool);
39216448Svikram 		return (BAM_ERROR);
39226448Svikram 	}
39236448Svikram 	/*NOTREACHED*/
39246448Svikram }
39256448Svikram 
39266448Svikram /*
39276448Svikram  * For ZFS, osdev can be one of two forms
39286448Svikram  * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
39296448Svikram  * It can be a /dev/[r]dsk special file. We handle both instances
39306448Svikram  */
39316448Svikram static char *
39326448Svikram get_pool(char *osdev)
39336448Svikram {
39346448Svikram 	char		cmd[PATH_MAX];
39356448Svikram 	char		buf[PATH_MAX];
39366448Svikram 	filelist_t	flist = {0};
39376448Svikram 	char		*pool;
39386448Svikram 	char		*cp;
39396448Svikram 	char		*slash;
39406448Svikram 	int		ret;
39416448Svikram 	const char	*fcn = "get_pool()";
39426448Svikram 
39436448Svikram 	INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
39446448Svikram 	if (osdev == NULL) {
39456448Svikram 		bam_error(GET_POOL_OSDEV_NULL);
39466448Svikram 		return (NULL);
39476448Svikram 	}
39486448Svikram 
39496448Svikram 	BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
39506448Svikram 
39516448Svikram 	if (osdev[0] != '/') {
39526448Svikram 		(void) strlcpy(buf, osdev, sizeof (buf));
39536448Svikram 		slash = strchr(buf, '/');
39546448Svikram 		if (slash)
39556448Svikram 			*slash = '\0';
39566448Svikram 		pool = s_strdup(buf);
39576448Svikram 		BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
39586448Svikram 		return (pool);
39596448Svikram 	} else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
39606448Svikram 	    strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
39616448Svikram 		bam_error(GET_POOL_BAD_OSDEV, osdev);
39626448Svikram 		return (NULL);
39636448Svikram 	}
39646448Svikram 
39656448Svikram 	(void) snprintf(cmd, sizeof (cmd),
39666448Svikram 	    "/usr/sbin/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
39676448Svikram 	    osdev);
39686448Svikram 
39696448Svikram 	ret = exec_cmd(cmd, &flist);
39706448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
39716448Svikram 	if (ret != 0) {
39726448Svikram 		bam_error(FSTYP_A_FAILED, osdev);
39736448Svikram 		return (NULL);
39746448Svikram 	}
39756448Svikram 
39766448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
39776448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
39786448Svikram 		bam_error(NULL_FSTYP_A, osdev);
39796448Svikram 		filelist_free(&flist);
39806448Svikram 		return (NULL);
39816448Svikram 	}
39826448Svikram 
39836448Svikram 	(void) strtok(flist.head->line, "'");
39846448Svikram 	cp = strtok(NULL, "'");
39856448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
39866448Svikram 	if (cp == NULL) {
39876448Svikram 		bam_error(BAD_FSTYP_A, osdev);
39886448Svikram 		filelist_free(&flist);
39896448Svikram 		return (NULL);
39906448Svikram 	}
39916448Svikram 
39926448Svikram 	pool = s_strdup(cp);
39936448Svikram 
39946448Svikram 	filelist_free(&flist);
39956448Svikram 
39966448Svikram 	BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
39976448Svikram 
39986448Svikram 	return (pool);
39996448Svikram }
40006448Svikram 
40016448Svikram static char *
40026448Svikram find_zfs_existing(char *osdev)
40036448Svikram {
40046448Svikram 	char		*pool;
40056448Svikram 	zfs_mnted_t	mnted;
40066448Svikram 	char		*mntpt;
40076448Svikram 	char		*sign;
40086448Svikram 	const char	*fcn = "find_zfs_existing()";
40096448Svikram 
40106448Svikram 	pool = get_pool(osdev);
40116448Svikram 	INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
40126448Svikram 	if (pool == NULL) {
40136448Svikram 		bam_error(ZFS_GET_POOL_FAILED, osdev);
40146448Svikram 		return (NULL);
40156448Svikram 	}
40166448Svikram 
40176448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
40186448Svikram 	INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
40196448Svikram 	if (mntpt == NULL) {
40206448Svikram 		bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
40216448Svikram 		free(pool);
40226448Svikram 		return (NULL);
40236448Svikram 	}
40246448Svikram 
40256448Svikram 	sign = find_primary_common(mntpt, "zfs");
40266448Svikram 	if (sign == NULL) {
40276448Svikram 		sign = find_backup_common(mntpt, "zfs");
40286448Svikram 		BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
40296448Svikram 	} else {
40306448Svikram 		BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
40316448Svikram 	}
40326448Svikram 
40336448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
40346448Svikram 
40356448Svikram 	free(pool);
40366448Svikram 
40376448Svikram 	return (sign);
40386448Svikram }
40396448Svikram 
40406448Svikram static char *
40416448Svikram find_existing_sign(char *osroot, char *osdev, char *fstype)
40426448Svikram {
40436448Svikram 	const char		*fcn = "find_existing_sign()";
40446448Svikram 
40456448Svikram 	INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
40466448Svikram 	if (strcmp(fstype, "ufs") == 0) {
40476448Svikram 		BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
40486448Svikram 		return (find_ufs_existing(osroot));
40496448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
40506448Svikram 		BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
40516448Svikram 		return (find_zfs_existing(osdev));
40526448Svikram 	} else {
40536448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
40546448Svikram 		return (NULL);
40556448Svikram 	}
40566448Svikram }
40576448Svikram 
40586448Svikram #define	MH_HASH_SZ	16
40596448Svikram 
40606448Svikram typedef enum {
40616448Svikram 	MH_ERROR = -1,
40626448Svikram 	MH_NOMATCH,
40636448Svikram 	MH_MATCH
40646448Svikram } mh_search_t;
40656448Svikram 
40666448Svikram typedef struct mcache {
40676448Svikram 	char	*mc_special;
40686448Svikram 	char	*mc_mntpt;
40696448Svikram 	char	*mc_fstype;
40706448Svikram 	struct mcache *mc_next;
40716448Svikram } mcache_t;
40726448Svikram 
40736448Svikram typedef struct mhash {
40746448Svikram 	mcache_t *mh_hash[MH_HASH_SZ];
40756448Svikram } mhash_t;
40766448Svikram 
40776448Svikram static int
40786448Svikram mhash_fcn(char *key)
40796448Svikram {
40806448Svikram 	int		i;
40816448Svikram 	uint64_t	sum = 0;
40826448Svikram 
40836448Svikram 	for (i = 0; key[i] != '\0'; i++) {
40846448Svikram 		sum += (uchar_t)key[i];
40856448Svikram 	}
40866448Svikram 
40876448Svikram 	sum %= MH_HASH_SZ;
40886448Svikram 
40896448Svikram 	assert(sum < MH_HASH_SZ);
40906448Svikram 
40916448Svikram 	return (sum);
40926448Svikram }
40936448Svikram 
40946448Svikram static mhash_t *
40956448Svikram cache_mnttab(void)
40966448Svikram {
40976448Svikram 	FILE		*mfp;
40986448Svikram 	struct extmnttab mnt;
40996448Svikram 	mcache_t	*mcp;
41006448Svikram 	mhash_t		*mhp;
41016448Svikram 	char		*ctds;
41026448Svikram 	int		idx;
41036448Svikram 	int		error;
41046448Svikram 	char		*special_dup;
41056448Svikram 	const char	*fcn = "cache_mnttab()";
41066448Svikram 
41076448Svikram 	mfp = fopen(MNTTAB, "r");
41086448Svikram 	error = errno;
41096448Svikram 	INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
41106448Svikram 	if (mfp == NULL) {
41116448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
41126448Svikram 		return (NULL);
41136448Svikram 	}
41146448Svikram 
41156448Svikram 	mhp = s_calloc(1, sizeof (mhash_t));
41166448Svikram 
41176448Svikram 	resetmnttab(mfp);
41186448Svikram 
41196448Svikram 	while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
41206448Svikram 		/* only cache ufs */
41216448Svikram 		if (strcmp(mnt.mnt_fstype, "ufs") != 0)
41226448Svikram 			continue;
41236448Svikram 
41246448Svikram 		/* basename() modifies its arg, so dup it */
41256448Svikram 		special_dup = s_strdup(mnt.mnt_special);
41266448Svikram 		ctds = basename(special_dup);
41276448Svikram 
41286448Svikram 		mcp = s_calloc(1, sizeof (mcache_t));
41296448Svikram 		mcp->mc_special = s_strdup(ctds);
41306448Svikram 		mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
41316448Svikram 		mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
41326448Svikram 		BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
41336448Svikram 		    mnt.mnt_mountp, mnt.mnt_fstype));
41346448Svikram 		idx = mhash_fcn(ctds);
41356448Svikram 		mcp->mc_next = mhp->mh_hash[idx];
41366448Svikram 		mhp->mh_hash[idx] = mcp;
41376448Svikram 		free(special_dup);
41386448Svikram 	}
41396448Svikram 
41406448Svikram 	(void) fclose(mfp);
41416448Svikram 
41426448Svikram 	return (mhp);
41436448Svikram }
41446448Svikram 
41456448Svikram static void
41466448Svikram free_mnttab(mhash_t *mhp)
41476448Svikram {
41486448Svikram 	mcache_t	*mcp;
41496448Svikram 	int		i;
41506448Svikram 
41516448Svikram 	for (i = 0; i < MH_HASH_SZ; i++) {
41526448Svikram 		/*LINTED*/
41536448Svikram 		while (mcp = mhp->mh_hash[i]) {
41546448Svikram 			mhp->mh_hash[i] = mcp->mc_next;
41556448Svikram 			free(mcp->mc_special);
41566448Svikram 			free(mcp->mc_mntpt);
41576448Svikram 			free(mcp->mc_fstype);
41586448Svikram 			free(mcp);
41596448Svikram 		}
41606448Svikram 	}
41616448Svikram 
41626448Svikram 	for (i = 0; i < MH_HASH_SZ; i++) {
41636448Svikram 		assert(mhp->mh_hash[i] == NULL);
41646448Svikram 	}
41656448Svikram 	free(mhp);
41666448Svikram }
41676448Svikram 
41686448Svikram static mh_search_t
41696448Svikram search_hash(mhash_t *mhp, char *special, char **mntpt)
41706448Svikram {
41716448Svikram 	int		idx;
41726448Svikram 	mcache_t	*mcp;
41736448Svikram 	const char 	*fcn = "search_hash()";
41746448Svikram 
41756448Svikram 	assert(mntpt);
41766448Svikram 
41776448Svikram 	*mntpt = NULL;
41786448Svikram 
41796448Svikram 	INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
41806448Svikram 	if (strchr(special, '/')) {
41816448Svikram 		bam_error(INVALID_MHASH_KEY, special);
41826448Svikram 		return (MH_ERROR);
41836448Svikram 	}
41846448Svikram 
41856448Svikram 	idx = mhash_fcn(special);
41866448Svikram 
41876448Svikram 	for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
41886448Svikram 		if (strcmp(mcp->mc_special, special) == 0)
41896448Svikram 			break;
41906448Svikram 	}
41916448Svikram 
41926448Svikram 	if (mcp == NULL) {
41936448Svikram 		BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
41946448Svikram 		return (MH_NOMATCH);
41956448Svikram 	}
41966448Svikram 
41976448Svikram 	assert(strcmp(mcp->mc_fstype, "ufs") == 0);
41986448Svikram 	*mntpt = mcp->mc_mntpt;
41996448Svikram 	BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
42006448Svikram 	return (MH_MATCH);
42016448Svikram }
42026448Svikram 
42036448Svikram static int
42046448Svikram check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
42056448Svikram {
42066448Svikram 	char		*sign;
42076448Svikram 	char		*signline;
42086448Svikram 	char		signbuf[MAXNAMELEN];
42096448Svikram 	int		len;
42106448Svikram 	int		error;
42116448Svikram 	const char	*fcn = "check_add_ufs_sign_to_list()";
42126448Svikram 
42136448Svikram 	/* safe to specify NULL as "osdev" arg for UFS */
42146448Svikram 	sign = find_existing_sign(mntpt, NULL, "ufs");
42156448Svikram 	if (sign == NULL) {
42166448Svikram 		/* No existing signature, nothing to add to list */
42176448Svikram 		BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
42186448Svikram 		return (0);
42196448Svikram 	}
42206448Svikram 
42216448Svikram 	(void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
42226448Svikram 	signline = signbuf;
42236448Svikram 
42246448Svikram 	INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
42256448Svikram 	if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
42266448Svikram 	    strlen(GRUBSIGN_UFS_PREFIX))) {
42276448Svikram 		bam_error(INVALID_UFS_SIGNATURE, sign);
42286448Svikram 		free(sign);
42296448Svikram 		/* ignore invalid signatures */
42306448Svikram 		return (0);
42316448Svikram 	}
42326448Svikram 
42336448Svikram 	len = fputs(signline, tfp);
42346448Svikram 	error = errno;
42356448Svikram 	INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
42366448Svikram 	if (len != strlen(signline)) {
42376448Svikram 		bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
42386448Svikram 		free(sign);
42396448Svikram 		return (-1);
42406448Svikram 	}
42416448Svikram 
42426448Svikram 	free(sign);
42436448Svikram 
42446448Svikram 	BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
42456448Svikram 	return (0);
42466448Svikram }
42476448Svikram 
42486448Svikram /*
42496448Svikram  * slice is a basename not a full pathname
42506448Svikram  */
42516448Svikram static int
42526448Svikram process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
42536448Svikram {
42546448Svikram 	int		ret;
42556448Svikram 	char		cmd[PATH_MAX];
42566448Svikram 	char		path[PATH_MAX];
42576448Svikram 	struct stat	sbuf;
42586448Svikram 	char		*mntpt;
42596448Svikram 	filelist_t	flist = {0};
42606448Svikram 	char		*fstype;
42616448Svikram 	char		blkslice[PATH_MAX];
42626448Svikram 	const char	*fcn = "process_slice_common()";
42636448Svikram 
42646448Svikram 
42656448Svikram 	ret = search_hash(mhp, slice, &mntpt);
42666448Svikram 	switch (ret) {
42676448Svikram 		case MH_MATCH:
42686448Svikram 			if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
42696448Svikram 				return (-1);
42706448Svikram 			else
42716448Svikram 				return (0);
42726448Svikram 		case MH_NOMATCH:
42736448Svikram 			break;
42746448Svikram 		case MH_ERROR:
42756448Svikram 		default:
42766448Svikram 			return (-1);
42776448Svikram 	}
42786448Svikram 
42796448Svikram 	(void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
42806448Svikram 	if (stat(path, &sbuf) == -1) {
42816448Svikram 		BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
42826448Svikram 		return (0);
42836448Svikram 	}
42846448Svikram 
42856448Svikram 	/* Check if ufs */
42866448Svikram 	(void) snprintf(cmd, sizeof (cmd),
42876448Svikram 	    "/usr/sbin/fstyp /dev/rdsk/%s 2>/dev/null",
42886448Svikram 	    slice);
42896448Svikram 
42906448Svikram 	if (exec_cmd(cmd, &flist) != 0) {
42916448Svikram 		if (bam_verbose)
42926448Svikram 			bam_print(FSTYP_FAILED, slice);
42936448Svikram 		return (0);
42946448Svikram 	}
42956448Svikram 
42966448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
42976448Svikram 		if (bam_verbose)
42986448Svikram 			bam_print(FSTYP_BAD, slice);
42996448Svikram 		filelist_free(&flist);
43006448Svikram 		return (0);
43016448Svikram 	}
43026448Svikram 
43036448Svikram 	fstype = strtok(flist.head->line, " \t\n");
43046448Svikram 	if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
43056448Svikram 		if (bam_verbose)
43066448Svikram 			bam_print(NOT_UFS_SLICE, slice, fstype);
43076448Svikram 		filelist_free(&flist);
43086448Svikram 		return (0);
43096448Svikram 	}
43106448Svikram 
43116448Svikram 	filelist_free(&flist);
43126448Svikram 
43136448Svikram 	/*
43146448Svikram 	 * Since we are mounting the filesystem read-only, the
43156448Svikram 	 * the last mount field of the superblock is unchanged
43166448Svikram 	 * and does not need to be fixed up post-mount;
43176448Svikram 	 */
43186448Svikram 
43196448Svikram 	(void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
43206448Svikram 	    slice);
43216448Svikram 
43226448Svikram 	(void) snprintf(cmd, sizeof (cmd),
43236448Svikram 	    "/usr/sbin/mount -F ufs -o ro %s %s "
43246448Svikram 	    "> /dev/null 2>&1", blkslice, tmpmnt);
43256448Svikram 
43266448Svikram 	if (exec_cmd(cmd, NULL) != 0) {
43276448Svikram 		if (bam_verbose)
43286448Svikram 			bam_print(MOUNT_FAILED, blkslice, "ufs");
43296448Svikram 		return (0);
43306448Svikram 	}
43316448Svikram 
43326448Svikram 	ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
43336448Svikram 
43346448Svikram 	(void) snprintf(cmd, sizeof (cmd),
43356448Svikram 	    "/usr/sbin/umount -f %s > /dev/null 2>&1",
43366448Svikram 	    tmpmnt);
43376448Svikram 
43386448Svikram 	if (exec_cmd(cmd, NULL) != 0) {
43396448Svikram 		bam_print(UMOUNT_FAILED, slice);
43406448Svikram 		return (0);
43416448Svikram 	}
43426448Svikram 
43436448Svikram 	return (ret);
43446448Svikram }
43456448Svikram 
43466448Svikram static int
43476448Svikram process_vtoc_slices(
43486448Svikram 	char *s0,
43496448Svikram 	struct vtoc *vtoc,
43506448Svikram 	FILE *tfp,
43516448Svikram 	mhash_t *mhp,
43526448Svikram 	char *tmpmnt)
43536448Svikram {
43546448Svikram 	int		idx;
43556448Svikram 	char		slice[PATH_MAX];
43566448Svikram 	size_t		len;
43576448Svikram 	char		*cp;
43586448Svikram 	const char	*fcn = "process_vtoc_slices()";
43596448Svikram 
43606448Svikram 	len = strlen(s0);
43616448Svikram 
43626448Svikram 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
43636448Svikram 
43646448Svikram 	s0[len - 1] = '\0';
43656448Svikram 
43666448Svikram 	(void) strlcpy(slice, s0, sizeof (slice));
43676448Svikram 
43686448Svikram 	s0[len - 1] = '0';
43696448Svikram 
43706448Svikram 	cp = slice + len - 1;
43716448Svikram 
43726448Svikram 	for (idx = 0; idx < vtoc->v_nparts; idx++) {
43736448Svikram 
43746448Svikram 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
43756448Svikram 
43766448Svikram 		if (vtoc->v_part[idx].p_size == 0) {
43776448Svikram 			BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
43786448Svikram 			continue;
43796448Svikram 		}
43806448Svikram 
43816448Svikram 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
43826448Svikram 		switch (vtoc->v_part[idx].p_tag) {
43836448Svikram 		case V_SWAP:
43846448Svikram 		case V_USR:
43856448Svikram 		case V_BACKUP:
43866448Svikram 		case V_VAR:
43876448Svikram 		case V_HOME:
43886448Svikram 		case V_ALTSCTR:
43896448Svikram 			BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
43906448Svikram 			continue;
43916448Svikram 		default:
43926448Svikram 			BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
43936448Svikram 			break;
43946448Svikram 		}
43956448Svikram 
43966448Svikram 		/* skip unmountable and readonly slices */
43976448Svikram 		switch (vtoc->v_part[idx].p_flag) {
43986448Svikram 		case V_UNMNT:
43996448Svikram 		case V_RONLY:
44006448Svikram 			BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
44016448Svikram 			continue;
44026448Svikram 		default:
44036448Svikram 			BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
44046448Svikram 			break;
44056448Svikram 		}
44066448Svikram 
44076448Svikram 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
44086448Svikram 			return (-1);
44096448Svikram 		}
44106448Svikram 	}
44116448Svikram 
44126448Svikram 	return (0);
44136448Svikram }
44146448Svikram 
44156448Svikram static int
44166448Svikram process_efi_slices(
44176448Svikram 	char *s0,
44186448Svikram 	struct dk_gpt *efi,
44196448Svikram 	FILE *tfp,
44206448Svikram 	mhash_t *mhp,
44216448Svikram 	char *tmpmnt)
44226448Svikram {
44236448Svikram 	int		idx;
44246448Svikram 	char		slice[PATH_MAX];
44256448Svikram 	size_t		len;
44266448Svikram 	char		*cp;
44276448Svikram 	const char	*fcn = "process_efi_slices()";
44286448Svikram 
44296448Svikram 	len = strlen(s0);
44306448Svikram 
44316448Svikram 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
44326448Svikram 
44336448Svikram 	s0[len - 1] = '\0';
44346448Svikram 
44356448Svikram 	(void) strlcpy(slice, s0, sizeof (slice));
44366448Svikram 
44376448Svikram 	s0[len - 1] = '0';
44386448Svikram 
44396448Svikram 	cp = slice + len - 1;
44406448Svikram 
44416448Svikram 	for (idx = 0; idx < efi->efi_nparts; idx++) {
44426448Svikram 
44436448Svikram 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
44446448Svikram 
44456448Svikram 		if (efi->efi_parts[idx].p_size == 0) {
44466448Svikram 			BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
44476448Svikram 			continue;
44486448Svikram 		}
44496448Svikram 
44506448Svikram 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
44516448Svikram 		switch (efi->efi_parts[idx].p_tag) {
44526448Svikram 		case V_SWAP:
44536448Svikram 		case V_USR:
44546448Svikram 		case V_BACKUP:
44556448Svikram 		case V_VAR:
44566448Svikram 		case V_HOME:
44576448Svikram 		case V_ALTSCTR:
44586448Svikram 			BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
44596448Svikram 			continue;
44606448Svikram 		default:
44616448Svikram 			BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
44626448Svikram 			break;
44636448Svikram 		}
44646448Svikram 
44656448Svikram 		/* skip unmountable and readonly slices */
44666448Svikram 		switch (efi->efi_parts[idx].p_flag) {
44676448Svikram 		case V_UNMNT:
44686448Svikram 		case V_RONLY:
44696448Svikram 			BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
44706448Svikram 			continue;
44716448Svikram 		default:
44726448Svikram 			BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
44736448Svikram 			break;
44746448Svikram 		}
44756448Svikram 
44766448Svikram 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
44776448Svikram 			return (-1);
44786448Svikram 		}
44796448Svikram 	}
44806448Svikram 
44816448Svikram 	return (0);
44826448Svikram }
44836448Svikram 
44846448Svikram /*
44856448Svikram  * s0 is a basename not a full path
44866448Svikram  */
44876448Svikram static int
44886448Svikram process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
44896448Svikram {
44906448Svikram 	struct vtoc		vtoc;
44916448Svikram 	struct dk_gpt		*efi;
44926448Svikram 	char			s0path[PATH_MAX];
44936448Svikram 	struct stat		sbuf;
44946448Svikram 	int			e_flag;
44956448Svikram 	int			v_flag;
44966448Svikram 	int			retval;
44976448Svikram 	int			err;
44986448Svikram 	int			fd;
44996448Svikram 	const char		*fcn = "process_slice0()";
45006448Svikram 
45016448Svikram 	(void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
45026448Svikram 
45036448Svikram 	if (stat(s0path, &sbuf) == -1) {
45046448Svikram 		BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
45056448Svikram 		return (0);
45066448Svikram 	}
45076448Svikram 
45086448Svikram 	fd = open(s0path, O_NONBLOCK|O_RDONLY);
45096448Svikram 	if (fd == -1) {
45106448Svikram 		bam_error(OPEN_FAIL, s0path, strerror(errno));
45116448Svikram 		return (0);
45126448Svikram 	}
45136448Svikram 
45146448Svikram 	e_flag = v_flag = 0;
45156448Svikram 	retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
45166448Svikram 	switch (retval) {
45176448Svikram 		case VT_EIO:
45186448Svikram 			BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
45196448Svikram 			break;
45206448Svikram 		case VT_EINVAL:
45216448Svikram 			BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
45226448Svikram 			break;
45236448Svikram 		case VT_ERROR:
45246448Svikram 			BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
45256448Svikram 			break;
45266448Svikram 		case VT_ENOTSUP:
45276448Svikram 			e_flag = 1;
45286448Svikram 			BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
45296448Svikram 			break;
45306448Svikram 		case 0:
45316448Svikram 			v_flag = 1;
45326448Svikram 			BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
45336448Svikram 			break;
45346448Svikram 		default:
45356448Svikram 			BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
45366448Svikram 			break;
45376448Svikram 	}
45386448Svikram 
45396448Svikram 
45406448Svikram 	if (e_flag) {
45416448Svikram 		e_flag = 0;
45426448Svikram 		retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
45436448Svikram 		switch (retval) {
45446448Svikram 		case VT_EIO:
45456448Svikram 			BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
45466448Svikram 			break;
45476448Svikram 		case VT_EINVAL:
45486448Svikram 			BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
45496448Svikram 			break;
45506448Svikram 		case VT_ERROR:
45516448Svikram 			BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
45526448Svikram 			break;
45536448Svikram 		case VT_ENOTSUP:
45546448Svikram 			BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
45556448Svikram 			break;
45566448Svikram 		case 0:
45576448Svikram 			e_flag = 1;
45586448Svikram 			BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
45596448Svikram 			break;
45606448Svikram 		default:
45616448Svikram 			BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
45626448Svikram 			break;
45636448Svikram 		}
45646448Svikram 	}
45656448Svikram 
45666448Svikram 	(void) close(fd);
45676448Svikram 
45686448Svikram 	if (v_flag) {
45696448Svikram 		retval = process_vtoc_slices(s0,
45706448Svikram 		    &vtoc, tfp, mhp, tmpmnt);
45716448Svikram 	} else if (e_flag) {
45726448Svikram 		retval = process_efi_slices(s0,
45736448Svikram 		    efi, tfp, mhp, tmpmnt);
45746448Svikram 	} else {
45756448Svikram 		BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
45766448Svikram 		return (0);
45776448Svikram 	}
45786448Svikram 
45796448Svikram 	return (retval);
45806448Svikram }
45816448Svikram 
45826448Svikram /*
45836448Svikram  * Find and create a list of all existing UFS boot signatures
45846448Svikram  */
45856448Svikram static int
45866448Svikram FindAllUfsSignatures(void)
45876448Svikram {
45886448Svikram 	mhash_t		*mnttab_hash;
45896448Svikram 	DIR		*dirp = NULL;
45906448Svikram 	struct dirent	*dp;
45916448Svikram 	char		tmpmnt[PATH_MAX];
45926448Svikram 	char		cmd[PATH_MAX];
45936448Svikram 	struct stat	sb;
45946448Svikram 	int		fd;
45956448Svikram 	FILE		*tfp;
45966448Svikram 	size_t		len;
45976448Svikram 	int		ret;
45986448Svikram 	int		error;
45996448Svikram 	const char	*fcn = "FindAllUfsSignatures()";
46006448Svikram 
46016448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) != -1)  {
46026448Svikram 		bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
46036448Svikram 		return (0);
46046448Svikram 	}
46056448Svikram 
46066448Svikram 	fd = open(UFS_SIGNATURE_LIST".tmp",
46076448Svikram 	    O_RDWR|O_CREAT|O_TRUNC, 0644);
46086448Svikram 	error = errno;
46096448Svikram 	INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
46106448Svikram 	if (fd == -1) {
46116448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
46126448Svikram 		return (-1);
46136448Svikram 	}
46146448Svikram 
46156448Svikram 	ret = close(fd);
46166448Svikram 	error = errno;
46176448Svikram 	INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
46186448Svikram 	if (ret == -1) {
46196448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
46206448Svikram 		    strerror(error));
46216448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
46226448Svikram 		return (-1);
46236448Svikram 	}
46246448Svikram 
46256448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
46266448Svikram 	error = errno;
46276448Svikram 	INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
46286448Svikram 	if (tfp == NULL) {
46296448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
46306448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
46316448Svikram 		return (-1);
46326448Svikram 	}
46336448Svikram 
46346448Svikram 	mnttab_hash = cache_mnttab();
46356448Svikram 	INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
46366448Svikram 	if (mnttab_hash == NULL) {
46376448Svikram 		(void) fclose(tfp);
46386448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
46396448Svikram 		bam_error(CACHE_MNTTAB_FAIL, fcn);
46406448Svikram 		return (-1);
46416448Svikram 	}
46426448Svikram 
46436448Svikram 	(void) snprintf(tmpmnt, sizeof (tmpmnt),
46446448Svikram 	    "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
46456448Svikram 	(void) unlink(tmpmnt);
46466448Svikram 
46476448Svikram 	ret = mkdirp(tmpmnt, 0755);
46486448Svikram 	error = errno;
46496448Svikram 	INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
46506448Svikram 	if (ret == -1) {
46516448Svikram 		bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
46526448Svikram 		free_mnttab(mnttab_hash);
46536448Svikram 		(void) fclose(tfp);
46546448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
46556448Svikram 		return (-1);
46566448Svikram 	}
46576448Svikram 
46586448Svikram 	dirp = opendir("/dev/rdsk");
46596448Svikram 	error = errno;
46606448Svikram 	INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
46616448Svikram 	if (dirp == NULL) {
46626448Svikram 		bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
46636448Svikram 		goto fail;
46646448Svikram 	}
46656448Svikram 
46666448Svikram 	while (dp = readdir(dirp)) {
46676448Svikram 		if (strcmp(dp->d_name, ".") == 0 ||
46686448Svikram 		    strcmp(dp->d_name, "..") == 0)
46696448Svikram 			continue;
46706448Svikram 
46716448Svikram 		/*
46726448Svikram 		 * we only look for the s0 slice. This is guranteed to
46736448Svikram 		 * have 's' at len - 2.
46746448Svikram 		 */
46756448Svikram 		len = strlen(dp->d_name);
46766448Svikram 		if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
46776448Svikram 			BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
46786448Svikram 			continue;
46796448Svikram 		}
46806448Svikram 
46816448Svikram 		ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
46826448Svikram 		INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
46836448Svikram 		if (ret == -1)
46846448Svikram 			goto fail;
46856448Svikram 	}
46866448Svikram 
46876448Svikram 	(void) closedir(dirp);
46886448Svikram 	free_mnttab(mnttab_hash);
46896448Svikram 	(void) rmdir(tmpmnt);
46906448Svikram 
46916448Svikram 	ret = fclose(tfp);
46926448Svikram 	error = errno;
46936448Svikram 	INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
46946448Svikram 	if (ret == EOF) {
46956448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
46966448Svikram 		    strerror(error));
46976448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
46986448Svikram 		return (-1);
46996448Svikram 	}
47006448Svikram 
47016448Svikram 	/* We have a list of existing GRUB signatures. Sort it first */
47026448Svikram 	(void) snprintf(cmd, sizeof (cmd),
47036448Svikram 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
47046448Svikram 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
47056448Svikram 
47066448Svikram 	ret = exec_cmd(cmd, NULL);
47076448Svikram 	INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
47086448Svikram 	if (ret != 0) {
47096448Svikram 		bam_error(GRUBSIGN_SORT_FAILED);
47106448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
47116448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
47126448Svikram 		return (-1);
47136448Svikram 	}
47146448Svikram 
47156448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
47166448Svikram 
47176448Svikram 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
47186448Svikram 	error = errno;
47196448Svikram 	INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
47206448Svikram 	if (ret == -1) {
47216448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
47226448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
47236448Svikram 		return (-1);
47246448Svikram 	}
47256448Svikram 
47266448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
47276448Svikram 		BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
47286448Svikram 	}
47296448Svikram 
47306448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
47316448Svikram 	return (0);
47326448Svikram 
47336448Svikram fail:
47346448Svikram 	if (dirp)
47356448Svikram 		(void) closedir(dirp);
47366448Svikram 	free_mnttab(mnttab_hash);
47376448Svikram 	(void) rmdir(tmpmnt);
47386448Svikram 	(void) fclose(tfp);
47396448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
47406448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
47416448Svikram 	return (-1);
47426448Svikram }
47436448Svikram 
47446448Svikram static char *
47456448Svikram create_ufs_sign(void)
47466448Svikram {
47476448Svikram 	struct stat	sb;
47486448Svikram 	int		signnum = -1;
47496448Svikram 	char		tmpsign[MAXNAMELEN + 1];
47506448Svikram 	char		*numstr;
47516448Svikram 	int		i;
47526448Svikram 	FILE		*tfp;
47536448Svikram 	int		ret;
47546448Svikram 	int		error;
47556448Svikram 	const char	*fcn = "create_ufs_sign()";
47566448Svikram 
47576448Svikram 	bam_print(SEARCHING_UFS_SIGN);
47586448Svikram 
47596448Svikram 	ret = FindAllUfsSignatures();
47606448Svikram 	INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
47616448Svikram 	if (ret == -1) {
47626448Svikram 		bam_error(ERR_FIND_UFS_SIGN);
47636448Svikram 		return (NULL);
47646448Svikram 	}
47656448Svikram 
47666448Svikram 	/* Make sure the list exists and is owned by root */
47676448Svikram 	INJECT_ERROR1("SIGNLIST_NOT_CREATED",
47686448Svikram 	    (void) unlink(UFS_SIGNATURE_LIST));
47696448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
47706448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
47716448Svikram 		bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
47726448Svikram 		return (NULL);
47736448Svikram 	}
47746448Svikram 
47756448Svikram 	if (sb.st_size == 0) {
47766448Svikram 		bam_print(GRUBSIGN_UFS_NONE);
47776448Svikram 		i = 0;
47786448Svikram 		goto found;
47796448Svikram 	}
47806448Svikram 
47816448Svikram 	/* The signature list was sorted when it was created */
47826448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST, "r");
47836448Svikram 	error = errno;
47846448Svikram 	INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
47856448Svikram 	if (tfp == NULL) {
47866448Svikram 		bam_error(UFS_SIGNATURE_LIST_OPENERR,
47876448Svikram 		    UFS_SIGNATURE_LIST, strerror(error));
47886448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
47896448Svikram 		return (NULL);
47906448Svikram 	}
47916448Svikram 
47926448Svikram 	for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
47936448Svikram 
47946448Svikram 		if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
47956448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
47966448Svikram 			(void) fclose(tfp);
47976448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
47986448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
47996448Svikram 			return (NULL);
48006448Svikram 		}
48016448Svikram 		numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
48026448Svikram 
48036448Svikram 		if (numstr[0] == '\0' || !isdigit(numstr[0])) {
48046448Svikram 			(void) fclose(tfp);
48056448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
48066448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
48076448Svikram 			return (NULL);
48086448Svikram 		}
48096448Svikram 
48106448Svikram 		signnum = atoi(numstr);
48116448Svikram 		INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
48126448Svikram 		if (signnum < 0) {
48136448Svikram 			(void) fclose(tfp);
48146448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
48156448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
48166448Svikram 			return (NULL);
48176448Svikram 		}
48186448Svikram 
48196448Svikram 		if (i != signnum) {
48206448Svikram 			BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
48216448Svikram 			break;
48226448Svikram 		}
48236448Svikram 	}
48246448Svikram 
48256448Svikram 	(void) fclose(tfp);
48266448Svikram 
48276448Svikram found:
48286448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
48296448Svikram 
48306448Svikram 	/* add the ufs signature to the /var/run list of signatures */
48316448Svikram 	ret = ufs_add_to_sign_list(tmpsign);
48326448Svikram 	INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
48336448Svikram 	if (ret == -1) {
48346448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
48356448Svikram 		bam_error(FAILED_ADD_SIGNLIST, tmpsign);
48366448Svikram 		return (NULL);
48376448Svikram 	}
48386448Svikram 
48396448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
48406448Svikram 
48416448Svikram 	return (s_strdup(tmpsign));
48426448Svikram }
48436448Svikram 
48446448Svikram static char *
48456448Svikram get_fstype(char *osroot)
48466448Svikram {
48476448Svikram 	FILE		*mntfp;
48486448Svikram 	struct mnttab	mp = {0};
48496448Svikram 	struct mnttab	mpref = {0};
48506448Svikram 	int		error;
48516448Svikram 	int		ret;
48526448Svikram 	const char	*fcn = "get_fstype()";
48536448Svikram 
48546448Svikram 	INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
48556448Svikram 	if (osroot == NULL) {
48566448Svikram 		bam_error(GET_FSTYPE_ARGS);
48576448Svikram 		return (NULL);
48586448Svikram 	}
48596448Svikram 
48606448Svikram 	mntfp = fopen(MNTTAB, "r");
48616448Svikram 	error = errno;
48626448Svikram 	INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
48636448Svikram 	if (mntfp == NULL) {
48646448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
48656448Svikram 		return (NULL);
48666448Svikram 	}
48676448Svikram 
48686448Svikram 	if (*osroot == '\0')
48696448Svikram 		mpref.mnt_mountp = "/";
48706448Svikram 	else
48716448Svikram 		mpref.mnt_mountp = osroot;
48726448Svikram 
48736448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
48746448Svikram 	INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
48756448Svikram 	if (ret != 0) {
48766448Svikram 		bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
48776448Svikram 		(void) fclose(mntfp);
48786448Svikram 		return (NULL);
48796448Svikram 	}
48806448Svikram 	(void) fclose(mntfp);
48816448Svikram 
48826448Svikram 	INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
48836448Svikram 	if (mp.mnt_fstype == NULL) {
48846448Svikram 		bam_error(MNTTAB_FSTYPE_NULL, osroot);
48856448Svikram 		return (NULL);
48866448Svikram 	}
48876448Svikram 
48886448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
48896448Svikram 
48906448Svikram 	return (s_strdup(mp.mnt_fstype));
48916448Svikram }
48926448Svikram 
48936448Svikram static char *
48946448Svikram create_zfs_sign(char *osdev)
48956448Svikram {
48966448Svikram 	char		tmpsign[PATH_MAX];
48976448Svikram 	char		*pool;
48986448Svikram 	const char	*fcn = "create_zfs_sign()";
48996448Svikram 
49006448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
49016448Svikram 
49026448Svikram 	/*
49036448Svikram 	 * First find the pool name
49046448Svikram 	 */
49056448Svikram 	pool = get_pool(osdev);
49066448Svikram 	INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
49076448Svikram 	if (pool == NULL) {
49086448Svikram 		bam_error(GET_POOL_FAILED, osdev);
49096448Svikram 		return (NULL);
49106448Svikram 	}
49116448Svikram 
49126448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
49136448Svikram 
49146448Svikram 	BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
49156448Svikram 
49166448Svikram 	free(pool);
49176448Svikram 
49186448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
49196448Svikram 
49206448Svikram 	return (s_strdup(tmpsign));
49216448Svikram }
49226448Svikram 
49236448Svikram static char *
49246448Svikram create_new_sign(char *osdev, char *fstype)
49256448Svikram {
49266448Svikram 	char		*sign;
49276448Svikram 	const char	*fcn = "create_new_sign()";
49286448Svikram 
49296448Svikram 	INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
49306448Svikram 
49316448Svikram 	if (strcmp(fstype, "zfs") == 0) {
49326448Svikram 		BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
49336448Svikram 		sign = create_zfs_sign(osdev);
49346448Svikram 	} else if (strcmp(fstype, "ufs") == 0) {
49356448Svikram 		BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
49366448Svikram 		sign = create_ufs_sign();
49376448Svikram 	} else {
49386448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
49396448Svikram 		sign = NULL;
49406448Svikram 	}
49416448Svikram 
49426448Svikram 	BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
49436448Svikram 	return (sign);
49446448Svikram }
49456448Svikram 
49466448Svikram static int
49476448Svikram set_backup_common(char *mntpt, char *sign)
49486448Svikram {
49496448Svikram 	FILE		*bfp;
49506448Svikram 	char		backup[PATH_MAX];
49516448Svikram 	char		tmpsign[PATH_MAX];
49526448Svikram 	int		error;
49536448Svikram 	char		*bdir;
49546448Svikram 	char		*backup_dup;
49556448Svikram 	struct stat	sb;
49566448Svikram 	int		ret;
49576448Svikram 	const char	*fcn = "set_backup_common()";
49586448Svikram 
49596448Svikram 	(void) snprintf(backup, sizeof (backup), "%s%s",
49606448Svikram 	    mntpt, GRUBSIGN_BACKUP);
49616448Svikram 
49626448Svikram 	/* First read the backup */
49636448Svikram 	bfp = fopen(backup, "r");
49646448Svikram 	if (bfp != NULL) {
49656448Svikram 		while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
49666448Svikram 			if (strcmp(tmpsign, sign) == 0) {
49676448Svikram 				BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
49686448Svikram 				(void) fclose(bfp);
49696448Svikram 				return (0);
49706448Svikram 			}
49716448Svikram 		}
49726448Svikram 		(void) fclose(bfp);
49736448Svikram 		BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
49746448Svikram 	} else {
49756448Svikram 		BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
49766448Svikram 	}
49776448Svikram 
49786448Svikram 	/*
49796448Svikram 	 * Didn't find the correct signature. First create
49806448Svikram 	 * the directory if necessary.
49816448Svikram 	 */
49826448Svikram 
49836448Svikram 	/* dirname() modifies its argument so dup it */
49846448Svikram 	backup_dup = s_strdup(backup);
49856448Svikram 	bdir = dirname(backup_dup);
49866448Svikram 	assert(bdir);
49876448Svikram 
49886448Svikram 	ret = stat(bdir, &sb);
49896448Svikram 	INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
49906448Svikram 	if (ret == -1) {
49916448Svikram 		BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
49926448Svikram 		ret = mkdirp(bdir, 0755);
49936448Svikram 		error = errno;
49946448Svikram 		INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
49956448Svikram 		if (ret == -1) {
49966448Svikram 			bam_error(GRUBSIGN_BACKUP_MKDIRERR,
49976448Svikram 			    GRUBSIGN_BACKUP, strerror(error));
49986448Svikram 			free(backup_dup);
49996448Svikram 			return (-1);
50006448Svikram 		}
50016448Svikram 	}
50026448Svikram 	free(backup_dup);
50036448Svikram 
50046448Svikram 	/*
50056448Svikram 	 * Open the backup in append mode to add the correct
50066448Svikram 	 * signature;
50076448Svikram 	 */
50086448Svikram 	bfp = fopen(backup, "a");
50096448Svikram 	error = errno;
50106448Svikram 	INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
50116448Svikram 	if (bfp == NULL) {
50126448Svikram 		bam_error(GRUBSIGN_BACKUP_OPENERR,
50136448Svikram 		    GRUBSIGN_BACKUP, strerror(error));
50146448Svikram 		return (-1);
50156448Svikram 	}
50166448Svikram 
50176448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
50186448Svikram 
50196448Svikram 	ret = fputs(tmpsign, bfp);
50206448Svikram 	error = errno;
50216448Svikram 	INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
50226448Svikram 	if (ret != strlen(tmpsign)) {
50236448Svikram 		bam_error(GRUBSIGN_BACKUP_WRITEERR,
50246448Svikram 		    GRUBSIGN_BACKUP, strerror(error));
50256448Svikram 		(void) fclose(bfp);
50266448Svikram 		return (-1);
50276448Svikram 	}
50286448Svikram 
50296448Svikram 	(void) fclose(bfp);
50306448Svikram 
50316448Svikram 	if (bam_verbose)
50326448Svikram 		bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
50336448Svikram 
50346448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
50356448Svikram 
50366448Svikram 	return (0);
50376448Svikram }
50386448Svikram 
50396448Svikram static int
50406448Svikram set_backup_ufs(char *osroot, char *sign)
50416448Svikram {
50426448Svikram 	const char	*fcn = "set_backup_ufs()";
50436448Svikram 
50446448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
50456448Svikram 	return (set_backup_common(osroot, sign));
50466448Svikram }
50476448Svikram 
50486448Svikram static int
50496448Svikram set_backup_zfs(char *osdev, char *sign)
50506448Svikram {
50516448Svikram 	char		*pool;
50526448Svikram 	char		*mntpt;
50536448Svikram 	zfs_mnted_t	mnted;
50546448Svikram 	int		ret;
50556448Svikram 	const char	*fcn = "set_backup_zfs()";
50566448Svikram 
50576448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
50586448Svikram 
50596448Svikram 	pool = get_pool(osdev);
50606448Svikram 	INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
50616448Svikram 	if (pool == NULL) {
50626448Svikram 		bam_error(GET_POOL_FAILED, osdev);
50636448Svikram 		return (-1);
50646448Svikram 	}
50656448Svikram 
50666448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
50676448Svikram 	INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
50686448Svikram 	if (mntpt == NULL) {
50696448Svikram 		bam_error(FAIL_MNT_TOP_DATASET, pool);
50706448Svikram 		free(pool);
50716448Svikram 		return (-1);
50726448Svikram 	}
50736448Svikram 
50746448Svikram 	ret = set_backup_common(mntpt, sign);
50756448Svikram 
50766448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
50776448Svikram 
50786448Svikram 	free(pool);
50796448Svikram 
50806448Svikram 	INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
50816448Svikram 	if (ret == 0) {
50826448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
50836448Svikram 	} else {
50846448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
50856448Svikram 	}
50866448Svikram 
50876448Svikram 	return (ret);
50886448Svikram }
50896448Svikram 
50906448Svikram static int
50916448Svikram set_backup(char *osroot, char *osdev, char *sign, char *fstype)
50926448Svikram {
50936448Svikram 	const char	*fcn = "set_backup()";
50946448Svikram 	int		ret;
50956448Svikram 
50966448Svikram 	INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
50976448Svikram 
50986448Svikram 	if (strcmp(fstype, "ufs") == 0) {
50996448Svikram 		BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
51006448Svikram 		ret = set_backup_ufs(osroot, sign);
51016448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
51026448Svikram 		BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
51036448Svikram 		ret = set_backup_zfs(osdev, sign);
51046448Svikram 	} else {
51056448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
51066448Svikram 		ret = -1;
51076448Svikram 	}
51086448Svikram 
51096448Svikram 	if (ret == 0) {
51106448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
51116448Svikram 	} else {
51126448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
51136448Svikram 	}
51146448Svikram 
51156448Svikram 	return (ret);
51166448Svikram }
51176448Svikram 
51186448Svikram static int
51196448Svikram set_primary_common(char *mntpt, char *sign)
51206448Svikram {
51216448Svikram 	char		signfile[PATH_MAX];
51226448Svikram 	char		signdir[PATH_MAX];
51236448Svikram 	struct stat	sb;
51246448Svikram 	int		fd;
51256448Svikram 	int		error;
51266448Svikram 	int		ret;
51276448Svikram 	const char	*fcn = "set_primary_common()";
51286448Svikram 
51296448Svikram 	(void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
51306448Svikram 	    mntpt, GRUBSIGN_DIR, sign);
51316448Svikram 
51326448Svikram 	if (stat(signfile, &sb) != -1) {
51336448Svikram 		if (bam_verbose)
51346448Svikram 			bam_print(PRIMARY_SIGN_EXISTS, sign);
51356448Svikram 		return (0);
51366448Svikram 	} else {
51376448Svikram 		BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
51386448Svikram 	}
51396448Svikram 
51406448Svikram 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
51416448Svikram 	    mntpt, GRUBSIGN_DIR);
51426448Svikram 
51436448Svikram 	if (stat(signdir, &sb) == -1) {
51446448Svikram 		BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
51456448Svikram 		ret = mkdirp(signdir, 0755);
51466448Svikram 		error = errno;
51476448Svikram 		INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
51486448Svikram 		if (ret == -1) {
51496448Svikram 			bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
51506448Svikram 			return (-1);
51516448Svikram 		}
51526448Svikram 	}
51536448Svikram 
51546448Svikram 	fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
51556448Svikram 	error = errno;
51566448Svikram 	INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
51576448Svikram 	if (fd == -1) {
51586448Svikram 		bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
51596448Svikram 		return (-1);
51606448Svikram 	}
51616448Svikram 
51626448Svikram 	ret = fsync(fd);
51636448Svikram 	error = errno;
51646448Svikram 	INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
51656448Svikram 	if (ret != 0) {
51666448Svikram 		bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
51676448Svikram 	}
51686448Svikram 
51696448Svikram 	(void) close(fd);
51706448Svikram 
51716448Svikram 	if (bam_verbose)
51726448Svikram 		bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
51736448Svikram 
51746448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
51756448Svikram 
51766448Svikram 	return (0);
51776448Svikram }
51786448Svikram 
51796448Svikram static int
51806448Svikram set_primary_ufs(char *osroot, char *sign)
51816448Svikram {
51826448Svikram 	const char	*fcn = "set_primary_ufs()";
51836448Svikram 
51846448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
51856448Svikram 	return (set_primary_common(osroot, sign));
51866448Svikram }
51876448Svikram 
51886448Svikram static int
51896448Svikram set_primary_zfs(char *osdev, char *sign)
51906448Svikram {
51916448Svikram 	char		*pool;
51926448Svikram 	char		*mntpt;
51936448Svikram 	zfs_mnted_t	mnted;
51946448Svikram 	int		ret;
51956448Svikram 	const char	*fcn = "set_primary_zfs()";
51966448Svikram 
51976448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
51986448Svikram 
51996448Svikram 	pool = get_pool(osdev);
52006448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
52016448Svikram 	if (pool == NULL) {
52026448Svikram 		bam_error(GET_POOL_FAILED, osdev);
52036448Svikram 		return (-1);
52046448Svikram 	}
52056448Svikram 
52066448Svikram 	/* Pool name must exist in the sign */
52076448Svikram 	ret = (strstr(sign, pool) != NULL);
52086448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
52096448Svikram 	if (ret == 0) {
52106448Svikram 		bam_error(POOL_SIGN_INCOMPAT, pool, sign);
52116448Svikram 		free(pool);
52126448Svikram 		return (-1);
52136448Svikram 	}
52146448Svikram 
52156448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
52166448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
52176448Svikram 	if (mntpt == NULL) {
52186448Svikram 		bam_error(FAIL_MNT_TOP_DATASET, pool);
52196448Svikram 		free(pool);
52206448Svikram 		return (-1);
52216448Svikram 	}
52226448Svikram 
52236448Svikram 	ret = set_primary_common(mntpt, sign);
52246448Svikram 
52256448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
52266448Svikram 
52276448Svikram 	free(pool);
52286448Svikram 
52296448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
52306448Svikram 	if (ret == 0) {
52316448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
52326448Svikram 	} else {
52336448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
52346448Svikram 	}
52356448Svikram 
52366448Svikram 	return (ret);
52376448Svikram }
52386448Svikram 
52396448Svikram static int
52406448Svikram set_primary(char *osroot, char *osdev, char *sign, char *fstype)
52416448Svikram {
52426448Svikram 	const char	*fcn = "set_primary()";
52436448Svikram 	int		ret;
52446448Svikram 
52456448Svikram 	INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
52466448Svikram 	if (strcmp(fstype, "ufs") == 0) {
52476448Svikram 		BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
52486448Svikram 		ret = set_primary_ufs(osroot, sign);
52496448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
52506448Svikram 		BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
52516448Svikram 		ret = set_primary_zfs(osdev, sign);
52526448Svikram 	} else {
52536448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
52546448Svikram 		ret = -1;
52556448Svikram 	}
52566448Svikram 
52576448Svikram 	if (ret == 0) {
52586448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
52596448Svikram 	} else {
52606448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
52616448Svikram 	}
52626448Svikram 
52636448Svikram 	return (ret);
52646448Svikram }
52656448Svikram 
52666448Svikram static int
52676448Svikram ufs_add_to_sign_list(char *sign)
52686448Svikram {
52696448Svikram 	FILE		*tfp;
52706448Svikram 	char		signline[MAXNAMELEN];
52716448Svikram 	char		cmd[PATH_MAX];
52726448Svikram 	int		ret;
52736448Svikram 	int		error;
52746448Svikram 	const char	*fcn = "ufs_add_to_sign_list()";
52756448Svikram 
52766448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
52776448Svikram 	if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
52786448Svikram 	    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
52796448Svikram 		bam_error(INVALID_UFS_SIGN, sign);
52806448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
52816448Svikram 		return (-1);
52826448Svikram 	}
52836448Svikram 
52846448Svikram 	/*
52856448Svikram 	 * most failures in this routine are not a fatal error
52866448Svikram 	 * We simply unlink the /var/run file and continue
52876448Svikram 	 */
52886448Svikram 
52896448Svikram 	ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
52906448Svikram 	error = errno;
52916448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
52926448Svikram 	if (ret == -1) {
52936448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
52946448Svikram 		    strerror(error));
52956448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
52966448Svikram 		return (0);
52976448Svikram 	}
52986448Svikram 
52996448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
53006448Svikram 	error = errno;
53016448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
53026448Svikram 	if (tfp == NULL) {
53036448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
53046448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
53056448Svikram 		return (0);
53066448Svikram 	}
53076448Svikram 
53086448Svikram 	(void) snprintf(signline, sizeof (signline), "%s\n", sign);
53096448Svikram 
53106448Svikram 	ret = fputs(signline, tfp);
53116448Svikram 	error = errno;
53126448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
53136448Svikram 	if (ret != strlen(signline)) {
53146448Svikram 		bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
53156448Svikram 		(void) fclose(tfp);
53166448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
53176448Svikram 		return (0);
53186448Svikram 	}
53196448Svikram 
53206448Svikram 	ret = fclose(tfp);
53216448Svikram 	error = errno;
53226448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
53236448Svikram 	if (ret == EOF) {
53246448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
53256448Svikram 		    strerror(error));
53266448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
53276448Svikram 		return (0);
53286448Svikram 	}
53296448Svikram 
53306448Svikram 	/* Sort the list again */
53316448Svikram 	(void) snprintf(cmd, sizeof (cmd),
53326448Svikram 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
53336448Svikram 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
53346448Svikram 
53356448Svikram 	ret = exec_cmd(cmd, NULL);
53366448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
53376448Svikram 	if (ret != 0) {
53386448Svikram 		bam_error(GRUBSIGN_SORT_FAILED);
53396448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
53406448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
53416448Svikram 		return (0);
53426448Svikram 	}
53436448Svikram 
53446448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
53456448Svikram 
53466448Svikram 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
53476448Svikram 	error = errno;
53486448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
53496448Svikram 	if (ret == -1) {
53506448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
53516448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
53526448Svikram 		return (0);
53536448Svikram 	}
53546448Svikram 
53556448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
53566448Svikram 
53576448Svikram 	return (0);
53586448Svikram }
53596448Svikram 
53606448Svikram static int
53616448Svikram set_signature(char *osroot, char *osdev, char *sign, char *fstype)
53626448Svikram {
53636448Svikram 	int		ret;
53646448Svikram 	const char	*fcn = "set_signature()";
53656448Svikram 
53666448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
53676448Svikram 
53686448Svikram 	ret = set_backup(osroot, osdev, sign, fstype);
53696448Svikram 	INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
53706448Svikram 	if (ret == -1) {
53716448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
53726448Svikram 		bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
53736448Svikram 		return (-1);
53746448Svikram 	}
53756448Svikram 
53766448Svikram 	ret = set_primary(osroot, osdev, sign, fstype);
53776448Svikram 	INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
53786448Svikram 
53796448Svikram 	if (ret == 0) {
53806448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
53816448Svikram 	} else {
53826448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
53836448Svikram 		bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
53846448Svikram 
53856448Svikram 	}
53866448Svikram 	return (ret);
53876448Svikram }
53886448Svikram 
53896448Svikram char *
53906448Svikram get_grubsign(char *osroot, char *osdev)
53916448Svikram {
53926448Svikram 	char		*grubsign;	/* (<sign>,#,#) */
53936448Svikram 	char		*slice;
53946448Svikram 	int		fdiskpart;
53956448Svikram 	char		*sign;
53966448Svikram 	char		*fstype;
53976448Svikram 	int		ret;
53986448Svikram 	const char	*fcn = "get_grubsign()";
53996448Svikram 
54006448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
54016448Svikram 
54026448Svikram 	fstype = get_fstype(osroot);
54036448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
54046448Svikram 	if (fstype == NULL) {
54056448Svikram 		bam_error(GET_FSTYPE_FAILED, osroot);
54066448Svikram 		return (NULL);
54076448Svikram 	}
54086448Svikram 
54096448Svikram 	sign = find_existing_sign(osroot, osdev, fstype);
54106448Svikram 	INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
54116448Svikram 	if (sign == NULL) {
54126448Svikram 		BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
54136448Svikram 		sign = create_new_sign(osdev, fstype);
54146448Svikram 		INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
54156448Svikram 		if (sign == NULL) {
54166448Svikram 			bam_error(GRUBSIGN_CREATE_FAIL, osdev);
54176448Svikram 			free(fstype);
54186448Svikram 			return (NULL);
54196448Svikram 		}
54206448Svikram 	}
54216448Svikram 
54226448Svikram 	ret = set_signature(osroot, osdev, sign, fstype);
54236448Svikram 	INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
54246448Svikram 	if (ret == -1) {
54256448Svikram 		bam_error(GRUBSIGN_WRITE_FAIL, osdev);
54266448Svikram 		free(sign);
54276448Svikram 		free(fstype);
54286448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
54296448Svikram 		return (NULL);
54306448Svikram 	}
54316448Svikram 
54326448Svikram 	free(fstype);
54336448Svikram 
54346448Svikram 	if (bam_verbose)
54356448Svikram 		bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
54366448Svikram 
54376448Svikram 	fdiskpart = get_partition(osdev);
54386448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1);
54396448Svikram 	if (fdiskpart == -1) {
54406448Svikram 		bam_error(FDISKPART_FAIL, osdev);
54416448Svikram 		free(sign);
54426448Svikram 		return (NULL);
54436448Svikram 	}
54446448Svikram 
54456448Svikram 	slice = strrchr(osdev, 's');
54466448Svikram 
54476448Svikram 	grubsign = s_calloc(1, MAXNAMELEN + 10);
54486448Svikram 	if (slice) {
54496448Svikram 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
54506448Svikram 		    sign, fdiskpart, slice[1] + 'a' - '0');
54516448Svikram 	} else
54526448Svikram 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
54536448Svikram 		    sign, fdiskpart);
54546448Svikram 
54556448Svikram 	free(sign);
54566448Svikram 
54576448Svikram 	BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
54586448Svikram 
54596448Svikram 	return (grubsign);
54600Sstevel@tonic-gate }
54610Sstevel@tonic-gate 
5462656Sszhou static char *
5463656Sszhou get_title(char *rootdir)
54640Sstevel@tonic-gate {
54656448Svikram 	static char	title[80];
54666448Svikram 	char		*cp = NULL;
54676448Svikram 	char		release[PATH_MAX];
54686448Svikram 	FILE		*fp;
54696448Svikram 	const char	*fcn = "get_title()";
54700Sstevel@tonic-gate 
54710Sstevel@tonic-gate 	/* open the /etc/release file */
54720Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
54730Sstevel@tonic-gate 
54740Sstevel@tonic-gate 	fp = fopen(release, "r");
54756448Svikram 	if (fp == NULL) {
54766448Svikram 		bam_error(OPEN_FAIL, release, strerror(errno));
54776448Svikram 		cp = NULL;
54786448Svikram 		goto out;
54796448Svikram 	}
54800Sstevel@tonic-gate 
54810Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
54820Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
54830Sstevel@tonic-gate 		if (cp)
54840Sstevel@tonic-gate 			break;
54850Sstevel@tonic-gate 	}
54860Sstevel@tonic-gate 	(void) fclose(fp);
54876448Svikram 
54886448Svikram out:
54896448Svikram 	cp = cp ? cp : "Solaris";
54906448Svikram 
54916448Svikram 	BAM_DPRINTF((D_GET_TITLE, fcn, cp));
54926448Svikram 
54936448Svikram 	return (cp);
54940Sstevel@tonic-gate }
54950Sstevel@tonic-gate 
54963446Smrj char *
54970Sstevel@tonic-gate get_special(char *mountp)
54980Sstevel@tonic-gate {
54996448Svikram 	FILE		*mntfp;
55006448Svikram 	struct mnttab	mp = {0};
55016448Svikram 	struct mnttab	mpref = {0};
55026448Svikram 	int		error;
55036448Svikram 	int		ret;
55046448Svikram 	const char 	*fcn = "get_special()";
55056448Svikram 
55066448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
55076448Svikram 	if (mountp == NULL) {
55086448Svikram 		bam_error(GET_SPECIAL_NULL_MNTPT);
55096448Svikram 		return (NULL);
55106448Svikram 	}
55110Sstevel@tonic-gate 
55120Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
55136448Svikram 	error = errno;
55146448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
55150Sstevel@tonic-gate 	if (mntfp == NULL) {
55166448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
55176448Svikram 		return (NULL);
55180Sstevel@tonic-gate 	}
55190Sstevel@tonic-gate 
55200Sstevel@tonic-gate 	if (*mountp == '\0')
55210Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
55220Sstevel@tonic-gate 	else
55230Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
55246448Svikram 
55256448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
55266448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
55276448Svikram 	if (ret != 0) {
55280Sstevel@tonic-gate 		(void) fclose(mntfp);
55296448Svikram 		BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
55300Sstevel@tonic-gate 		return (NULL);
55310Sstevel@tonic-gate 	}
55320Sstevel@tonic-gate 	(void) fclose(mntfp);
55330Sstevel@tonic-gate 
55346448Svikram 	BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
55356448Svikram 
55360Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
55370Sstevel@tonic-gate }
55380Sstevel@tonic-gate 
55396448Svikram static void
55406448Svikram free_physarray(char **physarray, int n)
55416448Svikram {
55426448Svikram 	int			i;
55436448Svikram 	const char		*fcn = "free_physarray()";
55446448Svikram 
55456448Svikram 	assert(physarray);
55466448Svikram 	assert(n);
55476448Svikram 
55486448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
55496448Svikram 
55506448Svikram 	for (i = 0; i < n; i++) {
55516448Svikram 		free(physarray[i]);
55526448Svikram 	}
55536448Svikram 	free(physarray);
55546448Svikram 
55556448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
55566448Svikram }
55576448Svikram 
55586448Svikram static int
55596448Svikram zfs_get_physical(char *special, char ***physarray, int *n)
55606448Svikram {
55616448Svikram 	char			sdup[PATH_MAX];
55626448Svikram 	char			cmd[PATH_MAX];
55636448Svikram 	char			dsk[PATH_MAX];
55646448Svikram 	char			*pool;
55656448Svikram 	filelist_t		flist = {0};
55666448Svikram 	line_t			*lp;
55676448Svikram 	line_t			*startlp;
55686448Svikram 	char			*comp1;
55696448Svikram 	int			i;
55706448Svikram 	int			ret;
55716448Svikram 	const char		*fcn = "zfs_get_physical()";
55726448Svikram 
55736448Svikram 	assert(special);
55746448Svikram 
55756448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
55766448Svikram 
55776448Svikram 	INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
55786448Svikram 	if (special[0] == '/') {
55796448Svikram 		bam_error(INVALID_ZFS_SPECIAL, special);
55806448Svikram 		return (-1);
55816448Svikram 	}
55826448Svikram 
55836448Svikram 	(void) strlcpy(sdup, special, sizeof (sdup));
55846448Svikram 
55856448Svikram 	pool = strtok(sdup, "/");
55866448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
55876448Svikram 	if (pool == NULL) {
55886448Svikram 		bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
55896448Svikram 		return (-1);
55906448Svikram 	}
55916448Svikram 
55926448Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
55936448Svikram 
55946448Svikram 	ret = exec_cmd(cmd, &flist);
55956448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
55966448Svikram 	if (ret != 0) {
55976448Svikram 		bam_error(ZFS_GET_POOL_STATUS, pool);
55986448Svikram 		return (-1);
55996448Svikram 	}
56006448Svikram 
56016448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
56026448Svikram 	if (flist.head == NULL) {
56036448Svikram 		bam_error(BAD_ZPOOL_STATUS, pool);
56046448Svikram 		filelist_free(&flist);
56056448Svikram 		return (-1);
56066448Svikram 	}
56076448Svikram 
56086448Svikram 	for (lp = flist.head; lp; lp = lp->next) {
56096448Svikram 		BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
56106448Svikram 		comp1 = strtok(lp->line, " \t");
56116448Svikram 		if (comp1 == NULL) {
56126448Svikram 			free(lp->line);
56136448Svikram 			lp->line = NULL;
56146448Svikram 		} else {
56156448Svikram 			comp1 = s_strdup(comp1);
56166448Svikram 			free(lp->line);
56176448Svikram 			lp->line = comp1;
56186448Svikram 		}
56196448Svikram 	}
56206448Svikram 
56216448Svikram 	for (lp = flist.head; lp; lp = lp->next) {
56226448Svikram 		if (lp->line == NULL)
56236448Svikram 			continue;
56246448Svikram 		if (strcmp(lp->line, pool) == 0) {
56256448Svikram 			BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
56266448Svikram 			break;
56276448Svikram 		}
56286448Svikram 	}
56296448Svikram 
56306448Svikram 	if (lp == NULL) {
56316448Svikram 		bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
56326448Svikram 		filelist_free(&flist);
56336448Svikram 		return (-1);
56346448Svikram 	}
56356448Svikram 
56366448Svikram 	startlp = lp->next;
56376448Svikram 	for (i = 0, lp = startlp; lp; lp = lp->next) {
56386448Svikram 		if (lp->line == NULL)
56396448Svikram 			continue;
56406448Svikram 		if (strcmp(lp->line, "mirror") == 0)
56416448Svikram 			continue;
56426448Svikram 		if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
56436448Svikram 			break;
56446448Svikram 		i++;
56456448Svikram 		BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
56466448Svikram 	}
56476448Svikram 
56486448Svikram 	if (i == 0) {
56496448Svikram 		bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
56506448Svikram 		filelist_free(&flist);
56516448Svikram 		return (-1);
56526448Svikram 	}
56536448Svikram 
56546448Svikram 	*n = i;
56556448Svikram 	*physarray = s_calloc(*n, sizeof (char *));
56566448Svikram 	for (i = 0, lp = startlp; lp; lp = lp->next) {
56576448Svikram 		if (lp->line == NULL)
56586448Svikram 			continue;
56596448Svikram 		if (strcmp(lp->line, "mirror") == 0)
56606448Svikram 			continue;
56616448Svikram 		if (strcmp(lp->line, "errors:") == 0)
56626448Svikram 			break;
56636448Svikram 		if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
56646448Svikram 		    strncmp(lp->line, "/dev/rdsk/",
56656448Svikram 		    strlen("/dev/rdsk/")) != 0)  {
56666448Svikram 			(void) snprintf(dsk, sizeof (dsk), "/dev/dsk/%s",
56676448Svikram 			    lp->line);
56686448Svikram 		} else {
56696448Svikram 			(void) strlcpy(dsk, lp->line, sizeof (dsk));
56706448Svikram 		}
56716448Svikram 		BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
56726448Svikram 		(*physarray)[i++] = s_strdup(dsk);
56736448Svikram 	}
56746448Svikram 
56756448Svikram 	assert(i == *n);
56766448Svikram 
56776448Svikram 	filelist_free(&flist);
56786448Svikram 
56796448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
56806448Svikram 	return (0);
56816448Svikram }
56826448Svikram 
56836694Svikram /*
56846694Svikram  * Certain services needed to run metastat successfully may not
56856694Svikram  * be enabled. Enable them now.
56866694Svikram  */
56876694Svikram /*
56886694Svikram  * Checks if the specified service is online
56896694Svikram  * Returns: 	1 if the service is online
56906694Svikram  *		0 if the service is not online
56916694Svikram  *		-1 on error
56926694Svikram  */
56936694Svikram static int
56946694Svikram is_svc_online(char *svc)
56956694Svikram {
56966694Svikram 	char			*state;
56976694Svikram 	const char		*fcn = "is_svc_online()";
56986694Svikram 
56996694Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
57006694Svikram 
57016694Svikram 	state = smf_get_state(svc);
57026694Svikram 	INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
57036694Svikram 	if (state == NULL) {
57046694Svikram 		bam_error(GET_SVC_STATE_ERR, svc);
57056694Svikram 		return (-1);
57066694Svikram 	}
57076694Svikram 	BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
57086694Svikram 
57096694Svikram 	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
57106694Svikram 		BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
57116694Svikram 		free(state);
57126694Svikram 		return (1);
57136694Svikram 	}
57146694Svikram 
57156694Svikram 	BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
57166694Svikram 
57176694Svikram 	free(state);
57186694Svikram 
57196694Svikram 	return (0);
57206694Svikram }
57216694Svikram 
57226694Svikram static int
57236694Svikram enable_svc(char *svc)
57246694Svikram {
57256694Svikram 	int			ret;
57266694Svikram 	int			sleeptime;
57276694Svikram 	const char		*fcn = "enable_svc()";
57286694Svikram 
57296694Svikram 	ret = is_svc_online(svc);
57306694Svikram 	if (ret == -1) {
57316694Svikram 		bam_error(SVC_IS_ONLINE_FAILED, svc);
57326694Svikram 		return (-1);
57336694Svikram 	} else if (ret == 1) {
57346694Svikram 		BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
57356694Svikram 		return (0);
57366694Svikram 	}
57376694Svikram 
57386694Svikram 	/* Service is not enabled. Enable it now. */
57396694Svikram 	ret = smf_enable_instance(svc, 0);
57406694Svikram 	INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
57416694Svikram 	if (ret != 0) {
57426694Svikram 		bam_error(ENABLE_SVC_FAILED, svc);
57436694Svikram 		return (-1);
57446694Svikram 	}
57456694Svikram 
57466694Svikram 	BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
57476694Svikram 
57486694Svikram 	sleeptime = 0;
57496694Svikram 	do {
57506694Svikram 		ret = is_svc_online(svc);
57516694Svikram 		INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
57526694Svikram 		INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
57536694Svikram 		INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
57546694Svikram 		if (ret == -1) {
57556694Svikram 			bam_error(ERR_SVC_GET_ONLINE, svc);
57566694Svikram 			return (-1);
57576694Svikram 		} else if (ret == 1) {
57586694Svikram 			BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
57596694Svikram 			return (1);
57606694Svikram 		}
57616694Svikram 		(void) sleep(1);
57626694Svikram 	} while (sleeptime < 60);
57636694Svikram 
57646694Svikram 	bam_error(TIMEOUT_ENABLE_SVC, svc);
57656694Svikram 
57666694Svikram 	return (-1);
57676694Svikram }
57686694Svikram 
57696448Svikram static int
57706448Svikram ufs_get_physical(char *special, char ***physarray, int *n)
57716448Svikram {
57726448Svikram 	char			cmd[PATH_MAX];
57736448Svikram 	char			*shortname;
57746448Svikram 	filelist_t		flist = {0};
57756448Svikram 	char			*meta;
57766448Svikram 	char			*type;
57776448Svikram 	char			*comp1;
57786448Svikram 	char			*comp2;
57796448Svikram 	char			*comp3;
57806448Svikram 	char			*comp4;
57816448Svikram 	int			i;
57826448Svikram 	line_t			*lp;
57836448Svikram 	int			ret;
57846694Svikram 	char			*svc;
57856448Svikram 	const char		*fcn = "ufs_get_physical()";
57866448Svikram 
57876448Svikram 	assert(special);
57886448Svikram 
57896448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
57906448Svikram 
57916448Svikram 	if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
57926448Svikram 		bam_error(UFS_GET_PHYS_NOT_SVM, special);
57936448Svikram 		return (-1);
57946448Svikram 	}
57956448Svikram 
57966448Svikram 	if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
57976448Svikram 		shortname = special + strlen("/dev/md/dsk/");
57986448Svikram 	} else if (strncmp(special, "/dev/md/rdsk/",
57996448Svikram 	    strlen("/dev/md/rdsk/")) == 0) {
58006448Svikram 		shortname = special + strlen("/dev/md/rdsk");
58016448Svikram 	} else {
58026448Svikram 		bam_error(UFS_GET_PHYS_INVALID_SVM, special);
58036448Svikram 		return (-1);
58046448Svikram 	}
58056448Svikram 
58066448Svikram 	BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
58076448Svikram 
58086694Svikram 	svc = "network/rpc/meta:default";
58096694Svikram 	if (enable_svc(svc) == -1) {
58106694Svikram 		bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
58116694Svikram 	}
58126694Svikram 
58136448Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
58146448Svikram 
58156448Svikram 	ret = exec_cmd(cmd, &flist);
58166448Svikram 	INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
58176448Svikram 	if (ret != 0) {
58186448Svikram 		bam_error(UFS_SVM_METASTAT_ERR, shortname);
58196448Svikram 		return (-1);
58206448Svikram 	}
58216448Svikram 
58226448Svikram 	INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
58236448Svikram 	if (flist.head == NULL) {
58246448Svikram 		bam_error(BAD_UFS_SVM_METASTAT, shortname);
58256448Svikram 		filelist_free(&flist);
58266448Svikram 		return (-1);
58276448Svikram 	}
58286448Svikram 
58296448Svikram 	/*
58306448Svikram 	 * Check if not a mirror. We only parse a single metadevice
58316448Svikram 	 * if not a mirror
58326448Svikram 	 */
58336448Svikram 	meta = strtok(flist.head->line, " \t");
58346448Svikram 	type = strtok(NULL, " \t");
58356448Svikram 	if (meta == NULL || type == NULL) {
58366448Svikram 		bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
58376448Svikram 		filelist_free(&flist);
58386448Svikram 		return (-1);
58396448Svikram 	}
58406448Svikram 	if (strcmp(type, "-m") != 0) {
58416448Svikram 		comp1 = strtok(NULL, " \t");
58426448Svikram 		comp2 = strtok(NULL, " \t");
58436448Svikram 		if (comp1 == NULL || comp2 != NULL) {
58446448Svikram 			bam_error(INVALID_UFS_SVM_METASTAT, shortname);
58456448Svikram 			filelist_free(&flist);
58466448Svikram 			return (-1);
58476448Svikram 		}
58486448Svikram 		BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
58496448Svikram 		*physarray = s_calloc(1, sizeof (char *));
58506448Svikram 		(*physarray)[0] = s_strdup(comp1);
58516448Svikram 		*n = 1;
58526448Svikram 		filelist_free(&flist);
58536448Svikram 		return (0);
58546448Svikram 	}
58556448Svikram 
58566448Svikram 	/*
58576448Svikram 	 * Okay we have a mirror. Everything after the first line
58586448Svikram 	 * is a submirror
58596448Svikram 	 */
58606448Svikram 	for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
58616448Svikram 		if (strstr(lp->line, "/dev/dsk/") == NULL &&
58626448Svikram 		    strstr(lp->line, "/dev/rdsk/") == NULL) {
58636448Svikram 			bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
58646448Svikram 			filelist_free(&flist);
58656448Svikram 			return (-1);
58666448Svikram 		}
58676448Svikram 		i++;
58686448Svikram 	}
58696448Svikram 
58706448Svikram 	*physarray = s_calloc(i, sizeof (char *));
58716448Svikram 	*n = i;
58726448Svikram 
58736448Svikram 	for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
58746448Svikram 		comp1 = strtok(lp->line, " \t");
58756448Svikram 		comp2 = strtok(NULL, " \t");
58766448Svikram 		comp3 = strtok(NULL, " \t");
58776448Svikram 		comp4 = strtok(NULL, " \t");
58786448Svikram 
58796448Svikram 		if (comp3 == NULL || comp4 == NULL ||
58806448Svikram 		    (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
58816448Svikram 		    strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
58826448Svikram 			bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
58836448Svikram 			filelist_free(&flist);
58846448Svikram 			free_physarray(*physarray, *n);
58856448Svikram 			return (-1);
58866448Svikram 		}
58876448Svikram 
58886448Svikram 		(*physarray)[i++] = s_strdup(comp4);
58896448Svikram 	}
58906448Svikram 
58916448Svikram 	assert(i == *n);
58926448Svikram 
58936448Svikram 	filelist_free(&flist);
58946448Svikram 
58956448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
58966448Svikram 	return (0);
58976448Svikram }
58986448Svikram 
58996448Svikram static int
59006448Svikram get_physical(char *menu_root, char ***physarray, int *n)
59016448Svikram {
59026448Svikram 	char			*special;
59036448Svikram 	int			ret;
59046448Svikram 	const char		*fcn = "get_physical()";
59056448Svikram 
59066448Svikram 	assert(menu_root);
59076448Svikram 	assert(physarray);
59086448Svikram 	assert(n);
59096448Svikram 
59106448Svikram 	*physarray = NULL;
59116448Svikram 	*n = 0;
59126448Svikram 
59136448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
59146448Svikram 
59156448Svikram 	/* First get the device special file from /etc/mnttab */
59166448Svikram 	special = get_special(menu_root);
59176448Svikram 	INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
59186448Svikram 	if (special == NULL) {
59196448Svikram 		bam_error(GET_SPECIAL_NULL, menu_root);
59206448Svikram 		return (-1);
59216448Svikram 	}
59226448Svikram 
59236448Svikram 	/* If already a physical device nothing to do */
59246448Svikram 	if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
59256448Svikram 	    strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
59266448Svikram 		BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
59276448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
59286448Svikram 		*physarray = s_calloc(1, sizeof (char *));
59296448Svikram 		(*physarray)[0] = special;
59306448Svikram 		*n = 1;
59316448Svikram 		return (0);
59326448Svikram 	}
59336448Svikram 
59346448Svikram 	if (is_zfs(menu_root)) {
59356448Svikram 		ret = zfs_get_physical(special, physarray, n);
59366448Svikram 	} else if (is_ufs(menu_root)) {
59376448Svikram 		ret = ufs_get_physical(special, physarray, n);
59386448Svikram 	} else {
59396448Svikram 		bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
59406448Svikram 		ret = -1;
59416448Svikram 	}
59426448Svikram 
59436448Svikram 	free(special);
59446448Svikram 
59456448Svikram 	INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
59466448Svikram 	if (ret == -1) {
59476448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
59486448Svikram 	} else {
59496448Svikram 		int	i;
59506448Svikram 		assert (*n > 0);
59516448Svikram 		for (i = 0; i < *n; i++) {
59526448Svikram 			BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
59536448Svikram 		}
59546448Svikram 	}
59556448Svikram 
59566448Svikram 	return (ret);
59576448Svikram }
59586448Svikram 
59596448Svikram static int
59606448Svikram is_bootdisk(char *osroot, char *physical)
59616448Svikram {
59626448Svikram 	int			ret;
59636448Svikram 	char			*grubroot;
59646448Svikram 	char			*bootp;
59656448Svikram 	const char		*fcn = "is_bootdisk()";
59666448Svikram 
59676448Svikram 	assert(osroot);
59686448Svikram 	assert(physical);
59696448Svikram 
59706448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
59716448Svikram 
59726448Svikram 	bootp = strstr(physical, "p0:boot");
59736448Svikram 	if (bootp)
59746448Svikram 		*bootp = '\0';
59756448Svikram 	/*
59766448Svikram 	 * We just want the BIOS mapping for menu disk.
59776448Svikram 	 * Don't pass menu_root to get_grubroot() as the
59786448Svikram 	 * check that it is used for is not relevant here.
59796448Svikram 	 * The osroot is immaterial as well - it is only used to
59806448Svikram 	 * to find create_diskmap script. Everything hinges on
59816448Svikram 	 * "physical"
59826448Svikram 	 */
59836448Svikram 	grubroot = get_grubroot(osroot, physical, NULL);
59846448Svikram 
59856448Svikram 	INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
59866448Svikram 	if (grubroot == NULL) {
59876448Svikram 		bam_error(NO_GRUBROOT_FOR_DISK, fcn, physical);
59886448Svikram 		return (0);
59896448Svikram 	}
59906448Svikram 	ret = grubroot[3] == '0';
59916448Svikram 	free(grubroot);
59926448Svikram 
59936448Svikram 	BAM_DPRINTF((D_RETURN_RET, fcn, ret));
59946448Svikram 
59956448Svikram 	return (ret);
59960Sstevel@tonic-gate }
59970Sstevel@tonic-gate 
59980Sstevel@tonic-gate /*
59996448Svikram  * Check if menu is on the boot device
60000Sstevel@tonic-gate  * Return 0 (false) on error
60010Sstevel@tonic-gate  */
60020Sstevel@tonic-gate static int
60036448Svikram menu_on_bootdisk(char *osroot, char *menu_root)
60046448Svikram {
60056448Svikram 	char		**physarray;
60066448Svikram 	int		ret;
60076448Svikram 	int		n;
60086448Svikram 	int		i;
60096448Svikram 	int		on_bootdisk;
60106448Svikram 	const char	*fcn = "menu_on_bootdisk()";
60116448Svikram 
60126448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
60136448Svikram 
60146448Svikram 	ret = get_physical(menu_root, &physarray, &n);
60156448Svikram 	INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
60166448Svikram 	if (ret != 0) {
60176448Svikram 		bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
60180Sstevel@tonic-gate 		return (0);
60196448Svikram 	}
60206448Svikram 
60216448Svikram 	assert(physarray);
60226448Svikram 	assert(n > 0);
60236448Svikram 
60246448Svikram 	on_bootdisk = 0;
60256448Svikram 	for (i = 0; i < n; i++) {
60266448Svikram 		assert(strncmp(physarray[i], "/dev/dsk/",
60276448Svikram 		    strlen("/dev/dsk/")) == 0 ||
60286448Svikram 		    strncmp(physarray[i], "/dev/rdsk/",
60296448Svikram 		    strlen("/dev/rdsk/")) == 0);
60306448Svikram 
60316448Svikram 		BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
60326448Svikram 		if (is_bootdisk(osroot, physarray[i])) {
60336448Svikram 			on_bootdisk = 1;
60346448Svikram 			BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
60356448Svikram 		}
60366448Svikram 	}
60376448Svikram 
60386448Svikram 	free_physarray(physarray, n);
60396448Svikram 
60406448Svikram 	INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
60416448Svikram 	INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
60426448Svikram 	if (on_bootdisk) {
60436448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
60446448Svikram 	} else {
60456448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
60466448Svikram 	}
60476448Svikram 
60486448Svikram 	return (on_bootdisk);
60496448Svikram }
60506448Svikram 
60516448Svikram void
60526448Svikram bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
60536448Svikram {
60546448Svikram 	const char	*fcn = "bam_add_line()";
60556448Svikram 
60566448Svikram 	assert(mp);
60576448Svikram 	assert(entry);
60586448Svikram 	assert(prev);
60596448Svikram 	assert(lp);
60606448Svikram 
60616448Svikram 	lp->next = prev->next;
60626448Svikram 	if (prev->next) {
60636448Svikram 		BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
60646448Svikram 		prev->next->prev = lp;
60656448Svikram 	} else {
60666448Svikram 		BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
60676448Svikram 	}
60686448Svikram 	prev->next = lp;
60696448Svikram 	lp->prev = prev;
60706448Svikram 
60716448Svikram 	if (entry->end == prev) {
60726448Svikram 		BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
60736448Svikram 		entry->end = lp;
60746448Svikram 	}
60756448Svikram 	if (mp->end == prev) {
60766448Svikram 		assert(lp->next == NULL);
60776448Svikram 		mp->end = lp;
60786448Svikram 		BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
60796448Svikram 	}
60800Sstevel@tonic-gate }
60810Sstevel@tonic-gate 
6082662Sszhou /*
6083662Sszhou  * look for matching bootadm entry with specified parameters
6084662Sszhou  * Here are the rules (based on existing usage):
6085662Sszhou  * - If title is specified, match on title only
60866448Svikram  * - Else, match on root/findroot, kernel, and module.
60876448Svikram  *   Note that, if root_opt is non-zero, the absence of
60886448Svikram  *   root line is considered a match.
6089662Sszhou  */
6090662Sszhou static entry_t *
60916448Svikram find_boot_entry(
60926448Svikram 	menu_t *mp,
60936448Svikram 	char *title,
60946448Svikram 	char *kernel,
60956448Svikram 	char *findroot,
60966448Svikram 	char *root,
60976448Svikram 	char *module,
60986448Svikram 	int root_opt,
60996448Svikram 	int *entry_num)
61006448Svikram {
61016448Svikram 	int		i;
61026448Svikram 	line_t		*lp;
61036448Svikram 	entry_t		*ent;
61046448Svikram 	const char	*fcn = "find_boot_entry()";
61056448Svikram 
61066448Svikram 	if (entry_num)
61076448Svikram 		*entry_num = BAM_ERROR;
6108662Sszhou 
6109662Sszhou 	/* find matching entry */
6110662Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
6111662Sszhou 		lp = ent->start;
6112662Sszhou 
6113662Sszhou 		/* first line of entry must be bootadm comment */
6114662Sszhou 		lp = ent->start;
61153446Smrj 		if (lp->flags != BAM_COMMENT ||
61163446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
6117662Sszhou 			continue;
6118662Sszhou 		}
6119662Sszhou 
6120662Sszhou 		/* advance to title line */
6121662Sszhou 		lp = lp->next;
6122662Sszhou 		if (title) {
6123662Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
61246448Svikram 			    strcmp(lp->arg, title) == 0) {
61256448Svikram 				BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
6126662Sszhou 				break;
61276448Svikram 			}
61286448Svikram 			BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
6129662Sszhou 			continue;	/* check title only */
6130662Sszhou 		}
6131662Sszhou 
6132662Sszhou 		lp = lp->next;	/* advance to root line */
61335084Sjohnlev 		if (lp == NULL) {
61345084Sjohnlev 			continue;
61356448Svikram 		} else if (strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
61366448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
61376448Svikram 			    findroot = NULL);
61386448Svikram 			if (findroot == NULL) {
61396448Svikram 				BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
61406448Svikram 				    fcn, lp->arg));
61416448Svikram 				continue;
61426448Svikram 			}
61436448Svikram 			/* findroot command found, try match  */
61446448Svikram 			if (strcmp(lp->arg, findroot) != 0) {
61456448Svikram 				BAM_DPRINTF((D_NOMATCH_FINDROOT,
61466448Svikram 				    fcn, findroot, lp->arg));
6147662Sszhou 				continue;
6148662Sszhou 			}
61496448Svikram 			BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
61506448Svikram 			lp = lp->next;	/* advance to kernel line */
61516448Svikram 		} else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
61526448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
61536448Svikram 			if (root == NULL) {
61546448Svikram 				BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
61556448Svikram 				    fcn, lp->arg));
61566448Svikram 				continue;
61576448Svikram 			}
61586448Svikram 			/* root cmd found, try match */
61596448Svikram 			if (strcmp(lp->arg, root) != 0) {
61606448Svikram 				BAM_DPRINTF((D_NOMATCH_ROOT,
61616448Svikram 				    fcn, root, lp->arg));
61626448Svikram 				continue;
61636448Svikram 			}
61646448Svikram 			BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
6165662Sszhou 			lp = lp->next;	/* advance to kernel line */
6166662Sszhou 		} else {
61676448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
61686448Svikram 			    root_opt = 0);
61696448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
61706448Svikram 			    root_opt = 1);
6171662Sszhou 			/* no root command, see if root is optional */
6172662Sszhou 			if (root_opt == 0) {
61736448Svikram 				BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
6174662Sszhou 				continue;
6175662Sszhou 			}
61766448Svikram 			BAM_DPRINTF((D_ROOT_OPT, fcn));
6177662Sszhou 		}
6178662Sszhou 
6179662Sszhou 		if (lp == NULL || lp->next == NULL) {
6180662Sszhou 			continue;
6181662Sszhou 		}
6182662Sszhou 
61835084Sjohnlev 		if (kernel &&
61845084Sjohnlev 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
61855084Sjohnlev 			continue;
61865084Sjohnlev 		}
61876448Svikram 		BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
61885084Sjohnlev 
61893467Srscott 		/*
61905084Sjohnlev 		 * Check for matching module entry (failsafe or normal).
61915084Sjohnlev 		 * If it fails to match, we go around the loop again.
61925084Sjohnlev 		 * For xpv entries, there are two module lines, so we
61935084Sjohnlev 		 * do the check twice.
61943467Srscott 		 */
6195662Sszhou 		lp = lp->next;	/* advance to module line */
61965084Sjohnlev 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
61975084Sjohnlev 		    (((lp = lp->next) != NULL) &&
61985084Sjohnlev 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
61995084Sjohnlev 			/* match found */
62006448Svikram 			BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
62015084Sjohnlev 			break;
6202662Sszhou 		}
62035084Sjohnlev 	}
62045084Sjohnlev 
62056448Svikram 	if (ent && entry_num) {
62065084Sjohnlev 		*entry_num = i;
62075084Sjohnlev 	}
62086448Svikram 
62096448Svikram 	if (ent) {
62106448Svikram 		BAM_DPRINTF((D_RETURN_RET, fcn, i));
62116448Svikram 	} else {
62126448Svikram 		BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
62136448Svikram 	}
6214662Sszhou 	return (ent);
6215662Sszhou }
6216662Sszhou 
6217662Sszhou static int
62186448Svikram update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
62196448Svikram     char *kernel, char *mod_kernel, char *module, int root_opt)
62206448Svikram {
62216448Svikram 	int		i;
62226448Svikram 	int		change_kernel = 0;
62236448Svikram 	entry_t		*ent;
62246448Svikram 	line_t		*lp;
62256448Svikram 	line_t		*tlp;
62266448Svikram 	char		linebuf[BAM_MAXLINE];
62276448Svikram 	const char	*fcn = "update_boot_entry()";
6228662Sszhou 
6229662Sszhou 	/* note: don't match on title, it's updated on upgrade */
62306448Svikram 	ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
62316448Svikram 	    root_opt, &i);
62323446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
62333446Smrj 		/*
62343446Smrj 		 * We may be upgrading a kernel from multiboot to
62356448Svikram 		 * directboot.  Look for a multiboot entry. A multiboot
62366448Svikram 		 * entry will not have a findroot line.
62373446Smrj 		 */
62386448Svikram 		ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
62396448Svikram 		    MULTIBOOT_ARCHIVE, root_opt, &i);
62403446Smrj 		if (ent != NULL) {
62416448Svikram 			BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
62423446Smrj 			change_kernel = 1;
62433446Smrj 		}
62446448Svikram 	} else if (ent) {
62456448Svikram 		BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
62466448Svikram 	}
62476448Svikram 
62486448Svikram 	if (ent == NULL) {
62496448Svikram 		BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
62506448Svikram 		return (add_boot_entry(mp, title, findroot,
62515084Sjohnlev 		    kernel, mod_kernel, module));
62526448Svikram 	}
62536448Svikram 
62546448Svikram 	/* replace title of existing entry and update findroot line */
6255662Sszhou 	lp = ent->start;
6256662Sszhou 	lp = lp->next;	/* title line */
6257662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
6258662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
6259662Sszhou 	free(lp->arg);
6260662Sszhou 	free(lp->line);
6261662Sszhou 	lp->arg = s_strdup(title);
6262662Sszhou 	lp->line = s_strdup(linebuf);
62636448Svikram 	BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
62646448Svikram 
62656448Svikram 	tlp = lp;	/* title line */
6266662Sszhou 	lp = lp->next;	/* root line */
62676448Svikram 
62686448Svikram 	/* if no root or findroot command, create a new line_t */
62696448Svikram 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
62706448Svikram 	    strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0) {
62716448Svikram 		lp = s_calloc(1, sizeof (line_t));
62726448Svikram 		bam_add_line(mp, ent, tlp, lp);
62736448Svikram 	} else {
62746448Svikram 		free(lp->cmd);
62756448Svikram 		free(lp->sep);
62766448Svikram 		free(lp->arg);
62776448Svikram 		free(lp->line);
62786448Svikram 	}
62796448Svikram 
62806448Svikram 	lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
62816448Svikram 	lp->sep = s_strdup(menu_cmds[SEP_CMD]);
62826448Svikram 	lp->arg = s_strdup(findroot);
62836448Svikram 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
62846448Svikram 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
62856448Svikram 	lp->line = s_strdup(linebuf);
62866448Svikram 	BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
62876448Svikram 
62886448Svikram 	/* kernel line */
62896448Svikram 	lp = lp->next;
62903446Smrj 
62913446Smrj 	if (change_kernel) {
62923446Smrj 		/*
62933446Smrj 		 * We're upgrading from multiboot to directboot.
62943446Smrj 		 */
62953446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
62963446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
62973446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
62983446Smrj 			    kernel);
62996448Svikram 			free(lp->cmd);
63003446Smrj 			free(lp->arg);
63013446Smrj 			free(lp->line);
63026448Svikram 			lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
63033446Smrj 			lp->arg = s_strdup(kernel);
63043446Smrj 			lp->line = s_strdup(linebuf);
63053446Smrj 			lp = lp->next;
63066448Svikram 			BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
63073446Smrj 		}
63083446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
63093446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
63103446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
63113446Smrj 			    module);
63126448Svikram 			free(lp->cmd);
63133446Smrj 			free(lp->arg);
63143446Smrj 			free(lp->line);
63156448Svikram 			lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
63163446Smrj 			lp->arg = s_strdup(module);
63173446Smrj 			lp->line = s_strdup(linebuf);
63183446Smrj 			lp = lp->next;
63196448Svikram 			BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
63206448Svikram 		}
63216448Svikram 	}
63226448Svikram 	BAM_DPRINTF((D_RETURN_RET, fcn, i));
6323662Sszhou 	return (i);
6324662Sszhou }
6325662Sszhou 
63266448Svikram int
63276448Svikram root_optional(char *osroot, char *menu_root)
63286448Svikram {
63296448Svikram 	char			*ospecial;
63306448Svikram 	char			*mspecial;
63316448Svikram 	char			*slash;
63326448Svikram 	int			root_opt;
63336448Svikram 	int			ret1;
63346448Svikram 	int			ret2;
63356448Svikram 	const char		*fcn = "root_optional()";
63366448Svikram 
63376448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
63386448Svikram 
63396448Svikram 	/*
63406448Svikram 	 * For all filesystems except ZFS, a straight compare of osroot
63416448Svikram 	 * and menu_root will tell us if root is optional.
63426448Svikram 	 * For ZFS, the situation is complicated by the fact that
63436448Svikram 	 * menu_root and osroot are always different
63446448Svikram 	 */
63456448Svikram 	ret1 = is_zfs(osroot);
63466448Svikram 	ret2 = is_zfs(menu_root);
63476448Svikram 	INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
63486448Svikram 	if (!ret1 || !ret2) {
63496448Svikram 		BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
63506448Svikram 		root_opt = (strcmp(osroot, menu_root) == 0);
63516448Svikram 		goto out;
63526448Svikram 	}
63536448Svikram 
63546448Svikram 	ospecial = get_special(osroot);
63556448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
63566448Svikram 	if (ospecial == NULL) {
63576448Svikram 		bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
63586448Svikram 		return (0);
63596448Svikram 	}
63606448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
63616448Svikram 
63626448Svikram 	mspecial = get_special(menu_root);
63636448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
63646448Svikram 	if (mspecial == NULL) {
63656448Svikram 		bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
63666448Svikram 		free(ospecial);
63676448Svikram 		return (0);
63686448Svikram 	}
63696448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
63706448Svikram 
63716448Svikram 	slash = strchr(ospecial, '/');
63726448Svikram 	if (slash)
63736448Svikram 		*slash = '\0';
63746448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
63756448Svikram 
63766448Svikram 	root_opt = (strcmp(ospecial, mspecial) == 0);
63776448Svikram 
63786448Svikram 	free(ospecial);
63796448Svikram 	free(mspecial);
63806448Svikram 
63816448Svikram out:
63826448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
63836448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
63846448Svikram 	if (root_opt) {
63856448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
63866448Svikram 	} else {
63876448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
63886448Svikram 	}
63896448Svikram 
63906448Svikram 	return (root_opt);
63916448Svikram }
63926448Svikram 
63930Sstevel@tonic-gate /*ARGSUSED*/
63940Sstevel@tonic-gate static error_t
63956448Svikram update_entry(menu_t *mp, char *menu_root, char *osdev)
63966448Svikram {
63976448Svikram 	int		entry;
63986448Svikram 	char		*grubsign;
63996448Svikram 	char		*grubroot;
64006448Svikram 	char		*title;
64016448Svikram 	char		osroot[PATH_MAX];
64026448Svikram 	char		*failsafe_kernel = NULL;
64036448Svikram 	struct stat	sbuf;
64046448Svikram 	char		failsafe[256];
64056448Svikram 	int		ret;
64066448Svikram 	const char	*fcn = "update_entry()";
64070Sstevel@tonic-gate 
64080Sstevel@tonic-gate 	assert(mp);
64096448Svikram 	assert(menu_root);
64106448Svikram 	assert(osdev);
64116448Svikram 	assert(bam_root);
64126448Svikram 
64136448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
64146448Svikram 
64156448Svikram 	(void) strlcpy(osroot, bam_root, sizeof (osroot));
64166448Svikram 
64170Sstevel@tonic-gate 	title = get_title(osroot);
64186448Svikram 	assert(title);
64196448Svikram 
64206448Svikram 	grubsign = get_grubsign(osroot, osdev);
64216448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
64226448Svikram 	if (grubsign == NULL) {
64236448Svikram 		bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
64240Sstevel@tonic-gate 		return (BAM_ERROR);
64250Sstevel@tonic-gate 	}
64266448Svikram 
64276448Svikram 	/*
64286448Svikram 	 * It is not a fatal error if get_grubroot() fails
64296448Svikram 	 * We no longer rely on biosdev to populate the
64306448Svikram 	 * menu
64316448Svikram 	 */
64326448Svikram 	grubroot = get_grubroot(osroot, osdev, menu_root);
64336448Svikram 	INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
64346448Svikram 	if (grubroot) {
64356448Svikram 		BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
64366448Svikram 		    fcn, osroot, osdev, menu_root));
64376448Svikram 	} else {
64386448Svikram 		BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
64396448Svikram 		    fcn, osroot, osdev, menu_root));
64400Sstevel@tonic-gate 	}
64410Sstevel@tonic-gate 
64420Sstevel@tonic-gate 	/* add the entry for normal Solaris */
64436448Svikram 	INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
64446448Svikram 	    bam_direct = BAM_DIRECT_MULTIBOOT);
64453446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
64466448Svikram 		entry = update_boot_entry(mp, title, grubsign, grubroot,
64476423Sgw25295 		    (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
64486448Svikram 		    NULL, DIRECT_BOOT_ARCHIVE,
64496448Svikram 		    root_optional(osroot, menu_root));
64506448Svikram 		BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
64515084Sjohnlev 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
64526448Svikram 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
64536448Svikram 			    grubroot, XEN_MENU, bam_zfs ?
64546448Svikram 			    XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
64556448Svikram 			    DIRECT_BOOT_ARCHIVE,
64566448Svikram 			    root_optional(osroot, menu_root));
64576448Svikram 			BAM_DPRINTF((D_UPDATED_HV_ENTRY,
64586448Svikram 			    fcn, bam_zfs, grubsign));
64595084Sjohnlev 		}
64603446Smrj 	} else {
64616448Svikram 		entry = update_boot_entry(mp, title, grubsign, grubroot,
64626448Svikram 		    MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
64636448Svikram 		    root_optional(osroot, menu_root));
64646448Svikram 
64656448Svikram 		BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
64665084Sjohnlev 	}
64675084Sjohnlev 
64685084Sjohnlev 	/*
64695084Sjohnlev 	 * Add the entry for failsafe archive.  On a bfu'd system, the
64705084Sjohnlev 	 * failsafe may be different than the installed kernel.
64715084Sjohnlev 	 */
64726448Svikram 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
64736448Svikram 	    osroot, FAILSAFE_ARCHIVE);
64743446Smrj 	if (stat(failsafe, &sbuf) == 0) {
64753449Srscott 
64763449Srscott 		/* Figure out where the kernel line should point */
64773449Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
64783449Srscott 		    DIRECT_BOOT_FAILSAFE_KERNEL);
64793449Srscott 		if (stat(failsafe, &sbuf) == 0) {
64806694Svikram 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
64813449Srscott 		} else {
64823449Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
64833449Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
64843449Srscott 			if (stat(failsafe, &sbuf) == 0) {
64853449Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
64863449Srscott 			}
64873449Srscott 		}
64883449Srscott 		if (failsafe_kernel != NULL) {
64896448Svikram 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
64906448Svikram 			    grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
64916448Svikram 			    root_optional(osroot, menu_root));
64926448Svikram 			BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
64936448Svikram 			    failsafe_kernel));
64946448Svikram 		}
64956448Svikram 	}
64966448Svikram 	free(grubroot);
64976448Svikram 
64986448Svikram 	INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
64990Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
65006448Svikram 		bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
65016448Svikram 		free(grubsign);
65020Sstevel@tonic-gate 		return (BAM_ERROR);
65030Sstevel@tonic-gate 	}
65046448Svikram 	free(grubsign);
65056448Svikram 
65066448Svikram 	update_numbering(mp);
65076448Svikram 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
65086448Svikram 	INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
65096448Svikram 	if (ret == BAM_ERROR) {
65106448Svikram 		bam_error(SET_DEFAULT_FAILED, entry);
65116448Svikram 	}
65126448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
65130Sstevel@tonic-gate 	return (BAM_WRITE);
65140Sstevel@tonic-gate }
65150Sstevel@tonic-gate 
6516662Sszhou static void
65173446Smrj save_default_entry(menu_t *mp, const char *which)
6518662Sszhou {
65196448Svikram 	int		lineNum;
65206448Svikram 	int		entryNum;
65216448Svikram 	int		entry = 0;	/* default is 0 */
65226448Svikram 	char		linebuf[BAM_MAXLINE];
65236448Svikram 	line_t		*lp = mp->curdefault;
65246448Svikram 	const char	*fcn = "save_default_entry()";
6525662Sszhou 
65263381Svikram 	if (mp->start) {
65273381Svikram 		lineNum = mp->end->lineNum;
65283381Svikram 		entryNum = mp->end->entryNum;
65293381Svikram 	} else {
65303381Svikram 		lineNum = LINE_INIT;
65313381Svikram 		entryNum = ENTRY_INIT;
65323381Svikram 	}
65333381Svikram 
6534662Sszhou 	if (lp)
6535662Sszhou 		entry = s_strtol(lp->arg);
6536662Sszhou 
65373446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
65386448Svikram 	BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
6539662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
65406448Svikram 	BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
6541662Sszhou }
6542662Sszhou 
6543662Sszhou static void
65443446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
6545662Sszhou {
65466448Svikram 	int		entry;
65476448Svikram 	char		*str;
65486448Svikram 	const char	*fcn = "restore_default_entry()";
65496448Svikram 
65506448Svikram 	if (lp == NULL) {
65516448Svikram 		BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
6552662Sszhou 		return;		/* nothing to restore */
65536448Svikram 	}
65546448Svikram 
65556448Svikram 	BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
6556662Sszhou 
65573446Smrj 	str = lp->arg + strlen(which);
6558662Sszhou 	entry = s_strtol(str);
6559662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
6560662Sszhou 
65616448Svikram 	BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
65626448Svikram 
6563662Sszhou 	/* delete saved old default line */
6564662Sszhou 	unlink_line(mp, lp);
6565662Sszhou 	line_free(lp);
6566662Sszhou }
6567662Sszhou 
65680Sstevel@tonic-gate /*
65690Sstevel@tonic-gate  * This function is for supporting reboot with args.
65700Sstevel@tonic-gate  * The opt value can be:
65710Sstevel@tonic-gate  * NULL		delete temp entry, if present
65726448Svikram  * entry=<n>	switches default entry to <n>
65730Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
65740Sstevel@tonic-gate  *		and make it the default
65756448Svikram  * Note that we are always rebooting the current OS instance
65766448Svikram  * so osroot == / always.
65770Sstevel@tonic-gate  */
65780Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
65790Sstevel@tonic-gate 
6580662Sszhou /*ARGSUSED*/
65810Sstevel@tonic-gate static error_t
65826448Svikram update_temp(menu_t *mp, char *dummy, char *opt)
65836448Svikram {
65846448Svikram 	int		entry;
65856448Svikram 	char		*osdev;
65866448Svikram 	char		*fstype;
65876448Svikram 	char		*sign;
65886448Svikram 	char		*opt_ptr;
65896448Svikram 	char		*path;
65906448Svikram 	char		kernbuf[BUFSIZ];
65916448Svikram 	char		args_buf[BUFSIZ];
65926448Svikram 	char		signbuf[PATH_MAX];
65936448Svikram 	int		ret;
65946448Svikram 	const char	*fcn = "update_temp()";
65950Sstevel@tonic-gate 
65960Sstevel@tonic-gate 	assert(mp);
65976448Svikram 	assert(dummy == NULL);
65986448Svikram 
65996448Svikram 	/* opt can be NULL */
66006448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
66016448Svikram 	BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
66026448Svikram 
66036448Svikram 	if (bam_alt_root || bam_rootlen != 1 ||
66046448Svikram 	    strcmp(bam_root, "/") != 0 ||
66056448Svikram 	    strcmp(rootbuf, "/") != 0) {
66066448Svikram 		bam_error(ALT_ROOT_INVALID, bam_root);
66076448Svikram 		return (BAM_ERROR);
66086448Svikram 	}
66090Sstevel@tonic-gate 
6610662Sszhou 	/* If no option, delete exiting reboot menu entry */
6611662Sszhou 	if (opt == NULL) {
66126448Svikram 		entry_t		*ent;
66136448Svikram 		BAM_DPRINTF((D_OPT_NULL, fcn));
66146448Svikram 		ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
66156448Svikram 		    NULL, NULL, 0, &entry);
66166448Svikram 		if (ent == NULL) {	/* not found is ok */
66176448Svikram 			BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
6618662Sszhou 			return (BAM_SUCCESS);
66196448Svikram 		}
6620662Sszhou 		(void) do_delete(mp, entry);
66213446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
66223446Smrj 		mp->olddefault = NULL;
66236448Svikram 		BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
66246448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6625662Sszhou 		return (BAM_WRITE);
6626662Sszhou 	}
6627662Sszhou 
6628662Sszhou 	/* if entry= is specified, set the default entry */
66296448Svikram 	if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
66306448Svikram 		int entryNum = s_strtol(opt + strlen("entry="));
66316448Svikram 		BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
66326448Svikram 		if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
66336448Svikram 			/* this is entry=# option */
66346448Svikram 			ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
66356448Svikram 			BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
66366448Svikram 			return (ret);
66376448Svikram 		} else {
66386448Svikram 			bam_error(SET_DEFAULT_FAILED, entryNum);
66396448Svikram 			return (BAM_ERROR);
66406448Svikram 		}
66410Sstevel@tonic-gate 	}
66420Sstevel@tonic-gate 
66430Sstevel@tonic-gate 	/*
66446448Svikram 	 * add a new menu entry based on opt and make it the default
66450Sstevel@tonic-gate 	 */
66466448Svikram 
66476448Svikram 	fstype = get_fstype("/");
66486448Svikram 	INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
66496448Svikram 	if (fstype == NULL) {
66506448Svikram 		bam_error(REBOOT_FSTYPE_FAILED);
66516448Svikram 		return (BAM_ERROR);
66526448Svikram 	}
66536448Svikram 
66546448Svikram 	osdev = get_special("/");
66556448Svikram 	INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
66566448Svikram 	if (osdev == NULL) {
66576448Svikram 		free(fstype);
66586448Svikram 		bam_error(REBOOT_SPECIAL_FAILED);
66596448Svikram 		return (BAM_ERROR);
66606448Svikram 	}
66616448Svikram 
66626448Svikram 	sign = find_existing_sign("/", osdev, fstype);
66636448Svikram 	INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
66646448Svikram 	if (sign == NULL) {
66656448Svikram 		free(fstype);
66666448Svikram 		free(osdev);
66676448Svikram 		bam_error(REBOOT_SIGN_FAILED);
66686448Svikram 		return (BAM_ERROR);
66696448Svikram 	}
66706448Svikram 
66716448Svikram 	free(fstype);
66726448Svikram 	free(osdev);
66736448Svikram 	(void) strlcpy(signbuf, sign, sizeof (signbuf));
66746448Svikram 	free(sign);
66756448Svikram 
66766448Svikram 	assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
66776448Svikram 	    strchr(signbuf, ')') == NULL);
66786448Svikram 
66796448Svikram 	/*
66806448Svikram 	 * There is no alternate root while doing reboot with args
66816448Svikram 	 * This version of bootadm is only delivered with a DBOOT
66826448Svikram 	 * version of Solaris.
66836448Svikram 	 */
66846448Svikram 	INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
66856448Svikram 	if (bam_direct != BAM_DIRECT_DBOOT) {
66866448Svikram 		bam_error(REBOOT_DIRECT_FAILED);
66870Sstevel@tonic-gate 		return (BAM_ERROR);
66880Sstevel@tonic-gate 	}
66890Sstevel@tonic-gate 
66900Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
66916448Svikram 	if (opt[0] == '-') {
66926448Svikram 		/* It's an option - first see if boot-file is set */
66936448Svikram 		ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
66946448Svikram 		INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
66956448Svikram 		if (ret != BAM_SUCCESS) {
66966448Svikram 			bam_error(REBOOT_GET_KERNEL_FAILED);
66976448Svikram 			return (BAM_ERROR);
66986448Svikram 		}
66996448Svikram 		if (kernbuf[0] == '\0')
67006448Svikram 			(void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
67016448Svikram 			    sizeof (kernbuf));
67026448Svikram 		(void) strlcat(kernbuf, " ", sizeof (kernbuf));
67036448Svikram 		(void) strlcat(kernbuf, opt, sizeof (kernbuf));
67046448Svikram 		BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
67056448Svikram 	} else if (opt[0] == '/') {
67066448Svikram 		/* It's a full path, so write it out. */
67076448Svikram 		(void) strlcpy(kernbuf, opt, sizeof (kernbuf));
67086448Svikram 
67096448Svikram 		/*
67106448Svikram 		 * If someone runs:
67116448Svikram 		 *
67126448Svikram 		 *	# eeprom boot-args='-kd'
67136448Svikram 		 *	# reboot /platform/i86pc/kernel/unix
67146448Svikram 		 *
67156448Svikram 		 * we want to use the boot-args as part of the boot
67166448Svikram 		 * line.  On the other hand, if someone runs:
67176448Svikram 		 *
67186448Svikram 		 *	# reboot "/platform/i86pc/kernel/unix -kd"
67196448Svikram 		 *
67206448Svikram 		 * we don't need to mess with boot-args.  If there's
67216448Svikram 		 * no space in the options string, assume we're in the
67226448Svikram 		 * first case.
67236448Svikram 		 */
67246448Svikram 		if (strchr(opt, ' ') == NULL) {
67256448Svikram 			ret = get_kernel(mp, ARGS_CMD, args_buf,
67266448Svikram 			    sizeof (args_buf));
67276448Svikram 			INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
67286448Svikram 			if (ret != BAM_SUCCESS) {
67296448Svikram 				bam_error(REBOOT_GET_ARGS_FAILED);
67303446Smrj 				return (BAM_ERROR);
67316448Svikram 			}
67326448Svikram 
67336448Svikram 			if (args_buf[0] != '\0') {
67346448Svikram 				(void) strlcat(kernbuf, " ", sizeof (kernbuf));
67356448Svikram 				(void) strlcat(kernbuf, args_buf,
67366448Svikram 				    sizeof (kernbuf));
67376448Svikram 			}
67386448Svikram 		}
67396448Svikram 		BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
67406448Svikram 	} else {
67416448Svikram 		/*
67426448Svikram 		 * It may be a partial path, or it may be a partial
67436448Svikram 		 * path followed by options.  Assume that only options
67446448Svikram 		 * follow a space.  If someone sends us a kernel path
67456448Svikram 		 * that includes a space, they deserve to be broken.
67466448Svikram 		 */
67476448Svikram 		opt_ptr = strchr(opt, ' ');
67486448Svikram 		if (opt_ptr != NULL) {
67496448Svikram 			*opt_ptr = '\0';
67506448Svikram 		}
67516448Svikram 
67526448Svikram 		path = expand_path(opt);
67536448Svikram 		if (path != NULL) {
67546448Svikram 			(void) strlcpy(kernbuf, path, sizeof (kernbuf));
67556448Svikram 			free(path);
67564346Srscott 
67574346Srscott 			/*
67586448Svikram 			 * If there were options given, use those.
67596448Svikram 			 * Otherwise, copy over the default options.
67604346Srscott 			 */
67616448Svikram 			if (opt_ptr != NULL) {
67626448Svikram 				/* Restore the space in opt string */
67636448Svikram 				*opt_ptr = ' ';
67646448Svikram 				(void) strlcat(kernbuf, opt_ptr,
67656448Svikram 				    sizeof (kernbuf));
67666448Svikram 			} else {
67676448Svikram 				ret = get_kernel(mp, ARGS_CMD, args_buf,
67686448Svikram 				    sizeof (args_buf));
67696448Svikram 				INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
67706448Svikram 				    ret = BAM_ERROR);
67716448Svikram 				if (ret != BAM_SUCCESS) {
67726448Svikram 					bam_error(REBOOT_GET_ARGS_FAILED);
67734346Srscott 					return (BAM_ERROR);
67746448Svikram 				}
67754346Srscott 
67764346Srscott 				if (args_buf[0] != '\0') {
67776448Svikram 					(void) strlcat(kernbuf, " ",
67786448Svikram 					    sizeof (kernbuf));
67796448Svikram 					(void) strlcat(kernbuf,
67806448Svikram 					    args_buf, sizeof (kernbuf));
67814346Srscott 				}
67824346Srscott 			}
67836448Svikram 			BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
67843446Smrj 		} else {
67856448Svikram 			bam_error(UNKNOWN_KERNEL, opt);
67866448Svikram 			bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
67876448Svikram 			return (BAM_ERROR);
67886448Svikram 		}
67896448Svikram 	}
67906448Svikram 	entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
67916448Svikram 	    NULL, NULL);
67926448Svikram 	INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
67930Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
67946448Svikram 		bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
67950Sstevel@tonic-gate 		return (BAM_ERROR);
67960Sstevel@tonic-gate 	}
6797662Sszhou 
67983446Smrj 	save_default_entry(mp, BAM_OLDDEF);
67996448Svikram 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
68006448Svikram 	INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
68016448Svikram 	if (ret == BAM_ERROR) {
68026448Svikram 		bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
68036448Svikram 	}
68046448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
68050Sstevel@tonic-gate 	return (BAM_WRITE);
68060Sstevel@tonic-gate }
68070Sstevel@tonic-gate 
68080Sstevel@tonic-gate static error_t
68090Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
68100Sstevel@tonic-gate {
68116448Svikram 	line_t		*lp;
68126448Svikram 	line_t		*found;
68136448Svikram 	line_t		*last;
68146448Svikram 	char		*cp;
68156448Svikram 	char		*str;
68166448Svikram 	char		prefix[BAM_MAXLINE];
68176448Svikram 	size_t		len;
68186448Svikram 	const char	*fcn = "set_global()";
68190Sstevel@tonic-gate 
68200Sstevel@tonic-gate 	assert(mp);
68210Sstevel@tonic-gate 	assert(globalcmd);
68220Sstevel@tonic-gate 
6823316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
68246448Svikram 		INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
68256448Svikram 		INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
68266448Svikram 		INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
6827316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
6828316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
6829316Svikram 			bam_error(INVALID_ENTRY, prefix);
6830316Svikram 			return (BAM_ERROR);
6831316Svikram 		}
6832316Svikram 	}
6833316Svikram 
68340Sstevel@tonic-gate 	found = last = NULL;
68350Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
68360Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
68370Sstevel@tonic-gate 			continue;
68380Sstevel@tonic-gate 
68390Sstevel@tonic-gate 		last = lp; /* track the last global found */
68400Sstevel@tonic-gate 
68416448Svikram 		INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
68420Sstevel@tonic-gate 		if (lp->cmd == NULL) {
68430Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
68440Sstevel@tonic-gate 			continue;
68450Sstevel@tonic-gate 		}
68460Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
68470Sstevel@tonic-gate 			continue;
68480Sstevel@tonic-gate 
68496448Svikram 		BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
68506448Svikram 
68510Sstevel@tonic-gate 		if (found) {
68520Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
68530Sstevel@tonic-gate 		}
68540Sstevel@tonic-gate 		found = lp;
68550Sstevel@tonic-gate 	}
68560Sstevel@tonic-gate 
68570Sstevel@tonic-gate 	if (found == NULL) {
68580Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
68590Sstevel@tonic-gate 		if (last == NULL) {
68600Sstevel@tonic-gate 			lp->next = mp->start;
68610Sstevel@tonic-gate 			mp->start = lp;
68620Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
68630Sstevel@tonic-gate 		} else {
68640Sstevel@tonic-gate 			lp->next = last->next;
68650Sstevel@tonic-gate 			last->next = lp;
68660Sstevel@tonic-gate 			if (lp->next == NULL)
68670Sstevel@tonic-gate 				mp->end = lp;
68680Sstevel@tonic-gate 		}
68690Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
68700Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
68710Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
68720Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
68730Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
68740Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
68756448Svikram 		BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
68766448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
68770Sstevel@tonic-gate 		return (BAM_WRITE);
68780Sstevel@tonic-gate 	}
68790Sstevel@tonic-gate 
68800Sstevel@tonic-gate 	/*
68810Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
68820Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
68830Sstevel@tonic-gate 	 * readability.
68840Sstevel@tonic-gate 	 */
68850Sstevel@tonic-gate 	str = found->line;
68860Sstevel@tonic-gate 	cp = prefix;
68870Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
68880Sstevel@tonic-gate 		*(cp++) = *(str++);
68890Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
68900Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
68910Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
68920Sstevel@tonic-gate 
68930Sstevel@tonic-gate 	free(found->line);
68940Sstevel@tonic-gate 	found->line = s_calloc(1, len);
68950Sstevel@tonic-gate 	(void) snprintf(found->line, len,
68964346Srscott 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
68970Sstevel@tonic-gate 
68986448Svikram 	BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
68996448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
69000Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
69010Sstevel@tonic-gate }
69020Sstevel@tonic-gate 
69033446Smrj /*
69043446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
69054346Srscott  * expand it to a full unix path.  The calling function is expected to
69064346Srscott  * output a message if an error occurs and NULL is returned.
69073446Smrj  */
69083446Smrj static char *
69093446Smrj expand_path(const char *partial_path)
69103446Smrj {
69116448Svikram 	int		new_path_len;
69126448Svikram 	char		*new_path;
69136448Svikram 	char		new_path2[PATH_MAX];
69146448Svikram 	struct stat	sb;
69156448Svikram 	const char	*fcn = "expand_path()";
69163446Smrj 
69173446Smrj 	new_path_len = strlen(partial_path) + 64;
69183446Smrj 	new_path = s_calloc(1, new_path_len);
69193446Smrj 
69203446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
69213446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
69223446Smrj 	    partial_path);
69233446Smrj 	if (stat(new_path, &sb) == 0) {
69246448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
69253446Smrj 		return (new_path);
69263446Smrj 	}
69273446Smrj 
69283446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
69293446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
69303446Smrj 		    DIRECT_BOOT_KERNEL);
69316448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
69323446Smrj 		return (new_path);
69333446Smrj 	}
69343446Smrj 
69353446Smrj 	/*
69363446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
69373446Smrj 	 * see if we were just given a glom name.
69383446Smrj 	 */
69393446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
69403446Smrj 	    partial_path);
69413446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
69423446Smrj 	    partial_path);
69433446Smrj 	if (stat(new_path, &sb) == 0) {
69443446Smrj 		if (stat(new_path2, &sb) == 0) {
69453446Smrj 			/*
69463446Smrj 			 * We matched both, so we actually
69473446Smrj 			 * want to write the $ISADIR version.
69483446Smrj 			 */
69493446Smrj 			(void) snprintf(new_path, new_path_len,
69503446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
69513446Smrj 			    partial_path);
69523446Smrj 		}
69536448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
69543446Smrj 		return (new_path);
69553446Smrj 	}
69563446Smrj 
69573446Smrj 	free(new_path);
69586448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
69593446Smrj 	return (NULL);
69603446Smrj }
69613446Smrj 
69623446Smrj /*
69633446Smrj  * The kernel cmd and arg have been changed, so
69643446Smrj  * check whether the archive line needs to change.
69653446Smrj  */
69663446Smrj static void
69673446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
69683446Smrj {
69696448Svikram 	line_t		*lp = entryp->start;
69706448Svikram 	char		*new_archive;
69716448Svikram 	menu_cmd_t	m_cmd;
69726448Svikram 	const char	*fcn = "set_archive_line()";
69733446Smrj 
69743446Smrj 	for (; lp != NULL; lp = lp->next) {
69753446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
69763446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
69773446Smrj 			break;
69783446Smrj 		}
69796448Svikram 
69806448Svikram 		INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
69816448Svikram 		if (lp == entryp->end) {
69826448Svikram 			BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
69836448Svikram 			    entryp->entryNum));
69843446Smrj 			return;
69856448Svikram 		}
69866448Svikram 	}
69876448Svikram 	INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
69886448Svikram 	if (lp == NULL) {
69896448Svikram 		BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
69903446Smrj 		return;
69916448Svikram 	}
69923446Smrj 
69933446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
69943446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
69953446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
69963446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
69973446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
69983446Smrj 		m_cmd = MODULE_CMD;
69993446Smrj 	} else {
70003446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
70013446Smrj 		m_cmd = MODULE_CMD;
70023446Smrj 	}
70033446Smrj 
70046448Svikram 	if (strcmp(lp->arg, new_archive) == 0) {
70056448Svikram 		BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
70063446Smrj 		return;
70076448Svikram 	}
70083446Smrj 
70093446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
70103446Smrj 		free(lp->cmd);
70113446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
70123446Smrj 	}
70133446Smrj 
70143446Smrj 	free(lp->arg);
70153446Smrj 	lp->arg = s_strdup(new_archive);
70163446Smrj 	update_line(lp);
70176448Svikram 	BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
70183446Smrj }
70193446Smrj 
70203446Smrj /*
70213446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
70223446Smrj  */
70233446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
70243446Smrj 
70253446Smrj /*
70263446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
70273446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
70283446Smrj  * string, reset the value to the default.  If path is a non-zero-length
70293446Smrj  * string, set the kernel or arguments.
70303446Smrj  */
70313446Smrj static error_t
70326448Svikram get_set_kernel(
70336448Svikram 	menu_t *mp,
70346448Svikram 	menu_cmd_t optnum,
70356448Svikram 	char *path,
70366448Svikram 	char *buf,
70376448Svikram 	size_t bufsize)
70386448Svikram {
70396448Svikram 	int		entryNum;
70406448Svikram 	int		rv = BAM_SUCCESS;
70416448Svikram 	int		free_new_path = 0;
70426448Svikram 	entry_t		*entryp;
70436448Svikram 	line_t		*ptr;
70446448Svikram 	line_t		*kernelp;
70456448Svikram 	char		*new_arg;
70466448Svikram 	char		*old_args;
70476448Svikram 	char		*space;
70486448Svikram 	char		*new_path;
70496448Svikram 	char		old_space;
70506448Svikram 	size_t		old_kernel_len;
70516448Svikram 	size_t		new_str_len;
70526448Svikram 	char		*fstype;
70536448Svikram 	char		*osdev;
70546448Svikram 	char		*sign;
70556448Svikram 	char		signbuf[PATH_MAX];
70566448Svikram 	int		ret;
70576448Svikram 	const char	*fcn = "get_set_kernel()";
70583446Smrj 
70593446Smrj 	assert(bufsize > 0);
70603446Smrj 
70613446Smrj 	ptr = kernelp = NULL;
70623446Smrj 	new_arg = old_args = space = NULL;
70636448Svikram 	new_path = NULL;
70643446Smrj 	buf[0] = '\0';
70653446Smrj 
70666448Svikram 	INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
70676448Svikram 	    bam_direct = BAM_DIRECT_MULTIBOOT);
70683446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
70693446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
70703446Smrj 		return (BAM_ERROR);
70713446Smrj 	}
70723446Smrj 
70733446Smrj 	/*
70743446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
70753446Smrj 	 * one, we don't want to mess with it.  Just print an error and
70763446Smrj 	 * return.
70773446Smrj 	 */
70783446Smrj 	if (mp->curdefault) {
70793446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
70803446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
70813446Smrj 			if (entryp->entryNum == entryNum)
70823446Smrj 				break;
70833446Smrj 		}
70843446Smrj 		if ((entryp != NULL) &&
70853446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
70863446Smrj 			bam_error(DEFAULT_NOT_BAM);
70873446Smrj 			return (BAM_ERROR);
70883446Smrj 		}
70893446Smrj 	}
70903446Smrj 
70916448Svikram 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
70926448Svikram 	    0, &entryNum);
70933446Smrj 
70943446Smrj 	if (entryp != NULL) {
70953446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
70963446Smrj 		    ptr = ptr->next) {
70973446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
70983446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
70993446Smrj 				kernelp = ptr;
71003446Smrj 				break;
71013446Smrj 			}
71023446Smrj 		}
71033446Smrj 		if (kernelp == NULL) {
71043446Smrj 			bam_error(NO_KERNEL, entryNum);
71053446Smrj 			return (BAM_ERROR);
71063446Smrj 		}
71073446Smrj 
71083446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
71093446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
71103446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
71113446Smrj 			old_args++;
71123446Smrj 	}
71133446Smrj 
71143446Smrj 	if (path == NULL) {
71156448Svikram 		if (entryp == NULL) {
71166448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
71176448Svikram 			BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
71183446Smrj 			return (BAM_SUCCESS);
71196448Svikram 		}
71206448Svikram 		assert(kernelp);
71213446Smrj 		if (optnum == ARGS_CMD) {
71226448Svikram 			if (old_args[0] != '\0') {
71233446Smrj 				(void) strlcpy(buf, old_args, bufsize);
71246448Svikram 				BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
71256448Svikram 			}
71263446Smrj 		} else {
71273446Smrj 			/*
71283446Smrj 			 * We need to print the kernel, so we just turn the
71293446Smrj 			 * first space into a '\0' and print the beginning.
71303446Smrj 			 * We don't print anything if it's the default kernel.
71313446Smrj 			 */
71323446Smrj 			old_space = *space;
71333446Smrj 			*space = '\0';
71346448Svikram 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
71353446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
71366448Svikram 				BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
71376448Svikram 			}
71383446Smrj 			*space = old_space;
71393446Smrj 		}
71406448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
71413446Smrj 		return (BAM_SUCCESS);
71423446Smrj 	}
71433446Smrj 
71443446Smrj 	/*
71453446Smrj 	 * First, check if we're resetting an entry to the default.
71463446Smrj 	 */
71473446Smrj 	if ((path[0] == '\0') ||
71483446Smrj 	    ((optnum == KERNEL_CMD) &&
71493446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
71503446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
71513446Smrj 			/* No previous entry, it's already the default */
71526448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
71533446Smrj 			return (BAM_SUCCESS);
71543446Smrj 		}
71553446Smrj 
71563446Smrj 		/*
71573446Smrj 		 * Check if we can delete the entry.  If we're resetting the
71583446Smrj 		 * kernel command, and the args is already empty, or if we're
71593446Smrj 		 * resetting the args command, and the kernel is already the
71603446Smrj 		 * default, we can restore the old default and delete the entry.
71613446Smrj 		 */
71623446Smrj 		if (((optnum == KERNEL_CMD) &&
71633446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
71643446Smrj 		    ((optnum == ARGS_CMD) &&
71653446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
71663446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
71673446Smrj 			kernelp = NULL;
71683446Smrj 			(void) do_delete(mp, entryNum);
71693446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
71703446Smrj 			    mp->old_rc_default);
71713446Smrj 			mp->old_rc_default = NULL;
71723446Smrj 			rv = BAM_WRITE;
71736448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
71743446Smrj 			goto done;
71753446Smrj 		}
71763446Smrj 
71773446Smrj 		if (optnum == KERNEL_CMD) {
71783446Smrj 			/*
71793446Smrj 			 * At this point, we've already checked that old_args
71803446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
71813446Smrj 			 * a space a the string termination character.
71823446Smrj 			 */
71833446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
71843446Smrj 			    strlen(old_args) + 2;
71853446Smrj 			new_arg = s_calloc(1, new_str_len);
71863446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
71873446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
71883446Smrj 			free(kernelp->arg);
71893446Smrj 			kernelp->arg = new_arg;
71903446Smrj 
71913446Smrj 			/*
71923446Smrj 			 * We have changed the kernel line, so we may need
71933446Smrj 			 * to update the archive line as well.
71943446Smrj 			 */
71953446Smrj 			set_archive_line(entryp, kernelp);
71966448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
71976448Svikram 			    fcn, kernelp->arg));
71983446Smrj 		} else {
71993446Smrj 			/*
72003446Smrj 			 * We're resetting the boot args to nothing, so
72013446Smrj 			 * we only need to copy the kernel.  We've already
72023446Smrj 			 * checked that the kernel is not the default.
72033446Smrj 			 */
72043446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
72053446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
72063446Smrj 			    kernelp->arg);
72073446Smrj 			free(kernelp->arg);
72083446Smrj 			kernelp->arg = new_arg;
72096448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
72106448Svikram 			    fcn, kernelp->arg));
72113446Smrj 		}
72123446Smrj 		rv = BAM_WRITE;
72133446Smrj 		goto done;
72143446Smrj 	}
72153446Smrj 
72163446Smrj 	/*
72173446Smrj 	 * Expand the kernel file to a full path, if necessary
72183446Smrj 	 */
72193446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
72203446Smrj 		new_path = expand_path(path);
72213446Smrj 		if (new_path == NULL) {
72224346Srscott 			bam_error(UNKNOWN_KERNEL, path);
72236448Svikram 			BAM_DPRINTF((D_RETURN_FAILURE, fcn));
72243446Smrj 			return (BAM_ERROR);
72253446Smrj 		}
72263446Smrj 		free_new_path = 1;
72273446Smrj 	} else {
72283446Smrj 		new_path = path;
72293446Smrj 		free_new_path = 0;
72303446Smrj 	}
72313446Smrj 
72323446Smrj 	/*
72333446Smrj 	 * At this point, we know we're setting a new value.  First, take care
72343446Smrj 	 * of the case where there was no previous entry.
72353446Smrj 	 */
72363446Smrj 	if (entryp == NULL) {
72376448Svikram 
72383446Smrj 		/* Similar to code in update_temp */
72396448Svikram 		fstype = get_fstype("/");
72406448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
72416448Svikram 		if (fstype == NULL) {
72426448Svikram 			bam_error(BOOTENV_FSTYPE_FAILED);
72436448Svikram 			rv = BAM_ERROR;
72446448Svikram 			goto done;
72456448Svikram 		}
72466448Svikram 
72476448Svikram 		osdev = get_special("/");
72486448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
72496448Svikram 		if (osdev == NULL) {
72506448Svikram 			free(fstype);
72516448Svikram 			bam_error(BOOTENV_SPECIAL_FAILED);
72523446Smrj 			rv = BAM_ERROR;
72533446Smrj 			goto done;
72543446Smrj 		}
72556448Svikram 
72566448Svikram 		sign = find_existing_sign("/", osdev, fstype);
72576448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
72586448Svikram 		if (sign == NULL) {
72596448Svikram 			free(fstype);
72606448Svikram 			free(osdev);
72616448Svikram 			bam_error(BOOTENV_SIGN_FAILED);
72626448Svikram 			rv = BAM_ERROR;
72636448Svikram 			goto done;
72646448Svikram 		}
72656448Svikram 
72666448Svikram 		free(fstype);
72676448Svikram 		free(osdev);
72686448Svikram 		(void) strlcpy(signbuf, sign, sizeof (signbuf));
72696448Svikram 		free(sign);
72706448Svikram 		assert(strchr(signbuf, '(') == NULL &&
72716448Svikram 		    strchr(signbuf, ',') == NULL &&
72726448Svikram 		    strchr(signbuf, ')') == NULL);
72736448Svikram 
72743446Smrj 		if (optnum == KERNEL_CMD) {
72756448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, new_path));
72763446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
72776448Svikram 			    signbuf, new_path, NULL, NULL);
72783446Smrj 		} else {
72793446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
72803446Smrj 			    strlen(path) + 8;
72813446Smrj 			new_arg = s_calloc(1, new_str_len);
72823446Smrj 
72833446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
72843446Smrj 			    DIRECT_BOOT_KERNEL, path);
72856448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
72863446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
72876448Svikram 			    signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
72886448Svikram 			free(new_arg);
72896448Svikram 		}
72906448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
72916448Svikram 		    entryNum = BAM_ERROR);
72926448Svikram 		if (entryNum == BAM_ERROR) {
72936448Svikram 			bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
72946448Svikram 			    BOOTENV_RC_TITLE);
72956448Svikram 			rv = BAM_ERROR;
72966448Svikram 			goto done;
72973446Smrj 		}
72983446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
72996448Svikram 		ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
73006448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
73016448Svikram 		if (ret == BAM_ERROR) {
73026448Svikram 			bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
73036448Svikram 		}
73043446Smrj 		rv = BAM_WRITE;
73053446Smrj 		goto done;
73063446Smrj 	}
73073446Smrj 
73083446Smrj 	/*
73093446Smrj 	 * There was already an bootenv entry which we need to edit.
73103446Smrj 	 */
73113446Smrj 	if (optnum == KERNEL_CMD) {
73123446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
73133446Smrj 		new_arg = s_calloc(1, new_str_len);
73143446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
73153446Smrj 		    old_args);
73163446Smrj 		free(kernelp->arg);
73173446Smrj 		kernelp->arg = new_arg;
73183446Smrj 
73193446Smrj 		/*
73203446Smrj 		 * If we have changed the kernel line, we may need to update
73213446Smrj 		 * the archive line as well.
73223446Smrj 		 */
73233446Smrj 		set_archive_line(entryp, kernelp);
73246448Svikram 		BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
73256448Svikram 		    kernelp->arg));
73263446Smrj 	} else {
73273446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
73283446Smrj 		new_arg = s_calloc(1, new_str_len);
73293446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
73303446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
73313446Smrj 		(void) strlcat(new_arg, path, new_str_len);
73323446Smrj 		free(kernelp->arg);
73333446Smrj 		kernelp->arg = new_arg;
73346448Svikram 		BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
73356448Svikram 		    kernelp->arg));
73363446Smrj 	}
73373446Smrj 	rv = BAM_WRITE;
73383446Smrj 
73393446Smrj done:
73403446Smrj 	if ((rv == BAM_WRITE) && kernelp)
73413446Smrj 		update_line(kernelp);
73423446Smrj 	if (free_new_path)
73433446Smrj 		free(new_path);
73446448Svikram 	if (rv == BAM_WRITE) {
73456448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
73466448Svikram 	} else {
73476448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
73486448Svikram 	}
73493446Smrj 	return (rv);
73503446Smrj }
73513446Smrj 
73526448Svikram static error_t
73536448Svikram get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
73546448Svikram {
73556448Svikram 	const char	*fcn = "get_kernel()";
73566448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
73576448Svikram 	return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
73586448Svikram }
73596448Svikram 
73606448Svikram static error_t
73616448Svikram set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
73626448Svikram {
73636448Svikram 	const char	*fcn = "set_kernel()";
73646448Svikram 	assert(path != NULL);
73656448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
73666448Svikram 	return (get_set_kernel(mp, optnum, path, buf, bufsize));
73676448Svikram }
73686448Svikram 
73690Sstevel@tonic-gate /*ARGSUSED*/
73700Sstevel@tonic-gate static error_t
73716448Svikram set_option(menu_t *mp, char *dummy, char *opt)
73726448Svikram {
73736448Svikram 	int		optnum;
73746448Svikram 	int		optval;
73756448Svikram 	char		*val;
73766448Svikram 	char		buf[BUFSIZ] = "";
73776448Svikram 	error_t		rv;
73786448Svikram 	const char	*fcn = "set_option()";
73790Sstevel@tonic-gate 
73800Sstevel@tonic-gate 	assert(mp);
73810Sstevel@tonic-gate 	assert(opt);
73826448Svikram 	assert(dummy == NULL);
73836448Svikram 
73846448Svikram 	/* opt is set from bam_argv[0] and is always non-NULL */
73856448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
73860Sstevel@tonic-gate 
73870Sstevel@tonic-gate 	val = strchr(opt, '=');
73883446Smrj 	if (val != NULL) {
73893446Smrj 		*val = '\0';
73900Sstevel@tonic-gate 	}
73910Sstevel@tonic-gate 
73920Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
73930Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
73940Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
73950Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
73963446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
73973446Smrj 		optnum = KERNEL_CMD;
73983446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
73993446Smrj 		optnum = ARGS_CMD;
74000Sstevel@tonic-gate 	} else {
74016448Svikram 		bam_error(INVALID_OPTION, opt);
74020Sstevel@tonic-gate 		return (BAM_ERROR);
74030Sstevel@tonic-gate 	}
74040Sstevel@tonic-gate 
74053446Smrj 	/*
74063446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
74073446Smrj 	 * others cause errors
74083446Smrj 	 */
74093446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
74106448Svikram 		bam_error(NO_OPTION_ARG, opt);
74113446Smrj 		return (BAM_ERROR);
74123446Smrj 	} else if (val != NULL) {
74133446Smrj 		*val = '=';
74143446Smrj 	}
74153446Smrj 
74163446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
74176448Svikram 		BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
74186448Svikram 		    val ? val + 1 : "NULL"));
74196448Svikram 
74206448Svikram 		if (val)
74216448Svikram 			rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
74226448Svikram 		else
74236448Svikram 			rv = get_kernel(mp, optnum, buf, sizeof (buf));
74243446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
74253446Smrj 			(void) printf("%s\n", buf);
74263446Smrj 	} else {
74273446Smrj 		optval = s_strtol(val + 1);
74286448Svikram 		BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
74296448Svikram 		rv = set_global(mp, menu_cmds[optnum], optval);
74306448Svikram 	}
74316448Svikram 
74326448Svikram 	if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
74336448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
74346448Svikram 	} else {
74356448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
74366448Svikram 	}
74376448Svikram 
74386448Svikram 	return (rv);
74390Sstevel@tonic-gate }
74400Sstevel@tonic-gate 
74410Sstevel@tonic-gate /*
74420Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
74430Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
74440Sstevel@tonic-gate  */
74450Sstevel@tonic-gate static error_t
74460Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
74470Sstevel@tonic-gate {
74480Sstevel@tonic-gate 	line_t *lp;
74490Sstevel@tonic-gate 	char *arg;
74500Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
74510Sstevel@tonic-gate 
74520Sstevel@tonic-gate 	assert(mp);
74530Sstevel@tonic-gate 	assert(menu_path);
74540Sstevel@tonic-gate 	assert(globalcmd);
74550Sstevel@tonic-gate 
74560Sstevel@tonic-gate 	if (mp->start == NULL) {
74570Sstevel@tonic-gate 		if (!quiet)
74580Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
74590Sstevel@tonic-gate 		return (BAM_ERROR);
74600Sstevel@tonic-gate 	}
74610Sstevel@tonic-gate 
74620Sstevel@tonic-gate 	done = 0;
74630Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
74640Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
74650Sstevel@tonic-gate 			continue;
74660Sstevel@tonic-gate 
74670Sstevel@tonic-gate 		if (lp->cmd == NULL) {
74680Sstevel@tonic-gate 			if (!quiet)
74690Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
74700Sstevel@tonic-gate 			continue;
74710Sstevel@tonic-gate 		}
74720Sstevel@tonic-gate 
74730Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
74740Sstevel@tonic-gate 			continue;
74750Sstevel@tonic-gate 
74760Sstevel@tonic-gate 		/* Found global. Check for duplicates */
74770Sstevel@tonic-gate 		if (done && !quiet) {
74780Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
74790Sstevel@tonic-gate 			ret = BAM_ERROR;
74800Sstevel@tonic-gate 		}
74810Sstevel@tonic-gate 
74820Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
74830Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
74840Sstevel@tonic-gate 		done = 1;
74850Sstevel@tonic-gate 	}
74860Sstevel@tonic-gate 
74870Sstevel@tonic-gate 	if (!done && bam_verbose)
74880Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
74890Sstevel@tonic-gate 
74900Sstevel@tonic-gate 	return (ret);
74910Sstevel@tonic-gate }
74920Sstevel@tonic-gate 
74930Sstevel@tonic-gate static error_t
74940Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
74950Sstevel@tonic-gate {
74966448Svikram 	const char *fcn = "menu_write()";
74976448Svikram 
74986448Svikram 	BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
74990Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
75000Sstevel@tonic-gate }
75010Sstevel@tonic-gate 
75026448Svikram void
75030Sstevel@tonic-gate line_free(line_t *lp)
75040Sstevel@tonic-gate {
75050Sstevel@tonic-gate 	if (lp == NULL)
75060Sstevel@tonic-gate 		return;
75070Sstevel@tonic-gate 
75080Sstevel@tonic-gate 	if (lp->cmd)
75090Sstevel@tonic-gate 		free(lp->cmd);
75100Sstevel@tonic-gate 	if (lp->sep)
75110Sstevel@tonic-gate 		free(lp->sep);
75120Sstevel@tonic-gate 	if (lp->arg)
75130Sstevel@tonic-gate 		free(lp->arg);
75140Sstevel@tonic-gate 	if (lp->line)
75150Sstevel@tonic-gate 		free(lp->line);
75160Sstevel@tonic-gate 	free(lp);
75170Sstevel@tonic-gate }
75180Sstevel@tonic-gate 
75190Sstevel@tonic-gate static void
75200Sstevel@tonic-gate linelist_free(line_t *start)
75210Sstevel@tonic-gate {
75220Sstevel@tonic-gate 	line_t *lp;
75230Sstevel@tonic-gate 
75240Sstevel@tonic-gate 	while (start) {
75250Sstevel@tonic-gate 		lp = start;
75260Sstevel@tonic-gate 		start = start->next;
75270Sstevel@tonic-gate 		line_free(lp);
75280Sstevel@tonic-gate 	}
75290Sstevel@tonic-gate }
75300Sstevel@tonic-gate 
75310Sstevel@tonic-gate static void
75320Sstevel@tonic-gate filelist_free(filelist_t *flistp)
75330Sstevel@tonic-gate {
75340Sstevel@tonic-gate 	linelist_free(flistp->head);
75350Sstevel@tonic-gate 	flistp->head = NULL;
75360Sstevel@tonic-gate 	flistp->tail = NULL;
75370Sstevel@tonic-gate }
75380Sstevel@tonic-gate 
75390Sstevel@tonic-gate static void
75400Sstevel@tonic-gate menu_free(menu_t *mp)
75410Sstevel@tonic-gate {
7542662Sszhou 	entry_t *ent, *tmp;
75430Sstevel@tonic-gate 	assert(mp);
75440Sstevel@tonic-gate 
75450Sstevel@tonic-gate 	if (mp->start)
75460Sstevel@tonic-gate 		linelist_free(mp->start);
7547662Sszhou 	ent = mp->entries;
7548662Sszhou 	while (ent) {
7549662Sszhou 		tmp = ent;
7550662Sszhou 		ent = tmp->next;
7551662Sszhou 		free(tmp);
7552662Sszhou 	}
7553662Sszhou 
75540Sstevel@tonic-gate 	free(mp);
75550Sstevel@tonic-gate }
75560Sstevel@tonic-gate 
75570Sstevel@tonic-gate /*
75580Sstevel@tonic-gate  * Utility routines
75590Sstevel@tonic-gate  */
75600Sstevel@tonic-gate 
75610Sstevel@tonic-gate 
75620Sstevel@tonic-gate /*
75630Sstevel@tonic-gate  * Returns 0 on success
75640Sstevel@tonic-gate  * Any other value indicates an error
75650Sstevel@tonic-gate  */
75660Sstevel@tonic-gate static int
75675648Ssetje exec_cmd(char *cmdline, filelist_t *flistp)
75680Sstevel@tonic-gate {
75690Sstevel@tonic-gate 	char buf[BUFSIZ];
75700Sstevel@tonic-gate 	int ret;
75710Sstevel@tonic-gate 	FILE *ptr;
75720Sstevel@tonic-gate 	sigset_t set;
75730Sstevel@tonic-gate 	void (*disp)(int);
75740Sstevel@tonic-gate 
75750Sstevel@tonic-gate 	/*
75760Sstevel@tonic-gate 	 * For security
75770Sstevel@tonic-gate 	 * - only absolute paths are allowed
75780Sstevel@tonic-gate 	 * - set IFS to space and tab
75790Sstevel@tonic-gate 	 */
75800Sstevel@tonic-gate 	if (*cmdline != '/') {
75810Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
75820Sstevel@tonic-gate 		return (-1);
75830Sstevel@tonic-gate 	}
75840Sstevel@tonic-gate 	(void) putenv("IFS= \t");
75850Sstevel@tonic-gate 
75860Sstevel@tonic-gate 	/*
75870Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
75880Sstevel@tonic-gate 	 * unblock it here
75890Sstevel@tonic-gate 	 */
75900Sstevel@tonic-gate 	(void) sigemptyset(&set);
75910Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
75920Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
75930Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
75940Sstevel@tonic-gate 		return (-1);
75950Sstevel@tonic-gate 	}
75960Sstevel@tonic-gate 
75970Sstevel@tonic-gate 	/*
75980Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
75990Sstevel@tonic-gate 	 */
76000Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
76010Sstevel@tonic-gate 	if (disp == SIG_ERR) {
76020Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
76030Sstevel@tonic-gate 		return (-1);
76040Sstevel@tonic-gate 	}
76050Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
76060Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
76070Sstevel@tonic-gate 		return (-1);
76080Sstevel@tonic-gate 	}
76090Sstevel@tonic-gate 
76100Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
76110Sstevel@tonic-gate 	if (ptr == NULL) {
76120Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
76130Sstevel@tonic-gate 		return (-1);
76140Sstevel@tonic-gate 	}
76150Sstevel@tonic-gate 
76160Sstevel@tonic-gate 	/*
76170Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
76180Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
76190Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
76200Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
76210Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
76220Sstevel@tonic-gate 	 * there is no reader process and the command dies with
76230Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
76240Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
76250Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
76260Sstevel@tonic-gate 	 * pclose().
76270Sstevel@tonic-gate 	 *
76280Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
76290Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
76300Sstevel@tonic-gate 	 * from the value returned by pclose()
76310Sstevel@tonic-gate 	 */
76325648Ssetje 	while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
76335648Ssetje 		if (flistp == NULL) {
76345648Ssetje 			/* s_fgets strips newlines, so insert them at the end */
76355648Ssetje 			bam_print(PRINT, buf);
76365648Ssetje 		} else {
76375648Ssetje 			append_to_flist(flistp, buf);
76380Sstevel@tonic-gate 		}
76390Sstevel@tonic-gate 	}
76400Sstevel@tonic-gate 
76410Sstevel@tonic-gate 	ret = pclose(ptr);
76420Sstevel@tonic-gate 	if (ret == -1) {
76430Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
76440Sstevel@tonic-gate 		return (-1);
76450Sstevel@tonic-gate 	}
76460Sstevel@tonic-gate 
76470Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
76480Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
76490Sstevel@tonic-gate 	} else {
76500Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
76510Sstevel@tonic-gate 		return (-1);
76520Sstevel@tonic-gate 	}
76530Sstevel@tonic-gate }
76540Sstevel@tonic-gate 
76550Sstevel@tonic-gate /*
76560Sstevel@tonic-gate  * Since this function returns -1 on error
76570Sstevel@tonic-gate  * it cannot be used to convert -1. However,
76580Sstevel@tonic-gate  * that is sufficient for what we need.
76590Sstevel@tonic-gate  */
76600Sstevel@tonic-gate static long
76610Sstevel@tonic-gate s_strtol(char *str)
76620Sstevel@tonic-gate {
76630Sstevel@tonic-gate 	long l;
76640Sstevel@tonic-gate 	char *res = NULL;
76650Sstevel@tonic-gate 
76660Sstevel@tonic-gate 	if (str == NULL) {
76670Sstevel@tonic-gate 		return (-1);
76680Sstevel@tonic-gate 	}
76690Sstevel@tonic-gate 
76700Sstevel@tonic-gate 	errno = 0;
76710Sstevel@tonic-gate 	l = strtol(str, &res, 10);
76720Sstevel@tonic-gate 	if (errno || *res != '\0') {
76730Sstevel@tonic-gate 		return (-1);
76740Sstevel@tonic-gate 	}
76750Sstevel@tonic-gate 
76760Sstevel@tonic-gate 	return (l);
76770Sstevel@tonic-gate }
76780Sstevel@tonic-gate 
76790Sstevel@tonic-gate /*
76800Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
76810Sstevel@tonic-gate  */
76820Sstevel@tonic-gate static int
76830Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
76840Sstevel@tonic-gate {
76850Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
76860Sstevel@tonic-gate 
76870Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
76880Sstevel@tonic-gate 	return (fputs(linebuf, fp));
76890Sstevel@tonic-gate }
76900Sstevel@tonic-gate 
76910Sstevel@tonic-gate /*
76920Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
76930Sstevel@tonic-gate  */
76943446Smrj char *
76950Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
76960Sstevel@tonic-gate {
76970Sstevel@tonic-gate 	int n;
76980Sstevel@tonic-gate 
76990Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
77000Sstevel@tonic-gate 	if (buf) {
77010Sstevel@tonic-gate 		n = strlen(buf);
77020Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
77030Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
77040Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
77050Sstevel@tonic-gate 	}
77060Sstevel@tonic-gate 
77070Sstevel@tonic-gate 	return (buf);
77080Sstevel@tonic-gate }
77090Sstevel@tonic-gate 
77103446Smrj void *
77110Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
77120Sstevel@tonic-gate {
77130Sstevel@tonic-gate 	void *ptr;
77140Sstevel@tonic-gate 
77150Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
77160Sstevel@tonic-gate 	if (ptr == NULL) {
77170Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
77180Sstevel@tonic-gate 		bam_exit(1);
77190Sstevel@tonic-gate 	}
77200Sstevel@tonic-gate 	return (ptr);
77210Sstevel@tonic-gate }
77220Sstevel@tonic-gate 
77233446Smrj void *
77243446Smrj s_realloc(void *ptr, size_t sz)
77253446Smrj {
77263446Smrj 	ptr = realloc(ptr, sz);
77273446Smrj 	if (ptr == NULL) {
77283446Smrj 		bam_error(NO_MEM, sz);
77293446Smrj 		bam_exit(1);
77303446Smrj 	}
77313446Smrj 	return (ptr);
77323446Smrj }
77333446Smrj 
77346448Svikram char *
77350Sstevel@tonic-gate s_strdup(char *str)
77360Sstevel@tonic-gate {
77370Sstevel@tonic-gate 	char *ptr;
77380Sstevel@tonic-gate 
77390Sstevel@tonic-gate 	if (str == NULL)
77400Sstevel@tonic-gate 		return (NULL);
77410Sstevel@tonic-gate 
77420Sstevel@tonic-gate 	ptr = strdup(str);
77430Sstevel@tonic-gate 	if (ptr == NULL) {
77440Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
77450Sstevel@tonic-gate 		bam_exit(1);
77460Sstevel@tonic-gate 	}
77470Sstevel@tonic-gate 	return (ptr);
77480Sstevel@tonic-gate }
77490Sstevel@tonic-gate 
77500Sstevel@tonic-gate /*
77510Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
77520Sstevel@tonic-gate  * Returns 0 otherwise
77530Sstevel@tonic-gate  */
77540Sstevel@tonic-gate static int
77550Sstevel@tonic-gate is_amd64(void)
77560Sstevel@tonic-gate {
77570Sstevel@tonic-gate 	static int amd64 = -1;
77580Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
77590Sstevel@tonic-gate 
77600Sstevel@tonic-gate 	if (amd64 != -1)
77610Sstevel@tonic-gate 		return (amd64);
77620Sstevel@tonic-gate 
77636319Sjg 	if (bam_alt_platform) {
77646319Sjg 		if (strcmp(bam_platform, "i86pc") == 0) {
77656319Sjg 			amd64 = 1;		/* diskless server */
77666319Sjg 		}
77676319Sjg 	} else {
77686319Sjg 		if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
77696319Sjg 		    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
77706319Sjg 			amd64 = 1;
77716319Sjg 		} else if (strstr(isabuf, "i386") == NULL) {
77726319Sjg 			amd64 = 1;		/* diskless server */
77736319Sjg 		}
77746319Sjg 	}
77756319Sjg 	if (amd64 == -1)
77760Sstevel@tonic-gate 		amd64 = 0;
77770Sstevel@tonic-gate 
77780Sstevel@tonic-gate 	return (amd64);
77790Sstevel@tonic-gate }
77800Sstevel@tonic-gate 
77816582Ssetje static char *
77826582Ssetje get_machine(void)
77836582Ssetje {
77846582Ssetje 	static int cached = -1;
77856582Ssetje 	static char mbuf[257];	/* from sysinfo(2) manpage */
77866582Ssetje 
77876582Ssetje 	if (cached == 0)
77886582Ssetje 		return (mbuf);
77896319Sjg 
77906319Sjg 	if (bam_alt_platform) {
77916582Ssetje 		return (bam_platform);
77926319Sjg 	} else {
77936582Ssetje 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
77946582Ssetje 			cached = 1;
77956582Ssetje 		}
77966582Ssetje 	}
77976582Ssetje 	if (cached == -1) {
77986582Ssetje 		mbuf[0] = '\0';
77996582Ssetje 		cached = 0;
78006582Ssetje 	}
78016582Ssetje 
78026582Ssetje 	return (mbuf);
78035648Ssetje }
78045648Ssetje 
78056448Svikram int
78066448Svikram is_sparc(void)
78076448Svikram {
78086582Ssetje 	static int issparc = -1;
78096582Ssetje 	char mbuf[257];	/* from sysinfo(2) manpage */
78106582Ssetje 
78116582Ssetje 	if (issparc != -1)
78126582Ssetje 		return (issparc);
78136582Ssetje 
78146582Ssetje 	if (bam_alt_platform) {
78156582Ssetje 		if (strncmp(bam_platform, "sun4", 4) == 0) {
78166582Ssetje 			issparc = 1;
78176582Ssetje 		}
78186582Ssetje 	} else {
78196582Ssetje 		if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
7820*7111Sjg 		    strcmp(mbuf, "sparc") == 0) {
78216582Ssetje 			issparc = 1;
7822*7111Sjg 		}
7823*7111Sjg 	}
7824*7111Sjg 	if (issparc == -1)
7825*7111Sjg 		issparc = 0;
78266582Ssetje 
78276582Ssetje 	return (issparc);
78286448Svikram }
78295648Ssetje 
78300Sstevel@tonic-gate static void
78310Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
78320Sstevel@tonic-gate {
78330Sstevel@tonic-gate 	line_t *lp;
78340Sstevel@tonic-gate 
78350Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
78360Sstevel@tonic-gate 	lp->line = s_strdup(s);
78370Sstevel@tonic-gate 	if (flistp->head == NULL)
78380Sstevel@tonic-gate 		flistp->head = lp;
78390Sstevel@tonic-gate 	else
78400Sstevel@tonic-gate 		flistp->tail->next = lp;
78410Sstevel@tonic-gate 	flistp->tail = lp;
78420Sstevel@tonic-gate }
78434581Ssherrym 
78445648Ssetje #if !defined(_OPB)
78454581Ssherrym 
78464581Ssherrym UCODE_VENDORS;
78474581Ssherrym 
78484581Ssherrym /*ARGSUSED*/
78494581Ssherrym static void
78504581Ssherrym ucode_install(char *root)
78514581Ssherrym {
78524581Ssherrym 	int i;
78534581Ssherrym 
78544581Ssherrym 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
78554581Ssherrym 		int cmd_len = PATH_MAX + 256;
78564581Ssherrym 		char cmd[PATH_MAX + 256];
78574581Ssherrym 		char file[PATH_MAX];
78584581Ssherrym 		char timestamp[PATH_MAX];
78594581Ssherrym 		struct stat fstatus, tstatus;
78604581Ssherrym 		struct utimbuf u_times;
78614581Ssherrym 
78624581Ssherrym 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt",
78634581Ssherrym 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr);
78644581Ssherrym 
78654581Ssherrym 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
78664581Ssherrym 			continue;
78674581Ssherrym 
78684581Ssherrym 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
78694581Ssherrym 
78704581Ssherrym 		if (stat(timestamp, &tstatus) == 0 &&
78714581Ssherrym 		    fstatus.st_mtime <= tstatus.st_mtime)
78724581Ssherrym 			continue;
78734581Ssherrym 
78744581Ssherrym 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
78754581Ssherrym 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
78764581Ssherrym 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
78774581Ssherrym 		if (system(cmd) != 0)
78784581Ssherrym 			return;
78794581Ssherrym 
78804581Ssherrym 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
78814581Ssherrym 			return;
78824581Ssherrym 
78834581Ssherrym 		u_times.actime = fstatus.st_atime;
78844581Ssherrym 		u_times.modtime = fstatus.st_mtime;
78854581Ssherrym 		(void) utime(timestamp, &u_times);
78864581Ssherrym 	}
78874581Ssherrym }
78884581Ssherrym #endif
7889