xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 9500:1c35c22e01f3)
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 /*
228642SVikram.Hegde@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * bootadm(1M) is a new utility for managing bootability of
280Sstevel@tonic-gate  * Solaris *Newboot* environments. It has two primary tasks:
290Sstevel@tonic-gate  * 	- Allow end users to manage bootability of Newboot Solaris instances
300Sstevel@tonic-gate  *	- Provide services to other subsystems in Solaris (primarily Install)
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate /* Headers */
340Sstevel@tonic-gate #include <stdio.h>
350Sstevel@tonic-gate #include <errno.h>
360Sstevel@tonic-gate #include <stdlib.h>
370Sstevel@tonic-gate #include <string.h>
380Sstevel@tonic-gate #include <unistd.h>
390Sstevel@tonic-gate #include <sys/types.h>
400Sstevel@tonic-gate #include <sys/stat.h>
410Sstevel@tonic-gate #include <stdarg.h>
420Sstevel@tonic-gate #include <limits.h>
430Sstevel@tonic-gate #include <signal.h>
440Sstevel@tonic-gate #include <sys/wait.h>
450Sstevel@tonic-gate #include <sys/mnttab.h>
467543SJerry.Gilliam@Sun.COM #include <sys/mntent.h>
470Sstevel@tonic-gate #include <sys/statvfs.h>
480Sstevel@tonic-gate #include <libnvpair.h>
490Sstevel@tonic-gate #include <ftw.h>
500Sstevel@tonic-gate #include <fcntl.h>
510Sstevel@tonic-gate #include <strings.h>
524581Ssherrym #include <utime.h>
530Sstevel@tonic-gate #include <sys/systeminfo.h>
540Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
552334Ssetje #include <sys/param.h>
566448Svikram #include <dirent.h>
576448Svikram #include <ctype.h>
586448Svikram #include <libgen.h>
596423Sgw25295 #include <sys/sysmacros.h>
608735SEnrico.Perla@Sun.COM #include <sys/elf.h>
616694Svikram #include <libscf.h>
628735SEnrico.Perla@Sun.COM #include <zlib.h>
638735SEnrico.Perla@Sun.COM #include <sys/lockfs.h>
648735SEnrico.Perla@Sun.COM #include <sys/filio.h>
655648Ssetje 
665648Ssetje #if !defined(_OPB)
674581Ssherrym #include <sys/ucode.h>
684581Ssherrym #endif
690Sstevel@tonic-gate 
700Sstevel@tonic-gate #include <pwd.h>
710Sstevel@tonic-gate #include <grp.h>
720Sstevel@tonic-gate #include <device_info.h>
736448Svikram #include <sys/vtoc.h>
746448Svikram #include <sys/efi_partition.h>
75*9500SGangadhar.M@Sun.COM #include <regex.h>
760Sstevel@tonic-gate #include <locale.h>
770Sstevel@tonic-gate 
780Sstevel@tonic-gate #include "message.h"
793446Smrj #include "bootadm.h"
800Sstevel@tonic-gate 
810Sstevel@tonic-gate #ifndef TEXT_DOMAIN
820Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
830Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate /* Type definitions */
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /* Primary subcmds */
880Sstevel@tonic-gate typedef enum {
890Sstevel@tonic-gate 	BAM_MENU = 3,
900Sstevel@tonic-gate 	BAM_ARCHIVE
910Sstevel@tonic-gate } subcmd_t;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate typedef enum {
940Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
950Sstevel@tonic-gate     OPT_REQ,		/* option required */
960Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
970Sstevel@tonic-gate } option_t;
980Sstevel@tonic-gate 
990Sstevel@tonic-gate typedef struct {
1000Sstevel@tonic-gate 	char	*subcmd;
1010Sstevel@tonic-gate 	option_t option;
1020Sstevel@tonic-gate 	error_t (*handler)();
1032115Svikram 	int	unpriv;			/* is this an unprivileged command */
1040Sstevel@tonic-gate } subcmd_defn_t;
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
1070Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
1080Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
1116694Svikram #define	GRUB_STAGE2		GRUB_DIR "/stage2"
1120Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
1130Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1146694Svikram #define	GRUB_BACKUP_MENU	"/etc/lu/GRUB_backup_menu"
1150Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
116621Svikram #define	STUBBOOT		"/stubboot"
1176448Svikram #define	MULTIBOOT		"/platform/i86pc/multiboot"
1186448Svikram #define	GRUBSIGN_DIR		"/boot/grub/bootsign"
1196448Svikram #define	GRUBSIGN_BACKUP		"/etc/bootsign"
1206448Svikram #define	GRUBSIGN_UFS_PREFIX	"rootfs"
1216448Svikram #define	GRUBSIGN_ZFS_PREFIX	"pool_"
1226448Svikram #define	GRUBSIGN_LU_PREFIX	"BE_"
1236448Svikram #define	UFS_SIGNATURE_LIST	"/var/run/grub_ufs_signatures"
1246448Svikram #define	ZFS_LEGACY_MNTPT	"/tmp/bootadm_mnt_zfs_legacy"
1256448Svikram 
1266448Svikram #define	BOOTADM_RDONLY_TEST	"BOOTADM_RDONLY_TEST"
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate /* lock related */
1290Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1300Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1310Sstevel@tonic-gate 
1325648Ssetje #define	CREATE_RAMDISK		"boot/solaris/bin/create_ramdisk"
1335648Ssetje #define	CREATE_DISKMAP		"boot/solaris/bin/create_diskmap"
1345648Ssetje #define	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
1350Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
1360Sstevel@tonic-gate 
137316Svikram #define	GRUB_slice		"/etc/lu/GRUB_slice"
138316Svikram #define	GRUB_root		"/etc/lu/GRUB_root"
1391746Svikram #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
1401746Svikram #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
1416694Svikram #define	FINDROOT_INSTALLGRUB	"/etc/lu/installgrub.findroot"
1426694Svikram #define	LULIB			"/usr/lib/lu/lulib"
1436694Svikram #define	LULIB_PROPAGATE_FILE	"lulib_propagate_file"
1446694Svikram #define	CKSUM			"/usr/bin/cksum"
1456694Svikram #define	LU_MENU_CKSUM		"/etc/lu/menu.cksum"
1466694Svikram #define	BOOTADM			"/sbin/bootadm"
147316Svikram 
148316Svikram #define	INSTALLGRUB		"/sbin/installgrub"
149316Svikram #define	STAGE1			"/boot/grub/stage1"
150316Svikram #define	STAGE2			"/boot/grub/stage2"
151316Svikram 
1526448Svikram typedef enum zfs_mnted {
1536448Svikram 	ZFS_MNT_ERROR = -1,
1546448Svikram 	LEGACY_MOUNTED = 1,
1556448Svikram 	LEGACY_ALREADY,
1566448Svikram 	ZFS_MOUNTED,
1576448Svikram 	ZFS_ALREADY
1586448Svikram } zfs_mnted_t;
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 #define	LOFS_PATCH_FILE		"/var/run/.patch_loopback_mode"
1684493Snadkarni #define	LOFS_PATCH_MNT		"/var/run/.patch_root_loopbackmnt"
1694493Snadkarni 
1704493Snadkarni /*
1710Sstevel@tonic-gate  * Default file attributes
1720Sstevel@tonic-gate  */
1730Sstevel@tonic-gate #define	DEFAULT_DEV_MODE	0644	/* default permissions */
1740Sstevel@tonic-gate #define	DEFAULT_DEV_UID		0	/* user root */
1750Sstevel@tonic-gate #define	DEFAULT_DEV_GID		3	/* group sys */
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate /*
1780Sstevel@tonic-gate  * Menu related
1790Sstevel@tonic-gate  * menu_cmd_t and menu_cmds must be kept in sync
1800Sstevel@tonic-gate  */
1813446Smrj char *menu_cmds[] = {
1820Sstevel@tonic-gate 	"default",	/* DEFAULT_CMD */
1830Sstevel@tonic-gate 	"timeout",	/* TIMEOUT_CMD */
1840Sstevel@tonic-gate 	"title",	/* TITLE_CMD */
1850Sstevel@tonic-gate 	"root",		/* ROOT_CMD */
1860Sstevel@tonic-gate 	"kernel",	/* KERNEL_CMD */
1873446Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1880Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
1893446Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1900Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1910Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
1923446Smrj 	"chainloader",	/* CHAINLOADER_CMD */
1933446Smrj 	"args",		/* ARGS_CMD */
1946448Svikram 	"findroot",	/* FINDROOT_CMD */
1950Sstevel@tonic-gate 	NULL
1960Sstevel@tonic-gate };
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate #define	OPT_ENTRY_NUM	"entry"
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate /*
2016448Svikram  * exec_cmd related
2020Sstevel@tonic-gate  */
2030Sstevel@tonic-gate typedef struct {
2040Sstevel@tonic-gate 	line_t *head;
2050Sstevel@tonic-gate 	line_t *tail;
2060Sstevel@tonic-gate } filelist_t;
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
2090Sstevel@tonic-gate #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
2120Sstevel@tonic-gate #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
2130Sstevel@tonic-gate #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
2140Sstevel@tonic-gate #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
2150Sstevel@tonic-gate 
2169233SEnrico.Perla@Sun.COM #define	FILE_STAT_TIMESTAMP	"boot/solaris/timestamp.cache"
2179233SEnrico.Perla@Sun.COM 
2180Sstevel@tonic-gate /* Globals */
2193446Smrj int bam_verbose;
2203446Smrj int bam_force;
2216448Svikram int bam_debug;
2220Sstevel@tonic-gate static char *prog;
2230Sstevel@tonic-gate static subcmd_t bam_cmd;
2240Sstevel@tonic-gate static char *bam_root;
2250Sstevel@tonic-gate static int bam_rootlen;
2260Sstevel@tonic-gate static int bam_root_readonly;
227621Svikram static int bam_alt_root;
2288735SEnrico.Perla@Sun.COM static int bam_extend = 0;
2298735SEnrico.Perla@Sun.COM static int bam_purge = 0;
2300Sstevel@tonic-gate static char *bam_subcmd;
2310Sstevel@tonic-gate static char *bam_opt;
2320Sstevel@tonic-gate static char **bam_argv;
2330Sstevel@tonic-gate static int bam_argc;
2340Sstevel@tonic-gate static int bam_check;
2359216SEnrico.Perla@Sun.COM static int bam_saved_check;
2360Sstevel@tonic-gate static int bam_smf_check;
2370Sstevel@tonic-gate static int bam_lock_fd = -1;
2386423Sgw25295 static int bam_zfs;
2390Sstevel@tonic-gate static char rootbuf[PATH_MAX] = "/";
240316Svikram static int bam_update_all;
2416319Sjg static int bam_alt_platform;
2426319Sjg static char *bam_platform;
2438954SEnrico.Perla@Sun.COM static char *bam_home_env = NULL;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate /* function prototypes */
2465648Ssetje static void parse_args_internal(int, char *[]);
2475648Ssetje static void parse_args(int, char *argv[]);
2485648Ssetje static error_t bam_menu(char *, char *, int, char *[]);
2495648Ssetje static error_t bam_archive(char *, char *);
2505648Ssetje 
2515648Ssetje static void bam_exit(int);
2520Sstevel@tonic-gate static void bam_lock(void);
2530Sstevel@tonic-gate static void bam_unlock(void);
2540Sstevel@tonic-gate 
2555648Ssetje static int exec_cmd(char *, filelist_t *);
2565648Ssetje static error_t read_globals(menu_t *, char *, char *, int);
2576448Svikram static int menu_on_bootdisk(char *os_root, char *menu_root);
2585648Ssetje static menu_t *menu_read(char *);
2595648Ssetje static error_t menu_write(char *, menu_t *);
2605648Ssetje static void linelist_free(line_t *);
2615648Ssetje static void menu_free(menu_t *);
2625648Ssetje static void filelist_free(filelist_t *);
2635648Ssetje static error_t list2file(char *, char *, char *, line_t *);
2645648Ssetje static error_t list_entry(menu_t *, char *, char *);
2655648Ssetje static error_t delete_all_entries(menu_t *, char *, char *);
2666448Svikram static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
2676448Svikram static error_t update_temp(menu_t *mp, char *dummy, char *opt);
2685648Ssetje 
2695648Ssetje static error_t update_archive(char *, char *);
2705648Ssetje static error_t list_archive(char *, char *);
2715648Ssetje static error_t update_all(char *, char *);
2725648Ssetje static error_t read_list(char *, filelist_t *);
2735648Ssetje static error_t set_global(menu_t *, char *, int);
2745648Ssetje static error_t set_option(menu_t *, char *, char *);
2755648Ssetje static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
2766448Svikram static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
2775648Ssetje static char *expand_path(const char *);
2785648Ssetje 
2795648Ssetje static long s_strtol(char *);
2805648Ssetje static int s_fputs(char *, FILE *);
2815648Ssetje 
2826448Svikram static int is_zfs(char *root);
2836448Svikram static int is_ufs(char *root);
2846448Svikram static int is_pcfs(char *root);
2850Sstevel@tonic-gate static int is_amd64(void);
2866582Ssetje static char *get_machine(void);
2870Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2886448Svikram static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted);
2896448Svikram static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt);
2906448Svikram static int ufs_add_to_sign_list(char *sign);
2916694Svikram static error_t synchronize_BE_menu(void);
2920Sstevel@tonic-gate 
2935648Ssetje #if !defined(_OPB)
2944581Ssherrym static void ucode_install();
2954581Ssherrym #endif
2964581Ssherrym 
2970Sstevel@tonic-gate /* Menu related sub commands */
2980Sstevel@tonic-gate static subcmd_defn_t menu_subcmds[] = {
2996448Svikram 	"set_option",		OPT_ABSENT,	set_option, 0,	/* PUB */
3002115Svikram 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
3012115Svikram 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
3022115Svikram 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
3032115Svikram 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
3043446Smrj 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
3052115Svikram 	NULL,			0,		NULL, 0	/* must be last */
3060Sstevel@tonic-gate };
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate /* Archive related sub commands */
3090Sstevel@tonic-gate static subcmd_defn_t arch_subcmds[] = {
3102115Svikram 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
3112115Svikram 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
3122115Svikram 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
3132115Svikram 	NULL,			0,		NULL, 0	/* must be last */
3140Sstevel@tonic-gate };
3150Sstevel@tonic-gate 
3168735SEnrico.Perla@Sun.COM enum dircache_copy_opt {
3178735SEnrico.Perla@Sun.COM 	FILE32 = 0,
3188735SEnrico.Perla@Sun.COM 	FILE64,
3198735SEnrico.Perla@Sun.COM 	CACHEDIR_NUM
3208735SEnrico.Perla@Sun.COM };
3218735SEnrico.Perla@Sun.COM 
3228735SEnrico.Perla@Sun.COM /*
3238735SEnrico.Perla@Sun.COM  * Directory specific flags:
3248735SEnrico.Perla@Sun.COM  * NEED_UPDATE : the specified archive needs to be updated
3258735SEnrico.Perla@Sun.COM  * NO_MULTI : don't extend the specified archive, but recreate it
3268735SEnrico.Perla@Sun.COM  */
3278735SEnrico.Perla@Sun.COM #define	NEED_UPDATE		0x00000001
3288735SEnrico.Perla@Sun.COM #define	NO_MULTI		0x00000002
3298735SEnrico.Perla@Sun.COM 
3308735SEnrico.Perla@Sun.COM #define	set_dir_flag(id, f)	(walk_arg.dirinfo[id].flags |= f)
3318735SEnrico.Perla@Sun.COM #define	unset_dir_flag(id, f)	(walk_arg.dirinfo[id].flags &= ~f)
3328735SEnrico.Perla@Sun.COM #define	is_dir_flag_on(id, f)	(walk_arg.dirinfo[id].flags & f ? 1 : 0)
3338735SEnrico.Perla@Sun.COM 
3348735SEnrico.Perla@Sun.COM #define	get_cachedir(id)	(walk_arg.dirinfo[id].cdir_path)
3358735SEnrico.Perla@Sun.COM #define	get_updatedir(id)	(walk_arg.dirinfo[id].update_path)
3368735SEnrico.Perla@Sun.COM #define	get_count(id)		(walk_arg.dirinfo[id].count)
3378735SEnrico.Perla@Sun.COM #define	has_cachedir(id)	(walk_arg.dirinfo[id].has_dir)
3388735SEnrico.Perla@Sun.COM #define	set_dir_present(id)	(walk_arg.dirinfo[id].has_dir = 1)
3398735SEnrico.Perla@Sun.COM 
3408735SEnrico.Perla@Sun.COM /*
3418735SEnrico.Perla@Sun.COM  * dirinfo_t (specific cache directory information):
3428735SEnrico.Perla@Sun.COM  * cdir_path:   path to the archive cache directory
3438735SEnrico.Perla@Sun.COM  * update_path: path to the update directory (contains the files that will be
3448735SEnrico.Perla@Sun.COM  *              used to extend the archive)
3458735SEnrico.Perla@Sun.COM  * has_dir:	the specified cache directory is active
3468735SEnrico.Perla@Sun.COM  * count:	the number of files to update
3478735SEnrico.Perla@Sun.COM  * flags:	directory specific flags
3488735SEnrico.Perla@Sun.COM  */
3498735SEnrico.Perla@Sun.COM typedef struct _dirinfo {
3508735SEnrico.Perla@Sun.COM 	char	cdir_path[PATH_MAX];
3518735SEnrico.Perla@Sun.COM 	char	update_path[PATH_MAX];
3528735SEnrico.Perla@Sun.COM 	int	has_dir;
3538735SEnrico.Perla@Sun.COM 	int	count;
3548735SEnrico.Perla@Sun.COM 	int	flags;
3558735SEnrico.Perla@Sun.COM } dirinfo_t;
3568735SEnrico.Perla@Sun.COM 
3578735SEnrico.Perla@Sun.COM /*
3588735SEnrico.Perla@Sun.COM  * Update flags:
3598735SEnrico.Perla@Sun.COM  * NEED_CACHE_DIR : cache directory is missing and needs to be created
3608735SEnrico.Perla@Sun.COM  * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
3618735SEnrico.Perla@Sun.COM  * UPDATE_ERROR : an error occourred while traversing the list of files
3628735SEnrico.Perla@Sun.COM  * RDONLY_FSCHK : the target filesystem is read-only
3638735SEnrico.Perla@Sun.COM  * RAMDSK_FSCHK : the target filesystem is on a ramdisk
3648735SEnrico.Perla@Sun.COM  */
3658735SEnrico.Perla@Sun.COM #define	NEED_CACHE_DIR		0x00000001
3668735SEnrico.Perla@Sun.COM #define	IS_SPARC_TARGET		0x00000002
3678735SEnrico.Perla@Sun.COM #define	UPDATE_ERROR		0x00000004
3688735SEnrico.Perla@Sun.COM #define	RDONLY_FSCHK		0x00000008
3699233SEnrico.Perla@Sun.COM #define	INVALIDATE_CACHE	0x00000010
3708735SEnrico.Perla@Sun.COM 
3718735SEnrico.Perla@Sun.COM #define	is_flag_on(flag)	(walk_arg.update_flags & flag ? 1 : 0)
3728735SEnrico.Perla@Sun.COM #define	set_flag(flag)		(walk_arg.update_flags |= flag)
3738735SEnrico.Perla@Sun.COM #define	unset_flag(flag)	(walk_arg.update_flags &= ~flag)
3748735SEnrico.Perla@Sun.COM 
3758735SEnrico.Perla@Sun.COM /*
3768735SEnrico.Perla@Sun.COM  * struct walk_arg :
3778735SEnrico.Perla@Sun.COM  * update_flags: flags related to the current updating process
3788735SEnrico.Perla@Sun.COM  * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
3798735SEnrico.Perla@Sun.COM  * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
3808735SEnrico.Perla@Sun.COM  */
3810Sstevel@tonic-gate static struct {
3828735SEnrico.Perla@Sun.COM 	int 		update_flags;
3838735SEnrico.Perla@Sun.COM 	nvlist_t 	*new_nvlp;
3848735SEnrico.Perla@Sun.COM 	nvlist_t 	*old_nvlp;
3858735SEnrico.Perla@Sun.COM 	FILE 		*sparcfile;
3868735SEnrico.Perla@Sun.COM 	dirinfo_t	dirinfo[CACHEDIR_NUM];
3870Sstevel@tonic-gate } walk_arg;
3880Sstevel@tonic-gate 
3892345Ssetje struct safefile {
3902334Ssetje 	char *name;
3912334Ssetje 	struct safefile *next;
3922334Ssetje };
3932334Ssetje 
3943446Smrj static struct safefile *safefiles = NULL;
3952334Ssetje #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
3962334Ssetje 
3978735SEnrico.Perla@Sun.COM /* Thanks growisofs */
3988735SEnrico.Perla@Sun.COM #define	CD_BLOCK	((off64_t)2048)
3998735SEnrico.Perla@Sun.COM #define	VOLDESC_OFF	16
4008735SEnrico.Perla@Sun.COM #define	DVD_BLOCK	(32*1024)
4018735SEnrico.Perla@Sun.COM #define	MAX_IVDs	16
4028735SEnrico.Perla@Sun.COM 
4038735SEnrico.Perla@Sun.COM struct iso_pdesc {
4048735SEnrico.Perla@Sun.COM     unsigned char type	[1];
4058735SEnrico.Perla@Sun.COM     unsigned char id	[5];
4068735SEnrico.Perla@Sun.COM     unsigned char void1	[80-5-1];
4078735SEnrico.Perla@Sun.COM     unsigned char volume_space_size [8];
4088735SEnrico.Perla@Sun.COM     unsigned char void2	[2048-80-8];
4098735SEnrico.Perla@Sun.COM };
4108735SEnrico.Perla@Sun.COM 
4118735SEnrico.Perla@Sun.COM /*
4128735SEnrico.Perla@Sun.COM  * COUNT_MAX:	maximum number of changed files to justify a multisession update
4138735SEnrico.Perla@Sun.COM  * BA_SIZE_MAX:	maximum size of the boot_archive to justify a multisession
4148735SEnrico.Perla@Sun.COM  * 		update
4158735SEnrico.Perla@Sun.COM  */
4168735SEnrico.Perla@Sun.COM #define	COUNT_MAX		50
4178735SEnrico.Perla@Sun.COM #define	BA_SIZE_MAX		(50 * 1024 * 1024)
4188735SEnrico.Perla@Sun.COM 
4198735SEnrico.Perla@Sun.COM #define	bam_nowrite()		(bam_check || bam_smf_check)
4208735SEnrico.Perla@Sun.COM 
4217656SSherry.Moore@Sun.COM static int sync_menu = 1;	/* whether we need to sync the BE menus */
4227656SSherry.Moore@Sun.COM 
4230Sstevel@tonic-gate static void
4240Sstevel@tonic-gate usage(void)
4250Sstevel@tonic-gate {
4260Sstevel@tonic-gate 	(void) fprintf(stderr, "USAGE:\n");
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	/* archive usage */
4296319Sjg 	(void) fprintf(stderr,
4306319Sjg 	    "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog);
4316319Sjg 	(void) fprintf(stderr,
4326319Sjg 	    "\t%s list-archive [-R altroot [-p platform>]]\n", prog);
4335648Ssetje #if !defined(_OPB)
4340Sstevel@tonic-gate 	/* x86 only */
4350Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
4360Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
4370Sstevel@tonic-gate #endif
4380Sstevel@tonic-gate }
4390Sstevel@tonic-gate 
4408954SEnrico.Perla@Sun.COM /*
4418954SEnrico.Perla@Sun.COM  * Best effort attempt to restore the $HOME value.
4428954SEnrico.Perla@Sun.COM  */
4438954SEnrico.Perla@Sun.COM static void
4448954SEnrico.Perla@Sun.COM restore_env()
4458954SEnrico.Perla@Sun.COM {
4468954SEnrico.Perla@Sun.COM 	char	home_env[PATH_MAX];
4478954SEnrico.Perla@Sun.COM 
4488954SEnrico.Perla@Sun.COM 	if (bam_home_env) {
4498954SEnrico.Perla@Sun.COM 		(void) snprintf(home_env, sizeof (home_env), "HOME=%s",
4508954SEnrico.Perla@Sun.COM 		    bam_home_env);
4518954SEnrico.Perla@Sun.COM 		(void) putenv(home_env);
4528954SEnrico.Perla@Sun.COM 	}
4538954SEnrico.Perla@Sun.COM }
4548954SEnrico.Perla@Sun.COM 
4558954SEnrico.Perla@Sun.COM 
4568954SEnrico.Perla@Sun.COM #define		SLEEP_TIME	5
4578954SEnrico.Perla@Sun.COM #define		MAX_TRIES	4
4588954SEnrico.Perla@Sun.COM 
4598954SEnrico.Perla@Sun.COM /*
4608954SEnrico.Perla@Sun.COM  * Sanitize the environment in which bootadm will execute its sub-processes
4618954SEnrico.Perla@Sun.COM  * (ex. mkisofs). This is done to prevent those processes from attempting
4628954SEnrico.Perla@Sun.COM  * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
4638954SEnrico.Perla@Sun.COM  * or, potentially, insecure.
4648954SEnrico.Perla@Sun.COM  */
4658954SEnrico.Perla@Sun.COM static void
4668954SEnrico.Perla@Sun.COM sanitize_env()
4678954SEnrico.Perla@Sun.COM {
4688954SEnrico.Perla@Sun.COM 	int	stry = 0;
4698954SEnrico.Perla@Sun.COM 
4708954SEnrico.Perla@Sun.COM 	/* don't depend on caller umask */
4718954SEnrico.Perla@Sun.COM 	(void) umask(0022);
4728954SEnrico.Perla@Sun.COM 
4738954SEnrico.Perla@Sun.COM 	/* move away from a potential unsafe current working directory */
4748954SEnrico.Perla@Sun.COM 	while (chdir("/") == -1) {
4758954SEnrico.Perla@Sun.COM 		if (errno != EINTR) {
4768954SEnrico.Perla@Sun.COM 			bam_print("WARNING: unable to chdir to /");
4778954SEnrico.Perla@Sun.COM 			break;
4788954SEnrico.Perla@Sun.COM 		}
4798954SEnrico.Perla@Sun.COM 	}
4808954SEnrico.Perla@Sun.COM 
4818954SEnrico.Perla@Sun.COM 	bam_home_env = getenv("HOME");
4828954SEnrico.Perla@Sun.COM 	while (bam_home_env != NULL && putenv("HOME=/") == -1) {
4838954SEnrico.Perla@Sun.COM 		if (errno == ENOMEM) {
4848954SEnrico.Perla@Sun.COM 			/* retry no more than MAX_TRIES times */
4858954SEnrico.Perla@Sun.COM 			if (++stry > MAX_TRIES) {
4868954SEnrico.Perla@Sun.COM 				bam_print("WARNING: unable to recover from "
4878954SEnrico.Perla@Sun.COM 				    "system memory pressure... aborting \n");
4888954SEnrico.Perla@Sun.COM 				bam_exit(EXIT_FAILURE);
4898954SEnrico.Perla@Sun.COM 			}
4908954SEnrico.Perla@Sun.COM 			/* memory is tight, try to sleep */
4918954SEnrico.Perla@Sun.COM 			bam_print("Attempting to recover from memory pressure: "
4928954SEnrico.Perla@Sun.COM 			    "sleeping for %d seconds\n", SLEEP_TIME * stry);
4938954SEnrico.Perla@Sun.COM 			(void) sleep(SLEEP_TIME * stry);
4948954SEnrico.Perla@Sun.COM 		} else {
4958954SEnrico.Perla@Sun.COM 			bam_print("WARNING: unable to sanitize HOME\n");
4968954SEnrico.Perla@Sun.COM 		}
4978954SEnrico.Perla@Sun.COM 	}
4988954SEnrico.Perla@Sun.COM }
4998954SEnrico.Perla@Sun.COM 
5000Sstevel@tonic-gate int
5010Sstevel@tonic-gate main(int argc, char *argv[])
5020Sstevel@tonic-gate {
5030Sstevel@tonic-gate 	error_t ret;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
5060Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
5090Sstevel@tonic-gate 		prog = argv[0];
5100Sstevel@tonic-gate 	} else {
5110Sstevel@tonic-gate 		prog++;
5120Sstevel@tonic-gate 	}
5130Sstevel@tonic-gate 
5146448Svikram 	INJECT_ERROR1("ASSERT_ON", assert(0))
5150Sstevel@tonic-gate 
5168954SEnrico.Perla@Sun.COM 	sanitize_env();
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	parse_args(argc, argv);
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	switch (bam_cmd) {
5210Sstevel@tonic-gate 		case BAM_MENU:
5220Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
5230Sstevel@tonic-gate 			break;
5240Sstevel@tonic-gate 		case BAM_ARCHIVE:
5250Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
5260Sstevel@tonic-gate 			break;
5270Sstevel@tonic-gate 		default:
5280Sstevel@tonic-gate 			usage();
5290Sstevel@tonic-gate 			bam_exit(1);
5300Sstevel@tonic-gate 	}
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
5330Sstevel@tonic-gate 		bam_exit(1);
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	bam_unlock();
5360Sstevel@tonic-gate 	return (0);
5370Sstevel@tonic-gate }
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate /*
5400Sstevel@tonic-gate  * Equivalence of public and internal commands:
5410Sstevel@tonic-gate  *	update-archive  -- -a update
5420Sstevel@tonic-gate  *	list-archive	-- -a list
5430Sstevel@tonic-gate  *	set-menu	-- -m set_option
5440Sstevel@tonic-gate  *	list-menu	-- -m list_entry
5450Sstevel@tonic-gate  *	update-menu	-- -m update_entry
5460Sstevel@tonic-gate  */
5470Sstevel@tonic-gate static struct cmd_map {
5480Sstevel@tonic-gate 	char *bam_cmdname;
5490Sstevel@tonic-gate 	int bam_cmd;
5500Sstevel@tonic-gate 	char *bam_subcmd;
5510Sstevel@tonic-gate } cmd_map[] = {
5520Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
5530Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
5540Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
5550Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
5560Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
5570Sstevel@tonic-gate 	{ NULL,			0,		NULL}
5580Sstevel@tonic-gate };
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate /*
5610Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
5620Sstevel@tonic-gate  */
5630Sstevel@tonic-gate static void
5640Sstevel@tonic-gate parse_args(int argc, char *argv[])
5650Sstevel@tonic-gate {
5660Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	/* command conforming to the final spec */
5690Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
5700Sstevel@tonic-gate 		/*
5710Sstevel@tonic-gate 		 * Map commands to internal table.
5720Sstevel@tonic-gate 		 */
5730Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
5740Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
5750Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
5760Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
5770Sstevel@tonic-gate 				break;
5780Sstevel@tonic-gate 			}
5790Sstevel@tonic-gate 			cmp++;
5800Sstevel@tonic-gate 		}
5810Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
5820Sstevel@tonic-gate 			usage();
5830Sstevel@tonic-gate 			bam_exit(1);
5840Sstevel@tonic-gate 		}
5850Sstevel@tonic-gate 		argc--;
5860Sstevel@tonic-gate 		argv++;
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	parse_args_internal(argc, argv);
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate /*
5930Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
5940Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
5950Sstevel@tonic-gate  *	-a update	-- update-archive
5960Sstevel@tonic-gate  *	-a list		-- list-archive
5970Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
5980Sstevel@tonic-gate  *	-m update_entry	-- update-menu
5990Sstevel@tonic-gate  *	-m list_entry	-- list-menu
6000Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
6010Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
6028735SEnrico.Perla@Sun.COM  * A set of private flags is there too:
6038735SEnrico.Perla@Sun.COM  *	-F		-- purge the cache directories and rebuild them
6048735SEnrico.Perla@Sun.COM  *	-e		-- use the (faster) archive update approach (used by
6058735SEnrico.Perla@Sun.COM  *			   reboot)
6060Sstevel@tonic-gate  */
6070Sstevel@tonic-gate static void
6080Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
6090Sstevel@tonic-gate {
6100Sstevel@tonic-gate 	int c, error;
6110Sstevel@tonic-gate 	extern char *optarg;
6120Sstevel@tonic-gate 	extern int optind, opterr;
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	/* Suppress error message from getopt */
6150Sstevel@tonic-gate 	opterr = 0;
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 	error = 0;
6188735SEnrico.Perla@Sun.COM 	while ((c = getopt(argc, argv, "a:d:fm:no:veFCR:p:Z")) != -1) {
6190Sstevel@tonic-gate 		switch (c) {
6200Sstevel@tonic-gate 		case 'a':
6210Sstevel@tonic-gate 			if (bam_cmd) {
6220Sstevel@tonic-gate 				error = 1;
6230Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
6240Sstevel@tonic-gate 			}
6250Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
6260Sstevel@tonic-gate 			bam_subcmd = optarg;
6270Sstevel@tonic-gate 			break;
6280Sstevel@tonic-gate 		case 'd':
6290Sstevel@tonic-gate 			if (bam_debug) {
6300Sstevel@tonic-gate 				error = 1;
6310Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
6320Sstevel@tonic-gate 			}
6330Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
6340Sstevel@tonic-gate 			break;
6350Sstevel@tonic-gate 		case 'f':
6360Sstevel@tonic-gate 			bam_force = 1;
6370Sstevel@tonic-gate 			break;
6388735SEnrico.Perla@Sun.COM 		case 'F':
6398735SEnrico.Perla@Sun.COM 			bam_purge = 1;
6408735SEnrico.Perla@Sun.COM 			break;
6410Sstevel@tonic-gate 		case 'm':
6420Sstevel@tonic-gate 			if (bam_cmd) {
6430Sstevel@tonic-gate 				error = 1;
6440Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
6450Sstevel@tonic-gate 			}
6460Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
6470Sstevel@tonic-gate 			bam_subcmd = optarg;
6480Sstevel@tonic-gate 			break;
6490Sstevel@tonic-gate 		case 'n':
6500Sstevel@tonic-gate 			bam_check = 1;
6519216SEnrico.Perla@Sun.COM 			/*
6529216SEnrico.Perla@Sun.COM 			 * We save the original value of bam_check. The new
6539216SEnrico.Perla@Sun.COM 			 * approach in case of a read-only filesystem is to
6549216SEnrico.Perla@Sun.COM 			 * behave as a check, so we need a way to restore the
6559216SEnrico.Perla@Sun.COM 			 * original value after the evaluation of the read-only
6569216SEnrico.Perla@Sun.COM 			 * filesystem has been done.
6579216SEnrico.Perla@Sun.COM 			 * Even if we don't allow at the moment a check with
6589216SEnrico.Perla@Sun.COM 			 * update_all, this approach is more robust than
6599216SEnrico.Perla@Sun.COM 			 * simply resetting bam_check to zero.
6609216SEnrico.Perla@Sun.COM 			 */
6619216SEnrico.Perla@Sun.COM 			bam_saved_check = 1;
6620Sstevel@tonic-gate 			break;
6630Sstevel@tonic-gate 		case 'o':
6640Sstevel@tonic-gate 			if (bam_opt) {
6650Sstevel@tonic-gate 				error = 1;
6660Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
6670Sstevel@tonic-gate 			}
6680Sstevel@tonic-gate 			bam_opt = optarg;
6690Sstevel@tonic-gate 			break;
6700Sstevel@tonic-gate 		case 'v':
6710Sstevel@tonic-gate 			bam_verbose = 1;
6720Sstevel@tonic-gate 			break;
673662Sszhou 		case 'C':
674662Sszhou 			bam_smf_check = 1;
675662Sszhou 			break;
6760Sstevel@tonic-gate 		case 'R':
6770Sstevel@tonic-gate 			if (bam_root) {
6780Sstevel@tonic-gate 				error = 1;
6790Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
6800Sstevel@tonic-gate 				break;
6810Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
6820Sstevel@tonic-gate 				error = 1;
6830Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
6840Sstevel@tonic-gate 				    strerror(errno));
6850Sstevel@tonic-gate 				break;
6860Sstevel@tonic-gate 			}
687621Svikram 			bam_alt_root = 1;
6880Sstevel@tonic-gate 			bam_root = rootbuf;
6890Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
6900Sstevel@tonic-gate 			break;
6916319Sjg 		case 'p':
6926319Sjg 			bam_alt_platform = 1;
6936319Sjg 			bam_platform = optarg;
6946319Sjg 			if ((strcmp(bam_platform, "i86pc") != 0) &&
6956319Sjg 			    (strcmp(bam_platform, "sun4u") != 0) &&
6966319Sjg 			    (strcmp(bam_platform, "sun4v") != 0)) {
6976319Sjg 				error = 1;
6986319Sjg 				bam_error(INVALID_PLAT, bam_platform);
6996319Sjg 			}
7006319Sjg 			break;
7016423Sgw25295 		case 'Z':
7026423Sgw25295 			bam_zfs = 1;
7036423Sgw25295 			break;
7048735SEnrico.Perla@Sun.COM 		case 'e':
7058735SEnrico.Perla@Sun.COM 			bam_extend = 1;
7068735SEnrico.Perla@Sun.COM 			break;
7070Sstevel@tonic-gate 		case '?':
7080Sstevel@tonic-gate 			error = 1;
7090Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
7100Sstevel@tonic-gate 			break;
7110Sstevel@tonic-gate 		default :
7120Sstevel@tonic-gate 			error = 1;
7130Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
7140Sstevel@tonic-gate 			break;
7150Sstevel@tonic-gate 		}
7160Sstevel@tonic-gate 	}
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 	/*
7196319Sjg 	 * An alternate platform requires an alternate root
7206319Sjg 	 */
7216319Sjg 	if (bam_alt_platform && bam_alt_root == 0) {
7226319Sjg 		usage();
7236319Sjg 		bam_exit(0);
7246319Sjg 	}
7256319Sjg 
7266319Sjg 	/*
7270Sstevel@tonic-gate 	 * A command option must be specfied
7280Sstevel@tonic-gate 	 */
7290Sstevel@tonic-gate 	if (!bam_cmd) {
7300Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
7310Sstevel@tonic-gate 			usage();
7320Sstevel@tonic-gate 			bam_exit(0);
7330Sstevel@tonic-gate 		}
7340Sstevel@tonic-gate 		bam_error(NEED_CMD);
7350Sstevel@tonic-gate 		error = 1;
7360Sstevel@tonic-gate 	}
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 	if (error) {
7390Sstevel@tonic-gate 		usage();
7400Sstevel@tonic-gate 		bam_exit(1);
7410Sstevel@tonic-gate 	}
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	if (optind > argc) {
7440Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
7450Sstevel@tonic-gate 		bam_exit(1);
7460Sstevel@tonic-gate 	} else if (optind < argc) {
7470Sstevel@tonic-gate 		bam_argv = &argv[optind];
7480Sstevel@tonic-gate 		bam_argc = argc - optind;
7490Sstevel@tonic-gate 	}
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	/*
7520Sstevel@tonic-gate 	 * -n implies verbose mode
7530Sstevel@tonic-gate 	 */
7540Sstevel@tonic-gate 	if (bam_check)
7550Sstevel@tonic-gate 		bam_verbose = 1;
7560Sstevel@tonic-gate }
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate static error_t
7590Sstevel@tonic-gate check_subcmd_and_options(
7600Sstevel@tonic-gate 	char *subcmd,
7610Sstevel@tonic-gate 	char *opt,
7620Sstevel@tonic-gate 	subcmd_defn_t *table,
7630Sstevel@tonic-gate 	error_t (**fp)())
7640Sstevel@tonic-gate {
7650Sstevel@tonic-gate 	int i;
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	if (subcmd == NULL) {
7680Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
7690Sstevel@tonic-gate 		return (BAM_ERROR);
7700Sstevel@tonic-gate 	}
7710Sstevel@tonic-gate 
7726448Svikram 	if (strcmp(subcmd, "set_option") == 0) {
7736448Svikram 		if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
7746448Svikram 			bam_error(MISSING_ARG);
7756448Svikram 			usage();
7766448Svikram 			return (BAM_ERROR);
7776448Svikram 		} else if (bam_argc > 1 || bam_argv[1] != NULL) {
7782115Svikram 			bam_error(TRAILING_ARGS);
7792115Svikram 			usage();
7802115Svikram 			return (BAM_ERROR);
7812115Svikram 		}
7827656SSherry.Moore@Sun.COM 	} else if (strcmp(subcmd, "update_all") == 0) {
7837656SSherry.Moore@Sun.COM 		/*
7847656SSherry.Moore@Sun.COM 		 * The only option we accept for the "update_all"
7857656SSherry.Moore@Sun.COM 		 * subcmd is "fastboot".
7867656SSherry.Moore@Sun.COM 		 */
7877656SSherry.Moore@Sun.COM 		if (bam_argc > 1 || (bam_argc == 1 &&
7887656SSherry.Moore@Sun.COM 		    strcmp(bam_argv[0], "fastboot") != 0)) {
7897656SSherry.Moore@Sun.COM 			bam_error(TRAILING_ARGS);
7907656SSherry.Moore@Sun.COM 			usage();
7917656SSherry.Moore@Sun.COM 			return (BAM_ERROR);
7927656SSherry.Moore@Sun.COM 		}
7937656SSherry.Moore@Sun.COM 		if (bam_argc == 1)
7947656SSherry.Moore@Sun.COM 			sync_menu = 0;
7956448Svikram 	} else if (bam_argc || bam_argv) {
7966448Svikram 		bam_error(TRAILING_ARGS);
7976448Svikram 		usage();
7986448Svikram 		return (BAM_ERROR);
7992115Svikram 	}
8002115Svikram 
8010Sstevel@tonic-gate 	if (bam_root == NULL) {
8020Sstevel@tonic-gate 		bam_root = rootbuf;
8030Sstevel@tonic-gate 		bam_rootlen = 1;
8040Sstevel@tonic-gate 	}
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 	/* verify that subcmd is valid */
8070Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
8080Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
8090Sstevel@tonic-gate 			break;
8100Sstevel@tonic-gate 	}
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
8130Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
8140Sstevel@tonic-gate 		return (BAM_ERROR);
8150Sstevel@tonic-gate 	}
8160Sstevel@tonic-gate 
8172115Svikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
8182115Svikram 		bam_error(MUST_BE_ROOT);
8192115Svikram 		return (BAM_ERROR);
8202115Svikram 	}
8212115Svikram 
8222115Svikram 	/*
8232115Svikram 	 * Currently only privileged commands need a lock
8242115Svikram 	 */
8252115Svikram 	if (table[i].unpriv == 0)
8262115Svikram 		bam_lock();
8272115Svikram 
8280Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
8290Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
8300Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
8310Sstevel@tonic-gate 			if (opt)
8320Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
8330Sstevel@tonic-gate 			else
8340Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
8350Sstevel@tonic-gate 			return (BAM_ERROR);
8360Sstevel@tonic-gate 		}
8370Sstevel@tonic-gate 	}
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	*fp = table[i].handler;
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	return (BAM_SUCCESS);
8420Sstevel@tonic-gate }
8430Sstevel@tonic-gate 
844621Svikram /*
845621Svikram  * NOTE: A single "/" is also considered a trailing slash and will
846621Svikram  * be deleted.
847621Svikram  */
848621Svikram static void
849621Svikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
850621Svikram {
851621Svikram 	size_t dstlen;
852621Svikram 
853621Svikram 	assert(src);
854621Svikram 	assert(dst);
855621Svikram 
856621Svikram 	(void) strlcpy(dst, src, dstsize);
857621Svikram 
858621Svikram 	dstlen = strlen(dst);
859621Svikram 	if (dst[dstlen - 1] == '/') {
860621Svikram 		dst[dstlen - 1] = '\0';
861621Svikram 	}
862621Svikram }
863621Svikram 
8648954SEnrico.Perla@Sun.COM static int
8658954SEnrico.Perla@Sun.COM is_safe_exec(char *path)
8668954SEnrico.Perla@Sun.COM {
8678954SEnrico.Perla@Sun.COM 	struct stat	sb;
8688954SEnrico.Perla@Sun.COM 
8698954SEnrico.Perla@Sun.COM 	if (lstat(path, &sb) != 0) {
8708954SEnrico.Perla@Sun.COM 		bam_error(STAT_FAIL, path, strerror(errno));
8718954SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
8728954SEnrico.Perla@Sun.COM 	}
8738954SEnrico.Perla@Sun.COM 
8748954SEnrico.Perla@Sun.COM 	if (!S_ISREG(sb.st_mode)) {
8758954SEnrico.Perla@Sun.COM 		bam_error(PATH_EXEC_LINK, path);
8768954SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
8778954SEnrico.Perla@Sun.COM 	}
8788954SEnrico.Perla@Sun.COM 
8798954SEnrico.Perla@Sun.COM 	if (sb.st_uid != getuid()) {
8808954SEnrico.Perla@Sun.COM 		bam_error(PATH_EXEC_OWNER, path, getuid());
8818954SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
8828954SEnrico.Perla@Sun.COM 	}
8838954SEnrico.Perla@Sun.COM 
8848954SEnrico.Perla@Sun.COM 	if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
8858954SEnrico.Perla@Sun.COM 		bam_error(PATH_EXEC_PERMS, path);
8868954SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
8878954SEnrico.Perla@Sun.COM 	}
8888954SEnrico.Perla@Sun.COM 
8898954SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
8908954SEnrico.Perla@Sun.COM }
8918954SEnrico.Perla@Sun.COM 
8920Sstevel@tonic-gate static error_t
8930Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
8940Sstevel@tonic-gate {
8956448Svikram 	error_t			ret;
8966448Svikram 	char			menu_path[PATH_MAX];
8976448Svikram 	char			clean_menu_root[PATH_MAX];
8986448Svikram 	char			path[PATH_MAX];
8996448Svikram 	menu_t			*menu;
9006448Svikram 	char			menu_root[PATH_MAX];
9016448Svikram 	struct stat		sb;
9020Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
9036448Svikram 	char			*special;
9046448Svikram 	char			*pool = NULL;
9056448Svikram 	zfs_mnted_t		zmnted;
9066448Svikram 	char			*zmntpt;
9076448Svikram 	char			*osdev;
9086448Svikram 	char			*osroot;
9096448Svikram 	const char		*fcn = "bam_menu()";
9105648Ssetje 
9115648Ssetje 	/*
9125648Ssetje 	 * Menu sub-command only applies to GRUB (i.e. x86)
9135648Ssetje 	 */
9146448Svikram 	if (!is_grub(bam_alt_root ? bam_root : "/")) {
9156448Svikram 		bam_error(NOT_GRUB_BOOT);
9165648Ssetje 		return (BAM_ERROR);
9175648Ssetje 	}
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	/*
9200Sstevel@tonic-gate 	 * Check arguments
9210Sstevel@tonic-gate 	 */
9220Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
9230Sstevel@tonic-gate 	if (ret == BAM_ERROR) {
9240Sstevel@tonic-gate 		return (BAM_ERROR);
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 
9276448Svikram 	assert(bam_root);
9286448Svikram 
9296448Svikram 	(void) strlcpy(menu_root, bam_root, sizeof (menu_root));
9306448Svikram 	osdev = osroot = NULL;
9316448Svikram 
9326448Svikram 	if (strcmp(subcmd, "update_entry") == 0) {
9336448Svikram 		assert(opt);
9346448Svikram 
9356448Svikram 		osdev = strtok(opt, ",");
9366448Svikram 		assert(osdev);
9376448Svikram 		osroot = strtok(NULL, ",");
9386448Svikram 		if (osroot) {
9396448Svikram 			/* fixup bam_root so that it points at osroot */
9406448Svikram 			if (realpath(osroot, rootbuf) == NULL) {
9416448Svikram 				bam_error(CANT_RESOLVE, osroot,
9426448Svikram 				    strerror(errno));
9436448Svikram 				return (BAM_ERROR);
9446448Svikram 			}
9456448Svikram 			bam_alt_root = 1;
9466448Svikram 			bam_root  = rootbuf;
9476448Svikram 			bam_rootlen = strlen(rootbuf);
9486448Svikram 		}
949621Svikram 	}
950621Svikram 
9516423Sgw25295 	/*
9526448Svikram 	 * We support menu on PCFS (under certain conditions), but
9536448Svikram 	 * not the OS root
9546423Sgw25295 	 */
9556448Svikram 	if (is_pcfs(bam_root)) {
9566448Svikram 		bam_error(PCFS_ROOT_NOTSUP, bam_root);
9576448Svikram 		return (BAM_ERROR);
9586448Svikram 	}
9596448Svikram 
9606448Svikram 	if (stat(menu_root, &sb) == -1) {
9616448Svikram 		bam_error(CANNOT_LOCATE_GRUB_MENU);
9626448Svikram 		return (BAM_ERROR);
9636448Svikram 	}
9646448Svikram 
9656448Svikram 	BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root));
9666423Sgw25295 
9676423Sgw25295 	/*
9686448Svikram 	 * We no longer use the GRUB slice file. If it exists, then
9696448Svikram 	 * the user is doing something that is unsupported (such as
9706448Svikram 	 * standard upgrading an old Live Upgrade BE). If that
9716448Svikram 	 * happens, mimic existing behavior i.e. pretend that it is
9726448Svikram 	 * not a BE. Emit a warning though.
9736423Sgw25295 	 */
9746448Svikram 	if (bam_alt_root) {
9756448Svikram 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
9766448Svikram 		    GRUB_slice);
9776448Svikram 	} else {
9786448Svikram 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
9796448Svikram 	}
9806448Svikram 
9818642SVikram.Hegde@Sun.COM 	if (bam_verbose && stat(path, &sb) == 0)
9826448Svikram 		bam_error(GRUB_SLICE_FILE_EXISTS, path);
9836448Svikram 
9846448Svikram 	if (is_zfs(menu_root)) {
9856448Svikram 		assert(strcmp(menu_root, bam_root) == 0);
9866448Svikram 		special = get_special(menu_root);
9876448Svikram 		INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
9886448Svikram 		if (special == NULL) {
9896448Svikram 			bam_error(CANT_FIND_SPECIAL, menu_root);
9906448Svikram 			return (BAM_ERROR);
9916448Svikram 		}
9926448Svikram 		pool = strtok(special, "/");
9936448Svikram 		INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
9946448Svikram 		if (pool == NULL) {
9956448Svikram 			free(special);
9966448Svikram 			bam_error(CANT_FIND_POOL, menu_root);
9976448Svikram 			return (BAM_ERROR);
9986448Svikram 		}
9996448Svikram 		BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool));
10006448Svikram 
10016448Svikram 		zmntpt = mount_top_dataset(pool, &zmnted);
10026448Svikram 		INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
10036448Svikram 		if (zmntpt == NULL) {
10046448Svikram 			bam_error(CANT_MOUNT_POOL_DATASET, pool);
10056448Svikram 			free(special);
10066448Svikram 			return (BAM_ERROR);
10076448Svikram 		}
10086448Svikram 		BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt));
10096448Svikram 
10106448Svikram 		(void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
10116448Svikram 		BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root));
10126448Svikram 	}
10136448Svikram 
10146448Svikram 	elide_trailing_slash(menu_root, clean_menu_root,
10156448Svikram 	    sizeof (clean_menu_root));
10166448Svikram 
10176448Svikram 	BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root));
10186448Svikram 
10196448Svikram 	(void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1020621Svikram 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1021621Svikram 
10226448Svikram 	BAM_DPRINTF((D_MENU_PATH, fcn, menu_path));
10236448Svikram 
1024621Svikram 	/*
10256448Svikram 	 * If listing the menu, display the menu location
1026621Svikram 	 */
1027621Svikram 	if (strcmp(subcmd, "list_entry") == 0) {
10286448Svikram 		bam_print(GRUB_MENU_PATH, menu_path);
10296448Svikram 	}
10306448Svikram 
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	menu = menu_read(menu_path);
10330Sstevel@tonic-gate 	assert(menu);
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	/*
10366448Svikram 	 * We already checked the following case in
10376448Svikram 	 * check_subcmd_and_suboptions() above. Complete the
10386448Svikram 	 * final step now.
10390Sstevel@tonic-gate 	 */
10400Sstevel@tonic-gate 	if (strcmp(subcmd, "set_option") == 0) {
10416448Svikram 		assert(largc == 1 && largv[0] && largv[1] == NULL);
10420Sstevel@tonic-gate 		opt = largv[0];
10436448Svikram 	} else {
10446448Svikram 		assert(largc == 0 && largv == NULL);
10456448Svikram 	}
10466448Svikram 
10476448Svikram 	ret = get_boot_cap(bam_root);
10486448Svikram 	if (ret != BAM_SUCCESS) {
10496448Svikram 		BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
10506448Svikram 		goto out;
10516448Svikram 	}
10523446Smrj 
10530Sstevel@tonic-gate 	/*
10540Sstevel@tonic-gate 	 * Once the sub-cmd handler has run
10550Sstevel@tonic-gate 	 * only the line field is guaranteed to have valid values
10560Sstevel@tonic-gate 	 */
10576448Svikram 	if (strcmp(subcmd, "update_entry") == 0)
10586448Svikram 		ret = f(menu, menu_root, osdev);
10596448Svikram 	else if (strcmp(subcmd, "upgrade") == 0)
10606448Svikram 		ret = f(menu, bam_root, menu_root);
10616448Svikram 	else if (strcmp(subcmd, "list_entry") == 0)
10626448Svikram 		ret = f(menu, menu_path, opt);
10630Sstevel@tonic-gate 	else
10646448Svikram 		ret = f(menu, NULL, opt);
10656448Svikram 
10660Sstevel@tonic-gate 	if (ret == BAM_WRITE) {
10676448Svikram 		BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root));
10686448Svikram 		ret = menu_write(clean_menu_root, menu);
10696448Svikram 	}
10706448Svikram 
10716448Svikram out:
10726448Svikram 	INJECT_ERROR1("POOL_SET", pool = "/pooldata");
10736448Svikram 	assert((is_zfs(menu_root)) ^ (pool == NULL));
10746448Svikram 	if (pool) {
10756448Svikram 		(void) umount_top_dataset(pool, zmnted, zmntpt);
10766448Svikram 		free(special);
10776448Svikram 	}
10780Sstevel@tonic-gate 	menu_free(menu);
10790Sstevel@tonic-gate 	return (ret);
10800Sstevel@tonic-gate }
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate static error_t
10840Sstevel@tonic-gate bam_archive(
10850Sstevel@tonic-gate 	char *subcmd,
10860Sstevel@tonic-gate 	char *opt)
10870Sstevel@tonic-gate {
10886448Svikram 	error_t			ret;
10896448Svikram 	error_t			(*f)(char *root, char *opt);
10906448Svikram 	const char		*fcn = "bam_archive()";
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 	/*
1093662Sszhou 	 * Add trailing / for archive subcommands
1094662Sszhou 	 */
1095662Sszhou 	if (rootbuf[strlen(rootbuf) - 1] != '/')
1096662Sszhou 		(void) strcat(rootbuf, "/");
1097662Sszhou 	bam_rootlen = strlen(rootbuf);
1098662Sszhou 
1099662Sszhou 	/*
11000Sstevel@tonic-gate 	 * Check arguments
11010Sstevel@tonic-gate 	 */
11020Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
11030Sstevel@tonic-gate 	if (ret != BAM_SUCCESS) {
11040Sstevel@tonic-gate 		return (BAM_ERROR);
11050Sstevel@tonic-gate 	}
11060Sstevel@tonic-gate 
11076448Svikram 	ret = get_boot_cap(rootbuf);
11086448Svikram 	if (ret != BAM_SUCCESS) {
11096448Svikram 		BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
11103446Smrj 		return (ret);
11116448Svikram 	}
11123446Smrj 
11130Sstevel@tonic-gate 	/*
11140Sstevel@tonic-gate 	 * Check archive not supported with update_all
11150Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
11160Sstevel@tonic-gate 	 * information for each BE.
11170Sstevel@tonic-gate 	 */
11180Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
11190Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
11200Sstevel@tonic-gate 		return (BAM_ERROR);
11210Sstevel@tonic-gate 	}
11220Sstevel@tonic-gate 
1123316Svikram 	if (strcmp(subcmd, "update_all") == 0)
1124316Svikram 		bam_update_all = 1;
1125316Svikram 
11265648Ssetje #if !defined(_OPB)
11274581Ssherrym 	ucode_install(bam_root);
11284581Ssherrym #endif
11294581Ssherrym 
1130316Svikram 	ret = f(bam_root, opt);
1131316Svikram 
1132316Svikram 	bam_update_all = 0;
1133316Svikram 
1134316Svikram 	return (ret);
11350Sstevel@tonic-gate }
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate /*PRINTFLIKE1*/
11383446Smrj void
11390Sstevel@tonic-gate bam_error(char *format, ...)
11400Sstevel@tonic-gate {
11410Sstevel@tonic-gate 	va_list ap;
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate 	va_start(ap, format);
11440Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
11450Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
11460Sstevel@tonic-gate 	va_end(ap);
11470Sstevel@tonic-gate }
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate /*PRINTFLIKE1*/
11506448Svikram void
11516448Svikram bam_derror(char *format, ...)
11526448Svikram {
11536448Svikram 	va_list ap;
11546448Svikram 
11556448Svikram 	assert(bam_debug);
11566448Svikram 
11576448Svikram 	va_start(ap, format);
11586448Svikram 	(void) fprintf(stderr, "DEBUG: ");
11596448Svikram 	(void) vfprintf(stderr, format, ap);
11606448Svikram 	va_end(ap);
11616448Svikram }
11626448Svikram 
11636448Svikram /*PRINTFLIKE1*/
11646448Svikram void
11650Sstevel@tonic-gate bam_print(char *format, ...)
11660Sstevel@tonic-gate {
11670Sstevel@tonic-gate 	va_list ap;
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 	va_start(ap, format);
11700Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
11710Sstevel@tonic-gate 	va_end(ap);
11720Sstevel@tonic-gate }
11730Sstevel@tonic-gate 
11743446Smrj /*PRINTFLIKE1*/
11753446Smrj void
11763446Smrj bam_print_stderr(char *format, ...)
11773446Smrj {
11783446Smrj 	va_list ap;
11793446Smrj 
11803446Smrj 	va_start(ap, format);
11813446Smrj 	(void) vfprintf(stderr, format, ap);
11823446Smrj 	va_end(ap);
11833446Smrj }
11843446Smrj 
11850Sstevel@tonic-gate static void
11860Sstevel@tonic-gate bam_exit(int excode)
11870Sstevel@tonic-gate {
11888954SEnrico.Perla@Sun.COM 	restore_env();
11890Sstevel@tonic-gate 	bam_unlock();
11900Sstevel@tonic-gate 	exit(excode);
11910Sstevel@tonic-gate }
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate static void
11940Sstevel@tonic-gate bam_lock(void)
11950Sstevel@tonic-gate {
11960Sstevel@tonic-gate 	struct flock lock;
11970Sstevel@tonic-gate 	pid_t pid;
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
12000Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
12010Sstevel@tonic-gate 		/*
12020Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
12030Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
12040Sstevel@tonic-gate 		 * Proceed without the lock
12050Sstevel@tonic-gate 		 */
12060Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
12070Sstevel@tonic-gate 			bam_root_readonly = 1;
12080Sstevel@tonic-gate 			return;
12090Sstevel@tonic-gate 		}
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
12120Sstevel@tonic-gate 		bam_exit(1);
12130Sstevel@tonic-gate 	}
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
12160Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
12170Sstevel@tonic-gate 	lock.l_start = 0;
12180Sstevel@tonic-gate 	lock.l_len = 0;
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
12210Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
12220Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
12230Sstevel@tonic-gate 			(void) close(bam_lock_fd);
12240Sstevel@tonic-gate 			bam_lock_fd = -1;
12250Sstevel@tonic-gate 			bam_exit(1);
12260Sstevel@tonic-gate 		}
12270Sstevel@tonic-gate 		pid = 0;
12280Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
12290Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
12320Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
12330Sstevel@tonic-gate 		lock.l_start = 0;
12340Sstevel@tonic-gate 		lock.l_len = 0;
12350Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
12360Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
12370Sstevel@tonic-gate 			(void) close(bam_lock_fd);
12380Sstevel@tonic-gate 			bam_lock_fd = -1;
12390Sstevel@tonic-gate 			bam_exit(1);
12400Sstevel@tonic-gate 		}
12410Sstevel@tonic-gate 	}
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	/* We own the lock now */
12440Sstevel@tonic-gate 	pid = getpid();
12450Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
12460Sstevel@tonic-gate }
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate static void
12490Sstevel@tonic-gate bam_unlock(void)
12500Sstevel@tonic-gate {
12510Sstevel@tonic-gate 	struct flock unlock;
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 	/*
12540Sstevel@tonic-gate 	 * NOP if we don't hold the lock
12550Sstevel@tonic-gate 	 */
12560Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
12570Sstevel@tonic-gate 		return;
12580Sstevel@tonic-gate 	}
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
12610Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
12620Sstevel@tonic-gate 	unlock.l_start = 0;
12630Sstevel@tonic-gate 	unlock.l_len = 0;
12640Sstevel@tonic-gate 
12650Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
12660Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
12670Sstevel@tonic-gate 	}
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
12700Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
12710Sstevel@tonic-gate 	}
12720Sstevel@tonic-gate 	bam_lock_fd = -1;
12730Sstevel@tonic-gate }
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate static error_t
12760Sstevel@tonic-gate list_archive(char *root, char *opt)
12770Sstevel@tonic-gate {
12780Sstevel@tonic-gate 	filelist_t flist;
12790Sstevel@tonic-gate 	filelist_t *flistp = &flist;
12800Sstevel@tonic-gate 	line_t *lp;
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 	assert(root);
12830Sstevel@tonic-gate 	assert(opt == NULL);
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
12860Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
12870Sstevel@tonic-gate 		return (BAM_ERROR);
12880Sstevel@tonic-gate 	}
12890Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
12920Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
12930Sstevel@tonic-gate 	}
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 	filelist_free(flistp);
12960Sstevel@tonic-gate 
12970Sstevel@tonic-gate 	return (BAM_SUCCESS);
12980Sstevel@tonic-gate }
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate /*
13010Sstevel@tonic-gate  * This routine writes a list of lines to a file.
13020Sstevel@tonic-gate  * The list is *not* freed
13030Sstevel@tonic-gate  */
13040Sstevel@tonic-gate static error_t
13050Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
13060Sstevel@tonic-gate {
13076448Svikram 	char		tmpfile[PATH_MAX];
13086448Svikram 	char		path[PATH_MAX];
13096448Svikram 	FILE		*fp;
13106448Svikram 	int		ret;
13116448Svikram 	struct stat	sb;
13126448Svikram 	mode_t		mode;
13136448Svikram 	uid_t		root_uid;
13146448Svikram 	gid_t		sys_gid;
13156448Svikram 	struct passwd	*pw;
13166448Svikram 	struct group	*gp;
13176448Svikram 	const char	*fcn = "list2file()";
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate 	if (start == NULL) {
13226448Svikram 		/* Empty GRUB menu */
13230Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
13240Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
13250Sstevel@tonic-gate 			if (unlink(path) != 0) {
13260Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
13270Sstevel@tonic-gate 				return (BAM_ERROR);
13280Sstevel@tonic-gate 			} else {
13290Sstevel@tonic-gate 				return (BAM_SUCCESS);
13300Sstevel@tonic-gate 			}
13310Sstevel@tonic-gate 		}
13326448Svikram 		return (BAM_SUCCESS);
13330Sstevel@tonic-gate 	}
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 	/*
13360Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
13370Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
13380Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
13390Sstevel@tonic-gate 	 */
13400Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
13410Sstevel@tonic-gate 		mode = sb.st_mode;
13420Sstevel@tonic-gate 		root_uid = sb.st_uid;
13430Sstevel@tonic-gate 		sys_gid = sb.st_gid;
13440Sstevel@tonic-gate 	} else {
13450Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
13460Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
13470Sstevel@tonic-gate 			root_uid = pw->pw_uid;
13480Sstevel@tonic-gate 		} else {
13496448Svikram 			bam_error(CANT_FIND_USER,
13506448Svikram 			    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
13510Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
13520Sstevel@tonic-gate 		}
13530Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
13540Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
13550Sstevel@tonic-gate 		} else {
13566448Svikram 			bam_error(CANT_FIND_GROUP,
13576448Svikram 			    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
13580Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
13590Sstevel@tonic-gate 		}
13600Sstevel@tonic-gate 	}
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
13630Sstevel@tonic-gate 
13640Sstevel@tonic-gate 	/* Truncate tmpfile first */
13650Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
13660Sstevel@tonic-gate 	if (fp == NULL) {
13670Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
13680Sstevel@tonic-gate 		return (BAM_ERROR);
13690Sstevel@tonic-gate 	}
13706694Svikram 	ret = fclose(fp);
13716694Svikram 	INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
13726694Svikram 	if (ret == EOF) {
13730Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
13740Sstevel@tonic-gate 		return (BAM_ERROR);
13750Sstevel@tonic-gate 	}
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 	/* Now open it in append mode */
13780Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
13790Sstevel@tonic-gate 	if (fp == NULL) {
13800Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
13810Sstevel@tonic-gate 		return (BAM_ERROR);
13820Sstevel@tonic-gate 	}
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 	for (; start; start = start->next) {
13856694Svikram 		ret = s_fputs(start->line, fp);
13866694Svikram 		INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
13876694Svikram 		if (ret == EOF) {
13880Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
13890Sstevel@tonic-gate 			(void) fclose(fp);
13900Sstevel@tonic-gate 			return (BAM_ERROR);
13910Sstevel@tonic-gate 		}
13920Sstevel@tonic-gate 	}
13930Sstevel@tonic-gate 
13946694Svikram 	ret = fclose(fp);
13956694Svikram 	INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
13966694Svikram 	if (ret == EOF) {
13970Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
13980Sstevel@tonic-gate 		return (BAM_ERROR);
13990Sstevel@tonic-gate 	}
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate 	/*
1402271Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1403271Sjg 	 * not supporting these operations - pcfs reports unsupported
1404271Sjg 	 * operations as EINVAL.
14050Sstevel@tonic-gate 	 */
14060Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1407271Sjg 	if (ret == -1 &&
1408271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
14090Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
14100Sstevel@tonic-gate 		return (BAM_ERROR);
14110Sstevel@tonic-gate 	}
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1414271Sjg 	if (ret == -1 &&
1415271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
14160Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
14170Sstevel@tonic-gate 		return (BAM_ERROR);
14180Sstevel@tonic-gate 	}
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 
14210Sstevel@tonic-gate 	/*
14220Sstevel@tonic-gate 	 * Do an atomic rename
14230Sstevel@tonic-gate 	 */
14240Sstevel@tonic-gate 	ret = rename(tmpfile, path);
14256694Svikram 	INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
14260Sstevel@tonic-gate 	if (ret != 0) {
14270Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
14280Sstevel@tonic-gate 		return (BAM_ERROR);
14290Sstevel@tonic-gate 	}
14300Sstevel@tonic-gate 
14316448Svikram 	BAM_DPRINTF((D_WROTE_FILE, fcn, path));
14320Sstevel@tonic-gate 	return (BAM_SUCCESS);
14330Sstevel@tonic-gate }
14340Sstevel@tonic-gate 
14350Sstevel@tonic-gate /*
14368735SEnrico.Perla@Sun.COM  * Checks if the path specified (without the file name at the end) exists
14378735SEnrico.Perla@Sun.COM  * and creates it if not. If the path exists and is not a directory, an attempt
14388735SEnrico.Perla@Sun.COM  * to unlink is made.
14390Sstevel@tonic-gate  */
14408735SEnrico.Perla@Sun.COM static int
14418735SEnrico.Perla@Sun.COM setup_path(char *path)
14428735SEnrico.Perla@Sun.COM {
14438735SEnrico.Perla@Sun.COM 	char 		*p;
14448735SEnrico.Perla@Sun.COM 	int		ret;
14458735SEnrico.Perla@Sun.COM 	struct stat	sb;
14468735SEnrico.Perla@Sun.COM 
14478735SEnrico.Perla@Sun.COM 	p = strrchr(path, '/');
14488735SEnrico.Perla@Sun.COM 	if (p != NULL) {
14498735SEnrico.Perla@Sun.COM 		*p = '\0';
14508735SEnrico.Perla@Sun.COM 		if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
14518735SEnrico.Perla@Sun.COM 			/* best effort attempt, mkdirp will catch the error */
14528735SEnrico.Perla@Sun.COM 			(void) unlink(path);
14538735SEnrico.Perla@Sun.COM 			if (bam_verbose)
14548735SEnrico.Perla@Sun.COM 				bam_print(NEED_DIRPATH, path);
14558735SEnrico.Perla@Sun.COM 			ret = mkdirp(path, DIR_PERMS);
14568735SEnrico.Perla@Sun.COM 			if (ret == -1) {
14578735SEnrico.Perla@Sun.COM 				bam_error(MKDIR_FAILED, path, strerror(errno));
14588735SEnrico.Perla@Sun.COM 				*p = '/';
14598735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
14608735SEnrico.Perla@Sun.COM 			}
14618735SEnrico.Perla@Sun.COM 		}
14628735SEnrico.Perla@Sun.COM 		*p = '/';
14638735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
14648735SEnrico.Perla@Sun.COM 	}
14658735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
14668735SEnrico.Perla@Sun.COM }
14678735SEnrico.Perla@Sun.COM 
14688735SEnrico.Perla@Sun.COM typedef union {
14698735SEnrico.Perla@Sun.COM 	gzFile	gzfile;
14708735SEnrico.Perla@Sun.COM 	int	fdfile;
14718735SEnrico.Perla@Sun.COM } outfile;
14728735SEnrico.Perla@Sun.COM 
14738735SEnrico.Perla@Sun.COM typedef struct {
14748735SEnrico.Perla@Sun.COM 	char		path[PATH_MAX];
14758735SEnrico.Perla@Sun.COM 	outfile		out;
14768735SEnrico.Perla@Sun.COM } cachefile;
14778735SEnrico.Perla@Sun.COM 
14788735SEnrico.Perla@Sun.COM static int
14798735SEnrico.Perla@Sun.COM setup_file(char *base, const char *path, cachefile *cf)
14808735SEnrico.Perla@Sun.COM {
14818735SEnrico.Perla@Sun.COM 	int	ret;
14828735SEnrico.Perla@Sun.COM 	char	*strip;
14838735SEnrico.Perla@Sun.COM 
14848735SEnrico.Perla@Sun.COM 	/* init gzfile or fdfile in case we fail before opening */
14858735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT)
14868735SEnrico.Perla@Sun.COM 		cf->out.gzfile = NULL;
14878735SEnrico.Perla@Sun.COM 	else
14888735SEnrico.Perla@Sun.COM 		cf->out.fdfile = -1;
14898735SEnrico.Perla@Sun.COM 
14908735SEnrico.Perla@Sun.COM 	/* strip the trailing altroot path */
14918735SEnrico.Perla@Sun.COM 	strip = (char *)path + strlen(rootbuf);
14928735SEnrico.Perla@Sun.COM 
14938735SEnrico.Perla@Sun.COM 	ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
14948735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (cf->path)) {
14958735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
14968735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
14978735SEnrico.Perla@Sun.COM 	}
14988735SEnrico.Perla@Sun.COM 
14998735SEnrico.Perla@Sun.COM 	/* Check if path is present in the archive cache directory */
15008735SEnrico.Perla@Sun.COM 	if (setup_path(cf->path) == BAM_ERROR)
15018735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
15028735SEnrico.Perla@Sun.COM 
15038735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT) {
15048735SEnrico.Perla@Sun.COM 		if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
15058954SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, cf->path, strerror(errno));
15068735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
15078735SEnrico.Perla@Sun.COM 		}
15088735SEnrico.Perla@Sun.COM 		(void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
15098735SEnrico.Perla@Sun.COM 		    Z_DEFAULT_STRATEGY);
15108735SEnrico.Perla@Sun.COM 	} else {
15118735SEnrico.Perla@Sun.COM 		if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
15128735SEnrico.Perla@Sun.COM 		    == -1) {
15138735SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, cf->path, strerror(errno));
15148735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
15158735SEnrico.Perla@Sun.COM 		}
15168735SEnrico.Perla@Sun.COM 	}
15178735SEnrico.Perla@Sun.COM 
15188735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
15198735SEnrico.Perla@Sun.COM }
15208735SEnrico.Perla@Sun.COM 
15218735SEnrico.Perla@Sun.COM static int
15228735SEnrico.Perla@Sun.COM cache_write(cachefile cf, char *buf, int size)
15238735SEnrico.Perla@Sun.COM {
15248735SEnrico.Perla@Sun.COM 	int	err;
15258735SEnrico.Perla@Sun.COM 
15268735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT) {
15278735SEnrico.Perla@Sun.COM 		if (gzwrite(cf.out.gzfile, buf, size) < 1) {
15288735SEnrico.Perla@Sun.COM 			bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err));
15298954SEnrico.Perla@Sun.COM 			if (err == Z_ERRNO && bam_verbose) {
15308735SEnrico.Perla@Sun.COM 				bam_error(WRITE_FAIL, cf.path, strerror(errno));
15318954SEnrico.Perla@Sun.COM 			}
15328735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
15338735SEnrico.Perla@Sun.COM 		}
15348735SEnrico.Perla@Sun.COM 	} else {
15358735SEnrico.Perla@Sun.COM 		if (write(cf.out.fdfile, buf, size) < 1) {
15368735SEnrico.Perla@Sun.COM 			bam_error(WRITE_FAIL, cf.path, strerror(errno));
15378735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
15388735SEnrico.Perla@Sun.COM 		}
15398735SEnrico.Perla@Sun.COM 	}
15408735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
15418735SEnrico.Perla@Sun.COM }
15428735SEnrico.Perla@Sun.COM 
15438735SEnrico.Perla@Sun.COM static int
15448735SEnrico.Perla@Sun.COM cache_close(cachefile cf)
15458735SEnrico.Perla@Sun.COM {
15468735SEnrico.Perla@Sun.COM 	int	ret;
15478735SEnrico.Perla@Sun.COM 
15488735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT) {
15498735SEnrico.Perla@Sun.COM 		if (cf.out.gzfile) {
15508735SEnrico.Perla@Sun.COM 			ret = gzclose(cf.out.gzfile);
15518954SEnrico.Perla@Sun.COM 			if (ret != Z_OK) {
15528735SEnrico.Perla@Sun.COM 				bam_error(CLOSE_FAIL, cf.path, strerror(errno));
15538954SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
15548954SEnrico.Perla@Sun.COM 			}
15558735SEnrico.Perla@Sun.COM 		}
15568735SEnrico.Perla@Sun.COM 	} else {
15578735SEnrico.Perla@Sun.COM 		if (cf.out.fdfile != -1) {
15588735SEnrico.Perla@Sun.COM 			ret = close(cf.out.fdfile);
15598735SEnrico.Perla@Sun.COM 			if (ret != 0) {
15608735SEnrico.Perla@Sun.COM 				bam_error(CLOSE_FAIL, cf.path, strerror(errno));
15618735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
15628735SEnrico.Perla@Sun.COM 			}
15638735SEnrico.Perla@Sun.COM 		}
15648735SEnrico.Perla@Sun.COM 	}
15658735SEnrico.Perla@Sun.COM 
15668735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
15678735SEnrico.Perla@Sun.COM }
15688735SEnrico.Perla@Sun.COM 
15698735SEnrico.Perla@Sun.COM static int
15708735SEnrico.Perla@Sun.COM dircache_updatefile(const char *path, int what)
15718735SEnrico.Perla@Sun.COM {
15728735SEnrico.Perla@Sun.COM 	int 		ret, exitcode;
15738735SEnrico.Perla@Sun.COM 	char 		buf[4096 * 4];
15748735SEnrico.Perla@Sun.COM 	FILE 		*infile;
15758735SEnrico.Perla@Sun.COM 	cachefile 	outfile, outupdt;
15768735SEnrico.Perla@Sun.COM 
15778954SEnrico.Perla@Sun.COM 	if (bam_nowrite()) {
15788954SEnrico.Perla@Sun.COM 		set_dir_flag(what, NEED_UPDATE);
15798954SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
15808954SEnrico.Perla@Sun.COM 	}
15818954SEnrico.Perla@Sun.COM 
15828954SEnrico.Perla@Sun.COM 	if (!has_cachedir(what))
15838735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
15848735SEnrico.Perla@Sun.COM 
15858735SEnrico.Perla@Sun.COM 	if ((infile = fopen(path, "rb")) == NULL) {
15868735SEnrico.Perla@Sun.COM 		bam_error(OPEN_FAIL, path, strerror(errno));
15878735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
15888735SEnrico.Perla@Sun.COM 	}
15898735SEnrico.Perla@Sun.COM 
15908735SEnrico.Perla@Sun.COM 	ret = setup_file(get_cachedir(what), path, &outfile);
15918735SEnrico.Perla@Sun.COM 	if (ret == BAM_ERROR) {
15928735SEnrico.Perla@Sun.COM 		exitcode = BAM_ERROR;
15938735SEnrico.Perla@Sun.COM 		goto out;
15948735SEnrico.Perla@Sun.COM 	}
15958735SEnrico.Perla@Sun.COM 	if (!is_dir_flag_on(what, NO_MULTI)) {
15968735SEnrico.Perla@Sun.COM 		ret = setup_file(get_updatedir(what), path, &outupdt);
15978735SEnrico.Perla@Sun.COM 		if (ret == BAM_ERROR)
15988735SEnrico.Perla@Sun.COM 			set_dir_flag(what, NO_MULTI);
15998735SEnrico.Perla@Sun.COM 	}
16008735SEnrico.Perla@Sun.COM 
16018735SEnrico.Perla@Sun.COM 	while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
16028735SEnrico.Perla@Sun.COM 		if (cache_write(outfile, buf, ret) == BAM_ERROR) {
16038735SEnrico.Perla@Sun.COM 			exitcode = BAM_ERROR;
16048735SEnrico.Perla@Sun.COM 			goto out;
16058735SEnrico.Perla@Sun.COM 		}
16068735SEnrico.Perla@Sun.COM 		if (!is_dir_flag_on(what, NO_MULTI))
16078735SEnrico.Perla@Sun.COM 			if (cache_write(outupdt, buf, ret) == BAM_ERROR)
16088735SEnrico.Perla@Sun.COM 				set_dir_flag(what, NO_MULTI);
16098735SEnrico.Perla@Sun.COM 	}
16108735SEnrico.Perla@Sun.COM 
16118735SEnrico.Perla@Sun.COM 	set_dir_flag(what, NEED_UPDATE);
16128735SEnrico.Perla@Sun.COM 	get_count(what)++;
16138954SEnrico.Perla@Sun.COM 	if (get_count(what) > COUNT_MAX)
16148954SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
16158735SEnrico.Perla@Sun.COM 	exitcode = BAM_SUCCESS;
16168735SEnrico.Perla@Sun.COM out:
16178735SEnrico.Perla@Sun.COM 	(void) fclose(infile);
16188735SEnrico.Perla@Sun.COM 	if (cache_close(outfile) == BAM_ERROR)
16198735SEnrico.Perla@Sun.COM 		exitcode = BAM_ERROR;
16208735SEnrico.Perla@Sun.COM 	if (!is_dir_flag_on(what, NO_MULTI) &&
16218735SEnrico.Perla@Sun.COM 	    cache_close(outupdt) == BAM_ERROR)
16228735SEnrico.Perla@Sun.COM 		exitcode = BAM_ERROR;
16238735SEnrico.Perla@Sun.COM 	if (exitcode == BAM_ERROR)
16248735SEnrico.Perla@Sun.COM 		set_flag(UPDATE_ERROR);
16258735SEnrico.Perla@Sun.COM 	return (exitcode);
16268735SEnrico.Perla@Sun.COM }
16278735SEnrico.Perla@Sun.COM 
16288735SEnrico.Perla@Sun.COM static int
16298735SEnrico.Perla@Sun.COM dircache_updatedir(const char *path, int what, int updt)
16308735SEnrico.Perla@Sun.COM {
16318735SEnrico.Perla@Sun.COM 	int		ret;
16328735SEnrico.Perla@Sun.COM 	char		dpath[PATH_MAX];
16338735SEnrico.Perla@Sun.COM 	char		*strip;
16348735SEnrico.Perla@Sun.COM 	struct stat	sb;
16358735SEnrico.Perla@Sun.COM 
16368735SEnrico.Perla@Sun.COM 	strip = (char *)path + strlen(rootbuf);
16378735SEnrico.Perla@Sun.COM 
16388735SEnrico.Perla@Sun.COM 	ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
16398735SEnrico.Perla@Sun.COM 	    get_updatedir(what) : get_cachedir(what), strip);
16408735SEnrico.Perla@Sun.COM 
16418735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (dpath)) {
16428735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
16438735SEnrico.Perla@Sun.COM 		set_flag(UPDATE_ERROR);
16448735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
16458735SEnrico.Perla@Sun.COM 	}
16468735SEnrico.Perla@Sun.COM 
16478735SEnrico.Perla@Sun.COM 	if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
16488735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
16498735SEnrico.Perla@Sun.COM 
16508735SEnrico.Perla@Sun.COM 	if (updt) {
16518735SEnrico.Perla@Sun.COM 		if (!is_dir_flag_on(what, NO_MULTI))
16528735SEnrico.Perla@Sun.COM 			if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
16538735SEnrico.Perla@Sun.COM 				set_dir_flag(what, NO_MULTI);
16548735SEnrico.Perla@Sun.COM 	} else {
16558735SEnrico.Perla@Sun.COM 		if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
16568735SEnrico.Perla@Sun.COM 			set_flag(UPDATE_ERROR);
16578735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
16588735SEnrico.Perla@Sun.COM 		}
16598735SEnrico.Perla@Sun.COM 	}
16608735SEnrico.Perla@Sun.COM 
16618735SEnrico.Perla@Sun.COM 	set_dir_flag(what, NEED_UPDATE);
16628735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
16638735SEnrico.Perla@Sun.COM }
16648735SEnrico.Perla@Sun.COM 
16658735SEnrico.Perla@Sun.COM #define	DO_CACHE_DIR	0
16668735SEnrico.Perla@Sun.COM #define	DO_UPDATE_DIR	1
16678735SEnrico.Perla@Sun.COM 
16688735SEnrico.Perla@Sun.COM #if defined(_LP64) || defined(_LONGLONG_TYPE)
16698735SEnrico.Perla@Sun.COM typedef		Elf64_Ehdr	_elfhdr;
16708735SEnrico.Perla@Sun.COM #else
16718735SEnrico.Perla@Sun.COM typedef		Elf32_Ehdr	_elfhdr;
16728735SEnrico.Perla@Sun.COM #endif
16738735SEnrico.Perla@Sun.COM 
16748735SEnrico.Perla@Sun.COM /*
16758735SEnrico.Perla@Sun.COM  * This routine updates the contents of the cache directory
16768735SEnrico.Perla@Sun.COM  */
16778735SEnrico.Perla@Sun.COM static int
16788735SEnrico.Perla@Sun.COM update_dircache(const char *path, int flags)
16798735SEnrico.Perla@Sun.COM {
16808735SEnrico.Perla@Sun.COM 	int rc = BAM_SUCCESS;
16818735SEnrico.Perla@Sun.COM 
16828735SEnrico.Perla@Sun.COM 	switch (flags) {
16838735SEnrico.Perla@Sun.COM 	case FTW_F:
16848735SEnrico.Perla@Sun.COM 		{
16858735SEnrico.Perla@Sun.COM 		int	fd;
16868735SEnrico.Perla@Sun.COM 		_elfhdr	elf;
16878735SEnrico.Perla@Sun.COM 
16888735SEnrico.Perla@Sun.COM 		if ((fd = open(path, O_RDONLY)) < 0) {
16898735SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, path, strerror(errno));
16908735SEnrico.Perla@Sun.COM 			set_flag(UPDATE_ERROR);
16918735SEnrico.Perla@Sun.COM 			rc = BAM_ERROR;
16928735SEnrico.Perla@Sun.COM 			break;
16938735SEnrico.Perla@Sun.COM 		}
16948735SEnrico.Perla@Sun.COM 
16958735SEnrico.Perla@Sun.COM 		/*
16968735SEnrico.Perla@Sun.COM 		 * libelf and gelf would be a cleaner and easier way to handle
16978735SEnrico.Perla@Sun.COM 		 * this, but libelf fails compilation if _ILP32 is defined &&
16988735SEnrico.Perla@Sun.COM 		 * _FILE_OFFSET_BITS is != 32 ...
16998735SEnrico.Perla@Sun.COM 		 */
17008735SEnrico.Perla@Sun.COM 		if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
17018735SEnrico.Perla@Sun.COM 			bam_error(READ_FAIL, path, strerror(errno));
17028735SEnrico.Perla@Sun.COM 			set_flag(UPDATE_ERROR);
17038735SEnrico.Perla@Sun.COM 			(void) close(fd);
17048735SEnrico.Perla@Sun.COM 			rc = BAM_ERROR;
17058735SEnrico.Perla@Sun.COM 			break;
17068735SEnrico.Perla@Sun.COM 		}
17078735SEnrico.Perla@Sun.COM 		(void) close(fd);
17088735SEnrico.Perla@Sun.COM 
17098735SEnrico.Perla@Sun.COM 		/*
17108735SEnrico.Perla@Sun.COM 		 * If the file is not an executable and is not inside an amd64
17118735SEnrico.Perla@Sun.COM 		 * directory, we copy it in both the cache directories,
17128735SEnrico.Perla@Sun.COM 		 * otherwise, we only copy it inside the 64-bit one.
17138735SEnrico.Perla@Sun.COM 		 */
17148735SEnrico.Perla@Sun.COM 		if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
17158735SEnrico.Perla@Sun.COM 			if (strstr(path, "/amd64")) {
17168954SEnrico.Perla@Sun.COM 				rc = dircache_updatefile(path, FILE64);
17178954SEnrico.Perla@Sun.COM 			} else {
17188735SEnrico.Perla@Sun.COM 				rc = dircache_updatefile(path, FILE32);
17198735SEnrico.Perla@Sun.COM 				if (rc == BAM_SUCCESS)
17208735SEnrico.Perla@Sun.COM 					rc = dircache_updatefile(path, FILE64);
17218735SEnrico.Perla@Sun.COM 			}
17228735SEnrico.Perla@Sun.COM 		} else {
17238735SEnrico.Perla@Sun.COM 			/*
17248735SEnrico.Perla@Sun.COM 			 * Based on the ELF class we copy the file in the 32-bit
17258735SEnrico.Perla@Sun.COM 			 * or the 64-bit cache directory.
17268735SEnrico.Perla@Sun.COM 			 */
17278954SEnrico.Perla@Sun.COM 			if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
17288735SEnrico.Perla@Sun.COM 				rc = dircache_updatefile(path, FILE32);
17298954SEnrico.Perla@Sun.COM 			} else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
17308954SEnrico.Perla@Sun.COM 				rc = dircache_updatefile(path, FILE64);
17318954SEnrico.Perla@Sun.COM 			} else {
17328735SEnrico.Perla@Sun.COM 				bam_print(NO3264ELF, path);
17338735SEnrico.Perla@Sun.COM 				/* paranoid */
17348735SEnrico.Perla@Sun.COM 				rc  = dircache_updatefile(path, FILE32);
17358735SEnrico.Perla@Sun.COM 				if (rc == BAM_SUCCESS)
17368735SEnrico.Perla@Sun.COM 					rc = dircache_updatefile(path, FILE64);
17378735SEnrico.Perla@Sun.COM 			}
17388735SEnrico.Perla@Sun.COM 		}
17398735SEnrico.Perla@Sun.COM 		break;
17408735SEnrico.Perla@Sun.COM 		}
17418735SEnrico.Perla@Sun.COM 	case FTW_D:
17428735SEnrico.Perla@Sun.COM 		if (strstr(path, "/amd64") == NULL) {
17438735SEnrico.Perla@Sun.COM 			rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
17448735SEnrico.Perla@Sun.COM 			if (rc == BAM_SUCCESS)
17458735SEnrico.Perla@Sun.COM 				rc = dircache_updatedir(path, FILE32,
17468735SEnrico.Perla@Sun.COM 				    DO_CACHE_DIR);
17478735SEnrico.Perla@Sun.COM 		} else {
17488735SEnrico.Perla@Sun.COM 			if (has_cachedir(FILE64)) {
17498735SEnrico.Perla@Sun.COM 				rc = dircache_updatedir(path, FILE64,
17508735SEnrico.Perla@Sun.COM 				    DO_UPDATE_DIR);
17518735SEnrico.Perla@Sun.COM 				if (rc == BAM_SUCCESS)
17528735SEnrico.Perla@Sun.COM 					rc = dircache_updatedir(path, FILE64,
17538735SEnrico.Perla@Sun.COM 					    DO_CACHE_DIR);
17548735SEnrico.Perla@Sun.COM 			}
17558735SEnrico.Perla@Sun.COM 		}
17568735SEnrico.Perla@Sun.COM 		break;
17578735SEnrico.Perla@Sun.COM 	default:
17588735SEnrico.Perla@Sun.COM 		rc = BAM_ERROR;
17598735SEnrico.Perla@Sun.COM 		break;
17608735SEnrico.Perla@Sun.COM 	}
17618735SEnrico.Perla@Sun.COM 
17628735SEnrico.Perla@Sun.COM 	return (rc);
17638735SEnrico.Perla@Sun.COM }
17648735SEnrico.Perla@Sun.COM 
17650Sstevel@tonic-gate /*ARGSUSED*/
17660Sstevel@tonic-gate static int
17670Sstevel@tonic-gate cmpstat(
17680Sstevel@tonic-gate 	const char *file,
17698735SEnrico.Perla@Sun.COM 	const struct stat *st,
17700Sstevel@tonic-gate 	int flags,
17710Sstevel@tonic-gate 	struct FTW *ftw)
17720Sstevel@tonic-gate {
17738735SEnrico.Perla@Sun.COM 	uint_t 		sz;
17748735SEnrico.Perla@Sun.COM 	uint64_t 	*value;
17758735SEnrico.Perla@Sun.COM 	uint64_t 	filestat[2];
1776*9500SGangadhar.M@Sun.COM 	int 		error, ret, status;
17770Sstevel@tonic-gate 
17782334Ssetje 	struct safefile *safefilep;
17798735SEnrico.Perla@Sun.COM 	FILE 		*fp;
17808735SEnrico.Perla@Sun.COM 	struct stat	sb;
1781*9500SGangadhar.M@Sun.COM 	regex_t re;
17822334Ssetje 
17830Sstevel@tonic-gate 	/*
17848735SEnrico.Perla@Sun.COM 	 * On SPARC we create/update links too.
17850Sstevel@tonic-gate 	 */
17868735SEnrico.Perla@Sun.COM 	if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
17878735SEnrico.Perla@Sun.COM 	    !is_flag_on(IS_SPARC_TARGET)))
17888735SEnrico.Perla@Sun.COM 		return (0);
17898735SEnrico.Perla@Sun.COM 
17908735SEnrico.Perla@Sun.COM 	/*
17918735SEnrico.Perla@Sun.COM 	 * Ignore broken links
17928735SEnrico.Perla@Sun.COM 	 */
17938735SEnrico.Perla@Sun.COM 	if (flags == FTW_SL && stat(file, &sb) < 0)
17940Sstevel@tonic-gate 		return (0);
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate 	/*
17970Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
17980Sstevel@tonic-gate 	 * but this is not fatal to update determination.
17990Sstevel@tonic-gate 	 */
18000Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
18018735SEnrico.Perla@Sun.COM 		filestat[0] = st->st_size;
18028735SEnrico.Perla@Sun.COM 		filestat[1] = st->st_mtime;
18030Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
18040Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
18050Sstevel@tonic-gate 		if (error)
18060Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
18070Sstevel@tonic-gate 	}
18080Sstevel@tonic-gate 
18090Sstevel@tonic-gate 	/*
18106319Sjg 	 * If we are invoked as part of system/filesystem/boot-archive, then
18112334Ssetje 	 * there are a number of things we should not worry about
18120Sstevel@tonic-gate 	 */
18132334Ssetje 	if (bam_smf_check) {
18142334Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
18152334Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
18162334Ssetje 			return (0);
18172334Ssetje 
18182334Ssetje 		/* read in list of safe files */
18192334Ssetje 		if (safefiles == NULL)
18202334Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
18212334Ssetje 				safefiles = s_calloc(1,
18222334Ssetje 				    sizeof (struct safefile));
18232334Ssetje 				safefilep = safefiles;
18242334Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
18252334Ssetje 				    MAXNAMELEN);
18262334Ssetje 				safefilep->next = NULL;
18272334Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
18282334Ssetje 				    MAXNAMELEN, fp) != NULL) {
18292334Ssetje 					safefilep->next = s_calloc(1,
18302334Ssetje 					    sizeof (struct safefile));
18312334Ssetje 					safefilep = safefilep->next;
18322334Ssetje 					safefilep->name = s_calloc(1,
18332334Ssetje 					    MAXPATHLEN + MAXNAMELEN);
18342334Ssetje 					safefilep->next = NULL;
18352334Ssetje 				}
18362334Ssetje 				(void) fclose(fp);
18372334Ssetje 			}
18382334Ssetje 	}
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 	/*
18418735SEnrico.Perla@Sun.COM 	 * On SPARC we create a -path-list file for mkisofs
18428735SEnrico.Perla@Sun.COM 	 */
18438735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
18448735SEnrico.Perla@Sun.COM 		if (flags != FTW_D) {
18458735SEnrico.Perla@Sun.COM 			char	*strip;
18468735SEnrico.Perla@Sun.COM 
18478735SEnrico.Perla@Sun.COM 			strip = (char *)file + strlen(rootbuf);
18488735SEnrico.Perla@Sun.COM 			(void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
18498735SEnrico.Perla@Sun.COM 			    file);
18508735SEnrico.Perla@Sun.COM 		}
18518735SEnrico.Perla@Sun.COM 	}
18528735SEnrico.Perla@Sun.COM 
18538735SEnrico.Perla@Sun.COM 	/*
18548735SEnrico.Perla@Sun.COM 	 * We are transitioning from the old model to the dircache or the cache
18558735SEnrico.Perla@Sun.COM 	 * directory was removed: create the entry without further checkings.
18568735SEnrico.Perla@Sun.COM 	 */
18578735SEnrico.Perla@Sun.COM 	if (is_flag_on(NEED_CACHE_DIR)) {
18588735SEnrico.Perla@Sun.COM 		if (bam_verbose)
18598735SEnrico.Perla@Sun.COM 			bam_print(PARSEABLE_NEW_FILE, file);
18608735SEnrico.Perla@Sun.COM 
18618735SEnrico.Perla@Sun.COM 		if (is_flag_on(IS_SPARC_TARGET)) {
18628735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
18638735SEnrico.Perla@Sun.COM 			return (0);
18648735SEnrico.Perla@Sun.COM 		}
18658735SEnrico.Perla@Sun.COM 
18668735SEnrico.Perla@Sun.COM 		ret = update_dircache(file, flags);
18678735SEnrico.Perla@Sun.COM 		if (ret == BAM_ERROR) {
18688735SEnrico.Perla@Sun.COM 			bam_error(UPDT_CACHE_FAIL, file);
18698735SEnrico.Perla@Sun.COM 			return (-1);
18708735SEnrico.Perla@Sun.COM 		}
18718735SEnrico.Perla@Sun.COM 
18728735SEnrico.Perla@Sun.COM 		return (0);
18738735SEnrico.Perla@Sun.COM 	}
18748735SEnrico.Perla@Sun.COM 
18758735SEnrico.Perla@Sun.COM 	/*
18760Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
18770Sstevel@tonic-gate 	 */
18780Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
18790Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
18800Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
18810Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
18820Sstevel@tonic-gate 			return (0);
18838735SEnrico.Perla@Sun.COM 
18848735SEnrico.Perla@Sun.COM 		if (is_flag_on(IS_SPARC_TARGET)) {
18858735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
18868735SEnrico.Perla@Sun.COM 		} else {
18878735SEnrico.Perla@Sun.COM 			ret = update_dircache(file, flags);
18888735SEnrico.Perla@Sun.COM 			if (ret == BAM_ERROR) {
18898735SEnrico.Perla@Sun.COM 				bam_error(UPDT_CACHE_FAIL, file);
18908735SEnrico.Perla@Sun.COM 				return (-1);
18918735SEnrico.Perla@Sun.COM 			}
18928735SEnrico.Perla@Sun.COM 		}
18938735SEnrico.Perla@Sun.COM 
18940Sstevel@tonic-gate 		if (bam_verbose)
18950Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
18960Sstevel@tonic-gate 		return (0);
18970Sstevel@tonic-gate 	}
18980Sstevel@tonic-gate 
18990Sstevel@tonic-gate 	/*
19008735SEnrico.Perla@Sun.COM 	 * If we got there, the file is already listed as to be included in the
19018735SEnrico.Perla@Sun.COM 	 * iso image. We just need to know if we are going to rebuild it or not
19028735SEnrico.Perla@Sun.COM 	 */
19038735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET) &&
19048954SEnrico.Perla@Sun.COM 	    is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
19058735SEnrico.Perla@Sun.COM 		return (0);
19068735SEnrico.Perla@Sun.COM 	/*
19070Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
19080Sstevel@tonic-gate 	 */
19090Sstevel@tonic-gate 	assert(sz == 2);
19100Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
19110Sstevel@tonic-gate 
19128735SEnrico.Perla@Sun.COM 	if (flags != FTW_D && (filestat[0] != st->st_size ||
19138735SEnrico.Perla@Sun.COM 	    filestat[1] != st->st_mtime)) {
19143615Ssetje 		if (bam_smf_check) {
19153615Ssetje 			safefilep = safefiles;
1916*9500SGangadhar.M@Sun.COM 			while (safefilep != NULL &&
1917*9500SGangadhar.M@Sun.COM 			    safefilep->name[0] != '\0') {
1918*9500SGangadhar.M@Sun.COM 				if (regcomp(&re, safefilep->name,
1919*9500SGangadhar.M@Sun.COM 				    REG_EXTENDED|REG_NOSUB) == 0) {
1920*9500SGangadhar.M@Sun.COM 					status = regexec(&re,
1921*9500SGangadhar.M@Sun.COM 					    file + bam_rootlen, 0, NULL, 0);
1922*9500SGangadhar.M@Sun.COM 					regfree(&re);
1923*9500SGangadhar.M@Sun.COM 					if (status == 0) {
1924*9500SGangadhar.M@Sun.COM 						(void) creat(NEED_UPDATE_FILE,
1925*9500SGangadhar.M@Sun.COM 						    0644);
1926*9500SGangadhar.M@Sun.COM 						return (0);
1927*9500SGangadhar.M@Sun.COM 					}
19283615Ssetje 				}
19293615Ssetje 				safefilep = safefilep->next;
19303615Ssetje 			}
19313615Ssetje 		}
19328735SEnrico.Perla@Sun.COM 
19338735SEnrico.Perla@Sun.COM 		if (is_flag_on(IS_SPARC_TARGET)) {
19348735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
19358735SEnrico.Perla@Sun.COM 		} else {
19368735SEnrico.Perla@Sun.COM 			ret = update_dircache(file, flags);
19378735SEnrico.Perla@Sun.COM 			if (ret == BAM_ERROR) {
19388735SEnrico.Perla@Sun.COM 				bam_error(UPDT_CACHE_FAIL, file);
19398735SEnrico.Perla@Sun.COM 				return (-1);
19408735SEnrico.Perla@Sun.COM 			}
19418735SEnrico.Perla@Sun.COM 		}
19428735SEnrico.Perla@Sun.COM 
19430Sstevel@tonic-gate 		if (bam_verbose)
19440Sstevel@tonic-gate 			if (bam_smf_check)
19450Sstevel@tonic-gate 				bam_print("    %s\n", file);
19460Sstevel@tonic-gate 			else
19470Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
19480Sstevel@tonic-gate 	}
19490Sstevel@tonic-gate 
19500Sstevel@tonic-gate 	return (0);
19510Sstevel@tonic-gate }
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate /*
19548735SEnrico.Perla@Sun.COM  * Remove a directory path recursively
19558735SEnrico.Perla@Sun.COM  */
19568735SEnrico.Perla@Sun.COM static int
19578735SEnrico.Perla@Sun.COM rmdir_r(char *path)
19588735SEnrico.Perla@Sun.COM {
19598735SEnrico.Perla@Sun.COM 	struct dirent 	*d = NULL;
19608735SEnrico.Perla@Sun.COM 	DIR 		*dir = NULL;
19618735SEnrico.Perla@Sun.COM 	char 		tpath[PATH_MAX];
19628735SEnrico.Perla@Sun.COM 	struct stat 	sb;
19638735SEnrico.Perla@Sun.COM 
19648735SEnrico.Perla@Sun.COM 	if ((dir = opendir(path)) == NULL)
19658735SEnrico.Perla@Sun.COM 		return (-1);
19668735SEnrico.Perla@Sun.COM 
19678735SEnrico.Perla@Sun.COM 	while (d = readdir(dir)) {
19688735SEnrico.Perla@Sun.COM 		if ((strcmp(d->d_name, ".") != 0) &&
19698735SEnrico.Perla@Sun.COM 		    (strcmp(d->d_name, "..") != 0)) {
19708735SEnrico.Perla@Sun.COM 			(void) snprintf(tpath, sizeof (tpath), "%s/%s",
19718735SEnrico.Perla@Sun.COM 			    path, d->d_name);
19728735SEnrico.Perla@Sun.COM 			if (stat(tpath, &sb) == 0) {
19738735SEnrico.Perla@Sun.COM 				if (sb.st_mode & S_IFDIR)
19748735SEnrico.Perla@Sun.COM 					(void) rmdir_r(tpath);
19758735SEnrico.Perla@Sun.COM 				else
19768735SEnrico.Perla@Sun.COM 					(void) remove(tpath);
19778735SEnrico.Perla@Sun.COM 			}
19788735SEnrico.Perla@Sun.COM 		}
19798735SEnrico.Perla@Sun.COM 	}
19808735SEnrico.Perla@Sun.COM 	return (remove(path));
19818735SEnrico.Perla@Sun.COM }
19828735SEnrico.Perla@Sun.COM 
19838735SEnrico.Perla@Sun.COM /*
19848735SEnrico.Perla@Sun.COM  * Check if cache directory exists and, if not, create it and update flags
19858735SEnrico.Perla@Sun.COM  * accordingly. If the path exists, but it's not a directory, a best effort
19868735SEnrico.Perla@Sun.COM  * attempt to remove and recreate it is made.
19878735SEnrico.Perla@Sun.COM  * If the user requested a 'purge', always recreate the directory from scratch.
19888735SEnrico.Perla@Sun.COM  */
19898735SEnrico.Perla@Sun.COM static int
19908735SEnrico.Perla@Sun.COM set_cache_dir(char *root, int what)
19918735SEnrico.Perla@Sun.COM {
19928735SEnrico.Perla@Sun.COM 	struct stat	sb;
19938735SEnrico.Perla@Sun.COM 	int		ret = 0;
19948735SEnrico.Perla@Sun.COM 
19958735SEnrico.Perla@Sun.COM 	ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
19968735SEnrico.Perla@Sun.COM 	    "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
19978735SEnrico.Perla@Sun.COM 	    "/amd64" : "", CACHEDIR_SUFFIX);
19988735SEnrico.Perla@Sun.COM 
19998735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (get_cachedir(what))) {
20008735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
20018735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
20028735SEnrico.Perla@Sun.COM 	}
20038735SEnrico.Perla@Sun.COM 
20049233SEnrico.Perla@Sun.COM 	if (bam_purge || is_flag_on(INVALIDATE_CACHE))
20058735SEnrico.Perla@Sun.COM 		(void) rmdir_r(get_cachedir(what));
20068735SEnrico.Perla@Sun.COM 
20078735SEnrico.Perla@Sun.COM 	if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
20088735SEnrico.Perla@Sun.COM 		/* best effort unlink attempt, mkdir will catch errors */
20098735SEnrico.Perla@Sun.COM 		(void) unlink(get_cachedir(what));
20108735SEnrico.Perla@Sun.COM 
20118735SEnrico.Perla@Sun.COM 		if (bam_verbose)
20128735SEnrico.Perla@Sun.COM 			bam_print(UPDATE_CDIR_MISS, get_cachedir(what));
20138735SEnrico.Perla@Sun.COM 		ret = mkdir(get_cachedir(what), DIR_PERMS);
20148735SEnrico.Perla@Sun.COM 		if (ret < 0) {
20158735SEnrico.Perla@Sun.COM 			bam_error(MKDIR_FAILED, get_cachedir(what),
20168735SEnrico.Perla@Sun.COM 			    strerror(errno));
20178735SEnrico.Perla@Sun.COM 			get_cachedir(what)[0] = '\0';
20188735SEnrico.Perla@Sun.COM 			return (ret);
20198735SEnrico.Perla@Sun.COM 		}
20208735SEnrico.Perla@Sun.COM 		set_flag(NEED_CACHE_DIR);
20218735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
20228735SEnrico.Perla@Sun.COM 	}
20238735SEnrico.Perla@Sun.COM 
20248735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
20258735SEnrico.Perla@Sun.COM }
20268735SEnrico.Perla@Sun.COM 
20278735SEnrico.Perla@Sun.COM static int
20288735SEnrico.Perla@Sun.COM set_update_dir(char *root, int what)
20298735SEnrico.Perla@Sun.COM {
20308735SEnrico.Perla@Sun.COM 	struct stat	sb;
20318735SEnrico.Perla@Sun.COM 	int		ret;
20328735SEnrico.Perla@Sun.COM 
20338735SEnrico.Perla@Sun.COM 	if (is_dir_flag_on(what, NO_MULTI))
20348735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
20358735SEnrico.Perla@Sun.COM 
20368735SEnrico.Perla@Sun.COM 	if (!bam_extend) {
20378735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
20388735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
20398735SEnrico.Perla@Sun.COM 	}
20408735SEnrico.Perla@Sun.COM 
20418735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
20428735SEnrico.Perla@Sun.COM 		ret = snprintf(get_updatedir(what),
20438735SEnrico.Perla@Sun.COM 		    sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
20448735SEnrico.Perla@Sun.COM 		    ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
20458735SEnrico.Perla@Sun.COM 	else
20468735SEnrico.Perla@Sun.COM 		ret = snprintf(get_updatedir(what),
20478735SEnrico.Perla@Sun.COM 		    sizeof (get_updatedir(what)), "%s%s%s%s", root,
20488735SEnrico.Perla@Sun.COM 		    ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
20498735SEnrico.Perla@Sun.COM 
20508735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (get_updatedir(what))) {
20518735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
20528735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
20538735SEnrico.Perla@Sun.COM 	}
20548735SEnrico.Perla@Sun.COM 
20558735SEnrico.Perla@Sun.COM 	if (stat(get_updatedir(what), &sb) == 0) {
20568735SEnrico.Perla@Sun.COM 		if (S_ISDIR(sb.st_mode))
20578735SEnrico.Perla@Sun.COM 			ret = rmdir_r(get_updatedir(what));
20588735SEnrico.Perla@Sun.COM 		else
20598735SEnrico.Perla@Sun.COM 			ret = unlink(get_updatedir(what));
20608735SEnrico.Perla@Sun.COM 
20618735SEnrico.Perla@Sun.COM 		if (ret != 0)
20628735SEnrico.Perla@Sun.COM 			set_dir_flag(what, NO_MULTI);
20638735SEnrico.Perla@Sun.COM 	}
20648735SEnrico.Perla@Sun.COM 
20658735SEnrico.Perla@Sun.COM 	if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
20668735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
20678735SEnrico.Perla@Sun.COM 
20688735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
20698735SEnrico.Perla@Sun.COM }
20708735SEnrico.Perla@Sun.COM 
20718735SEnrico.Perla@Sun.COM static int
20728735SEnrico.Perla@Sun.COM is_valid_archive(char *root, int what)
20738735SEnrico.Perla@Sun.COM {
20748735SEnrico.Perla@Sun.COM 	char 		archive_path[PATH_MAX];
20759233SEnrico.Perla@Sun.COM 	char		timestamp_path[PATH_MAX];
20769233SEnrico.Perla@Sun.COM 	struct stat 	sb, timestamp;
20778735SEnrico.Perla@Sun.COM 	int 		ret;
20788735SEnrico.Perla@Sun.COM 
20798735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
20808735SEnrico.Perla@Sun.COM 		ret = snprintf(archive_path, sizeof (archive_path),
20818735SEnrico.Perla@Sun.COM 		    "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
20828735SEnrico.Perla@Sun.COM 		    ARCHIVE_SUFFIX);
20838735SEnrico.Perla@Sun.COM 	else
20848735SEnrico.Perla@Sun.COM 		ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
20858735SEnrico.Perla@Sun.COM 		    root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
20868735SEnrico.Perla@Sun.COM 
20878735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (archive_path)) {
20888735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
20898735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
20908735SEnrico.Perla@Sun.COM 	}
20918735SEnrico.Perla@Sun.COM 
20928735SEnrico.Perla@Sun.COM 	if (stat(archive_path, &sb) != 0) {
20938735SEnrico.Perla@Sun.COM 		if (bam_verbose && !bam_check)
20948735SEnrico.Perla@Sun.COM 			bam_print(UPDATE_ARCH_MISS, archive_path);
20958735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NEED_UPDATE);
20968735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
20978735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
20988735SEnrico.Perla@Sun.COM 	}
20998735SEnrico.Perla@Sun.COM 
21009233SEnrico.Perla@Sun.COM 	/*
21019233SEnrico.Perla@Sun.COM 	 * The timestamp file is used to prevent stale files in the archive
21029233SEnrico.Perla@Sun.COM 	 * cache.
21039233SEnrico.Perla@Sun.COM 	 * Stale files can happen if the system is booted back and forth across
21049233SEnrico.Perla@Sun.COM 	 * the transition from bootadm-before-the-cache to
21059233SEnrico.Perla@Sun.COM 	 * bootadm-after-the-cache, since older versions of bootadm don't know
21069233SEnrico.Perla@Sun.COM 	 * about the existence of the archive cache.
21079233SEnrico.Perla@Sun.COM 	 *
21089233SEnrico.Perla@Sun.COM 	 * Since only bootadm-after-the-cache versions know about about this
21099233SEnrico.Perla@Sun.COM 	 * file, we require that the boot archive be older than this file.
21109233SEnrico.Perla@Sun.COM 	 */
21119233SEnrico.Perla@Sun.COM 	ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
21129233SEnrico.Perla@Sun.COM 	    FILE_STAT_TIMESTAMP);
21139233SEnrico.Perla@Sun.COM 
21149233SEnrico.Perla@Sun.COM 	if (ret >= sizeof (timestamp_path)) {
21159233SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
21169233SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
21179233SEnrico.Perla@Sun.COM 	}
21189233SEnrico.Perla@Sun.COM 
21199233SEnrico.Perla@Sun.COM 	if (stat(timestamp_path, &timestamp) != 0 ||
21209233SEnrico.Perla@Sun.COM 	    sb.st_mtime > timestamp.st_mtime) {
21219233SEnrico.Perla@Sun.COM 		if (bam_verbose && !bam_check)
21229233SEnrico.Perla@Sun.COM 			bam_print(UPDATE_CACHE_OLD, timestamp);
21239233SEnrico.Perla@Sun.COM 		/*
21249233SEnrico.Perla@Sun.COM 		 * Don't generate a false positive for the boot-archive service
21259233SEnrico.Perla@Sun.COM 		 * but trigger an update of the archive cache in
21269233SEnrico.Perla@Sun.COM 		 * boot-archive-update.
21279233SEnrico.Perla@Sun.COM 		 */
21289233SEnrico.Perla@Sun.COM 		if (bam_smf_check) {
21299233SEnrico.Perla@Sun.COM 			(void) creat(NEED_UPDATE_FILE, 0644);
21309233SEnrico.Perla@Sun.COM 			return (BAM_SUCCESS);
21319233SEnrico.Perla@Sun.COM 		}
21329233SEnrico.Perla@Sun.COM 
21339233SEnrico.Perla@Sun.COM 		set_flag(INVALIDATE_CACHE);
21349233SEnrico.Perla@Sun.COM 		set_dir_flag(what, NEED_UPDATE);
21359233SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
21369233SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
21379233SEnrico.Perla@Sun.COM 	}
21389233SEnrico.Perla@Sun.COM 
21398735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET))
21408735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
21418735SEnrico.Perla@Sun.COM 
21428735SEnrico.Perla@Sun.COM 	if (bam_extend && sb.st_size > BA_SIZE_MAX) {
21438735SEnrico.Perla@Sun.COM 		if (bam_verbose && !bam_check)
21448735SEnrico.Perla@Sun.COM 			bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX);
21458735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
21468735SEnrico.Perla@Sun.COM 	}
21478735SEnrico.Perla@Sun.COM 
21488735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
21498735SEnrico.Perla@Sun.COM }
21508735SEnrico.Perla@Sun.COM 
21518735SEnrico.Perla@Sun.COM /*
21528735SEnrico.Perla@Sun.COM  * Check flags and presence of required files and directories.
21530Sstevel@tonic-gate  * The force flag and/or absence of files should
21540Sstevel@tonic-gate  * trigger an update.
21550Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
21560Sstevel@tonic-gate  * (as -n should only produce parseable output.)
21570Sstevel@tonic-gate  */
21588735SEnrico.Perla@Sun.COM static int
21590Sstevel@tonic-gate check_flags_and_files(char *root)
21600Sstevel@tonic-gate {
21618735SEnrico.Perla@Sun.COM 
21628735SEnrico.Perla@Sun.COM 	struct stat 	sb;
21638735SEnrico.Perla@Sun.COM 	int 		ret;
21648735SEnrico.Perla@Sun.COM 
21658735SEnrico.Perla@Sun.COM 	/*
21668735SEnrico.Perla@Sun.COM 	 * If archive is missing, create archive
21678735SEnrico.Perla@Sun.COM 	 */
21688735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET)) {
21698735SEnrico.Perla@Sun.COM 		ret = is_valid_archive(root, FILE64);
21708954SEnrico.Perla@Sun.COM 		if (ret == BAM_ERROR)
21718954SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
21728735SEnrico.Perla@Sun.COM 	} else {
21738735SEnrico.Perla@Sun.COM 		int	what = FILE32;
21748735SEnrico.Perla@Sun.COM 		do {
21758735SEnrico.Perla@Sun.COM 			ret = is_valid_archive(root, what);
21768735SEnrico.Perla@Sun.COM 			if (ret == BAM_ERROR)
21778735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
21788735SEnrico.Perla@Sun.COM 			what++;
21798735SEnrico.Perla@Sun.COM 		} while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
21808735SEnrico.Perla@Sun.COM 	}
21818735SEnrico.Perla@Sun.COM 
21828735SEnrico.Perla@Sun.COM 	if (bam_nowrite())
21838735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
21848735SEnrico.Perla@Sun.COM 
21858735SEnrico.Perla@Sun.COM 
21868735SEnrico.Perla@Sun.COM 	/*
21878735SEnrico.Perla@Sun.COM 	 * check if cache directories exist on x86.
21888735SEnrico.Perla@Sun.COM 	 * check (and always open) the cache file on SPARC.
21898735SEnrico.Perla@Sun.COM 	 */
21908735SEnrico.Perla@Sun.COM 	if (is_sparc()) {
21918735SEnrico.Perla@Sun.COM 		ret = snprintf(get_cachedir(FILE64),
21928735SEnrico.Perla@Sun.COM 		    sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
21938735SEnrico.Perla@Sun.COM 		    ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
21948735SEnrico.Perla@Sun.COM 
21958735SEnrico.Perla@Sun.COM 		if (ret >= sizeof (get_cachedir(FILE64))) {
21968735SEnrico.Perla@Sun.COM 			bam_error(PATH_TOO_LONG, rootbuf);
21978735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
21988735SEnrico.Perla@Sun.COM 		}
21998735SEnrico.Perla@Sun.COM 
22008735SEnrico.Perla@Sun.COM 		if (stat(get_cachedir(FILE64), &sb) != 0) {
22018735SEnrico.Perla@Sun.COM 			set_flag(NEED_CACHE_DIR);
22028735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
22038735SEnrico.Perla@Sun.COM 		}
22048735SEnrico.Perla@Sun.COM 
22058735SEnrico.Perla@Sun.COM 		walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
22068735SEnrico.Perla@Sun.COM 		if (walk_arg.sparcfile == NULL) {
22078735SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, get_cachedir(FILE64),
22088735SEnrico.Perla@Sun.COM 			    strerror(errno));
22098735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
22108735SEnrico.Perla@Sun.COM 		}
22118735SEnrico.Perla@Sun.COM 
22128735SEnrico.Perla@Sun.COM 		set_dir_present(FILE64);
22138735SEnrico.Perla@Sun.COM 	} else {
22148735SEnrico.Perla@Sun.COM 		int	what = FILE32;
22158735SEnrico.Perla@Sun.COM 
22168735SEnrico.Perla@Sun.COM 		do {
22178735SEnrico.Perla@Sun.COM 			if (set_cache_dir(root, what) != 0)
22188735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
22198735SEnrico.Perla@Sun.COM 
22208735SEnrico.Perla@Sun.COM 			set_dir_present(what);
22218735SEnrico.Perla@Sun.COM 
22228735SEnrico.Perla@Sun.COM 			if (set_update_dir(root, what) != 0)
22238735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
22248735SEnrico.Perla@Sun.COM 			what++;
22258735SEnrico.Perla@Sun.COM 		} while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
22268735SEnrico.Perla@Sun.COM 	}
22270Sstevel@tonic-gate 
22280Sstevel@tonic-gate 	/*
22290Sstevel@tonic-gate 	 * if force, create archive unconditionally
22300Sstevel@tonic-gate 	 */
22310Sstevel@tonic-gate 	if (bam_force) {
22328735SEnrico.Perla@Sun.COM 		if (!is_sparc())
22338735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE32, NEED_UPDATE);
22348735SEnrico.Perla@Sun.COM 		set_dir_flag(FILE64, NEED_UPDATE);
22358735SEnrico.Perla@Sun.COM 		if (bam_verbose)
22360Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
22378735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
22388735SEnrico.Perla@Sun.COM 	}
22398735SEnrico.Perla@Sun.COM 
22408735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
22410Sstevel@tonic-gate }
22420Sstevel@tonic-gate 
22430Sstevel@tonic-gate static error_t
22440Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
22450Sstevel@tonic-gate {
22468735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
22478735SEnrico.Perla@Sun.COM 	FILE 		*fp;
22488735SEnrico.Perla@Sun.COM 	char 		buf[BAM_MAXLINE];
22498735SEnrico.Perla@Sun.COM 	const char 	*fcn = "read_one_list()";
22500Sstevel@tonic-gate 
22510Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
22520Sstevel@tonic-gate 
22530Sstevel@tonic-gate 	fp = fopen(path, "r");
22540Sstevel@tonic-gate 	if (fp == NULL) {
22556448Svikram 		BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
22560Sstevel@tonic-gate 		return (BAM_ERROR);
22570Sstevel@tonic-gate 	}
22580Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2259316Svikram 		/* skip blank lines */
2260316Svikram 		if (strspn(buf, " \t") == strlen(buf))
2261316Svikram 			continue;
22620Sstevel@tonic-gate 		append_to_flist(flistp, buf);
22630Sstevel@tonic-gate 	}
22640Sstevel@tonic-gate 	if (fclose(fp) != 0) {
22650Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
22660Sstevel@tonic-gate 		return (BAM_ERROR);
22670Sstevel@tonic-gate 	}
22680Sstevel@tonic-gate 	return (BAM_SUCCESS);
22690Sstevel@tonic-gate }
22700Sstevel@tonic-gate 
22710Sstevel@tonic-gate static error_t
22720Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
22730Sstevel@tonic-gate {
22748735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
22758735SEnrico.Perla@Sun.COM 	char 		cmd[PATH_MAX];
22768735SEnrico.Perla@Sun.COM 	struct stat 	sb;
22778735SEnrico.Perla@Sun.COM 	int 		n, rval;
22788735SEnrico.Perla@Sun.COM 	const char 	*fcn = "read_list()";
22790Sstevel@tonic-gate 
22800Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
22810Sstevel@tonic-gate 
22820Sstevel@tonic-gate 	/*
22835648Ssetje 	 * build and check path to extract_boot_filelist.ksh
22845648Ssetje 	 */
22855648Ssetje 	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
22865648Ssetje 	if (n >= sizeof (path)) {
22875648Ssetje 		bam_error(NO_FLIST);
22885648Ssetje 		return (BAM_ERROR);
22895648Ssetje 	}
22905648Ssetje 
22918954SEnrico.Perla@Sun.COM 	if (is_safe_exec(path) == BAM_ERROR)
22928954SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
22938954SEnrico.Perla@Sun.COM 
22945648Ssetje 	/*
22955648Ssetje 	 * If extract_boot_filelist is present, exec it, otherwise read
22965648Ssetje 	 * the filelists directly, for compatibility with older images.
22970Sstevel@tonic-gate 	 */
22985648Ssetje 	if (stat(path, &sb) == 0) {
22995648Ssetje 		/*
23005648Ssetje 		 * build arguments to exec extract_boot_filelist.ksh
23015648Ssetje 		 */
23026319Sjg 		char *rootarg, *platarg;
23036319Sjg 		int platarglen = 1, rootarglen = 1;
23046319Sjg 		if (strlen(root) > 1)
23056319Sjg 			rootarglen += strlen(root) + strlen("-R ");
23066319Sjg 		if (bam_alt_platform)
23076319Sjg 			platarglen += strlen(bam_platform) + strlen("-p ");
23086319Sjg 		platarg = s_calloc(1, platarglen);
23096319Sjg 		rootarg = s_calloc(1, rootarglen);
23106319Sjg 		*platarg = 0;
23116319Sjg 		*rootarg = 0;
23126319Sjg 
23135648Ssetje 		if (strlen(root) > 1) {
23146319Sjg 			(void) snprintf(rootarg, rootarglen,
23156319Sjg 			    "-R %s", root);
23165648Ssetje 		}
23176319Sjg 		if (bam_alt_platform) {
23186319Sjg 			(void) snprintf(platarg, platarglen,
23196319Sjg 			    "-p %s", bam_platform);
23206319Sjg 		}
23216319Sjg 		n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
23226319Sjg 		    path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
23236319Sjg 		free(platarg);
23246319Sjg 		free(rootarg);
23255648Ssetje 		if (n >= sizeof (cmd)) {
23265648Ssetje 			bam_error(NO_FLIST);
23275648Ssetje 			return (BAM_ERROR);
23285648Ssetje 		}
23295648Ssetje 		if (exec_cmd(cmd, flistp) != 0) {
23306448Svikram 			BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
23315648Ssetje 			return (BAM_ERROR);
23325648Ssetje 		}
23335648Ssetje 	} else {
23345648Ssetje 		/*
23355648Ssetje 		 * Read current lists of files - only the first is mandatory
23365648Ssetje 		 */
23375648Ssetje 		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
23385648Ssetje 		if (rval != BAM_SUCCESS)
23395648Ssetje 			return (rval);
23405648Ssetje 		(void) read_one_list(root, flistp, ETC_FILE_LIST);
23415648Ssetje 	}
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 	if (flistp->head == NULL) {
23440Sstevel@tonic-gate 		bam_error(NO_FLIST);
23450Sstevel@tonic-gate 		return (BAM_ERROR);
23460Sstevel@tonic-gate 	}
23470Sstevel@tonic-gate 
23480Sstevel@tonic-gate 	return (BAM_SUCCESS);
23490Sstevel@tonic-gate }
23500Sstevel@tonic-gate 
23510Sstevel@tonic-gate static void
23520Sstevel@tonic-gate getoldstat(char *root)
23530Sstevel@tonic-gate {
23548735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
23558735SEnrico.Perla@Sun.COM 	int 		fd, error;
23568735SEnrico.Perla@Sun.COM 	struct stat 	sb;
23578735SEnrico.Perla@Sun.COM 	char 		*ostat;
23580Sstevel@tonic-gate 
23590Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
23600Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
23610Sstevel@tonic-gate 	if (fd == -1) {
23620Sstevel@tonic-gate 		if (bam_verbose)
23630Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
23648735SEnrico.Perla@Sun.COM 		goto out_err;
23650Sstevel@tonic-gate 	}
23660Sstevel@tonic-gate 
23670Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
23680Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
23698735SEnrico.Perla@Sun.COM 		goto out_err;
23700Sstevel@tonic-gate 	}
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
23730Sstevel@tonic-gate 
23740Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
23750Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
23760Sstevel@tonic-gate 		free(ostat);
23778735SEnrico.Perla@Sun.COM 		goto out_err;
23780Sstevel@tonic-gate 	}
23790Sstevel@tonic-gate 
23800Sstevel@tonic-gate 	(void) close(fd);
23818735SEnrico.Perla@Sun.COM 	fd = -1;
23820Sstevel@tonic-gate 
23830Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
23840Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
23850Sstevel@tonic-gate 
23860Sstevel@tonic-gate 	free(ostat);
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate 	if (error) {
23890Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
23900Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
23918735SEnrico.Perla@Sun.COM 		goto out_err;
23928735SEnrico.Perla@Sun.COM 	} else {
23930Sstevel@tonic-gate 		return;
23940Sstevel@tonic-gate 	}
23958735SEnrico.Perla@Sun.COM 
23968735SEnrico.Perla@Sun.COM out_err:
23978735SEnrico.Perla@Sun.COM 	if (fd != -1)
23988735SEnrico.Perla@Sun.COM 		(void) close(fd);
23998954SEnrico.Perla@Sun.COM 	if (!is_flag_on(IS_SPARC_TARGET))
24008954SEnrico.Perla@Sun.COM 		set_dir_flag(FILE32, NEED_UPDATE);
24018735SEnrico.Perla@Sun.COM 	set_dir_flag(FILE64, NEED_UPDATE);
24028735SEnrico.Perla@Sun.COM }
24038735SEnrico.Perla@Sun.COM 
24048735SEnrico.Perla@Sun.COM /* Best effort stale entry removal */
24058735SEnrico.Perla@Sun.COM static void
24068735SEnrico.Perla@Sun.COM delete_stale(char *file, int what)
24078735SEnrico.Perla@Sun.COM {
24088735SEnrico.Perla@Sun.COM 	char		path[PATH_MAX];
24098735SEnrico.Perla@Sun.COM 	struct stat	sb;
24108735SEnrico.Perla@Sun.COM 
24118735SEnrico.Perla@Sun.COM 	(void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
24128735SEnrico.Perla@Sun.COM 	if (!bam_check && stat(path, &sb) == 0) {
24138735SEnrico.Perla@Sun.COM 		if (sb.st_mode & S_IFDIR)
24148735SEnrico.Perla@Sun.COM 			(void) rmdir_r(path);
24158735SEnrico.Perla@Sun.COM 		else
24168735SEnrico.Perla@Sun.COM 			(void) unlink(path);
24178735SEnrico.Perla@Sun.COM 
24188735SEnrico.Perla@Sun.COM 		set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
24198735SEnrico.Perla@Sun.COM 	}
24200Sstevel@tonic-gate }
24210Sstevel@tonic-gate 
24222583Svikram /*
24232583Svikram  * Checks if a file in the current (old) archive has
24242583Svikram  * been deleted from the root filesystem. This is needed for
24252583Svikram  * software like Trusted Extensions (TX) that switch early
24262583Svikram  * in boot based on presence/absence of a kernel module.
24272583Svikram  */
24282583Svikram static void
24292583Svikram check4stale(char *root)
24302583Svikram {
24312583Svikram 	nvpair_t	*nvp;
24322583Svikram 	nvlist_t	*nvlp;
24332583Svikram 	char 		*file;
24342583Svikram 	char		path[PATH_MAX];
24352583Svikram 
24362583Svikram 	/*
24372583Svikram 	 * Skip stale file check during smf check
24382583Svikram 	 */
24392583Svikram 	if (bam_smf_check)
24402583Svikram 		return;
24412583Svikram 
24428735SEnrico.Perla@Sun.COM 	/*
24438735SEnrico.Perla@Sun.COM 	 * If we need to (re)create the cache, there's no need to check for
24448735SEnrico.Perla@Sun.COM 	 * stale files
24458735SEnrico.Perla@Sun.COM 	 */
24468735SEnrico.Perla@Sun.COM 	if (is_flag_on(NEED_CACHE_DIR))
24478735SEnrico.Perla@Sun.COM 		return;
24488735SEnrico.Perla@Sun.COM 
24492583Svikram 	/* Nothing to do if no old stats */
24502583Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
24512583Svikram 		return;
24522583Svikram 
24532583Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
24542583Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
24552583Svikram 		file = nvpair_name(nvp);
24562583Svikram 		if (file == NULL)
24572583Svikram 			continue;
24582583Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
24592583Svikram 		    root, file);
24608735SEnrico.Perla@Sun.COM 		if (access(path, F_OK) < 0) {
24618735SEnrico.Perla@Sun.COM 			int	what;
24628735SEnrico.Perla@Sun.COM 
24638954SEnrico.Perla@Sun.COM 			if (bam_verbose)
24648954SEnrico.Perla@Sun.COM 				bam_print(PARSEABLE_STALE_FILE, path);
24658954SEnrico.Perla@Sun.COM 
24668954SEnrico.Perla@Sun.COM 			if (is_flag_on(IS_SPARC_TARGET)) {
24678735SEnrico.Perla@Sun.COM 				set_dir_flag(FILE64, NEED_UPDATE);
24688954SEnrico.Perla@Sun.COM 			} else {
24698954SEnrico.Perla@Sun.COM 				for (what = FILE32; what < CACHEDIR_NUM; what++)
24708954SEnrico.Perla@Sun.COM 					if (has_cachedir(what))
24718954SEnrico.Perla@Sun.COM 						delete_stale(file, what);
24728954SEnrico.Perla@Sun.COM 			}
24732583Svikram 		}
24742583Svikram 	}
24752583Svikram }
24762583Svikram 
24770Sstevel@tonic-gate static void
24780Sstevel@tonic-gate create_newstat(void)
24790Sstevel@tonic-gate {
24800Sstevel@tonic-gate 	int error;
24810Sstevel@tonic-gate 
24820Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
24830Sstevel@tonic-gate 	if (error) {
24840Sstevel@tonic-gate 		/*
24850Sstevel@tonic-gate 		 * Not fatal - we can still create archive
24860Sstevel@tonic-gate 		 */
24870Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
24880Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
24890Sstevel@tonic-gate 	}
24900Sstevel@tonic-gate }
24910Sstevel@tonic-gate 
24928735SEnrico.Perla@Sun.COM static int
24930Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
24940Sstevel@tonic-gate {
24950Sstevel@tonic-gate 	char path[PATH_MAX];
24960Sstevel@tonic-gate 	line_t *lp;
24970Sstevel@tonic-gate 
24980Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
24995648Ssetje 		/*
25005648Ssetje 		 * Don't follow symlinks.  A symlink must refer to
25015648Ssetje 		 * a file that would appear in the archive through
25025648Ssetje 		 * a direct reference.  This matches the archive
25035648Ssetje 		 * construction behavior.
25045648Ssetje 		 */
25050Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
25065648Ssetje 		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
25078735SEnrico.Perla@Sun.COM 			if (is_flag_on(UPDATE_ERROR))
25088735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
25090Sstevel@tonic-gate 			/*
25100Sstevel@tonic-gate 			 * Some files may not exist.
25110Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
25120Sstevel@tonic-gate 			 * Emit verbose message only
25130Sstevel@tonic-gate 			 */
25140Sstevel@tonic-gate 			if (bam_verbose)
25150Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
25160Sstevel@tonic-gate 		}
25170Sstevel@tonic-gate 	}
25188735SEnrico.Perla@Sun.COM 
25198735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
25200Sstevel@tonic-gate }
25210Sstevel@tonic-gate 
25229233SEnrico.Perla@Sun.COM /*
25239233SEnrico.Perla@Sun.COM  * Update the timestamp file.
25249233SEnrico.Perla@Sun.COM  */
25259233SEnrico.Perla@Sun.COM static void
25269233SEnrico.Perla@Sun.COM update_timestamp(char *root)
25279233SEnrico.Perla@Sun.COM {
25289233SEnrico.Perla@Sun.COM 	char	timestamp_path[PATH_MAX];
25299233SEnrico.Perla@Sun.COM 
25309233SEnrico.Perla@Sun.COM 	/* this path length has already been checked in check_flags_and_files */
25319233SEnrico.Perla@Sun.COM 	(void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
25329233SEnrico.Perla@Sun.COM 	    FILE_STAT_TIMESTAMP);
25339233SEnrico.Perla@Sun.COM 
25349233SEnrico.Perla@Sun.COM 	/*
25359233SEnrico.Perla@Sun.COM 	 * recreate the timestamp file. Since an outdated or absent timestamp
25369233SEnrico.Perla@Sun.COM 	 * file translates in a complete rebuild of the archive cache, notify
25379233SEnrico.Perla@Sun.COM 	 * the user of the performance issue.
25389233SEnrico.Perla@Sun.COM 	 */
25399233SEnrico.Perla@Sun.COM 	if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
25409233SEnrico.Perla@Sun.COM 		bam_error(OPEN_FAIL, timestamp_path, strerror(errno));
25419233SEnrico.Perla@Sun.COM 		bam_error(TIMESTAMP_FAIL, rootbuf);
25429233SEnrico.Perla@Sun.COM 	}
25439233SEnrico.Perla@Sun.COM }
25449233SEnrico.Perla@Sun.COM 
25459233SEnrico.Perla@Sun.COM 
25460Sstevel@tonic-gate static void
25470Sstevel@tonic-gate savenew(char *root)
25480Sstevel@tonic-gate {
25498735SEnrico.Perla@Sun.COM 	char 	path[PATH_MAX];
25508735SEnrico.Perla@Sun.COM 	char 	path2[PATH_MAX];
25518735SEnrico.Perla@Sun.COM 	size_t 	sz;
25528735SEnrico.Perla@Sun.COM 	char 	*nstat;
25538735SEnrico.Perla@Sun.COM 	int 	fd, wrote, error;
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate 	nstat = NULL;
25560Sstevel@tonic-gate 	sz = 0;
25570Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
25580Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
25590Sstevel@tonic-gate 	if (error) {
25600Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
25610Sstevel@tonic-gate 		return;
25620Sstevel@tonic-gate 	}
25630Sstevel@tonic-gate 
25640Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
25650Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
25660Sstevel@tonic-gate 	if (fd == -1) {
25670Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
25680Sstevel@tonic-gate 		free(nstat);
25690Sstevel@tonic-gate 		return;
25700Sstevel@tonic-gate 	}
25710Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
25720Sstevel@tonic-gate 	if (wrote != sz) {
25730Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
25740Sstevel@tonic-gate 		(void) close(fd);
25750Sstevel@tonic-gate 		free(nstat);
25760Sstevel@tonic-gate 		return;
25770Sstevel@tonic-gate 	}
25780Sstevel@tonic-gate 	(void) close(fd);
25790Sstevel@tonic-gate 	free(nstat);
25800Sstevel@tonic-gate 
25810Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
25820Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
25830Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
25840Sstevel@tonic-gate 	}
25850Sstevel@tonic-gate }
25860Sstevel@tonic-gate 
25878735SEnrico.Perla@Sun.COM #define	init_walk_args()	bzero(&walk_arg, sizeof (walk_arg))
25888735SEnrico.Perla@Sun.COM 
25890Sstevel@tonic-gate static void
25900Sstevel@tonic-gate clear_walk_args(void)
25910Sstevel@tonic-gate {
25920Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
25930Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
25940Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
25950Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
25968735SEnrico.Perla@Sun.COM 	if (walk_arg.sparcfile)
25978735SEnrico.Perla@Sun.COM 		(void) fclose(walk_arg.sparcfile);
25980Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
25990Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
26008735SEnrico.Perla@Sun.COM 	walk_arg.sparcfile = NULL;
26010Sstevel@tonic-gate }
26020Sstevel@tonic-gate 
26030Sstevel@tonic-gate /*
26040Sstevel@tonic-gate  * Returns:
26050Sstevel@tonic-gate  *	0 - no update necessary
26060Sstevel@tonic-gate  *	1 - update required.
26070Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
26080Sstevel@tonic-gate  *
26090Sstevel@tonic-gate  * Special handling for check (-n):
26100Sstevel@tonic-gate  * ================================
26110Sstevel@tonic-gate  * The check (-n) option produces parseable output.
26120Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
26130Sstevel@tonic-gate  * to out of sync files.
26140Sstevel@tonic-gate  * All stderr messages are still printed though.
26150Sstevel@tonic-gate  *
26160Sstevel@tonic-gate  */
26170Sstevel@tonic-gate static int
26180Sstevel@tonic-gate update_required(char *root)
26190Sstevel@tonic-gate {
26208735SEnrico.Perla@Sun.COM 	struct stat 	sb;
26218735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
26228735SEnrico.Perla@Sun.COM 	filelist_t 	flist;
26238735SEnrico.Perla@Sun.COM 	filelist_t 	*flistp = &flist;
26248735SEnrico.Perla@Sun.COM 	int 		ret;
26250Sstevel@tonic-gate 
26260Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
26270Sstevel@tonic-gate 
26288735SEnrico.Perla@Sun.COM 	if (is_sparc())
26298735SEnrico.Perla@Sun.COM 		set_flag(IS_SPARC_TARGET);
26300Sstevel@tonic-gate 
26310Sstevel@tonic-gate 	/*
26328735SEnrico.Perla@Sun.COM 	 * Check if cache directories and archives are present
26330Sstevel@tonic-gate 	 */
26348735SEnrico.Perla@Sun.COM 
26358735SEnrico.Perla@Sun.COM 	ret = check_flags_and_files(root);
26368735SEnrico.Perla@Sun.COM 	if (ret < 0)
26378735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
26380Sstevel@tonic-gate 
26390Sstevel@tonic-gate 	/*
26400Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
26410Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
26420Sstevel@tonic-gate 	 */
26430Sstevel@tonic-gate 	if (bam_smf_check) {
26440Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
26450Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
26460Sstevel@tonic-gate 			return (0);
26470Sstevel@tonic-gate 	}
26480Sstevel@tonic-gate 
26498735SEnrico.Perla@Sun.COM 	getoldstat(root);
26500Sstevel@tonic-gate 
26510Sstevel@tonic-gate 	/*
26522583Svikram 	 * Check if the archive contains files that are no longer
26532583Svikram 	 * present on the root filesystem.
26542583Svikram 	 */
26558735SEnrico.Perla@Sun.COM 	check4stale(root);
26562583Svikram 
26572583Svikram 	/*
26580Sstevel@tonic-gate 	 * read list of files
26590Sstevel@tonic-gate 	 */
26600Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
26610Sstevel@tonic-gate 		clear_walk_args();
26620Sstevel@tonic-gate 		return (BAM_ERROR);
26630Sstevel@tonic-gate 	}
26640Sstevel@tonic-gate 
26650Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
26660Sstevel@tonic-gate 
26670Sstevel@tonic-gate 	/*
26680Sstevel@tonic-gate 	 * At this point either the update is required
26690Sstevel@tonic-gate 	 * or the decision is pending. In either case
26700Sstevel@tonic-gate 	 * we need to create new stat nvlist
26710Sstevel@tonic-gate 	 */
26720Sstevel@tonic-gate 	create_newstat();
26730Sstevel@tonic-gate 	/*
26740Sstevel@tonic-gate 	 * This walk does 2 things:
26750Sstevel@tonic-gate 	 *  	- gets new stat data for every file
26760Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
26770Sstevel@tonic-gate 	 */
26788735SEnrico.Perla@Sun.COM 	ret = walk_list(root, &flist);
26790Sstevel@tonic-gate 
26800Sstevel@tonic-gate 	/* done with the file list */
26810Sstevel@tonic-gate 	filelist_free(flistp);
26820Sstevel@tonic-gate 
26838735SEnrico.Perla@Sun.COM 	/* something went wrong */
26848735SEnrico.Perla@Sun.COM 
26858735SEnrico.Perla@Sun.COM 	if (ret == BAM_ERROR) {
26868735SEnrico.Perla@Sun.COM 		bam_error(CACHE_FAIL);
26878735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
26888735SEnrico.Perla@Sun.COM 	}
26898735SEnrico.Perla@Sun.COM 
26900Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
26918954SEnrico.Perla@Sun.COM 		if (walk_arg.sparcfile != NULL)
26928954SEnrico.Perla@Sun.COM 			(void) fclose(walk_arg.sparcfile);
26930Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
26948735SEnrico.Perla@Sun.COM 	}
26958735SEnrico.Perla@Sun.COM 
26968735SEnrico.Perla@Sun.COM 	/* If nothing was updated, discard newstat. */
26978735SEnrico.Perla@Sun.COM 
26988735SEnrico.Perla@Sun.COM 	if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
26998735SEnrico.Perla@Sun.COM 	    !is_dir_flag_on(FILE64, NEED_UPDATE)) {
27000Sstevel@tonic-gate 		clear_walk_args();
27018735SEnrico.Perla@Sun.COM 		return (0);
27028735SEnrico.Perla@Sun.COM 	}
27038735SEnrico.Perla@Sun.COM 
27048954SEnrico.Perla@Sun.COM 	if (walk_arg.sparcfile != NULL)
27058954SEnrico.Perla@Sun.COM 		(void) fclose(walk_arg.sparcfile);
27068735SEnrico.Perla@Sun.COM 
27078735SEnrico.Perla@Sun.COM 	return (1);
27088735SEnrico.Perla@Sun.COM }
27098735SEnrico.Perla@Sun.COM 
27108735SEnrico.Perla@Sun.COM static int
27118735SEnrico.Perla@Sun.COM flushfs(char *root)
27128735SEnrico.Perla@Sun.COM {
27138735SEnrico.Perla@Sun.COM 	char	cmd[PATH_MAX + 30];
27148735SEnrico.Perla@Sun.COM 
27158735SEnrico.Perla@Sun.COM 	(void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
27168735SEnrico.Perla@Sun.COM 	    LOCKFS_PATH, root);
27178735SEnrico.Perla@Sun.COM 
27188735SEnrico.Perla@Sun.COM 	return (exec_cmd(cmd, NULL));
27198735SEnrico.Perla@Sun.COM }
27208735SEnrico.Perla@Sun.COM 
27218735SEnrico.Perla@Sun.COM static int
27228735SEnrico.Perla@Sun.COM do_archive_copy(char *source, char *dest)
27238735SEnrico.Perla@Sun.COM {
27248735SEnrico.Perla@Sun.COM 
27258954SEnrico.Perla@Sun.COM 	sync();
27268735SEnrico.Perla@Sun.COM 
27278735SEnrico.Perla@Sun.COM 	/* the equivalent of mv archive-new-$pid boot_archive */
27288954SEnrico.Perla@Sun.COM 	if (rename(source, dest) != 0) {
27298954SEnrico.Perla@Sun.COM 		(void) unlink(source);
27308735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
27318954SEnrico.Perla@Sun.COM 	}
27328735SEnrico.Perla@Sun.COM 
27338735SEnrico.Perla@Sun.COM 	if (flushfs(bam_root) != 0)
27348954SEnrico.Perla@Sun.COM 		sync();
27358735SEnrico.Perla@Sun.COM 
27368735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
27378735SEnrico.Perla@Sun.COM }
27388735SEnrico.Perla@Sun.COM 
27398735SEnrico.Perla@Sun.COM static int
27408735SEnrico.Perla@Sun.COM check_cmdline(filelist_t flist)
27418735SEnrico.Perla@Sun.COM {
27428735SEnrico.Perla@Sun.COM 	line_t	*lp;
27438735SEnrico.Perla@Sun.COM 
27448735SEnrico.Perla@Sun.COM 	for (lp = flist.head; lp; lp = lp->next) {
27458735SEnrico.Perla@Sun.COM 		if (strstr(lp->line, "Error:") != NULL ||
27468735SEnrico.Perla@Sun.COM 		    strstr(lp->line, "Inode number overflow") != NULL) {
27478954SEnrico.Perla@Sun.COM 			(void) fprintf(stderr, "%s\n", lp->line);
27488735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
27498735SEnrico.Perla@Sun.COM 		}
27508735SEnrico.Perla@Sun.COM 	}
27518735SEnrico.Perla@Sun.COM 
27528735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
27538735SEnrico.Perla@Sun.COM }
27548735SEnrico.Perla@Sun.COM 
27558954SEnrico.Perla@Sun.COM static void
27568954SEnrico.Perla@Sun.COM dump_errormsg(filelist_t flist)
27578954SEnrico.Perla@Sun.COM {
27588954SEnrico.Perla@Sun.COM 	line_t	*lp;
27598954SEnrico.Perla@Sun.COM 
27608954SEnrico.Perla@Sun.COM 	for (lp = flist.head; lp; lp = lp->next)
27618954SEnrico.Perla@Sun.COM 		(void) fprintf(stderr, "%s\n", lp->line);
27628954SEnrico.Perla@Sun.COM }
27638954SEnrico.Perla@Sun.COM 
27648735SEnrico.Perla@Sun.COM static int
27658735SEnrico.Perla@Sun.COM check_archive(char *dest)
27668735SEnrico.Perla@Sun.COM {
27678735SEnrico.Perla@Sun.COM 	struct stat	sb;
27688735SEnrico.Perla@Sun.COM 
27698735SEnrico.Perla@Sun.COM 	if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
27708735SEnrico.Perla@Sun.COM 	    sb.st_size < 10000) {
27718735SEnrico.Perla@Sun.COM 		bam_error(ARCHIVE_BAD, dest);
27728735SEnrico.Perla@Sun.COM 		(void) unlink(dest);
27738735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
27748735SEnrico.Perla@Sun.COM 	}
27758735SEnrico.Perla@Sun.COM 
27768735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
27778735SEnrico.Perla@Sun.COM }
27788735SEnrico.Perla@Sun.COM 
27798735SEnrico.Perla@Sun.COM /*
27808735SEnrico.Perla@Sun.COM  * Returns 1 if mkiso is in the expected PATH, 0 otherwise
27818735SEnrico.Perla@Sun.COM  */
27828735SEnrico.Perla@Sun.COM static int
27838735SEnrico.Perla@Sun.COM is_mkisofs()
27848735SEnrico.Perla@Sun.COM {
27858954SEnrico.Perla@Sun.COM 	if (access(MKISOFS_PATH, X_OK) == 0)
27868954SEnrico.Perla@Sun.COM 		return (1);
27878735SEnrico.Perla@Sun.COM 	return (0);
27888735SEnrico.Perla@Sun.COM }
27898735SEnrico.Perla@Sun.COM 
27908735SEnrico.Perla@Sun.COM #define	MKISO_PARAMS	" -quiet -graft-points -dlrDJN -relaxed-filenames "
27918735SEnrico.Perla@Sun.COM 
27928735SEnrico.Perla@Sun.COM static int
27938735SEnrico.Perla@Sun.COM create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
27948735SEnrico.Perla@Sun.COM {
27958735SEnrico.Perla@Sun.COM 	int		ret;
27968735SEnrico.Perla@Sun.COM 	char		cmdline[3 * PATH_MAX + 64];
27978735SEnrico.Perla@Sun.COM 	filelist_t	flist = {0};
27988735SEnrico.Perla@Sun.COM 	const char	*func = "create_sparc_archive()";
27998735SEnrico.Perla@Sun.COM 
28008735SEnrico.Perla@Sun.COM 	if (access(bootblk, R_OK) == 1) {
28018735SEnrico.Perla@Sun.COM 		bam_error(BOOTBLK_FAIL, bootblk);
28028735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
28038735SEnrico.Perla@Sun.COM 	}
28048735SEnrico.Perla@Sun.COM 
28058735SEnrico.Perla@Sun.COM 	/*
28068735SEnrico.Perla@Sun.COM 	 * Prepare mkisofs command line and execute it
28078735SEnrico.Perla@Sun.COM 	 */
28088735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
28098954SEnrico.Perla@Sun.COM 	    "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
28108735SEnrico.Perla@Sun.COM 	    tempname, list);
28118735SEnrico.Perla@Sun.COM 
28128735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
28138735SEnrico.Perla@Sun.COM 
28148735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
28158954SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
28168954SEnrico.Perla@Sun.COM 		dump_errormsg(flist);
28178735SEnrico.Perla@Sun.COM 		goto out_err;
28188954SEnrico.Perla@Sun.COM 	}
28198735SEnrico.Perla@Sun.COM 
28208735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
28218735SEnrico.Perla@Sun.COM 
28228735SEnrico.Perla@Sun.COM 	/*
28238735SEnrico.Perla@Sun.COM 	 * Prepare dd command line to copy the bootblk on the new archive and
28248735SEnrico.Perla@Sun.COM 	 * execute it
28258735SEnrico.Perla@Sun.COM 	 */
28268735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
28278954SEnrico.Perla@Sun.COM 	    " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
28288735SEnrico.Perla@Sun.COM 	    bootblk, tempname);
28298735SEnrico.Perla@Sun.COM 
28308735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
28318735SEnrico.Perla@Sun.COM 
28328735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
28338735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
28348735SEnrico.Perla@Sun.COM 		goto out_err;
28358735SEnrico.Perla@Sun.COM 
28368735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
28378735SEnrico.Perla@Sun.COM 
28388735SEnrico.Perla@Sun.COM 	/* Did we get a valid archive ? */
28398735SEnrico.Perla@Sun.COM 	if (check_archive(tempname) == BAM_ERROR)
28408735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
28418735SEnrico.Perla@Sun.COM 
28428735SEnrico.Perla@Sun.COM 	return (do_archive_copy(tempname, archive));
28438735SEnrico.Perla@Sun.COM 
28448735SEnrico.Perla@Sun.COM out_err:
28458735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
28468735SEnrico.Perla@Sun.COM 	bam_error(ARCHIVE_FAIL, cmdline);
28478735SEnrico.Perla@Sun.COM 	(void) unlink(tempname);
28488735SEnrico.Perla@Sun.COM 	return (BAM_ERROR);
28498735SEnrico.Perla@Sun.COM }
28508735SEnrico.Perla@Sun.COM 
28518735SEnrico.Perla@Sun.COM static unsigned int
28528735SEnrico.Perla@Sun.COM from_733(unsigned char *s)
28538735SEnrico.Perla@Sun.COM {
28548735SEnrico.Perla@Sun.COM 	int		i;
28558735SEnrico.Perla@Sun.COM 	unsigned int	ret = 0;
28568735SEnrico.Perla@Sun.COM 
28578735SEnrico.Perla@Sun.COM 	for (i = 0; i < 4; i++)
28588735SEnrico.Perla@Sun.COM 		ret |= s[i] << (8 * i);
28598735SEnrico.Perla@Sun.COM 
28608735SEnrico.Perla@Sun.COM 	return (ret);
28618735SEnrico.Perla@Sun.COM }
28628735SEnrico.Perla@Sun.COM 
28638735SEnrico.Perla@Sun.COM static void
28648735SEnrico.Perla@Sun.COM to_733(unsigned char *s, unsigned int val)
28658735SEnrico.Perla@Sun.COM {
28668735SEnrico.Perla@Sun.COM 	int	i;
28678735SEnrico.Perla@Sun.COM 
28688735SEnrico.Perla@Sun.COM 	for (i = 0; i < 4; i++)
28698735SEnrico.Perla@Sun.COM 		s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
28708735SEnrico.Perla@Sun.COM }
28718735SEnrico.Perla@Sun.COM 
28728735SEnrico.Perla@Sun.COM /*
28738735SEnrico.Perla@Sun.COM  * Extends the current boot archive without recreating it from scratch
28748735SEnrico.Perla@Sun.COM  */
28758735SEnrico.Perla@Sun.COM static int
28768735SEnrico.Perla@Sun.COM extend_iso_archive(char *archive, char *tempname, char *update_dir)
28778735SEnrico.Perla@Sun.COM {
28788735SEnrico.Perla@Sun.COM 	int			fd = -1, newfd = -1, ret, i;
28798735SEnrico.Perla@Sun.COM 	int			next_session = 0, new_size = 0;
28808735SEnrico.Perla@Sun.COM 	char			cmdline[3 * PATH_MAX + 64];
28818735SEnrico.Perla@Sun.COM 	const char		*func = "extend_iso_archive()";
28828735SEnrico.Perla@Sun.COM 	filelist_t		flist = {0};
28838735SEnrico.Perla@Sun.COM 	struct iso_pdesc	saved_desc[MAX_IVDs];
28848735SEnrico.Perla@Sun.COM 
28858735SEnrico.Perla@Sun.COM 	fd = open(archive, O_RDWR);
28868735SEnrico.Perla@Sun.COM 	if (fd == -1) {
28878954SEnrico.Perla@Sun.COM 		if (bam_verbose)
28888954SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, archive, strerror(errno));
28898735SEnrico.Perla@Sun.COM 		goto out_err;
28908735SEnrico.Perla@Sun.COM 	}
28910Sstevel@tonic-gate 
28920Sstevel@tonic-gate 	/*
28938735SEnrico.Perla@Sun.COM 	 * A partial read is likely due to a corrupted file
28948735SEnrico.Perla@Sun.COM 	 */
28958735SEnrico.Perla@Sun.COM 	ret = pread64(fd, saved_desc, sizeof (saved_desc),
28968735SEnrico.Perla@Sun.COM 	    VOLDESC_OFF * CD_BLOCK);
28978735SEnrico.Perla@Sun.COM 	if (ret != sizeof (saved_desc)) {
28988954SEnrico.Perla@Sun.COM 		if (bam_verbose)
28998954SEnrico.Perla@Sun.COM 			bam_error(READ_FAIL, archive, strerror(errno));
29008735SEnrico.Perla@Sun.COM 		goto out_err;
29018735SEnrico.Perla@Sun.COM 	}
29028735SEnrico.Perla@Sun.COM 
29038735SEnrico.Perla@Sun.COM 	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
29048954SEnrico.Perla@Sun.COM 		if (bam_verbose)
29058954SEnrico.Perla@Sun.COM 			bam_error(SIGN_FAIL, archive);
29068735SEnrico.Perla@Sun.COM 		goto out_err;
29078735SEnrico.Perla@Sun.COM 	}
29088735SEnrico.Perla@Sun.COM 
29098735SEnrico.Perla@Sun.COM 	/*
29108735SEnrico.Perla@Sun.COM 	 * Read primary descriptor and locate next_session offset (it should
29118735SEnrico.Perla@Sun.COM 	 * point to the end of the archive)
29120Sstevel@tonic-gate 	 */
29138735SEnrico.Perla@Sun.COM 	next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
29148735SEnrico.Perla@Sun.COM 
29158735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
29168954SEnrico.Perla@Sun.COM 	    "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
29178954SEnrico.Perla@Sun.COM 	    MKISO_PARAMS, tempname, update_dir);
29188735SEnrico.Perla@Sun.COM 
29198735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
29208735SEnrico.Perla@Sun.COM 
29218735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
29228735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
29238954SEnrico.Perla@Sun.COM 		if (bam_verbose) {
29248954SEnrico.Perla@Sun.COM 			bam_error(MULTI_FAIL, cmdline);
29258954SEnrico.Perla@Sun.COM 			dump_errormsg(flist);
29268954SEnrico.Perla@Sun.COM 		}
29278735SEnrico.Perla@Sun.COM 		goto out_flist_err;
29288735SEnrico.Perla@Sun.COM 	}
29298735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
29308735SEnrico.Perla@Sun.COM 
29318735SEnrico.Perla@Sun.COM 	newfd = open(tempname, O_RDONLY);
29328735SEnrico.Perla@Sun.COM 	if (newfd == -1) {
29338954SEnrico.Perla@Sun.COM 		if (bam_verbose)
29348954SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, archive, strerror(errno));
29358735SEnrico.Perla@Sun.COM 		goto out_err;
29368735SEnrico.Perla@Sun.COM 	}
29378735SEnrico.Perla@Sun.COM 
29388735SEnrico.Perla@Sun.COM 	ret = pread64(newfd, saved_desc, sizeof (saved_desc),
29398735SEnrico.Perla@Sun.COM 	    VOLDESC_OFF * CD_BLOCK);
29408735SEnrico.Perla@Sun.COM 	if (ret != sizeof (saved_desc)) {
29418954SEnrico.Perla@Sun.COM 		if (bam_verbose)
29428954SEnrico.Perla@Sun.COM 			bam_error(READ_FAIL, archive, strerror(errno));
29438735SEnrico.Perla@Sun.COM 		goto out_err;
29448735SEnrico.Perla@Sun.COM 	}
29458735SEnrico.Perla@Sun.COM 
29468735SEnrico.Perla@Sun.COM 	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
29478954SEnrico.Perla@Sun.COM 		if (bam_verbose)
29488954SEnrico.Perla@Sun.COM 			bam_error(SIGN_FAIL, archive);
29498735SEnrico.Perla@Sun.COM 		goto out_err;
29508735SEnrico.Perla@Sun.COM 	}
29518735SEnrico.Perla@Sun.COM 
29528735SEnrico.Perla@Sun.COM 	new_size = from_733(saved_desc[0].volume_space_size) + next_session;
29538735SEnrico.Perla@Sun.COM 	to_733(saved_desc[0].volume_space_size, new_size);
29548735SEnrico.Perla@Sun.COM 
29558735SEnrico.Perla@Sun.COM 	for (i = 1; i < MAX_IVDs; i++) {
29568735SEnrico.Perla@Sun.COM 		if (saved_desc[i].type[0] == (unsigned char)255)
29578735SEnrico.Perla@Sun.COM 			break;
29588735SEnrico.Perla@Sun.COM 		if (memcmp(saved_desc[i].id, "CD001", 5))
29598735SEnrico.Perla@Sun.COM 			break;
29608735SEnrico.Perla@Sun.COM 
29618735SEnrico.Perla@Sun.COM 		if (bam_verbose)
29628735SEnrico.Perla@Sun.COM 			bam_print("%s: Updating descriptor entry [%d]\n", func,
29638735SEnrico.Perla@Sun.COM 			    i);
29648735SEnrico.Perla@Sun.COM 
29658735SEnrico.Perla@Sun.COM 		to_733(saved_desc[i].volume_space_size, new_size);
29668735SEnrico.Perla@Sun.COM 	}
29678735SEnrico.Perla@Sun.COM 
29688735SEnrico.Perla@Sun.COM 	ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
29698735SEnrico.Perla@Sun.COM 	if (ret != DVD_BLOCK) {
29708954SEnrico.Perla@Sun.COM 		if (bam_verbose)
29718954SEnrico.Perla@Sun.COM 			bam_error(WRITE_FAIL, archive, strerror(errno));
29728735SEnrico.Perla@Sun.COM 		goto out_err;
29738735SEnrico.Perla@Sun.COM 	}
29748735SEnrico.Perla@Sun.COM 	(void) close(newfd);
29758735SEnrico.Perla@Sun.COM 	newfd = -1;
29768735SEnrico.Perla@Sun.COM 
29778954SEnrico.Perla@Sun.COM 	ret = fsync(fd);
29788954SEnrico.Perla@Sun.COM 	if (ret != 0)
29798954SEnrico.Perla@Sun.COM 		sync();
29808954SEnrico.Perla@Sun.COM 
29818735SEnrico.Perla@Sun.COM 	ret = close(fd);
29828735SEnrico.Perla@Sun.COM 	if (ret != 0) {
29838954SEnrico.Perla@Sun.COM 		if (bam_verbose)
29848954SEnrico.Perla@Sun.COM 			bam_error(CLOSE_FAIL, archive, strerror(errno));
29858735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
29868735SEnrico.Perla@Sun.COM 	}
29878735SEnrico.Perla@Sun.COM 	fd = -1;
29888735SEnrico.Perla@Sun.COM 
29898735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
29908954SEnrico.Perla@Sun.COM 	    "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
29918735SEnrico.Perla@Sun.COM 	    (next_session/16));
29928735SEnrico.Perla@Sun.COM 
29938735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
29948735SEnrico.Perla@Sun.COM 
29958735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
29968735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
29978954SEnrico.Perla@Sun.COM 		if (bam_verbose)
29988954SEnrico.Perla@Sun.COM 			bam_error(MULTI_FAIL, cmdline);
29998735SEnrico.Perla@Sun.COM 		goto out_flist_err;
30008735SEnrico.Perla@Sun.COM 	}
30018735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
30028735SEnrico.Perla@Sun.COM 
30038735SEnrico.Perla@Sun.COM 	(void) unlink(tempname);
30048735SEnrico.Perla@Sun.COM 
30058954SEnrico.Perla@Sun.COM 	if (flushfs(bam_root) != 0)
30068954SEnrico.Perla@Sun.COM 		sync();
30078954SEnrico.Perla@Sun.COM 
30088735SEnrico.Perla@Sun.COM 	if (bam_verbose)
30098735SEnrico.Perla@Sun.COM 		bam_print("boot archive updated successfully\n");
30108735SEnrico.Perla@Sun.COM 
30118735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
30128735SEnrico.Perla@Sun.COM 
30138735SEnrico.Perla@Sun.COM out_flist_err:
30148735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
30158735SEnrico.Perla@Sun.COM out_err:
30168735SEnrico.Perla@Sun.COM 	if (fd != -1)
30178735SEnrico.Perla@Sun.COM 		(void) close(fd);
30188735SEnrico.Perla@Sun.COM 	if (newfd != -1)
30198735SEnrico.Perla@Sun.COM 		(void) close(newfd);
30208735SEnrico.Perla@Sun.COM 	return (BAM_ERROR);
30218735SEnrico.Perla@Sun.COM }
30228735SEnrico.Perla@Sun.COM 
30238735SEnrico.Perla@Sun.COM static int
30248735SEnrico.Perla@Sun.COM create_x86_archive(char *archive, char *tempname, char *update_dir)
30258735SEnrico.Perla@Sun.COM {
30268735SEnrico.Perla@Sun.COM 	int		ret;
30278735SEnrico.Perla@Sun.COM 	char		cmdline[3 * PATH_MAX + 64];
30288735SEnrico.Perla@Sun.COM 	filelist_t	flist = {0};
30298735SEnrico.Perla@Sun.COM 	const char	*func = "create_x86_archive()";
30308735SEnrico.Perla@Sun.COM 
30318735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
30328954SEnrico.Perla@Sun.COM 	    "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
30338735SEnrico.Perla@Sun.COM 
30348735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
30358735SEnrico.Perla@Sun.COM 
30368735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
30378735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
30388735SEnrico.Perla@Sun.COM 		bam_error(ARCHIVE_FAIL, cmdline);
30398954SEnrico.Perla@Sun.COM 		dump_errormsg(flist);
30408735SEnrico.Perla@Sun.COM 		filelist_free(&flist);
30418735SEnrico.Perla@Sun.COM 		(void) unlink(tempname);
30428735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
30438735SEnrico.Perla@Sun.COM 	}
30448735SEnrico.Perla@Sun.COM 
30458735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
30468735SEnrico.Perla@Sun.COM 
30478735SEnrico.Perla@Sun.COM 	if (check_archive(tempname) == BAM_ERROR)
30488735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
30498735SEnrico.Perla@Sun.COM 
30508735SEnrico.Perla@Sun.COM 	return (do_archive_copy(tempname, archive));
30518735SEnrico.Perla@Sun.COM }
30528735SEnrico.Perla@Sun.COM 
30538735SEnrico.Perla@Sun.COM static int
30548735SEnrico.Perla@Sun.COM mkisofs_archive(char *root, int what)
30558735SEnrico.Perla@Sun.COM {
30568735SEnrico.Perla@Sun.COM 	int		ret;
30578735SEnrico.Perla@Sun.COM 	char		temp[PATH_MAX];
30588735SEnrico.Perla@Sun.COM 	char 		bootblk[PATH_MAX];
30598735SEnrico.Perla@Sun.COM 	char		boot_archive[PATH_MAX];
30608735SEnrico.Perla@Sun.COM 
30618735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
30628735SEnrico.Perla@Sun.COM 		ret = snprintf(temp, sizeof (temp),
30638735SEnrico.Perla@Sun.COM 		    "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
30648735SEnrico.Perla@Sun.COM 		    get_machine(), getpid());
30658735SEnrico.Perla@Sun.COM 	else
30668735SEnrico.Perla@Sun.COM 		ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
30678735SEnrico.Perla@Sun.COM 		    root, ARCHIVE_PREFIX, get_machine(), getpid());
30688735SEnrico.Perla@Sun.COM 
30698735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (temp))
30708735SEnrico.Perla@Sun.COM 		goto out_path_err;
30718735SEnrico.Perla@Sun.COM 
30728735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
30738735SEnrico.Perla@Sun.COM 		ret = snprintf(boot_archive, sizeof (boot_archive),
30748735SEnrico.Perla@Sun.COM 		    "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
30758735SEnrico.Perla@Sun.COM 		    ARCHIVE_SUFFIX);
30768735SEnrico.Perla@Sun.COM 	else
30778735SEnrico.Perla@Sun.COM 		ret = snprintf(boot_archive, sizeof (boot_archive),
30788735SEnrico.Perla@Sun.COM 		    "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
30798735SEnrico.Perla@Sun.COM 		    ARCHIVE_SUFFIX);
30808735SEnrico.Perla@Sun.COM 
30818735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (boot_archive))
30828735SEnrico.Perla@Sun.COM 		goto out_path_err;
30838735SEnrico.Perla@Sun.COM 
30848735SEnrico.Perla@Sun.COM 	bam_print("updating %s\n", boot_archive);
30858735SEnrico.Perla@Sun.COM 
30868735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET)) {
30878735SEnrico.Perla@Sun.COM 		ret = snprintf(bootblk, sizeof (bootblk),
30888735SEnrico.Perla@Sun.COM 		    "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
30898735SEnrico.Perla@Sun.COM 		if (ret >= sizeof (bootblk))
30908735SEnrico.Perla@Sun.COM 			goto out_path_err;
30918735SEnrico.Perla@Sun.COM 
30928735SEnrico.Perla@Sun.COM 		ret = create_sparc_archive(boot_archive, temp, bootblk,
30938735SEnrico.Perla@Sun.COM 		    get_cachedir(what));
30948735SEnrico.Perla@Sun.COM 	} else {
30958735SEnrico.Perla@Sun.COM 		if (!is_dir_flag_on(what, NO_MULTI)) {
30968735SEnrico.Perla@Sun.COM 			if (bam_verbose)
30978735SEnrico.Perla@Sun.COM 				bam_print("Attempting to extend x86 archive: "
30988735SEnrico.Perla@Sun.COM 				    "%s\n", boot_archive);
30998735SEnrico.Perla@Sun.COM 
31008735SEnrico.Perla@Sun.COM 			ret = extend_iso_archive(boot_archive, temp,
31018735SEnrico.Perla@Sun.COM 			    get_updatedir(what));
31028735SEnrico.Perla@Sun.COM 			if (ret == BAM_SUCCESS) {
31038735SEnrico.Perla@Sun.COM 				if (bam_verbose)
31048735SEnrico.Perla@Sun.COM 					bam_print("Successfully extended %s\n",
31058735SEnrico.Perla@Sun.COM 					    boot_archive);
31068735SEnrico.Perla@Sun.COM 
31078735SEnrico.Perla@Sun.COM 				(void) rmdir_r(get_updatedir(what));
31088735SEnrico.Perla@Sun.COM 				return (BAM_SUCCESS);
31098735SEnrico.Perla@Sun.COM 			}
31108735SEnrico.Perla@Sun.COM 		}
31118735SEnrico.Perla@Sun.COM 		/*
31128735SEnrico.Perla@Sun.COM 		 * The boot archive will be recreated from scratch. We get here
31138735SEnrico.Perla@Sun.COM 		 * if at least one of these conditions is true:
31148735SEnrico.Perla@Sun.COM 		 * - bootadm was called without the -e switch
31158735SEnrico.Perla@Sun.COM 		 * - the archive (or the archive cache) doesn't exist
31168735SEnrico.Perla@Sun.COM 		 * - archive size is bigger than BA_SIZE_MAX
31178735SEnrico.Perla@Sun.COM 		 * - more than COUNT_MAX files need to be updated
31188735SEnrico.Perla@Sun.COM 		 * - an error occourred either populating the /updates directory
31198735SEnrico.Perla@Sun.COM 		 *   or extend_iso_archive() failed
31208735SEnrico.Perla@Sun.COM 		 */
31218735SEnrico.Perla@Sun.COM 		if (bam_verbose)
31228735SEnrico.Perla@Sun.COM 			bam_print("Unable to extend %s... rebuilding archive\n",
31238735SEnrico.Perla@Sun.COM 			    boot_archive);
31248735SEnrico.Perla@Sun.COM 
31258735SEnrico.Perla@Sun.COM 		if (get_updatedir(what)[0] != '\0')
31268735SEnrico.Perla@Sun.COM 			(void) rmdir_r(get_updatedir(what));
31278735SEnrico.Perla@Sun.COM 
31288735SEnrico.Perla@Sun.COM 
31298735SEnrico.Perla@Sun.COM 		ret = create_x86_archive(boot_archive, temp,
31308735SEnrico.Perla@Sun.COM 		    get_cachedir(what));
31318735SEnrico.Perla@Sun.COM 	}
31328735SEnrico.Perla@Sun.COM 
31338735SEnrico.Perla@Sun.COM 	if (ret == BAM_SUCCESS && bam_verbose)
31348735SEnrico.Perla@Sun.COM 		bam_print("Successfully created %s\n", boot_archive);
31358735SEnrico.Perla@Sun.COM 
31368735SEnrico.Perla@Sun.COM 	return (ret);
31378735SEnrico.Perla@Sun.COM 
31388735SEnrico.Perla@Sun.COM out_path_err:
31398735SEnrico.Perla@Sun.COM 	bam_error(PATH_TOO_LONG, root);
31408735SEnrico.Perla@Sun.COM 	return (BAM_ERROR);
31410Sstevel@tonic-gate }
31420Sstevel@tonic-gate 
31430Sstevel@tonic-gate static error_t
31440Sstevel@tonic-gate create_ramdisk(char *root)
31450Sstevel@tonic-gate {
31460Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
31470Sstevel@tonic-gate 	size_t len;
31480Sstevel@tonic-gate 	struct stat sb;
31498954SEnrico.Perla@Sun.COM 	int ret, what, status = BAM_SUCCESS;
31508735SEnrico.Perla@Sun.COM 
31518735SEnrico.Perla@Sun.COM 	/* If there is mkisofs, use it to create the required archives */
31528735SEnrico.Perla@Sun.COM 	if (is_mkisofs()) {
31538735SEnrico.Perla@Sun.COM 		for (what = FILE32; what < CACHEDIR_NUM; what++) {
31548954SEnrico.Perla@Sun.COM 			if (has_cachedir(what) && is_dir_flag_on(what,
31558954SEnrico.Perla@Sun.COM 			    NEED_UPDATE)) {
31568735SEnrico.Perla@Sun.COM 				ret = mkisofs_archive(root, what);
31578735SEnrico.Perla@Sun.COM 				if (ret != 0)
31588954SEnrico.Perla@Sun.COM 					status = BAM_ERROR;
31598735SEnrico.Perla@Sun.COM 			}
31608735SEnrico.Perla@Sun.COM 		}
31618954SEnrico.Perla@Sun.COM 		return (status);
31628735SEnrico.Perla@Sun.COM 	}
31630Sstevel@tonic-gate 
31640Sstevel@tonic-gate 	/*
31658735SEnrico.Perla@Sun.COM 	 * Else setup command args for create_ramdisk.ksh for the UFS archives
31660Sstevel@tonic-gate 	 */
31678735SEnrico.Perla@Sun.COM 	if (bam_verbose)
31688735SEnrico.Perla@Sun.COM 		bam_print("mkisofs not found, creating UFS archive\n");
31698735SEnrico.Perla@Sun.COM 
31705648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
31710Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
31720Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
31730Sstevel@tonic-gate 		return (BAM_ERROR);
31740Sstevel@tonic-gate 	}
31750Sstevel@tonic-gate 
31768954SEnrico.Perla@Sun.COM 	if (is_safe_exec(path) == BAM_ERROR)
31778954SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
31788954SEnrico.Perla@Sun.COM 
31790Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
31806319Sjg 	if (bam_alt_platform)
31816319Sjg 		len += strlen(bam_platform) + strlen("-p ");
31820Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
31830Sstevel@tonic-gate 
31846319Sjg 	if (bam_alt_platform) {
31856319Sjg 		assert(strlen(root) > 1);
31866319Sjg 		(void) snprintf(cmdline, len, "%s -p %s -R %s",
31876319Sjg 		    path, bam_platform, root);
31886319Sjg 		/* chop off / at the end */
31896319Sjg 		cmdline[strlen(cmdline) - 1] = '\0';
31906319Sjg 	} else if (strlen(root) > 1) {
31910Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
31920Sstevel@tonic-gate 		/* chop off / at the end */
31930Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
31940Sstevel@tonic-gate 	} else
31950Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
31960Sstevel@tonic-gate 
31975648Ssetje 	if (exec_cmd(cmdline, NULL) != 0) {
31980Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
31990Sstevel@tonic-gate 		free(cmdline);
32000Sstevel@tonic-gate 		return (BAM_ERROR);
32010Sstevel@tonic-gate 	}
32020Sstevel@tonic-gate 	free(cmdline);
32030Sstevel@tonic-gate 	/*
32045648Ssetje 	 * The existence of the expected archives used to be
32055648Ssetje 	 * verified here. This check is done in create_ramdisk as
32065648Ssetje 	 * it needs to be in sync with the altroot operated upon.
32070Sstevel@tonic-gate 	 */
32080Sstevel@tonic-gate 	return (BAM_SUCCESS);
32090Sstevel@tonic-gate }
32100Sstevel@tonic-gate 
32110Sstevel@tonic-gate /*
32120Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
32130Sstevel@tonic-gate  * 1 - is miniroot
32140Sstevel@tonic-gate  * 0 - is not
32150Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
32160Sstevel@tonic-gate  */
32170Sstevel@tonic-gate static int
32180Sstevel@tonic-gate is_ramdisk(char *root)
32190Sstevel@tonic-gate {
32200Sstevel@tonic-gate 	struct extmnttab mnt;
32210Sstevel@tonic-gate 	FILE *fp;
32220Sstevel@tonic-gate 	int found;
3223316Svikram 	char mntpt[PATH_MAX];
3224316Svikram 	char *cp;
32250Sstevel@tonic-gate 
32260Sstevel@tonic-gate 	/*
32270Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
32280Sstevel@tonic-gate 	 * of dubious value:
3229316Svikram 	 *	- create boot_archive on a lofi-mounted boot_archive
32300Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
32310Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
32320Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
32330Sstevel@tonic-gate 	 * worth it.
32340Sstevel@tonic-gate 	 * The other two conditions are handled here
32350Sstevel@tonic-gate 	 */
32360Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
32370Sstevel@tonic-gate 	if (fp == NULL) {
32380Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
32390Sstevel@tonic-gate 		return (0);
32400Sstevel@tonic-gate 	}
32410Sstevel@tonic-gate 
32420Sstevel@tonic-gate 	resetmnttab(fp);
32430Sstevel@tonic-gate 
3244316Svikram 	/*
3245316Svikram 	 * Remove any trailing / from the mount point
3246316Svikram 	 */
3247316Svikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
3248316Svikram 	if (strcmp(root, "/") != 0) {
3249316Svikram 		cp = mntpt + strlen(mntpt) - 1;
3250316Svikram 		if (*cp == '/')
3251316Svikram 			*cp = '\0';
3252316Svikram 	}
32530Sstevel@tonic-gate 	found = 0;
32540Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3255316Svikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
32560Sstevel@tonic-gate 			found = 1;
32570Sstevel@tonic-gate 			break;
32580Sstevel@tonic-gate 		}
32590Sstevel@tonic-gate 	}
32600Sstevel@tonic-gate 
32610Sstevel@tonic-gate 	if (!found) {
32620Sstevel@tonic-gate 		if (bam_verbose)
3263316Svikram 			bam_error(NOT_IN_MNTTAB, mntpt);
32640Sstevel@tonic-gate 		(void) fclose(fp);
32650Sstevel@tonic-gate 		return (0);
32660Sstevel@tonic-gate 	}
32670Sstevel@tonic-gate 
32680Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
32690Sstevel@tonic-gate 		if (bam_verbose)
32700Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
32710Sstevel@tonic-gate 		(void) fclose(fp);
32720Sstevel@tonic-gate 		return (1);
32730Sstevel@tonic-gate 	}
32740Sstevel@tonic-gate 
32750Sstevel@tonic-gate 	(void) fclose(fp);
32760Sstevel@tonic-gate 
32770Sstevel@tonic-gate 	return (0);
32780Sstevel@tonic-gate }
32790Sstevel@tonic-gate 
32800Sstevel@tonic-gate static int
32815648Ssetje is_boot_archive(char *root)
32820Sstevel@tonic-gate {
32836448Svikram 	char		path[PATH_MAX];
32846448Svikram 	struct stat	sb;
32856448Svikram 	int		error;
32866448Svikram 	const char	*fcn = "is_boot_archive()";
32870Sstevel@tonic-gate 
32880Sstevel@tonic-gate 	/*
32895648Ssetje 	 * We can't create an archive without the create_ramdisk script
32900Sstevel@tonic-gate 	 */
32915648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
32926448Svikram 	error = stat(path, &sb);
32936448Svikram 	INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
32946448Svikram 	if (error == -1) {
32950Sstevel@tonic-gate 		if (bam_verbose)
32960Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
32976448Svikram 		BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
32980Sstevel@tonic-gate 		return (0);
32990Sstevel@tonic-gate 	}
33000Sstevel@tonic-gate 
33016448Svikram 	BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
33025648Ssetje 	return (1);
33035648Ssetje }
33045648Ssetje 
33055648Ssetje /*
33065648Ssetje  * Need to call this for anything that operates on the GRUB menu
33076694Svikram  * In the x86 live upgrade case the directory /boot/grub may be present
33086694Svikram  * even on pre-newboot BEs. The authoritative way to check for a GRUB target
33096694Svikram  * is to check for the presence of the stage2 binary which is present
33106694Svikram  * only on GRUB targets (even on x86 boot partitions). Checking for the
33116694Svikram  * presence of the multiboot binary is not correct as it is not present
33126694Svikram  * on x86 boot partitions.
33135648Ssetje  */
33145648Ssetje int
33155648Ssetje is_grub(const char *root)
33165648Ssetje {
33175648Ssetje 	char path[PATH_MAX];
33185648Ssetje 	struct stat sb;
33196448Svikram 	const char *fcn = "is_grub()";
33206448Svikram 
33216694Svikram 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
33220Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
33236448Svikram 		BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
33240Sstevel@tonic-gate 		return (0);
33250Sstevel@tonic-gate 	}
33260Sstevel@tonic-gate 
33270Sstevel@tonic-gate 	return (1);
33280Sstevel@tonic-gate }
33290Sstevel@tonic-gate 
33300Sstevel@tonic-gate static int
33316448Svikram is_zfs(char *root)
33326448Svikram {
33336448Svikram 	struct statvfs		vfs;
33346448Svikram 	int			ret;
33356448Svikram 	const char		*fcn = "is_zfs()";
33366448Svikram 
33376448Svikram 	ret = statvfs(root, &vfs);
33386448Svikram 	INJECT_ERROR1("STATVFS_ZFS", ret = 1);
33396448Svikram 	if (ret != 0) {
33406448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
33416448Svikram 		return (0);
33426448Svikram 	}
33436448Svikram 
33446448Svikram 	if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
33456448Svikram 		BAM_DPRINTF((D_IS_ZFS, fcn, root));
33466448Svikram 		return (1);
33476448Svikram 	} else {
33486448Svikram 		BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
33490Sstevel@tonic-gate 		return (0);
33500Sstevel@tonic-gate 	}
33516448Svikram }
33526448Svikram 
33536448Svikram static int
33546448Svikram is_ufs(char *root)
33556448Svikram {
33566448Svikram 	struct statvfs		vfs;
33576448Svikram 	int			ret;
33586448Svikram 	const char		*fcn = "is_ufs()";
33596448Svikram 
33606448Svikram 	ret = statvfs(root, &vfs);
33616448Svikram 	INJECT_ERROR1("STATVFS_UFS", ret = 1);
33626448Svikram 	if (ret != 0) {
33636448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
33646448Svikram 		return (0);
33656448Svikram 	}
33666448Svikram 
33676448Svikram 	if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
33686448Svikram 		BAM_DPRINTF((D_IS_UFS, fcn, root));
33690Sstevel@tonic-gate 		return (1);
33706448Svikram 	} else {
33716448Svikram 		BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
33726448Svikram 		return (0);
33736448Svikram 	}
33740Sstevel@tonic-gate }
33750Sstevel@tonic-gate 
33766423Sgw25295 static int
33776448Svikram is_pcfs(char *root)
33786448Svikram {
33796448Svikram 	struct statvfs		vfs;
33806448Svikram 	int			ret;
33816448Svikram 	const char		*fcn = "is_pcfs()";
33826448Svikram 
33836448Svikram 	ret = statvfs(root, &vfs);
33846448Svikram 	INJECT_ERROR1("STATVFS_PCFS", ret = 1);
33856448Svikram 	if (ret != 0) {
33866448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
33876423Sgw25295 		return (0);
33886423Sgw25295 	}
33896423Sgw25295 
33906448Svikram 	if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
33916448Svikram 		BAM_DPRINTF((D_IS_PCFS, fcn, root));
33926448Svikram 		return (1);
33936448Svikram 	} else {
33946448Svikram 		BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
33956423Sgw25295 		return (0);
33966448Svikram 	}
33976448Svikram }
33986448Svikram 
33996448Svikram static int
34006448Svikram is_readonly(char *root)
34016448Svikram {
34026448Svikram 	int		fd;
34036448Svikram 	int		error;
34046448Svikram 	char		testfile[PATH_MAX];
34056448Svikram 	const char	*fcn = "is_readonly()";
34066423Sgw25295 
34076423Sgw25295 	/*
34086448Svikram 	 * Using statvfs() to check for a read-only filesystem is not
34096448Svikram 	 * reliable. The only way to reliably test is to attempt to
34106448Svikram 	 * create a file
34116423Sgw25295 	 */
34126448Svikram 	(void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
34136448Svikram 	    root, BOOTADM_RDONLY_TEST, getpid());
34146448Svikram 
34156448Svikram 	(void) unlink(testfile);
34166448Svikram 
34176448Svikram 	errno = 0;
34186448Svikram 	fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
34196448Svikram 	error = errno;
34206448Svikram 	INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
34216448Svikram 	if (fd == -1 && error == EROFS) {
34226448Svikram 		BAM_DPRINTF((D_RDONLY_FS, fcn, root));
34236423Sgw25295 		return (1);
34246448Svikram 	} else if (fd == -1) {
34256448Svikram 		bam_error(RDONLY_TEST_ERROR, root, strerror(error));
34266448Svikram 	}
34276448Svikram 
34286448Svikram 	(void) close(fd);
34296448Svikram 	(void) unlink(testfile);
34306448Svikram 
34316448Svikram 	BAM_DPRINTF((D_RDWR_FS, fcn, root));
34326423Sgw25295 	return (0);
34336423Sgw25295 }
34346423Sgw25295 
34350Sstevel@tonic-gate static error_t
34360Sstevel@tonic-gate update_archive(char *root, char *opt)
34370Sstevel@tonic-gate {
34380Sstevel@tonic-gate 	error_t ret;
34390Sstevel@tonic-gate 
34400Sstevel@tonic-gate 	assert(root);
34410Sstevel@tonic-gate 	assert(opt == NULL);
34420Sstevel@tonic-gate 
34438735SEnrico.Perla@Sun.COM 	init_walk_args();
34448735SEnrico.Perla@Sun.COM 	(void) umask(022);
34458735SEnrico.Perla@Sun.COM 
34460Sstevel@tonic-gate 	/*
34476448Svikram 	 * root must belong to a boot archive based OS,
34480Sstevel@tonic-gate 	 */
34495648Ssetje 	if (!is_boot_archive(root)) {
3450316Svikram 		/*
3451316Svikram 		 * Emit message only if not in context of update_all.
3452316Svikram 		 * If in update_all, emit only if verbose flag is set.
3453316Svikram 		 */
3454316Svikram 		if (!bam_update_all || bam_verbose)
34556448Svikram 			bam_print(NOT_ARCHIVE_BOOT, root);
34568735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
34570Sstevel@tonic-gate 	}
34580Sstevel@tonic-gate 
34590Sstevel@tonic-gate 	/*
3460662Sszhou 	 * If smf check is requested when / is writable (can happen
3461662Sszhou 	 * on first reboot following an upgrade because service
3462662Sszhou 	 * dependency is messed up), skip the check.
3463662Sszhou 	 */
34648735SEnrico.Perla@Sun.COM 	if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
3465662Sszhou 		return (BAM_SUCCESS);
3466662Sszhou 
3467662Sszhou 	/*
34689216SEnrico.Perla@Sun.COM 	 * Don't generate archive on ramdisk.
34699216SEnrico.Perla@Sun.COM 	 */
34709216SEnrico.Perla@Sun.COM 	if (is_ramdisk(root))
34719216SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
34729216SEnrico.Perla@Sun.COM 
34739216SEnrico.Perla@Sun.COM 	/*
3474662Sszhou 	 * root must be writable. This check applies to alternate
3475662Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
34768735SEnrico.Perla@Sun.COM 	 * The behaviour translates into being the one of a 'check'.
34770Sstevel@tonic-gate 	 */
3478756Ssetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
34798735SEnrico.Perla@Sun.COM 		set_flag(RDONLY_FSCHK);
34808735SEnrico.Perla@Sun.COM 		bam_check = 1;
34810Sstevel@tonic-gate 	}
34820Sstevel@tonic-gate 
34830Sstevel@tonic-gate 	/*
34848954SEnrico.Perla@Sun.COM 	 * Now check if an update is really needed.
34850Sstevel@tonic-gate 	 */
34860Sstevel@tonic-gate 	ret = update_required(root);
34870Sstevel@tonic-gate 
34880Sstevel@tonic-gate 	/*
34898954SEnrico.Perla@Sun.COM 	 * The check command (-n) is *not* a dry run.
34900Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
34918954SEnrico.Perla@Sun.COM 	 * A readonly filesystem has to be considered an error only if an update
34928954SEnrico.Perla@Sun.COM 	 * is required.
34930Sstevel@tonic-gate 	 */
34948954SEnrico.Perla@Sun.COM 	if (bam_nowrite()) {
34958735SEnrico.Perla@Sun.COM 		if (is_flag_on(RDONLY_FSCHK)) {
34969216SEnrico.Perla@Sun.COM 			bam_check = bam_saved_check;
34978735SEnrico.Perla@Sun.COM 			if (ret > 0)
34988735SEnrico.Perla@Sun.COM 				bam_error(RDONLY_FS, root);
34998735SEnrico.Perla@Sun.COM 			if (bam_update_all)
35008735SEnrico.Perla@Sun.COM 				return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
35018735SEnrico.Perla@Sun.COM 		}
35028735SEnrico.Perla@Sun.COM 
35030Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
35040Sstevel@tonic-gate 	}
35050Sstevel@tonic-gate 
35060Sstevel@tonic-gate 	if (ret == 1) {
35070Sstevel@tonic-gate 		/* create the ramdisk */
35080Sstevel@tonic-gate 		ret = create_ramdisk(root);
35090Sstevel@tonic-gate 	}
35105648Ssetje 
35119233SEnrico.Perla@Sun.COM 	/*
35129233SEnrico.Perla@Sun.COM 	 * if the archive is updated, save the new stat data and update the
35139233SEnrico.Perla@Sun.COM 	 * timestamp file
35149233SEnrico.Perla@Sun.COM 	 */
35155648Ssetje 	if (ret == 0 && walk_arg.new_nvlp != NULL) {
35165648Ssetje 		savenew(root);
35179233SEnrico.Perla@Sun.COM 		update_timestamp(root);
35185648Ssetje 	}
35195648Ssetje 
35205648Ssetje 	clear_walk_args();
35215648Ssetje 
35220Sstevel@tonic-gate 	return (ret);
35230Sstevel@tonic-gate }
35240Sstevel@tonic-gate 
35259464SMark.Musante@Sun.COM static char *
35269464SMark.Musante@Sun.COM find_root_pool()
35279464SMark.Musante@Sun.COM {
35289464SMark.Musante@Sun.COM 	char *special = get_special("/");
35299464SMark.Musante@Sun.COM 	char *p;
35309464SMark.Musante@Sun.COM 
35319464SMark.Musante@Sun.COM 	if (special == NULL)
35329464SMark.Musante@Sun.COM 		return (NULL);
35339464SMark.Musante@Sun.COM 
35349464SMark.Musante@Sun.COM 	if (*special == '/') {
35359464SMark.Musante@Sun.COM 		free(special);
35369464SMark.Musante@Sun.COM 		return (NULL);
35379464SMark.Musante@Sun.COM 	}
35389464SMark.Musante@Sun.COM 
35399464SMark.Musante@Sun.COM 	if ((p = strchr(special, '/')) != NULL)
35409464SMark.Musante@Sun.COM 		*p = '\0';
35419464SMark.Musante@Sun.COM 
35429464SMark.Musante@Sun.COM 	return (special);
35439464SMark.Musante@Sun.COM }
35449464SMark.Musante@Sun.COM 
35456694Svikram static error_t
35466694Svikram synchronize_BE_menu(void)
35476694Svikram {
35486694Svikram 	struct stat	sb;
35496694Svikram 	char		cmdline[PATH_MAX];
35506694Svikram 	char		cksum_line[PATH_MAX];
35516694Svikram 	filelist_t	flist = {0};
35526694Svikram 	char		*old_cksum_str;
35536694Svikram 	char		*old_size_str;
35546694Svikram 	char		*old_file;
35556694Svikram 	char		*curr_cksum_str;
35566694Svikram 	char		*curr_size_str;
35576694Svikram 	char		*curr_file;
35589464SMark.Musante@Sun.COM 	char		*pool;
35596694Svikram 	FILE		*cfp;
35606694Svikram 	int		found;
35616694Svikram 	int		ret;
35626694Svikram 	const char	*fcn = "synchronize_BE_menu()";
35636694Svikram 
35646694Svikram 	BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
35656694Svikram 
35666694Svikram 	/* Check if findroot enabled LU BE */
35676694Svikram 	if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
35686694Svikram 		BAM_DPRINTF((D_NOT_LU_BE, fcn));
35696694Svikram 		return (BAM_SUCCESS);
35706694Svikram 	}
35716694Svikram 
35726694Svikram 	if (stat(LU_MENU_CKSUM, &sb) != 0) {
35736694Svikram 		BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
35746694Svikram 		goto menu_sync;
35756694Svikram 	}
35766694Svikram 
35776694Svikram 	cfp = fopen(LU_MENU_CKSUM, "r");
35786694Svikram 	INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
35796694Svikram 	if (cfp == NULL) {
35806694Svikram 		bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
35816694Svikram 		goto menu_sync;
35826694Svikram 	}
35836694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
35846694Svikram 
35856694Svikram 	found = 0;
35866694Svikram 	while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
35876694Svikram 		INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
35886694Svikram 		if (found) {
35896694Svikram 			bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
35906694Svikram 			(void) fclose(cfp);
35916694Svikram 			goto menu_sync;
35926694Svikram 		}
35936694Svikram 		found = 1;
35946694Svikram 	}
35956694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
35966694Svikram 
35976694Svikram 
35986694Svikram 	old_cksum_str = strtok(cksum_line, " \t");
35996694Svikram 	old_size_str = strtok(NULL, " \t");
36006694Svikram 	old_file = strtok(NULL, " \t");
36016694Svikram 
36026694Svikram 	INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
36036694Svikram 	INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
36046694Svikram 	INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
36056694Svikram 	if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
36066694Svikram 		bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
36076694Svikram 		goto menu_sync;
36086694Svikram 	}
36096694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
36106694Svikram 
36116694Svikram 	/* Get checksum of current menu */
36129464SMark.Musante@Sun.COM 	pool = find_root_pool();
36139464SMark.Musante@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s%s%s",
36149464SMark.Musante@Sun.COM 	    CKSUM, pool == NULL ? "" : "/", pool == NULL ? "" : pool,
36159464SMark.Musante@Sun.COM 	    GRUB_MENU);
36169464SMark.Musante@Sun.COM 	free(pool);
36176694Svikram 	ret = exec_cmd(cmdline, &flist);
36186694Svikram 	INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
36196694Svikram 	if (ret != 0) {
36206694Svikram 		bam_error(MENU_CKSUM_FAIL);
36216694Svikram 		return (BAM_ERROR);
36226694Svikram 	}
36236694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
36246694Svikram 
36256694Svikram 	INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
36266694Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
36276694Svikram 		bam_error(BAD_CKSUM);
36286694Svikram 		filelist_free(&flist);
36296694Svikram 		return (BAM_ERROR);
36306694Svikram 	}
36316694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
36326694Svikram 
36336694Svikram 	curr_cksum_str = strtok(flist.head->line, " \t");
36346694Svikram 	curr_size_str = strtok(NULL, " \t");
36356694Svikram 	curr_file = strtok(NULL, " \t");
36366694Svikram 
36376694Svikram 	INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
36386694Svikram 	INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
36396694Svikram 	INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
36406694Svikram 	if (curr_cksum_str == NULL || curr_size_str == NULL ||
36416694Svikram 	    curr_file == NULL) {
36426694Svikram 		bam_error(BAD_CKSUM_PARSE);
36436694Svikram 		filelist_free(&flist);
36446694Svikram 		return (BAM_ERROR);
36456694Svikram 	}
36466694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
36476694Svikram 
36486694Svikram 	if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
36496694Svikram 	    strcmp(old_size_str, curr_size_str) == 0 &&
36506694Svikram 	    strcmp(old_file, curr_file) == 0) {
36516694Svikram 		filelist_free(&flist);
36526694Svikram 		BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
36536694Svikram 		return (BAM_SUCCESS);
36546694Svikram 	}
36556694Svikram 
36566694Svikram 	filelist_free(&flist);
36576694Svikram 
36586694Svikram 	/* cksum doesn't match - the menu has changed */
36596694Svikram 	BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
36606694Svikram 
36616694Svikram menu_sync:
36626694Svikram 	bam_print(PROP_GRUB_MENU);
36636694Svikram 
36646694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
36657048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
36666694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
36676694Svikram 	ret = exec_cmd(cmdline, NULL);
36686694Svikram 	INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
36696694Svikram 	if (ret != 0) {
36706694Svikram 		bam_error(MENU_PROP_FAIL);
36716694Svikram 		return (BAM_ERROR);
36726694Svikram 	}
36736694Svikram 	BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
36746694Svikram 
36757048Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
36766694Svikram 	    GRUB_MENU, GRUB_BACKUP_MENU);
36776694Svikram 	ret = exec_cmd(cmdline, NULL);
36786694Svikram 	INJECT_ERROR1("CREATE_BACKUP", ret = 1);
36796694Svikram 	if (ret != 0) {
36806694Svikram 		bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
36816694Svikram 		return (BAM_ERROR);
36826694Svikram 	}
36836694Svikram 	BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
36846694Svikram 
36856694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
36867048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
36876694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
36886694Svikram 	ret = exec_cmd(cmdline, NULL);
36896694Svikram 	INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
36906694Svikram 	if (ret != 0) {
36916694Svikram 		bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
36926694Svikram 		return (BAM_ERROR);
36936694Svikram 	}
36946694Svikram 	BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
36956694Svikram 
36966694Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
36976694Svikram 	    CKSUM, GRUB_MENU, LU_MENU_CKSUM);
36986694Svikram 	ret = exec_cmd(cmdline, NULL);
36996694Svikram 	INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
37006694Svikram 	if (ret != 0) {
37016694Svikram 		bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
37026694Svikram 		return (BAM_ERROR);
37036694Svikram 	}
37046694Svikram 	BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
37056694Svikram 
37066694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
37077048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
37086694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
37096694Svikram 	ret = exec_cmd(cmdline, NULL);
37106694Svikram 	INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
37116694Svikram 	if (ret != 0) {
37126694Svikram 		bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
37136694Svikram 		return (BAM_ERROR);
37146694Svikram 	}
37156694Svikram 	BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
37166694Svikram 
37176694Svikram 	return (BAM_SUCCESS);
37181746Svikram }
37191746Svikram 
37200Sstevel@tonic-gate static error_t
37210Sstevel@tonic-gate update_all(char *root, char *opt)
37220Sstevel@tonic-gate {
37230Sstevel@tonic-gate 	struct extmnttab mnt;
37240Sstevel@tonic-gate 	struct stat sb;
37250Sstevel@tonic-gate 	FILE *fp;
37260Sstevel@tonic-gate 	char multibt[PATH_MAX];
37275648Ssetje 	char creatram[PATH_MAX];
37280Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
37290Sstevel@tonic-gate 
3730621Svikram 	assert(root);
37310Sstevel@tonic-gate 	assert(opt == NULL);
37320Sstevel@tonic-gate 
3733621Svikram 	if (bam_rootlen != 1 || *root != '/') {
3734621Svikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
3735621Svikram 		bam_error(ALT_ROOT_INVALID, multibt);
3736621Svikram 		return (BAM_ERROR);
3737621Svikram 	}
3738621Svikram 
37390Sstevel@tonic-gate 	/*
37404493Snadkarni 	 * Check to see if we are in the midst of safemode patching
37414493Snadkarni 	 * If so skip building the archive for /. Instead build it
37424493Snadkarni 	 * against the latest bits obtained by creating a fresh lofs
37434493Snadkarni 	 * mount of root.
37440Sstevel@tonic-gate 	 */
37454493Snadkarni 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
37468735SEnrico.Perla@Sun.COM 		if (mkdir(LOFS_PATCH_MNT, DIR_PERMS) == -1 &&
37474493Snadkarni 		    errno != EEXIST) {
37484493Snadkarni 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
37494493Snadkarni 			    strerror(errno));
37504493Snadkarni 			ret = BAM_ERROR;
37514493Snadkarni 			goto out;
37524493Snadkarni 		}
37534493Snadkarni 		(void) snprintf(multibt, sizeof (multibt),
37544493Snadkarni 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
37555648Ssetje 		if (exec_cmd(multibt, NULL) != 0) {
37564493Snadkarni 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
37574493Snadkarni 			ret = BAM_ERROR;
37584493Snadkarni 		}
37594493Snadkarni 		if (ret != BAM_ERROR) {
37604493Snadkarni 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
37614493Snadkarni 			    LOFS_PATCH_MNT);
37624493Snadkarni 			bam_rootlen = strlen(rootbuf);
37634493Snadkarni 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
37644493Snadkarni 				ret = BAM_ERROR;
37654550Snadkarni 			/*
37664550Snadkarni 			 * unmount the lofs mount since there could be
37674550Snadkarni 			 * multiple invocations of bootadm -a update_all
37684550Snadkarni 			 */
37694550Snadkarni 			(void) snprintf(multibt, sizeof (multibt),
37704550Snadkarni 			    "/sbin/umount %s", LOFS_PATCH_MNT);
37715648Ssetje 			if (exec_cmd(multibt, NULL) != 0) {
37724550Snadkarni 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
37734550Snadkarni 				ret = BAM_ERROR;
37744550Snadkarni 			}
37754493Snadkarni 		}
37764493Snadkarni 	} else {
37774493Snadkarni 		/*
37784493Snadkarni 		 * First update archive for current root
37794493Snadkarni 		 */
37804493Snadkarni 		if (update_archive(root, opt) != BAM_SUCCESS)
37814493Snadkarni 			ret = BAM_ERROR;
37824493Snadkarni 	}
37834493Snadkarni 
37844493Snadkarni 	if (ret == BAM_ERROR)
37854493Snadkarni 		goto out;
37860Sstevel@tonic-gate 
37870Sstevel@tonic-gate 	/*
37880Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
37890Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
37900Sstevel@tonic-gate 	 */
37910Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
37920Sstevel@tonic-gate 	if (fp == NULL) {
37930Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3794316Svikram 		ret = BAM_ERROR;
3795316Svikram 		goto out;
37960Sstevel@tonic-gate 	}
37970Sstevel@tonic-gate 
37980Sstevel@tonic-gate 	resetmnttab(fp);
37990Sstevel@tonic-gate 
38000Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
38010Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
38020Sstevel@tonic-gate 			continue;
38037543SJerry.Gilliam@Sun.COM 		if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
38047543SJerry.Gilliam@Sun.COM 		    (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
38050Sstevel@tonic-gate 			continue;
38060Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
38070Sstevel@tonic-gate 			continue;
38080Sstevel@tonic-gate 
38095648Ssetje 		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
38105648Ssetje 		    mnt.mnt_mountp, CREATE_RAMDISK);
38115648Ssetje 
38125648Ssetje 		if (stat(creatram, &sb) == -1)
38130Sstevel@tonic-gate 			continue;
38140Sstevel@tonic-gate 
38150Sstevel@tonic-gate 		/*
38160Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
38170Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
38180Sstevel@tonic-gate 		 */
38190Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
38200Sstevel@tonic-gate 		    mnt.mnt_mountp);
38210Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
38223446Smrj 
38233446Smrj 		/*
38243446Smrj 		 * It's possible that other mounts may be an alternate boot
38253446Smrj 		 * architecture, so check it again.
38263446Smrj 		 */
38276448Svikram 		if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
38283446Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
38290Sstevel@tonic-gate 			ret = BAM_ERROR;
38300Sstevel@tonic-gate 	}
38310Sstevel@tonic-gate 
38320Sstevel@tonic-gate 	(void) fclose(fp);
38330Sstevel@tonic-gate 
3834316Svikram out:
38351746Svikram 	/*
38366694Svikram 	 * We no longer use biosdev for Live Upgrade. Hence
38376694Svikram 	 * there is no need to defer (to shutdown time) any fdisk
38386694Svikram 	 * updates
38391746Svikram 	 */
38406694Svikram 	if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
38416694Svikram 		bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
38426694Svikram 	}
38436694Svikram 
38446694Svikram 	/*
38456694Svikram 	 * If user has updated menu in current BE, propagate the
38466694Svikram 	 * updates to all BEs.
38476694Svikram 	 */
38487656SSherry.Moore@Sun.COM 	if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
38491746Svikram 		ret = BAM_ERROR;
38501746Svikram 
38510Sstevel@tonic-gate 	return (ret);
38520Sstevel@tonic-gate }
38530Sstevel@tonic-gate 
38540Sstevel@tonic-gate static void
38550Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
38560Sstevel@tonic-gate {
38570Sstevel@tonic-gate 	if (mp->start == NULL) {
38580Sstevel@tonic-gate 		mp->start = lp;
38590Sstevel@tonic-gate 	} else {
38600Sstevel@tonic-gate 		mp->end->next = lp;
3861662Sszhou 		lp->prev = mp->end;
38620Sstevel@tonic-gate 	}
38630Sstevel@tonic-gate 	mp->end = lp;
38640Sstevel@tonic-gate }
38650Sstevel@tonic-gate 
38666448Svikram void
3867662Sszhou unlink_line(menu_t *mp, line_t *lp)
3868662Sszhou {
3869662Sszhou 	/* unlink from list */
3870662Sszhou 	if (lp->prev)
3871662Sszhou 		lp->prev->next = lp->next;
3872662Sszhou 	else
3873662Sszhou 		mp->start = lp->next;
3874662Sszhou 	if (lp->next)
3875662Sszhou 		lp->next->prev = lp->prev;
3876662Sszhou 	else
3877662Sszhou 		mp->end = lp->prev;
3878662Sszhou }
3879662Sszhou 
3880662Sszhou static entry_t *
3881662Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
3882662Sszhou {
3883662Sszhou 	entry_t *ent, *prev;
38846448Svikram 	const char *fcn = "boot_entry_new()";
38856448Svikram 
38866448Svikram 	assert(mp);
38876448Svikram 	assert(start);
38886448Svikram 	assert(end);
3889662Sszhou 
3890662Sszhou 	ent = s_calloc(1, sizeof (entry_t));
38916448Svikram 	BAM_DPRINTF((D_ENTRY_NEW, fcn));
3892662Sszhou 	ent->start = start;
3893662Sszhou 	ent->end = end;
3894662Sszhou 
3895662Sszhou 	if (mp->entries == NULL) {
3896662Sszhou 		mp->entries = ent;
38976448Svikram 		BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
3898662Sszhou 		return (ent);
3899662Sszhou 	}
3900662Sszhou 
3901662Sszhou 	prev = mp->entries;
3902662Sszhou 	while (prev->next)
39036448Svikram 		prev = prev->next;
3904662Sszhou 	prev->next = ent;
3905662Sszhou 	ent->prev = prev;
39066448Svikram 	BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
3907662Sszhou 	return (ent);
3908662Sszhou }
3909662Sszhou 
3910662Sszhou static void
3911662Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
3912662Sszhou {
3913662Sszhou 	if (ent)
3914662Sszhou 		ent->end = lp;
3915662Sszhou }
3916662Sszhou 
39170Sstevel@tonic-gate /*
39185084Sjohnlev  * Check whether cmd matches the one indexed by which, and whether arg matches
39195084Sjohnlev  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
39205084Sjohnlev  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
39215084Sjohnlev  * strstr(), so it can be a partial match.
39225084Sjohnlev  */
39235084Sjohnlev static int
39245084Sjohnlev check_cmd(const char *cmd, const int which, const char *arg, const char *str)
39255084Sjohnlev {
39266448Svikram 	int			ret;
39276448Svikram 	const char		*fcn = "check_cmd()";
39286448Svikram 
39296448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
39306448Svikram 
39315084Sjohnlev 	if ((strcmp(cmd, menu_cmds[which]) != 0) &&
39325084Sjohnlev 	    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
39336448Svikram 		BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
39346448Svikram 		    fcn, cmd, menu_cmds[which]));
39355084Sjohnlev 		return (0);
39365084Sjohnlev 	}
39376448Svikram 	ret = (strstr(arg, str) != NULL);
39386448Svikram 
39396448Svikram 	if (ret) {
39406448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
39416448Svikram 	} else {
39426448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
39436448Svikram 	}
39446448Svikram 
39456448Svikram 	return (ret);
39466448Svikram }
39476448Svikram 
39486448Svikram static error_t
39496448Svikram kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
39506448Svikram {
39516448Svikram 	const char		*fcn  = "kernel_parser()";
39526448Svikram 
39536448Svikram 	assert(entry);
39546448Svikram 	assert(cmd);
39556448Svikram 	assert(arg);
39566448Svikram 
39576448Svikram 	if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
39586448Svikram 	    strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
39596448Svikram 		BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
39606448Svikram 		return (BAM_ERROR);
39616448Svikram 	}
39626448Svikram 
39636448Svikram 	if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
39646448Svikram 		BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
39656448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
39666448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_KERNEL,
39676448Svikram 	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
39686448Svikram 		BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
39696448Svikram 		entry->flags |= BAM_ENTRY_DBOOT;
39706448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_64,
39716448Svikram 	    sizeof (DIRECT_BOOT_64) - 1) == 0) {
39726448Svikram 		BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
39736448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
39746448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
39756448Svikram 	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
39766448Svikram 		BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
39776448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
39788104SEnrico.Perla@Sun.COM 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
39798104SEnrico.Perla@Sun.COM 	    sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
39808104SEnrico.Perla@Sun.COM 		BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg));
39818104SEnrico.Perla@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
39828104SEnrico.Perla@Sun.COM 		    | BAM_ENTRY_32BIT;
39838104SEnrico.Perla@Sun.COM 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
39848104SEnrico.Perla@Sun.COM 	    sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
39858104SEnrico.Perla@Sun.COM 		BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg));
39868104SEnrico.Perla@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
39878104SEnrico.Perla@Sun.COM 		    | BAM_ENTRY_64BIT;
39886448Svikram 	} else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
39896448Svikram 		BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
39906448Svikram 		entry->flags |= BAM_ENTRY_MULTIBOOT;
39916448Svikram 	} else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
39926448Svikram 	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
39936448Svikram 		BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
39946448Svikram 		entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
39956448Svikram 	} else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
39966448Svikram 		BAM_DPRINTF((D_SET_HV, fcn, arg));
39976448Svikram 		entry->flags |= BAM_ENTRY_HV;
39986448Svikram 	} else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
39996448Svikram 		BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
40006448Svikram 		return (BAM_ERROR);
40018642SVikram.Hegde@Sun.COM 	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
40028642SVikram.Hegde@Sun.COM 	    strstr(arg, UNIX_SPACE)) {
40038642SVikram.Hegde@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
40048642SVikram.Hegde@Sun.COM 	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
40058642SVikram.Hegde@Sun.COM 	    strstr(arg, AMD_UNIX_SPACE)) {
40068642SVikram.Hegde@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
40076448Svikram 	} else {
40086448Svikram 		BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
40096448Svikram 		bam_error(UNKNOWN_KERNEL_LINE, linenum);
40106448Svikram 		return (BAM_ERROR);
40116448Svikram 	}
40126448Svikram 
40136448Svikram 	return (BAM_SUCCESS);
40146448Svikram }
40156448Svikram 
40166448Svikram static error_t
40176448Svikram module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
40186448Svikram {
40196448Svikram 	const char		*fcn = "module_parser()";
40206448Svikram 
40216448Svikram 	assert(entry);
40226448Svikram 	assert(cmd);
40236448Svikram 	assert(arg);
40246448Svikram 
40256448Svikram 	if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
40266448Svikram 	    strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
40276448Svikram 		BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
40286448Svikram 		return (BAM_ERROR);
40296448Svikram 	}
40306448Svikram 
40316448Svikram 	if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
40326448Svikram 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
40336448Svikram 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
40346448Svikram 	    strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
40356448Svikram 	    strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
40368104SEnrico.Perla@Sun.COM 	    strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
40378104SEnrico.Perla@Sun.COM 	    strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
40386448Svikram 	    strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
40396448Svikram 	    strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
40406448Svikram 		BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
40416448Svikram 		return (BAM_SUCCESS);
40426448Svikram 	} else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
40436448Svikram 	    !(entry->flags & BAM_ENTRY_LU)) {
40446448Svikram 		/* don't emit warning for hand entries */
40456448Svikram 		BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
40466448Svikram 		return (BAM_ERROR);
40476448Svikram 	} else {
40486448Svikram 		BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
40496448Svikram 		bam_error(UNKNOWN_MODULE_LINE, linenum);
40506448Svikram 		return (BAM_ERROR);
40516448Svikram 	}
40525084Sjohnlev }
40535084Sjohnlev 
40545084Sjohnlev /*
40550Sstevel@tonic-gate  * A line in menu.lst looks like
40560Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
40570Sstevel@tonic-gate  */
40580Sstevel@tonic-gate static void
40590Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
40600Sstevel@tonic-gate {
40610Sstevel@tonic-gate 	/*
40620Sstevel@tonic-gate 	 * save state across calls. This is so that
40630Sstevel@tonic-gate 	 * header gets the right entry# after title has
40640Sstevel@tonic-gate 	 * been processed
40650Sstevel@tonic-gate 	 */
4066662Sszhou 	static line_t *prev = NULL;
4067662Sszhou 	static entry_t *curr_ent = NULL;
40683446Smrj 	static int in_liveupgrade = 0;
40690Sstevel@tonic-gate 
40700Sstevel@tonic-gate 	line_t	*lp;
40710Sstevel@tonic-gate 	char *cmd, *sep, *arg;
40720Sstevel@tonic-gate 	char save, *cp, *line;
40730Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
40746448Svikram 	const char *fcn = "line_parser()";
40750Sstevel@tonic-gate 
40760Sstevel@tonic-gate 	if (str == NULL) {
40770Sstevel@tonic-gate 		return;
40780Sstevel@tonic-gate 	}
40790Sstevel@tonic-gate 
40800Sstevel@tonic-gate 	/*
40810Sstevel@tonic-gate 	 * First save a copy of the entire line.
40820Sstevel@tonic-gate 	 * We use this later to set the line field.
40830Sstevel@tonic-gate 	 */
40840Sstevel@tonic-gate 	line = s_strdup(str);
40850Sstevel@tonic-gate 
40860Sstevel@tonic-gate 	/* Eat up leading whitespace */
40870Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
40880Sstevel@tonic-gate 		str++;
40890Sstevel@tonic-gate 
40900Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
40910Sstevel@tonic-gate 		cmd = s_strdup("#");
40920Sstevel@tonic-gate 		sep = NULL;
40930Sstevel@tonic-gate 		arg = s_strdup(str + 1);
40940Sstevel@tonic-gate 		flag = BAM_COMMENT;
40953446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
40963446Smrj 			in_liveupgrade = 1;
40973446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
40983446Smrj 			in_liveupgrade = 0;
40993446Smrj 		}
41000Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
41010Sstevel@tonic-gate 		cmd = sep = arg = NULL;
41020Sstevel@tonic-gate 		flag = BAM_EMPTY;
41030Sstevel@tonic-gate 	} else {
41040Sstevel@tonic-gate 		/*
41050Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
41060Sstevel@tonic-gate 		 * However various development bits use '=' as a
41070Sstevel@tonic-gate 		 * separator. In addition, external users also
41080Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
41090Sstevel@tonic-gate 		 */
41100Sstevel@tonic-gate 		cp = str;
41110Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
41120Sstevel@tonic-gate 			if (*str == '\0') {
41130Sstevel@tonic-gate 				cmd = s_strdup(cp);
41140Sstevel@tonic-gate 				sep = arg = NULL;
41150Sstevel@tonic-gate 				break;
41160Sstevel@tonic-gate 			}
41170Sstevel@tonic-gate 			str++;
41180Sstevel@tonic-gate 		}
41190Sstevel@tonic-gate 
41200Sstevel@tonic-gate 		if (*str != '\0') {
41210Sstevel@tonic-gate 			save = *str;
41220Sstevel@tonic-gate 			*str = '\0';
41230Sstevel@tonic-gate 			cmd = s_strdup(cp);
41240Sstevel@tonic-gate 			*str = save;
41250Sstevel@tonic-gate 
41260Sstevel@tonic-gate 			str++;
41270Sstevel@tonic-gate 			save = *str;
41280Sstevel@tonic-gate 			*str = '\0';
41290Sstevel@tonic-gate 			sep = s_strdup(str - 1);
41300Sstevel@tonic-gate 			*str = save;
41310Sstevel@tonic-gate 
41320Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
41330Sstevel@tonic-gate 				str++;
41340Sstevel@tonic-gate 			if (*str == '\0')
41350Sstevel@tonic-gate 				arg = NULL;
41360Sstevel@tonic-gate 			else
41370Sstevel@tonic-gate 				arg = s_strdup(str);
41380Sstevel@tonic-gate 		}
41390Sstevel@tonic-gate 	}
41400Sstevel@tonic-gate 
41410Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
41420Sstevel@tonic-gate 
41430Sstevel@tonic-gate 	lp->cmd = cmd;
41440Sstevel@tonic-gate 	lp->sep = sep;
41450Sstevel@tonic-gate 	lp->arg = arg;
41460Sstevel@tonic-gate 	lp->line = line;
41470Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
41480Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
41490Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
41500Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
41510Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
41523446Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
41530Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
4154662Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
41556448Svikram 			curr_ent->flags |= BAM_ENTRY_BOOTADM;
41566448Svikram 			BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
4157662Sszhou 		} else {
4158662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
41593446Smrj 			if (in_liveupgrade) {
41606448Svikram 				curr_ent->flags |= BAM_ENTRY_LU;
41616448Svikram 				BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
41623446Smrj 			}
4163662Sszhou 		}
41643446Smrj 		curr_ent->entryNum = *entryNum;
41650Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
41660Sstevel@tonic-gate 		/*
41670Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
41680Sstevel@tonic-gate 		 * by the subsequent title
41690Sstevel@tonic-gate 		 */
41700Sstevel@tonic-gate 		lp->entryNum = *entryNum;
41710Sstevel@tonic-gate 		lp->flags = flag;
41720Sstevel@tonic-gate 	} else {
41730Sstevel@tonic-gate 		lp->entryNum = *entryNum;
41743446Smrj 
41753446Smrj 		if (*entryNum == ENTRY_INIT) {
41763446Smrj 			lp->flags = BAM_GLOBAL;
41773446Smrj 		} else {
41783446Smrj 			lp->flags = BAM_ENTRY;
41793446Smrj 
41803446Smrj 			if (cmd && arg) {
41816448Svikram 				if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
41826448Svikram 					BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
41833446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
41846448Svikram 				} else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
41856448Svikram 				    == 0) {
41866448Svikram 					BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
41876448Svikram 					    arg));
41886448Svikram 					curr_ent->flags |= BAM_ENTRY_FINDROOT;
41896448Svikram 				} else if (strcmp(cmd,
41906448Svikram 				    menu_cmds[CHAINLOADER_CMD]) == 0) {
41916448Svikram 					BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
41926448Svikram 					    arg));
41933446Smrj 					curr_ent->flags |=
41943446Smrj 					    BAM_ENTRY_CHAINLOADER;
41956448Svikram 				} else if (kernel_parser(curr_ent, cmd, arg,
41966448Svikram 				    lp->lineNum) != BAM_SUCCESS) {
41976448Svikram 					(void) module_parser(curr_ent, cmd,
41986448Svikram 					    arg, lp->lineNum);
41996448Svikram 				}
42003446Smrj 			}
42013446Smrj 		}
42020Sstevel@tonic-gate 	}
42030Sstevel@tonic-gate 
4204662Sszhou 	/* record default, old default, and entry line ranges */
4205662Sszhou 	if (lp->flags == BAM_GLOBAL &&
4206662Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4207662Sszhou 		mp->curdefault = lp;
4208662Sszhou 	} else if (lp->flags == BAM_COMMENT &&
4209662Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4210662Sszhou 		mp->olddefault = lp;
42113446Smrj 	} else if (lp->flags == BAM_COMMENT &&
42123446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
42133446Smrj 		mp->old_rc_default = lp;
4214662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
42153446Smrj 	    (lp->flags == BAM_COMMENT &&
42163446Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
4217662Sszhou 		boot_entry_addline(curr_ent, lp);
4218662Sszhou 	}
42190Sstevel@tonic-gate 	append_line(mp, lp);
42200Sstevel@tonic-gate 
42210Sstevel@tonic-gate 	prev = lp;
42220Sstevel@tonic-gate }
42230Sstevel@tonic-gate 
42246448Svikram void
4225621Svikram update_numbering(menu_t *mp)
4226621Svikram {
4227621Svikram 	int lineNum;
4228621Svikram 	int entryNum;
4229621Svikram 	int old_default_value;
4230621Svikram 	line_t *lp, *prev, *default_lp, *default_entry;
4231621Svikram 	char buf[PATH_MAX];
4232621Svikram 
4233621Svikram 	if (mp->start == NULL) {
4234621Svikram 		return;
4235621Svikram 	}
4236621Svikram 
4237621Svikram 	lineNum = LINE_INIT;
4238621Svikram 	entryNum = ENTRY_INIT;
4239621Svikram 	old_default_value = ENTRY_INIT;
4240621Svikram 	lp = default_lp = default_entry = NULL;
4241621Svikram 
4242621Svikram 	prev = NULL;
4243621Svikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4244621Svikram 		lp->lineNum = ++lineNum;
4245621Svikram 
4246621Svikram 		/*
4247621Svikram 		 * Get the value of the default command
4248621Svikram 		 */
4249621Svikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
4250621Svikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4251621Svikram 		    lp->arg) {
4252621Svikram 			old_default_value = atoi(lp->arg);
4253621Svikram 			default_lp = lp;
4254621Svikram 		}
4255621Svikram 
4256621Svikram 		/*
42576448Svikram 		 * If not a booting entry, nothing else to fix for this
4258621Svikram 		 * entry
4259621Svikram 		 */
4260621Svikram 		if (lp->entryNum == ENTRY_INIT)
4261621Svikram 			continue;
4262621Svikram 
4263621Svikram 		/*
4264621Svikram 		 * Record the position of the default entry.
4265621Svikram 		 * The following works because global
4266621Svikram 		 * commands like default and timeout should precede
4267621Svikram 		 * actual boot entries, so old_default_value
4268621Svikram 		 * is already known (or default cmd is missing).
4269621Svikram 		 */
4270621Svikram 		if (default_entry == NULL &&
4271621Svikram 		    old_default_value != ENTRY_INIT &&
4272621Svikram 		    lp->entryNum == old_default_value) {
4273621Svikram 			default_entry = lp;
4274621Svikram 		}
4275621Svikram 
4276621Svikram 		/*
4277621Svikram 		 * Now fixup the entry number
4278621Svikram 		 */
4279621Svikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4280621Svikram 			lp->entryNum = ++entryNum;
4281621Svikram 			/* fixup the bootadm header */
4282621Svikram 			if (prev && prev->flags == BAM_COMMENT &&
42833446Smrj 			    prev->arg &&
42843446Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4285621Svikram 				prev->entryNum = lp->entryNum;
4286621Svikram 			}
4287621Svikram 		} else {
4288621Svikram 			lp->entryNum = entryNum;
4289621Svikram 		}
4290621Svikram 	}
4291621Svikram 
4292621Svikram 	/*
4293621Svikram 	 * No default command in menu, simply return
4294621Svikram 	 */
4295621Svikram 	if (default_lp == NULL) {
4296621Svikram 		return;
4297621Svikram 	}
4298621Svikram 
4299621Svikram 	free(default_lp->arg);
4300621Svikram 	free(default_lp->line);
4301621Svikram 
4302621Svikram 	if (default_entry == NULL) {
4303621Svikram 		default_lp->arg = s_strdup("0");
4304621Svikram 	} else {
4305621Svikram 		(void) snprintf(buf, sizeof (buf), "%d",
4306621Svikram 		    default_entry->entryNum);
4307621Svikram 		default_lp->arg = s_strdup(buf);
4308621Svikram 	}
4309621Svikram 
4310621Svikram 	/*
4311621Svikram 	 * The following is required since only the line field gets
4312621Svikram 	 * written back to menu.lst
4313621Svikram 	 */
4314621Svikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
4315621Svikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4316621Svikram 	default_lp->line = s_strdup(buf);
4317621Svikram }
4318621Svikram 
4319621Svikram 
43200Sstevel@tonic-gate static menu_t *
43210Sstevel@tonic-gate menu_read(char *menu_path)
43220Sstevel@tonic-gate {
43230Sstevel@tonic-gate 	FILE *fp;
43240Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
43250Sstevel@tonic-gate 	menu_t *mp;
43260Sstevel@tonic-gate 	int line, entry, len, n;
43270Sstevel@tonic-gate 
43280Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
43290Sstevel@tonic-gate 
43300Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
43310Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
43320Sstevel@tonic-gate 		return (mp);
43330Sstevel@tonic-gate 	}
43340Sstevel@tonic-gate 
43350Sstevel@tonic-gate 
43360Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
43370Sstevel@tonic-gate 	line = LINE_INIT;
43380Sstevel@tonic-gate 	entry = ENTRY_INIT;
43390Sstevel@tonic-gate 	cp = buf;
43400Sstevel@tonic-gate 	len = sizeof (buf);
43410Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
43420Sstevel@tonic-gate 		n = strlen(cp);
43430Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
43440Sstevel@tonic-gate 			len -= n - 1;
43450Sstevel@tonic-gate 			assert(len >= 2);
43460Sstevel@tonic-gate 			cp += n - 1;
43470Sstevel@tonic-gate 			continue;
43480Sstevel@tonic-gate 		}
43490Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
43500Sstevel@tonic-gate 		cp = buf;
43510Sstevel@tonic-gate 		len = sizeof (buf);
43520Sstevel@tonic-gate 	}
43530Sstevel@tonic-gate 
43540Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
43550Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
43560Sstevel@tonic-gate 	}
43570Sstevel@tonic-gate 
43580Sstevel@tonic-gate 	return (mp);
43590Sstevel@tonic-gate }
43600Sstevel@tonic-gate 
43610Sstevel@tonic-gate static error_t
43620Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
43630Sstevel@tonic-gate {
43640Sstevel@tonic-gate 	char *eq;
43650Sstevel@tonic-gate 	char *opt_dup;
43660Sstevel@tonic-gate 	int entryNum;
43670Sstevel@tonic-gate 
43680Sstevel@tonic-gate 	assert(mp);
43690Sstevel@tonic-gate 	assert(mp->start);
43700Sstevel@tonic-gate 	assert(opt);
43710Sstevel@tonic-gate 
43720Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
43730Sstevel@tonic-gate 
43740Sstevel@tonic-gate 	if (entry)
43750Sstevel@tonic-gate 		*entry = ENTRY_INIT;
43760Sstevel@tonic-gate 	if (title)
43770Sstevel@tonic-gate 		*title = NULL;
43780Sstevel@tonic-gate 
43790Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
43800Sstevel@tonic-gate 	if (eq == NULL) {
43810Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
43820Sstevel@tonic-gate 		free(opt_dup);
43830Sstevel@tonic-gate 		return (BAM_ERROR);
43840Sstevel@tonic-gate 	}
43850Sstevel@tonic-gate 
43860Sstevel@tonic-gate 	*eq = '\0';
43870Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
43880Sstevel@tonic-gate 		assert(mp->end);
43890Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
43900Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
43910Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
43920Sstevel@tonic-gate 			free(opt_dup);
43930Sstevel@tonic-gate 			return (BAM_ERROR);
43940Sstevel@tonic-gate 		}
43950Sstevel@tonic-gate 		*entry = entryNum;
43960Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
43970Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
43980Sstevel@tonic-gate 	} else {
43990Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
44000Sstevel@tonic-gate 		free(opt_dup);
44010Sstevel@tonic-gate 		return (BAM_ERROR);
44020Sstevel@tonic-gate 	}
44030Sstevel@tonic-gate 
44040Sstevel@tonic-gate 	free(opt_dup);
44050Sstevel@tonic-gate 	return (BAM_SUCCESS);
44060Sstevel@tonic-gate }
44070Sstevel@tonic-gate 
44080Sstevel@tonic-gate /*
44090Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
44100Sstevel@tonic-gate  * only title lines in file are printed.
44110Sstevel@tonic-gate  *
44120Sstevel@tonic-gate  * If invoked with a title or entry #, all
44130Sstevel@tonic-gate  * lines in *every* matching entry are listed
44140Sstevel@tonic-gate  */
44150Sstevel@tonic-gate static error_t
44160Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
44170Sstevel@tonic-gate {
44180Sstevel@tonic-gate 	line_t *lp;
44190Sstevel@tonic-gate 	int entry = ENTRY_INIT;
44200Sstevel@tonic-gate 	int found;
44210Sstevel@tonic-gate 	char *title = NULL;
44220Sstevel@tonic-gate 
44230Sstevel@tonic-gate 	assert(mp);
44240Sstevel@tonic-gate 	assert(menu_path);
44250Sstevel@tonic-gate 
44266448Svikram 	/* opt is optional */
44276448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
44286448Svikram 	    opt ? opt : "<NULL>"));
44296448Svikram 
44300Sstevel@tonic-gate 	if (mp->start == NULL) {
44310Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
44320Sstevel@tonic-gate 		return (BAM_ERROR);
44330Sstevel@tonic-gate 	}
44340Sstevel@tonic-gate 
44350Sstevel@tonic-gate 	if (opt != NULL) {
44360Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
44370Sstevel@tonic-gate 			return (BAM_ERROR);
44380Sstevel@tonic-gate 		}
44390Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
44400Sstevel@tonic-gate 	} else {
44410Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
44420Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
44430Sstevel@tonic-gate 	}
44440Sstevel@tonic-gate 
44450Sstevel@tonic-gate 	found = 0;
44460Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
44470Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
44480Sstevel@tonic-gate 			continue;
44490Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
44500Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
44510Sstevel@tonic-gate 			    lp->arg);
44520Sstevel@tonic-gate 			found = 1;
44530Sstevel@tonic-gate 			continue;
44540Sstevel@tonic-gate 		}
44550Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
44560Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
44570Sstevel@tonic-gate 			found = 1;
44580Sstevel@tonic-gate 			continue;
44590Sstevel@tonic-gate 		}
44600Sstevel@tonic-gate 
44610Sstevel@tonic-gate 		/*
44620Sstevel@tonic-gate 		 * We set the entry value here so that all lines
44630Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
44640Sstevel@tonic-gate 		 * title in other entries, all lines in those
44650Sstevel@tonic-gate 		 * entries get printed as well.
44660Sstevel@tonic-gate 		 */
44670Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
44680Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
44690Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
44700Sstevel@tonic-gate 			entry = lp->entryNum;
44710Sstevel@tonic-gate 			found = 1;
44720Sstevel@tonic-gate 			continue;
44730Sstevel@tonic-gate 		}
44740Sstevel@tonic-gate 	}
44750Sstevel@tonic-gate 
44760Sstevel@tonic-gate 	if (!found) {
44770Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
44780Sstevel@tonic-gate 		return (BAM_ERROR);
44790Sstevel@tonic-gate 	}
44800Sstevel@tonic-gate 
44810Sstevel@tonic-gate 	return (BAM_SUCCESS);
44820Sstevel@tonic-gate }
44830Sstevel@tonic-gate 
44845084Sjohnlev int
44850Sstevel@tonic-gate add_boot_entry(menu_t *mp,
44860Sstevel@tonic-gate 	char *title,
44876448Svikram 	char *findroot,
44880Sstevel@tonic-gate 	char *kernel,
44895084Sjohnlev 	char *mod_kernel,
44900Sstevel@tonic-gate 	char *module)
44910Sstevel@tonic-gate {
44926448Svikram 	int		lineNum;
44936448Svikram 	int		entryNum;
44946448Svikram 	char		linebuf[BAM_MAXLINE];
44956448Svikram 	menu_cmd_t	k_cmd;
44966448Svikram 	menu_cmd_t	m_cmd;
44976448Svikram 	const char	*fcn = "add_boot_entry()";
44980Sstevel@tonic-gate 
44990Sstevel@tonic-gate 	assert(mp);
45000Sstevel@tonic-gate 
45016448Svikram 	INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
45026448Svikram 	if (findroot == NULL) {
45036448Svikram 		bam_error(NULL_FINDROOT);
45046448Svikram 		return (BAM_ERROR);
45056448Svikram 	}
45066448Svikram 
45070Sstevel@tonic-gate 	if (title == NULL) {
4508656Sszhou 		title = "Solaris";	/* default to Solaris */
45090Sstevel@tonic-gate 	}
45100Sstevel@tonic-gate 	if (kernel == NULL) {
45110Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
45120Sstevel@tonic-gate 		return (BAM_ERROR);
45130Sstevel@tonic-gate 	}
45140Sstevel@tonic-gate 	if (module == NULL) {
45153446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
45163446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
45173446Smrj 			return (BAM_ERROR);
45183446Smrj 		}
45193446Smrj 
45203446Smrj 		/* Figure the commands out from the kernel line */
45213446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
45223446Smrj 			module = DIRECT_BOOT_ARCHIVE;
45233446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
45243446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
45253446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
45263446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
45273446Smrj 			k_cmd = KERNEL_CMD;
45283446Smrj 			m_cmd = MODULE_CMD;
45293446Smrj 		} else {
45303446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
45313446Smrj 			k_cmd = KERNEL_CMD;
45323446Smrj 			m_cmd = MODULE_CMD;
45333446Smrj 		}
45343446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
45353446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
45363446Smrj 		/*
45373446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
45383446Smrj 		 * command.  Otherwise, use "kernel".
45393446Smrj 		 */
45403446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
45413446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
45423446Smrj 	} else {
45433446Smrj 		k_cmd = KERNEL_CMD;
45443446Smrj 		m_cmd = MODULE_CMD;
45450Sstevel@tonic-gate 	}
45460Sstevel@tonic-gate 
45470Sstevel@tonic-gate 	if (mp->start) {
45480Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
45490Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
45500Sstevel@tonic-gate 	} else {
45510Sstevel@tonic-gate 		lineNum = LINE_INIT;
45520Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
45530Sstevel@tonic-gate 	}
45540Sstevel@tonic-gate 
45550Sstevel@tonic-gate 	/*
45560Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
45570Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
45580Sstevel@tonic-gate 	 */
45590Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
45603446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
4561662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
45620Sstevel@tonic-gate 
45630Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
45640Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
4565662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
4566662Sszhou 
45676448Svikram 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
45686448Svikram 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
45696448Svikram 	line_parser(mp, linebuf, &lineNum, &entryNum);
45706448Svikram 	BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
45710Sstevel@tonic-gate 
45720Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
45733446Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
4574662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
45750Sstevel@tonic-gate 
45765084Sjohnlev 	if (mod_kernel != NULL) {
45775084Sjohnlev 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
45785084Sjohnlev 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
45795084Sjohnlev 		line_parser(mp, linebuf, &lineNum, &entryNum);
45805084Sjohnlev 	}
45815084Sjohnlev 
45820Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
45833446Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
4584662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
45850Sstevel@tonic-gate 
45860Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
45873446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
4588662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
45890Sstevel@tonic-gate 
45900Sstevel@tonic-gate 	return (entryNum);
45910Sstevel@tonic-gate }
45920Sstevel@tonic-gate 
45930Sstevel@tonic-gate static error_t
45940Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
45950Sstevel@tonic-gate {
45966448Svikram 	line_t		*lp;
45976448Svikram 	line_t		*freed;
45986448Svikram 	entry_t		*ent;
45996448Svikram 	entry_t		*tmp;
46006448Svikram 	int		deleted;
46016448Svikram 	const char	*fcn = "do_delete()";
46020Sstevel@tonic-gate 
46030Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
46040Sstevel@tonic-gate 
46056448Svikram 	tmp = NULL;
46066448Svikram 
4607662Sszhou 	ent = mp->entries;
4608662Sszhou 	while (ent) {
4609662Sszhou 		lp = ent->start;
4610662Sszhou 		/* check entry number and make sure it's a bootadm entry */
4611662Sszhou 		if (lp->flags != BAM_COMMENT ||
46123446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
4613662Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
4614662Sszhou 			ent = ent->next;
46150Sstevel@tonic-gate 			continue;
46160Sstevel@tonic-gate 		}
46170Sstevel@tonic-gate 
4618662Sszhou 		/* free the entry content */
4619662Sszhou 		do {
4620662Sszhou 			freed = lp;
4621662Sszhou 			lp = lp->next;	/* prev stays the same */
46226448Svikram 			BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
4623662Sszhou 			unlink_line(mp, freed);
4624662Sszhou 			line_free(freed);
4625662Sszhou 		} while (freed != ent->end);
4626662Sszhou 
4627662Sszhou 		/* free the entry_t structure */
46286448Svikram 		assert(tmp == NULL);
4629662Sszhou 		tmp = ent;
4630662Sszhou 		ent = ent->next;
4631662Sszhou 		if (tmp->prev)
4632662Sszhou 			tmp->prev->next = ent;
46330Sstevel@tonic-gate 		else
4634662Sszhou 			mp->entries = ent;
4635662Sszhou 		if (ent)
4636662Sszhou 			ent->prev = tmp->prev;
46376448Svikram 		BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
46386448Svikram 		free(tmp);
46396448Svikram 		tmp = NULL;
46400Sstevel@tonic-gate 		deleted = 1;
46410Sstevel@tonic-gate 	}
46420Sstevel@tonic-gate 
46436448Svikram 	assert(tmp == NULL);
46446448Svikram 
46450Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
46460Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
46470Sstevel@tonic-gate 		return (BAM_ERROR);
46480Sstevel@tonic-gate 	}
46490Sstevel@tonic-gate 
4650621Svikram 	/*
4651621Svikram 	 * Now that we have deleted an entry, update
4652621Svikram 	 * the entry numbering and the default cmd.
4653621Svikram 	 */
4654621Svikram 	update_numbering(mp);
4655621Svikram 
46560Sstevel@tonic-gate 	return (BAM_SUCCESS);
46570Sstevel@tonic-gate }
46580Sstevel@tonic-gate 
46590Sstevel@tonic-gate static error_t
46606448Svikram delete_all_entries(menu_t *mp, char *dummy, char *opt)
46610Sstevel@tonic-gate {
46620Sstevel@tonic-gate 	assert(mp);
46636448Svikram 	assert(dummy == NULL);
46640Sstevel@tonic-gate 	assert(opt == NULL);
46650Sstevel@tonic-gate 
46666448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
46676448Svikram 
46680Sstevel@tonic-gate 	if (mp->start == NULL) {
46696448Svikram 		bam_print(EMPTY_MENU);
46700Sstevel@tonic-gate 		return (BAM_SUCCESS);
46710Sstevel@tonic-gate 	}
46720Sstevel@tonic-gate 
46730Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
46740Sstevel@tonic-gate 		return (BAM_ERROR);
46750Sstevel@tonic-gate 	}
46760Sstevel@tonic-gate 
46770Sstevel@tonic-gate 	return (BAM_WRITE);
46780Sstevel@tonic-gate }
46790Sstevel@tonic-gate 
46800Sstevel@tonic-gate static FILE *
46816448Svikram create_diskmap(char *osroot)
46820Sstevel@tonic-gate {
46830Sstevel@tonic-gate 	FILE *fp;
46848954SEnrico.Perla@Sun.COM 	char cmd[PATH_MAX + 16];
46858954SEnrico.Perla@Sun.COM 	char path[PATH_MAX];
46866448Svikram 	const char *fcn = "create_diskmap()";
46870Sstevel@tonic-gate 
46880Sstevel@tonic-gate 	/* make sure we have a map file */
46890Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
46900Sstevel@tonic-gate 	if (fp == NULL) {
46918954SEnrico.Perla@Sun.COM 		int	ret;
46928954SEnrico.Perla@Sun.COM 
46938954SEnrico.Perla@Sun.COM 		ret = snprintf(path, sizeof (path), "%s/%s", osroot,
46948954SEnrico.Perla@Sun.COM 		    CREATE_DISKMAP);
46958954SEnrico.Perla@Sun.COM 		if (ret >= sizeof (path)) {
46968954SEnrico.Perla@Sun.COM 			bam_error(PATH_TOO_LONG, osroot);
46978954SEnrico.Perla@Sun.COM 			return (NULL);
46988954SEnrico.Perla@Sun.COM 		}
46998954SEnrico.Perla@Sun.COM 		if (is_safe_exec(path) == BAM_ERROR)
47008954SEnrico.Perla@Sun.COM 			return (NULL);
47018954SEnrico.Perla@Sun.COM 
47020Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
47036448Svikram 		    "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
47046448Svikram 		if (exec_cmd(cmd, NULL) != 0)
47056448Svikram 			return (NULL);
47060Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
47076448Svikram 		INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
47086448Svikram 		if (fp) {
47096448Svikram 			BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
47106448Svikram 		} else {
47116448Svikram 			BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
47126448Svikram 		}
47130Sstevel@tonic-gate 	}
47140Sstevel@tonic-gate 	return (fp);
47150Sstevel@tonic-gate }
47160Sstevel@tonic-gate 
47170Sstevel@tonic-gate #define	SECTOR_SIZE	512
47180Sstevel@tonic-gate 
47190Sstevel@tonic-gate static int
47200Sstevel@tonic-gate get_partition(char *device)
47210Sstevel@tonic-gate {
47220Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
47230Sstevel@tonic-gate 	struct mboot *mboot;
47240Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
47250Sstevel@tonic-gate 	char *wholedisk, *slice;
47260Sstevel@tonic-gate 
47270Sstevel@tonic-gate 	/* form whole disk (p0) */
47280Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
47290Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
47300Sstevel@tonic-gate 	if (!is_pcfs)
47310Sstevel@tonic-gate 		*slice = '\0';
47320Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
47330Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
47340Sstevel@tonic-gate 	if (!is_pcfs)
47350Sstevel@tonic-gate 		*slice = 's';
47360Sstevel@tonic-gate 
47370Sstevel@tonic-gate 	/* read boot sector */
47380Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
47390Sstevel@tonic-gate 	free(wholedisk);
47400Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
47410Sstevel@tonic-gate 		return (partno);
47420Sstevel@tonic-gate 	}
47430Sstevel@tonic-gate 	(void) close(fd);
47440Sstevel@tonic-gate 
47450Sstevel@tonic-gate 	/* parse fdisk table */
47460Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
47470Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
47480Sstevel@tonic-gate 		struct ipart *part =
47490Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
47500Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
47510Sstevel@tonic-gate 			if (part->systid == 0xbe) {
47520Sstevel@tonic-gate 				partno = i;
47530Sstevel@tonic-gate 				break;
47540Sstevel@tonic-gate 			}
47550Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
47560Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
47570Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
47580Sstevel@tonic-gate 				partno = i;
47590Sstevel@tonic-gate 				break;
47600Sstevel@tonic-gate 			}
47610Sstevel@tonic-gate 		}
47620Sstevel@tonic-gate 	}
47630Sstevel@tonic-gate 	return (partno);
47640Sstevel@tonic-gate }
47650Sstevel@tonic-gate 
47666448Svikram char *
47676448Svikram get_grubroot(char *osroot, char *osdev, char *menu_root)
47686448Svikram {
47696448Svikram 	char		*grubroot;	/* (hd#,#,#) */
47706448Svikram 	char		*slice;
47716448Svikram 	char		*grubhd;
47726448Svikram 	int		fdiskpart;
47736448Svikram 	int		found = 0;
47746448Svikram 	char		*devname;
47756448Svikram 	char		*ctdname = strstr(osdev, "dsk/");
47766448Svikram 	char		linebuf[PATH_MAX];
47776448Svikram 	FILE		*fp;
47786448Svikram 
47796448Svikram 	INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
47806448Svikram 	if (ctdname == NULL) {
47816448Svikram 		bam_error(INVALID_DEV_DSK, osdev);
47820Sstevel@tonic-gate 		return (NULL);
47836448Svikram 	}
47846448Svikram 
47856448Svikram 	if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
47866448Svikram 		/* menu bears no resemblance to our reality */
47878642SVikram.Hegde@Sun.COM 		bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev);
47886448Svikram 		return (NULL);
47896448Svikram 	}
47900Sstevel@tonic-gate 
47910Sstevel@tonic-gate 	ctdname += strlen("dsk/");
47920Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
47930Sstevel@tonic-gate 	if (slice)
47940Sstevel@tonic-gate 		*slice = '\0';
47950Sstevel@tonic-gate 
47966448Svikram 	fp = create_diskmap(osroot);
47976448Svikram 	if (fp == NULL) {
47986448Svikram 		bam_error(DISKMAP_FAIL, osroot);
47996448Svikram 		return (NULL);
48006448Svikram 	}
48016448Svikram 
48020Sstevel@tonic-gate 	rewind(fp);
48030Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
48040Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
48050Sstevel@tonic-gate 		if (grubhd)
48060Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
48070Sstevel@tonic-gate 		else
48080Sstevel@tonic-gate 			devname = NULL;
48090Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
48100Sstevel@tonic-gate 			found = 1;
48110Sstevel@tonic-gate 			break;
48120Sstevel@tonic-gate 		}
48130Sstevel@tonic-gate 	}
48140Sstevel@tonic-gate 
48150Sstevel@tonic-gate 	if (slice)
48160Sstevel@tonic-gate 		*slice = 's';
48170Sstevel@tonic-gate 
48186448Svikram 	(void) fclose(fp);
48196448Svikram 	fp = NULL;
48206448Svikram 
48216448Svikram 	INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
48220Sstevel@tonic-gate 	if (found == 0) {
48238642SVikram.Hegde@Sun.COM 		bam_error(BIOSDEV_SKIP, osdev);
48240Sstevel@tonic-gate 		return (NULL);
48256448Svikram 	}
48266448Svikram 
48276448Svikram 	fdiskpart = get_partition(osdev);
48286448Svikram 	INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1);
48296448Svikram 	if (fdiskpart == -1) {
48306448Svikram 		bam_error(FDISKPART_FAIL, osdev);
48316448Svikram 		return (NULL);
48326448Svikram 	}
48336448Svikram 
48346448Svikram 	grubroot = s_calloc(1, 10);
48350Sstevel@tonic-gate 	if (slice) {
48366448Svikram 		(void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
48370Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
48380Sstevel@tonic-gate 	} else
48396448Svikram 		(void) snprintf(grubroot, 10, "(hd%s,%d)",
48400Sstevel@tonic-gate 		    grubhd, fdiskpart);
48410Sstevel@tonic-gate 
48426448Svikram 	assert(fp == NULL);
48436448Svikram 	assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
48446448Svikram 	return (grubroot);
48456448Svikram }
48466448Svikram 
48476448Svikram static char *
48486448Svikram find_primary_common(char *mntpt, char *fstype)
48496448Svikram {
48506448Svikram 	char		signdir[PATH_MAX];
48516448Svikram 	char		tmpsign[MAXNAMELEN + 1];
48526448Svikram 	char		*lu;
48536448Svikram 	char		*ufs;
48546448Svikram 	char		*zfs;
48556448Svikram 	DIR		*dirp = NULL;
48566448Svikram 	struct dirent	*entp;
48576448Svikram 	struct stat	sb;
48586448Svikram 	const char	*fcn = "find_primary_common()";
48596448Svikram 
48606448Svikram 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
48616448Svikram 	    mntpt, GRUBSIGN_DIR);
48626448Svikram 
48636448Svikram 	if (stat(signdir, &sb) == -1) {
48646448Svikram 		BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
48656448Svikram 		return (NULL);
48666448Svikram 	}
48676448Svikram 
48686448Svikram 	dirp = opendir(signdir);
48696448Svikram 	INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
48706448Svikram 	if (dirp == NULL) {
48716448Svikram 		bam_error(OPENDIR_FAILED, signdir, strerror(errno));
48726448Svikram 		return (NULL);
48736448Svikram 	}
48746448Svikram 
48756448Svikram 	ufs = zfs = lu = NULL;
48766448Svikram 
48776448Svikram 	while (entp = readdir(dirp)) {
48786448Svikram 		if (strcmp(entp->d_name, ".") == 0 ||
48796448Svikram 		    strcmp(entp->d_name, "..") == 0)
48806448Svikram 			continue;
48816448Svikram 
48826448Svikram 		(void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
48836448Svikram 
48846448Svikram 		if (lu == NULL &&
48856448Svikram 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
48866448Svikram 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
48876448Svikram 			lu = s_strdup(tmpsign);
48886448Svikram 		}
48896448Svikram 
48906448Svikram 		if (ufs == NULL &&
48916448Svikram 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
48926448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
48936448Svikram 			ufs = s_strdup(tmpsign);
48946448Svikram 		}
48956448Svikram 
48966448Svikram 		if (zfs == NULL &&
48976448Svikram 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
48986448Svikram 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
48996448Svikram 			zfs = s_strdup(tmpsign);
49006448Svikram 		}
49016448Svikram 	}
49026448Svikram 
49036448Svikram 	BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
49046448Svikram 	    zfs ? zfs : "NULL",
49056448Svikram 	    ufs ? ufs : "NULL",
49066448Svikram 	    lu ? lu : "NULL"));
49076448Svikram 
49086448Svikram 	if (dirp) {
49096448Svikram 		(void) closedir(dirp);
49106448Svikram 		dirp = NULL;
49116448Svikram 	}
49126448Svikram 
49136448Svikram 	if (strcmp(fstype, "ufs") == 0 && zfs) {
49146448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
49156448Svikram 		free(zfs);
49166448Svikram 		zfs = NULL;
49176448Svikram 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
49186448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
49196448Svikram 		free(ufs);
49206448Svikram 		ufs = NULL;
49216448Svikram 	}
49226448Svikram 
49236448Svikram 	assert(dirp == NULL);
49246448Svikram 
49256448Svikram 	/* For now, we let Live Upgrade take care of its signature itself */
49266448Svikram 	if (lu) {
49276448Svikram 		BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
49286448Svikram 		free(lu);
49296448Svikram 		lu = NULL;
49306448Svikram 	}
49316448Svikram 
49326448Svikram 	return (zfs ? zfs : ufs);
49336448Svikram }
49346448Svikram 
49356448Svikram static char *
49366448Svikram find_backup_common(char *mntpt, char *fstype)
49376448Svikram {
49386448Svikram 	FILE		*bfp = NULL;
49396448Svikram 	char		tmpsign[MAXNAMELEN + 1];
49406448Svikram 	char		backup[PATH_MAX];
49416448Svikram 	char		*ufs;
49426448Svikram 	char		*zfs;
49436448Svikram 	char		*lu;
49446448Svikram 	int		error;
49456448Svikram 	const char	*fcn = "find_backup_common()";
49466448Svikram 
49476448Svikram 	/*
49486448Svikram 	 * We didn't find it in the primary directory.
49496448Svikram 	 * Look at the backup
49506448Svikram 	 */
49516448Svikram 	(void) snprintf(backup, sizeof (backup), "%s%s",
49526448Svikram 	    mntpt, GRUBSIGN_BACKUP);
49536448Svikram 
49546448Svikram 	bfp = fopen(backup, "r");
49556448Svikram 	if (bfp == NULL) {
49566448Svikram 		error = errno;
49576448Svikram 		if (bam_verbose) {
49586448Svikram 			bam_error(OPEN_FAIL, backup, strerror(error));
49596448Svikram 		}
49606448Svikram 		BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
49616448Svikram 		return (NULL);
49626448Svikram 	}
49636448Svikram 
49646448Svikram 	ufs = zfs = lu = NULL;
49656448Svikram 
49666448Svikram 	while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
49676448Svikram 
49686448Svikram 		if (lu == NULL &&
49696448Svikram 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
49706448Svikram 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
49716448Svikram 			lu = s_strdup(tmpsign);
49726448Svikram 		}
49736448Svikram 
49746448Svikram 		if (ufs == NULL &&
49756448Svikram 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
49766448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
49776448Svikram 			ufs = s_strdup(tmpsign);
49786448Svikram 		}
49796448Svikram 
49806448Svikram 		if (zfs == NULL &&
49816448Svikram 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
49826448Svikram 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
49836448Svikram 			zfs = s_strdup(tmpsign);
49846448Svikram 		}
49856448Svikram 	}
49866448Svikram 
49876448Svikram 	BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
49886448Svikram 	    zfs ? zfs : "NULL",
49896448Svikram 	    ufs ? ufs : "NULL",
49906448Svikram 	    lu ? lu : "NULL"));
49916448Svikram 
49926448Svikram 	if (bfp) {
49936448Svikram 		(void) fclose(bfp);
49946448Svikram 		bfp = NULL;
49956448Svikram 	}
49966448Svikram 
49976448Svikram 	if (strcmp(fstype, "ufs") == 0 && zfs) {
49986448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
49996448Svikram 		free(zfs);
50006448Svikram 		zfs = NULL;
50016448Svikram 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
50026448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
50036448Svikram 		free(ufs);
50046448Svikram 		ufs = NULL;
50056448Svikram 	}
50066448Svikram 
50076448Svikram 	assert(bfp == NULL);
50086448Svikram 
50096448Svikram 	/* For now, we let Live Upgrade take care of its signature itself */
50106448Svikram 	if (lu) {
50116448Svikram 		BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
50126448Svikram 		free(lu);
50136448Svikram 		lu = NULL;
50146448Svikram 	}
50156448Svikram 
50166448Svikram 	return (zfs ? zfs : ufs);
50176448Svikram }
50186448Svikram 
50196448Svikram static char *
50206448Svikram find_ufs_existing(char *osroot)
50216448Svikram {
50226448Svikram 	char		*sign;
50236448Svikram 	const char	*fcn = "find_ufs_existing()";
50246448Svikram 
50256448Svikram 	sign = find_primary_common(osroot, "ufs");
50266448Svikram 	if (sign == NULL) {
50276448Svikram 		sign = find_backup_common(osroot, "ufs");
50286448Svikram 		BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
50296448Svikram 	} else {
50306448Svikram 		BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
50316448Svikram 	}
50326448Svikram 
50336448Svikram 	return (sign);
50346448Svikram }
50356448Svikram 
50366448Svikram char *
50376448Svikram get_mountpoint(char *special, char *fstype)
50386448Svikram {
50396448Svikram 	FILE		*mntfp;
50406448Svikram 	struct mnttab	mp = {0};
50416448Svikram 	struct mnttab	mpref = {0};
50426448Svikram 	int		error;
50436448Svikram 	int		ret;
50446448Svikram 	const char	*fcn = "get_mountpoint()";
50456448Svikram 
50466448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
50476448Svikram 
50486448Svikram 	mntfp = fopen(MNTTAB, "r");
50496448Svikram 	error = errno;
50506448Svikram 	INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
50516448Svikram 	if (mntfp == NULL) {
50526448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
50536448Svikram 		return (NULL);
50546448Svikram 	}
50556448Svikram 
50566448Svikram 	mpref.mnt_special = special;
50576448Svikram 	mpref.mnt_fstype = fstype;
50586448Svikram 
50596448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
50606448Svikram 	INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
50616448Svikram 	if (ret != 0) {
50626448Svikram 		(void) fclose(mntfp);
50636448Svikram 		BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
50646448Svikram 		return (NULL);
50656448Svikram 	}
50666448Svikram 	(void) fclose(mntfp);
50676448Svikram 
50686448Svikram 	assert(mp.mnt_mountp);
50696448Svikram 
50706448Svikram 	BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
50716448Svikram 
50726448Svikram 	return (s_strdup(mp.mnt_mountp));
50736448Svikram }
50746448Svikram 
50756448Svikram /*
50766448Svikram  * Mounts a "legacy" top dataset (if needed)
50776448Svikram  * Returns:	The mountpoint of the legacy top dataset or NULL on error
50786448Svikram  * 		mnted returns one of the above values defined for zfs_mnted_t
50796448Svikram  */
50806448Svikram static char *
50816448Svikram mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
50826448Svikram {
50836448Svikram 	char		cmd[PATH_MAX];
50846448Svikram 	char		tmpmnt[PATH_MAX];
50856448Svikram 	filelist_t	flist = {0};
50866448Svikram 	char		*is_mounted;
50876448Svikram 	struct stat	sb;
50886448Svikram 	int		ret;
50896448Svikram 	const char	*fcn = "mount_legacy_dataset()";
50906448Svikram 
50916448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
50926448Svikram 
50936448Svikram 	*mnted = ZFS_MNT_ERROR;
50946448Svikram 
50956448Svikram 	(void) snprintf(cmd, sizeof (cmd),
50966448Svikram 	    "/sbin/zfs get -Ho value mounted %s",
50976448Svikram 	    pool);
50986448Svikram 
50996448Svikram 	ret = exec_cmd(cmd, &flist);
51006448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
51016448Svikram 	if (ret != 0) {
51026448Svikram 		bam_error(ZFS_MNTED_FAILED, pool);
51036448Svikram 		return (NULL);
51046448Svikram 	}
51056448Svikram 
51066448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
51076448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
51086448Svikram 		bam_error(BAD_ZFS_MNTED, pool);
51096448Svikram 		filelist_free(&flist);
51106448Svikram 		return (NULL);
51116448Svikram 	}
51126448Svikram 
51136448Svikram 	is_mounted = strtok(flist.head->line, " \t\n");
51146448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
51156448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
51166448Svikram 	if (strcmp(is_mounted, "no") != 0) {
51176448Svikram 		filelist_free(&flist);
51186448Svikram 		*mnted = LEGACY_ALREADY;
51196448Svikram 		/* get_mountpoint returns a strdup'ed string */
51206448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
51216448Svikram 		return (get_mountpoint(pool, "zfs"));
51226448Svikram 	}
51236448Svikram 
51246448Svikram 	filelist_free(&flist);
51256448Svikram 
51266448Svikram 	/*
51276448Svikram 	 * legacy top dataset is not mounted. Mount it now
51286448Svikram 	 * First create a mountpoint.
51296448Svikram 	 */
51306448Svikram 	(void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
51316448Svikram 	    ZFS_LEGACY_MNTPT, getpid());
51326448Svikram 
51336448Svikram 	ret = stat(tmpmnt, &sb);
51346448Svikram 	if (ret == -1) {
51356448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
51368735SEnrico.Perla@Sun.COM 		ret = mkdirp(tmpmnt, DIR_PERMS);
51376448Svikram 		INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
51386448Svikram 		if (ret == -1) {
51396448Svikram 			bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
51406448Svikram 			return (NULL);
51416448Svikram 		}
51426448Svikram 	} else {
51436448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
51446448Svikram 	}
51456448Svikram 
51466448Svikram 	(void) snprintf(cmd, sizeof (cmd),
51476448Svikram 	    "/sbin/mount -F zfs %s %s",
51486448Svikram 	    pool, tmpmnt);
51496448Svikram 
51506448Svikram 	ret = exec_cmd(cmd, NULL);
51516448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
51526448Svikram 	if (ret != 0) {
51536448Svikram 		bam_error(ZFS_MOUNT_FAILED, pool);
51546448Svikram 		(void) rmdir(tmpmnt);
51556448Svikram 		return (NULL);
51566448Svikram 	}
51576448Svikram 
51586448Svikram 	*mnted = LEGACY_MOUNTED;
51596448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
51606448Svikram 	return (s_strdup(tmpmnt));
51616448Svikram }
51626448Svikram 
51636448Svikram /*
51646448Svikram  * Mounts the top dataset (if needed)
51656448Svikram  * Returns:	The mountpoint of the top dataset or NULL on error
51666448Svikram  * 		mnted returns one of the above values defined for zfs_mnted_t
51676448Svikram  */
51686448Svikram static char *
51696448Svikram mount_top_dataset(char *pool, zfs_mnted_t *mnted)
51706448Svikram {
51716448Svikram 	char		cmd[PATH_MAX];
51726448Svikram 	filelist_t	flist = {0};
51736448Svikram 	char		*is_mounted;
51746448Svikram 	char		*mntpt;
51756448Svikram 	char		*zmntpt;
51766448Svikram 	int		ret;
51776448Svikram 	const char	*fcn = "mount_top_dataset()";
51786448Svikram 
51796448Svikram 	*mnted = ZFS_MNT_ERROR;
51806448Svikram 
51816448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
51826448Svikram 
51836448Svikram 	/*
51846448Svikram 	 * First check if the top dataset is a "legacy" dataset
51856448Svikram 	 */
51866448Svikram 	(void) snprintf(cmd, sizeof (cmd),
51876448Svikram 	    "/sbin/zfs get -Ho value mountpoint %s",
51886448Svikram 	    pool);
51896448Svikram 	ret = exec_cmd(cmd, &flist);
51906448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
51916448Svikram 	if (ret != 0) {
51926448Svikram 		bam_error(ZFS_MNTPT_FAILED, pool);
51936448Svikram 		return (NULL);
51946448Svikram 	}
51956448Svikram 
51966448Svikram 	if (flist.head && (flist.head == flist.tail)) {
51976448Svikram 		char *legacy = strtok(flist.head->line, " \t\n");
51986448Svikram 		if (legacy && strcmp(legacy, "legacy") == 0) {
51996448Svikram 			filelist_free(&flist);
52006448Svikram 			BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
52016448Svikram 			return (mount_legacy_dataset(pool, mnted));
52026448Svikram 		}
52036448Svikram 	}
52046448Svikram 
52056448Svikram 	filelist_free(&flist);
52066448Svikram 
52076448Svikram 	BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
52086448Svikram 
52096448Svikram 	(void) snprintf(cmd, sizeof (cmd),
52106448Svikram 	    "/sbin/zfs get -Ho value mounted %s",
52116448Svikram 	    pool);
52126448Svikram 
52136448Svikram 	ret = exec_cmd(cmd, &flist);
52146448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
52156448Svikram 	if (ret != 0) {
52166448Svikram 		bam_error(ZFS_MNTED_FAILED, pool);
52176448Svikram 		return (NULL);
52186448Svikram 	}
52196448Svikram 
52206448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
52216448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
52226448Svikram 		bam_error(BAD_ZFS_MNTED, pool);
52236448Svikram 		filelist_free(&flist);
52246448Svikram 		return (NULL);
52256448Svikram 	}
52266448Svikram 
52276448Svikram 	is_mounted = strtok(flist.head->line, " \t\n");
52286448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
52296448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
52306448Svikram 	if (strcmp(is_mounted, "no") != 0) {
52316448Svikram 		filelist_free(&flist);
52326448Svikram 		*mnted = ZFS_ALREADY;
52336448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
52346448Svikram 		goto mounted;
52356448Svikram 	}
52366448Svikram 
52376448Svikram 	filelist_free(&flist);
52386448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
52396448Svikram 
52406448Svikram 	/* top dataset is not mounted. Mount it now */
52416448Svikram 	(void) snprintf(cmd, sizeof (cmd),
52426448Svikram 	    "/sbin/zfs mount %s", pool);
52436448Svikram 	ret = exec_cmd(cmd, NULL);
52446448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
52456448Svikram 	if (ret != 0) {
52466448Svikram 		bam_error(ZFS_MOUNT_FAILED, pool);
52476448Svikram 		return (NULL);
52486448Svikram 	}
52496448Svikram 	*mnted = ZFS_MOUNTED;
52506448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
52516448Svikram 	/*FALLTHRU*/
52526448Svikram mounted:
52536448Svikram 	/*
52546448Svikram 	 * Now get the mountpoint
52556448Svikram 	 */
52566448Svikram 	(void) snprintf(cmd, sizeof (cmd),
52576448Svikram 	    "/sbin/zfs get -Ho value mountpoint %s",
52586448Svikram 	    pool);
52596448Svikram 
52606448Svikram 	ret = exec_cmd(cmd, &flist);
52616448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
52626448Svikram 	if (ret != 0) {
52636448Svikram 		bam_error(ZFS_MNTPT_FAILED, pool);
52646448Svikram 		goto error;
52656448Svikram 	}
52666448Svikram 
52676448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
52686448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
52696448Svikram 		bam_error(NULL_ZFS_MNTPT, pool);
52706448Svikram 		goto error;
52716448Svikram 	}
52726448Svikram 
52736448Svikram 	mntpt = strtok(flist.head->line, " \t\n");
52746448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
52756448Svikram 	if (*mntpt != '/') {
52766448Svikram 		bam_error(BAD_ZFS_MNTPT, pool, mntpt);
52776448Svikram 		goto error;
52786448Svikram 	}
52796448Svikram 	zmntpt = s_strdup(mntpt);
52806448Svikram 
52816448Svikram 	filelist_free(&flist);
52826448Svikram 
52836448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
52846448Svikram 
52856448Svikram 	return (zmntpt);
52866448Svikram 
52876448Svikram error:
52886448Svikram 	filelist_free(&flist);
52896448Svikram 	(void) umount_top_dataset(pool, *mnted, NULL);
52906448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
52916448Svikram 	return (NULL);
52926448Svikram }
52936448Svikram 
52946448Svikram static int
52956448Svikram umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
52966448Svikram {
52976448Svikram 	char		cmd[PATH_MAX];
52986448Svikram 	int		ret;
52996448Svikram 	const char	*fcn = "umount_top_dataset()";
53006448Svikram 
53016448Svikram 	INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
53026448Svikram 	switch (mnted) {
53036448Svikram 	case LEGACY_ALREADY:
53046448Svikram 	case ZFS_ALREADY:
53056448Svikram 		/* nothing to do */
53066448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
53076448Svikram 		    mntpt ? mntpt : "NULL"));
53086448Svikram 		free(mntpt);
53096448Svikram 		return (BAM_SUCCESS);
53106448Svikram 	case LEGACY_MOUNTED:
53116448Svikram 		(void) snprintf(cmd, sizeof (cmd),
53126448Svikram 		    "/sbin/umount %s", pool);
53136448Svikram 		ret = exec_cmd(cmd, NULL);
53146448Svikram 		INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
53156448Svikram 		if (ret != 0) {
53166448Svikram 			bam_error(UMOUNT_FAILED, pool);
53176448Svikram 			free(mntpt);
53186448Svikram 			return (BAM_ERROR);
53196448Svikram 		}
53206448Svikram 		if (mntpt)
53216448Svikram 			(void) rmdir(mntpt);
53226448Svikram 		free(mntpt);
53236448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
53246448Svikram 		return (BAM_SUCCESS);
53256448Svikram 	case ZFS_MOUNTED:
53266448Svikram 		free(mntpt);
53276448Svikram 		(void) snprintf(cmd, sizeof (cmd),
53286448Svikram 		    "/sbin/zfs unmount %s", pool);
53296448Svikram 		ret = exec_cmd(cmd, NULL);
53306448Svikram 		INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
53316448Svikram 		if (ret != 0) {
53326448Svikram 			bam_error(UMOUNT_FAILED, pool);
53336448Svikram 			return (BAM_ERROR);
53346448Svikram 		}
53356448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
53366448Svikram 		return (BAM_SUCCESS);
53376448Svikram 	default:
53386448Svikram 		bam_error(INT_BAD_MNTSTATE, pool);
53396448Svikram 		return (BAM_ERROR);
53406448Svikram 	}
53416448Svikram 	/*NOTREACHED*/
53426448Svikram }
53436448Svikram 
53446448Svikram /*
53456448Svikram  * For ZFS, osdev can be one of two forms
53466448Svikram  * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
53476448Svikram  * It can be a /dev/[r]dsk special file. We handle both instances
53486448Svikram  */
53496448Svikram static char *
53506448Svikram get_pool(char *osdev)
53516448Svikram {
53526448Svikram 	char		cmd[PATH_MAX];
53536448Svikram 	char		buf[PATH_MAX];
53546448Svikram 	filelist_t	flist = {0};
53556448Svikram 	char		*pool;
53566448Svikram 	char		*cp;
53576448Svikram 	char		*slash;
53586448Svikram 	int		ret;
53596448Svikram 	const char	*fcn = "get_pool()";
53606448Svikram 
53616448Svikram 	INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
53626448Svikram 	if (osdev == NULL) {
53636448Svikram 		bam_error(GET_POOL_OSDEV_NULL);
53646448Svikram 		return (NULL);
53656448Svikram 	}
53666448Svikram 
53676448Svikram 	BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
53686448Svikram 
53696448Svikram 	if (osdev[0] != '/') {
53706448Svikram 		(void) strlcpy(buf, osdev, sizeof (buf));
53716448Svikram 		slash = strchr(buf, '/');
53726448Svikram 		if (slash)
53736448Svikram 			*slash = '\0';
53746448Svikram 		pool = s_strdup(buf);
53756448Svikram 		BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
53766448Svikram 		return (pool);
53776448Svikram 	} else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
53786448Svikram 	    strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
53796448Svikram 		bam_error(GET_POOL_BAD_OSDEV, osdev);
53806448Svikram 		return (NULL);
53816448Svikram 	}
53826448Svikram 
53836448Svikram 	(void) snprintf(cmd, sizeof (cmd),
53846448Svikram 	    "/usr/sbin/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
53856448Svikram 	    osdev);
53866448Svikram 
53876448Svikram 	ret = exec_cmd(cmd, &flist);
53886448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
53896448Svikram 	if (ret != 0) {
53906448Svikram 		bam_error(FSTYP_A_FAILED, osdev);
53916448Svikram 		return (NULL);
53926448Svikram 	}
53936448Svikram 
53946448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
53956448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
53966448Svikram 		bam_error(NULL_FSTYP_A, osdev);
53976448Svikram 		filelist_free(&flist);
53986448Svikram 		return (NULL);
53996448Svikram 	}
54006448Svikram 
54016448Svikram 	(void) strtok(flist.head->line, "'");
54026448Svikram 	cp = strtok(NULL, "'");
54036448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
54046448Svikram 	if (cp == NULL) {
54056448Svikram 		bam_error(BAD_FSTYP_A, osdev);
54066448Svikram 		filelist_free(&flist);
54076448Svikram 		return (NULL);
54086448Svikram 	}
54096448Svikram 
54106448Svikram 	pool = s_strdup(cp);
54116448Svikram 
54126448Svikram 	filelist_free(&flist);
54136448Svikram 
54146448Svikram 	BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
54156448Svikram 
54166448Svikram 	return (pool);
54176448Svikram }
54186448Svikram 
54196448Svikram static char *
54206448Svikram find_zfs_existing(char *osdev)
54216448Svikram {
54226448Svikram 	char		*pool;
54236448Svikram 	zfs_mnted_t	mnted;
54246448Svikram 	char		*mntpt;
54256448Svikram 	char		*sign;
54266448Svikram 	const char	*fcn = "find_zfs_existing()";
54276448Svikram 
54286448Svikram 	pool = get_pool(osdev);
54296448Svikram 	INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
54306448Svikram 	if (pool == NULL) {
54316448Svikram 		bam_error(ZFS_GET_POOL_FAILED, osdev);
54326448Svikram 		return (NULL);
54336448Svikram 	}
54346448Svikram 
54356448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
54366448Svikram 	INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
54376448Svikram 	if (mntpt == NULL) {
54386448Svikram 		bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
54396448Svikram 		free(pool);
54406448Svikram 		return (NULL);
54416448Svikram 	}
54426448Svikram 
54436448Svikram 	sign = find_primary_common(mntpt, "zfs");
54446448Svikram 	if (sign == NULL) {
54456448Svikram 		sign = find_backup_common(mntpt, "zfs");
54466448Svikram 		BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
54476448Svikram 	} else {
54486448Svikram 		BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
54496448Svikram 	}
54506448Svikram 
54516448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
54526448Svikram 
54536448Svikram 	free(pool);
54546448Svikram 
54556448Svikram 	return (sign);
54566448Svikram }
54576448Svikram 
54586448Svikram static char *
54596448Svikram find_existing_sign(char *osroot, char *osdev, char *fstype)
54606448Svikram {
54616448Svikram 	const char		*fcn = "find_existing_sign()";
54626448Svikram 
54636448Svikram 	INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
54646448Svikram 	if (strcmp(fstype, "ufs") == 0) {
54656448Svikram 		BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
54666448Svikram 		return (find_ufs_existing(osroot));
54676448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
54686448Svikram 		BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
54696448Svikram 		return (find_zfs_existing(osdev));
54706448Svikram 	} else {
54716448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
54726448Svikram 		return (NULL);
54736448Svikram 	}
54746448Svikram }
54756448Svikram 
54766448Svikram #define	MH_HASH_SZ	16
54776448Svikram 
54786448Svikram typedef enum {
54796448Svikram 	MH_ERROR = -1,
54806448Svikram 	MH_NOMATCH,
54816448Svikram 	MH_MATCH
54826448Svikram } mh_search_t;
54836448Svikram 
54846448Svikram typedef struct mcache {
54856448Svikram 	char	*mc_special;
54866448Svikram 	char	*mc_mntpt;
54876448Svikram 	char	*mc_fstype;
54886448Svikram 	struct mcache *mc_next;
54896448Svikram } mcache_t;
54906448Svikram 
54916448Svikram typedef struct mhash {
54926448Svikram 	mcache_t *mh_hash[MH_HASH_SZ];
54936448Svikram } mhash_t;
54946448Svikram 
54956448Svikram static int
54966448Svikram mhash_fcn(char *key)
54976448Svikram {
54986448Svikram 	int		i;
54996448Svikram 	uint64_t	sum = 0;
55006448Svikram 
55016448Svikram 	for (i = 0; key[i] != '\0'; i++) {
55026448Svikram 		sum += (uchar_t)key[i];
55036448Svikram 	}
55046448Svikram 
55056448Svikram 	sum %= MH_HASH_SZ;
55066448Svikram 
55076448Svikram 	assert(sum < MH_HASH_SZ);
55086448Svikram 
55096448Svikram 	return (sum);
55106448Svikram }
55116448Svikram 
55126448Svikram static mhash_t *
55136448Svikram cache_mnttab(void)
55146448Svikram {
55156448Svikram 	FILE		*mfp;
55166448Svikram 	struct extmnttab mnt;
55176448Svikram 	mcache_t	*mcp;
55186448Svikram 	mhash_t		*mhp;
55196448Svikram 	char		*ctds;
55206448Svikram 	int		idx;
55216448Svikram 	int		error;
55226448Svikram 	char		*special_dup;
55236448Svikram 	const char	*fcn = "cache_mnttab()";
55246448Svikram 
55256448Svikram 	mfp = fopen(MNTTAB, "r");
55266448Svikram 	error = errno;
55276448Svikram 	INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
55286448Svikram 	if (mfp == NULL) {
55296448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
55306448Svikram 		return (NULL);
55316448Svikram 	}
55326448Svikram 
55336448Svikram 	mhp = s_calloc(1, sizeof (mhash_t));
55346448Svikram 
55356448Svikram 	resetmnttab(mfp);
55366448Svikram 
55376448Svikram 	while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
55386448Svikram 		/* only cache ufs */
55396448Svikram 		if (strcmp(mnt.mnt_fstype, "ufs") != 0)
55406448Svikram 			continue;
55416448Svikram 
55426448Svikram 		/* basename() modifies its arg, so dup it */
55436448Svikram 		special_dup = s_strdup(mnt.mnt_special);
55446448Svikram 		ctds = basename(special_dup);
55456448Svikram 
55466448Svikram 		mcp = s_calloc(1, sizeof (mcache_t));
55476448Svikram 		mcp->mc_special = s_strdup(ctds);
55486448Svikram 		mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
55496448Svikram 		mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
55506448Svikram 		BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
55516448Svikram 		    mnt.mnt_mountp, mnt.mnt_fstype));
55526448Svikram 		idx = mhash_fcn(ctds);
55536448Svikram 		mcp->mc_next = mhp->mh_hash[idx];
55546448Svikram 		mhp->mh_hash[idx] = mcp;
55556448Svikram 		free(special_dup);
55566448Svikram 	}
55576448Svikram 
55586448Svikram 	(void) fclose(mfp);
55596448Svikram 
55606448Svikram 	return (mhp);
55616448Svikram }
55626448Svikram 
55636448Svikram static void
55646448Svikram free_mnttab(mhash_t *mhp)
55656448Svikram {
55666448Svikram 	mcache_t	*mcp;
55676448Svikram 	int		i;
55686448Svikram 
55696448Svikram 	for (i = 0; i < MH_HASH_SZ; i++) {
55706448Svikram 		/*LINTED*/
55716448Svikram 		while (mcp = mhp->mh_hash[i]) {
55726448Svikram 			mhp->mh_hash[i] = mcp->mc_next;
55736448Svikram 			free(mcp->mc_special);
55746448Svikram 			free(mcp->mc_mntpt);
55756448Svikram 			free(mcp->mc_fstype);
55766448Svikram 			free(mcp);
55776448Svikram 		}
55786448Svikram 	}
55796448Svikram 
55806448Svikram 	for (i = 0; i < MH_HASH_SZ; i++) {
55816448Svikram 		assert(mhp->mh_hash[i] == NULL);
55826448Svikram 	}
55836448Svikram 	free(mhp);
55846448Svikram }
55856448Svikram 
55866448Svikram static mh_search_t
55876448Svikram search_hash(mhash_t *mhp, char *special, char **mntpt)
55886448Svikram {
55896448Svikram 	int		idx;
55906448Svikram 	mcache_t	*mcp;
55916448Svikram 	const char 	*fcn = "search_hash()";
55926448Svikram 
55936448Svikram 	assert(mntpt);
55946448Svikram 
55956448Svikram 	*mntpt = NULL;
55966448Svikram 
55976448Svikram 	INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
55986448Svikram 	if (strchr(special, '/')) {
55996448Svikram 		bam_error(INVALID_MHASH_KEY, special);
56006448Svikram 		return (MH_ERROR);
56016448Svikram 	}
56026448Svikram 
56036448Svikram 	idx = mhash_fcn(special);
56046448Svikram 
56056448Svikram 	for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
56066448Svikram 		if (strcmp(mcp->mc_special, special) == 0)
56076448Svikram 			break;
56086448Svikram 	}
56096448Svikram 
56106448Svikram 	if (mcp == NULL) {
56116448Svikram 		BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
56126448Svikram 		return (MH_NOMATCH);
56136448Svikram 	}
56146448Svikram 
56156448Svikram 	assert(strcmp(mcp->mc_fstype, "ufs") == 0);
56166448Svikram 	*mntpt = mcp->mc_mntpt;
56176448Svikram 	BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
56186448Svikram 	return (MH_MATCH);
56196448Svikram }
56206448Svikram 
56216448Svikram static int
56226448Svikram check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
56236448Svikram {
56246448Svikram 	char		*sign;
56256448Svikram 	char		*signline;
56266448Svikram 	char		signbuf[MAXNAMELEN];
56276448Svikram 	int		len;
56286448Svikram 	int		error;
56296448Svikram 	const char	*fcn = "check_add_ufs_sign_to_list()";
56306448Svikram 
56316448Svikram 	/* safe to specify NULL as "osdev" arg for UFS */
56326448Svikram 	sign = find_existing_sign(mntpt, NULL, "ufs");
56336448Svikram 	if (sign == NULL) {
56346448Svikram 		/* No existing signature, nothing to add to list */
56356448Svikram 		BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
56366448Svikram 		return (0);
56376448Svikram 	}
56386448Svikram 
56396448Svikram 	(void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
56406448Svikram 	signline = signbuf;
56416448Svikram 
56426448Svikram 	INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
56436448Svikram 	if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
56446448Svikram 	    strlen(GRUBSIGN_UFS_PREFIX))) {
56456448Svikram 		bam_error(INVALID_UFS_SIGNATURE, sign);
56466448Svikram 		free(sign);
56476448Svikram 		/* ignore invalid signatures */
56486448Svikram 		return (0);
56496448Svikram 	}
56506448Svikram 
56516448Svikram 	len = fputs(signline, tfp);
56526448Svikram 	error = errno;
56536448Svikram 	INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
56546448Svikram 	if (len != strlen(signline)) {
56556448Svikram 		bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
56566448Svikram 		free(sign);
56576448Svikram 		return (-1);
56586448Svikram 	}
56596448Svikram 
56606448Svikram 	free(sign);
56616448Svikram 
56626448Svikram 	BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
56636448Svikram 	return (0);
56646448Svikram }
56656448Svikram 
56666448Svikram /*
56676448Svikram  * slice is a basename not a full pathname
56686448Svikram  */
56696448Svikram static int
56706448Svikram process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
56716448Svikram {
56726448Svikram 	int		ret;
56736448Svikram 	char		cmd[PATH_MAX];
56746448Svikram 	char		path[PATH_MAX];
56756448Svikram 	struct stat	sbuf;
56766448Svikram 	char		*mntpt;
56776448Svikram 	filelist_t	flist = {0};
56786448Svikram 	char		*fstype;
56796448Svikram 	char		blkslice[PATH_MAX];
56806448Svikram 	const char	*fcn = "process_slice_common()";
56816448Svikram 
56826448Svikram 
56836448Svikram 	ret = search_hash(mhp, slice, &mntpt);
56846448Svikram 	switch (ret) {
56856448Svikram 		case MH_MATCH:
56866448Svikram 			if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
56876448Svikram 				return (-1);
56886448Svikram 			else
56896448Svikram 				return (0);
56906448Svikram 		case MH_NOMATCH:
56916448Svikram 			break;
56926448Svikram 		case MH_ERROR:
56936448Svikram 		default:
56946448Svikram 			return (-1);
56956448Svikram 	}
56966448Svikram 
56976448Svikram 	(void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
56986448Svikram 	if (stat(path, &sbuf) == -1) {
56996448Svikram 		BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
57006448Svikram 		return (0);
57016448Svikram 	}
57026448Svikram 
57036448Svikram 	/* Check if ufs */
57046448Svikram 	(void) snprintf(cmd, sizeof (cmd),
57056448Svikram 	    "/usr/sbin/fstyp /dev/rdsk/%s 2>/dev/null",
57066448Svikram 	    slice);
57076448Svikram 
57086448Svikram 	if (exec_cmd(cmd, &flist) != 0) {
57096448Svikram 		if (bam_verbose)
57106448Svikram 			bam_print(FSTYP_FAILED, slice);
57116448Svikram 		return (0);
57126448Svikram 	}
57136448Svikram 
57146448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
57156448Svikram 		if (bam_verbose)
57166448Svikram 			bam_print(FSTYP_BAD, slice);
57176448Svikram 		filelist_free(&flist);
57186448Svikram 		return (0);
57196448Svikram 	}
57206448Svikram 
57216448Svikram 	fstype = strtok(flist.head->line, " \t\n");
57226448Svikram 	if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
57236448Svikram 		if (bam_verbose)
57246448Svikram 			bam_print(NOT_UFS_SLICE, slice, fstype);
57256448Svikram 		filelist_free(&flist);
57266448Svikram 		return (0);
57276448Svikram 	}
57286448Svikram 
57296448Svikram 	filelist_free(&flist);
57306448Svikram 
57316448Svikram 	/*
57326448Svikram 	 * Since we are mounting the filesystem read-only, the
57336448Svikram 	 * the last mount field of the superblock is unchanged
57346448Svikram 	 * and does not need to be fixed up post-mount;
57356448Svikram 	 */
57366448Svikram 
57376448Svikram 	(void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
57386448Svikram 	    slice);
57396448Svikram 
57406448Svikram 	(void) snprintf(cmd, sizeof (cmd),
57416448Svikram 	    "/usr/sbin/mount -F ufs -o ro %s %s "
57426448Svikram 	    "> /dev/null 2>&1", blkslice, tmpmnt);
57436448Svikram 
57446448Svikram 	if (exec_cmd(cmd, NULL) != 0) {
57456448Svikram 		if (bam_verbose)
57466448Svikram 			bam_print(MOUNT_FAILED, blkslice, "ufs");
57476448Svikram 		return (0);
57486448Svikram 	}
57496448Svikram 
57506448Svikram 	ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
57516448Svikram 
57526448Svikram 	(void) snprintf(cmd, sizeof (cmd),
57536448Svikram 	    "/usr/sbin/umount -f %s > /dev/null 2>&1",
57546448Svikram 	    tmpmnt);
57556448Svikram 
57566448Svikram 	if (exec_cmd(cmd, NULL) != 0) {
57576448Svikram 		bam_print(UMOUNT_FAILED, slice);
57586448Svikram 		return (0);
57596448Svikram 	}
57606448Svikram 
57616448Svikram 	return (ret);
57626448Svikram }
57636448Svikram 
57646448Svikram static int
57656448Svikram process_vtoc_slices(
57666448Svikram 	char *s0,
57676448Svikram 	struct vtoc *vtoc,
57686448Svikram 	FILE *tfp,
57696448Svikram 	mhash_t *mhp,
57706448Svikram 	char *tmpmnt)
57716448Svikram {
57726448Svikram 	int		idx;
57736448Svikram 	char		slice[PATH_MAX];
57746448Svikram 	size_t		len;
57756448Svikram 	char		*cp;
57766448Svikram 	const char	*fcn = "process_vtoc_slices()";
57776448Svikram 
57786448Svikram 	len = strlen(s0);
57796448Svikram 
57806448Svikram 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
57816448Svikram 
57826448Svikram 	s0[len - 1] = '\0';
57836448Svikram 
57846448Svikram 	(void) strlcpy(slice, s0, sizeof (slice));
57856448Svikram 
57866448Svikram 	s0[len - 1] = '0';
57876448Svikram 
57886448Svikram 	cp = slice + len - 1;
57896448Svikram 
57906448Svikram 	for (idx = 0; idx < vtoc->v_nparts; idx++) {
57916448Svikram 
57926448Svikram 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
57936448Svikram 
57946448Svikram 		if (vtoc->v_part[idx].p_size == 0) {
57956448Svikram 			BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
57966448Svikram 			continue;
57976448Svikram 		}
57986448Svikram 
57996448Svikram 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
58006448Svikram 		switch (vtoc->v_part[idx].p_tag) {
58016448Svikram 		case V_SWAP:
58026448Svikram 		case V_USR:
58036448Svikram 		case V_BACKUP:
58046448Svikram 		case V_VAR:
58056448Svikram 		case V_HOME:
58066448Svikram 		case V_ALTSCTR:
58076448Svikram 			BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
58086448Svikram 			continue;
58096448Svikram 		default:
58106448Svikram 			BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
58116448Svikram 			break;
58126448Svikram 		}
58136448Svikram 
58146448Svikram 		/* skip unmountable and readonly slices */
58156448Svikram 		switch (vtoc->v_part[idx].p_flag) {
58166448Svikram 		case V_UNMNT:
58176448Svikram 		case V_RONLY:
58186448Svikram 			BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
58196448Svikram 			continue;
58206448Svikram 		default:
58216448Svikram 			BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
58226448Svikram 			break;
58236448Svikram 		}
58246448Svikram 
58256448Svikram 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
58266448Svikram 			return (-1);
58276448Svikram 		}
58286448Svikram 	}
58296448Svikram 
58306448Svikram 	return (0);
58316448Svikram }
58326448Svikram 
58336448Svikram static int
58346448Svikram process_efi_slices(
58356448Svikram 	char *s0,
58366448Svikram 	struct dk_gpt *efi,
58376448Svikram 	FILE *tfp,
58386448Svikram 	mhash_t *mhp,
58396448Svikram 	char *tmpmnt)
58406448Svikram {
58416448Svikram 	int		idx;
58426448Svikram 	char		slice[PATH_MAX];
58436448Svikram 	size_t		len;
58446448Svikram 	char		*cp;
58456448Svikram 	const char	*fcn = "process_efi_slices()";
58466448Svikram 
58476448Svikram 	len = strlen(s0);
58486448Svikram 
58496448Svikram 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
58506448Svikram 
58516448Svikram 	s0[len - 1] = '\0';
58526448Svikram 
58536448Svikram 	(void) strlcpy(slice, s0, sizeof (slice));
58546448Svikram 
58556448Svikram 	s0[len - 1] = '0';
58566448Svikram 
58576448Svikram 	cp = slice + len - 1;
58586448Svikram 
58596448Svikram 	for (idx = 0; idx < efi->efi_nparts; idx++) {
58606448Svikram 
58616448Svikram 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
58626448Svikram 
58636448Svikram 		if (efi->efi_parts[idx].p_size == 0) {
58646448Svikram 			BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
58656448Svikram 			continue;
58666448Svikram 		}
58676448Svikram 
58686448Svikram 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
58696448Svikram 		switch (efi->efi_parts[idx].p_tag) {
58706448Svikram 		case V_SWAP:
58716448Svikram 		case V_USR:
58726448Svikram 		case V_BACKUP:
58736448Svikram 		case V_VAR:
58746448Svikram 		case V_HOME:
58756448Svikram 		case V_ALTSCTR:
58766448Svikram 			BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
58776448Svikram 			continue;
58786448Svikram 		default:
58796448Svikram 			BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
58806448Svikram 			break;
58816448Svikram 		}
58826448Svikram 
58836448Svikram 		/* skip unmountable and readonly slices */
58846448Svikram 		switch (efi->efi_parts[idx].p_flag) {
58856448Svikram 		case V_UNMNT:
58866448Svikram 		case V_RONLY:
58876448Svikram 			BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
58886448Svikram 			continue;
58896448Svikram 		default:
58906448Svikram 			BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
58916448Svikram 			break;
58926448Svikram 		}
58936448Svikram 
58946448Svikram 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
58956448Svikram 			return (-1);
58966448Svikram 		}
58976448Svikram 	}
58986448Svikram 
58996448Svikram 	return (0);
59006448Svikram }
59016448Svikram 
59026448Svikram /*
59036448Svikram  * s0 is a basename not a full path
59046448Svikram  */
59056448Svikram static int
59066448Svikram process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
59076448Svikram {
59086448Svikram 	struct vtoc		vtoc;
59096448Svikram 	struct dk_gpt		*efi;
59106448Svikram 	char			s0path[PATH_MAX];
59116448Svikram 	struct stat		sbuf;
59126448Svikram 	int			e_flag;
59136448Svikram 	int			v_flag;
59146448Svikram 	int			retval;
59156448Svikram 	int			err;
59166448Svikram 	int			fd;
59176448Svikram 	const char		*fcn = "process_slice0()";
59186448Svikram 
59196448Svikram 	(void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
59206448Svikram 
59216448Svikram 	if (stat(s0path, &sbuf) == -1) {
59226448Svikram 		BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
59236448Svikram 		return (0);
59246448Svikram 	}
59256448Svikram 
59266448Svikram 	fd = open(s0path, O_NONBLOCK|O_RDONLY);
59276448Svikram 	if (fd == -1) {
59286448Svikram 		bam_error(OPEN_FAIL, s0path, strerror(errno));
59296448Svikram 		return (0);
59306448Svikram 	}
59316448Svikram 
59326448Svikram 	e_flag = v_flag = 0;
59336448Svikram 	retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
59346448Svikram 	switch (retval) {
59356448Svikram 		case VT_EIO:
59366448Svikram 			BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
59376448Svikram 			break;
59386448Svikram 		case VT_EINVAL:
59396448Svikram 			BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
59406448Svikram 			break;
59416448Svikram 		case VT_ERROR:
59426448Svikram 			BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
59436448Svikram 			break;
59446448Svikram 		case VT_ENOTSUP:
59456448Svikram 			e_flag = 1;
59466448Svikram 			BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
59476448Svikram 			break;
59486448Svikram 		case 0:
59496448Svikram 			v_flag = 1;
59506448Svikram 			BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
59516448Svikram 			break;
59526448Svikram 		default:
59536448Svikram 			BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
59546448Svikram 			break;
59556448Svikram 	}
59566448Svikram 
59576448Svikram 
59586448Svikram 	if (e_flag) {
59596448Svikram 		e_flag = 0;
59606448Svikram 		retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
59616448Svikram 		switch (retval) {
59626448Svikram 		case VT_EIO:
59636448Svikram 			BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
59646448Svikram 			break;
59656448Svikram 		case VT_EINVAL:
59666448Svikram 			BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
59676448Svikram 			break;
59686448Svikram 		case VT_ERROR:
59696448Svikram 			BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
59706448Svikram 			break;
59716448Svikram 		case VT_ENOTSUP:
59726448Svikram 			BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
59736448Svikram 			break;
59746448Svikram 		case 0:
59756448Svikram 			e_flag = 1;
59766448Svikram 			BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
59776448Svikram 			break;
59786448Svikram 		default:
59796448Svikram 			BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
59806448Svikram 			break;
59816448Svikram 		}
59826448Svikram 	}
59836448Svikram 
59846448Svikram 	(void) close(fd);
59856448Svikram 
59866448Svikram 	if (v_flag) {
59876448Svikram 		retval = process_vtoc_slices(s0,
59886448Svikram 		    &vtoc, tfp, mhp, tmpmnt);
59896448Svikram 	} else if (e_flag) {
59906448Svikram 		retval = process_efi_slices(s0,
59916448Svikram 		    efi, tfp, mhp, tmpmnt);
59926448Svikram 	} else {
59936448Svikram 		BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
59946448Svikram 		return (0);
59956448Svikram 	}
59966448Svikram 
59976448Svikram 	return (retval);
59986448Svikram }
59996448Svikram 
60006448Svikram /*
60016448Svikram  * Find and create a list of all existing UFS boot signatures
60026448Svikram  */
60036448Svikram static int
60046448Svikram FindAllUfsSignatures(void)
60056448Svikram {
60066448Svikram 	mhash_t		*mnttab_hash;
60076448Svikram 	DIR		*dirp = NULL;
60086448Svikram 	struct dirent	*dp;
60096448Svikram 	char		tmpmnt[PATH_MAX];
60106448Svikram 	char		cmd[PATH_MAX];
60116448Svikram 	struct stat	sb;
60126448Svikram 	int		fd;
60136448Svikram 	FILE		*tfp;
60146448Svikram 	size_t		len;
60156448Svikram 	int		ret;
60166448Svikram 	int		error;
60176448Svikram 	const char	*fcn = "FindAllUfsSignatures()";
60186448Svikram 
60196448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) != -1)  {
60206448Svikram 		bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
60216448Svikram 		return (0);
60226448Svikram 	}
60236448Svikram 
60246448Svikram 	fd = open(UFS_SIGNATURE_LIST".tmp",
60256448Svikram 	    O_RDWR|O_CREAT|O_TRUNC, 0644);
60266448Svikram 	error = errno;
60276448Svikram 	INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
60286448Svikram 	if (fd == -1) {
60296448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
60306448Svikram 		return (-1);
60316448Svikram 	}
60326448Svikram 
60336448Svikram 	ret = close(fd);
60346448Svikram 	error = errno;
60356448Svikram 	INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
60366448Svikram 	if (ret == -1) {
60376448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
60386448Svikram 		    strerror(error));
60396448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
60406448Svikram 		return (-1);
60416448Svikram 	}
60426448Svikram 
60436448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
60446448Svikram 	error = errno;
60456448Svikram 	INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
60466448Svikram 	if (tfp == NULL) {
60476448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
60486448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
60496448Svikram 		return (-1);
60506448Svikram 	}
60516448Svikram 
60526448Svikram 	mnttab_hash = cache_mnttab();
60536448Svikram 	INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
60546448Svikram 	if (mnttab_hash == NULL) {
60556448Svikram 		(void) fclose(tfp);
60566448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
60576448Svikram 		bam_error(CACHE_MNTTAB_FAIL, fcn);
60586448Svikram 		return (-1);
60596448Svikram 	}
60606448Svikram 
60616448Svikram 	(void) snprintf(tmpmnt, sizeof (tmpmnt),
60626448Svikram 	    "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
60636448Svikram 	(void) unlink(tmpmnt);
60646448Svikram 
60658735SEnrico.Perla@Sun.COM 	ret = mkdirp(tmpmnt, DIR_PERMS);
60666448Svikram 	error = errno;
60676448Svikram 	INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
60686448Svikram 	if (ret == -1) {
60696448Svikram 		bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
60706448Svikram 		free_mnttab(mnttab_hash);
60716448Svikram 		(void) fclose(tfp);
60726448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
60736448Svikram 		return (-1);
60746448Svikram 	}
60756448Svikram 
60766448Svikram 	dirp = opendir("/dev/rdsk");
60776448Svikram 	error = errno;
60786448Svikram 	INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
60796448Svikram 	if (dirp == NULL) {
60806448Svikram 		bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
60816448Svikram 		goto fail;
60826448Svikram 	}
60836448Svikram 
60846448Svikram 	while (dp = readdir(dirp)) {
60856448Svikram 		if (strcmp(dp->d_name, ".") == 0 ||
60866448Svikram 		    strcmp(dp->d_name, "..") == 0)
60876448Svikram 			continue;
60886448Svikram 
60896448Svikram 		/*
60906448Svikram 		 * we only look for the s0 slice. This is guranteed to
60916448Svikram 		 * have 's' at len - 2.
60926448Svikram 		 */
60936448Svikram 		len = strlen(dp->d_name);
60946448Svikram 		if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
60956448Svikram 			BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
60966448Svikram 			continue;
60976448Svikram 		}
60986448Svikram 
60996448Svikram 		ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
61006448Svikram 		INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
61016448Svikram 		if (ret == -1)
61026448Svikram 			goto fail;
61036448Svikram 	}
61046448Svikram 
61056448Svikram 	(void) closedir(dirp);
61066448Svikram 	free_mnttab(mnttab_hash);
61076448Svikram 	(void) rmdir(tmpmnt);
61086448Svikram 
61096448Svikram 	ret = fclose(tfp);
61106448Svikram 	error = errno;
61116448Svikram 	INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
61126448Svikram 	if (ret == EOF) {
61136448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
61146448Svikram 		    strerror(error));
61156448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
61166448Svikram 		return (-1);
61176448Svikram 	}
61186448Svikram 
61196448Svikram 	/* We have a list of existing GRUB signatures. Sort it first */
61206448Svikram 	(void) snprintf(cmd, sizeof (cmd),
61216448Svikram 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
61226448Svikram 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
61236448Svikram 
61246448Svikram 	ret = exec_cmd(cmd, NULL);
61256448Svikram 	INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
61266448Svikram 	if (ret != 0) {
61276448Svikram 		bam_error(GRUBSIGN_SORT_FAILED);
61286448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
61296448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
61306448Svikram 		return (-1);
61316448Svikram 	}
61326448Svikram 
61336448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
61346448Svikram 
61356448Svikram 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
61366448Svikram 	error = errno;
61376448Svikram 	INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
61386448Svikram 	if (ret == -1) {
61396448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
61406448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
61416448Svikram 		return (-1);
61426448Svikram 	}
61436448Svikram 
61446448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
61456448Svikram 		BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
61466448Svikram 	}
61476448Svikram 
61486448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
61496448Svikram 	return (0);
61506448Svikram 
61516448Svikram fail:
61526448Svikram 	if (dirp)
61536448Svikram 		(void) closedir(dirp);
61546448Svikram 	free_mnttab(mnttab_hash);
61556448Svikram 	(void) rmdir(tmpmnt);
61566448Svikram 	(void) fclose(tfp);
61576448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
61586448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
61596448Svikram 	return (-1);
61606448Svikram }
61616448Svikram 
61626448Svikram static char *
61636448Svikram create_ufs_sign(void)
61646448Svikram {
61656448Svikram 	struct stat	sb;
61666448Svikram 	int		signnum = -1;
61676448Svikram 	char		tmpsign[MAXNAMELEN + 1];
61686448Svikram 	char		*numstr;
61696448Svikram 	int		i;
61706448Svikram 	FILE		*tfp;
61716448Svikram 	int		ret;
61726448Svikram 	int		error;
61736448Svikram 	const char	*fcn = "create_ufs_sign()";
61746448Svikram 
61756448Svikram 	bam_print(SEARCHING_UFS_SIGN);
61766448Svikram 
61776448Svikram 	ret = FindAllUfsSignatures();
61786448Svikram 	INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
61796448Svikram 	if (ret == -1) {
61806448Svikram 		bam_error(ERR_FIND_UFS_SIGN);
61816448Svikram 		return (NULL);
61826448Svikram 	}
61836448Svikram 
61846448Svikram 	/* Make sure the list exists and is owned by root */
61856448Svikram 	INJECT_ERROR1("SIGNLIST_NOT_CREATED",
61866448Svikram 	    (void) unlink(UFS_SIGNATURE_LIST));
61876448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
61886448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
61896448Svikram 		bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
61906448Svikram 		return (NULL);
61916448Svikram 	}
61926448Svikram 
61936448Svikram 	if (sb.st_size == 0) {
61946448Svikram 		bam_print(GRUBSIGN_UFS_NONE);
61956448Svikram 		i = 0;
61966448Svikram 		goto found;
61976448Svikram 	}
61986448Svikram 
61996448Svikram 	/* The signature list was sorted when it was created */
62006448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST, "r");
62016448Svikram 	error = errno;
62026448Svikram 	INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
62036448Svikram 	if (tfp == NULL) {
62046448Svikram 		bam_error(UFS_SIGNATURE_LIST_OPENERR,
62056448Svikram 		    UFS_SIGNATURE_LIST, strerror(error));
62066448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
62076448Svikram 		return (NULL);
62086448Svikram 	}
62096448Svikram 
62106448Svikram 	for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
62116448Svikram 
62126448Svikram 		if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
62136448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
62146448Svikram 			(void) fclose(tfp);
62156448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
62166448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
62176448Svikram 			return (NULL);
62186448Svikram 		}
62196448Svikram 		numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
62206448Svikram 
62216448Svikram 		if (numstr[0] == '\0' || !isdigit(numstr[0])) {
62226448Svikram 			(void) fclose(tfp);
62236448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
62246448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
62256448Svikram 			return (NULL);
62266448Svikram 		}
62276448Svikram 
62286448Svikram 		signnum = atoi(numstr);
62296448Svikram 		INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
62306448Svikram 		if (signnum < 0) {
62316448Svikram 			(void) fclose(tfp);
62326448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
62336448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
62346448Svikram 			return (NULL);
62356448Svikram 		}
62366448Svikram 
62376448Svikram 		if (i != signnum) {
62386448Svikram 			BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
62396448Svikram 			break;
62406448Svikram 		}
62416448Svikram 	}
62426448Svikram 
62436448Svikram 	(void) fclose(tfp);
62446448Svikram 
62456448Svikram found:
62466448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
62476448Svikram 
62486448Svikram 	/* add the ufs signature to the /var/run list of signatures */
62496448Svikram 	ret = ufs_add_to_sign_list(tmpsign);
62506448Svikram 	INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
62516448Svikram 	if (ret == -1) {
62526448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
62536448Svikram 		bam_error(FAILED_ADD_SIGNLIST, tmpsign);
62546448Svikram 		return (NULL);
62556448Svikram 	}
62566448Svikram 
62576448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
62586448Svikram 
62596448Svikram 	return (s_strdup(tmpsign));
62606448Svikram }
62616448Svikram 
62626448Svikram static char *
62636448Svikram get_fstype(char *osroot)
62646448Svikram {
62656448Svikram 	FILE		*mntfp;
62666448Svikram 	struct mnttab	mp = {0};
62676448Svikram 	struct mnttab	mpref = {0};
62686448Svikram 	int		error;
62696448Svikram 	int		ret;
62706448Svikram 	const char	*fcn = "get_fstype()";
62716448Svikram 
62726448Svikram 	INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
62736448Svikram 	if (osroot == NULL) {
62746448Svikram 		bam_error(GET_FSTYPE_ARGS);
62756448Svikram 		return (NULL);
62766448Svikram 	}
62776448Svikram 
62786448Svikram 	mntfp = fopen(MNTTAB, "r");
62796448Svikram 	error = errno;
62806448Svikram 	INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
62816448Svikram 	if (mntfp == NULL) {
62826448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
62836448Svikram 		return (NULL);
62846448Svikram 	}
62856448Svikram 
62866448Svikram 	if (*osroot == '\0')
62876448Svikram 		mpref.mnt_mountp = "/";
62886448Svikram 	else
62896448Svikram 		mpref.mnt_mountp = osroot;
62906448Svikram 
62916448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
62926448Svikram 	INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
62936448Svikram 	if (ret != 0) {
62946448Svikram 		bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
62956448Svikram 		(void) fclose(mntfp);
62966448Svikram 		return (NULL);
62976448Svikram 	}
62986448Svikram 	(void) fclose(mntfp);
62996448Svikram 
63006448Svikram 	INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
63016448Svikram 	if (mp.mnt_fstype == NULL) {
63026448Svikram 		bam_error(MNTTAB_FSTYPE_NULL, osroot);
63036448Svikram 		return (NULL);
63046448Svikram 	}
63056448Svikram 
63066448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
63076448Svikram 
63086448Svikram 	return (s_strdup(mp.mnt_fstype));
63096448Svikram }
63106448Svikram 
63116448Svikram static char *
63126448Svikram create_zfs_sign(char *osdev)
63136448Svikram {
63146448Svikram 	char		tmpsign[PATH_MAX];
63156448Svikram 	char		*pool;
63166448Svikram 	const char	*fcn = "create_zfs_sign()";
63176448Svikram 
63186448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
63196448Svikram 
63206448Svikram 	/*
63216448Svikram 	 * First find the pool name
63226448Svikram 	 */
63236448Svikram 	pool = get_pool(osdev);
63246448Svikram 	INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
63256448Svikram 	if (pool == NULL) {
63266448Svikram 		bam_error(GET_POOL_FAILED, osdev);
63276448Svikram 		return (NULL);
63286448Svikram 	}
63296448Svikram 
63306448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
63316448Svikram 
63326448Svikram 	BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
63336448Svikram 
63346448Svikram 	free(pool);
63356448Svikram 
63366448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
63376448Svikram 
63386448Svikram 	return (s_strdup(tmpsign));
63396448Svikram }
63406448Svikram 
63416448Svikram static char *
63426448Svikram create_new_sign(char *osdev, char *fstype)
63436448Svikram {
63446448Svikram 	char		*sign;
63456448Svikram 	const char	*fcn = "create_new_sign()";
63466448Svikram 
63476448Svikram 	INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
63486448Svikram 
63496448Svikram 	if (strcmp(fstype, "zfs") == 0) {
63506448Svikram 		BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
63516448Svikram 		sign = create_zfs_sign(osdev);
63526448Svikram 	} else if (strcmp(fstype, "ufs") == 0) {
63536448Svikram 		BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
63546448Svikram 		sign = create_ufs_sign();
63556448Svikram 	} else {
63566448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
63576448Svikram 		sign = NULL;
63586448Svikram 	}
63596448Svikram 
63606448Svikram 	BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
63616448Svikram 	return (sign);
63626448Svikram }
63636448Svikram 
63646448Svikram static int
63656448Svikram set_backup_common(char *mntpt, char *sign)
63666448Svikram {
63676448Svikram 	FILE		*bfp;
63686448Svikram 	char		backup[PATH_MAX];
63696448Svikram 	char		tmpsign[PATH_MAX];
63706448Svikram 	int		error;
63716448Svikram 	char		*bdir;
63726448Svikram 	char		*backup_dup;
63736448Svikram 	struct stat	sb;
63746448Svikram 	int		ret;
63756448Svikram 	const char	*fcn = "set_backup_common()";
63766448Svikram 
63776448Svikram 	(void) snprintf(backup, sizeof (backup), "%s%s",
63786448Svikram 	    mntpt, GRUBSIGN_BACKUP);
63796448Svikram 
63806448Svikram 	/* First read the backup */
63816448Svikram 	bfp = fopen(backup, "r");
63826448Svikram 	if (bfp != NULL) {
63836448Svikram 		while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
63846448Svikram 			if (strcmp(tmpsign, sign) == 0) {
63856448Svikram 				BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
63866448Svikram 				(void) fclose(bfp);
63876448Svikram 				return (0);
63886448Svikram 			}
63896448Svikram 		}
63906448Svikram 		(void) fclose(bfp);
63916448Svikram 		BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
63926448Svikram 	} else {
63936448Svikram 		BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
63946448Svikram 	}
63956448Svikram 
63966448Svikram 	/*
63976448Svikram 	 * Didn't find the correct signature. First create
63986448Svikram 	 * the directory if necessary.
63996448Svikram 	 */
64006448Svikram 
64016448Svikram 	/* dirname() modifies its argument so dup it */
64026448Svikram 	backup_dup = s_strdup(backup);
64036448Svikram 	bdir = dirname(backup_dup);
64046448Svikram 	assert(bdir);
64056448Svikram 
64066448Svikram 	ret = stat(bdir, &sb);
64076448Svikram 	INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
64086448Svikram 	if (ret == -1) {
64096448Svikram 		BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
64108735SEnrico.Perla@Sun.COM 		ret = mkdirp(bdir, DIR_PERMS);
64116448Svikram 		error = errno;
64126448Svikram 		INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
64136448Svikram 		if (ret == -1) {
64146448Svikram 			bam_error(GRUBSIGN_BACKUP_MKDIRERR,
64156448Svikram 			    GRUBSIGN_BACKUP, strerror(error));
64166448Svikram 			free(backup_dup);
64176448Svikram 			return (-1);
64186448Svikram 		}
64196448Svikram 	}
64206448Svikram 	free(backup_dup);
64216448Svikram 
64226448Svikram 	/*
64236448Svikram 	 * Open the backup in append mode to add the correct
64246448Svikram 	 * signature;
64256448Svikram 	 */
64266448Svikram 	bfp = fopen(backup, "a");
64276448Svikram 	error = errno;
64286448Svikram 	INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
64296448Svikram 	if (bfp == NULL) {
64306448Svikram 		bam_error(GRUBSIGN_BACKUP_OPENERR,
64316448Svikram 		    GRUBSIGN_BACKUP, strerror(error));
64326448Svikram 		return (-1);
64336448Svikram 	}
64346448Svikram 
64356448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
64366448Svikram 
64376448Svikram 	ret = fputs(tmpsign, bfp);
64386448Svikram 	error = errno;
64396448Svikram 	INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
64406448Svikram 	if (ret != strlen(tmpsign)) {
64416448Svikram 		bam_error(GRUBSIGN_BACKUP_WRITEERR,
64426448Svikram 		    GRUBSIGN_BACKUP, strerror(error));
64436448Svikram 		(void) fclose(bfp);
64446448Svikram 		return (-1);
64456448Svikram 	}
64466448Svikram 
64476448Svikram 	(void) fclose(bfp);
64486448Svikram 
64496448Svikram 	if (bam_verbose)
64506448Svikram 		bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
64516448Svikram 
64526448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
64536448Svikram 
64546448Svikram 	return (0);
64556448Svikram }
64566448Svikram 
64576448Svikram static int
64586448Svikram set_backup_ufs(char *osroot, char *sign)
64596448Svikram {
64606448Svikram 	const char	*fcn = "set_backup_ufs()";
64616448Svikram 
64626448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
64636448Svikram 	return (set_backup_common(osroot, sign));
64646448Svikram }
64656448Svikram 
64666448Svikram static int
64676448Svikram set_backup_zfs(char *osdev, char *sign)
64686448Svikram {
64696448Svikram 	char		*pool;
64706448Svikram 	char		*mntpt;
64716448Svikram 	zfs_mnted_t	mnted;
64726448Svikram 	int		ret;
64736448Svikram 	const char	*fcn = "set_backup_zfs()";
64746448Svikram 
64756448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
64766448Svikram 
64776448Svikram 	pool = get_pool(osdev);
64786448Svikram 	INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
64796448Svikram 	if (pool == NULL) {
64806448Svikram 		bam_error(GET_POOL_FAILED, osdev);
64816448Svikram 		return (-1);
64826448Svikram 	}
64836448Svikram 
64846448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
64856448Svikram 	INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
64866448Svikram 	if (mntpt == NULL) {
64876448Svikram 		bam_error(FAIL_MNT_TOP_DATASET, pool);
64886448Svikram 		free(pool);
64896448Svikram 		return (-1);
64906448Svikram 	}
64916448Svikram 
64926448Svikram 	ret = set_backup_common(mntpt, sign);
64936448Svikram 
64946448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
64956448Svikram 
64966448Svikram 	free(pool);
64976448Svikram 
64986448Svikram 	INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
64996448Svikram 	if (ret == 0) {
65006448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
65016448Svikram 	} else {
65026448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
65036448Svikram 	}
65046448Svikram 
65056448Svikram 	return (ret);
65066448Svikram }
65076448Svikram 
65086448Svikram static int
65096448Svikram set_backup(char *osroot, char *osdev, char *sign, char *fstype)
65106448Svikram {
65116448Svikram 	const char	*fcn = "set_backup()";
65126448Svikram 	int		ret;
65136448Svikram 
65146448Svikram 	INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
65156448Svikram 
65166448Svikram 	if (strcmp(fstype, "ufs") == 0) {
65176448Svikram 		BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
65186448Svikram 		ret = set_backup_ufs(osroot, sign);
65196448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
65206448Svikram 		BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
65216448Svikram 		ret = set_backup_zfs(osdev, sign);
65226448Svikram 	} else {
65236448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
65246448Svikram 		ret = -1;
65256448Svikram 	}
65266448Svikram 
65276448Svikram 	if (ret == 0) {
65286448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
65296448Svikram 	} else {
65306448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
65316448Svikram 	}
65326448Svikram 
65336448Svikram 	return (ret);
65346448Svikram }
65356448Svikram 
65366448Svikram static int
65376448Svikram set_primary_common(char *mntpt, char *sign)
65386448Svikram {
65396448Svikram 	char		signfile[PATH_MAX];
65406448Svikram 	char		signdir[PATH_MAX];
65416448Svikram 	struct stat	sb;
65426448Svikram 	int		fd;
65436448Svikram 	int		error;
65446448Svikram 	int		ret;
65456448Svikram 	const char	*fcn = "set_primary_common()";
65466448Svikram 
65476448Svikram 	(void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
65486448Svikram 	    mntpt, GRUBSIGN_DIR, sign);
65496448Svikram 
65506448Svikram 	if (stat(signfile, &sb) != -1) {
65516448Svikram 		if (bam_verbose)
65526448Svikram 			bam_print(PRIMARY_SIGN_EXISTS, sign);
65536448Svikram 		return (0);
65546448Svikram 	} else {
65556448Svikram 		BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
65566448Svikram 	}
65576448Svikram 
65586448Svikram 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
65596448Svikram 	    mntpt, GRUBSIGN_DIR);
65606448Svikram 
65616448Svikram 	if (stat(signdir, &sb) == -1) {
65626448Svikram 		BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
65638735SEnrico.Perla@Sun.COM 		ret = mkdirp(signdir, DIR_PERMS);
65646448Svikram 		error = errno;
65656448Svikram 		INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
65666448Svikram 		if (ret == -1) {
65676448Svikram 			bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
65686448Svikram 			return (-1);
65696448Svikram 		}
65706448Svikram 	}
65716448Svikram 
65726448Svikram 	fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
65736448Svikram 	error = errno;
65746448Svikram 	INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
65756448Svikram 	if (fd == -1) {
65766448Svikram 		bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
65776448Svikram 		return (-1);
65786448Svikram 	}
65796448Svikram 
65806448Svikram 	ret = fsync(fd);
65816448Svikram 	error = errno;
65826448Svikram 	INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
65836448Svikram 	if (ret != 0) {
65846448Svikram 		bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
65856448Svikram 	}
65866448Svikram 
65876448Svikram 	(void) close(fd);
65886448Svikram 
65896448Svikram 	if (bam_verbose)
65906448Svikram 		bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
65916448Svikram 
65926448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
65936448Svikram 
65946448Svikram 	return (0);
65956448Svikram }
65966448Svikram 
65976448Svikram static int
65986448Svikram set_primary_ufs(char *osroot, char *sign)
65996448Svikram {
66006448Svikram 	const char	*fcn = "set_primary_ufs()";
66016448Svikram 
66026448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
66036448Svikram 	return (set_primary_common(osroot, sign));
66046448Svikram }
66056448Svikram 
66066448Svikram static int
66076448Svikram set_primary_zfs(char *osdev, char *sign)
66086448Svikram {
66096448Svikram 	char		*pool;
66106448Svikram 	char		*mntpt;
66116448Svikram 	zfs_mnted_t	mnted;
66126448Svikram 	int		ret;
66136448Svikram 	const char	*fcn = "set_primary_zfs()";
66146448Svikram 
66156448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
66166448Svikram 
66176448Svikram 	pool = get_pool(osdev);
66186448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
66196448Svikram 	if (pool == NULL) {
66206448Svikram 		bam_error(GET_POOL_FAILED, osdev);
66216448Svikram 		return (-1);
66226448Svikram 	}
66236448Svikram 
66246448Svikram 	/* Pool name must exist in the sign */
66256448Svikram 	ret = (strstr(sign, pool) != NULL);
66266448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
66276448Svikram 	if (ret == 0) {
66286448Svikram 		bam_error(POOL_SIGN_INCOMPAT, pool, sign);
66296448Svikram 		free(pool);
66306448Svikram 		return (-1);
66316448Svikram 	}
66326448Svikram 
66336448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
66346448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
66356448Svikram 	if (mntpt == NULL) {
66366448Svikram 		bam_error(FAIL_MNT_TOP_DATASET, pool);
66376448Svikram 		free(pool);
66386448Svikram 		return (-1);
66396448Svikram 	}
66406448Svikram 
66416448Svikram 	ret = set_primary_common(mntpt, sign);
66426448Svikram 
66436448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
66446448Svikram 
66456448Svikram 	free(pool);
66466448Svikram 
66476448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
66486448Svikram 	if (ret == 0) {
66496448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
66506448Svikram 	} else {
66516448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
66526448Svikram 	}
66536448Svikram 
66546448Svikram 	return (ret);
66556448Svikram }
66566448Svikram 
66576448Svikram static int
66586448Svikram set_primary(char *osroot, char *osdev, char *sign, char *fstype)
66596448Svikram {
66606448Svikram 	const char	*fcn = "set_primary()";
66616448Svikram 	int		ret;
66626448Svikram 
66636448Svikram 	INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
66646448Svikram 	if (strcmp(fstype, "ufs") == 0) {
66656448Svikram 		BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
66666448Svikram 		ret = set_primary_ufs(osroot, sign);
66676448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
66686448Svikram 		BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
66696448Svikram 		ret = set_primary_zfs(osdev, sign);
66706448Svikram 	} else {
66716448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
66726448Svikram 		ret = -1;
66736448Svikram 	}
66746448Svikram 
66756448Svikram 	if (ret == 0) {
66766448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
66776448Svikram 	} else {
66786448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
66796448Svikram 	}
66806448Svikram 
66816448Svikram 	return (ret);
66826448Svikram }
66836448Svikram 
66846448Svikram static int
66856448Svikram ufs_add_to_sign_list(char *sign)
66866448Svikram {
66876448Svikram 	FILE		*tfp;
66886448Svikram 	char		signline[MAXNAMELEN];
66896448Svikram 	char		cmd[PATH_MAX];
66906448Svikram 	int		ret;
66916448Svikram 	int		error;
66926448Svikram 	const char	*fcn = "ufs_add_to_sign_list()";
66936448Svikram 
66946448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
66956448Svikram 	if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
66966448Svikram 	    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
66976448Svikram 		bam_error(INVALID_UFS_SIGN, sign);
66986448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
66996448Svikram 		return (-1);
67006448Svikram 	}
67016448Svikram 
67026448Svikram 	/*
67036448Svikram 	 * most failures in this routine are not a fatal error
67046448Svikram 	 * We simply unlink the /var/run file and continue
67056448Svikram 	 */
67066448Svikram 
67076448Svikram 	ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
67086448Svikram 	error = errno;
67096448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
67106448Svikram 	if (ret == -1) {
67116448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
67126448Svikram 		    strerror(error));
67136448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
67146448Svikram 		return (0);
67156448Svikram 	}
67166448Svikram 
67176448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
67186448Svikram 	error = errno;
67196448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
67206448Svikram 	if (tfp == NULL) {
67216448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
67226448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
67236448Svikram 		return (0);
67246448Svikram 	}
67256448Svikram 
67266448Svikram 	(void) snprintf(signline, sizeof (signline), "%s\n", sign);
67276448Svikram 
67286448Svikram 	ret = fputs(signline, tfp);
67296448Svikram 	error = errno;
67306448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
67316448Svikram 	if (ret != strlen(signline)) {
67326448Svikram 		bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
67336448Svikram 		(void) fclose(tfp);
67346448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
67356448Svikram 		return (0);
67366448Svikram 	}
67376448Svikram 
67386448Svikram 	ret = fclose(tfp);
67396448Svikram 	error = errno;
67406448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
67416448Svikram 	if (ret == EOF) {
67426448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
67436448Svikram 		    strerror(error));
67446448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
67456448Svikram 		return (0);
67466448Svikram 	}
67476448Svikram 
67486448Svikram 	/* Sort the list again */
67496448Svikram 	(void) snprintf(cmd, sizeof (cmd),
67506448Svikram 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
67516448Svikram 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
67526448Svikram 
67536448Svikram 	ret = exec_cmd(cmd, NULL);
67546448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
67556448Svikram 	if (ret != 0) {
67566448Svikram 		bam_error(GRUBSIGN_SORT_FAILED);
67576448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
67586448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
67596448Svikram 		return (0);
67606448Svikram 	}
67616448Svikram 
67626448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
67636448Svikram 
67646448Svikram 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
67656448Svikram 	error = errno;
67666448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
67676448Svikram 	if (ret == -1) {
67686448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
67696448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
67706448Svikram 		return (0);
67716448Svikram 	}
67726448Svikram 
67736448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
67746448Svikram 
67756448Svikram 	return (0);
67766448Svikram }
67776448Svikram 
67786448Svikram static int
67796448Svikram set_signature(char *osroot, char *osdev, char *sign, char *fstype)
67806448Svikram {
67816448Svikram 	int		ret;
67826448Svikram 	const char	*fcn = "set_signature()";
67836448Svikram 
67846448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
67856448Svikram 
67866448Svikram 	ret = set_backup(osroot, osdev, sign, fstype);
67876448Svikram 	INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
67886448Svikram 	if (ret == -1) {
67896448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
67906448Svikram 		bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
67916448Svikram 		return (-1);
67926448Svikram 	}
67936448Svikram 
67946448Svikram 	ret = set_primary(osroot, osdev, sign, fstype);
67956448Svikram 	INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
67966448Svikram 
67976448Svikram 	if (ret == 0) {
67986448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
67996448Svikram 	} else {
68006448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
68016448Svikram 		bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
68026448Svikram 
68036448Svikram 	}
68046448Svikram 	return (ret);
68056448Svikram }
68066448Svikram 
68076448Svikram char *
68086448Svikram get_grubsign(char *osroot, char *osdev)
68096448Svikram {
68106448Svikram 	char		*grubsign;	/* (<sign>,#,#) */
68116448Svikram 	char		*slice;
68126448Svikram 	int		fdiskpart;
68136448Svikram 	char		*sign;
68146448Svikram 	char		*fstype;
68156448Svikram 	int		ret;
68166448Svikram 	const char	*fcn = "get_grubsign()";
68176448Svikram 
68186448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
68196448Svikram 
68206448Svikram 	fstype = get_fstype(osroot);
68216448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
68226448Svikram 	if (fstype == NULL) {
68236448Svikram 		bam_error(GET_FSTYPE_FAILED, osroot);
68246448Svikram 		return (NULL);
68256448Svikram 	}
68266448Svikram 
68276448Svikram 	sign = find_existing_sign(osroot, osdev, fstype);
68286448Svikram 	INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
68296448Svikram 	if (sign == NULL) {
68306448Svikram 		BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
68316448Svikram 		sign = create_new_sign(osdev, fstype);
68326448Svikram 		INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
68336448Svikram 		if (sign == NULL) {
68346448Svikram 			bam_error(GRUBSIGN_CREATE_FAIL, osdev);
68356448Svikram 			free(fstype);
68366448Svikram 			return (NULL);
68376448Svikram 		}
68386448Svikram 	}
68396448Svikram 
68406448Svikram 	ret = set_signature(osroot, osdev, sign, fstype);
68416448Svikram 	INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
68426448Svikram 	if (ret == -1) {
68436448Svikram 		bam_error(GRUBSIGN_WRITE_FAIL, osdev);
68446448Svikram 		free(sign);
68456448Svikram 		free(fstype);
68466448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
68476448Svikram 		return (NULL);
68486448Svikram 	}
68496448Svikram 
68506448Svikram 	free(fstype);
68516448Svikram 
68526448Svikram 	if (bam_verbose)
68536448Svikram 		bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
68546448Svikram 
68556448Svikram 	fdiskpart = get_partition(osdev);
68566448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1);
68576448Svikram 	if (fdiskpart == -1) {
68586448Svikram 		bam_error(FDISKPART_FAIL, osdev);
68596448Svikram 		free(sign);
68606448Svikram 		return (NULL);
68616448Svikram 	}
68626448Svikram 
68636448Svikram 	slice = strrchr(osdev, 's');
68646448Svikram 
68656448Svikram 	grubsign = s_calloc(1, MAXNAMELEN + 10);
68666448Svikram 	if (slice) {
68676448Svikram 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
68686448Svikram 		    sign, fdiskpart, slice[1] + 'a' - '0');
68696448Svikram 	} else
68706448Svikram 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
68716448Svikram 		    sign, fdiskpart);
68726448Svikram 
68736448Svikram 	free(sign);
68746448Svikram 
68756448Svikram 	BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
68766448Svikram 
68776448Svikram 	return (grubsign);
68780Sstevel@tonic-gate }
68790Sstevel@tonic-gate 
6880656Sszhou static char *
6881656Sszhou get_title(char *rootdir)
68820Sstevel@tonic-gate {
68836448Svikram 	static char	title[80];
68846448Svikram 	char		*cp = NULL;
68856448Svikram 	char		release[PATH_MAX];
68866448Svikram 	FILE		*fp;
68876448Svikram 	const char	*fcn = "get_title()";
68880Sstevel@tonic-gate 
68890Sstevel@tonic-gate 	/* open the /etc/release file */
68900Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
68910Sstevel@tonic-gate 
68920Sstevel@tonic-gate 	fp = fopen(release, "r");
68936448Svikram 	if (fp == NULL) {
68946448Svikram 		bam_error(OPEN_FAIL, release, strerror(errno));
68956448Svikram 		cp = NULL;
68966448Svikram 		goto out;
68976448Svikram 	}
68980Sstevel@tonic-gate 
68990Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
69000Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
69010Sstevel@tonic-gate 		if (cp)
69020Sstevel@tonic-gate 			break;
69030Sstevel@tonic-gate 	}
69040Sstevel@tonic-gate 	(void) fclose(fp);
69056448Svikram 
69066448Svikram out:
69076448Svikram 	cp = cp ? cp : "Solaris";
69086448Svikram 
69096448Svikram 	BAM_DPRINTF((D_GET_TITLE, fcn, cp));
69106448Svikram 
69116448Svikram 	return (cp);
69120Sstevel@tonic-gate }
69130Sstevel@tonic-gate 
69143446Smrj char *
69150Sstevel@tonic-gate get_special(char *mountp)
69160Sstevel@tonic-gate {
69176448Svikram 	FILE		*mntfp;
69186448Svikram 	struct mnttab	mp = {0};
69196448Svikram 	struct mnttab	mpref = {0};
69206448Svikram 	int		error;
69216448Svikram 	int		ret;
69226448Svikram 	const char 	*fcn = "get_special()";
69236448Svikram 
69246448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
69256448Svikram 	if (mountp == NULL) {
69266448Svikram 		bam_error(GET_SPECIAL_NULL_MNTPT);
69276448Svikram 		return (NULL);
69286448Svikram 	}
69290Sstevel@tonic-gate 
69300Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
69316448Svikram 	error = errno;
69326448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
69330Sstevel@tonic-gate 	if (mntfp == NULL) {
69346448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
69356448Svikram 		return (NULL);
69360Sstevel@tonic-gate 	}
69370Sstevel@tonic-gate 
69380Sstevel@tonic-gate 	if (*mountp == '\0')
69390Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
69400Sstevel@tonic-gate 	else
69410Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
69426448Svikram 
69436448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
69446448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
69456448Svikram 	if (ret != 0) {
69460Sstevel@tonic-gate 		(void) fclose(mntfp);
69476448Svikram 		BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
69480Sstevel@tonic-gate 		return (NULL);
69490Sstevel@tonic-gate 	}
69500Sstevel@tonic-gate 	(void) fclose(mntfp);
69510Sstevel@tonic-gate 
69526448Svikram 	BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
69536448Svikram 
69540Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
69550Sstevel@tonic-gate }
69560Sstevel@tonic-gate 
69576448Svikram static void
69586448Svikram free_physarray(char **physarray, int n)
69596448Svikram {
69606448Svikram 	int			i;
69616448Svikram 	const char		*fcn = "free_physarray()";
69626448Svikram 
69636448Svikram 	assert(physarray);
69646448Svikram 	assert(n);
69656448Svikram 
69666448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
69676448Svikram 
69686448Svikram 	for (i = 0; i < n; i++) {
69696448Svikram 		free(physarray[i]);
69706448Svikram 	}
69716448Svikram 	free(physarray);
69726448Svikram 
69736448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
69746448Svikram }
69756448Svikram 
69766448Svikram static int
69776448Svikram zfs_get_physical(char *special, char ***physarray, int *n)
69786448Svikram {
69796448Svikram 	char			sdup[PATH_MAX];
69806448Svikram 	char			cmd[PATH_MAX];
69816448Svikram 	char			dsk[PATH_MAX];
69826448Svikram 	char			*pool;
69836448Svikram 	filelist_t		flist = {0};
69846448Svikram 	line_t			*lp;
69856448Svikram 	line_t			*startlp;
69866448Svikram 	char			*comp1;
69876448Svikram 	int			i;
69886448Svikram 	int			ret;
69896448Svikram 	const char		*fcn = "zfs_get_physical()";
69906448Svikram 
69916448Svikram 	assert(special);
69926448Svikram 
69936448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
69946448Svikram 
69956448Svikram 	INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
69966448Svikram 	if (special[0] == '/') {
69976448Svikram 		bam_error(INVALID_ZFS_SPECIAL, special);
69986448Svikram 		return (-1);
69996448Svikram 	}
70006448Svikram 
70016448Svikram 	(void) strlcpy(sdup, special, sizeof (sdup));
70026448Svikram 
70036448Svikram 	pool = strtok(sdup, "/");
70046448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
70056448Svikram 	if (pool == NULL) {
70066448Svikram 		bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
70076448Svikram 		return (-1);
70086448Svikram 	}
70096448Svikram 
70106448Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
70116448Svikram 
70126448Svikram 	ret = exec_cmd(cmd, &flist);
70136448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
70146448Svikram 	if (ret != 0) {
70156448Svikram 		bam_error(ZFS_GET_POOL_STATUS, pool);
70166448Svikram 		return (-1);
70176448Svikram 	}
70186448Svikram 
70196448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
70206448Svikram 	if (flist.head == NULL) {
70216448Svikram 		bam_error(BAD_ZPOOL_STATUS, pool);
70226448Svikram 		filelist_free(&flist);
70236448Svikram 		return (-1);
70246448Svikram 	}
70256448Svikram 
70266448Svikram 	for (lp = flist.head; lp; lp = lp->next) {
70276448Svikram 		BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
70286448Svikram 		comp1 = strtok(lp->line, " \t");
70296448Svikram 		if (comp1 == NULL) {
70306448Svikram 			free(lp->line);
70316448Svikram 			lp->line = NULL;
70326448Svikram 		} else {
70336448Svikram 			comp1 = s_strdup(comp1);
70346448Svikram 			free(lp->line);
70356448Svikram 			lp->line = comp1;
70366448Svikram 		}
70376448Svikram 	}
70386448Svikram 
70396448Svikram 	for (lp = flist.head; lp; lp = lp->next) {
70406448Svikram 		if (lp->line == NULL)
70416448Svikram 			continue;
70426448Svikram 		if (strcmp(lp->line, pool) == 0) {
70436448Svikram 			BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
70446448Svikram 			break;
70456448Svikram 		}
70466448Svikram 	}
70476448Svikram 
70486448Svikram 	if (lp == NULL) {
70496448Svikram 		bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
70506448Svikram 		filelist_free(&flist);
70516448Svikram 		return (-1);
70526448Svikram 	}
70536448Svikram 
70546448Svikram 	startlp = lp->next;
70556448Svikram 	for (i = 0, lp = startlp; lp; lp = lp->next) {
70566448Svikram 		if (lp->line == NULL)
70576448Svikram 			continue;
70586448Svikram 		if (strcmp(lp->line, "mirror") == 0)
70596448Svikram 			continue;
70606448Svikram 		if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
70616448Svikram 			break;
70626448Svikram 		i++;
70636448Svikram 		BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
70646448Svikram 	}
70656448Svikram 
70666448Svikram 	if (i == 0) {
70676448Svikram 		bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
70686448Svikram 		filelist_free(&flist);
70696448Svikram 		return (-1);
70706448Svikram 	}
70716448Svikram 
70726448Svikram 	*n = i;
70736448Svikram 	*physarray = s_calloc(*n, sizeof (char *));
70746448Svikram 	for (i = 0, lp = startlp; lp; lp = lp->next) {
70756448Svikram 		if (lp->line == NULL)
70766448Svikram 			continue;
70776448Svikram 		if (strcmp(lp->line, "mirror") == 0)
70786448Svikram 			continue;
70796448Svikram 		if (strcmp(lp->line, "errors:") == 0)
70806448Svikram 			break;
70816448Svikram 		if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
70826448Svikram 		    strncmp(lp->line, "/dev/rdsk/",
70836448Svikram 		    strlen("/dev/rdsk/")) != 0)  {
70846448Svikram 			(void) snprintf(dsk, sizeof (dsk), "/dev/dsk/%s",
70856448Svikram 			    lp->line);
70866448Svikram 		} else {
70876448Svikram 			(void) strlcpy(dsk, lp->line, sizeof (dsk));
70886448Svikram 		}
70896448Svikram 		BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
70906448Svikram 		(*physarray)[i++] = s_strdup(dsk);
70916448Svikram 	}
70926448Svikram 
70936448Svikram 	assert(i == *n);
70946448Svikram 
70956448Svikram 	filelist_free(&flist);
70966448Svikram 
70976448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
70986448Svikram 	return (0);
70996448Svikram }
71006448Svikram 
71016694Svikram /*
71026694Svikram  * Certain services needed to run metastat successfully may not
71036694Svikram  * be enabled. Enable them now.
71046694Svikram  */
71056694Svikram /*
71066694Svikram  * Checks if the specified service is online
71076694Svikram  * Returns: 	1 if the service is online
71086694Svikram  *		0 if the service is not online
71096694Svikram  *		-1 on error
71106694Svikram  */
71116694Svikram static int
71126694Svikram is_svc_online(char *svc)
71136694Svikram {
71146694Svikram 	char			*state;
71156694Svikram 	const char		*fcn = "is_svc_online()";
71166694Svikram 
71176694Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
71186694Svikram 
71196694Svikram 	state = smf_get_state(svc);
71206694Svikram 	INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
71216694Svikram 	if (state == NULL) {
71226694Svikram 		bam_error(GET_SVC_STATE_ERR, svc);
71236694Svikram 		return (-1);
71246694Svikram 	}
71256694Svikram 	BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
71266694Svikram 
71276694Svikram 	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
71286694Svikram 		BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
71296694Svikram 		free(state);
71306694Svikram 		return (1);
71316694Svikram 	}
71326694Svikram 
71336694Svikram 	BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
71346694Svikram 
71356694Svikram 	free(state);
71366694Svikram 
71376694Svikram 	return (0);
71386694Svikram }
71396694Svikram 
71406694Svikram static int
71416694Svikram enable_svc(char *svc)
71426694Svikram {
71436694Svikram 	int			ret;
71446694Svikram 	int			sleeptime;
71456694Svikram 	const char		*fcn = "enable_svc()";
71466694Svikram 
71476694Svikram 	ret = is_svc_online(svc);
71486694Svikram 	if (ret == -1) {
71496694Svikram 		bam_error(SVC_IS_ONLINE_FAILED, svc);
71506694Svikram 		return (-1);
71516694Svikram 	} else if (ret == 1) {
71526694Svikram 		BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
71536694Svikram 		return (0);
71546694Svikram 	}
71556694Svikram 
71566694Svikram 	/* Service is not enabled. Enable it now. */
71576694Svikram 	ret = smf_enable_instance(svc, 0);
71586694Svikram 	INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
71596694Svikram 	if (ret != 0) {
71606694Svikram 		bam_error(ENABLE_SVC_FAILED, svc);
71616694Svikram 		return (-1);
71626694Svikram 	}
71636694Svikram 
71646694Svikram 	BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
71656694Svikram 
71666694Svikram 	sleeptime = 0;
71676694Svikram 	do {
71686694Svikram 		ret = is_svc_online(svc);
71696694Svikram 		INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
71706694Svikram 		INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
71716694Svikram 		INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
71726694Svikram 		if (ret == -1) {
71736694Svikram 			bam_error(ERR_SVC_GET_ONLINE, svc);
71746694Svikram 			return (-1);
71756694Svikram 		} else if (ret == 1) {
71766694Svikram 			BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
71776694Svikram 			return (1);
71786694Svikram 		}
71796694Svikram 		(void) sleep(1);
71808992SRobert.Harris@Sun.COM 	} while (++sleeptime < 60);
71816694Svikram 
71826694Svikram 	bam_error(TIMEOUT_ENABLE_SVC, svc);
71836694Svikram 
71846694Svikram 	return (-1);
71856694Svikram }
71866694Svikram 
71876448Svikram static int
71886448Svikram ufs_get_physical(char *special, char ***physarray, int *n)
71896448Svikram {
71906448Svikram 	char			cmd[PATH_MAX];
71916448Svikram 	char			*shortname;
71926448Svikram 	filelist_t		flist = {0};
71936448Svikram 	char			*meta;
71946448Svikram 	char			*type;
71956448Svikram 	char			*comp1;
71966448Svikram 	char			*comp2;
71976448Svikram 	char			*comp3;
71986448Svikram 	char			*comp4;
71996448Svikram 	int			i;
72006448Svikram 	line_t			*lp;
72016448Svikram 	int			ret;
72026694Svikram 	char			*svc;
72036448Svikram 	const char		*fcn = "ufs_get_physical()";
72046448Svikram 
72056448Svikram 	assert(special);
72066448Svikram 
72076448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
72086448Svikram 
72096448Svikram 	if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
72106448Svikram 		bam_error(UFS_GET_PHYS_NOT_SVM, special);
72116448Svikram 		return (-1);
72126448Svikram 	}
72136448Svikram 
72146448Svikram 	if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
72156448Svikram 		shortname = special + strlen("/dev/md/dsk/");
72166448Svikram 	} else if (strncmp(special, "/dev/md/rdsk/",
72176448Svikram 	    strlen("/dev/md/rdsk/")) == 0) {
72186448Svikram 		shortname = special + strlen("/dev/md/rdsk");
72196448Svikram 	} else {
72206448Svikram 		bam_error(UFS_GET_PHYS_INVALID_SVM, special);
72216448Svikram 		return (-1);
72226448Svikram 	}
72236448Svikram 
72246448Svikram 	BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
72256448Svikram 
72266694Svikram 	svc = "network/rpc/meta:default";
72276694Svikram 	if (enable_svc(svc) == -1) {
72286694Svikram 		bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
72296694Svikram 	}
72306694Svikram 
72316448Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
72326448Svikram 
72336448Svikram 	ret = exec_cmd(cmd, &flist);
72346448Svikram 	INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
72356448Svikram 	if (ret != 0) {
72366448Svikram 		bam_error(UFS_SVM_METASTAT_ERR, shortname);
72376448Svikram 		return (-1);
72386448Svikram 	}
72396448Svikram 
72406448Svikram 	INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
72416448Svikram 	if (flist.head == NULL) {
72426448Svikram 		bam_error(BAD_UFS_SVM_METASTAT, shortname);
72436448Svikram 		filelist_free(&flist);
72446448Svikram 		return (-1);
72456448Svikram 	}
72466448Svikram 
72476448Svikram 	/*
72486448Svikram 	 * Check if not a mirror. We only parse a single metadevice
72496448Svikram 	 * if not a mirror
72506448Svikram 	 */
72516448Svikram 	meta = strtok(flist.head->line, " \t");
72526448Svikram 	type = strtok(NULL, " \t");
72536448Svikram 	if (meta == NULL || type == NULL) {
72546448Svikram 		bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
72556448Svikram 		filelist_free(&flist);
72566448Svikram 		return (-1);
72576448Svikram 	}
72586448Svikram 	if (strcmp(type, "-m") != 0) {
72596448Svikram 		comp1 = strtok(NULL, " \t");
72606448Svikram 		comp2 = strtok(NULL, " \t");
72616448Svikram 		if (comp1 == NULL || comp2 != NULL) {
72626448Svikram 			bam_error(INVALID_UFS_SVM_METASTAT, shortname);
72636448Svikram 			filelist_free(&flist);
72646448Svikram 			return (-1);
72656448Svikram 		}
72666448Svikram 		BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
72676448Svikram 		*physarray = s_calloc(1, sizeof (char *));
72686448Svikram 		(*physarray)[0] = s_strdup(comp1);
72696448Svikram 		*n = 1;
72706448Svikram 		filelist_free(&flist);
72716448Svikram 		return (0);
72726448Svikram 	}
72736448Svikram 
72746448Svikram 	/*
72756448Svikram 	 * Okay we have a mirror. Everything after the first line
72766448Svikram 	 * is a submirror
72776448Svikram 	 */
72786448Svikram 	for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
72796448Svikram 		if (strstr(lp->line, "/dev/dsk/") == NULL &&
72806448Svikram 		    strstr(lp->line, "/dev/rdsk/") == NULL) {
72816448Svikram 			bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
72826448Svikram 			filelist_free(&flist);
72836448Svikram 			return (-1);
72846448Svikram 		}
72856448Svikram 		i++;
72866448Svikram 	}
72876448Svikram 
72886448Svikram 	*physarray = s_calloc(i, sizeof (char *));
72896448Svikram 	*n = i;
72906448Svikram 
72916448Svikram 	for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
72926448Svikram 		comp1 = strtok(lp->line, " \t");
72936448Svikram 		comp2 = strtok(NULL, " \t");
72946448Svikram 		comp3 = strtok(NULL, " \t");
72956448Svikram 		comp4 = strtok(NULL, " \t");
72966448Svikram 
72976448Svikram 		if (comp3 == NULL || comp4 == NULL ||
72986448Svikram 		    (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
72996448Svikram 		    strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
73006448Svikram 			bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
73016448Svikram 			filelist_free(&flist);
73026448Svikram 			free_physarray(*physarray, *n);
73036448Svikram 			return (-1);
73046448Svikram 		}
73056448Svikram 
73066448Svikram 		(*physarray)[i++] = s_strdup(comp4);
73076448Svikram 	}
73086448Svikram 
73096448Svikram 	assert(i == *n);
73106448Svikram 
73116448Svikram 	filelist_free(&flist);
73126448Svikram 
73136448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
73146448Svikram 	return (0);
73156448Svikram }
73166448Svikram 
73176448Svikram static int
73186448Svikram get_physical(char *menu_root, char ***physarray, int *n)
73196448Svikram {
73206448Svikram 	char			*special;
73216448Svikram 	int			ret;
73226448Svikram 	const char		*fcn = "get_physical()";
73236448Svikram 
73246448Svikram 	assert(menu_root);
73256448Svikram 	assert(physarray);
73266448Svikram 	assert(n);
73276448Svikram 
73286448Svikram 	*physarray = NULL;
73296448Svikram 	*n = 0;
73306448Svikram 
73316448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
73326448Svikram 
73336448Svikram 	/* First get the device special file from /etc/mnttab */
73346448Svikram 	special = get_special(menu_root);
73356448Svikram 	INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
73366448Svikram 	if (special == NULL) {
73376448Svikram 		bam_error(GET_SPECIAL_NULL, menu_root);
73386448Svikram 		return (-1);
73396448Svikram 	}
73406448Svikram 
73416448Svikram 	/* If already a physical device nothing to do */
73426448Svikram 	if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
73436448Svikram 	    strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
73446448Svikram 		BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
73456448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
73466448Svikram 		*physarray = s_calloc(1, sizeof (char *));
73476448Svikram 		(*physarray)[0] = special;
73486448Svikram 		*n = 1;
73496448Svikram 		return (0);
73506448Svikram 	}
73516448Svikram 
73526448Svikram 	if (is_zfs(menu_root)) {
73536448Svikram 		ret = zfs_get_physical(special, physarray, n);
73546448Svikram 	} else if (is_ufs(menu_root)) {
73556448Svikram 		ret = ufs_get_physical(special, physarray, n);
73566448Svikram 	} else {
73576448Svikram 		bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
73586448Svikram 		ret = -1;
73596448Svikram 	}
73606448Svikram 
73616448Svikram 	free(special);
73626448Svikram 
73636448Svikram 	INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
73646448Svikram 	if (ret == -1) {
73656448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
73666448Svikram 	} else {
73676448Svikram 		int	i;
73686448Svikram 		assert (*n > 0);
73696448Svikram 		for (i = 0; i < *n; i++) {
73706448Svikram 			BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
73716448Svikram 		}
73726448Svikram 	}
73736448Svikram 
73746448Svikram 	return (ret);
73756448Svikram }
73766448Svikram 
73776448Svikram static int
73786448Svikram is_bootdisk(char *osroot, char *physical)
73796448Svikram {
73806448Svikram 	int			ret;
73816448Svikram 	char			*grubroot;
73826448Svikram 	char			*bootp;
73836448Svikram 	const char		*fcn = "is_bootdisk()";
73846448Svikram 
73856448Svikram 	assert(osroot);
73866448Svikram 	assert(physical);
73876448Svikram 
73886448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
73896448Svikram 
73906448Svikram 	bootp = strstr(physical, "p0:boot");
73916448Svikram 	if (bootp)
73926448Svikram 		*bootp = '\0';
73936448Svikram 	/*
73946448Svikram 	 * We just want the BIOS mapping for menu disk.
73956448Svikram 	 * Don't pass menu_root to get_grubroot() as the
73966448Svikram 	 * check that it is used for is not relevant here.
73976448Svikram 	 * The osroot is immaterial as well - it is only used to
73986448Svikram 	 * to find create_diskmap script. Everything hinges on
73996448Svikram 	 * "physical"
74006448Svikram 	 */
74016448Svikram 	grubroot = get_grubroot(osroot, physical, NULL);
74026448Svikram 
74036448Svikram 	INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
74046448Svikram 	if (grubroot == NULL) {
74058642SVikram.Hegde@Sun.COM 		if (bam_verbose)
74068642SVikram.Hegde@Sun.COM 			bam_error(NO_GRUBROOT_FOR_DISK, physical);
74076448Svikram 		return (0);
74086448Svikram 	}
74096448Svikram 	ret = grubroot[3] == '0';
74106448Svikram 	free(grubroot);
74116448Svikram 
74126448Svikram 	BAM_DPRINTF((D_RETURN_RET, fcn, ret));
74136448Svikram 
74146448Svikram 	return (ret);
74150Sstevel@tonic-gate }
74160Sstevel@tonic-gate 
74170Sstevel@tonic-gate /*
74186448Svikram  * Check if menu is on the boot device
74190Sstevel@tonic-gate  * Return 0 (false) on error
74200Sstevel@tonic-gate  */
74210Sstevel@tonic-gate static int
74226448Svikram menu_on_bootdisk(char *osroot, char *menu_root)
74236448Svikram {
74246448Svikram 	char		**physarray;
74256448Svikram 	int		ret;
74266448Svikram 	int		n;
74276448Svikram 	int		i;
74286448Svikram 	int		on_bootdisk;
74296448Svikram 	const char	*fcn = "menu_on_bootdisk()";
74306448Svikram 
74316448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
74326448Svikram 
74336448Svikram 	ret = get_physical(menu_root, &physarray, &n);
74346448Svikram 	INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
74356448Svikram 	if (ret != 0) {
74366448Svikram 		bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
74370Sstevel@tonic-gate 		return (0);
74386448Svikram 	}
74396448Svikram 
74406448Svikram 	assert(physarray);
74416448Svikram 	assert(n > 0);
74426448Svikram 
74436448Svikram 	on_bootdisk = 0;
74446448Svikram 	for (i = 0; i < n; i++) {
74456448Svikram 		assert(strncmp(physarray[i], "/dev/dsk/",
74466448Svikram 		    strlen("/dev/dsk/")) == 0 ||
74476448Svikram 		    strncmp(physarray[i], "/dev/rdsk/",
74486448Svikram 		    strlen("/dev/rdsk/")) == 0);
74496448Svikram 
74506448Svikram 		BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
74516448Svikram 		if (is_bootdisk(osroot, physarray[i])) {
74526448Svikram 			on_bootdisk = 1;
74536448Svikram 			BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
74546448Svikram 		}
74556448Svikram 	}
74566448Svikram 
74576448Svikram 	free_physarray(physarray, n);
74586448Svikram 
74596448Svikram 	INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
74606448Svikram 	INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
74616448Svikram 	if (on_bootdisk) {
74626448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
74636448Svikram 	} else {
74646448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
74656448Svikram 	}
74666448Svikram 
74676448Svikram 	return (on_bootdisk);
74686448Svikram }
74696448Svikram 
74706448Svikram void
74716448Svikram bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
74726448Svikram {
74736448Svikram 	const char	*fcn = "bam_add_line()";
74746448Svikram 
74756448Svikram 	assert(mp);
74766448Svikram 	assert(entry);
74776448Svikram 	assert(prev);
74786448Svikram 	assert(lp);
74796448Svikram 
74806448Svikram 	lp->next = prev->next;
74816448Svikram 	if (prev->next) {
74826448Svikram 		BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
74836448Svikram 		prev->next->prev = lp;
74846448Svikram 	} else {
74856448Svikram 		BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
74866448Svikram 	}
74876448Svikram 	prev->next = lp;
74886448Svikram 	lp->prev = prev;
74896448Svikram 
74906448Svikram 	if (entry->end == prev) {
74916448Svikram 		BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
74926448Svikram 		entry->end = lp;
74936448Svikram 	}
74946448Svikram 	if (mp->end == prev) {
74956448Svikram 		assert(lp->next == NULL);
74966448Svikram 		mp->end = lp;
74976448Svikram 		BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
74986448Svikram 	}
74990Sstevel@tonic-gate }
75000Sstevel@tonic-gate 
7501662Sszhou /*
7502662Sszhou  * look for matching bootadm entry with specified parameters
7503662Sszhou  * Here are the rules (based on existing usage):
7504662Sszhou  * - If title is specified, match on title only
75056448Svikram  * - Else, match on root/findroot, kernel, and module.
75066448Svikram  *   Note that, if root_opt is non-zero, the absence of
75076448Svikram  *   root line is considered a match.
7508662Sszhou  */
7509662Sszhou static entry_t *
75106448Svikram find_boot_entry(
75116448Svikram 	menu_t *mp,
75126448Svikram 	char *title,
75136448Svikram 	char *kernel,
75146448Svikram 	char *findroot,
75156448Svikram 	char *root,
75166448Svikram 	char *module,
75176448Svikram 	int root_opt,
75186448Svikram 	int *entry_num)
75196448Svikram {
75206448Svikram 	int		i;
75216448Svikram 	line_t		*lp;
75226448Svikram 	entry_t		*ent;
75236448Svikram 	const char	*fcn = "find_boot_entry()";
75246448Svikram 
75256448Svikram 	if (entry_num)
75266448Svikram 		*entry_num = BAM_ERROR;
7527662Sszhou 
7528662Sszhou 	/* find matching entry */
7529662Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
7530662Sszhou 		lp = ent->start;
7531662Sszhou 
7532662Sszhou 		/* first line of entry must be bootadm comment */
7533662Sszhou 		lp = ent->start;
75343446Smrj 		if (lp->flags != BAM_COMMENT ||
75353446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
7536662Sszhou 			continue;
7537662Sszhou 		}
7538662Sszhou 
7539662Sszhou 		/* advance to title line */
7540662Sszhou 		lp = lp->next;
7541662Sszhou 		if (title) {
7542662Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
75436448Svikram 			    strcmp(lp->arg, title) == 0) {
75446448Svikram 				BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
7545662Sszhou 				break;
75466448Svikram 			}
75476448Svikram 			BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
7548662Sszhou 			continue;	/* check title only */
7549662Sszhou 		}
7550662Sszhou 
7551662Sszhou 		lp = lp->next;	/* advance to root line */
75525084Sjohnlev 		if (lp == NULL) {
75535084Sjohnlev 			continue;
75546448Svikram 		} else if (strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
75556448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
75566448Svikram 			    findroot = NULL);
75576448Svikram 			if (findroot == NULL) {
75586448Svikram 				BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
75596448Svikram 				    fcn, lp->arg));
75606448Svikram 				continue;
75616448Svikram 			}
75626448Svikram 			/* findroot command found, try match  */
75636448Svikram 			if (strcmp(lp->arg, findroot) != 0) {
75646448Svikram 				BAM_DPRINTF((D_NOMATCH_FINDROOT,
75656448Svikram 				    fcn, findroot, lp->arg));
7566662Sszhou 				continue;
7567662Sszhou 			}
75686448Svikram 			BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
75696448Svikram 			lp = lp->next;	/* advance to kernel line */
75706448Svikram 		} else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
75716448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
75726448Svikram 			if (root == NULL) {
75736448Svikram 				BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
75746448Svikram 				    fcn, lp->arg));
75756448Svikram 				continue;
75766448Svikram 			}
75776448Svikram 			/* root cmd found, try match */
75786448Svikram 			if (strcmp(lp->arg, root) != 0) {
75796448Svikram 				BAM_DPRINTF((D_NOMATCH_ROOT,
75806448Svikram 				    fcn, root, lp->arg));
75816448Svikram 				continue;
75826448Svikram 			}
75836448Svikram 			BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
7584662Sszhou 			lp = lp->next;	/* advance to kernel line */
7585662Sszhou 		} else {
75866448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
75876448Svikram 			    root_opt = 0);
75886448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
75896448Svikram 			    root_opt = 1);
7590662Sszhou 			/* no root command, see if root is optional */
7591662Sszhou 			if (root_opt == 0) {
75926448Svikram 				BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
7593662Sszhou 				continue;
7594662Sszhou 			}
75956448Svikram 			BAM_DPRINTF((D_ROOT_OPT, fcn));
7596662Sszhou 		}
7597662Sszhou 
7598662Sszhou 		if (lp == NULL || lp->next == NULL) {
7599662Sszhou 			continue;
7600662Sszhou 		}
7601662Sszhou 
76025084Sjohnlev 		if (kernel &&
76035084Sjohnlev 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
76048104SEnrico.Perla@Sun.COM 			if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
76058104SEnrico.Perla@Sun.COM 			    !(ent->flags & BAM_ENTRY_DBOOT) ||
76068104SEnrico.Perla@Sun.COM 			    strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
76078104SEnrico.Perla@Sun.COM 				continue;
76088104SEnrico.Perla@Sun.COM 
76098104SEnrico.Perla@Sun.COM 			ent->flags |= BAM_ENTRY_UPGFSKERNEL;
76108104SEnrico.Perla@Sun.COM 
76115084Sjohnlev 		}
76126448Svikram 		BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
76135084Sjohnlev 
76143467Srscott 		/*
76155084Sjohnlev 		 * Check for matching module entry (failsafe or normal).
76165084Sjohnlev 		 * If it fails to match, we go around the loop again.
76175084Sjohnlev 		 * For xpv entries, there are two module lines, so we
76185084Sjohnlev 		 * do the check twice.
76193467Srscott 		 */
7620662Sszhou 		lp = lp->next;	/* advance to module line */
76215084Sjohnlev 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
76225084Sjohnlev 		    (((lp = lp->next) != NULL) &&
76235084Sjohnlev 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
76245084Sjohnlev 			/* match found */
76256448Svikram 			BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
76265084Sjohnlev 			break;
7627662Sszhou 		}
76288104SEnrico.Perla@Sun.COM 
76298104SEnrico.Perla@Sun.COM 		if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
76308104SEnrico.Perla@Sun.COM 		    (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
76318104SEnrico.Perla@Sun.COM 		    strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
76328104SEnrico.Perla@Sun.COM 			ent->flags |= BAM_ENTRY_UPGFSMODULE;
76338104SEnrico.Perla@Sun.COM 			break;
76348104SEnrico.Perla@Sun.COM 		}
76358104SEnrico.Perla@Sun.COM 
76365084Sjohnlev 	}
76375084Sjohnlev 
76386448Svikram 	if (ent && entry_num) {
76395084Sjohnlev 		*entry_num = i;
76405084Sjohnlev 	}
76416448Svikram 
76426448Svikram 	if (ent) {
76436448Svikram 		BAM_DPRINTF((D_RETURN_RET, fcn, i));
76446448Svikram 	} else {
76456448Svikram 		BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
76466448Svikram 	}
7647662Sszhou 	return (ent);
7648662Sszhou }
7649662Sszhou 
7650662Sszhou static int
76516448Svikram update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
76526448Svikram     char *kernel, char *mod_kernel, char *module, int root_opt)
76536448Svikram {
76546448Svikram 	int		i;
76556448Svikram 	int		change_kernel = 0;
76566448Svikram 	entry_t		*ent;
76576448Svikram 	line_t		*lp;
76586448Svikram 	line_t		*tlp;
76596448Svikram 	char		linebuf[BAM_MAXLINE];
76606448Svikram 	const char	*fcn = "update_boot_entry()";
7661662Sszhou 
7662662Sszhou 	/* note: don't match on title, it's updated on upgrade */
76636448Svikram 	ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
76646448Svikram 	    root_opt, &i);
76653446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
76663446Smrj 		/*
76673446Smrj 		 * We may be upgrading a kernel from multiboot to
76686448Svikram 		 * directboot.  Look for a multiboot entry. A multiboot
76696448Svikram 		 * entry will not have a findroot line.
76703446Smrj 		 */
76716448Svikram 		ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
76726448Svikram 		    MULTIBOOT_ARCHIVE, root_opt, &i);
76733446Smrj 		if (ent != NULL) {
76746448Svikram 			BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
76753446Smrj 			change_kernel = 1;
76763446Smrj 		}
76776448Svikram 	} else if (ent) {
76786448Svikram 		BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
76796448Svikram 	}
76806448Svikram 
76816448Svikram 	if (ent == NULL) {
76826448Svikram 		BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
76836448Svikram 		return (add_boot_entry(mp, title, findroot,
76845084Sjohnlev 		    kernel, mod_kernel, module));
76856448Svikram 	}
76866448Svikram 
76876448Svikram 	/* replace title of existing entry and update findroot line */
7688662Sszhou 	lp = ent->start;
7689662Sszhou 	lp = lp->next;	/* title line */
7690662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7691662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
7692662Sszhou 	free(lp->arg);
7693662Sszhou 	free(lp->line);
7694662Sszhou 	lp->arg = s_strdup(title);
7695662Sszhou 	lp->line = s_strdup(linebuf);
76966448Svikram 	BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
76976448Svikram 
76986448Svikram 	tlp = lp;	/* title line */
7699662Sszhou 	lp = lp->next;	/* root line */
77006448Svikram 
77016448Svikram 	/* if no root or findroot command, create a new line_t */
77026448Svikram 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
77036448Svikram 	    strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0) {
77046448Svikram 		lp = s_calloc(1, sizeof (line_t));
77056448Svikram 		bam_add_line(mp, ent, tlp, lp);
77066448Svikram 	} else {
77076448Svikram 		free(lp->cmd);
77086448Svikram 		free(lp->sep);
77096448Svikram 		free(lp->arg);
77106448Svikram 		free(lp->line);
77116448Svikram 	}
77126448Svikram 
77136448Svikram 	lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
77146448Svikram 	lp->sep = s_strdup(menu_cmds[SEP_CMD]);
77156448Svikram 	lp->arg = s_strdup(findroot);
77166448Svikram 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
77176448Svikram 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
77186448Svikram 	lp->line = s_strdup(linebuf);
77196448Svikram 	BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
77206448Svikram 
77216448Svikram 	/* kernel line */
77226448Svikram 	lp = lp->next;
77233446Smrj 
77248104SEnrico.Perla@Sun.COM 	if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
77258104SEnrico.Perla@Sun.COM 		char		*params = NULL;
77268104SEnrico.Perla@Sun.COM 
77278104SEnrico.Perla@Sun.COM 		params = strstr(lp->line, "-s");
77288104SEnrico.Perla@Sun.COM 		if (params != NULL)
77298104SEnrico.Perla@Sun.COM 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
77308104SEnrico.Perla@Sun.COM 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
77318104SEnrico.Perla@Sun.COM 			    kernel, params+2);
77328104SEnrico.Perla@Sun.COM 		else
77338104SEnrico.Perla@Sun.COM 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
77348104SEnrico.Perla@Sun.COM 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
77358104SEnrico.Perla@Sun.COM 			    kernel);
77368104SEnrico.Perla@Sun.COM 
77378104SEnrico.Perla@Sun.COM 		free(lp->cmd);
77388104SEnrico.Perla@Sun.COM 		free(lp->arg);
77398104SEnrico.Perla@Sun.COM 		free(lp->line);
77408104SEnrico.Perla@Sun.COM 		lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
77418104SEnrico.Perla@Sun.COM 		lp->arg = s_strdup(strstr(linebuf, "/"));
77428104SEnrico.Perla@Sun.COM 		lp->line = s_strdup(linebuf);
77438104SEnrico.Perla@Sun.COM 		ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
77448104SEnrico.Perla@Sun.COM 		BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd));
77458104SEnrico.Perla@Sun.COM 	}
77468104SEnrico.Perla@Sun.COM 
77473446Smrj 	if (change_kernel) {
77483446Smrj 		/*
77493446Smrj 		 * We're upgrading from multiboot to directboot.
77503446Smrj 		 */
77513446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
77523446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
77533446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
77543446Smrj 			    kernel);
77556448Svikram 			free(lp->cmd);
77563446Smrj 			free(lp->arg);
77573446Smrj 			free(lp->line);
77586448Svikram 			lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
77593446Smrj 			lp->arg = s_strdup(kernel);
77603446Smrj 			lp->line = s_strdup(linebuf);
77613446Smrj 			lp = lp->next;
77626448Svikram 			BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
77633446Smrj 		}
77643446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
77653446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
77663446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
77673446Smrj 			    module);
77686448Svikram 			free(lp->cmd);
77693446Smrj 			free(lp->arg);
77703446Smrj 			free(lp->line);
77716448Svikram 			lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
77723446Smrj 			lp->arg = s_strdup(module);
77733446Smrj 			lp->line = s_strdup(linebuf);
77743446Smrj 			lp = lp->next;
77756448Svikram 			BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
77766448Svikram 		}
77776448Svikram 	}
77788104SEnrico.Perla@Sun.COM 
77798104SEnrico.Perla@Sun.COM 	/* module line */
77808104SEnrico.Perla@Sun.COM 	lp = lp->next;
77818104SEnrico.Perla@Sun.COM 
77828104SEnrico.Perla@Sun.COM 	if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
77838104SEnrico.Perla@Sun.COM 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
77848104SEnrico.Perla@Sun.COM 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
77858104SEnrico.Perla@Sun.COM 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
77868104SEnrico.Perla@Sun.COM 			    module);
77878104SEnrico.Perla@Sun.COM 			free(lp->cmd);
77888104SEnrico.Perla@Sun.COM 			free(lp->arg);
77898104SEnrico.Perla@Sun.COM 			free(lp->line);
77908104SEnrico.Perla@Sun.COM 			lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
77918104SEnrico.Perla@Sun.COM 			lp->arg = s_strdup(module);
77928104SEnrico.Perla@Sun.COM 			lp->line = s_strdup(linebuf);
77938104SEnrico.Perla@Sun.COM 			lp = lp->next;
77948104SEnrico.Perla@Sun.COM 			ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
77958104SEnrico.Perla@Sun.COM 			BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
77968104SEnrico.Perla@Sun.COM 		}
77978104SEnrico.Perla@Sun.COM 	}
77988104SEnrico.Perla@Sun.COM 
77996448Svikram 	BAM_DPRINTF((D_RETURN_RET, fcn, i));
7800662Sszhou 	return (i);
7801662Sszhou }
7802662Sszhou 
78036448Svikram int
78046448Svikram root_optional(char *osroot, char *menu_root)
78056448Svikram {
78066448Svikram 	char			*ospecial;
78076448Svikram 	char			*mspecial;
78086448Svikram 	char			*slash;
78096448Svikram 	int			root_opt;
78106448Svikram 	int			ret1;
78116448Svikram 	int			ret2;
78126448Svikram 	const char		*fcn = "root_optional()";
78136448Svikram 
78146448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
78156448Svikram 
78166448Svikram 	/*
78176448Svikram 	 * For all filesystems except ZFS, a straight compare of osroot
78186448Svikram 	 * and menu_root will tell us if root is optional.
78196448Svikram 	 * For ZFS, the situation is complicated by the fact that
78206448Svikram 	 * menu_root and osroot are always different
78216448Svikram 	 */
78226448Svikram 	ret1 = is_zfs(osroot);
78236448Svikram 	ret2 = is_zfs(menu_root);
78246448Svikram 	INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
78256448Svikram 	if (!ret1 || !ret2) {
78266448Svikram 		BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
78276448Svikram 		root_opt = (strcmp(osroot, menu_root) == 0);
78286448Svikram 		goto out;
78296448Svikram 	}
78306448Svikram 
78316448Svikram 	ospecial = get_special(osroot);
78326448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
78336448Svikram 	if (ospecial == NULL) {
78346448Svikram 		bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
78356448Svikram 		return (0);
78366448Svikram 	}
78376448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
78386448Svikram 
78396448Svikram 	mspecial = get_special(menu_root);
78406448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
78416448Svikram 	if (mspecial == NULL) {
78426448Svikram 		bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
78436448Svikram 		free(ospecial);
78446448Svikram 		return (0);
78456448Svikram 	}
78466448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
78476448Svikram 
78486448Svikram 	slash = strchr(ospecial, '/');
78496448Svikram 	if (slash)
78506448Svikram 		*slash = '\0';
78516448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
78526448Svikram 
78536448Svikram 	root_opt = (strcmp(ospecial, mspecial) == 0);
78546448Svikram 
78556448Svikram 	free(ospecial);
78566448Svikram 	free(mspecial);
78576448Svikram 
78586448Svikram out:
78596448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
78606448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
78616448Svikram 	if (root_opt) {
78626448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
78636448Svikram 	} else {
78646448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
78656448Svikram 	}
78666448Svikram 
78676448Svikram 	return (root_opt);
78686448Svikram }
78696448Svikram 
78700Sstevel@tonic-gate /*ARGSUSED*/
78710Sstevel@tonic-gate static error_t
78726448Svikram update_entry(menu_t *mp, char *menu_root, char *osdev)
78736448Svikram {
78746448Svikram 	int		entry;
78756448Svikram 	char		*grubsign;
78766448Svikram 	char		*grubroot;
78776448Svikram 	char		*title;
78786448Svikram 	char		osroot[PATH_MAX];
78796448Svikram 	char		*failsafe_kernel = NULL;
78806448Svikram 	struct stat	sbuf;
78816448Svikram 	char		failsafe[256];
78828104SEnrico.Perla@Sun.COM 	char		failsafe_64[256];
78836448Svikram 	int		ret;
78846448Svikram 	const char	*fcn = "update_entry()";
78850Sstevel@tonic-gate 
78860Sstevel@tonic-gate 	assert(mp);
78876448Svikram 	assert(menu_root);
78886448Svikram 	assert(osdev);
78896448Svikram 	assert(bam_root);
78906448Svikram 
78916448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
78926448Svikram 
78936448Svikram 	(void) strlcpy(osroot, bam_root, sizeof (osroot));
78946448Svikram 
78950Sstevel@tonic-gate 	title = get_title(osroot);
78966448Svikram 	assert(title);
78976448Svikram 
78986448Svikram 	grubsign = get_grubsign(osroot, osdev);
78996448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
79006448Svikram 	if (grubsign == NULL) {
79016448Svikram 		bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
79020Sstevel@tonic-gate 		return (BAM_ERROR);
79030Sstevel@tonic-gate 	}
79046448Svikram 
79056448Svikram 	/*
79066448Svikram 	 * It is not a fatal error if get_grubroot() fails
79076448Svikram 	 * We no longer rely on biosdev to populate the
79086448Svikram 	 * menu
79096448Svikram 	 */
79106448Svikram 	grubroot = get_grubroot(osroot, osdev, menu_root);
79116448Svikram 	INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
79126448Svikram 	if (grubroot) {
79136448Svikram 		BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
79146448Svikram 		    fcn, osroot, osdev, menu_root));
79156448Svikram 	} else {
79166448Svikram 		BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
79176448Svikram 		    fcn, osroot, osdev, menu_root));
79180Sstevel@tonic-gate 	}
79190Sstevel@tonic-gate 
79200Sstevel@tonic-gate 	/* add the entry for normal Solaris */
79216448Svikram 	INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
79226448Svikram 	    bam_direct = BAM_DIRECT_MULTIBOOT);
79233446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
79246448Svikram 		entry = update_boot_entry(mp, title, grubsign, grubroot,
79256423Sgw25295 		    (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
79266448Svikram 		    NULL, DIRECT_BOOT_ARCHIVE,
79276448Svikram 		    root_optional(osroot, menu_root));
79286448Svikram 		BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
79295084Sjohnlev 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
79306448Svikram 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
79316448Svikram 			    grubroot, XEN_MENU, bam_zfs ?
79326448Svikram 			    XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
79336448Svikram 			    DIRECT_BOOT_ARCHIVE,
79346448Svikram 			    root_optional(osroot, menu_root));
79356448Svikram 			BAM_DPRINTF((D_UPDATED_HV_ENTRY,
79366448Svikram 			    fcn, bam_zfs, grubsign));
79375084Sjohnlev 		}
79383446Smrj 	} else {
79396448Svikram 		entry = update_boot_entry(mp, title, grubsign, grubroot,
79406448Svikram 		    MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
79416448Svikram 		    root_optional(osroot, menu_root));
79426448Svikram 
79436448Svikram 		BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
79445084Sjohnlev 	}
79455084Sjohnlev 
79465084Sjohnlev 	/*
79475084Sjohnlev 	 * Add the entry for failsafe archive.  On a bfu'd system, the
79485084Sjohnlev 	 * failsafe may be different than the installed kernel.
79495084Sjohnlev 	 */
79506448Svikram 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
79518104SEnrico.Perla@Sun.COM 	    osroot, FAILSAFE_ARCHIVE_32);
79528104SEnrico.Perla@Sun.COM 	(void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
79538104SEnrico.Perla@Sun.COM 	    osroot, FAILSAFE_ARCHIVE_64);
79548104SEnrico.Perla@Sun.COM 
79558104SEnrico.Perla@Sun.COM 	/*
79568104SEnrico.Perla@Sun.COM 	 * Check if at least one of the two archives exists
79578104SEnrico.Perla@Sun.COM 	 * Using $ISADIR as the default line, we have an entry which works
79588104SEnrico.Perla@Sun.COM 	 * for both the cases.
79598104SEnrico.Perla@Sun.COM 	 */
79608104SEnrico.Perla@Sun.COM 
79618104SEnrico.Perla@Sun.COM 	if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
79623449Srscott 
79633449Srscott 		/* Figure out where the kernel line should point */
79643449Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
79658104SEnrico.Perla@Sun.COM 		    DIRECT_BOOT_FAILSAFE_32);
79668104SEnrico.Perla@Sun.COM 		(void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
79678104SEnrico.Perla@Sun.COM 		    osroot, DIRECT_BOOT_FAILSAFE_64);
79688104SEnrico.Perla@Sun.COM 		if (stat(failsafe, &sbuf) == 0 ||
79698104SEnrico.Perla@Sun.COM 		    stat(failsafe_64, &sbuf) == 0) {
79706694Svikram 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
79713449Srscott 		} else {
79723449Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
79733449Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
79743449Srscott 			if (stat(failsafe, &sbuf) == 0) {
79753449Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
79763449Srscott 			}
79773449Srscott 		}
79783449Srscott 		if (failsafe_kernel != NULL) {
79796448Svikram 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
79806448Svikram 			    grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
79816448Svikram 			    root_optional(osroot, menu_root));
79826448Svikram 			BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
79836448Svikram 			    failsafe_kernel));
79846448Svikram 		}
79856448Svikram 	}
79866448Svikram 	free(grubroot);
79876448Svikram 
79886448Svikram 	INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
79890Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
79906448Svikram 		bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
79916448Svikram 		free(grubsign);
79920Sstevel@tonic-gate 		return (BAM_ERROR);
79930Sstevel@tonic-gate 	}
79946448Svikram 	free(grubsign);
79956448Svikram 
79966448Svikram 	update_numbering(mp);
79976448Svikram 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
79986448Svikram 	INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
79996448Svikram 	if (ret == BAM_ERROR) {
80006448Svikram 		bam_error(SET_DEFAULT_FAILED, entry);
80016448Svikram 	}
80026448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
80030Sstevel@tonic-gate 	return (BAM_WRITE);
80040Sstevel@tonic-gate }
80050Sstevel@tonic-gate 
8006662Sszhou static void
80073446Smrj save_default_entry(menu_t *mp, const char *which)
8008662Sszhou {
80096448Svikram 	int		lineNum;
80106448Svikram 	int		entryNum;
80116448Svikram 	int		entry = 0;	/* default is 0 */
80126448Svikram 	char		linebuf[BAM_MAXLINE];
80136448Svikram 	line_t		*lp = mp->curdefault;
80146448Svikram 	const char	*fcn = "save_default_entry()";
8015662Sszhou 
80163381Svikram 	if (mp->start) {
80173381Svikram 		lineNum = mp->end->lineNum;
80183381Svikram 		entryNum = mp->end->entryNum;
80193381Svikram 	} else {
80203381Svikram 		lineNum = LINE_INIT;
80213381Svikram 		entryNum = ENTRY_INIT;
80223381Svikram 	}
80233381Svikram 
8024662Sszhou 	if (lp)
8025662Sszhou 		entry = s_strtol(lp->arg);
8026662Sszhou 
80273446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
80286448Svikram 	BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
8029662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
80306448Svikram 	BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
8031662Sszhou }
8032662Sszhou 
8033662Sszhou static void
80343446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8035662Sszhou {
80366448Svikram 	int		entry;
80376448Svikram 	char		*str;
80386448Svikram 	const char	*fcn = "restore_default_entry()";
80396448Svikram 
80406448Svikram 	if (lp == NULL) {
80416448Svikram 		BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
8042662Sszhou 		return;		/* nothing to restore */
80436448Svikram 	}
80446448Svikram 
80456448Svikram 	BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
8046662Sszhou 
80473446Smrj 	str = lp->arg + strlen(which);
8048662Sszhou 	entry = s_strtol(str);
8049662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8050662Sszhou 
80516448Svikram 	BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
80526448Svikram 
8053662Sszhou 	/* delete saved old default line */
8054662Sszhou 	unlink_line(mp, lp);
8055662Sszhou 	line_free(lp);
8056662Sszhou }
8057662Sszhou 
80580Sstevel@tonic-gate /*
80590Sstevel@tonic-gate  * This function is for supporting reboot with args.
80600Sstevel@tonic-gate  * The opt value can be:
80610Sstevel@tonic-gate  * NULL		delete temp entry, if present
80626448Svikram  * entry=<n>	switches default entry to <n>
80630Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
80640Sstevel@tonic-gate  *		and make it the default
80656448Svikram  * Note that we are always rebooting the current OS instance
80666448Svikram  * so osroot == / always.
80670Sstevel@tonic-gate  */
80680Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
80690Sstevel@tonic-gate 
8070662Sszhou /*ARGSUSED*/
80710Sstevel@tonic-gate static error_t
80726448Svikram update_temp(menu_t *mp, char *dummy, char *opt)
80736448Svikram {
80746448Svikram 	int		entry;
80756448Svikram 	char		*osdev;
80766448Svikram 	char		*fstype;
80776448Svikram 	char		*sign;
80786448Svikram 	char		*opt_ptr;
80796448Svikram 	char		*path;
80806448Svikram 	char		kernbuf[BUFSIZ];
80816448Svikram 	char		args_buf[BUFSIZ];
80826448Svikram 	char		signbuf[PATH_MAX];
80836448Svikram 	int		ret;
80846448Svikram 	const char	*fcn = "update_temp()";
80850Sstevel@tonic-gate 
80860Sstevel@tonic-gate 	assert(mp);
80876448Svikram 	assert(dummy == NULL);
80886448Svikram 
80896448Svikram 	/* opt can be NULL */
80906448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
80916448Svikram 	BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
80926448Svikram 
80936448Svikram 	if (bam_alt_root || bam_rootlen != 1 ||
80946448Svikram 	    strcmp(bam_root, "/") != 0 ||
80956448Svikram 	    strcmp(rootbuf, "/") != 0) {
80966448Svikram 		bam_error(ALT_ROOT_INVALID, bam_root);
80976448Svikram 		return (BAM_ERROR);
80986448Svikram 	}
80990Sstevel@tonic-gate 
8100662Sszhou 	/* If no option, delete exiting reboot menu entry */
8101662Sszhou 	if (opt == NULL) {
81026448Svikram 		entry_t		*ent;
81036448Svikram 		BAM_DPRINTF((D_OPT_NULL, fcn));
81046448Svikram 		ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
81056448Svikram 		    NULL, NULL, 0, &entry);
81066448Svikram 		if (ent == NULL) {	/* not found is ok */
81076448Svikram 			BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
8108662Sszhou 			return (BAM_SUCCESS);
81096448Svikram 		}
8110662Sszhou 		(void) do_delete(mp, entry);
81113446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
81123446Smrj 		mp->olddefault = NULL;
81136448Svikram 		BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
81146448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8115662Sszhou 		return (BAM_WRITE);
8116662Sszhou 	}
8117662Sszhou 
8118662Sszhou 	/* if entry= is specified, set the default entry */
81196448Svikram 	if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
81206448Svikram 		int entryNum = s_strtol(opt + strlen("entry="));
81216448Svikram 		BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
81226448Svikram 		if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
81236448Svikram 			/* this is entry=# option */
81246448Svikram 			ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
81256448Svikram 			BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
81266448Svikram 			return (ret);
81276448Svikram 		} else {
81286448Svikram 			bam_error(SET_DEFAULT_FAILED, entryNum);
81296448Svikram 			return (BAM_ERROR);
81306448Svikram 		}
81310Sstevel@tonic-gate 	}
81320Sstevel@tonic-gate 
81330Sstevel@tonic-gate 	/*
81346448Svikram 	 * add a new menu entry based on opt and make it the default
81350Sstevel@tonic-gate 	 */
81366448Svikram 
81376448Svikram 	fstype = get_fstype("/");
81386448Svikram 	INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
81396448Svikram 	if (fstype == NULL) {
81406448Svikram 		bam_error(REBOOT_FSTYPE_FAILED);
81416448Svikram 		return (BAM_ERROR);
81426448Svikram 	}
81436448Svikram 
81446448Svikram 	osdev = get_special("/");
81456448Svikram 	INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
81466448Svikram 	if (osdev == NULL) {
81476448Svikram 		free(fstype);
81486448Svikram 		bam_error(REBOOT_SPECIAL_FAILED);
81496448Svikram 		return (BAM_ERROR);
81506448Svikram 	}
81516448Svikram 
81526448Svikram 	sign = find_existing_sign("/", osdev, fstype);
81536448Svikram 	INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
81546448Svikram 	if (sign == NULL) {
81556448Svikram 		free(fstype);
81566448Svikram 		free(osdev);
81576448Svikram 		bam_error(REBOOT_SIGN_FAILED);
81586448Svikram 		return (BAM_ERROR);
81596448Svikram 	}
81606448Svikram 
81616448Svikram 	free(osdev);
81626448Svikram 	(void) strlcpy(signbuf, sign, sizeof (signbuf));
81636448Svikram 	free(sign);
81646448Svikram 
81656448Svikram 	assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
81666448Svikram 	    strchr(signbuf, ')') == NULL);
81676448Svikram 
81686448Svikram 	/*
81696448Svikram 	 * There is no alternate root while doing reboot with args
81706448Svikram 	 * This version of bootadm is only delivered with a DBOOT
81716448Svikram 	 * version of Solaris.
81726448Svikram 	 */
81736448Svikram 	INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
81746448Svikram 	if (bam_direct != BAM_DIRECT_DBOOT) {
81758751SLin.Ling@Sun.COM 		free(fstype);
81766448Svikram 		bam_error(REBOOT_DIRECT_FAILED);
81770Sstevel@tonic-gate 		return (BAM_ERROR);
81780Sstevel@tonic-gate 	}
81790Sstevel@tonic-gate 
81800Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
81816448Svikram 	if (opt[0] == '-') {
81826448Svikram 		/* It's an option - first see if boot-file is set */
81836448Svikram 		ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
81846448Svikram 		INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
81856448Svikram 		if (ret != BAM_SUCCESS) {
81868751SLin.Ling@Sun.COM 			free(fstype);
81876448Svikram 			bam_error(REBOOT_GET_KERNEL_FAILED);
81886448Svikram 			return (BAM_ERROR);
81896448Svikram 		}
81906448Svikram 		if (kernbuf[0] == '\0')
81916448Svikram 			(void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
81926448Svikram 			    sizeof (kernbuf));
81938751SLin.Ling@Sun.COM 		/*
81948751SLin.Ling@Sun.COM 		 * If this is a zfs file system and kernbuf does not
81958751SLin.Ling@Sun.COM 		 * have "-B $ZFS-BOOTFS" string yet, add it.
81968751SLin.Ling@Sun.COM 		 */
81978751SLin.Ling@Sun.COM 		if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
81988751SLin.Ling@Sun.COM 			(void) strlcat(kernbuf, " ", sizeof (kernbuf));
81998751SLin.Ling@Sun.COM 			(void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
82008751SLin.Ling@Sun.COM 		}
82016448Svikram 		(void) strlcat(kernbuf, " ", sizeof (kernbuf));
82026448Svikram 		(void) strlcat(kernbuf, opt, sizeof (kernbuf));
82036448Svikram 		BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
82046448Svikram 	} else if (opt[0] == '/') {
82056448Svikram 		/* It's a full path, so write it out. */
82066448Svikram 		(void) strlcpy(kernbuf, opt, sizeof (kernbuf));
82076448Svikram 
82086448Svikram 		/*
82096448Svikram 		 * If someone runs:
82106448Svikram 		 *
82116448Svikram 		 *	# eeprom boot-args='-kd'
82126448Svikram 		 *	# reboot /platform/i86pc/kernel/unix
82136448Svikram 		 *
82146448Svikram 		 * we want to use the boot-args as part of the boot
82156448Svikram 		 * line.  On the other hand, if someone runs:
82166448Svikram 		 *
82176448Svikram 		 *	# reboot "/platform/i86pc/kernel/unix -kd"
82186448Svikram 		 *
82196448Svikram 		 * we don't need to mess with boot-args.  If there's
82206448Svikram 		 * no space in the options string, assume we're in the
82216448Svikram 		 * first case.
82226448Svikram 		 */
82236448Svikram 		if (strchr(opt, ' ') == NULL) {
82246448Svikram 			ret = get_kernel(mp, ARGS_CMD, args_buf,
82256448Svikram 			    sizeof (args_buf));
82266448Svikram 			INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
82276448Svikram 			if (ret != BAM_SUCCESS) {
82288751SLin.Ling@Sun.COM 				free(fstype);
82296448Svikram 				bam_error(REBOOT_GET_ARGS_FAILED);
82303446Smrj 				return (BAM_ERROR);
82316448Svikram 			}
82326448Svikram 
82336448Svikram 			if (args_buf[0] != '\0') {
82346448Svikram 				(void) strlcat(kernbuf, " ", sizeof (kernbuf));
82356448Svikram 				(void) strlcat(kernbuf, args_buf,
82366448Svikram 				    sizeof (kernbuf));
82376448Svikram 			}
82386448Svikram 		}
82396448Svikram 		BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
82406448Svikram 	} else {
82416448Svikram 		/*
82426448Svikram 		 * It may be a partial path, or it may be a partial
82436448Svikram 		 * path followed by options.  Assume that only options
82446448Svikram 		 * follow a space.  If someone sends us a kernel path
82456448Svikram 		 * that includes a space, they deserve to be broken.
82466448Svikram 		 */
82476448Svikram 		opt_ptr = strchr(opt, ' ');
82486448Svikram 		if (opt_ptr != NULL) {
82496448Svikram 			*opt_ptr = '\0';
82506448Svikram 		}
82516448Svikram 
82526448Svikram 		path = expand_path(opt);
82536448Svikram 		if (path != NULL) {
82546448Svikram 			(void) strlcpy(kernbuf, path, sizeof (kernbuf));
82556448Svikram 			free(path);
82564346Srscott 
82574346Srscott 			/*
82586448Svikram 			 * If there were options given, use those.
82596448Svikram 			 * Otherwise, copy over the default options.
82604346Srscott 			 */
82616448Svikram 			if (opt_ptr != NULL) {
82626448Svikram 				/* Restore the space in opt string */
82636448Svikram 				*opt_ptr = ' ';
82646448Svikram 				(void) strlcat(kernbuf, opt_ptr,
82656448Svikram 				    sizeof (kernbuf));
82666448Svikram 			} else {
82676448Svikram 				ret = get_kernel(mp, ARGS_CMD, args_buf,
82686448Svikram 				    sizeof (args_buf));
82696448Svikram 				INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
82706448Svikram 				    ret = BAM_ERROR);
82716448Svikram 				if (ret != BAM_SUCCESS) {
82728751SLin.Ling@Sun.COM 					free(fstype);
82736448Svikram 					bam_error(REBOOT_GET_ARGS_FAILED);
82744346Srscott 					return (BAM_ERROR);
82756448Svikram 				}
82764346Srscott 
82774346Srscott 				if (args_buf[0] != '\0') {
82786448Svikram 					(void) strlcat(kernbuf, " ",
82796448Svikram 					    sizeof (kernbuf));
82806448Svikram 					(void) strlcat(kernbuf,
82816448Svikram 					    args_buf, sizeof (kernbuf));
82824346Srscott 				}
82834346Srscott 			}
82846448Svikram 			BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
82853446Smrj 		} else {
82868751SLin.Ling@Sun.COM 			free(fstype);
82876448Svikram 			bam_error(UNKNOWN_KERNEL, opt);
82886448Svikram 			bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
82896448Svikram 			return (BAM_ERROR);
82906448Svikram 		}
82916448Svikram 	}
82928751SLin.Ling@Sun.COM 	free(fstype);
82936448Svikram 	entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
82946448Svikram 	    NULL, NULL);
82956448Svikram 	INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
82960Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
82976448Svikram 		bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
82980Sstevel@tonic-gate 		return (BAM_ERROR);
82990Sstevel@tonic-gate 	}
8300662Sszhou 
83013446Smrj 	save_default_entry(mp, BAM_OLDDEF);
83026448Svikram 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
83036448Svikram 	INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
83046448Svikram 	if (ret == BAM_ERROR) {
83056448Svikram 		bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
83066448Svikram 	}
83076448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
83080Sstevel@tonic-gate 	return (BAM_WRITE);
83090Sstevel@tonic-gate }
83100Sstevel@tonic-gate 
83110Sstevel@tonic-gate static error_t
83120Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
83130Sstevel@tonic-gate {
83146448Svikram 	line_t		*lp;
83156448Svikram 	line_t		*found;
83166448Svikram 	line_t		*last;
83176448Svikram 	char		*cp;
83186448Svikram 	char		*str;
83196448Svikram 	char		prefix[BAM_MAXLINE];
83206448Svikram 	size_t		len;
83216448Svikram 	const char	*fcn = "set_global()";
83220Sstevel@tonic-gate 
83230Sstevel@tonic-gate 	assert(mp);
83240Sstevel@tonic-gate 	assert(globalcmd);
83250Sstevel@tonic-gate 
8326316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
83276448Svikram 		INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
83286448Svikram 		INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
83296448Svikram 		INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
8330316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
8331316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
8332316Svikram 			bam_error(INVALID_ENTRY, prefix);
8333316Svikram 			return (BAM_ERROR);
8334316Svikram 		}
8335316Svikram 	}
8336316Svikram 
83370Sstevel@tonic-gate 	found = last = NULL;
83380Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
83390Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
83400Sstevel@tonic-gate 			continue;
83410Sstevel@tonic-gate 
83420Sstevel@tonic-gate 		last = lp; /* track the last global found */
83430Sstevel@tonic-gate 
83446448Svikram 		INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
83450Sstevel@tonic-gate 		if (lp->cmd == NULL) {
83460Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
83470Sstevel@tonic-gate 			continue;
83480Sstevel@tonic-gate 		}
83490Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
83500Sstevel@tonic-gate 			continue;
83510Sstevel@tonic-gate 
83526448Svikram 		BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
83536448Svikram 
83540Sstevel@tonic-gate 		if (found) {
83550Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
83560Sstevel@tonic-gate 		}
83570Sstevel@tonic-gate 		found = lp;
83580Sstevel@tonic-gate 	}
83590Sstevel@tonic-gate 
83600Sstevel@tonic-gate 	if (found == NULL) {
83610Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
83620Sstevel@tonic-gate 		if (last == NULL) {
83630Sstevel@tonic-gate 			lp->next = mp->start;
83640Sstevel@tonic-gate 			mp->start = lp;
83650Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
83660Sstevel@tonic-gate 		} else {
83670Sstevel@tonic-gate 			lp->next = last->next;
83680Sstevel@tonic-gate 			last->next = lp;
83690Sstevel@tonic-gate 			if (lp->next == NULL)
83700Sstevel@tonic-gate 				mp->end = lp;
83710Sstevel@tonic-gate 		}
83720Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
83730Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
83740Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
83750Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
83760Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
83770Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
83786448Svikram 		BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
83796448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
83800Sstevel@tonic-gate 		return (BAM_WRITE);
83810Sstevel@tonic-gate 	}
83820Sstevel@tonic-gate 
83830Sstevel@tonic-gate 	/*
83840Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
83850Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
83860Sstevel@tonic-gate 	 * readability.
83870Sstevel@tonic-gate 	 */
83880Sstevel@tonic-gate 	str = found->line;
83890Sstevel@tonic-gate 	cp = prefix;
83900Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
83910Sstevel@tonic-gate 		*(cp++) = *(str++);
83920Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
83930Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
83940Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
83950Sstevel@tonic-gate 
83960Sstevel@tonic-gate 	free(found->line);
83970Sstevel@tonic-gate 	found->line = s_calloc(1, len);
83980Sstevel@tonic-gate 	(void) snprintf(found->line, len,
83994346Srscott 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
84000Sstevel@tonic-gate 
84016448Svikram 	BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
84026448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
84030Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
84040Sstevel@tonic-gate }
84050Sstevel@tonic-gate 
84063446Smrj /*
84073446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
84084346Srscott  * expand it to a full unix path.  The calling function is expected to
84094346Srscott  * output a message if an error occurs and NULL is returned.
84103446Smrj  */
84113446Smrj static char *
84123446Smrj expand_path(const char *partial_path)
84133446Smrj {
84146448Svikram 	int		new_path_len;
84156448Svikram 	char		*new_path;
84166448Svikram 	char		new_path2[PATH_MAX];
84176448Svikram 	struct stat	sb;
84186448Svikram 	const char	*fcn = "expand_path()";
84193446Smrj 
84203446Smrj 	new_path_len = strlen(partial_path) + 64;
84213446Smrj 	new_path = s_calloc(1, new_path_len);
84223446Smrj 
84233446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
84243446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
84253446Smrj 	    partial_path);
84263446Smrj 	if (stat(new_path, &sb) == 0) {
84276448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
84283446Smrj 		return (new_path);
84293446Smrj 	}
84303446Smrj 
84313446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
84323446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
84333446Smrj 		    DIRECT_BOOT_KERNEL);
84346448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
84353446Smrj 		return (new_path);
84363446Smrj 	}
84373446Smrj 
84383446Smrj 	/*
84393446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
84403446Smrj 	 * see if we were just given a glom name.
84413446Smrj 	 */
84423446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
84433446Smrj 	    partial_path);
84443446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
84453446Smrj 	    partial_path);
84463446Smrj 	if (stat(new_path, &sb) == 0) {
84473446Smrj 		if (stat(new_path2, &sb) == 0) {
84483446Smrj 			/*
84493446Smrj 			 * We matched both, so we actually
84503446Smrj 			 * want to write the $ISADIR version.
84513446Smrj 			 */
84523446Smrj 			(void) snprintf(new_path, new_path_len,
84533446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
84543446Smrj 			    partial_path);
84553446Smrj 		}
84566448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
84573446Smrj 		return (new_path);
84583446Smrj 	}
84593446Smrj 
84603446Smrj 	free(new_path);
84616448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
84623446Smrj 	return (NULL);
84633446Smrj }
84643446Smrj 
84653446Smrj /*
84663446Smrj  * The kernel cmd and arg have been changed, so
84673446Smrj  * check whether the archive line needs to change.
84683446Smrj  */
84693446Smrj static void
84703446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
84713446Smrj {
84726448Svikram 	line_t		*lp = entryp->start;
84736448Svikram 	char		*new_archive;
84746448Svikram 	menu_cmd_t	m_cmd;
84756448Svikram 	const char	*fcn = "set_archive_line()";
84763446Smrj 
84773446Smrj 	for (; lp != NULL; lp = lp->next) {
84783446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
84793446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
84803446Smrj 			break;
84813446Smrj 		}
84826448Svikram 
84836448Svikram 		INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
84846448Svikram 		if (lp == entryp->end) {
84856448Svikram 			BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
84866448Svikram 			    entryp->entryNum));
84873446Smrj 			return;
84886448Svikram 		}
84896448Svikram 	}
84906448Svikram 	INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
84916448Svikram 	if (lp == NULL) {
84926448Svikram 		BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
84933446Smrj 		return;
84946448Svikram 	}
84953446Smrj 
84963446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
84973446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
84983446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
84993446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
85003446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
85013446Smrj 		m_cmd = MODULE_CMD;
85023446Smrj 	} else {
85033446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
85043446Smrj 		m_cmd = MODULE_CMD;
85053446Smrj 	}
85063446Smrj 
85076448Svikram 	if (strcmp(lp->arg, new_archive) == 0) {
85086448Svikram 		BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
85093446Smrj 		return;
85106448Svikram 	}
85113446Smrj 
85123446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
85133446Smrj 		free(lp->cmd);
85143446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
85153446Smrj 	}
85163446Smrj 
85173446Smrj 	free(lp->arg);
85183446Smrj 	lp->arg = s_strdup(new_archive);
85193446Smrj 	update_line(lp);
85206448Svikram 	BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
85213446Smrj }
85223446Smrj 
85233446Smrj /*
85243446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
85253446Smrj  */
85263446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
85273446Smrj 
85283446Smrj /*
85293446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
85303446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
85313446Smrj  * string, reset the value to the default.  If path is a non-zero-length
85323446Smrj  * string, set the kernel or arguments.
85333446Smrj  */
85343446Smrj static error_t
85356448Svikram get_set_kernel(
85366448Svikram 	menu_t *mp,
85376448Svikram 	menu_cmd_t optnum,
85386448Svikram 	char *path,
85396448Svikram 	char *buf,
85406448Svikram 	size_t bufsize)
85416448Svikram {
85426448Svikram 	int		entryNum;
85436448Svikram 	int		rv = BAM_SUCCESS;
85446448Svikram 	int		free_new_path = 0;
85456448Svikram 	entry_t		*entryp;
85466448Svikram 	line_t		*ptr;
85476448Svikram 	line_t		*kernelp;
85486448Svikram 	char		*new_arg;
85496448Svikram 	char		*old_args;
85506448Svikram 	char		*space;
85516448Svikram 	char		*new_path;
85526448Svikram 	char		old_space;
85536448Svikram 	size_t		old_kernel_len;
85546448Svikram 	size_t		new_str_len;
85556448Svikram 	char		*fstype;
85566448Svikram 	char		*osdev;
85576448Svikram 	char		*sign;
85586448Svikram 	char		signbuf[PATH_MAX];
85596448Svikram 	int		ret;
85606448Svikram 	const char	*fcn = "get_set_kernel()";
85613446Smrj 
85623446Smrj 	assert(bufsize > 0);
85633446Smrj 
85643446Smrj 	ptr = kernelp = NULL;
85653446Smrj 	new_arg = old_args = space = NULL;
85666448Svikram 	new_path = NULL;
85673446Smrj 	buf[0] = '\0';
85683446Smrj 
85696448Svikram 	INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
85706448Svikram 	    bam_direct = BAM_DIRECT_MULTIBOOT);
85713446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
85723446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
85733446Smrj 		return (BAM_ERROR);
85743446Smrj 	}
85753446Smrj 
85763446Smrj 	/*
85773446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
85783446Smrj 	 * one, we don't want to mess with it.  Just print an error and
85793446Smrj 	 * return.
85803446Smrj 	 */
85813446Smrj 	if (mp->curdefault) {
85823446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
85833446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
85843446Smrj 			if (entryp->entryNum == entryNum)
85853446Smrj 				break;
85863446Smrj 		}
85873446Smrj 		if ((entryp != NULL) &&
85883446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
85893446Smrj 			bam_error(DEFAULT_NOT_BAM);
85903446Smrj 			return (BAM_ERROR);
85913446Smrj 		}
85923446Smrj 	}
85933446Smrj 
85946448Svikram 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
85956448Svikram 	    0, &entryNum);
85963446Smrj 
85973446Smrj 	if (entryp != NULL) {
85983446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
85993446Smrj 		    ptr = ptr->next) {
86003446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
86013446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
86023446Smrj 				kernelp = ptr;
86033446Smrj 				break;
86043446Smrj 			}
86053446Smrj 		}
86063446Smrj 		if (kernelp == NULL) {
86073446Smrj 			bam_error(NO_KERNEL, entryNum);
86083446Smrj 			return (BAM_ERROR);
86093446Smrj 		}
86103446Smrj 
86113446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
86123446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
86133446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
86143446Smrj 			old_args++;
86153446Smrj 	}
86163446Smrj 
86173446Smrj 	if (path == NULL) {
86186448Svikram 		if (entryp == NULL) {
86196448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
86206448Svikram 			BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
86213446Smrj 			return (BAM_SUCCESS);
86226448Svikram 		}
86236448Svikram 		assert(kernelp);
86243446Smrj 		if (optnum == ARGS_CMD) {
86256448Svikram 			if (old_args[0] != '\0') {
86263446Smrj 				(void) strlcpy(buf, old_args, bufsize);
86276448Svikram 				BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
86286448Svikram 			}
86293446Smrj 		} else {
86303446Smrj 			/*
86313446Smrj 			 * We need to print the kernel, so we just turn the
86323446Smrj 			 * first space into a '\0' and print the beginning.
86333446Smrj 			 * We don't print anything if it's the default kernel.
86343446Smrj 			 */
86353446Smrj 			old_space = *space;
86363446Smrj 			*space = '\0';
86376448Svikram 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
86383446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
86396448Svikram 				BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
86406448Svikram 			}
86413446Smrj 			*space = old_space;
86423446Smrj 		}
86436448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
86443446Smrj 		return (BAM_SUCCESS);
86453446Smrj 	}
86463446Smrj 
86473446Smrj 	/*
86483446Smrj 	 * First, check if we're resetting an entry to the default.
86493446Smrj 	 */
86503446Smrj 	if ((path[0] == '\0') ||
86513446Smrj 	    ((optnum == KERNEL_CMD) &&
86523446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
86533446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
86543446Smrj 			/* No previous entry, it's already the default */
86556448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
86563446Smrj 			return (BAM_SUCCESS);
86573446Smrj 		}
86583446Smrj 
86593446Smrj 		/*
86603446Smrj 		 * Check if we can delete the entry.  If we're resetting the
86613446Smrj 		 * kernel command, and the args is already empty, or if we're
86623446Smrj 		 * resetting the args command, and the kernel is already the
86633446Smrj 		 * default, we can restore the old default and delete the entry.
86643446Smrj 		 */
86653446Smrj 		if (((optnum == KERNEL_CMD) &&
86663446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
86673446Smrj 		    ((optnum == ARGS_CMD) &&
86683446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
86693446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
86703446Smrj 			kernelp = NULL;
86713446Smrj 			(void) do_delete(mp, entryNum);
86723446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
86733446Smrj 			    mp->old_rc_default);
86743446Smrj 			mp->old_rc_default = NULL;
86753446Smrj 			rv = BAM_WRITE;
86766448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
86773446Smrj 			goto done;
86783446Smrj 		}
86793446Smrj 
86803446Smrj 		if (optnum == KERNEL_CMD) {
86813446Smrj 			/*
86823446Smrj 			 * At this point, we've already checked that old_args
86833446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
86843446Smrj 			 * a space a the string termination character.
86853446Smrj 			 */
86863446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
86873446Smrj 			    strlen(old_args) + 2;
86883446Smrj 			new_arg = s_calloc(1, new_str_len);
86893446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
86903446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
86913446Smrj 			free(kernelp->arg);
86923446Smrj 			kernelp->arg = new_arg;
86933446Smrj 
86943446Smrj 			/*
86953446Smrj 			 * We have changed the kernel line, so we may need
86963446Smrj 			 * to update the archive line as well.
86973446Smrj 			 */
86983446Smrj 			set_archive_line(entryp, kernelp);
86996448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
87006448Svikram 			    fcn, kernelp->arg));
87013446Smrj 		} else {
87023446Smrj 			/*
87033446Smrj 			 * We're resetting the boot args to nothing, so
87043446Smrj 			 * we only need to copy the kernel.  We've already
87053446Smrj 			 * checked that the kernel is not the default.
87063446Smrj 			 */
87073446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
87083446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
87093446Smrj 			    kernelp->arg);
87103446Smrj 			free(kernelp->arg);
87113446Smrj 			kernelp->arg = new_arg;
87126448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
87136448Svikram 			    fcn, kernelp->arg));
87143446Smrj 		}
87153446Smrj 		rv = BAM_WRITE;
87163446Smrj 		goto done;
87173446Smrj 	}
87183446Smrj 
87193446Smrj 	/*
87203446Smrj 	 * Expand the kernel file to a full path, if necessary
87213446Smrj 	 */
87223446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
87233446Smrj 		new_path = expand_path(path);
87243446Smrj 		if (new_path == NULL) {
87254346Srscott 			bam_error(UNKNOWN_KERNEL, path);
87266448Svikram 			BAM_DPRINTF((D_RETURN_FAILURE, fcn));
87273446Smrj 			return (BAM_ERROR);
87283446Smrj 		}
87293446Smrj 		free_new_path = 1;
87303446Smrj 	} else {
87313446Smrj 		new_path = path;
87323446Smrj 		free_new_path = 0;
87333446Smrj 	}
87343446Smrj 
87353446Smrj 	/*
87363446Smrj 	 * At this point, we know we're setting a new value.  First, take care
87373446Smrj 	 * of the case where there was no previous entry.
87383446Smrj 	 */
87393446Smrj 	if (entryp == NULL) {
87406448Svikram 
87413446Smrj 		/* Similar to code in update_temp */
87426448Svikram 		fstype = get_fstype("/");
87436448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
87446448Svikram 		if (fstype == NULL) {
87456448Svikram 			bam_error(BOOTENV_FSTYPE_FAILED);
87466448Svikram 			rv = BAM_ERROR;
87476448Svikram 			goto done;
87486448Svikram 		}
87496448Svikram 
87506448Svikram 		osdev = get_special("/");
87516448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
87526448Svikram 		if (osdev == NULL) {
87536448Svikram 			free(fstype);
87546448Svikram 			bam_error(BOOTENV_SPECIAL_FAILED);
87553446Smrj 			rv = BAM_ERROR;
87563446Smrj 			goto done;
87573446Smrj 		}
87586448Svikram 
87596448Svikram 		sign = find_existing_sign("/", osdev, fstype);
87606448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
87616448Svikram 		if (sign == NULL) {
87626448Svikram 			free(fstype);
87636448Svikram 			free(osdev);
87646448Svikram 			bam_error(BOOTENV_SIGN_FAILED);
87656448Svikram 			rv = BAM_ERROR;
87666448Svikram 			goto done;
87676448Svikram 		}
87686448Svikram 
87696448Svikram 		free(fstype);
87706448Svikram 		free(osdev);
87716448Svikram 		(void) strlcpy(signbuf, sign, sizeof (signbuf));
87726448Svikram 		free(sign);
87736448Svikram 		assert(strchr(signbuf, '(') == NULL &&
87746448Svikram 		    strchr(signbuf, ',') == NULL &&
87756448Svikram 		    strchr(signbuf, ')') == NULL);
87766448Svikram 
87773446Smrj 		if (optnum == KERNEL_CMD) {
87786448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, new_path));
87793446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
87806448Svikram 			    signbuf, new_path, NULL, NULL);
87813446Smrj 		} else {
87823446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
87833446Smrj 			    strlen(path) + 8;
87843446Smrj 			new_arg = s_calloc(1, new_str_len);
87853446Smrj 
87863446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
87873446Smrj 			    DIRECT_BOOT_KERNEL, path);
87886448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
87893446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
87906448Svikram 			    signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
87916448Svikram 			free(new_arg);
87926448Svikram 		}
87936448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
87946448Svikram 		    entryNum = BAM_ERROR);
87956448Svikram 		if (entryNum == BAM_ERROR) {
87966448Svikram 			bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
87976448Svikram 			    BOOTENV_RC_TITLE);
87986448Svikram 			rv = BAM_ERROR;
87996448Svikram 			goto done;
88003446Smrj 		}
88013446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
88026448Svikram 		ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
88036448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
88046448Svikram 		if (ret == BAM_ERROR) {
88056448Svikram 			bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
88066448Svikram 		}
88073446Smrj 		rv = BAM_WRITE;
88083446Smrj 		goto done;
88093446Smrj 	}
88103446Smrj 
88113446Smrj 	/*
88123446Smrj 	 * There was already an bootenv entry which we need to edit.
88133446Smrj 	 */
88143446Smrj 	if (optnum == KERNEL_CMD) {
88153446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
88163446Smrj 		new_arg = s_calloc(1, new_str_len);
88173446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
88183446Smrj 		    old_args);
88193446Smrj 		free(kernelp->arg);
88203446Smrj 		kernelp->arg = new_arg;
88213446Smrj 
88223446Smrj 		/*
88233446Smrj 		 * If we have changed the kernel line, we may need to update
88243446Smrj 		 * the archive line as well.
88253446Smrj 		 */
88263446Smrj 		set_archive_line(entryp, kernelp);
88276448Svikram 		BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
88286448Svikram 		    kernelp->arg));
88293446Smrj 	} else {
88303446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
88313446Smrj 		new_arg = s_calloc(1, new_str_len);
88323446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
88333446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
88343446Smrj 		(void) strlcat(new_arg, path, new_str_len);
88353446Smrj 		free(kernelp->arg);
88363446Smrj 		kernelp->arg = new_arg;
88376448Svikram 		BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
88386448Svikram 		    kernelp->arg));
88393446Smrj 	}
88403446Smrj 	rv = BAM_WRITE;
88413446Smrj 
88423446Smrj done:
88433446Smrj 	if ((rv == BAM_WRITE) && kernelp)
88443446Smrj 		update_line(kernelp);
88453446Smrj 	if (free_new_path)
88463446Smrj 		free(new_path);
88476448Svikram 	if (rv == BAM_WRITE) {
88486448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
88496448Svikram 	} else {
88506448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
88516448Svikram 	}
88523446Smrj 	return (rv);
88533446Smrj }
88543446Smrj 
88556448Svikram static error_t
88566448Svikram get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
88576448Svikram {
88586448Svikram 	const char	*fcn = "get_kernel()";
88596448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
88606448Svikram 	return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
88616448Svikram }
88626448Svikram 
88636448Svikram static error_t
88646448Svikram set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
88656448Svikram {
88666448Svikram 	const char	*fcn = "set_kernel()";
88676448Svikram 	assert(path != NULL);
88686448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
88696448Svikram 	return (get_set_kernel(mp, optnum, path, buf, bufsize));
88706448Svikram }
88716448Svikram 
88720Sstevel@tonic-gate /*ARGSUSED*/
88730Sstevel@tonic-gate static error_t
88746448Svikram set_option(menu_t *mp, char *dummy, char *opt)
88756448Svikram {
88766448Svikram 	int		optnum;
88776448Svikram 	int		optval;
88786448Svikram 	char		*val;
88796448Svikram 	char		buf[BUFSIZ] = "";
88806448Svikram 	error_t		rv;
88816448Svikram 	const char	*fcn = "set_option()";
88820Sstevel@tonic-gate 
88830Sstevel@tonic-gate 	assert(mp);
88840Sstevel@tonic-gate 	assert(opt);
88856448Svikram 	assert(dummy == NULL);
88866448Svikram 
88876448Svikram 	/* opt is set from bam_argv[0] and is always non-NULL */
88886448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
88890Sstevel@tonic-gate 
88900Sstevel@tonic-gate 	val = strchr(opt, '=');
88913446Smrj 	if (val != NULL) {
88923446Smrj 		*val = '\0';
88930Sstevel@tonic-gate 	}
88940Sstevel@tonic-gate 
88950Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
88960Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
88970Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
88980Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
88993446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
89003446Smrj 		optnum = KERNEL_CMD;
89013446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
89023446Smrj 		optnum = ARGS_CMD;
89030Sstevel@tonic-gate 	} else {
89046448Svikram 		bam_error(INVALID_OPTION, opt);
89050Sstevel@tonic-gate 		return (BAM_ERROR);
89060Sstevel@tonic-gate 	}
89070Sstevel@tonic-gate 
89083446Smrj 	/*
89093446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
89103446Smrj 	 * others cause errors
89113446Smrj 	 */
89123446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
89136448Svikram 		bam_error(NO_OPTION_ARG, opt);
89143446Smrj 		return (BAM_ERROR);
89153446Smrj 	} else if (val != NULL) {
89163446Smrj 		*val = '=';
89173446Smrj 	}
89183446Smrj 
89193446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
89206448Svikram 		BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
89216448Svikram 		    val ? val + 1 : "NULL"));
89226448Svikram 
89236448Svikram 		if (val)
89246448Svikram 			rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
89256448Svikram 		else
89266448Svikram 			rv = get_kernel(mp, optnum, buf, sizeof (buf));
89273446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
89283446Smrj 			(void) printf("%s\n", buf);
89293446Smrj 	} else {
89303446Smrj 		optval = s_strtol(val + 1);
89316448Svikram 		BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
89326448Svikram 		rv = set_global(mp, menu_cmds[optnum], optval);
89336448Svikram 	}
89346448Svikram 
89356448Svikram 	if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
89366448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
89376448Svikram 	} else {
89386448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
89396448Svikram 	}
89406448Svikram 
89416448Svikram 	return (rv);
89420Sstevel@tonic-gate }
89430Sstevel@tonic-gate 
89440Sstevel@tonic-gate /*
89450Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
89460Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
89470Sstevel@tonic-gate  */
89480Sstevel@tonic-gate static error_t
89490Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
89500Sstevel@tonic-gate {
89510Sstevel@tonic-gate 	line_t *lp;
89520Sstevel@tonic-gate 	char *arg;
89530Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
89540Sstevel@tonic-gate 
89550Sstevel@tonic-gate 	assert(mp);
89560Sstevel@tonic-gate 	assert(menu_path);
89570Sstevel@tonic-gate 	assert(globalcmd);
89580Sstevel@tonic-gate 
89590Sstevel@tonic-gate 	if (mp->start == NULL) {
89600Sstevel@tonic-gate 		if (!quiet)
89610Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
89620Sstevel@tonic-gate 		return (BAM_ERROR);
89630Sstevel@tonic-gate 	}
89640Sstevel@tonic-gate 
89650Sstevel@tonic-gate 	done = 0;
89660Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
89670Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
89680Sstevel@tonic-gate 			continue;
89690Sstevel@tonic-gate 
89700Sstevel@tonic-gate 		if (lp->cmd == NULL) {
89710Sstevel@tonic-gate 			if (!quiet)
89720Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
89730Sstevel@tonic-gate 			continue;
89740Sstevel@tonic-gate 		}
89750Sstevel@tonic-gate 
89760Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
89770Sstevel@tonic-gate 			continue;
89780Sstevel@tonic-gate 
89790Sstevel@tonic-gate 		/* Found global. Check for duplicates */
89800Sstevel@tonic-gate 		if (done && !quiet) {
89810Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
89820Sstevel@tonic-gate 			ret = BAM_ERROR;
89830Sstevel@tonic-gate 		}
89840Sstevel@tonic-gate 
89850Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
89860Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
89870Sstevel@tonic-gate 		done = 1;
89880Sstevel@tonic-gate 	}
89890Sstevel@tonic-gate 
89900Sstevel@tonic-gate 	if (!done && bam_verbose)
89910Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
89920Sstevel@tonic-gate 
89930Sstevel@tonic-gate 	return (ret);
89940Sstevel@tonic-gate }
89950Sstevel@tonic-gate 
89960Sstevel@tonic-gate static error_t
89970Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
89980Sstevel@tonic-gate {
89996448Svikram 	const char *fcn = "menu_write()";
90006448Svikram 
90016448Svikram 	BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
90020Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
90030Sstevel@tonic-gate }
90040Sstevel@tonic-gate 
90056448Svikram void
90060Sstevel@tonic-gate line_free(line_t *lp)
90070Sstevel@tonic-gate {
90080Sstevel@tonic-gate 	if (lp == NULL)
90090Sstevel@tonic-gate 		return;
90100Sstevel@tonic-gate 
90110Sstevel@tonic-gate 	if (lp->cmd)
90120Sstevel@tonic-gate 		free(lp->cmd);
90130Sstevel@tonic-gate 	if (lp->sep)
90140Sstevel@tonic-gate 		free(lp->sep);
90150Sstevel@tonic-gate 	if (lp->arg)
90160Sstevel@tonic-gate 		free(lp->arg);
90170Sstevel@tonic-gate 	if (lp->line)
90180Sstevel@tonic-gate 		free(lp->line);
90190Sstevel@tonic-gate 	free(lp);
90200Sstevel@tonic-gate }
90210Sstevel@tonic-gate 
90220Sstevel@tonic-gate static void
90230Sstevel@tonic-gate linelist_free(line_t *start)
90240Sstevel@tonic-gate {
90250Sstevel@tonic-gate 	line_t *lp;
90260Sstevel@tonic-gate 
90270Sstevel@tonic-gate 	while (start) {
90280Sstevel@tonic-gate 		lp = start;
90290Sstevel@tonic-gate 		start = start->next;
90300Sstevel@tonic-gate 		line_free(lp);
90310Sstevel@tonic-gate 	}
90320Sstevel@tonic-gate }
90330Sstevel@tonic-gate 
90340Sstevel@tonic-gate static void
90350Sstevel@tonic-gate filelist_free(filelist_t *flistp)
90360Sstevel@tonic-gate {
90370Sstevel@tonic-gate 	linelist_free(flistp->head);
90380Sstevel@tonic-gate 	flistp->head = NULL;
90390Sstevel@tonic-gate 	flistp->tail = NULL;
90400Sstevel@tonic-gate }
90410Sstevel@tonic-gate 
90420Sstevel@tonic-gate static void
90430Sstevel@tonic-gate menu_free(menu_t *mp)
90440Sstevel@tonic-gate {
9045662Sszhou 	entry_t *ent, *tmp;
90460Sstevel@tonic-gate 	assert(mp);
90470Sstevel@tonic-gate 
90480Sstevel@tonic-gate 	if (mp->start)
90490Sstevel@tonic-gate 		linelist_free(mp->start);
9050662Sszhou 	ent = mp->entries;
9051662Sszhou 	while (ent) {
9052662Sszhou 		tmp = ent;
9053662Sszhou 		ent = tmp->next;
9054662Sszhou 		free(tmp);
9055662Sszhou 	}
9056662Sszhou 
90570Sstevel@tonic-gate 	free(mp);
90580Sstevel@tonic-gate }
90590Sstevel@tonic-gate 
90600Sstevel@tonic-gate /*
90610Sstevel@tonic-gate  * Utility routines
90620Sstevel@tonic-gate  */
90630Sstevel@tonic-gate 
90640Sstevel@tonic-gate 
90650Sstevel@tonic-gate /*
90660Sstevel@tonic-gate  * Returns 0 on success
90670Sstevel@tonic-gate  * Any other value indicates an error
90680Sstevel@tonic-gate  */
90690Sstevel@tonic-gate static int
90705648Ssetje exec_cmd(char *cmdline, filelist_t *flistp)
90710Sstevel@tonic-gate {
90720Sstevel@tonic-gate 	char buf[BUFSIZ];
90730Sstevel@tonic-gate 	int ret;
90740Sstevel@tonic-gate 	FILE *ptr;
90750Sstevel@tonic-gate 	sigset_t set;
90760Sstevel@tonic-gate 	void (*disp)(int);
90770Sstevel@tonic-gate 
90780Sstevel@tonic-gate 	/*
90790Sstevel@tonic-gate 	 * For security
90800Sstevel@tonic-gate 	 * - only absolute paths are allowed
90810Sstevel@tonic-gate 	 * - set IFS to space and tab
90820Sstevel@tonic-gate 	 */
90830Sstevel@tonic-gate 	if (*cmdline != '/') {
90840Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
90850Sstevel@tonic-gate 		return (-1);
90860Sstevel@tonic-gate 	}
90870Sstevel@tonic-gate 	(void) putenv("IFS= \t");
90880Sstevel@tonic-gate 
90890Sstevel@tonic-gate 	/*
90900Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
90910Sstevel@tonic-gate 	 * unblock it here
90920Sstevel@tonic-gate 	 */
90930Sstevel@tonic-gate 	(void) sigemptyset(&set);
90940Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
90950Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
90960Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
90970Sstevel@tonic-gate 		return (-1);
90980Sstevel@tonic-gate 	}
90990Sstevel@tonic-gate 
91000Sstevel@tonic-gate 	/*
91010Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
91020Sstevel@tonic-gate 	 */
91030Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
91040Sstevel@tonic-gate 	if (disp == SIG_ERR) {
91050Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
91060Sstevel@tonic-gate 		return (-1);
91070Sstevel@tonic-gate 	}
91080Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
91090Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
91100Sstevel@tonic-gate 		return (-1);
91110Sstevel@tonic-gate 	}
91120Sstevel@tonic-gate 
91130Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
91140Sstevel@tonic-gate 	if (ptr == NULL) {
91150Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
91160Sstevel@tonic-gate 		return (-1);
91170Sstevel@tonic-gate 	}
91180Sstevel@tonic-gate 
91190Sstevel@tonic-gate 	/*
91200Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
91210Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
91220Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
91230Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
91240Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
91250Sstevel@tonic-gate 	 * there is no reader process and the command dies with
91260Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
91270Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
91280Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
91290Sstevel@tonic-gate 	 * pclose().
91300Sstevel@tonic-gate 	 *
91310Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
91320Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
91330Sstevel@tonic-gate 	 * from the value returned by pclose()
91340Sstevel@tonic-gate 	 */
91355648Ssetje 	while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
91365648Ssetje 		if (flistp == NULL) {
91375648Ssetje 			/* s_fgets strips newlines, so insert them at the end */
91385648Ssetje 			bam_print(PRINT, buf);
91395648Ssetje 		} else {
91405648Ssetje 			append_to_flist(flistp, buf);
91410Sstevel@tonic-gate 		}
91420Sstevel@tonic-gate 	}
91430Sstevel@tonic-gate 
91440Sstevel@tonic-gate 	ret = pclose(ptr);
91450Sstevel@tonic-gate 	if (ret == -1) {
91460Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
91470Sstevel@tonic-gate 		return (-1);
91480Sstevel@tonic-gate 	}
91490Sstevel@tonic-gate 
91500Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
91510Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
91520Sstevel@tonic-gate 	} else {
91530Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
91540Sstevel@tonic-gate 		return (-1);
91550Sstevel@tonic-gate 	}
91560Sstevel@tonic-gate }
91570Sstevel@tonic-gate 
91580Sstevel@tonic-gate /*
91590Sstevel@tonic-gate  * Since this function returns -1 on error
91600Sstevel@tonic-gate  * it cannot be used to convert -1. However,
91610Sstevel@tonic-gate  * that is sufficient for what we need.
91620Sstevel@tonic-gate  */
91630Sstevel@tonic-gate static long
91640Sstevel@tonic-gate s_strtol(char *str)
91650Sstevel@tonic-gate {
91660Sstevel@tonic-gate 	long l;
91670Sstevel@tonic-gate 	char *res = NULL;
91680Sstevel@tonic-gate 
91690Sstevel@tonic-gate 	if (str == NULL) {
91700Sstevel@tonic-gate 		return (-1);
91710Sstevel@tonic-gate 	}
91720Sstevel@tonic-gate 
91730Sstevel@tonic-gate 	errno = 0;
91740Sstevel@tonic-gate 	l = strtol(str, &res, 10);
91750Sstevel@tonic-gate 	if (errno || *res != '\0') {
91760Sstevel@tonic-gate 		return (-1);
91770Sstevel@tonic-gate 	}
91780Sstevel@tonic-gate 
91790Sstevel@tonic-gate 	return (l);
91800Sstevel@tonic-gate }
91810Sstevel@tonic-gate 
91820Sstevel@tonic-gate /*
91830Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
91840Sstevel@tonic-gate  */
91850Sstevel@tonic-gate static int
91860Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
91870Sstevel@tonic-gate {
91880Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
91890Sstevel@tonic-gate 
91900Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
91910Sstevel@tonic-gate 	return (fputs(linebuf, fp));
91920Sstevel@tonic-gate }
91930Sstevel@tonic-gate 
91940Sstevel@tonic-gate /*
91950Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
91960Sstevel@tonic-gate  */
91973446Smrj char *
91980Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
91990Sstevel@tonic-gate {
92000Sstevel@tonic-gate 	int n;
92010Sstevel@tonic-gate 
92020Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
92030Sstevel@tonic-gate 	if (buf) {
92040Sstevel@tonic-gate 		n = strlen(buf);
92050Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
92060Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
92070Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
92080Sstevel@tonic-gate 	}
92090Sstevel@tonic-gate 
92100Sstevel@tonic-gate 	return (buf);
92110Sstevel@tonic-gate }
92120Sstevel@tonic-gate 
92133446Smrj void *
92140Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
92150Sstevel@tonic-gate {
92160Sstevel@tonic-gate 	void *ptr;
92170Sstevel@tonic-gate 
92180Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
92190Sstevel@tonic-gate 	if (ptr == NULL) {
92200Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
92210Sstevel@tonic-gate 		bam_exit(1);
92220Sstevel@tonic-gate 	}
92230Sstevel@tonic-gate 	return (ptr);
92240Sstevel@tonic-gate }
92250Sstevel@tonic-gate 
92263446Smrj void *
92273446Smrj s_realloc(void *ptr, size_t sz)
92283446Smrj {
92293446Smrj 	ptr = realloc(ptr, sz);
92303446Smrj 	if (ptr == NULL) {
92313446Smrj 		bam_error(NO_MEM, sz);
92323446Smrj 		bam_exit(1);
92333446Smrj 	}
92343446Smrj 	return (ptr);
92353446Smrj }
92363446Smrj 
92376448Svikram char *
92380Sstevel@tonic-gate s_strdup(char *str)
92390Sstevel@tonic-gate {
92400Sstevel@tonic-gate 	char *ptr;
92410Sstevel@tonic-gate 
92420Sstevel@tonic-gate 	if (str == NULL)
92430Sstevel@tonic-gate 		return (NULL);
92440Sstevel@tonic-gate 
92450Sstevel@tonic-gate 	ptr = strdup(str);
92460Sstevel@tonic-gate 	if (ptr == NULL) {
92470Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
92480Sstevel@tonic-gate 		bam_exit(1);
92490Sstevel@tonic-gate 	}
92500Sstevel@tonic-gate 	return (ptr);
92510Sstevel@tonic-gate }
92520Sstevel@tonic-gate 
92530Sstevel@tonic-gate /*
92540Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
92550Sstevel@tonic-gate  * Returns 0 otherwise
92560Sstevel@tonic-gate  */
92570Sstevel@tonic-gate static int
92580Sstevel@tonic-gate is_amd64(void)
92590Sstevel@tonic-gate {
92600Sstevel@tonic-gate 	static int amd64 = -1;
92610Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
92620Sstevel@tonic-gate 
92630Sstevel@tonic-gate 	if (amd64 != -1)
92640Sstevel@tonic-gate 		return (amd64);
92650Sstevel@tonic-gate 
92666319Sjg 	if (bam_alt_platform) {
92676319Sjg 		if (strcmp(bam_platform, "i86pc") == 0) {
92686319Sjg 			amd64 = 1;		/* diskless server */
92696319Sjg 		}
92706319Sjg 	} else {
92716319Sjg 		if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
92726319Sjg 		    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
92736319Sjg 			amd64 = 1;
92746319Sjg 		} else if (strstr(isabuf, "i386") == NULL) {
92756319Sjg 			amd64 = 1;		/* diskless server */
92766319Sjg 		}
92776319Sjg 	}
92786319Sjg 	if (amd64 == -1)
92790Sstevel@tonic-gate 		amd64 = 0;
92800Sstevel@tonic-gate 
92810Sstevel@tonic-gate 	return (amd64);
92820Sstevel@tonic-gate }
92830Sstevel@tonic-gate 
92846582Ssetje static char *
92856582Ssetje get_machine(void)
92866582Ssetje {
92876582Ssetje 	static int cached = -1;
92886582Ssetje 	static char mbuf[257];	/* from sysinfo(2) manpage */
92896582Ssetje 
92906582Ssetje 	if (cached == 0)
92916582Ssetje 		return (mbuf);
92926319Sjg 
92936319Sjg 	if (bam_alt_platform) {
92946582Ssetje 		return (bam_platform);
92956319Sjg 	} else {
92966582Ssetje 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
92976582Ssetje 			cached = 1;
92986582Ssetje 		}
92996582Ssetje 	}
93006582Ssetje 	if (cached == -1) {
93016582Ssetje 		mbuf[0] = '\0';
93026582Ssetje 		cached = 0;
93036582Ssetje 	}
93046582Ssetje 
93056582Ssetje 	return (mbuf);
93065648Ssetje }
93075648Ssetje 
93086448Svikram int
93096448Svikram is_sparc(void)
93106448Svikram {
93116582Ssetje 	static int issparc = -1;
93126582Ssetje 	char mbuf[257];	/* from sysinfo(2) manpage */
93136582Ssetje 
93146582Ssetje 	if (issparc != -1)
93156582Ssetje 		return (issparc);
93166582Ssetje 
93176582Ssetje 	if (bam_alt_platform) {
93186582Ssetje 		if (strncmp(bam_platform, "sun4", 4) == 0) {
93196582Ssetje 			issparc = 1;
93206582Ssetje 		}
93216582Ssetje 	} else {
93226582Ssetje 		if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
93237111Sjg 		    strcmp(mbuf, "sparc") == 0) {
93246582Ssetje 			issparc = 1;
93257111Sjg 		}
93267111Sjg 	}
93277111Sjg 	if (issparc == -1)
93287111Sjg 		issparc = 0;
93296582Ssetje 
93306582Ssetje 	return (issparc);
93316448Svikram }
93325648Ssetje 
93330Sstevel@tonic-gate static void
93340Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
93350Sstevel@tonic-gate {
93360Sstevel@tonic-gate 	line_t *lp;
93370Sstevel@tonic-gate 
93380Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
93390Sstevel@tonic-gate 	lp->line = s_strdup(s);
93400Sstevel@tonic-gate 	if (flistp->head == NULL)
93410Sstevel@tonic-gate 		flistp->head = lp;
93420Sstevel@tonic-gate 	else
93430Sstevel@tonic-gate 		flistp->tail->next = lp;
93440Sstevel@tonic-gate 	flistp->tail = lp;
93450Sstevel@tonic-gate }
93464581Ssherrym 
93475648Ssetje #if !defined(_OPB)
93484581Ssherrym 
93494581Ssherrym UCODE_VENDORS;
93504581Ssherrym 
93514581Ssherrym /*ARGSUSED*/
93524581Ssherrym static void
93534581Ssherrym ucode_install(char *root)
93544581Ssherrym {
93554581Ssherrym 	int i;
93564581Ssherrym 
93574581Ssherrym 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
93584581Ssherrym 		int cmd_len = PATH_MAX + 256;
93594581Ssherrym 		char cmd[PATH_MAX + 256];
93604581Ssherrym 		char file[PATH_MAX];
93614581Ssherrym 		char timestamp[PATH_MAX];
93624581Ssherrym 		struct stat fstatus, tstatus;
93634581Ssherrym 		struct utimbuf u_times;
93644581Ssherrym 
93657605SMark.Johnson@Sun.COM 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
93667605SMark.Johnson@Sun.COM 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
93677605SMark.Johnson@Sun.COM 		    ucode_vendors[i].extstr);
93684581Ssherrym 
93694581Ssherrym 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
93704581Ssherrym 			continue;
93714581Ssherrym 
93724581Ssherrym 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
93734581Ssherrym 
93744581Ssherrym 		if (stat(timestamp, &tstatus) == 0 &&
93754581Ssherrym 		    fstatus.st_mtime <= tstatus.st_mtime)
93764581Ssherrym 			continue;
93774581Ssherrym 
93784581Ssherrym 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
93794581Ssherrym 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
93804581Ssherrym 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
93814581Ssherrym 		if (system(cmd) != 0)
93824581Ssherrym 			return;
93834581Ssherrym 
93844581Ssherrym 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
93854581Ssherrym 			return;
93864581Ssherrym 
93874581Ssherrym 		u_times.actime = fstatus.st_atime;
93884581Ssherrym 		u_times.modtime = fstatus.st_mtime;
93894581Ssherrym 		(void) utime(timestamp, &u_times);
93904581Ssherrym 	}
93914581Ssherrym }
93924581Ssherrym #endif
9393