4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 * Copyright (c) 2015 by Delphix. All rights reserved.
26 * Copyright 2016 Toomas Soome <tsoome@me.com>
27 * Copyright 2016 Nexenta Systems, Inc.
31 * bootadm(1M) is a new utility for managing bootability of
32 * Solaris *Newboot* environments. It has two primary tasks:
33 * - Allow end users to manage bootability of Newboot Solaris instances
34 * - Provide services to other subsystems in Solaris (primarily Install)
43 #include <sys/types.h>
50 #include <sys/mnttab.h>
51 #include <sys/mntent.h>
52 #include <sys/statvfs.h>
53 #include <libnvpair.h>
58 #include <sys/systeminfo.h>
59 #include <sys/dktp/fdisk.h>
60 #include <sys/param.h>
64 #include <sys/sysmacros.h>
68 #include <sys/lockfs.h>
69 #include <sys/filio.h>
77 #include <sys/ucode.h>
82 #include <device_info.h>
84 #include <sys/efi_partition.h>
87 #include <sys/mkdev.h>
92 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
93 #endif /* TEXT_DOMAIN */
95 /* Type definitions */
104 #define LINE_INIT 0 /* lineNum initial value */
105 #define ENTRY_INIT -1 /* entryNum initial value */
106 #define ALL_ENTRIES -2 /* selects all boot entries */
108 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
109 #define PARTNO_EFI -2 /* EFI partition table found */
111 #define GRUB_DIR "/boot/grub"
112 #define GRUB_STAGE2 GRUB_DIR "/stage2"
113 #define GRUB_MENU "/boot/grub/menu.lst"
114 #define MENU_TMP "/boot/grub/menu.lst.tmp"
115 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
116 #define RAMDISK_SPECIAL "/devices/ramdisk"
117 #define STUBBOOT "/stubboot"
118 #define MULTIBOOT "/platform/i86pc/multiboot"
119 #define GRUBSIGN_DIR "/boot/grub/bootsign"
120 #define GRUBSIGN_BACKUP "/etc/bootsign"
121 #define GRUBSIGN_UFS_PREFIX "rootfs"
122 #define GRUBSIGN_ZFS_PREFIX "pool_"
123 #define GRUBSIGN_LU_PREFIX "BE_"
124 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
125 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
128 #define BE_DEFAULTS "/etc/default/be"
129 #define BE_DFLT_BE_HAS_GRUB "BE_HAS_GRUB="
131 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
134 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
135 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
137 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
138 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
139 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
140 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
142 #define GRUB_slice "/etc/lu/GRUB_slice"
143 #define GRUB_root "/etc/lu/GRUB_root"
144 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
145 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
146 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
147 #define LULIB "/usr/lib/lu/lulib"
148 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
149 #define CKSUM "/usr/bin/cksum"
150 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
151 #define BOOTADM "/sbin/bootadm"
153 #define INSTALLGRUB "/sbin/installgrub"
154 #define STAGE1 "/boot/grub/stage1"
155 #define STAGE2 "/boot/grub/stage2"
158 * Default file attributes
160 #define DEFAULT_DEV_MODE 0644 /* default permissions */
161 #define DEFAULT_DEV_UID 0 /* user root */
162 #define DEFAULT_DEV_GID 3 /* group sys */
166 * menu_cmd_t and menu_cmds must be kept in sync
168 char *menu_cmds
[] = {
169 "default", /* DEFAULT_CMD */
170 "timeout", /* TIMEOUT_CMD */
171 "title", /* TITLE_CMD */
172 "root", /* ROOT_CMD */
173 "kernel", /* KERNEL_CMD */
174 "kernel$", /* KERNEL_DOLLAR_CMD */
175 "module", /* MODULE_CMD */
176 "module$", /* MODULE_DOLLAR_CMD */
178 "#", /* COMMENT_CMD */
179 "chainloader", /* CHAINLOADER_CMD */
180 "args", /* ARGS_CMD */
181 "findroot", /* FINDROOT_CMD */
182 "bootfs", /* BOOTFS_CMD */
186 #define OPT_ENTRY_NUM "entry"
196 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
197 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
199 #define FILE_STAT "boot/solaris/filestat.ramdisk"
200 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
201 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
202 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
204 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
211 static subcmd_t bam_cmd
;
214 static int bam_root_readonly
;
216 static int bam_extend
= 0;
217 static int bam_purge
= 0;
218 static char *bam_subcmd
;
219 static char *bam_opt
;
220 static char **bam_argv
;
221 static char *bam_pool
;
223 static int bam_check
;
224 static int bam_saved_check
;
225 static int bam_smf_check
;
226 static int bam_lock_fd
= -1;
229 char rootbuf
[PATH_MAX
] = "/";
230 static int bam_update_all
;
231 static int bam_alt_platform
;
232 static char *bam_platform
;
233 static char *bam_home_env
= NULL
;
235 /* function prototypes */
236 static void parse_args_internal(int, char *[]);
237 static void parse_args(int, char *argv
[]);
238 static error_t
bam_menu(char *, char *, int, char *[]);
239 static error_t
bam_install(char *, char *);
240 static error_t
bam_archive(char *, char *);
242 static void bam_lock(void);
243 static void bam_unlock(void);
245 static int exec_cmd(char *, filelist_t
*);
246 static error_t
read_globals(menu_t
*, char *, char *, int);
247 static int menu_on_bootdisk(char *os_root
, char *menu_root
);
248 static menu_t
*menu_read(char *);
249 static error_t
menu_write(char *, menu_t
*);
250 static void linelist_free(line_t
*);
251 static void menu_free(menu_t
*);
252 static void filelist_free(filelist_t
*);
253 static error_t
list2file(char *, char *, char *, line_t
*);
254 static error_t
list_entry(menu_t
*, char *, char *);
255 static error_t
list_setting(menu_t
*, char *, char *);
256 static error_t
delete_all_entries(menu_t
*, char *, char *);
257 static error_t
update_entry(menu_t
*mp
, char *menu_root
, char *opt
);
258 static error_t
update_temp(menu_t
*mp
, char *dummy
, char *opt
);
260 static error_t
install_bootloader(void);
261 static error_t
update_archive(char *, char *);
262 static error_t
list_archive(char *, char *);
263 static error_t
update_all(char *, char *);
264 static error_t
read_list(char *, filelist_t
*);
265 static error_t
set_option(menu_t
*, char *, char *);
266 static error_t
set_kernel(menu_t
*, menu_cmd_t
, char *, char *, size_t);
267 static error_t
get_kernel(menu_t
*, menu_cmd_t
, char *, size_t);
268 static char *expand_path(const char *);
270 static long s_strtol(char *);
271 static int s_fputs(char *, FILE *);
273 static int is_amd64(void);
274 static char *get_machine(void);
275 static void append_to_flist(filelist_t
*, char *);
276 static int ufs_add_to_sign_list(char *sign
);
277 static error_t
synchronize_BE_menu(void);
280 static void ucode_install();
283 /* Menu related sub commands */
284 static subcmd_defn_t menu_subcmds
[] = {
285 "set_option", OPT_ABSENT
, set_option
, 0, /* PUB */
286 "list_entry", OPT_OPTIONAL
, list_entry
, 1, /* PUB */
287 "delete_all_entries", OPT_ABSENT
, delete_all_entries
, 0, /* PVT */
288 "update_entry", OPT_REQ
, update_entry
, 0, /* menu */
289 "update_temp", OPT_OPTIONAL
, update_temp
, 0, /* reboot */
290 "upgrade", OPT_ABSENT
, upgrade_menu
, 0, /* menu */
291 "list_setting", OPT_OPTIONAL
, list_setting
, 1, /* menu */
292 "disable_hypervisor", OPT_ABSENT
, cvt_to_metal
, 0, /* menu */
293 "enable_hypervisor", OPT_ABSENT
, cvt_to_hyper
, 0, /* menu */
294 NULL
, 0, NULL
, 0 /* must be last */
297 /* Archive related sub commands */
298 static subcmd_defn_t arch_subcmds
[] = {
299 "update", OPT_ABSENT
, update_archive
, 0, /* PUB */
300 "update_all", OPT_ABSENT
, update_all
, 0, /* PVT */
301 "list", OPT_OPTIONAL
, list_archive
, 1, /* PUB */
302 NULL
, 0, NULL
, 0 /* must be last */
305 /* Install related sub commands */
306 static subcmd_defn_t inst_subcmds
[] = {
307 "install_bootloader", OPT_ABSENT
, install_bootloader
, 0, /* PUB */
308 NULL
, 0, NULL
, 0 /* must be last */
311 enum dircache_copy_opt
{
318 * Directory specific flags:
319 * NEED_UPDATE : the specified archive needs to be updated
320 * NO_MULTI : don't extend the specified archive, but recreate it
322 #define NEED_UPDATE 0x00000001
323 #define NO_MULTI 0x00000002
325 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
326 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
327 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
329 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
330 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
331 #define get_count(id) (walk_arg.dirinfo[id].count)
332 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
333 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
336 * dirinfo_t (specific cache directory information):
337 * cdir_path: path to the archive cache directory
338 * update_path: path to the update directory (contains the files that will be
339 * used to extend the archive)
340 * has_dir: the specified cache directory is active
341 * count: the number of files to update
342 * flags: directory specific flags
344 typedef struct _dirinfo
{
345 char cdir_path
[PATH_MAX
];
346 char update_path
[PATH_MAX
];
354 * NEED_CACHE_DIR : cache directory is missing and needs to be created
355 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
356 * UPDATE_ERROR : an error occourred while traversing the list of files
357 * RDONLY_FSCHK : the target filesystem is read-only
358 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
360 #define NEED_CACHE_DIR 0x00000001
361 #define IS_SPARC_TARGET 0x00000002
362 #define UPDATE_ERROR 0x00000004
363 #define RDONLY_FSCHK 0x00000008
364 #define INVALIDATE_CACHE 0x00000010
366 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
367 #define set_flag(flag) (walk_arg.update_flags |= flag)
368 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
372 * update_flags: flags related to the current updating process
373 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
374 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
381 dirinfo_t dirinfo
[CACHEDIR_NUM
];
386 struct safefile
*next
;
389 static struct safefile
*safefiles
= NULL
;
392 * svc:/system/filesystem/usr:default service checks for this file and
393 * does a boot archive update and then reboot the system.
395 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
398 * svc:/system/boot-archive-update:default checks for this file and
399 * updates the boot archive.
401 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
403 /* Thanks growisofs */
404 #define CD_BLOCK ((off64_t)2048)
405 #define VOLDESC_OFF 16
406 #define DVD_BLOCK (32*1024)
410 unsigned char type
[1];
411 unsigned char id
[5];
412 unsigned char void1
[80-5-1];
413 unsigned char volume_space_size
[8];
414 unsigned char void2
[2048-80-8];
418 * COUNT_MAX: maximum number of changed files to justify a multisession update
419 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
423 #define BA_SIZE_MAX (50 * 1024 * 1024)
425 #define bam_nowrite() (bam_check || bam_smf_check)
427 static int sync_menu
= 1; /* whether we need to sync the BE menus */
432 (void) fprintf(stderr
, "USAGE:\n");
435 (void) fprintf(stderr
,
436 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog
);
437 (void) fprintf(stderr
,
438 "\t%s list-archive [-R altroot [-p platform]]\n", prog
);
440 (void) fprintf(stderr
,
441 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog
);
443 (void) fprintf(stderr
,
444 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog
);
448 (void) fprintf(stderr
, "\t%s set-menu [-R altroot] key=value\n", prog
);
449 (void) fprintf(stderr
, "\t%s list-menu [-R altroot]\n", prog
);
454 * Best effort attempt to restore the $HOME value.
459 char home_env
[PATH_MAX
];
462 (void) snprintf(home_env
, sizeof (home_env
), "HOME=%s",
464 (void) putenv(home_env
);
473 * Sanitize the environment in which bootadm will execute its sub-processes
474 * (ex. mkisofs). This is done to prevent those processes from attempting
475 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
476 * or, potentially, insecure.
483 /* don't depend on caller umask */
486 /* move away from a potential unsafe current working directory */
487 while (chdir("/") == -1) {
488 if (errno
!= EINTR
) {
489 bam_print("WARNING: unable to chdir to /");
494 bam_home_env
= getenv("HOME");
495 while (bam_home_env
!= NULL
&& putenv("HOME=/") == -1) {
496 if (errno
== ENOMEM
) {
497 /* retry no more than MAX_TRIES times */
498 if (++stry
> MAX_TRIES
) {
499 bam_print("WARNING: unable to recover from "
500 "system memory pressure... aborting \n");
501 bam_exit(EXIT_FAILURE
);
503 /* memory is tight, try to sleep */
504 bam_print("Attempting to recover from memory pressure: "
505 "sleeping for %d seconds\n", SLEEP_TIME
* stry
);
506 (void) sleep(SLEEP_TIME
* stry
);
508 bam_print("WARNING: unable to sanitize HOME\n");
514 main(int argc
, char *argv
[])
516 error_t ret
= BAM_SUCCESS
;
518 (void) setlocale(LC_ALL
, "");
519 (void) textdomain(TEXT_DOMAIN
);
521 if ((prog
= strrchr(argv
[0], '/')) == NULL
) {
527 INJECT_ERROR1("ASSERT_ON", assert(0))
531 parse_args(argc
, argv
);
535 if (is_grub(bam_alt_root
? bam_root
: "/")) {
536 ret
= bam_menu(bam_subcmd
, bam_opt
,
539 ret
= bam_loader_menu(bam_subcmd
, bam_opt
,
544 ret
= bam_archive(bam_subcmd
, bam_opt
);
547 ret
= bam_install(bam_subcmd
, bam_opt
);
554 if (ret
!= BAM_SUCCESS
)
555 bam_exit((ret
== BAM_NOCHANGE
) ? 2 : 1);
562 * Equivalence of public and internal commands:
563 * update-archive -- -a update
564 * list-archive -- -a list
565 * set-menu -- -m set_option
566 * list-menu -- -m list_entry
567 * update-menu -- -m update_entry
568 * install-bootloader -- -i install_bootloader
570 static struct cmd_map
{
575 { "update-archive", BAM_ARCHIVE
, "update"},
576 { "list-archive", BAM_ARCHIVE
, "list"},
577 { "set-menu", BAM_MENU
, "set_option"},
578 { "list-menu", BAM_MENU
, "list_entry"},
579 { "update-menu", BAM_MENU
, "update_entry"},
580 { "install-bootloader", BAM_INSTALL
, "install_bootloader"},
585 * Commands syntax published in bootadm(1M) are parsed here
588 parse_args(int argc
, char *argv
[])
590 struct cmd_map
*cmp
= cmd_map
;
592 /* command conforming to the final spec */
593 if (argc
> 1 && argv
[1][0] != '-') {
595 * Map commands to internal table.
597 while (cmp
->bam_cmdname
) {
598 if (strcmp(argv
[1], cmp
->bam_cmdname
) == 0) {
599 bam_cmd
= cmp
->bam_cmd
;
600 bam_subcmd
= cmp
->bam_subcmd
;
605 if (cmp
->bam_cmdname
== NULL
) {
613 parse_args_internal(argc
, argv
);
617 * A combination of public and private commands are parsed here.
618 * The internal syntax and the corresponding functionality are:
619 * -a update -- update-archive
620 * -a list -- list-archive
621 * -a update-all -- (reboot to sync all mnted OS archive)
622 * -i install_bootloader -- install-bootloader
623 * -m update_entry -- update-menu
624 * -m list_entry -- list-menu
625 * -m update_temp -- (reboot -- [boot-args])
626 * -m delete_all_entries -- (called from install)
627 * -m enable_hypervisor [args] -- cvt_to_hyper
628 * -m disable_hypervisor -- cvt_to_metal
629 * -m list_setting [entry] [value] -- list_setting
631 * A set of private flags is there too:
632 * -F -- purge the cache directories and rebuild them
633 * -e -- use the (faster) archive update approach (used by
637 parse_args_internal(int argc
, char *argv
[])
641 extern int optind
, opterr
;
643 const char *optstring
= "a:d:fi:m:no:veFCR:p:P:XZ";
645 const char *optstring
= "a:d:fi:m:no:veFCMR:p:P:XZ";
648 /* Suppress error message from getopt */
652 while ((c
= getopt(argc
, argv
, optstring
)) != -1) {
658 _("multiple commands specified: -%c\n"), c
);
660 bam_cmd
= BAM_ARCHIVE
;
667 _("duplicate options specified: -%c\n"), c
);
669 bam_debug
= s_strtol(optarg
);
681 _("multiple commands specified: -%c\n"), c
);
683 bam_cmd
= BAM_INSTALL
;
690 _("multiple commands specified: -%c\n"), c
);
703 * We save the original value of bam_check. The new
704 * approach in case of a read-only filesystem is to
705 * behave as a check, so we need a way to restore the
706 * original value after the evaluation of the read-only
707 * filesystem has been done.
708 * Even if we don't allow at the moment a check with
709 * update_all, this approach is more robust than
710 * simply resetting bam_check to zero.
718 _("duplicate options specified: -%c\n"), c
);
729 if (bam_pool
!= NULL
) {
732 _("duplicate options specified: -%c\n"), c
);
740 _("duplicate options specified: -%c\n"), c
);
742 } else if (realpath(optarg
, rootbuf
) == NULL
) {
744 bam_error(_("cannot resolve path %s: %s\n"),
745 optarg
, strerror(errno
));
750 bam_rootlen
= strlen(rootbuf
);
753 bam_alt_platform
= 1;
754 bam_platform
= optarg
;
755 if ((strcmp(bam_platform
, "i86pc") != 0) &&
756 (strcmp(bam_platform
, "sun4u") != 0) &&
757 (strcmp(bam_platform
, "sun4v") != 0)) {
759 bam_error(_("invalid platform %s - must be "
760 "one of sun4u, sun4v or i86pc\n"),
765 bam_is_hv
= BAM_HV_PRESENT
;
775 bam_error(_("invalid option or missing option "
776 "argument: -%c\n"), optopt
);
780 bam_error(_("invalid option or missing option "
781 "argument: -%c\n"), c
);
787 * An alternate platform requires an alternate root
789 if (bam_alt_platform
&& bam_alt_root
== 0) {
795 * A command option must be specfied
798 if (bam_opt
&& strcmp(bam_opt
, "all") == 0) {
802 bam_error(_("a command option must be specified\n"));
812 bam_error(_("Internal error: %s\n"), "parse_args");
814 } else if (optind
< argc
) {
815 bam_argv
= &argv
[optind
];
816 bam_argc
= argc
- optind
;
820 * mbr and pool are options for install_bootloader
822 if (bam_cmd
!= BAM_INSTALL
&& (bam_mbr
|| bam_pool
!= NULL
)) {
828 * -n implies verbose mode
835 check_subcmd_and_options(
838 subcmd_defn_t
*table
,
843 if (subcmd
== NULL
) {
844 bam_error(_("this command requires a sub-command\n"));
848 if (strcmp(subcmd
, "set_option") == 0) {
849 if (bam_argc
== 0 || bam_argv
== NULL
|| bam_argv
[0] == NULL
) {
850 bam_error(_("missing argument for sub-command\n"));
853 } else if (bam_argc
> 1 || bam_argv
[1] != NULL
) {
854 bam_error(_("invalid trailing arguments\n"));
858 } else if (strcmp(subcmd
, "update_all") == 0) {
860 * The only option we accept for the "update_all"
861 * subcmd is "fastboot".
863 if (bam_argc
> 1 || (bam_argc
== 1 &&
864 strcmp(bam_argv
[0], "fastboot") != 0)) {
865 bam_error(_("invalid trailing arguments\n"));
871 } else if (((strcmp(subcmd
, "enable_hypervisor") != 0) &&
872 (strcmp(subcmd
, "list_setting") != 0)) && (bam_argc
|| bam_argv
)) {
874 * Of the remaining subcommands, only "enable_hypervisor" and
875 * "list_setting" take trailing arguments.
877 bam_error(_("invalid trailing arguments\n"));
882 if (bam_root
== NULL
) {
887 /* verify that subcmd is valid */
888 for (i
= 0; table
[i
].subcmd
!= NULL
; i
++) {
889 if (strcmp(table
[i
].subcmd
, subcmd
) == 0)
893 if (table
[i
].subcmd
== NULL
) {
894 bam_error(_("invalid sub-command specified: %s\n"), subcmd
);
898 if (table
[i
].unpriv
== 0 && geteuid() != 0) {
899 bam_error(_("you must be root to run this command\n"));
904 * Currently only privileged commands need a lock
906 if (table
[i
].unpriv
== 0)
909 /* subcmd verifies that opt is appropriate */
910 if (table
[i
].option
!= OPT_OPTIONAL
) {
911 if ((table
[i
].option
== OPT_REQ
) ^ (opt
!= NULL
)) {
913 bam_error(_("this sub-command (%s) does not "
914 "take options\n"), subcmd
);
916 bam_error(_("an option is required for this "
917 "sub-command: %s\n"), subcmd
);
922 *fp
= table
[i
].handler
;
924 return (BAM_SUCCESS
);
928 * NOTE: A single "/" is also considered a trailing slash and will
932 elide_trailing_slash(const char *src
, char *dst
, size_t dstsize
)
939 (void) strlcpy(dst
, src
, dstsize
);
941 dstlen
= strlen(dst
);
942 if (dst
[dstlen
- 1] == '/') {
943 dst
[dstlen
- 1] = '\0';
948 is_safe_exec(char *path
)
952 if (lstat(path
, &sb
) != 0) {
953 bam_error(_("stat of file failed: %s: %s\n"), path
,
958 if (!S_ISREG(sb
.st_mode
)) {
959 bam_error(_("%s is not a regular file, skipping\n"), path
);
963 if (sb
.st_uid
!= getuid()) {
964 bam_error(_("%s is not owned by %d, skipping\n"),
969 if (sb
.st_mode
& S_IWOTH
|| sb
.st_mode
& S_IWGRP
) {
970 bam_error(_("%s is others or group writable, skipping\n"),
975 return (BAM_SUCCESS
);
979 list_setting(menu_t
*mp
, char *which
, char *setting
)
992 if (*which
!= NULL
) {
994 * If "which" is not a number, assume it's a setting we want
995 * to look for and so set up the routine to look for "which"
996 * in the default entry.
999 if (!(isdigit((int)*p
++))) {
1001 which
= mp
->curdefault
->arg
;
1005 which
= mp
->curdefault
->arg
;
1008 entry
= atoi(which
);
1010 for (ent
= mp
->entries
; ((ent
!= NULL
) && (ent
->entryNum
!= entry
));
1015 bam_error(_("no matching entry found\n"));
1019 found
= (*setting
== NULL
);
1021 for (lp
= ent
->start
; lp
!= NULL
; lp
= lp
->next
) {
1022 if ((*setting
== NULL
) && (lp
->flags
!= BAM_COMMENT
))
1023 bam_print("%s\n", lp
->line
);
1024 else if (lp
->cmd
!= NULL
&& strcmp(setting
, lp
->cmd
) == 0) {
1025 bam_print("%s\n", lp
->arg
);
1034 bam_error(_("no matching entry found\n"));
1038 return (BAM_SUCCESS
);
1042 install_bootloader(void)
1047 struct extmnttab mnt
;
1048 struct stat statbuf
= {0};
1049 be_node_list_t
*be_nodes
, *node
;
1051 char *root_ds
= NULL
;
1052 int ret
= BAM_ERROR
;
1054 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0) {
1055 bam_error(_("out of memory\n"));
1060 * if bam_alt_root is set, the stage files are used from alt root.
1061 * if pool is set, the target devices are pool devices, stage files
1062 * are read from pool bootfs unless alt root is set.
1064 * use arguments as targets, stage files are from alt or current root
1065 * if no arguments and no pool, install on current boot pool.
1069 if (stat(bam_root
, &statbuf
) != 0) {
1070 bam_error(_("stat of file failed: %s: %s\n"), bam_root
,
1074 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
1075 bam_error(_("failed to open file: %s: %s\n"),
1076 MNTTAB
, strerror(errno
));
1080 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
1081 if (mnt
.mnt_major
== major(statbuf
.st_dev
) &&
1082 mnt
.mnt_minor
== minor(statbuf
.st_dev
)) {
1084 root_ds
= strdup(mnt
.mnt_special
);
1091 bam_error(_("alternate root %s not in mnttab\n"),
1095 if (root_ds
== NULL
) {
1096 bam_error(_("out of memory\n"));
1100 if (be_list(NULL
, &be_nodes
) != BE_SUCCESS
) {
1101 bam_error(_("No BE's found\n"));
1104 for (node
= be_nodes
; node
!= NULL
; node
= node
->be_next_node
)
1105 if (strcmp(root_ds
, node
->be_root_ds
) == 0)
1109 bam_error(_("BE (%s) does not exist\n"), root_ds
);
1114 be_free_list(be_nodes
);
1117 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_NAME
,
1118 node
->be_node_name
);
1119 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_ROOT
,
1121 be_free_list(be_nodes
);
1129 flags
|= BE_INSTALLBOOT_FLAG_FORCE
;
1131 flags
|= BE_INSTALLBOOT_FLAG_MBR
;
1133 flags
|= BE_INSTALLBOOT_FLAG_VERBOSE
;
1135 if (nvlist_add_uint16(nvl
, BE_ATTR_INSTALL_FLAGS
, flags
) != 0) {
1136 bam_error(_("out of memory\n"));
1142 * if altroot was set, we got be name and be root, only need
1143 * to set pool name as target.
1144 * if no altroot, need to find be name and root from pool.
1146 if (bam_pool
!= NULL
) {
1147 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_POOL
, bam_pool
);
1153 ret
= be_installboot(nvl
);
1160 if (be_list(NULL
, &be_nodes
) != BE_SUCCESS
) {
1161 bam_error(_("No BE's found\n"));
1166 if (bam_pool
!= NULL
) {
1168 * find active be_node in bam_pool
1170 for (node
= be_nodes
; node
!= NULL
; node
= node
->be_next_node
) {
1171 if (strcmp(bam_pool
, node
->be_rpool
) != 0)
1173 if (node
->be_active_on_boot
)
1177 bam_error(_("No active BE in %s\n"), bam_pool
);
1178 be_free_list(be_nodes
);
1182 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_NAME
,
1183 node
->be_node_name
);
1184 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_ROOT
,
1186 be_free_list(be_nodes
);
1191 ret
= be_installboot(nvl
);
1198 * get dataset for "/" and fill up the args.
1200 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
1201 bam_error(_("failed to open file: %s: %s\n"),
1202 MNTTAB
, strerror(errno
));
1204 be_free_list(be_nodes
);
1209 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
1210 if (strcmp(mnt
.mnt_mountp
, "/") == 0) {
1212 root_ds
= strdup(mnt
.mnt_special
);
1219 bam_error(_("alternate root %s not in mnttab\n"), "/");
1221 be_free_list(be_nodes
);
1224 if (root_ds
== NULL
) {
1225 bam_error(_("out of memory\n"));
1227 be_free_list(be_nodes
);
1231 for (node
= be_nodes
; node
!= NULL
; node
= node
->be_next_node
) {
1232 if (strcmp(root_ds
, node
->be_root_ds
) == 0)
1237 bam_error(_("No such BE: %s\n"), root_ds
);
1239 be_free_list(be_nodes
);
1245 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_NAME
, node
->be_node_name
);
1246 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_ROOT
, node
->be_root_ds
);
1247 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_POOL
, node
->be_rpool
);
1248 be_free_list(be_nodes
);
1253 ret
= be_installboot(nvl
) ? BAM_ERROR
: 0;
1261 bam_install(char *subcmd
, char *opt
)
1268 if (check_subcmd_and_options(subcmd
, opt
, inst_subcmds
, &f
) ==
1276 bam_menu(char *subcmd
, char *opt
, int largc
, char *largv
[])
1279 char menu_path
[PATH_MAX
];
1280 char clean_menu_root
[PATH_MAX
];
1281 char path
[PATH_MAX
];
1283 char menu_root
[PATH_MAX
];
1285 error_t (*f
)(menu_t
*mp
, char *menu_path
, char *opt
);
1286 char *special
= NULL
;
1289 char *zmntpt
= NULL
;
1292 const char *fcn
= "bam_menu()";
1295 * Menu sub-command only applies to GRUB (i.e. x86)
1297 if (!is_grub(bam_alt_root
? bam_root
: "/")) {
1298 bam_error(_("not a GRUB 0.97 based Illumos instance. "
1299 "Operation not supported\n"));
1306 ret
= check_subcmd_and_options(subcmd
, opt
, menu_subcmds
, &f
);
1307 if (ret
== BAM_ERROR
) {
1313 (void) strlcpy(menu_root
, bam_root
, sizeof (menu_root
));
1314 osdev
= osroot
= NULL
;
1316 if (strcmp(subcmd
, "update_entry") == 0) {
1319 osdev
= strtok(opt
, ",");
1321 osroot
= strtok(NULL
, ",");
1323 /* fixup bam_root so that it points at osroot */
1324 if (realpath(osroot
, rootbuf
) == NULL
) {
1325 bam_error(_("cannot resolve path %s: %s\n"),
1326 osroot
, strerror(errno
));
1331 bam_rootlen
= strlen(rootbuf
);
1336 * We support menu on PCFS (under certain conditions), but
1339 if (is_pcfs(bam_root
)) {
1340 bam_error(_("root <%s> on PCFS is not supported\n"), bam_root
);
1344 if (stat(menu_root
, &sb
) == -1) {
1345 bam_error(_("cannot find GRUB menu\n"));
1349 BAM_DPRINTF(("%s: menu root is %s\n", fcn
, menu_root
));
1352 * We no longer use the GRUB slice file. If it exists, then
1353 * the user is doing something that is unsupported (such as
1354 * standard upgrading an old Live Upgrade BE). If that
1355 * happens, mimic existing behavior i.e. pretend that it is
1356 * not a BE. Emit a warning though.
1359 (void) snprintf(path
, sizeof (path
), "%s%s", bam_root
,
1362 (void) snprintf(path
, sizeof (path
), "%s", GRUB_slice
);
1365 if (bam_verbose
&& stat(path
, &sb
) == 0)
1366 bam_error(_("unsupported GRUB slice file (%s) exists - "
1367 "ignoring.\n"), path
);
1369 if (is_zfs(menu_root
)) {
1370 assert(strcmp(menu_root
, bam_root
) == 0);
1371 special
= get_special(menu_root
);
1372 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special
= NULL
);
1373 if (special
== NULL
) {
1374 bam_error(_("cant find special file for "
1375 "mount-point %s\n"), menu_root
);
1378 pool
= strtok(special
, "/");
1379 INJECT_ERROR1("Z_MENU_GET_POOL", pool
= NULL
);
1382 bam_error(_("cant find pool for mount-point %s\n"),
1386 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn
, pool
));
1388 zmntpt
= mount_top_dataset(pool
, &zmnted
);
1389 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt
= NULL
);
1390 if (zmntpt
== NULL
) {
1391 bam_error(_("cannot mount pool dataset for pool: %s\n"),
1396 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn
, zmntpt
));
1398 (void) strlcpy(menu_root
, zmntpt
, sizeof (menu_root
));
1399 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn
, menu_root
));
1402 elide_trailing_slash(menu_root
, clean_menu_root
,
1403 sizeof (clean_menu_root
));
1405 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn
, clean_menu_root
));
1407 (void) strlcpy(menu_path
, clean_menu_root
, sizeof (menu_path
));
1408 (void) strlcat(menu_path
, GRUB_MENU
, sizeof (menu_path
));
1410 BAM_DPRINTF(("%s: menu path is: %s\n", fcn
, menu_path
));
1413 * If listing the menu, display the menu location
1415 if (strcmp(subcmd
, "list_entry") == 0)
1416 bam_print(_("the location for the active GRUB menu is: %s\n"),
1419 if ((menu
= menu_read(menu_path
)) == NULL
) {
1420 bam_error(_("cannot find GRUB menu file: %s\n"), menu_path
);
1427 * We already checked the following case in
1428 * check_subcmd_and_suboptions() above. Complete the
1431 if (strcmp(subcmd
, "set_option") == 0) {
1432 assert(largc
== 1 && largv
[0] && largv
[1] == NULL
);
1434 } else if ((strcmp(subcmd
, "enable_hypervisor") != 0) &&
1435 (strcmp(subcmd
, "list_setting") != 0)) {
1436 assert(largc
== 0 && largv
== NULL
);
1439 ret
= get_boot_cap(bam_root
);
1440 if (ret
!= BAM_SUCCESS
) {
1441 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn
));
1446 * Once the sub-cmd handler has run
1447 * only the line field is guaranteed to have valid values
1449 if (strcmp(subcmd
, "update_entry") == 0) {
1450 ret
= f(menu
, menu_root
, osdev
);
1451 } else if (strcmp(subcmd
, "upgrade") == 0) {
1452 ret
= f(menu
, bam_root
, menu_root
);
1453 } else if (strcmp(subcmd
, "list_entry") == 0) {
1454 ret
= f(menu
, menu_path
, opt
);
1455 } else if (strcmp(subcmd
, "list_setting") == 0) {
1456 ret
= f(menu
, ((largc
> 0) ? largv
[0] : ""),
1457 ((largc
> 1) ? largv
[1] : ""));
1458 } else if (strcmp(subcmd
, "disable_hypervisor") == 0) {
1460 bam_error(_("%s operation unsupported on SPARC "
1461 "machines\n"), subcmd
);
1464 ret
= f(menu
, bam_root
, NULL
);
1466 } else if (strcmp(subcmd
, "enable_hypervisor") == 0) {
1468 bam_error(_("%s operation unsupported on SPARC "
1469 "machines\n"), subcmd
);
1472 char *extra_args
= NULL
;
1475 * Compress all arguments passed in the largv[] array
1476 * into one string that can then be appended to the
1477 * end of the kernel$ string the routine to enable the
1478 * hypervisor will build.
1480 * This allows the caller to supply arbitrary unparsed
1481 * arguments, such as dom0 memory settings or APIC
1484 * This concatenation will be done without ANY syntax
1485 * checking whatsoever, so it's the responsibility of
1486 * the caller to make sure the arguments are valid and
1487 * do not duplicate arguments the conversion routines
1493 for (extra_len
= 0, i
= 0; i
< largc
; i
++)
1494 extra_len
+= strlen(largv
[i
]);
1497 * Allocate space for argument strings,
1498 * intervening spaces and terminating NULL.
1500 extra_args
= alloca(extra_len
+ largc
);
1502 (void) strcpy(extra_args
, largv
[0]);
1504 for (i
= 1; i
< largc
; i
++) {
1505 (void) strcat(extra_args
, " ");
1506 (void) strcat(extra_args
, largv
[i
]);
1510 ret
= f(menu
, bam_root
, extra_args
);
1513 ret
= f(menu
, NULL
, opt
);
1515 if (ret
== BAM_WRITE
) {
1516 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
1517 fcn
, clean_menu_root
));
1518 ret
= menu_write(clean_menu_root
, menu
);
1522 INJECT_ERROR1("POOL_SET", pool
= "/pooldata");
1523 assert((is_zfs(menu_root
)) ^ (pool
== NULL
));
1525 (void) umount_top_dataset(pool
, zmnted
, zmntpt
);
1539 error_t (*f
)(char *root
, char *opt
);
1540 const char *fcn
= "bam_archive()";
1543 * Add trailing / for archive subcommands
1545 if (rootbuf
[strlen(rootbuf
) - 1] != '/')
1546 (void) strcat(rootbuf
, "/");
1547 bam_rootlen
= strlen(rootbuf
);
1552 ret
= check_subcmd_and_options(subcmd
, opt
, arch_subcmds
, &f
);
1553 if (ret
!= BAM_SUCCESS
) {
1557 ret
= get_boot_cap(rootbuf
);
1558 if (ret
!= BAM_SUCCESS
) {
1559 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn
));
1564 * Check archive not supported with update_all
1565 * since it is awkward to display out-of-sync
1566 * information for each BE.
1568 if (bam_check
&& strcmp(subcmd
, "update_all") == 0) {
1569 bam_error(_("the check option is not supported with "
1570 "subcmd: %s\n"), subcmd
);
1574 if (strcmp(subcmd
, "update_all") == 0)
1578 ucode_install(bam_root
);
1581 ret
= f(bam_root
, opt
);
1590 bam_error(char *format
, ...)
1594 va_start(ap
, format
);
1595 (void) fprintf(stderr
, "%s: ", prog
);
1596 (void) vfprintf(stderr
, format
, ap
);
1602 bam_derror(char *format
, ...)
1608 va_start(ap
, format
);
1609 (void) fprintf(stderr
, "DEBUG: ");
1610 (void) vfprintf(stderr
, format
, ap
);
1616 bam_print(char *format
, ...)
1620 va_start(ap
, format
);
1621 (void) vfprintf(stdout
, format
, ap
);
1627 bam_print_stderr(char *format
, ...)
1631 va_start(ap
, format
);
1632 (void) vfprintf(stderr
, format
, ap
);
1637 bam_exit(int excode
)
1650 bam_lock_fd
= open(BAM_LOCK_FILE
, O_CREAT
|O_RDWR
, LOCK_FILE_PERMS
);
1651 if (bam_lock_fd
< 0) {
1653 * We may be invoked early in boot for archive verification.
1654 * In this case, root is readonly and /var/run may not exist.
1655 * Proceed without the lock
1657 if (errno
== EROFS
|| errno
== ENOENT
) {
1658 bam_root_readonly
= 1;
1662 bam_error(_("failed to open file: %s: %s\n"),
1663 BAM_LOCK_FILE
, strerror(errno
));
1667 lock
.l_type
= F_WRLCK
;
1668 lock
.l_whence
= SEEK_SET
;
1672 if (fcntl(bam_lock_fd
, F_SETLK
, &lock
) == -1) {
1673 if (errno
!= EACCES
&& errno
!= EAGAIN
) {
1674 bam_error(_("failed to lock file: %s: %s\n"),
1675 BAM_LOCK_FILE
, strerror(errno
));
1676 (void) close(bam_lock_fd
);
1681 (void) pread(bam_lock_fd
, &pid
, sizeof (pid_t
), 0);
1683 _("another instance of bootadm (pid %lu) is running\n"),
1686 lock
.l_type
= F_WRLCK
;
1687 lock
.l_whence
= SEEK_SET
;
1690 if (fcntl(bam_lock_fd
, F_SETLKW
, &lock
) == -1) {
1691 bam_error(_("failed to lock file: %s: %s\n"),
1692 BAM_LOCK_FILE
, strerror(errno
));
1693 (void) close(bam_lock_fd
);
1699 /* We own the lock now */
1701 (void) write(bam_lock_fd
, &pid
, sizeof (pid
));
1707 struct flock unlock
;
1710 * NOP if we don't hold the lock
1712 if (bam_lock_fd
< 0) {
1716 unlock
.l_type
= F_UNLCK
;
1717 unlock
.l_whence
= SEEK_SET
;
1721 if (fcntl(bam_lock_fd
, F_SETLK
, &unlock
) == -1) {
1722 bam_error(_("failed to unlock file: %s: %s\n"),
1723 BAM_LOCK_FILE
, strerror(errno
));
1726 if (close(bam_lock_fd
) == -1) {
1727 bam_error(_("failed to close file: %s: %s\n"),
1728 BAM_LOCK_FILE
, strerror(errno
));
1734 list_archive(char *root
, char *opt
)
1737 filelist_t
*flistp
= &flist
;
1741 assert(opt
== NULL
);
1743 flistp
->head
= flistp
->tail
= NULL
;
1744 if (read_list(root
, flistp
) != BAM_SUCCESS
) {
1747 assert(flistp
->head
&& flistp
->tail
);
1749 for (lp
= flistp
->head
; lp
; lp
= lp
->next
) {
1750 bam_print(_("%s\n"), lp
->line
);
1753 filelist_free(flistp
);
1755 return (BAM_SUCCESS
);
1759 * This routine writes a list of lines to a file.
1760 * The list is *not* freed
1763 list2file(char *root
, char *tmp
, char *final
, line_t
*start
)
1765 char tmpfile
[PATH_MAX
];
1766 char path
[PATH_MAX
];
1775 const char *fcn
= "list2file()";
1777 (void) snprintf(path
, sizeof (path
), "%s%s", root
, final
);
1779 if (start
== NULL
) {
1780 /* Empty GRUB menu */
1781 if (stat(path
, &sb
) != -1) {
1782 bam_print(_("file is empty, deleting file: %s\n"),
1784 if (unlink(path
) != 0) {
1785 bam_error(_("failed to unlink file: %s: %s\n"),
1786 path
, strerror(errno
));
1789 return (BAM_SUCCESS
);
1792 return (BAM_SUCCESS
);
1796 * Preserve attributes of existing file if possible,
1797 * otherwise ask the system for uid/gid of root/sys.
1798 * If all fails, fall back on hard-coded defaults.
1800 if (stat(path
, &sb
) != -1) {
1802 root_uid
= sb
.st_uid
;
1803 sys_gid
= sb
.st_gid
;
1805 mode
= DEFAULT_DEV_MODE
;
1806 if ((pw
= getpwnam(DEFAULT_DEV_USER
)) != NULL
) {
1807 root_uid
= pw
->pw_uid
;
1809 bam_error(_("getpwnam: uid for %s failed, "
1810 "defaulting to %d\n"),
1811 DEFAULT_DEV_USER
, DEFAULT_DEV_UID
);
1812 root_uid
= (uid_t
)DEFAULT_DEV_UID
;
1814 if ((gp
= getgrnam(DEFAULT_DEV_GROUP
)) != NULL
) {
1815 sys_gid
= gp
->gr_gid
;
1817 bam_error(_("getgrnam: gid for %s failed, "
1818 "defaulting to %d\n"),
1819 DEFAULT_DEV_GROUP
, DEFAULT_DEV_GID
);
1820 sys_gid
= (gid_t
)DEFAULT_DEV_GID
;
1824 (void) snprintf(tmpfile
, sizeof (tmpfile
), "%s%s", root
, tmp
);
1826 /* Truncate tmpfile first */
1827 fp
= fopen(tmpfile
, "w");
1829 bam_error(_("failed to open file: %s: %s\n"), tmpfile
,
1834 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret
= EOF
);
1836 bam_error(_("failed to close file: %s: %s\n"),
1837 tmpfile
, strerror(errno
));
1841 /* Now open it in append mode */
1842 fp
= fopen(tmpfile
, "a");
1844 bam_error(_("failed to open file: %s: %s\n"), tmpfile
,
1849 for (; start
; start
= start
->next
) {
1850 ret
= s_fputs(start
->line
, fp
);
1851 INJECT_ERROR1("LIST2FILE_FPUTS", ret
= EOF
);
1853 bam_error(_("write to file failed: %s: %s\n"),
1854 tmpfile
, strerror(errno
));
1861 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret
= EOF
);
1863 bam_error(_("failed to close file: %s: %s\n"),
1864 tmpfile
, strerror(errno
));
1869 * Set up desired attributes. Ignore failures on filesystems
1870 * not supporting these operations - pcfs reports unsupported
1871 * operations as EINVAL.
1873 ret
= chmod(tmpfile
, mode
);
1875 errno
!= EINVAL
&& errno
!= ENOTSUP
) {
1876 bam_error(_("chmod operation on %s failed - %s\n"),
1877 tmpfile
, strerror(errno
));
1881 ret
= chown(tmpfile
, root_uid
, sys_gid
);
1883 errno
!= EINVAL
&& errno
!= ENOTSUP
) {
1884 bam_error(_("chgrp operation on %s failed - %s\n"),
1885 tmpfile
, strerror(errno
));
1890 * Do an atomic rename
1892 ret
= rename(tmpfile
, path
);
1893 INJECT_ERROR1("LIST2FILE_RENAME", ret
= -1);
1895 bam_error(_("rename to file failed: %s: %s\n"), path
,
1900 BAM_DPRINTF(("%s: wrote file successfully: %s\n", fcn
, path
));
1901 return (BAM_SUCCESS
);
1905 * Checks if the path specified (without the file name at the end) exists
1906 * and creates it if not. If the path exists and is not a directory, an attempt
1907 * to unlink is made.
1910 setup_path(char *path
)
1916 p
= strrchr(path
, '/');
1919 if (stat(path
, &sb
) != 0 || !(S_ISDIR(sb
.st_mode
))) {
1920 /* best effort attempt, mkdirp will catch the error */
1921 (void) unlink(path
);
1923 bam_print(_("need to create directory "
1924 "path for %s\n"), path
);
1925 ret
= mkdirp(path
, DIR_PERMS
);
1927 bam_error(_("mkdir of %s failed: %s\n"),
1928 path
, strerror(errno
));
1934 return (BAM_SUCCESS
);
1936 return (BAM_SUCCESS
);
1945 char path
[PATH_MAX
];
1950 setup_file(char *base
, const char *path
, cachefile
*cf
)
1955 /* init gzfile or fdfile in case we fail before opening */
1956 if (bam_direct
== BAM_DIRECT_DBOOT
)
1957 cf
->out
.gzfile
= NULL
;
1959 cf
->out
.fdfile
= -1;
1961 /* strip the trailing altroot path */
1962 strip
= (char *)path
+ strlen(rootbuf
);
1964 ret
= snprintf(cf
->path
, sizeof (cf
->path
), "%s/%s", base
, strip
);
1965 if (ret
>= sizeof (cf
->path
)) {
1966 bam_error(_("unable to create path on mountpoint %s, "
1967 "path too long\n"), rootbuf
);
1971 /* Check if path is present in the archive cache directory */
1972 if (setup_path(cf
->path
) == BAM_ERROR
)
1975 if (bam_direct
== BAM_DIRECT_DBOOT
) {
1976 if ((cf
->out
.gzfile
= gzopen(cf
->path
, "wb")) == NULL
) {
1977 bam_error(_("failed to open file: %s: %s\n"),
1978 cf
->path
, strerror(errno
));
1981 (void) gzsetparams(cf
->out
.gzfile
, Z_BEST_SPEED
,
1982 Z_DEFAULT_STRATEGY
);
1984 if ((cf
->out
.fdfile
= open(cf
->path
, O_WRONLY
| O_CREAT
, 0644))
1986 bam_error(_("failed to open file: %s: %s\n"),
1987 cf
->path
, strerror(errno
));
1992 return (BAM_SUCCESS
);
1996 cache_write(cachefile cf
, char *buf
, int size
)
2000 if (bam_direct
== BAM_DIRECT_DBOOT
) {
2001 if (gzwrite(cf
.out
.gzfile
, buf
, size
) < 1) {
2002 bam_error(_("failed to write to %s\n"),
2003 gzerror(cf
.out
.gzfile
, &err
));
2004 if (err
== Z_ERRNO
&& bam_verbose
) {
2005 bam_error(_("write to file failed: %s: %s\n"),
2006 cf
.path
, strerror(errno
));
2011 if (write(cf
.out
.fdfile
, buf
, size
) < 1) {
2012 bam_error(_("write to file failed: %s: %s\n"),
2013 cf
.path
, strerror(errno
));
2017 return (BAM_SUCCESS
);
2021 cache_close(cachefile cf
)
2025 if (bam_direct
== BAM_DIRECT_DBOOT
) {
2026 if (cf
.out
.gzfile
) {
2027 ret
= gzclose(cf
.out
.gzfile
);
2029 bam_error(_("failed to close file: %s: %s\n"),
2030 cf
.path
, strerror(errno
));
2035 if (cf
.out
.fdfile
!= -1) {
2036 ret
= close(cf
.out
.fdfile
);
2038 bam_error(_("failed to close file: %s: %s\n"),
2039 cf
.path
, strerror(errno
));
2045 return (BAM_SUCCESS
);
2049 dircache_updatefile(const char *path
, int what
)
2054 cachefile outfile
, outupdt
;
2056 if (bam_nowrite()) {
2057 set_dir_flag(what
, NEED_UPDATE
);
2058 return (BAM_SUCCESS
);
2061 if (!has_cachedir(what
))
2062 return (BAM_SUCCESS
);
2064 if ((infile
= fopen(path
, "rb")) == NULL
) {
2065 bam_error(_("failed to open file: %s: %s\n"), path
,
2070 ret
= setup_file(get_cachedir(what
), path
, &outfile
);
2071 if (ret
== BAM_ERROR
) {
2072 exitcode
= BAM_ERROR
;
2075 if (!is_dir_flag_on(what
, NO_MULTI
)) {
2076 ret
= setup_file(get_updatedir(what
), path
, &outupdt
);
2077 if (ret
== BAM_ERROR
)
2078 set_dir_flag(what
, NO_MULTI
);
2081 while ((ret
= fread(buf
, 1, sizeof (buf
), infile
)) > 0) {
2082 if (cache_write(outfile
, buf
, ret
) == BAM_ERROR
) {
2083 exitcode
= BAM_ERROR
;
2086 if (!is_dir_flag_on(what
, NO_MULTI
))
2087 if (cache_write(outupdt
, buf
, ret
) == BAM_ERROR
)
2088 set_dir_flag(what
, NO_MULTI
);
2091 set_dir_flag(what
, NEED_UPDATE
);
2093 if (get_count(what
) > COUNT_MAX
)
2094 set_dir_flag(what
, NO_MULTI
);
2095 exitcode
= BAM_SUCCESS
;
2097 (void) fclose(infile
);
2098 if (cache_close(outfile
) == BAM_ERROR
)
2099 exitcode
= BAM_ERROR
;
2100 if (!is_dir_flag_on(what
, NO_MULTI
) &&
2101 cache_close(outupdt
) == BAM_ERROR
)
2102 exitcode
= BAM_ERROR
;
2103 if (exitcode
== BAM_ERROR
)
2104 set_flag(UPDATE_ERROR
);
2109 dircache_updatedir(const char *path
, int what
, int updt
)
2112 char dpath
[PATH_MAX
];
2116 strip
= (char *)path
+ strlen(rootbuf
);
2118 ret
= snprintf(dpath
, sizeof (dpath
), "%s/%s", updt
?
2119 get_updatedir(what
) : get_cachedir(what
), strip
);
2121 if (ret
>= sizeof (dpath
)) {
2122 bam_error(_("unable to create path on mountpoint %s, "
2123 "path too long\n"), rootbuf
);
2124 set_flag(UPDATE_ERROR
);
2128 if (stat(dpath
, &sb
) == 0 && S_ISDIR(sb
.st_mode
))
2129 return (BAM_SUCCESS
);
2132 if (!is_dir_flag_on(what
, NO_MULTI
))
2133 if (!bam_nowrite() && mkdirp(dpath
, DIR_PERMS
) == -1)
2134 set_dir_flag(what
, NO_MULTI
);
2136 if (!bam_nowrite() && mkdirp(dpath
, DIR_PERMS
) == -1) {
2137 set_flag(UPDATE_ERROR
);
2142 set_dir_flag(what
, NEED_UPDATE
);
2143 return (BAM_SUCCESS
);
2146 #define DO_CACHE_DIR 0
2147 #define DO_UPDATE_DIR 1
2149 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2150 typedef Elf64_Ehdr _elfhdr
;
2152 typedef Elf32_Ehdr _elfhdr
;
2156 * This routine updates the contents of the cache directory
2159 update_dircache(const char *path
, int flags
)
2161 int rc
= BAM_SUCCESS
;
2169 if ((fd
= open(path
, O_RDONLY
)) < 0) {
2170 bam_error(_("failed to open file: %s: %s\n"),
2171 path
, strerror(errno
));
2172 set_flag(UPDATE_ERROR
);
2178 * libelf and gelf would be a cleaner and easier way to handle
2179 * this, but libelf fails compilation if _ILP32 is defined &&
2180 * _FILE_OFFSET_BITS is != 32 ...
2182 if (read(fd
, (void *)&elf
, sizeof (_elfhdr
)) < 0) {
2183 bam_error(_("read failed for file: %s: %s\n"),
2184 path
, strerror(errno
));
2185 set_flag(UPDATE_ERROR
);
2193 * If the file is not an executable and is not inside an amd64
2194 * directory, we copy it in both the cache directories,
2195 * otherwise, we only copy it inside the 64-bit one.
2197 if (memcmp(elf
.e_ident
, ELFMAG
, 4) != 0) {
2198 if (strstr(path
, "/amd64")) {
2199 rc
= dircache_updatefile(path
, FILE64
);
2201 rc
= dircache_updatefile(path
, FILE32
);
2202 if (rc
== BAM_SUCCESS
)
2203 rc
= dircache_updatefile(path
, FILE64
);
2207 * Based on the ELF class we copy the file in the 32-bit
2208 * or the 64-bit cache directory.
2210 if (elf
.e_ident
[EI_CLASS
] == ELFCLASS32
) {
2211 rc
= dircache_updatefile(path
, FILE32
);
2212 } else if (elf
.e_ident
[EI_CLASS
] == ELFCLASS64
) {
2213 rc
= dircache_updatefile(path
, FILE64
);
2215 bam_print(_("WARNING: file %s is neither a "
2216 "32-bit nor a 64-bit ELF\n"), path
);
2218 rc
= dircache_updatefile(path
, FILE32
);
2219 if (rc
== BAM_SUCCESS
)
2220 rc
= dircache_updatefile(path
, FILE64
);
2226 if (strstr(path
, "/amd64") == NULL
) {
2227 rc
= dircache_updatedir(path
, FILE32
, DO_UPDATE_DIR
);
2228 if (rc
== BAM_SUCCESS
)
2229 rc
= dircache_updatedir(path
, FILE32
,
2232 if (has_cachedir(FILE64
)) {
2233 rc
= dircache_updatedir(path
, FILE64
,
2235 if (rc
== BAM_SUCCESS
)
2236 rc
= dircache_updatedir(path
, FILE64
,
2253 const struct stat
*st
,
2259 uint64_t filestat
[2];
2260 int error
, ret
, status
;
2262 struct safefile
*safefilep
;
2268 * On SPARC we create/update links too.
2270 if (flags
!= FTW_F
&& flags
!= FTW_D
&& (flags
== FTW_SL
&&
2271 !is_flag_on(IS_SPARC_TARGET
)))
2275 * Ignore broken links
2277 if (flags
== FTW_SL
&& stat(file
, &sb
) < 0)
2281 * new_nvlp may be NULL if there were errors earlier
2282 * but this is not fatal to update determination.
2284 if (walk_arg
.new_nvlp
) {
2285 filestat
[0] = st
->st_size
;
2286 filestat
[1] = st
->st_mtime
;
2287 error
= nvlist_add_uint64_array(walk_arg
.new_nvlp
,
2288 file
+ bam_rootlen
, filestat
, 2);
2290 bam_error(_("failed to update stat data for: %s: %s\n"),
2291 file
, strerror(error
));
2295 * If we are invoked as part of system/filesystem/boot-archive, then
2296 * there are a number of things we should not worry about
2298 if (bam_smf_check
) {
2299 /* ignore amd64 modules unless we are booted amd64. */
2300 if (!is_amd64() && strstr(file
, "/amd64/") != 0)
2303 /* read in list of safe files */
2304 if (safefiles
== NULL
) {
2305 fp
= fopen("/boot/solaris/filelist.safe", "r");
2307 safefiles
= s_calloc(1,
2308 sizeof (struct safefile
));
2309 safefilep
= safefiles
;
2310 safefilep
->name
= s_calloc(1, MAXPATHLEN
+
2312 safefilep
->next
= NULL
;
2313 while (s_fgets(safefilep
->name
, MAXPATHLEN
+
2314 MAXNAMELEN
, fp
) != NULL
) {
2315 safefilep
->next
= s_calloc(1,
2316 sizeof (struct safefile
));
2317 safefilep
= safefilep
->next
;
2318 safefilep
->name
= s_calloc(1,
2319 MAXPATHLEN
+ MAXNAMELEN
);
2320 safefilep
->next
= NULL
;
2328 * On SPARC we create a -path-list file for mkisofs
2330 if (is_flag_on(IS_SPARC_TARGET
) && !bam_nowrite()) {
2331 if (flags
!= FTW_D
) {
2334 strip
= (char *)file
+ strlen(rootbuf
);
2335 (void) fprintf(walk_arg
.sparcfile
, "/%s=%s\n", strip
,
2341 * We are transitioning from the old model to the dircache or the cache
2342 * directory was removed: create the entry without further checkings.
2344 if (is_flag_on(NEED_CACHE_DIR
)) {
2346 bam_print(_(" new %s\n"), file
);
2348 if (is_flag_on(IS_SPARC_TARGET
)) {
2349 set_dir_flag(FILE64
, NEED_UPDATE
);
2353 ret
= update_dircache(file
, flags
);
2354 if (ret
== BAM_ERROR
) {
2355 bam_error(_("directory cache update failed for %s\n"),
2364 * We need an update if file doesn't exist in old archive
2366 if (walk_arg
.old_nvlp
== NULL
||
2367 nvlist_lookup_uint64_array(walk_arg
.old_nvlp
,
2368 file
+ bam_rootlen
, &value
, &sz
) != 0) {
2369 if (bam_smf_check
) /* ignore new during smf check */
2372 if (is_flag_on(IS_SPARC_TARGET
)) {
2373 set_dir_flag(FILE64
, NEED_UPDATE
);
2375 ret
= update_dircache(file
, flags
);
2376 if (ret
== BAM_ERROR
) {
2377 bam_error(_("directory cache update "
2378 "failed for %s\n"), file
);
2384 bam_print(_(" new %s\n"), file
);
2389 * If we got there, the file is already listed as to be included in the
2390 * iso image. We just need to know if we are going to rebuild it or not
2392 if (is_flag_on(IS_SPARC_TARGET
) &&
2393 is_dir_flag_on(FILE64
, NEED_UPDATE
) && !bam_nowrite())
2396 * File exists in old archive. Check if file has changed
2399 bcopy(value
, filestat
, sizeof (filestat
));
2401 if (flags
!= FTW_D
&& (filestat
[0] != st
->st_size
||
2402 filestat
[1] != st
->st_mtime
)) {
2403 if (bam_smf_check
) {
2404 safefilep
= safefiles
;
2405 while (safefilep
!= NULL
&&
2406 safefilep
->name
[0] != '\0') {
2407 if (regcomp(&re
, safefilep
->name
,
2408 REG_EXTENDED
|REG_NOSUB
) == 0) {
2409 status
= regexec(&re
,
2410 file
+ bam_rootlen
, 0, NULL
, 0);
2414 NEED_UPDATE_SAFE_FILE
,
2419 safefilep
= safefilep
->next
;
2423 if (is_flag_on(IS_SPARC_TARGET
)) {
2424 set_dir_flag(FILE64
, NEED_UPDATE
);
2426 ret
= update_dircache(file
, flags
);
2427 if (ret
== BAM_ERROR
) {
2428 bam_error(_("directory cache update failed "
2436 bam_print(" %s\n", file
);
2438 bam_print(_(" changed %s\n"), file
);
2446 * Remove a directory path recursively
2451 struct dirent
*d
= NULL
;
2453 char tpath
[PATH_MAX
];
2456 if ((dir
= opendir(path
)) == NULL
)
2459 while ((d
= readdir(dir
)) != NULL
) {
2460 if ((strcmp(d
->d_name
, ".") != 0) &&
2461 (strcmp(d
->d_name
, "..") != 0)) {
2462 (void) snprintf(tpath
, sizeof (tpath
), "%s/%s",
2464 if (stat(tpath
, &sb
) == 0) {
2465 if (sb
.st_mode
& S_IFDIR
)
2466 (void) rmdir_r(tpath
);
2468 (void) remove(tpath
);
2472 return (remove(path
));
2476 * Check if cache directory exists and, if not, create it and update flags
2477 * accordingly. If the path exists, but it's not a directory, a best effort
2478 * attempt to remove and recreate it is made.
2479 * If the user requested a 'purge', always recreate the directory from scratch.
2482 set_cache_dir(char *root
, int what
)
2487 ret
= snprintf(get_cachedir(what
), sizeof (get_cachedir(what
)),
2488 "%s%s%s%s%s", root
, ARCHIVE_PREFIX
, get_machine(), what
== FILE64
?
2489 "/amd64" : "", CACHEDIR_SUFFIX
);
2491 if (ret
>= sizeof (get_cachedir(what
))) {
2492 bam_error(_("unable to create path on mountpoint %s, "
2493 "path too long\n"), rootbuf
);
2497 if (bam_purge
|| is_flag_on(INVALIDATE_CACHE
))
2498 (void) rmdir_r(get_cachedir(what
));
2500 if (stat(get_cachedir(what
), &sb
) != 0 || !(S_ISDIR(sb
.st_mode
))) {
2501 /* best effort unlink attempt, mkdir will catch errors */
2502 (void) unlink(get_cachedir(what
));
2505 bam_print(_("archive cache directory not found: %s\n"),
2506 get_cachedir(what
));
2507 ret
= mkdir(get_cachedir(what
), DIR_PERMS
);
2509 bam_error(_("mkdir of %s failed: %s\n"),
2510 get_cachedir(what
), strerror(errno
));
2511 get_cachedir(what
)[0] = '\0';
2514 set_flag(NEED_CACHE_DIR
);
2515 set_dir_flag(what
, NO_MULTI
);
2518 return (BAM_SUCCESS
);
2522 set_update_dir(char *root
, int what
)
2527 if (is_dir_flag_on(what
, NO_MULTI
))
2528 return (BAM_SUCCESS
);
2531 set_dir_flag(what
, NO_MULTI
);
2532 return (BAM_SUCCESS
);
2535 if (what
== FILE64
&& !is_flag_on(IS_SPARC_TARGET
))
2536 ret
= snprintf(get_updatedir(what
),
2537 sizeof (get_updatedir(what
)), "%s%s%s/amd64%s", root
,
2538 ARCHIVE_PREFIX
, get_machine(), UPDATEDIR_SUFFIX
);
2540 ret
= snprintf(get_updatedir(what
),
2541 sizeof (get_updatedir(what
)), "%s%s%s%s", root
,
2542 ARCHIVE_PREFIX
, get_machine(), UPDATEDIR_SUFFIX
);
2544 if (ret
>= sizeof (get_updatedir(what
))) {
2545 bam_error(_("unable to create path on mountpoint %s, "
2546 "path too long\n"), rootbuf
);
2550 if (stat(get_updatedir(what
), &sb
) == 0) {
2551 if (S_ISDIR(sb
.st_mode
))
2552 ret
= rmdir_r(get_updatedir(what
));
2554 ret
= unlink(get_updatedir(what
));
2557 set_dir_flag(what
, NO_MULTI
);
2560 if (mkdir(get_updatedir(what
), DIR_PERMS
) < 0)
2561 set_dir_flag(what
, NO_MULTI
);
2563 return (BAM_SUCCESS
);
2567 is_valid_archive(char *root
, int what
)
2569 char archive_path
[PATH_MAX
];
2570 char timestamp_path
[PATH_MAX
];
2571 struct stat sb
, timestamp
;
2574 if (what
== FILE64
&& !is_flag_on(IS_SPARC_TARGET
))
2575 ret
= snprintf(archive_path
, sizeof (archive_path
),
2576 "%s%s%s/amd64%s", root
, ARCHIVE_PREFIX
, get_machine(),
2579 ret
= snprintf(archive_path
, sizeof (archive_path
), "%s%s%s%s",
2580 root
, ARCHIVE_PREFIX
, get_machine(), ARCHIVE_SUFFIX
);
2582 if (ret
>= sizeof (archive_path
)) {
2583 bam_error(_("unable to create path on mountpoint %s, "
2584 "path too long\n"), rootbuf
);
2588 if (stat(archive_path
, &sb
) != 0) {
2589 if (bam_verbose
&& !bam_check
)
2590 bam_print(_("archive not found: %s\n"), archive_path
);
2591 set_dir_flag(what
, NEED_UPDATE
);
2592 set_dir_flag(what
, NO_MULTI
);
2593 return (BAM_SUCCESS
);
2597 * The timestamp file is used to prevent stale files in the archive
2599 * Stale files can happen if the system is booted back and forth across
2600 * the transition from bootadm-before-the-cache to
2601 * bootadm-after-the-cache, since older versions of bootadm don't know
2602 * about the existence of the archive cache.
2604 * Since only bootadm-after-the-cache versions know about about this
2605 * file, we require that the boot archive be older than this file.
2607 ret
= snprintf(timestamp_path
, sizeof (timestamp_path
), "%s%s", root
,
2608 FILE_STAT_TIMESTAMP
);
2610 if (ret
>= sizeof (timestamp_path
)) {
2611 bam_error(_("unable to create path on mountpoint %s, "
2612 "path too long\n"), rootbuf
);
2616 if (stat(timestamp_path
, ×tamp
) != 0 ||
2617 sb
.st_mtime
> timestamp
.st_mtime
) {
2618 if (bam_verbose
&& !bam_check
)
2620 _("archive cache is out of sync. Rebuilding.\n"));
2622 * Don't generate a false positive for the boot-archive service
2623 * but trigger an update of the archive cache in
2624 * boot-archive-update.
2626 if (bam_smf_check
) {
2627 (void) creat(NEED_UPDATE_FILE
, 0644);
2628 return (BAM_SUCCESS
);
2631 set_flag(INVALIDATE_CACHE
);
2632 set_dir_flag(what
, NEED_UPDATE
);
2633 set_dir_flag(what
, NO_MULTI
);
2634 return (BAM_SUCCESS
);
2637 if (is_flag_on(IS_SPARC_TARGET
))
2638 return (BAM_SUCCESS
);
2640 if (bam_extend
&& sb
.st_size
> BA_SIZE_MAX
) {
2641 if (bam_verbose
&& !bam_check
)
2642 bam_print(_("archive %s is bigger than %d bytes and "
2643 "will be rebuilt\n"), archive_path
, BA_SIZE_MAX
);
2644 set_dir_flag(what
, NO_MULTI
);
2647 return (BAM_SUCCESS
);
2651 * Check flags and presence of required files and directories.
2652 * The force flag and/or absence of files should
2653 * trigger an update.
2654 * Suppress stdout output if check (-n) option is set
2655 * (as -n should only produce parseable output.)
2658 check_flags_and_files(char *root
)
2665 * If archive is missing, create archive
2667 if (is_flag_on(IS_SPARC_TARGET
)) {
2668 ret
= is_valid_archive(root
, FILE64
);
2669 if (ret
== BAM_ERROR
)
2674 ret
= is_valid_archive(root
, what
);
2675 if (ret
== BAM_ERROR
)
2678 } while (bam_direct
== BAM_DIRECT_DBOOT
&& what
< CACHEDIR_NUM
);
2682 return (BAM_SUCCESS
);
2686 * check if cache directories exist on x86.
2687 * check (and always open) the cache file on SPARC.
2690 ret
= snprintf(get_cachedir(FILE64
),
2691 sizeof (get_cachedir(FILE64
)), "%s%s%s/%s", root
,
2692 ARCHIVE_PREFIX
, get_machine(), CACHEDIR_SUFFIX
);
2694 if (ret
>= sizeof (get_cachedir(FILE64
))) {
2695 bam_error(_("unable to create path on mountpoint %s, "
2696 "path too long\n"), rootbuf
);
2700 if (stat(get_cachedir(FILE64
), &sb
) != 0) {
2701 set_flag(NEED_CACHE_DIR
);
2702 set_dir_flag(FILE64
, NEED_UPDATE
);
2705 walk_arg
.sparcfile
= fopen(get_cachedir(FILE64
), "w");
2706 if (walk_arg
.sparcfile
== NULL
) {
2707 bam_error(_("failed to open file: %s: %s\n"),
2708 get_cachedir(FILE64
), strerror(errno
));
2712 set_dir_present(FILE64
);
2717 if (set_cache_dir(root
, what
) != 0)
2720 set_dir_present(what
);
2722 if (set_update_dir(root
, what
) != 0)
2725 } while (bam_direct
== BAM_DIRECT_DBOOT
&& what
< CACHEDIR_NUM
);
2729 * if force, create archive unconditionally
2733 set_dir_flag(FILE32
, NEED_UPDATE
);
2734 set_dir_flag(FILE64
, NEED_UPDATE
);
2736 bam_print(_("forced update of archive requested\n"));
2737 return (BAM_SUCCESS
);
2740 return (BAM_SUCCESS
);
2744 read_one_list(char *root
, filelist_t
*flistp
, char *filelist
)
2746 char path
[PATH_MAX
];
2748 char buf
[BAM_MAXLINE
];
2749 const char *fcn
= "read_one_list()";
2751 (void) snprintf(path
, sizeof (path
), "%s%s", root
, filelist
);
2753 fp
= fopen(path
, "r");
2755 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2756 fcn
, path
, strerror(errno
)));
2759 while (s_fgets(buf
, sizeof (buf
), fp
) != NULL
) {
2760 /* skip blank lines */
2761 if (strspn(buf
, " \t") == strlen(buf
))
2763 append_to_flist(flistp
, buf
);
2765 if (fclose(fp
) != 0) {
2766 bam_error(_("failed to close file: %s: %s\n"),
2767 path
, strerror(errno
));
2770 return (BAM_SUCCESS
);
2774 read_list(char *root
, filelist_t
*flistp
)
2776 char path
[PATH_MAX
];
2780 const char *fcn
= "read_list()";
2782 flistp
->head
= flistp
->tail
= NULL
;
2785 * build and check path to extract_boot_filelist.ksh
2787 n
= snprintf(path
, sizeof (path
), "%s%s", root
, EXTRACT_BOOT_FILELIST
);
2788 if (n
>= sizeof (path
)) {
2789 bam_error(_("archive filelist is empty\n"));
2793 if (is_safe_exec(path
) == BAM_ERROR
)
2797 * If extract_boot_filelist is present, exec it, otherwise read
2798 * the filelists directly, for compatibility with older images.
2800 if (stat(path
, &sb
) == 0) {
2802 * build arguments to exec extract_boot_filelist.ksh
2804 char *rootarg
, *platarg
;
2805 int platarglen
= 1, rootarglen
= 1;
2806 if (strlen(root
) > 1)
2807 rootarglen
+= strlen(root
) + strlen("-R ");
2808 if (bam_alt_platform
)
2809 platarglen
+= strlen(bam_platform
) + strlen("-p ");
2810 platarg
= s_calloc(1, platarglen
);
2811 rootarg
= s_calloc(1, rootarglen
);
2815 if (strlen(root
) > 1) {
2816 (void) snprintf(rootarg
, rootarglen
,
2819 if (bam_alt_platform
) {
2820 (void) snprintf(platarg
, platarglen
,
2821 "-p %s", bam_platform
);
2823 n
= snprintf(cmd
, sizeof (cmd
), "%s %s %s /%s /%s",
2824 path
, rootarg
, platarg
, BOOT_FILE_LIST
, ETC_FILE_LIST
);
2827 if (n
>= sizeof (cmd
)) {
2828 bam_error(_("archive filelist is empty\n"));
2831 if (exec_cmd(cmd
, flistp
) != 0) {
2832 BAM_DPRINTF(("%s: failed to open archive "
2833 "filelist: %s: %s\n", fcn
, path
, strerror(errno
)));
2838 * Read current lists of files - only the first is mandatory
2840 rval
= read_one_list(root
, flistp
, BOOT_FILE_LIST
);
2841 if (rval
!= BAM_SUCCESS
)
2843 (void) read_one_list(root
, flistp
, ETC_FILE_LIST
);
2846 if (flistp
->head
== NULL
) {
2847 bam_error(_("archive filelist is empty\n"));
2851 return (BAM_SUCCESS
);
2855 getoldstat(char *root
)
2857 char path
[PATH_MAX
];
2862 (void) snprintf(path
, sizeof (path
), "%s%s", root
, FILE_STAT
);
2863 fd
= open(path
, O_RDONLY
);
2866 bam_print(_("failed to open file: %s: %s\n"),
2867 path
, strerror(errno
));
2871 if (fstat(fd
, &sb
) != 0) {
2872 bam_error(_("stat of file failed: %s: %s\n"), path
,
2877 ostat
= s_calloc(1, sb
.st_size
);
2879 if (read(fd
, ostat
, sb
.st_size
) != sb
.st_size
) {
2880 bam_error(_("read failed for file: %s: %s\n"), path
,
2889 walk_arg
.old_nvlp
= NULL
;
2890 error
= nvlist_unpack(ostat
, sb
.st_size
, &walk_arg
.old_nvlp
, 0);
2895 bam_error(_("failed to unpack stat data: %s: %s\n"),
2896 path
, strerror(error
));
2897 walk_arg
.old_nvlp
= NULL
;
2906 if (!is_flag_on(IS_SPARC_TARGET
))
2907 set_dir_flag(FILE32
, NEED_UPDATE
);
2908 set_dir_flag(FILE64
, NEED_UPDATE
);
2911 /* Best effort stale entry removal */
2913 delete_stale(char *file
, int what
)
2915 char path
[PATH_MAX
];
2918 (void) snprintf(path
, sizeof (path
), "%s/%s", get_cachedir(what
), file
);
2919 if (!bam_check
&& stat(path
, &sb
) == 0) {
2920 if (sb
.st_mode
& S_IFDIR
)
2921 (void) rmdir_r(path
);
2923 (void) unlink(path
);
2925 set_dir_flag(what
, (NEED_UPDATE
| NO_MULTI
));
2930 * Checks if a file in the current (old) archive has
2931 * been deleted from the root filesystem. This is needed for
2932 * software like Trusted Extensions (TX) that switch early
2933 * in boot based on presence/absence of a kernel module.
2936 check4stale(char *root
)
2941 char path
[PATH_MAX
];
2944 * Skip stale file check during smf check
2950 * If we need to (re)create the cache, there's no need to check for
2953 if (is_flag_on(NEED_CACHE_DIR
))
2956 /* Nothing to do if no old stats */
2957 if ((nvlp
= walk_arg
.old_nvlp
) == NULL
)
2960 for (nvp
= nvlist_next_nvpair(nvlp
, NULL
); nvp
;
2961 nvp
= nvlist_next_nvpair(nvlp
, nvp
)) {
2962 file
= nvpair_name(nvp
);
2965 (void) snprintf(path
, sizeof (path
), "%s/%s",
2967 if (access(path
, F_OK
) < 0) {
2971 bam_print(_(" stale %s\n"), path
);
2973 if (is_flag_on(IS_SPARC_TARGET
)) {
2974 set_dir_flag(FILE64
, NEED_UPDATE
);
2976 for (what
= FILE32
; what
< CACHEDIR_NUM
; what
++)
2977 if (has_cachedir(what
))
2978 delete_stale(file
, what
);
2985 create_newstat(void)
2989 error
= nvlist_alloc(&walk_arg
.new_nvlp
, NV_UNIQUE_NAME
, 0);
2992 * Not fatal - we can still create archive
2994 walk_arg
.new_nvlp
= NULL
;
2995 bam_error(_("failed to create stat data: %s\n"),
3001 walk_list(char *root
, filelist_t
*flistp
)
3003 char path
[PATH_MAX
];
3006 for (lp
= flistp
->head
; lp
; lp
= lp
->next
) {
3008 * Don't follow symlinks. A symlink must refer to
3009 * a file that would appear in the archive through
3010 * a direct reference. This matches the archive
3011 * construction behavior.
3013 (void) snprintf(path
, sizeof (path
), "%s%s", root
, lp
->line
);
3014 if (nftw(path
, cmpstat
, 20, FTW_PHYS
) == -1) {
3015 if (is_flag_on(UPDATE_ERROR
))
3018 * Some files may not exist.
3019 * For example: etc/rtc_config on a x86 diskless system
3020 * Emit verbose message only
3023 bam_print(_("cannot find: %s: %s\n"),
3024 path
, strerror(errno
));
3028 return (BAM_SUCCESS
);
3032 * Update the timestamp file.
3035 update_timestamp(char *root
)
3037 char timestamp_path
[PATH_MAX
];
3039 /* this path length has already been checked in check_flags_and_files */
3040 (void) snprintf(timestamp_path
, sizeof (timestamp_path
), "%s%s", root
,
3041 FILE_STAT_TIMESTAMP
);
3044 * recreate the timestamp file. Since an outdated or absent timestamp
3045 * file translates in a complete rebuild of the archive cache, notify
3046 * the user of the performance issue.
3048 if (creat(timestamp_path
, FILE_STAT_MODE
) < 0) {
3049 bam_error(_("failed to open file: %s: %s\n"), timestamp_path
,
3051 bam_error(_("failed to update the timestamp file, next"
3052 " archive update may experience reduced performance\n"));
3060 char path
[PATH_MAX
];
3061 char path2
[PATH_MAX
];
3064 int fd
, wrote
, error
;
3068 error
= nvlist_pack(walk_arg
.new_nvlp
, &nstat
, &sz
,
3071 bam_error(_("failed to pack stat data: %s\n"),
3076 (void) snprintf(path
, sizeof (path
), "%s%s", root
, FILE_STAT_TMP
);
3077 fd
= open(path
, O_RDWR
|O_CREAT
|O_TRUNC
, FILE_STAT_MODE
);
3079 bam_error(_("failed to open file: %s: %s\n"), path
,
3084 wrote
= write(fd
, nstat
, sz
);
3086 bam_error(_("write to file failed: %s: %s\n"), path
,
3095 (void) snprintf(path2
, sizeof (path2
), "%s%s", root
, FILE_STAT
);
3096 if (rename(path
, path2
) != 0) {
3097 bam_error(_("rename to file failed: %s: %s\n"), path2
,
3102 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3105 clear_walk_args(void)
3107 nvlist_free(walk_arg
.old_nvlp
);
3108 nvlist_free(walk_arg
.new_nvlp
);
3109 if (walk_arg
.sparcfile
)
3110 (void) fclose(walk_arg
.sparcfile
);
3111 walk_arg
.old_nvlp
= NULL
;
3112 walk_arg
.new_nvlp
= NULL
;
3113 walk_arg
.sparcfile
= NULL
;
3118 * 0 - no update necessary
3119 * 1 - update required.
3120 * BAM_ERROR (-1) - An error occurred
3122 * Special handling for check (-n):
3123 * ================================
3124 * The check (-n) option produces parseable output.
3125 * To do this, we suppress all stdout messages unrelated
3126 * to out of sync files.
3127 * All stderr messages are still printed though.
3131 update_required(char *root
)
3134 char path
[PATH_MAX
];
3136 filelist_t
*flistp
= &flist
;
3139 flistp
->head
= flistp
->tail
= NULL
;
3142 set_flag(IS_SPARC_TARGET
);
3145 * Check if cache directories and archives are present
3148 ret
= check_flags_and_files(root
);
3153 * In certain deployment scenarios, filestat may not
3154 * exist. Do not stop the boot process, but trigger an update
3155 * of the archives (which will recreate filestat.ramdisk).
3157 if (bam_smf_check
) {
3158 (void) snprintf(path
, sizeof (path
), "%s%s", root
, FILE_STAT
);
3159 if (stat(path
, &sb
) != 0) {
3160 (void) creat(NEED_UPDATE_FILE
, 0644);
3168 * Check if the archive contains files that are no longer
3169 * present on the root filesystem.
3174 * read list of files
3176 if (read_list(root
, flistp
) != BAM_SUCCESS
) {
3181 assert(flistp
->head
&& flistp
->tail
);
3184 * At this point either the update is required
3185 * or the decision is pending. In either case
3186 * we need to create new stat nvlist
3190 * This walk does 2 things:
3191 * - gets new stat data for every file
3192 * - (optional) compare old and new stat data
3194 ret
= walk_list(root
, &flist
);
3196 /* done with the file list */
3197 filelist_free(flistp
);
3199 /* something went wrong */
3201 if (ret
== BAM_ERROR
) {
3202 bam_error(_("Failed to gather cache files, archives "
3203 "generation aborted\n"));
3207 if (walk_arg
.new_nvlp
== NULL
) {
3208 if (walk_arg
.sparcfile
!= NULL
)
3209 (void) fclose(walk_arg
.sparcfile
);
3210 bam_error(_("cannot create new stat data\n"));
3213 /* If nothing was updated, discard newstat. */
3215 if (!is_dir_flag_on(FILE32
, NEED_UPDATE
) &&
3216 !is_dir_flag_on(FILE64
, NEED_UPDATE
)) {
3221 if (walk_arg
.sparcfile
!= NULL
)
3222 (void) fclose(walk_arg
.sparcfile
);
3230 char cmd
[PATH_MAX
+ 30];
3232 (void) snprintf(cmd
, sizeof (cmd
), "%s -f \"%s\" 2>/dev/null",
3235 return (exec_cmd(cmd
, NULL
));
3239 do_archive_copy(char *source
, char *dest
)
3244 /* the equivalent of mv archive-new-$pid boot_archive */
3245 if (rename(source
, dest
) != 0) {
3246 (void) unlink(source
);
3250 if (flushfs(bam_root
) != 0)
3253 return (BAM_SUCCESS
);
3257 check_cmdline(filelist_t flist
)
3261 for (lp
= flist
.head
; lp
; lp
= lp
->next
) {
3262 if (strstr(lp
->line
, "Error:") != NULL
||
3263 strstr(lp
->line
, "Inode number overflow") != NULL
) {
3264 (void) fprintf(stderr
, "%s\n", lp
->line
);
3269 return (BAM_SUCCESS
);
3273 dump_errormsg(filelist_t flist
)
3277 for (lp
= flist
.head
; lp
; lp
= lp
->next
)
3278 (void) fprintf(stderr
, "%s\n", lp
->line
);
3282 check_archive(char *dest
)
3286 if (stat(dest
, &sb
) != 0 || !S_ISREG(sb
.st_mode
) ||
3287 sb
.st_size
< 10000) {
3288 bam_error(_("archive file %s not generated correctly\n"), dest
);
3289 (void) unlink(dest
);
3293 return (BAM_SUCCESS
);
3300 libzfs_handle_t
*hdl
;
3301 be_node_list_t
*be_nodes
= NULL
;
3302 be_node_list_t
*cur_be
;
3303 boolean_t be_exist
= B_FALSE
;
3304 char ds_path
[ZFS_MAX_DATASET_NAME_LEN
];
3309 * Get dataset for mountpoint
3311 if ((hdl
= libzfs_init()) == NULL
)
3314 if ((zhp
= zfs_path_to_zhandle(hdl
, root
,
3315 ZFS_TYPE_FILESYSTEM
)) == NULL
) {
3320 (void) strlcpy(ds_path
, zfs_get_name(zhp
), sizeof (ds_path
));
3323 * Check if the current dataset is BE
3325 if (be_list(NULL
, &be_nodes
) == BE_SUCCESS
) {
3326 for (cur_be
= be_nodes
; cur_be
!= NULL
;
3327 cur_be
= cur_be
->be_next_node
) {
3330 * Because we guarantee that cur_be->be_root_ds
3331 * is null-terminated by internal data structure,
3332 * we can safely use strcmp()
3334 if (strcmp(ds_path
, cur_be
->be_root_ds
) == 0) {
3339 be_free_list(be_nodes
);
3348 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
3353 if (access(MKISOFS_PATH
, X_OK
) == 0)
3358 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3361 create_sparc_archive(char *archive
, char *tempname
, char *bootblk
, char *list
)
3364 char cmdline
[3 * PATH_MAX
+ 64];
3365 filelist_t flist
= {0};
3366 const char *func
= "create_sparc_archive()";
3368 if (access(bootblk
, R_OK
) == 1) {
3369 bam_error(_("unable to access bootblk file : %s\n"), bootblk
);
3374 * Prepare mkisofs command line and execute it
3376 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s -G %s -o \"%s\" "
3377 "-path-list \"%s\" 2>&1", MKISOFS_PATH
, MKISO_PARAMS
, bootblk
,
3380 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3382 ret
= exec_cmd(cmdline
, &flist
);
3383 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3384 dump_errormsg(flist
);
3388 filelist_free(&flist
);
3391 * Prepare dd command line to copy the bootblk on the new archive and
3394 (void) snprintf(cmdline
, sizeof (cmdline
), "%s if=\"%s\" of=\"%s\""
3395 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR
,
3398 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3400 ret
= exec_cmd(cmdline
, &flist
);
3401 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
)
3404 filelist_free(&flist
);
3406 /* Did we get a valid archive ? */
3407 if (check_archive(tempname
) == BAM_ERROR
)
3410 return (do_archive_copy(tempname
, archive
));
3413 filelist_free(&flist
);
3414 bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline
);
3415 (void) unlink(tempname
);
3420 from_733(unsigned char *s
)
3423 unsigned int ret
= 0;
3425 for (i
= 0; i
< 4; i
++)
3426 ret
|= s
[i
] << (8 * i
);
3432 to_733(unsigned char *s
, unsigned int val
)
3436 for (i
= 0; i
< 4; i
++)
3437 s
[i
] = s
[7-i
] = (val
>> (8 * i
)) & 0xFF;
3441 * creates sha1 hash of archive
3444 digest_archive(const char *archive
)
3451 (void) asprintf(&archive_hash
, "%s.hash", archive
);
3452 if (archive_hash
== NULL
)
3455 if ((ret
= bootadm_digest(archive
, &hash
)) == BAM_ERROR
) {
3460 fp
= fopen(archive_hash
, "w");
3467 (void) fprintf(fp
, "%s\n", hash
);
3471 return (BAM_SUCCESS
);
3475 * Extends the current boot archive without recreating it from scratch
3478 extend_iso_archive(char *archive
, char *tempname
, char *update_dir
)
3480 int fd
= -1, newfd
= -1, ret
, i
;
3481 int next_session
= 0, new_size
= 0;
3482 char cmdline
[3 * PATH_MAX
+ 64];
3483 const char *func
= "extend_iso_archive()";
3484 filelist_t flist
= {0};
3485 struct iso_pdesc saved_desc
[MAX_IVDs
];
3487 fd
= open(archive
, O_RDWR
);
3490 bam_error(_("failed to open file: %s: %s\n"),
3491 archive
, strerror(errno
));
3496 * A partial read is likely due to a corrupted file
3498 ret
= pread64(fd
, saved_desc
, sizeof (saved_desc
),
3499 VOLDESC_OFF
* CD_BLOCK
);
3500 if (ret
!= sizeof (saved_desc
)) {
3502 bam_error(_("read failed for file: %s: %s\n"),
3503 archive
, strerror(errno
));
3507 if (memcmp(saved_desc
[0].type
, "\1CD001", 6)) {
3509 bam_error(_("iso descriptor signature for %s is "
3510 "invalid\n"), archive
);
3515 * Read primary descriptor and locate next_session offset (it should
3516 * point to the end of the archive)
3518 next_session
= P2ROUNDUP(from_733(saved_desc
[0].volume_space_size
), 16);
3520 (void) snprintf(cmdline
, sizeof (cmdline
), "%s -C 16,%d -M %s %s -o \""
3521 "%s\" \"%s\" 2>&1", MKISOFS_PATH
, next_session
, archive
,
3522 MKISO_PARAMS
, tempname
, update_dir
);
3524 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3526 ret
= exec_cmd(cmdline
, &flist
);
3527 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3529 bam_error(_("Command '%s' failed while generating "
3530 "multisession archive\n"), cmdline
);
3531 dump_errormsg(flist
);
3535 filelist_free(&flist
);
3537 newfd
= open(tempname
, O_RDONLY
);
3540 bam_error(_("failed to open file: %s: %s\n"),
3541 archive
, strerror(errno
));
3545 ret
= pread64(newfd
, saved_desc
, sizeof (saved_desc
),
3546 VOLDESC_OFF
* CD_BLOCK
);
3547 if (ret
!= sizeof (saved_desc
)) {
3549 bam_error(_("read failed for file: %s: %s\n"),
3550 archive
, strerror(errno
));
3554 if (memcmp(saved_desc
[0].type
, "\1CD001", 6)) {
3556 bam_error(_("iso descriptor signature for %s is "
3557 "invalid\n"), archive
);
3561 new_size
= from_733(saved_desc
[0].volume_space_size
) + next_session
;
3562 to_733(saved_desc
[0].volume_space_size
, new_size
);
3564 for (i
= 1; i
< MAX_IVDs
; i
++) {
3565 if (saved_desc
[i
].type
[0] == (unsigned char)255)
3567 if (memcmp(saved_desc
[i
].id
, "CD001", 5))
3571 bam_print("%s: Updating descriptor entry [%d]\n", func
,
3574 to_733(saved_desc
[i
].volume_space_size
, new_size
);
3577 ret
= pwrite64(fd
, saved_desc
, DVD_BLOCK
, VOLDESC_OFF
*CD_BLOCK
);
3578 if (ret
!= DVD_BLOCK
) {
3580 bam_error(_("write to file failed: %s: %s\n"),
3581 archive
, strerror(errno
));
3584 (void) close(newfd
);
3594 bam_error(_("failed to close file: %s: %s\n"),
3595 archive
, strerror(errno
));
3600 (void) snprintf(cmdline
, sizeof (cmdline
), "%s if=%s of=%s bs=32k "
3601 "seek=%d conv=sync 2>&1", DD_PATH_USR
, tempname
, archive
,
3604 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3606 ret
= exec_cmd(cmdline
, &flist
);
3607 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3609 bam_error(_("Command '%s' failed while generating "
3610 "multisession archive\n"), cmdline
);
3613 filelist_free(&flist
);
3615 (void) unlink(tempname
);
3617 if (digest_archive(archive
) == BAM_ERROR
&& bam_verbose
)
3618 bam_print("boot archive hashing failed\n");
3620 if (flushfs(bam_root
) != 0)
3624 bam_print("boot archive updated successfully\n");
3626 return (BAM_SUCCESS
);
3629 filelist_free(&flist
);
3634 (void) close(newfd
);
3639 create_x86_archive(char *archive
, char *tempname
, char *update_dir
)
3642 char cmdline
[3 * PATH_MAX
+ 64];
3643 filelist_t flist
= {0};
3644 const char *func
= "create_x86_archive()";
3646 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s -o \"%s\" \"%s\" "
3647 "2>&1", MKISOFS_PATH
, MKISO_PARAMS
, tempname
, update_dir
);
3649 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3651 ret
= exec_cmd(cmdline
, &flist
);
3652 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3653 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3655 dump_errormsg(flist
);
3656 filelist_free(&flist
);
3657 (void) unlink(tempname
);
3661 filelist_free(&flist
);
3663 if (check_archive(tempname
) == BAM_ERROR
)
3666 return (do_archive_copy(tempname
, archive
));
3670 mkisofs_archive(char *root
, int what
)
3673 char temp
[PATH_MAX
];
3674 char bootblk
[PATH_MAX
];
3675 char boot_archive
[PATH_MAX
];
3677 if (what
== FILE64
&& !is_flag_on(IS_SPARC_TARGET
))
3678 ret
= snprintf(temp
, sizeof (temp
),
3679 "%s%s%s/amd64/archive-new-%d", root
, ARCHIVE_PREFIX
,
3680 get_machine(), getpid());
3682 ret
= snprintf(temp
, sizeof (temp
), "%s%s%s/archive-new-%d",
3683 root
, ARCHIVE_PREFIX
, get_machine(), getpid());
3685 if (ret
>= sizeof (temp
))
3688 if (what
== FILE64
&& !is_flag_on(IS_SPARC_TARGET
))
3689 ret
= snprintf(boot_archive
, sizeof (boot_archive
),
3690 "%s%s%s/amd64%s", root
, ARCHIVE_PREFIX
, get_machine(),
3693 ret
= snprintf(boot_archive
, sizeof (boot_archive
),
3694 "%s%s%s%s", root
, ARCHIVE_PREFIX
, get_machine(),
3697 if (ret
>= sizeof (boot_archive
))
3700 bam_print("updating %s\n", boot_archive
);
3702 if (is_flag_on(IS_SPARC_TARGET
)) {
3703 ret
= snprintf(bootblk
, sizeof (bootblk
),
3704 "%s/platform/%s/lib/fs/hsfs/bootblk", root
, get_machine());
3705 if (ret
>= sizeof (bootblk
))
3708 ret
= create_sparc_archive(boot_archive
, temp
, bootblk
,
3709 get_cachedir(what
));
3711 if (!is_dir_flag_on(what
, NO_MULTI
)) {
3713 bam_print("Attempting to extend x86 archive: "
3714 "%s\n", boot_archive
);
3716 ret
= extend_iso_archive(boot_archive
, temp
,
3717 get_updatedir(what
));
3718 if (ret
== BAM_SUCCESS
) {
3720 bam_print("Successfully extended %s\n",
3723 (void) rmdir_r(get_updatedir(what
));
3724 return (BAM_SUCCESS
);
3728 * The boot archive will be recreated from scratch. We get here
3729 * if at least one of these conditions is true:
3730 * - bootadm was called without the -e switch
3731 * - the archive (or the archive cache) doesn't exist
3732 * - archive size is bigger than BA_SIZE_MAX
3733 * - more than COUNT_MAX files need to be updated
3734 * - an error occourred either populating the /updates directory
3735 * or extend_iso_archive() failed
3738 bam_print("Unable to extend %s... rebuilding archive\n",
3741 if (get_updatedir(what
)[0] != '\0')
3742 (void) rmdir_r(get_updatedir(what
));
3745 ret
= create_x86_archive(boot_archive
, temp
,
3746 get_cachedir(what
));
3749 if (digest_archive(boot_archive
) == BAM_ERROR
&& bam_verbose
)
3750 bam_print("boot archive hashing failed\n");
3752 if (ret
== BAM_SUCCESS
&& bam_verbose
)
3753 bam_print("Successfully created %s\n", boot_archive
);
3758 bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3764 create_ramdisk(char *root
)
3766 char *cmdline
, path
[PATH_MAX
];
3769 int ret
, what
, status
= BAM_SUCCESS
;
3771 /* If there is mkisofs, use it to create the required archives */
3773 for (what
= FILE32
; what
< CACHEDIR_NUM
; what
++) {
3774 if (has_cachedir(what
) && is_dir_flag_on(what
,
3776 ret
= mkisofs_archive(root
, what
);
3785 * Else setup command args for create_ramdisk.ksh for the UFS archives
3786 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3789 bam_print("mkisofs not found, creating UFS archive\n");
3791 (void) snprintf(path
, sizeof (path
), "%s/%s", root
, CREATE_RAMDISK
);
3792 if (stat(path
, &sb
) != 0) {
3793 bam_error(_("archive creation file not found: %s: %s\n"),
3794 path
, strerror(errno
));
3798 if (is_safe_exec(path
) == BAM_ERROR
)
3801 len
= strlen(path
) + strlen(root
) + 10; /* room for space + -R */
3802 if (bam_alt_platform
)
3803 len
+= strlen(bam_platform
) + strlen("-p ");
3804 cmdline
= s_calloc(1, len
);
3806 if (bam_alt_platform
) {
3807 assert(strlen(root
) > 1);
3808 (void) snprintf(cmdline
, len
, "%s -p %s -R %s",
3809 path
, bam_platform
, root
);
3810 /* chop off / at the end */
3811 cmdline
[strlen(cmdline
) - 1] = '\0';
3812 } else if (strlen(root
) > 1) {
3813 (void) snprintf(cmdline
, len
, "%s -R %s", path
, root
);
3814 /* chop off / at the end */
3815 cmdline
[strlen(cmdline
) - 1] = '\0';
3817 (void) snprintf(cmdline
, len
, "%s", path
);
3819 if (exec_cmd(cmdline
, NULL
) != 0) {
3820 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3827 * The existence of the expected archives used to be
3828 * verified here. This check is done in create_ramdisk as
3829 * it needs to be in sync with the altroot operated upon.
3831 return (BAM_SUCCESS
);
3835 * Checks if target filesystem is on a ramdisk
3838 * When in doubt assume it is not a ramdisk.
3841 is_ramdisk(char *root
)
3843 struct extmnttab mnt
;
3846 char mntpt
[PATH_MAX
];
3850 * There are 3 situations where creating archive is
3852 * - create boot_archive on a lofi-mounted boot_archive
3853 * - create it on a ramdisk which is the root filesystem
3854 * - create it on a ramdisk mounted somewhere else
3855 * The first is not easy to detect and checking for it is not
3857 * The other two conditions are handled here
3859 fp
= fopen(MNTTAB
, "r");
3861 bam_error(_("failed to open file: %s: %s\n"),
3862 MNTTAB
, strerror(errno
));
3869 * Remove any trailing / from the mount point
3871 (void) strlcpy(mntpt
, root
, sizeof (mntpt
));
3872 if (strcmp(root
, "/") != 0) {
3873 cp
= mntpt
+ strlen(mntpt
) - 1;
3878 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
3879 if (strcmp(mnt
.mnt_mountp
, mntpt
) == 0) {
3887 bam_error(_("alternate root %s not in mnttab\n"),
3893 if (strncmp(mnt
.mnt_special
, RAMDISK_SPECIAL
,
3894 strlen(RAMDISK_SPECIAL
)) == 0) {
3896 bam_error(_("%s is on a ramdisk device\n"), bam_root
);
3907 is_boot_archive(char *root
)
3909 char path
[PATH_MAX
];
3912 const char *fcn
= "is_boot_archive()";
3915 * We can't create an archive without the create_ramdisk script
3917 (void) snprintf(path
, sizeof (path
), "%s/%s", root
, CREATE_RAMDISK
);
3918 error
= stat(path
, &sb
);
3919 INJECT_ERROR1("NOT_ARCHIVE_BASED", error
= -1);
3922 bam_print(_("file not found: %s\n"), path
);
3923 BAM_DPRINTF(("%s: not a boot archive based Solaris "
3924 "instance: %s\n", fcn
, root
));
3928 BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
3934 * Need to call this for anything that operates on the GRUB menu
3935 * In the x86 live upgrade case the directory /boot/grub may be present
3936 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3937 * is to check for the presence of the stage2 binary which is present
3938 * only on GRUB targets (even on x86 boot partitions). Checking for the
3939 * presence of the multiboot binary is not correct as it is not present
3940 * on x86 boot partitions.
3943 is_grub(const char *root
)
3945 char path
[PATH_MAX
];
3948 boolean_t grub
= B_FALSE
;
3949 const char *res
= NULL
;
3950 const char *fcn
= "is_grub()";
3952 /* grub is disabled by default */
3953 if ((defp
= defopen_r(BE_DEFAULTS
)) == NULL
) {
3956 res
= defread_r(BE_DFLT_BE_HAS_GRUB
, defp
);
3957 if (res
!= NULL
&& res
[0] != '\0') {
3958 if (strcasecmp(res
, "true") == 0)
3964 if (grub
== B_TRUE
) {
3965 (void) snprintf(path
, sizeof (path
), "%s%s", root
, GRUB_STAGE2
);
3966 if (stat(path
, &sb
) == -1) {
3967 BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
3982 const char *fcn
= "is_zfs()";
3984 ret
= statvfs(root
, &vfs
);
3985 INJECT_ERROR1("STATVFS_ZFS", ret
= 1);
3987 bam_error(_("statvfs failed for %s: %s\n"), root
,
3992 if (strncmp(vfs
.f_basetype
, "zfs", strlen("zfs")) == 0) {
3993 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn
, root
));
3996 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn
, root
));
4006 const char *fcn
= "is_pcfs()";
4008 ret
= statvfs(root
, &vfs
);
4009 INJECT_ERROR1("STATVFS_PCFS", ret
= 1);
4011 bam_error(_("statvfs failed for %s: %s\n"), root
,
4016 if (strncmp(vfs
.f_basetype
, "pcfs", strlen("pcfs")) == 0) {
4017 BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn
, root
));
4020 BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4027 is_readonly(char *root
)
4031 char testfile
[PATH_MAX
];
4032 const char *fcn
= "is_readonly()";
4035 * Using statvfs() to check for a read-only filesystem is not
4036 * reliable. The only way to reliably test is to attempt to
4039 (void) snprintf(testfile
, sizeof (testfile
), "%s/%s.%d",
4040 root
, BOOTADM_RDONLY_TEST
, getpid());
4042 (void) unlink(testfile
);
4045 fd
= open(testfile
, O_RDWR
|O_CREAT
|O_EXCL
, 0644);
4047 INJECT_ERROR2("RDONLY_TEST_ERROR", fd
= -1, error
= EACCES
);
4048 if (fd
== -1 && error
== EROFS
) {
4049 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn
, root
));
4051 } else if (fd
== -1) {
4052 bam_error(_("error during read-only test on %s: %s\n"),
4053 root
, strerror(error
));
4057 (void) unlink(testfile
);
4059 BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn
, root
));
4064 update_archive(char *root
, char *opt
)
4069 assert(opt
== NULL
);
4075 * Never update non-BE root in update_all
4077 if (!is_be(root
) && bam_update_all
)
4078 return (BAM_SUCCESS
);
4080 * root must belong to a boot archive based OS,
4082 if (!is_boot_archive(root
)) {
4084 * Emit message only if not in context of update_all.
4085 * If in update_all, emit only if verbose flag is set.
4087 if (!bam_update_all
|| bam_verbose
)
4088 bam_print(_("%s: not a boot archive based Solaris "
4089 "instance\n"), root
);
4094 * If smf check is requested when / is writable (can happen
4095 * on first reboot following an upgrade because service
4096 * dependency is messed up), skip the check.
4098 if (bam_smf_check
&& !bam_root_readonly
&& !is_zfs(root
))
4099 return (BAM_SUCCESS
);
4102 * Don't generate archive on ramdisk.
4104 if (is_ramdisk(root
))
4105 return (BAM_SUCCESS
);
4108 * root must be writable. This check applies to alternate
4109 * root (-R option); bam_root_readonly applies to '/' only.
4110 * The behaviour translates into being the one of a 'check'.
4112 if (!bam_smf_check
&& !bam_check
&& is_readonly(root
)) {
4113 set_flag(RDONLY_FSCHK
);
4118 * Now check if an update is really needed.
4120 ret
= update_required(root
);
4123 * The check command (-n) is *not* a dry run.
4124 * It only checks if the archive is in sync.
4125 * A readonly filesystem has to be considered an error only if an update
4128 if (bam_nowrite()) {
4129 if (is_flag_on(RDONLY_FSCHK
)) {
4130 bam_check
= bam_saved_check
;
4132 bam_error(_("%s filesystem is read-only, "
4133 "skipping archives update\n"), root
);
4135 return ((ret
!= 0) ? BAM_ERROR
: BAM_SUCCESS
);
4138 bam_exit((ret
!= 0) ? 1 : 0);
4142 /* create the ramdisk */
4143 ret
= create_ramdisk(root
);
4147 * if the archive is updated, save the new stat data and update the
4150 if (ret
== 0 && walk_arg
.new_nvlp
!= NULL
) {
4152 update_timestamp(root
);
4163 char *special
= get_special("/");
4166 if (special
== NULL
)
4169 if (*special
== '/') {
4174 if ((p
= strchr(special
, '/')) != NULL
)
4181 synchronize_BE_menu(void)
4184 char cmdline
[PATH_MAX
];
4185 char cksum_line
[PATH_MAX
];
4186 filelist_t flist
= {0};
4187 char *old_cksum_str
;
4190 char *curr_cksum_str
;
4191 char *curr_size_str
;
4199 const char *fcn
= "synchronize_BE_menu()";
4201 BAM_DPRINTF(("%s: entered. No args\n", fcn
));
4203 /* Check if findroot enabled LU BE */
4204 if (stat(FINDROOT_INSTALLGRUB
, &sb
) != 0) {
4205 BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn
));
4206 return (BAM_SUCCESS
);
4209 if (stat(LU_MENU_CKSUM
, &sb
) != 0) {
4210 BAM_DPRINTF(("%s: checksum file absent: %s\n",
4211 fcn
, LU_MENU_CKSUM
));
4215 cfp
= fopen(LU_MENU_CKSUM
, "r");
4216 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp
= NULL
);
4218 bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4222 BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn
, LU_MENU_CKSUM
));
4225 while (s_fgets(cksum_line
, sizeof (cksum_line
), cfp
) != NULL
) {
4226 INJECT_ERROR1("MULTIPLE_CKSUM", found
= 1);
4228 bam_error(_("multiple checksums for GRUB menu in "
4229 "checksum file: %s\n"), LU_MENU_CKSUM
);
4235 BAM_DPRINTF(("%s: read checksum file: %s\n", fcn
, LU_MENU_CKSUM
));
4238 old_cksum_str
= strtok(cksum_line
, " \t");
4239 old_size_str
= strtok(NULL
, " \t");
4240 old_file
= strtok(NULL
, " \t");
4242 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str
= NULL
);
4243 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str
= NULL
);
4244 INJECT_ERROR1("OLD_FILE_NULL", old_file
= NULL
);
4245 if (old_cksum_str
== NULL
|| old_size_str
== NULL
|| old_file
== NULL
) {
4246 bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4250 BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn
, LU_MENU_CKSUM
));
4252 /* Get checksum of current menu */
4253 pool
= find_root_pool();
4255 mntpt
= mount_top_dataset(pool
, &mnted
);
4256 if (mntpt
== NULL
) {
4257 bam_error(_("failed to mount top dataset for %s\n"),
4262 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s%s",
4263 CKSUM
, mntpt
, GRUB_MENU
);
4265 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s",
4268 ret
= exec_cmd(cmdline
, &flist
);
4270 (void) umount_top_dataset(pool
, mnted
, mntpt
);
4273 INJECT_ERROR1("GET_CURR_CKSUM", ret
= 1);
4275 bam_error(_("error generating checksum of GRUB menu\n"));
4278 BAM_DPRINTF(("%s: successfully generated checksum\n", fcn
));
4280 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist
.head
= NULL
);
4281 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
4282 bam_error(_("bad checksum generated for GRUB menu\n"));
4283 filelist_free(&flist
);
4286 BAM_DPRINTF(("%s: generated checksum output valid\n", fcn
));
4288 curr_cksum_str
= strtok(flist
.head
->line
, " \t");
4289 curr_size_str
= strtok(NULL
, " \t");
4290 curr_file
= strtok(NULL
, " \t");
4292 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str
= NULL
);
4293 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str
= NULL
);
4294 INJECT_ERROR1("CURR_FILE_NULL", curr_file
= NULL
);
4295 if (curr_cksum_str
== NULL
|| curr_size_str
== NULL
||
4296 curr_file
== NULL
) {
4297 bam_error(_("error parsing checksum generated "
4298 "for GRUB menu\n"));
4299 filelist_free(&flist
);
4302 BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn
));
4304 if (strcmp(old_cksum_str
, curr_cksum_str
) == 0 &&
4305 strcmp(old_size_str
, curr_size_str
) == 0 &&
4306 strcmp(old_file
, curr_file
) == 0) {
4307 filelist_free(&flist
);
4308 BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn
));
4309 return (BAM_SUCCESS
);
4312 filelist_free(&flist
);
4314 /* cksum doesn't match - the menu has changed */
4315 BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn
));
4318 bam_print(_("propagating updated GRUB menu\n"));
4320 (void) snprintf(cmdline
, sizeof (cmdline
),
4321 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4322 LULIB
, LULIB_PROPAGATE_FILE
, GRUB_MENU
);
4323 ret
= exec_cmd(cmdline
, NULL
);
4324 INJECT_ERROR1("PROPAGATE_MENU", ret
= 1);
4326 bam_error(_("error propagating updated GRUB menu\n"));
4329 BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn
));
4331 (void) snprintf(cmdline
, sizeof (cmdline
), "/bin/cp %s %s > /dev/null",
4332 GRUB_MENU
, GRUB_BACKUP_MENU
);
4333 ret
= exec_cmd(cmdline
, NULL
);
4334 INJECT_ERROR1("CREATE_BACKUP", ret
= 1);
4336 bam_error(_("failed to create backup for GRUB menu: %s\n"),
4340 BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4341 fcn
, GRUB_BACKUP_MENU
));
4343 (void) snprintf(cmdline
, sizeof (cmdline
),
4344 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4345 LULIB
, LULIB_PROPAGATE_FILE
, GRUB_BACKUP_MENU
);
4346 ret
= exec_cmd(cmdline
, NULL
);
4347 INJECT_ERROR1("PROPAGATE_BACKUP", ret
= 1);
4349 bam_error(_("error propagating backup GRUB menu: %s\n"),
4353 BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4354 fcn
, GRUB_BACKUP_MENU
));
4356 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s > %s",
4357 CKSUM
, GRUB_MENU
, LU_MENU_CKSUM
);
4358 ret
= exec_cmd(cmdline
, NULL
);
4359 INJECT_ERROR1("CREATE_CKSUM_FILE", ret
= 1);
4361 bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4365 BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4366 fcn
, LU_MENU_CKSUM
));
4368 (void) snprintf(cmdline
, sizeof (cmdline
),
4369 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4370 LULIB
, LULIB_PROPAGATE_FILE
, LU_MENU_CKSUM
);
4371 ret
= exec_cmd(cmdline
, NULL
);
4372 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret
= 1);
4374 bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4378 BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4379 fcn
, LU_MENU_CKSUM
));
4381 return (BAM_SUCCESS
);
4385 update_all(char *root
, char *opt
)
4387 struct extmnttab mnt
;
4390 char multibt
[PATH_MAX
];
4391 char creatram
[PATH_MAX
];
4392 error_t ret
= BAM_SUCCESS
;
4395 assert(opt
== NULL
);
4397 if (bam_rootlen
!= 1 || *root
!= '/') {
4398 elide_trailing_slash(root
, multibt
, sizeof (multibt
));
4399 bam_error(_("an alternate root (%s) cannot be used with this "
4400 "sub-command\n"), multibt
);
4405 * First update archive for current root
4407 if (update_archive(root
, opt
) != BAM_SUCCESS
)
4410 if (ret
== BAM_ERROR
)
4414 * Now walk the mount table, performing archive update
4415 * for all mounted Newboot root filesystems
4417 fp
= fopen(MNTTAB
, "r");
4419 bam_error(_("failed to open file: %s: %s\n"),
4420 MNTTAB
, strerror(errno
));
4427 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
4428 if (mnt
.mnt_special
== NULL
)
4430 if ((strcmp(mnt
.mnt_fstype
, MNTTYPE_ZFS
) != 0) &&
4431 (strncmp(mnt
.mnt_special
, "/dev/", strlen("/dev/")) != 0))
4433 if (strcmp(mnt
.mnt_mountp
, "/") == 0)
4436 (void) snprintf(creatram
, sizeof (creatram
), "%s/%s",
4437 mnt
.mnt_mountp
, CREATE_RAMDISK
);
4439 if (stat(creatram
, &sb
) == -1)
4443 * We put a trailing slash to be consistent with root = "/"
4444 * case, such that we don't have to print // in some cases.
4446 (void) snprintf(rootbuf
, sizeof (rootbuf
), "%s/",
4448 bam_rootlen
= strlen(rootbuf
);
4451 * It's possible that other mounts may be an alternate boot
4452 * architecture, so check it again.
4454 if ((get_boot_cap(rootbuf
) != BAM_SUCCESS
) ||
4455 (update_archive(rootbuf
, opt
) != BAM_SUCCESS
))
4463 * We no longer use biosdev for Live Upgrade. Hence
4464 * there is no need to defer (to shutdown time) any fdisk
4467 if (stat(GRUB_fdisk
, &sb
) == 0 || stat(GRUB_fdisk_target
, &sb
) == 0) {
4468 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4469 "Not supported.\n"), GRUB_fdisk
, GRUB_fdisk_target
);
4473 * If user has updated menu in current BE, propagate the
4474 * updates to all BEs.
4476 if (sync_menu
&& synchronize_BE_menu() != BAM_SUCCESS
)
4483 append_line(menu_t
*mp
, line_t
*lp
)
4485 if (mp
->start
== NULL
) {
4495 unlink_line(menu_t
*mp
, line_t
*lp
)
4497 /* unlink from list */
4499 lp
->prev
->next
= lp
->next
;
4501 mp
->start
= lp
->next
;
4503 lp
->next
->prev
= lp
->prev
;
4509 boot_entry_new(menu_t
*mp
, line_t
*start
, line_t
*end
)
4511 entry_t
*ent
, *prev
;
4512 const char *fcn
= "boot_entry_new()";
4518 ent
= s_calloc(1, sizeof (entry_t
));
4519 BAM_DPRINTF(("%s: new boot entry alloced\n", fcn
));
4523 if (mp
->entries
== NULL
) {
4525 BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn
));
4534 BAM_DPRINTF(("%s: new boot entry linked in\n", fcn
));
4539 boot_entry_addline(entry_t
*ent
, line_t
*lp
)
4546 * Check whether cmd matches the one indexed by which, and whether arg matches
4547 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4548 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4549 * strstr(), so it can be a partial match.
4552 check_cmd(const char *cmd
, const int which
, const char *arg
, const char *str
)
4555 const char *fcn
= "check_cmd()";
4557 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, arg
, str
));
4560 if ((strcmp(cmd
, menu_cmds
[which
]) != 0) &&
4561 (strcmp(cmd
, menu_cmds
[which
+ 1]) != 0)) {
4562 BAM_DPRINTF(("%s: command %s does not match %s\n",
4563 fcn
, cmd
, menu_cmds
[which
]));
4566 ret
= (strstr(arg
, str
) != NULL
);
4571 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
4573 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
4580 kernel_parser(entry_t
*entry
, char *cmd
, char *arg
, int linenum
)
4582 const char *fcn
= "kernel_parser()";
4588 if (strcmp(cmd
, menu_cmds
[KERNEL_CMD
]) != 0 &&
4589 strcmp(cmd
, menu_cmds
[KERNEL_DOLLAR_CMD
]) != 0) {
4590 BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn
, cmd
));
4594 if (strncmp(arg
, DIRECT_BOOT_32
, sizeof (DIRECT_BOOT_32
) - 1) == 0) {
4595 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4597 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_32BIT
;
4598 } else if (strncmp(arg
, DIRECT_BOOT_KERNEL
,
4599 sizeof (DIRECT_BOOT_KERNEL
) - 1) == 0) {
4600 BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn
, arg
));
4601 entry
->flags
|= BAM_ENTRY_DBOOT
;
4602 } else if (strncmp(arg
, DIRECT_BOOT_64
,
4603 sizeof (DIRECT_BOOT_64
) - 1) == 0) {
4604 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4606 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_64BIT
;
4607 } else if (strncmp(arg
, DIRECT_BOOT_FAILSAFE_KERNEL
,
4608 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL
) - 1) == 0) {
4609 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4611 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_FAILSAFE
;
4612 } else if (strncmp(arg
, DIRECT_BOOT_FAILSAFE_32
,
4613 sizeof (DIRECT_BOOT_FAILSAFE_32
) - 1) == 0) {
4614 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4615 "flag: %s\n", fcn
, arg
));
4616 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_FAILSAFE
4618 } else if (strncmp(arg
, DIRECT_BOOT_FAILSAFE_64
,
4619 sizeof (DIRECT_BOOT_FAILSAFE_64
) - 1) == 0) {
4620 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4621 "flag: %s\n", fcn
, arg
));
4622 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_FAILSAFE
4624 } else if (strncmp(arg
, MULTI_BOOT
, sizeof (MULTI_BOOT
) - 1) == 0) {
4625 BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn
, arg
));
4626 entry
->flags
|= BAM_ENTRY_MULTIBOOT
;
4627 } else if (strncmp(arg
, MULTI_BOOT_FAILSAFE
,
4628 sizeof (MULTI_BOOT_FAILSAFE
) - 1) == 0) {
4629 BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4630 "flag: %s\n", fcn
, arg
));
4631 entry
->flags
|= BAM_ENTRY_MULTIBOOT
| BAM_ENTRY_FAILSAFE
;
4632 } else if (strstr(arg
, XEN_KERNEL_SUBSTR
)) {
4633 BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn
, arg
));
4634 entry
->flags
|= BAM_ENTRY_HV
;
4635 } else if (!(entry
->flags
& (BAM_ENTRY_BOOTADM
|BAM_ENTRY_LU
))) {
4636 BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn
, arg
));
4638 } else if (strncmp(arg
, KERNEL_PREFIX
, strlen(KERNEL_PREFIX
)) == 0 &&
4639 strstr(arg
, UNIX_SPACE
)) {
4640 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_32BIT
;
4641 } else if (strncmp(arg
, KERNEL_PREFIX
, strlen(KERNEL_PREFIX
)) == 0 &&
4642 strstr(arg
, AMD_UNIX_SPACE
)) {
4643 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_64BIT
;
4645 BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn
, arg
));
4646 bam_error(_("kernel command on line %d not recognized.\n"),
4651 return (BAM_SUCCESS
);
4655 module_parser(entry_t
*entry
, char *cmd
, char *arg
, int linenum
)
4657 const char *fcn
= "module_parser()";
4663 if (strcmp(cmd
, menu_cmds
[MODULE_CMD
]) != 0 &&
4664 strcmp(cmd
, menu_cmds
[MODULE_DOLLAR_CMD
]) != 0) {
4665 BAM_DPRINTF(("%s: not module cmd: %s\n", fcn
, cmd
));
4669 if (strcmp(arg
, DIRECT_BOOT_ARCHIVE
) == 0 ||
4670 strcmp(arg
, DIRECT_BOOT_ARCHIVE_32
) == 0 ||
4671 strcmp(arg
, DIRECT_BOOT_ARCHIVE_64
) == 0 ||
4672 strcmp(arg
, MULTIBOOT_ARCHIVE
) == 0 ||
4673 strcmp(arg
, FAILSAFE_ARCHIVE
) == 0 ||
4674 strcmp(arg
, FAILSAFE_ARCHIVE_32
) == 0 ||
4675 strcmp(arg
, FAILSAFE_ARCHIVE_64
) == 0 ||
4676 strcmp(arg
, XEN_KERNEL_MODULE_LINE
) == 0 ||
4677 strcmp(arg
, XEN_KERNEL_MODULE_LINE_ZFS
) == 0) {
4678 BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn
, arg
));
4679 return (BAM_SUCCESS
);
4680 } else if (!(entry
->flags
& BAM_ENTRY_BOOTADM
) &&
4681 !(entry
->flags
& BAM_ENTRY_LU
)) {
4682 /* don't emit warning for hand entries */
4683 BAM_DPRINTF(("%s: is HAND module: %s\n", fcn
, arg
));
4686 BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn
, arg
));
4687 bam_error(_("module command on line %d not recognized.\n"),
4694 * A line in menu.lst looks like
4695 * [ ]*<cmd>[ \t=]*<arg>*
4698 line_parser(menu_t
*mp
, char *str
, int *lineNum
, int *entryNum
)
4701 * save state across calls. This is so that
4702 * header gets the right entry# after title has
4705 static line_t
*prev
= NULL
;
4706 static entry_t
*curr_ent
= NULL
;
4707 static int in_liveupgrade
= 0;
4708 static int is_libbe_ent
= 0;
4711 char *cmd
, *sep
, *arg
;
4712 char save
, *cp
, *line
;
4713 menu_flag_t flag
= BAM_INVALID
;
4714 const char *fcn
= "line_parser()";
4722 * First save a copy of the entire line.
4723 * We use this later to set the line field.
4725 line
= s_strdup(str
);
4727 /* Eat up leading whitespace */
4728 while (*str
== ' ' || *str
== '\t')
4731 if (*str
== '#') { /* comment */
4732 cmd
= s_strdup("#");
4734 arg
= s_strdup(str
+ 1);
4736 if (strstr(arg
, BAM_LU_HDR
) != NULL
) {
4738 } else if (strstr(arg
, BAM_LU_FTR
) != NULL
) {
4740 } else if (strstr(arg
, BAM_LIBBE_FTR
) != NULL
) {
4743 } else if (*str
== '\0') { /* blank line */
4744 cmd
= sep
= arg
= NULL
;
4748 * '=' is not a documented separator in grub syntax.
4749 * However various development bits use '=' as a
4750 * separator. In addition, external users also
4751 * use = as a separator. So we will allow that usage.
4754 while (*str
!= ' ' && *str
!= '\t' && *str
!= '=') {
4772 sep
= s_strdup(str
- 1);
4775 while (*str
== ' ' || *str
== '\t')
4780 arg
= s_strdup(str
);
4784 lp
= s_calloc(1, sizeof (line_t
));
4790 lp
->lineNum
= ++(*lineNum
);
4791 if (cmd
&& strcmp(cmd
, menu_cmds
[TITLE_CMD
]) == 0) {
4792 lp
->entryNum
= ++(*entryNum
);
4793 lp
->flags
= BAM_TITLE
;
4794 if (prev
&& prev
->flags
== BAM_COMMENT
&&
4795 prev
->arg
&& strcmp(prev
->arg
, BAM_BOOTADM_HDR
) == 0) {
4796 prev
->entryNum
= lp
->entryNum
;
4797 curr_ent
= boot_entry_new(mp
, prev
, lp
);
4798 curr_ent
->flags
|= BAM_ENTRY_BOOTADM
;
4799 BAM_DPRINTF(("%s: is bootadm(1M) entry: %s\n",
4802 curr_ent
= boot_entry_new(mp
, lp
, lp
);
4803 if (in_liveupgrade
) {
4804 curr_ent
->flags
|= BAM_ENTRY_LU
;
4805 BAM_DPRINTF(("%s: is LU entry: %s\n",
4809 curr_ent
->entryNum
= *entryNum
;
4810 } else if (flag
!= BAM_INVALID
) {
4812 * For header comments, the entry# is "fixed up"
4813 * by the subsequent title
4815 lp
->entryNum
= *entryNum
;
4818 lp
->entryNum
= *entryNum
;
4820 if (*entryNum
== ENTRY_INIT
) {
4821 lp
->flags
= BAM_GLOBAL
;
4823 lp
->flags
= BAM_ENTRY
;
4826 if (strcmp(cmd
, menu_cmds
[ROOT_CMD
]) == 0) {
4827 BAM_DPRINTF(("%s: setting ROOT: %s\n",
4829 curr_ent
->flags
|= BAM_ENTRY_ROOT
;
4830 } else if (strcmp(cmd
, menu_cmds
[FINDROOT_CMD
])
4832 BAM_DPRINTF(("%s: setting "
4833 "FINDROOT: %s\n", fcn
, arg
));
4834 curr_ent
->flags
|= BAM_ENTRY_FINDROOT
;
4835 } else if (strcmp(cmd
,
4836 menu_cmds
[CHAINLOADER_CMD
]) == 0) {
4837 BAM_DPRINTF(("%s: setting "
4838 "CHAINLOADER: %s\n", fcn
, arg
));
4840 BAM_ENTRY_CHAINLOADER
;
4841 } else if (kernel_parser(curr_ent
, cmd
, arg
,
4842 lp
->lineNum
) != BAM_SUCCESS
) {
4843 (void) module_parser(curr_ent
, cmd
,
4850 /* record default, old default, and entry line ranges */
4851 if (lp
->flags
== BAM_GLOBAL
&& lp
->cmd
!= NULL
&&
4852 strcmp(lp
->cmd
, menu_cmds
[DEFAULT_CMD
]) == 0) {
4853 mp
->curdefault
= lp
;
4854 } else if (lp
->flags
== BAM_COMMENT
&&
4855 strncmp(lp
->arg
, BAM_OLDDEF
, strlen(BAM_OLDDEF
)) == 0) {
4856 mp
->olddefault
= lp
;
4857 } else if (lp
->flags
== BAM_COMMENT
&&
4858 strncmp(lp
->arg
, BAM_OLD_RC_DEF
, strlen(BAM_OLD_RC_DEF
)) == 0) {
4859 mp
->old_rc_default
= lp
;
4860 } else if (lp
->flags
== BAM_ENTRY
||
4861 (lp
->flags
== BAM_COMMENT
&&
4862 ((strcmp(lp
->arg
, BAM_BOOTADM_FTR
) == 0) || is_libbe_ent
))) {
4864 curr_ent
->flags
|= BAM_ENTRY_LIBBE
;
4868 boot_entry_addline(curr_ent
, lp
);
4870 append_line(mp
, lp
);
4876 update_numbering(menu_t
*mp
)
4880 int old_default_value
;
4881 line_t
*lp
, *prev
, *default_lp
, *default_entry
;
4884 if (mp
->start
== NULL
) {
4888 lineNum
= LINE_INIT
;
4889 entryNum
= ENTRY_INIT
;
4890 old_default_value
= ENTRY_INIT
;
4891 lp
= default_lp
= default_entry
= NULL
;
4894 for (lp
= mp
->start
; lp
; prev
= lp
, lp
= lp
->next
) {
4895 lp
->lineNum
= ++lineNum
;
4898 * Get the value of the default command
4900 if (lp
->entryNum
== ENTRY_INIT
&& lp
->cmd
!= NULL
&&
4901 strcmp(lp
->cmd
, menu_cmds
[DEFAULT_CMD
]) == 0 &&
4903 old_default_value
= atoi(lp
->arg
);
4908 * If not a booting entry, nothing else to fix for this
4911 if (lp
->entryNum
== ENTRY_INIT
)
4915 * Record the position of the default entry.
4916 * The following works because global
4917 * commands like default and timeout should precede
4918 * actual boot entries, so old_default_value
4919 * is already known (or default cmd is missing).
4921 if (default_entry
== NULL
&&
4922 old_default_value
!= ENTRY_INIT
&&
4923 lp
->entryNum
== old_default_value
) {
4928 * Now fixup the entry number
4930 if (lp
->cmd
!= NULL
&&
4931 strcmp(lp
->cmd
, menu_cmds
[TITLE_CMD
]) == 0) {
4932 lp
->entryNum
= ++entryNum
;
4933 /* fixup the bootadm header */
4934 if (prev
&& prev
->flags
== BAM_COMMENT
&&
4936 strcmp(prev
->arg
, BAM_BOOTADM_HDR
) == 0) {
4937 prev
->entryNum
= lp
->entryNum
;
4940 lp
->entryNum
= entryNum
;
4945 * No default command in menu, simply return
4947 if (default_lp
== NULL
) {
4951 free(default_lp
->arg
);
4952 free(default_lp
->line
);
4954 if (default_entry
== NULL
) {
4955 default_lp
->arg
= s_strdup("0");
4957 (void) snprintf(buf
, sizeof (buf
), "%d",
4958 default_entry
->entryNum
);
4959 default_lp
->arg
= s_strdup(buf
);
4963 * The following is required since only the line field gets
4964 * written back to menu.lst
4966 (void) snprintf(buf
, sizeof (buf
), "%s%s%s",
4967 menu_cmds
[DEFAULT_CMD
], menu_cmds
[SEP_CMD
], default_lp
->arg
);
4968 default_lp
->line
= s_strdup(buf
);
4973 menu_read(char *menu_path
)
4976 char buf
[BAM_MAXLINE
], *cp
;
4978 int line
, entry
, len
, n
;
4980 mp
= s_calloc(1, sizeof (menu_t
));
4982 fp
= fopen(menu_path
, "r");
4983 if (fp
== NULL
) { /* Let the caller handle this error */
4988 /* Note: GRUB boot entry number starts with 0 */
4993 while (s_fgets(cp
, len
, fp
) != NULL
) {
4995 if (cp
[n
- 1] == '\\') {
5001 line_parser(mp
, buf
, &line
, &entry
);
5006 if (fclose(fp
) == EOF
) {
5007 bam_error(_("failed to close file: %s: %s\n"), menu_path
,
5015 selector(menu_t
*mp
, char *opt
, int *entry
, char **title
)
5025 opt_dup
= s_strdup(opt
);
5028 *entry
= ENTRY_INIT
;
5032 eq
= strchr(opt_dup
, '=');
5034 bam_error(_("invalid option: %s\n"), opt
);
5040 if (entry
&& strcmp(opt_dup
, OPT_ENTRY_NUM
) == 0) {
5042 entryNum
= s_strtol(eq
+ 1);
5043 if (entryNum
< 0 || entryNum
> mp
->end
->entryNum
) {
5044 bam_error(_("invalid boot entry number: %s\n"), eq
+ 1);
5049 } else if (title
&& strcmp(opt_dup
, menu_cmds
[TITLE_CMD
]) == 0) {
5050 *title
= opt
+ (eq
- opt_dup
) + 1;
5052 bam_error(_("invalid option: %s\n"), opt
);
5058 return (BAM_SUCCESS
);
5062 * If invoked with no titles/entries (opt == NULL)
5063 * only title lines in file are printed.
5065 * If invoked with a title or entry #, all
5066 * lines in *every* matching entry are listed
5069 list_entry(menu_t
*mp
, char *menu_path
, char *opt
)
5072 int entry
= ENTRY_INIT
;
5079 /* opt is optional */
5080 BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path
,
5081 opt
? opt
: "<NULL>"));
5083 if (mp
->start
== NULL
) {
5084 bam_error(_("menu file not found: %s\n"), menu_path
);
5089 if (selector(mp
, opt
, &entry
, &title
) != BAM_SUCCESS
) {
5092 assert((entry
!= ENTRY_INIT
) ^ (title
!= NULL
));
5094 (void) read_globals(mp
, menu_path
, menu_cmds
[DEFAULT_CMD
], 0);
5095 (void) read_globals(mp
, menu_path
, menu_cmds
[TIMEOUT_CMD
], 0);
5099 for (lp
= mp
->start
; lp
; lp
= lp
->next
) {
5100 if (lp
->flags
== BAM_COMMENT
|| lp
->flags
== BAM_EMPTY
)
5102 if (opt
== NULL
&& lp
->flags
== BAM_TITLE
) {
5103 bam_print(_("%d %s\n"), lp
->entryNum
,
5108 if (entry
!= ENTRY_INIT
&& lp
->entryNum
== entry
) {
5109 bam_print(_("%s\n"), lp
->line
);
5115 * We set the entry value here so that all lines
5116 * in entry get printed. If we subsequently match
5117 * title in other entries, all lines in those
5118 * entries get printed as well.
5120 if (title
&& lp
->flags
== BAM_TITLE
&& lp
->arg
&&
5121 strncmp(title
, lp
->arg
, strlen(title
)) == 0) {
5122 bam_print(_("%s\n"), lp
->line
);
5123 entry
= lp
->entryNum
;
5130 bam_error(_("no matching entry found\n"));
5134 return (BAM_SUCCESS
);
5138 add_boot_entry(menu_t
*mp
,
5148 char linebuf
[BAM_MAXLINE
];
5151 const char *fcn
= "add_boot_entry()";
5155 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot
= NULL
);
5156 if (findroot
== NULL
) {
5157 bam_error(_("can't find argument for findroot command\n"));
5161 if (title
== NULL
) {
5162 title
= "Solaris"; /* default to Solaris */
5164 if (kernel
== NULL
) {
5165 bam_error(_("missing suboption: %s\n"), menu_cmds
[KERNEL_CMD
]);
5168 if (module
== NULL
) {
5169 if (bam_direct
!= BAM_DIRECT_DBOOT
) {
5170 bam_error(_("missing suboption: %s\n"),
5171 menu_cmds
[MODULE_CMD
]);
5175 /* Figure the commands out from the kernel line */
5176 if (strstr(kernel
, "$ISADIR") != NULL
) {
5177 module
= DIRECT_BOOT_ARCHIVE
;
5178 } else if (strstr(kernel
, "amd64") != NULL
) {
5179 module
= DIRECT_BOOT_ARCHIVE_64
;
5181 module
= DIRECT_BOOT_ARCHIVE_32
;
5185 k_cmd
= KERNEL_DOLLAR_CMD
;
5186 m_cmd
= MODULE_DOLLAR_CMD
;
5189 lineNum
= mp
->end
->lineNum
;
5190 entryNum
= mp
->end
->entryNum
;
5192 lineNum
= LINE_INIT
;
5193 entryNum
= ENTRY_INIT
;
5197 * No separator for comment (HDR/FTR) commands
5198 * The syntax for comments is #<comment>
5200 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s",
5201 menu_cmds
[COMMENT_CMD
], BAM_BOOTADM_HDR
);
5202 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5204 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5205 menu_cmds
[TITLE_CMD
], menu_cmds
[SEP_CMD
], title
);
5206 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5208 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5209 menu_cmds
[FINDROOT_CMD
], menu_cmds
[SEP_CMD
], findroot
);
5210 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5211 BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5212 fcn
, lineNum
, entryNum
));
5214 if (bootfs
!= NULL
) {
5215 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5216 menu_cmds
[BOOTFS_CMD
], menu_cmds
[SEP_CMD
], bootfs
);
5217 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5220 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5221 menu_cmds
[k_cmd
], menu_cmds
[SEP_CMD
], kernel
);
5222 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5224 if (mod_kernel
!= NULL
) {
5225 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5226 menu_cmds
[m_cmd
], menu_cmds
[SEP_CMD
], mod_kernel
);
5227 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5230 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5231 menu_cmds
[m_cmd
], menu_cmds
[SEP_CMD
], module
);
5232 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5234 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s",
5235 menu_cmds
[COMMENT_CMD
], BAM_BOOTADM_FTR
);
5236 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5242 delete_boot_entry(menu_t
*mp
, int entryNum
, int quiet
)
5249 const char *fcn
= "delete_boot_entry()";
5251 assert(entryNum
!= ENTRY_INIT
);
5260 * Check entry number and make sure it's a modifiable entry.
5263 * + We can modify a bootadm-created entry
5264 * + We can modify a libbe-created entry
5266 if ((lp
->flags
!= BAM_COMMENT
&&
5267 (((ent
->flags
& BAM_ENTRY_LIBBE
) == 0) &&
5268 strcmp(lp
->arg
, BAM_BOOTADM_HDR
) != 0)) ||
5269 (entryNum
!= ALL_ENTRIES
&& lp
->entryNum
!= entryNum
)) {
5274 /* free the entry content */
5277 lp
= lp
->next
; /* prev stays the same */
5278 BAM_DPRINTF(("%s: freeing line: %d\n",
5279 fcn
, freed
->lineNum
));
5280 unlink_line(mp
, freed
);
5282 } while (freed
!= ent
->end
);
5284 /* free the entry_t structure */
5285 assert(tmp
== NULL
);
5289 tmp
->prev
->next
= ent
;
5293 ent
->prev
= tmp
->prev
;
5294 BAM_DPRINTF(("%s: freeing entry: %d\n", fcn
, tmp
->entryNum
));
5300 assert(tmp
== NULL
);
5302 if (!deleted
&& entryNum
!= ALL_ENTRIES
) {
5303 if (quiet
== DBE_PRINTERR
)
5304 bam_error(_("no matching bootadm entry found\n"));
5309 * Now that we have deleted an entry, update
5310 * the entry numbering and the default cmd.
5312 update_numbering(mp
);
5314 return (BAM_SUCCESS
);
5318 delete_all_entries(menu_t
*mp
, char *dummy
, char *opt
)
5321 assert(dummy
== NULL
);
5322 assert(opt
== NULL
);
5324 BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5326 if (mp
->start
== NULL
) {
5327 bam_print(_("the GRUB menu is empty\n"));
5328 return (BAM_SUCCESS
);
5331 if (delete_boot_entry(mp
, ALL_ENTRIES
, DBE_PRINTERR
) != BAM_SUCCESS
) {
5339 create_diskmap(char *osroot
)
5342 char cmd
[PATH_MAX
+ 16];
5343 char path
[PATH_MAX
];
5344 const char *fcn
= "create_diskmap()";
5346 /* make sure we have a map file */
5347 fp
= fopen(GRUBDISK_MAP
, "r");
5351 ret
= snprintf(path
, sizeof (path
), "%s/%s", osroot
,
5353 if (ret
>= sizeof (path
)) {
5354 bam_error(_("unable to create path on mountpoint %s, "
5355 "path too long\n"), osroot
);
5358 if (is_safe_exec(path
) == BAM_ERROR
)
5361 (void) snprintf(cmd
, sizeof (cmd
),
5362 "%s/%s > /dev/null", osroot
, CREATE_DISKMAP
);
5363 if (exec_cmd(cmd
, NULL
) != 0)
5365 fp
= fopen(GRUBDISK_MAP
, "r");
5366 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp
= NULL
);
5368 BAM_DPRINTF(("%s: created diskmap file: %s\n",
5369 fcn
, GRUBDISK_MAP
));
5371 BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5372 fcn
, GRUBDISK_MAP
));
5378 #define SECTOR_SIZE 512
5381 get_partition(char *device
)
5383 int i
, fd
, is_pcfs
, partno
= PARTNO_NOTFOUND
;
5384 struct mboot
*mboot
;
5385 char boot_sect
[SECTOR_SIZE
];
5386 char *wholedisk
, *slice
;
5389 uint32_t secnum
, numsec
;
5390 int rval
, pno
, ext_partno
= PARTNO_NOTFOUND
;
5393 /* form whole disk (p0) */
5394 slice
= device
+ strlen(device
) - 2;
5395 is_pcfs
= (*slice
!= 's');
5398 wholedisk
= s_calloc(1, strlen(device
) + 3);
5399 (void) snprintf(wholedisk
, strlen(device
) + 3, "%sp0", device
);
5403 /* read boot sector */
5404 fd
= open(wholedisk
, O_RDONLY
);
5405 if (fd
== -1 || read(fd
, boot_sect
, SECTOR_SIZE
) != SECTOR_SIZE
) {
5411 /* Read/Initialize extended partition information */
5412 if ((rval
= libfdisk_init(&epp
, wholedisk
, NULL
, FDISK_READ_DISK
))
5416 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5417 * be considered as soft errors and hence
5420 case FDISK_EBADLOGDRIVE
:
5422 case FDISK_ENOLOGDRIVE
:
5424 case FDISK_EBADMAGIC
:
5428 libfdisk_fini(&epp
);
5435 /* parse fdisk table */
5436 mboot
= (struct mboot
*)((void *)boot_sect
);
5437 for (i
= 0; i
< FD_NUMPART
; i
++) {
5438 struct ipart
*part
=
5439 (struct ipart
*)(uintptr_t)mboot
->parts
+ i
;
5440 if (is_pcfs
) { /* looking for solaris boot part */
5441 if (part
->systid
== 0xbe) {
5445 } else { /* look for solaris partition, old and new */
5446 if (part
->systid
== EFI_PMBR
) {
5447 partno
= PARTNO_EFI
;
5452 if ((part
->systid
== SUNIXOS
&&
5453 (fdisk_is_linux_swap(epp
, part
->relsect
,
5454 NULL
) != 0)) || part
->systid
== SUNIXOS2
) {
5456 if (part
->systid
== SUNIXOS
||
5457 part
->systid
== SUNIXOS2
) {
5464 if (fdisk_is_dos_extended(part
->systid
))
5470 /* If no primary solaris partition, check extended partition */
5471 if ((partno
== PARTNO_NOTFOUND
) && (ext_partno
!= PARTNO_NOTFOUND
)) {
5472 rval
= fdisk_get_solaris_part(epp
, &pno
, &secnum
, &numsec
);
5473 if (rval
== FDISK_SUCCESS
) {
5477 libfdisk_fini(&epp
);
5483 get_grubroot(char *osroot
, char *osdev
, char *menu_root
)
5485 char *grubroot
; /* (hd#,#,#) */
5487 char *grubhd
= NULL
;
5491 char *ctdname
= strstr(osdev
, "dsk/");
5492 char linebuf
[PATH_MAX
];
5495 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname
= NULL
);
5496 if (ctdname
== NULL
) {
5497 bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev
);
5501 if (menu_root
&& !menu_on_bootdisk(osroot
, menu_root
)) {
5502 /* menu bears no resemblance to our reality */
5503 bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5504 "bootdisk: %s\n"), osdev
);
5508 ctdname
+= strlen("dsk/");
5509 slice
= strrchr(ctdname
, 's');
5513 fp
= create_diskmap(osroot
);
5515 bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5521 while (s_fgets(linebuf
, sizeof (linebuf
), fp
) != NULL
) {
5522 grubhd
= strtok(linebuf
, " \t\n");
5524 devname
= strtok(NULL
, " \t\n");
5527 if (devname
&& strcmp(devname
, ctdname
) == 0) {
5539 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found
= 0);
5541 bam_error(_("not using biosdev command for disk: %s.\n"),
5546 fdiskpart
= get_partition(osdev
);
5547 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart
= PARTNO_NOTFOUND
);
5548 if (fdiskpart
== PARTNO_NOTFOUND
) {
5549 bam_error(_("failed to determine fdisk partition: %s\n"),
5554 grubroot
= s_calloc(1, 10);
5555 if (fdiskpart
== PARTNO_EFI
) {
5556 fdiskpart
= atoi(&slice
[1]);
5561 (void) snprintf(grubroot
, 10, "(hd%s,%d,%c)",
5562 grubhd
, fdiskpart
, slice
[1] + 'a' - '0');
5564 (void) snprintf(grubroot
, 10, "(hd%s,%d)",
5568 assert(strncmp(grubroot
, "(hd", strlen("(hd")) == 0);
5573 find_primary_common(char *mntpt
, char *fstype
)
5575 char signdir
[PATH_MAX
];
5576 char tmpsign
[MAXNAMELEN
+ 1];
5581 struct dirent
*entp
;
5583 const char *fcn
= "find_primary_common()";
5585 (void) snprintf(signdir
, sizeof (signdir
), "%s/%s",
5586 mntpt
, GRUBSIGN_DIR
);
5588 if (stat(signdir
, &sb
) == -1) {
5589 BAM_DPRINTF(("%s: no sign dir: %s\n", fcn
, signdir
));
5593 dirp
= opendir(signdir
);
5594 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp
= NULL
);
5596 bam_error(_("opendir of %s failed: %s\n"), signdir
,
5601 ufs
= zfs
= lu
= NULL
;
5603 while ((entp
= readdir(dirp
)) != NULL
) {
5604 if (strcmp(entp
->d_name
, ".") == 0 ||
5605 strcmp(entp
->d_name
, "..") == 0)
5608 (void) snprintf(tmpsign
, sizeof (tmpsign
), "%s", entp
->d_name
);
5611 strncmp(tmpsign
, GRUBSIGN_LU_PREFIX
,
5612 strlen(GRUBSIGN_LU_PREFIX
)) == 0) {
5613 lu
= s_strdup(tmpsign
);
5617 strncmp(tmpsign
, GRUBSIGN_UFS_PREFIX
,
5618 strlen(GRUBSIGN_UFS_PREFIX
)) == 0) {
5619 ufs
= s_strdup(tmpsign
);
5623 strncmp(tmpsign
, GRUBSIGN_ZFS_PREFIX
,
5624 strlen(GRUBSIGN_ZFS_PREFIX
)) == 0) {
5625 zfs
= s_strdup(tmpsign
);
5629 BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn
,
5635 (void) closedir(dirp
);
5639 if (strcmp(fstype
, "ufs") == 0 && zfs
) {
5640 bam_error(_("found mismatched boot signature %s for "
5641 "filesystem type: %s.\n"), zfs
, "ufs");
5644 } else if (strcmp(fstype
, "zfs") == 0 && ufs
) {
5645 bam_error(_("found mismatched boot signature %s for "
5646 "filesystem type: %s.\n"), ufs
, "zfs");
5651 assert(dirp
== NULL
);
5653 /* For now, we let Live Upgrade take care of its signature itself */
5655 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn
, lu
));
5660 return (zfs
? zfs
: ufs
);
5664 find_backup_common(char *mntpt
, char *fstype
)
5667 char tmpsign
[MAXNAMELEN
+ 1];
5668 char backup
[PATH_MAX
];
5673 const char *fcn
= "find_backup_common()";
5676 * We didn't find it in the primary directory.
5677 * Look at the backup
5679 (void) snprintf(backup
, sizeof (backup
), "%s%s",
5680 mntpt
, GRUBSIGN_BACKUP
);
5682 bfp
= fopen(backup
, "r");
5686 bam_error(_("failed to open file: %s: %s\n"),
5687 backup
, strerror(error
));
5689 BAM_DPRINTF(("%s: failed to open %s: %s\n",
5690 fcn
, backup
, strerror(error
)));
5694 ufs
= zfs
= lu
= NULL
;
5696 while (s_fgets(tmpsign
, sizeof (tmpsign
), bfp
) != NULL
) {
5699 strncmp(tmpsign
, GRUBSIGN_LU_PREFIX
,
5700 strlen(GRUBSIGN_LU_PREFIX
)) == 0) {
5701 lu
= s_strdup(tmpsign
);
5705 strncmp(tmpsign
, GRUBSIGN_UFS_PREFIX
,
5706 strlen(GRUBSIGN_UFS_PREFIX
)) == 0) {
5707 ufs
= s_strdup(tmpsign
);
5711 strncmp(tmpsign
, GRUBSIGN_ZFS_PREFIX
,
5712 strlen(GRUBSIGN_ZFS_PREFIX
)) == 0) {
5713 zfs
= s_strdup(tmpsign
);
5717 BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn
,
5727 if (strcmp(fstype
, "ufs") == 0 && zfs
) {
5728 bam_error(_("found mismatched boot signature %s for "
5729 "filesystem type: %s.\n"), zfs
, "ufs");
5732 } else if (strcmp(fstype
, "zfs") == 0 && ufs
) {
5733 bam_error(_("found mismatched boot signature %s for "
5734 "filesystem type: %s.\n"), ufs
, "zfs");
5739 assert(bfp
== NULL
);
5741 /* For now, we let Live Upgrade take care of its signature itself */
5743 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn
, lu
));
5748 return (zfs
? zfs
: ufs
);
5752 find_ufs_existing(char *osroot
)
5755 const char *fcn
= "find_ufs_existing()";
5757 sign
= find_primary_common(osroot
, "ufs");
5759 sign
= find_backup_common(osroot
, "ufs");
5760 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn
,
5761 sign
? sign
: "NULL"));
5763 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn
, sign
));
5770 get_mountpoint(char *special
, char *fstype
)
5773 struct mnttab mp
= {0};
5774 struct mnttab mpref
= {0};
5777 const char *fcn
= "get_mountpoint()";
5779 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, special
, fstype
));
5781 mntfp
= fopen(MNTTAB
, "r");
5783 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp
= NULL
);
5784 if (mntfp
== NULL
) {
5785 bam_error(_("failed to open file: %s: %s\n"),
5786 MNTTAB
, strerror(error
));
5790 mpref
.mnt_special
= special
;
5791 mpref
.mnt_fstype
= fstype
;
5793 ret
= getmntany(mntfp
, &mp
, &mpref
);
5794 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret
= 1);
5796 (void) fclose(mntfp
);
5797 BAM_DPRINTF(("%s: no mount-point for special=%s and "
5798 "fstype=%s\n", fcn
, special
, fstype
));
5801 (void) fclose(mntfp
);
5803 assert(mp
.mnt_mountp
);
5805 BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5806 fcn
, special
, mp
.mnt_mountp
));
5808 return (s_strdup(mp
.mnt_mountp
));
5812 * Mounts a "legacy" top dataset (if needed)
5813 * Returns: The mountpoint of the legacy top dataset or NULL on error
5814 * mnted returns one of the above values defined for zfs_mnted_t
5817 mount_legacy_dataset(char *pool
, zfs_mnted_t
*mnted
)
5820 char tmpmnt
[PATH_MAX
];
5821 filelist_t flist
= {0};
5825 const char *fcn
= "mount_legacy_dataset()";
5827 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, pool
));
5829 *mnted
= ZFS_MNT_ERROR
;
5831 (void) snprintf(cmd
, sizeof (cmd
),
5832 "/sbin/zfs get -Ho value mounted %s",
5835 ret
= exec_cmd(cmd
, &flist
);
5836 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret
= 1);
5838 bam_error(_("failed to determine mount status of ZFS "
5839 "pool %s\n"), pool
);
5843 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist
.head
= NULL
);
5844 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
5845 bam_error(_("ZFS pool %s has bad mount status\n"), pool
);
5846 filelist_free(&flist
);
5850 is_mounted
= strtok(flist
.head
->line
, " \t\n");
5851 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted
= "yes");
5852 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted
= "no");
5853 if (strcmp(is_mounted
, "no") != 0) {
5854 filelist_free(&flist
);
5855 *mnted
= LEGACY_ALREADY
;
5856 /* get_mountpoint returns a strdup'ed string */
5857 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
5859 return (get_mountpoint(pool
, "zfs"));
5862 filelist_free(&flist
);
5865 * legacy top dataset is not mounted. Mount it now
5866 * First create a mountpoint.
5868 (void) snprintf(tmpmnt
, sizeof (tmpmnt
), "%s.%d",
5869 ZFS_LEGACY_MNTPT
, getpid());
5871 ret
= stat(tmpmnt
, &sb
);
5873 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
5874 fcn
, pool
, tmpmnt
));
5875 ret
= mkdirp(tmpmnt
, DIR_PERMS
);
5876 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret
= -1);
5878 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt
,
5883 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
5884 "present\n", fcn
, pool
, tmpmnt
));
5887 (void) snprintf(cmd
, sizeof (cmd
),
5888 "/sbin/mount -F zfs %s %s",
5891 ret
= exec_cmd(cmd
, NULL
);
5892 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret
= 1);
5894 bam_error(_("mount of ZFS pool %s failed\n"), pool
);
5895 (void) rmdir(tmpmnt
);
5899 *mnted
= LEGACY_MOUNTED
;
5900 BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
5901 fcn
, pool
, tmpmnt
));
5902 return (s_strdup(tmpmnt
));
5906 * Mounts the top dataset (if needed)
5907 * Returns: The mountpoint of the top dataset or NULL on error
5908 * mnted returns one of the above values defined for zfs_mnted_t
5911 mount_top_dataset(char *pool
, zfs_mnted_t
*mnted
)
5914 filelist_t flist
= {0};
5919 const char *fcn
= "mount_top_dataset()";
5921 *mnted
= ZFS_MNT_ERROR
;
5923 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, pool
));
5926 * First check if the top dataset is a "legacy" dataset
5928 (void) snprintf(cmd
, sizeof (cmd
),
5929 "/sbin/zfs get -Ho value mountpoint %s",
5931 ret
= exec_cmd(cmd
, &flist
);
5932 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret
= 1);
5934 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
5939 if (flist
.head
&& (flist
.head
== flist
.tail
)) {
5940 char *legacy
= strtok(flist
.head
->line
, " \t\n");
5941 if (legacy
&& strcmp(legacy
, "legacy") == 0) {
5942 filelist_free(&flist
);
5943 BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn
, pool
));
5944 return (mount_legacy_dataset(pool
, mnted
));
5948 filelist_free(&flist
);
5950 BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn
, pool
));
5952 (void) snprintf(cmd
, sizeof (cmd
),
5953 "/sbin/zfs get -Ho value mounted %s",
5956 ret
= exec_cmd(cmd
, &flist
);
5957 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret
= 1);
5959 bam_error(_("failed to determine mount status of ZFS "
5960 "pool %s\n"), pool
);
5964 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist
.head
= NULL
);
5965 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
5966 bam_error(_("ZFS pool %s has bad mount status\n"), pool
);
5967 filelist_free(&flist
);
5971 is_mounted
= strtok(flist
.head
->line
, " \t\n");
5972 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted
= "yes");
5973 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted
= "no");
5974 if (strcmp(is_mounted
, "no") != 0) {
5975 filelist_free(&flist
);
5976 *mnted
= ZFS_ALREADY
;
5977 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
5982 filelist_free(&flist
);
5983 BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
5986 /* top dataset is not mounted. Mount it now */
5987 (void) snprintf(cmd
, sizeof (cmd
),
5988 "/sbin/zfs mount %s", pool
);
5989 ret
= exec_cmd(cmd
, NULL
);
5990 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret
= 1);
5992 bam_error(_("mount of ZFS pool %s failed\n"), pool
);
5995 *mnted
= ZFS_MOUNTED
;
5996 BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn
, pool
));
6000 * Now get the mountpoint
6002 (void) snprintf(cmd
, sizeof (cmd
),
6003 "/sbin/zfs get -Ho value mountpoint %s",
6006 ret
= exec_cmd(cmd
, &flist
);
6007 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret
= 1);
6009 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6014 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist
.head
= NULL
);
6015 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6016 bam_error(_("ZFS pool %s has no mount-point\n"), pool
);
6020 mntpt
= strtok(flist
.head
->line
, " \t\n");
6021 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt
= "foo");
6022 if (*mntpt
!= '/') {
6023 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6027 zmntpt
= s_strdup(mntpt
);
6029 filelist_free(&flist
);
6031 BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6032 fcn
, pool
, zmntpt
));
6037 filelist_free(&flist
);
6038 (void) umount_top_dataset(pool
, *mnted
, NULL
);
6039 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
6044 umount_top_dataset(char *pool
, zfs_mnted_t mnted
, char *mntpt
)
6048 const char *fcn
= "umount_top_dataset()";
6050 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted
= ZFS_MNT_ERROR
);
6052 case LEGACY_ALREADY
:
6055 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6056 "to umount\n", fcn
, pool
, mntpt
? mntpt
: "NULL"));
6058 return (BAM_SUCCESS
);
6059 case LEGACY_MOUNTED
:
6060 (void) snprintf(cmd
, sizeof (cmd
),
6061 "/sbin/umount %s", pool
);
6062 ret
= exec_cmd(cmd
, NULL
);
6063 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret
= 1);
6065 bam_error(_("umount of %s failed\n"), pool
);
6070 (void) rmdir(mntpt
);
6072 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6073 "successfully unmounted\n", fcn
, pool
));
6074 return (BAM_SUCCESS
);
6077 (void) snprintf(cmd
, sizeof (cmd
),
6078 "/sbin/zfs unmount %s", pool
);
6079 ret
= exec_cmd(cmd
, NULL
);
6080 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret
= 1);
6082 bam_error(_("umount of %s failed\n"), pool
);
6085 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6086 "successfully unmounted\n", fcn
, pool
));
6087 return (BAM_SUCCESS
);
6089 bam_error(_("Internal error: bad saved mount state for "
6090 "pool %s\n"), pool
);
6097 * For ZFS, osdev can be one of two forms
6098 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6099 * It can be a /dev/[r]dsk special file. We handle both instances
6102 get_pool(char *osdev
)
6106 filelist_t flist
= {0};
6111 const char *fcn
= "get_pool()";
6113 INJECT_ERROR1("GET_POOL_OSDEV", osdev
= NULL
);
6114 if (osdev
== NULL
) {
6115 bam_error(_("NULL device: cannot determine pool name\n"));
6119 BAM_DPRINTF(("%s: osdev arg = %s\n", fcn
, osdev
));
6121 if (osdev
[0] != '/') {
6122 (void) strlcpy(buf
, osdev
, sizeof (buf
));
6123 slash
= strchr(buf
, '/');
6126 pool
= s_strdup(buf
);
6127 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn
, pool
));
6129 } else if (strncmp(osdev
, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6130 strncmp(osdev
, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6131 bam_error(_("invalid device %s: cannot determine pool name\n"),
6137 * Call the zfs fstyp directly since this is a zpool. This avoids
6138 * potential pcfs conflicts if the first block wasn't cleared.
6140 (void) snprintf(cmd
, sizeof (cmd
),
6141 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6144 ret
= exec_cmd(cmd
, &flist
);
6145 INJECT_ERROR1("GET_POOL_FSTYP", ret
= 1);
6147 bam_error(_("fstyp -a on device %s failed\n"), osdev
);
6151 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist
.head
= NULL
);
6152 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6153 bam_error(_("NULL fstyp -a output for device %s\n"), osdev
);
6154 filelist_free(&flist
);
6158 (void) strtok(flist
.head
->line
, "'");
6159 cp
= strtok(NULL
, "'");
6160 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp
= NULL
);
6162 bam_error(_("bad fstyp -a output for device %s\n"), osdev
);
6163 filelist_free(&flist
);
6167 pool
= s_strdup(cp
);
6169 filelist_free(&flist
);
6171 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn
, pool
));
6177 find_zfs_existing(char *osdev
)
6183 const char *fcn
= "find_zfs_existing()";
6185 pool
= get_pool(osdev
);
6186 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool
= NULL
);
6188 bam_error(_("failed to get pool for device: %s\n"), osdev
);
6192 mntpt
= mount_top_dataset(pool
, &mnted
);
6193 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt
= NULL
);
6194 if (mntpt
== NULL
) {
6195 bam_error(_("failed to mount top dataset for pool: %s\n"),
6201 sign
= find_primary_common(mntpt
, "zfs");
6203 sign
= find_backup_common(mntpt
, "zfs");
6204 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn
,
6205 sign
? sign
: "NULL"));
6207 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn
, sign
));
6210 (void) umount_top_dataset(pool
, mnted
, mntpt
);
6218 find_existing_sign(char *osroot
, char *osdev
, char *fstype
)
6220 const char *fcn
= "find_existing_sign()";
6222 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype
= "foofs");
6223 if (strcmp(fstype
, "ufs") == 0) {
6224 BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn
));
6225 return (find_ufs_existing(osroot
));
6226 } else if (strcmp(fstype
, "zfs") == 0) {
6227 BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn
));
6228 return (find_zfs_existing(osdev
));
6230 bam_error(_("boot signature not supported for fstype: %s\n"),
6236 #define MH_HASH_SZ 16
6244 typedef struct mcache
{
6248 struct mcache
*mc_next
;
6251 typedef struct mhash
{
6252 mcache_t
*mh_hash
[MH_HASH_SZ
];
6256 mhash_fcn(char *key
)
6261 for (i
= 0; key
[i
] != '\0'; i
++) {
6262 sum
+= (uchar_t
)key
[i
];
6267 assert(sum
< MH_HASH_SZ
);
6276 struct extmnttab mnt
;
6283 const char *fcn
= "cache_mnttab()";
6285 mfp
= fopen(MNTTAB
, "r");
6287 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp
= NULL
);
6289 bam_error(_("failed to open file: %s: %s\n"), MNTTAB
,
6294 mhp
= s_calloc(1, sizeof (mhash_t
));
6298 while (getextmntent(mfp
, &mnt
, sizeof (mnt
)) == 0) {
6299 /* only cache ufs */
6300 if (strcmp(mnt
.mnt_fstype
, "ufs") != 0)
6303 /* basename() modifies its arg, so dup it */
6304 special_dup
= s_strdup(mnt
.mnt_special
);
6305 ctds
= basename(special_dup
);
6307 mcp
= s_calloc(1, sizeof (mcache_t
));
6308 mcp
->mc_special
= s_strdup(ctds
);
6309 mcp
->mc_mntpt
= s_strdup(mnt
.mnt_mountp
);
6310 mcp
->mc_fstype
= s_strdup(mnt
.mnt_fstype
);
6311 BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6312 "fstype=%s\n", fcn
, ctds
, mnt
.mnt_mountp
, mnt
.mnt_fstype
));
6313 idx
= mhash_fcn(ctds
);
6314 mcp
->mc_next
= mhp
->mh_hash
[idx
];
6315 mhp
->mh_hash
[idx
] = mcp
;
6325 free_mnttab(mhash_t
*mhp
)
6330 for (i
= 0; i
< MH_HASH_SZ
; i
++) {
6331 while ((mcp
= mhp
->mh_hash
[i
]) != NULL
) {
6332 mhp
->mh_hash
[i
] = mcp
->mc_next
;
6333 free(mcp
->mc_special
);
6334 free(mcp
->mc_mntpt
);
6335 free(mcp
->mc_fstype
);
6340 for (i
= 0; i
< MH_HASH_SZ
; i
++) {
6341 assert(mhp
->mh_hash
[i
] == NULL
);
6347 search_hash(mhash_t
*mhp
, char *special
, char **mntpt
)
6351 const char *fcn
= "search_hash()";
6357 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special
= "/foo");
6358 if (strchr(special
, '/')) {
6359 bam_error(_("invalid key for mnttab hash: %s\n"), special
);
6363 idx
= mhash_fcn(special
);
6365 for (mcp
= mhp
->mh_hash
[idx
]; mcp
; mcp
= mcp
->mc_next
) {
6366 if (strcmp(mcp
->mc_special
, special
) == 0)
6371 BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn
, special
));
6372 return (MH_NOMATCH
);
6375 assert(strcmp(mcp
->mc_fstype
, "ufs") == 0);
6376 *mntpt
= mcp
->mc_mntpt
;
6377 BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn
, special
));
6382 check_add_ufs_sign_to_list(FILE *tfp
, char *mntpt
)
6386 char signbuf
[MAXNAMELEN
];
6389 const char *fcn
= "check_add_ufs_sign_to_list()";
6391 /* safe to specify NULL as "osdev" arg for UFS */
6392 sign
= find_existing_sign(mntpt
, NULL
, "ufs");
6394 /* No existing signature, nothing to add to list */
6395 BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6400 (void) snprintf(signbuf
, sizeof (signbuf
), "%s\n", sign
);
6403 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline
= "pool_rpool10\n");
6404 if (strncmp(signline
, GRUBSIGN_UFS_PREFIX
,
6405 strlen(GRUBSIGN_UFS_PREFIX
))) {
6406 bam_error(_("invalid UFS boot signature %s\n"), sign
);
6408 /* ignore invalid signatures */
6412 len
= fputs(signline
, tfp
);
6414 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len
= 0);
6415 if (len
!= strlen(signline
)) {
6416 bam_error(_("failed to write signature %s to signature "
6417 "list: %s\n"), sign
, strerror(error
));
6424 BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6430 * slice is a basename not a full pathname
6433 process_slice_common(char *slice
, FILE *tfp
, mhash_t
*mhp
, char *tmpmnt
)
6437 char path
[PATH_MAX
];
6440 filelist_t flist
= {0};
6442 char blkslice
[PATH_MAX
];
6443 const char *fcn
= "process_slice_common()";
6446 ret
= search_hash(mhp
, slice
, &mntpt
);
6449 if (check_add_ufs_sign_to_list(tfp
, mntpt
) == -1)
6460 (void) snprintf(path
, sizeof (path
), "/dev/rdsk/%s", slice
);
6461 if (stat(path
, &sbuf
) == -1) {
6462 BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn
, path
));
6466 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6467 (void) snprintf(cmd
, sizeof (cmd
),
6468 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6471 if (exec_cmd(cmd
, &flist
) != 0) {
6473 bam_print(_("fstyp failed for slice: %s\n"), slice
);
6477 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6479 bam_print(_("bad output from fstyp for slice: %s\n"),
6481 filelist_free(&flist
);
6485 fstype
= strtok(flist
.head
->line
, " \t\n");
6486 if (fstype
== NULL
|| strcmp(fstype
, "ufs") != 0) {
6488 bam_print(_("%s is not a ufs slice: %s\n"),
6490 filelist_free(&flist
);
6494 filelist_free(&flist
);
6497 * Since we are mounting the filesystem read-only, the
6498 * the last mount field of the superblock is unchanged
6499 * and does not need to be fixed up post-mount;
6502 (void) snprintf(blkslice
, sizeof (blkslice
), "/dev/dsk/%s",
6505 (void) snprintf(cmd
, sizeof (cmd
),
6506 "/usr/sbin/mount -F ufs -o ro %s %s "
6507 "> /dev/null 2>&1", blkslice
, tmpmnt
);
6509 if (exec_cmd(cmd
, NULL
) != 0) {
6511 bam_print(_("mount of %s (fstype %s) failed\n"),
6516 ret
= check_add_ufs_sign_to_list(tfp
, tmpmnt
);
6518 (void) snprintf(cmd
, sizeof (cmd
),
6519 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6522 if (exec_cmd(cmd
, NULL
) != 0) {
6523 bam_print(_("umount of %s failed\n"), slice
);
6531 process_vtoc_slices(
6539 char slice
[PATH_MAX
];
6542 const char *fcn
= "process_vtoc_slices()";
6546 assert(s0
[len
- 2] == 's' && s0
[len
- 1] == '0');
6550 (void) strlcpy(slice
, s0
, sizeof (slice
));
6554 cp
= slice
+ len
- 1;
6556 for (idx
= 0; idx
< vtoc
->v_nparts
; idx
++) {
6558 (void) snprintf(cp
, sizeof (slice
) - (len
- 1), "%u", idx
);
6560 if (vtoc
->v_part
[idx
].p_size
== 0) {
6561 BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6566 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6567 switch (vtoc
->v_part
[idx
].p_tag
) {
6574 BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6575 "skipping: %s\n", fcn
, slice
));
6578 BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6583 /* skip unmountable and readonly slices */
6584 switch (vtoc
->v_part
[idx
].p_flag
) {
6587 BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6591 BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6596 if (process_slice_common(slice
, tfp
, mhp
, tmpmnt
) == -1) {
6613 char slice
[PATH_MAX
];
6616 const char *fcn
= "process_efi_slices()";
6620 assert(s0
[len
- 2] == 's' && s0
[len
- 1] == '0');
6624 (void) strlcpy(slice
, s0
, sizeof (slice
));
6628 cp
= slice
+ len
- 1;
6630 for (idx
= 0; idx
< efi
->efi_nparts
; idx
++) {
6632 (void) snprintf(cp
, sizeof (slice
) - (len
- 1), "%u", idx
);
6634 if (efi
->efi_parts
[idx
].p_size
== 0) {
6635 BAM_DPRINTF(("%s: EFI: skipping 0-length slice: %s\n",
6640 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6641 switch (efi
->efi_parts
[idx
].p_tag
) {
6648 BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6652 BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6657 /* skip unmountable and readonly slices */
6658 switch (efi
->efi_parts
[idx
].p_flag
) {
6661 BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6665 BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6670 if (process_slice_common(slice
, tfp
, mhp
, tmpmnt
) == -1) {
6679 * s0 is a basename not a full path
6682 process_slice0(char *s0
, FILE *tfp
, mhash_t
*mhp
, char *tmpmnt
)
6686 char s0path
[PATH_MAX
];
6693 const char *fcn
= "process_slice0()";
6695 (void) snprintf(s0path
, sizeof (s0path
), "/dev/rdsk/%s", s0
);
6697 if (stat(s0path
, &sbuf
) == -1) {
6698 BAM_DPRINTF(("%s: slice 0 does not exist: %s\n", fcn
, s0path
));
6702 fd
= open(s0path
, O_NONBLOCK
|O_RDONLY
);
6704 bam_error(_("failed to open file: %s: %s\n"), s0path
,
6709 e_flag
= v_flag
= 0;
6710 retval
= ((err
= read_vtoc(fd
, &vtoc
)) >= 0) ? 0 : err
;
6713 BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6717 BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6721 BAM_DPRINTF(("%s: VTOC: unknown error while "
6722 "reading: %s\n", fcn
, s0path
));
6726 BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6731 BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6735 BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6736 "code: %s\n", fcn
, s0path
));
6743 retval
= ((err
= efi_alloc_and_read(fd
, &efi
)) >= 0) ? 0 : err
;
6746 BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6750 BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn
, s0path
));
6753 BAM_DPRINTF(("%s: EFI: unknown error while "
6754 "reading: %s\n", fcn
, s0path
));
6757 BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6762 BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6766 BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6775 retval
= process_vtoc_slices(s0
,
6776 &vtoc
, tfp
, mhp
, tmpmnt
);
6777 } else if (e_flag
) {
6778 retval
= process_efi_slices(s0
,
6779 efi
, tfp
, mhp
, tmpmnt
);
6781 BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6790 * Find and create a list of all existing UFS boot signatures
6793 FindAllUfsSignatures(void)
6795 mhash_t
*mnttab_hash
;
6798 char tmpmnt
[PATH_MAX
];
6806 const char *fcn
= "FindAllUfsSignatures()";
6808 if (stat(UFS_SIGNATURE_LIST
, &sb
) != -1) {
6809 bam_print(_(" - signature list %s exists\n"),
6810 UFS_SIGNATURE_LIST
);
6814 fd
= open(UFS_SIGNATURE_LIST
".tmp",
6815 O_RDWR
|O_CREAT
|O_TRUNC
, 0644);
6817 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd
= -1);
6819 bam_error(_("failed to open file: %s: %s\n"),
6820 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
6826 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret
= -1);
6828 bam_error(_("failed to close file: %s: %s\n"),
6829 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
6830 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6834 tfp
= fopen(UFS_SIGNATURE_LIST
".tmp", "a");
6836 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp
= NULL
);
6838 bam_error(_("failed to open file: %s: %s\n"),
6839 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
6840 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6844 mnttab_hash
= cache_mnttab();
6845 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash
= NULL
);
6846 if (mnttab_hash
== NULL
) {
6848 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6849 bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn
);
6853 (void) snprintf(tmpmnt
, sizeof (tmpmnt
),
6854 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6855 (void) unlink(tmpmnt
);
6857 ret
= mkdirp(tmpmnt
, DIR_PERMS
);
6859 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret
= -1);
6861 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt
,
6863 free_mnttab(mnttab_hash
);
6865 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6869 dirp
= opendir("/dev/rdsk");
6871 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp
= NULL
);
6873 bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
6878 while ((dp
= readdir(dirp
)) != NULL
) {
6879 if (strcmp(dp
->d_name
, ".") == 0 ||
6880 strcmp(dp
->d_name
, "..") == 0)
6884 * we only look for the s0 slice. This is guranteed to
6885 * have 's' at len - 2.
6887 len
= strlen(dp
->d_name
);
6888 if (dp
->d_name
[len
- 2 ] != 's' || dp
->d_name
[len
- 1] != '0') {
6889 BAM_DPRINTF(("%s: skipping non-s0 slice: %s\n",
6894 ret
= process_slice0(dp
->d_name
, tfp
, mnttab_hash
, tmpmnt
);
6895 INJECT_ERROR1("PROCESS_S0_FAIL", ret
= -1);
6900 (void) closedir(dirp
);
6901 free_mnttab(mnttab_hash
);
6902 (void) rmdir(tmpmnt
);
6906 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret
= EOF
);
6908 bam_error(_("failed to close file: %s: %s\n"),
6909 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
6910 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6914 /* We have a list of existing GRUB signatures. Sort it first */
6915 (void) snprintf(cmd
, sizeof (cmd
),
6916 "/usr/bin/sort -u %s.tmp > %s.sorted",
6917 UFS_SIGNATURE_LIST
, UFS_SIGNATURE_LIST
);
6919 ret
= exec_cmd(cmd
, NULL
);
6920 INJECT_ERROR1("SORT_SIGN_LIST", ret
= 1);
6922 bam_error(_("error sorting GRUB UFS boot signatures\n"));
6923 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
6924 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6928 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6930 ret
= rename(UFS_SIGNATURE_LIST
".sorted", UFS_SIGNATURE_LIST
);
6932 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret
= -1);
6934 bam_error(_("rename to file failed: %s: %s\n"),
6935 UFS_SIGNATURE_LIST
, strerror(error
));
6936 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
6940 if (stat(UFS_SIGNATURE_LIST
, &sb
) == 0 && sb
.st_size
== 0) {
6941 BAM_DPRINTF(("%s: generated zero length signlist: %s.\n",
6942 fcn
, UFS_SIGNATURE_LIST
));
6945 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
6950 (void) closedir(dirp
);
6951 free_mnttab(mnttab_hash
);
6952 (void) rmdir(tmpmnt
);
6954 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
6955 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
6960 create_ufs_sign(void)
6964 char tmpsign
[MAXNAMELEN
+ 1];
6970 const char *fcn
= "create_ufs_sign()";
6972 bam_print(_(" - searching for UFS boot signatures\n"));
6974 ret
= FindAllUfsSignatures();
6975 INJECT_ERROR1("FIND_ALL_UFS", ret
= -1);
6977 bam_error(_("search for UFS boot signatures failed\n"));
6981 /* Make sure the list exists and is owned by root */
6982 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6983 (void) unlink(UFS_SIGNATURE_LIST
));
6984 if (stat(UFS_SIGNATURE_LIST
, &sb
) == -1 || sb
.st_uid
!= 0) {
6985 (void) unlink(UFS_SIGNATURE_LIST
);
6986 bam_error(_("missing UFS signature list file: %s\n"),
6987 UFS_SIGNATURE_LIST
);
6991 if (sb
.st_size
== 0) {
6992 bam_print(_(" - no existing UFS boot signatures\n"));
6997 /* The signature list was sorted when it was created */
6998 tfp
= fopen(UFS_SIGNATURE_LIST
, "r");
7000 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp
= NULL
);
7002 bam_error(_("error opening UFS boot signature list "
7003 "file %s: %s\n"), UFS_SIGNATURE_LIST
, strerror(error
));
7004 (void) unlink(UFS_SIGNATURE_LIST
);
7008 for (i
= 0; s_fgets(tmpsign
, sizeof (tmpsign
), tfp
); i
++) {
7010 if (strncmp(tmpsign
, GRUBSIGN_UFS_PREFIX
,
7011 strlen(GRUBSIGN_UFS_PREFIX
)) != 0) {
7013 (void) unlink(UFS_SIGNATURE_LIST
);
7014 bam_error(_("bad UFS boot signature: %s\n"), tmpsign
);
7017 numstr
= tmpsign
+ strlen(GRUBSIGN_UFS_PREFIX
);
7019 if (numstr
[0] == '\0' || !isdigit(numstr
[0])) {
7021 (void) unlink(UFS_SIGNATURE_LIST
);
7022 bam_error(_("bad UFS boot signature: %s\n"), tmpsign
);
7026 signnum
= atoi(numstr
);
7027 INJECT_ERROR1("NEGATIVE_SIGN", signnum
= -1);
7030 (void) unlink(UFS_SIGNATURE_LIST
);
7031 bam_error(_("bad UFS boot signature: %s\n"), tmpsign
);
7036 BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7045 (void) snprintf(tmpsign
, sizeof (tmpsign
), "rootfs%d", i
);
7047 /* add the ufs signature to the /var/run list of signatures */
7048 ret
= ufs_add_to_sign_list(tmpsign
);
7049 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret
= -1);
7051 (void) unlink(UFS_SIGNATURE_LIST
);
7052 bam_error(_("failed to add sign %s to signlist.\n"), tmpsign
);
7056 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7058 return (s_strdup(tmpsign
));
7062 get_fstype(char *osroot
)
7065 struct mnttab mp
= {0};
7066 struct mnttab mpref
= {0};
7069 const char *fcn
= "get_fstype()";
7071 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot
= NULL
);
7072 if (osroot
== NULL
) {
7073 bam_error(_("no OS mountpoint. Cannot determine fstype\n"));
7077 mntfp
= fopen(MNTTAB
, "r");
7079 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp
= NULL
);
7080 if (mntfp
== NULL
) {
7081 bam_error(_("failed to open file: %s: %s\n"), MNTTAB
,
7086 if (*osroot
== '\0')
7087 mpref
.mnt_mountp
= "/";
7089 mpref
.mnt_mountp
= osroot
;
7091 ret
= getmntany(mntfp
, &mp
, &mpref
);
7092 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret
= 1);
7094 bam_error(_("failed to find OS mountpoint %s in %s\n"),
7096 (void) fclose(mntfp
);
7099 (void) fclose(mntfp
);
7101 INJECT_ERROR1("GET_FSTYPE_NULL", mp
.mnt_fstype
= NULL
);
7102 if (mp
.mnt_fstype
== NULL
) {
7103 bam_error(_("NULL fstype found for OS root %s\n"), osroot
);
7107 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7109 return (s_strdup(mp
.mnt_fstype
));
7113 create_zfs_sign(char *osdev
)
7115 char tmpsign
[PATH_MAX
];
7117 const char *fcn
= "create_zfs_sign()";
7119 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, osdev
));
7122 * First find the pool name
7124 pool
= get_pool(osdev
);
7125 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool
= NULL
);
7127 bam_error(_("failed to get pool name from %s\n"), osdev
);
7131 (void) snprintf(tmpsign
, sizeof (tmpsign
), "pool_%s", pool
);
7133 BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn
, tmpsign
));
7137 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7139 return (s_strdup(tmpsign
));
7143 create_new_sign(char *osdev
, char *fstype
)
7146 const char *fcn
= "create_new_sign()";
7148 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype
= "foofs");
7150 if (strcmp(fstype
, "zfs") == 0) {
7151 BAM_DPRINTF(("%s: created new ZFS sign\n", fcn
));
7152 sign
= create_zfs_sign(osdev
);
7153 } else if (strcmp(fstype
, "ufs") == 0) {
7154 BAM_DPRINTF(("%s: created new UFS sign\n", fcn
));
7155 sign
= create_ufs_sign();
7157 bam_error(_("boot signature not supported for fstype: %s\n"),
7162 BAM_DPRINTF(("%s: created new sign: %s\n", fcn
,
7163 sign
? sign
: "<NULL>"));
7168 set_backup_common(char *mntpt
, char *sign
)
7171 char backup
[PATH_MAX
];
7172 char tmpsign
[PATH_MAX
];
7178 const char *fcn
= "set_backup_common()";
7180 (void) snprintf(backup
, sizeof (backup
), "%s%s",
7181 mntpt
, GRUBSIGN_BACKUP
);
7183 /* First read the backup */
7184 bfp
= fopen(backup
, "r");
7186 while (s_fgets(tmpsign
, sizeof (tmpsign
), bfp
)) {
7187 if (strcmp(tmpsign
, sign
) == 0) {
7188 BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7195 BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7198 BAM_DPRINTF(("%s: no backup file (%s) found.\n", fcn
, backup
));
7202 * Didn't find the correct signature. First create
7203 * the directory if necessary.
7206 /* dirname() modifies its argument so dup it */
7207 backup_dup
= s_strdup(backup
);
7208 bdir
= dirname(backup_dup
);
7211 ret
= stat(bdir
, &sb
);
7212 INJECT_ERROR1("SET_BACKUP_STAT", ret
= -1);
7214 BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7216 ret
= mkdirp(bdir
, DIR_PERMS
);
7218 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret
= -1);
7220 bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7221 GRUBSIGN_BACKUP
, strerror(error
));
7229 * Open the backup in append mode to add the correct
7232 bfp
= fopen(backup
, "a");
7234 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp
= NULL
);
7236 bam_error(_("error opening boot signature backup "
7237 "file %s: %s\n"), GRUBSIGN_BACKUP
, strerror(error
));
7241 (void) snprintf(tmpsign
, sizeof (tmpsign
), "%s\n", sign
);
7243 ret
= fputs(tmpsign
, bfp
);
7245 INJECT_ERROR1("SET_BACKUP_FPUTS", ret
= 0);
7246 if (ret
!= strlen(tmpsign
)) {
7247 bam_error(_("error writing boot signature backup "
7248 "file %s: %s\n"), GRUBSIGN_BACKUP
, strerror(error
));
7256 bam_print(_("updated boot signature backup file %s\n"),
7259 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7265 set_backup_ufs(char *osroot
, char *sign
)
7267 const char *fcn
= "set_backup_ufs()";
7269 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, sign
));
7270 return (set_backup_common(osroot
, sign
));
7274 set_backup_zfs(char *osdev
, char *sign
)
7280 const char *fcn
= "set_backup_zfs()";
7282 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osdev
, sign
));
7284 pool
= get_pool(osdev
);
7285 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool
= NULL
);
7287 bam_error(_("failed to get pool name from %s\n"), osdev
);
7291 mntpt
= mount_top_dataset(pool
, &mnted
);
7292 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt
= NULL
);
7293 if (mntpt
== NULL
) {
7294 bam_error(_("failed to mount top dataset for %s\n"), pool
);
7299 ret
= set_backup_common(mntpt
, sign
);
7301 (void) umount_top_dataset(pool
, mnted
, mntpt
);
7305 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret
= 1);
7307 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7309 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7316 set_backup(char *osroot
, char *osdev
, char *sign
, char *fstype
)
7318 const char *fcn
= "set_backup()";
7321 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype
= "foofs");
7323 if (strcmp(fstype
, "ufs") == 0) {
7324 BAM_DPRINTF(("%s: setting UFS backup sign\n", fcn
));
7325 ret
= set_backup_ufs(osroot
, sign
);
7326 } else if (strcmp(fstype
, "zfs") == 0) {
7327 BAM_DPRINTF(("%s: setting ZFS backup sign\n", fcn
));
7328 ret
= set_backup_zfs(osdev
, sign
);
7330 bam_error(_("boot signature not supported for fstype: %s\n"),
7336 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7338 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7345 set_primary_common(char *mntpt
, char *sign
)
7347 char signfile
[PATH_MAX
];
7348 char signdir
[PATH_MAX
];
7353 const char *fcn
= "set_primary_common()";
7355 (void) snprintf(signfile
, sizeof (signfile
), "%s/%s/%s",
7356 mntpt
, GRUBSIGN_DIR
, sign
);
7358 if (stat(signfile
, &sb
) != -1) {
7360 bam_print(_("primary sign %s exists\n"), sign
);
7363 BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7367 (void) snprintf(signdir
, sizeof (signdir
), "%s/%s",
7368 mntpt
, GRUBSIGN_DIR
);
7370 if (stat(signdir
, &sb
) == -1) {
7371 BAM_DPRINTF(("%s: primary signdir (%s) does not exist\n",
7373 ret
= mkdirp(signdir
, DIR_PERMS
);
7375 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret
= -1);
7377 bam_error(_("error creating boot signature "
7378 "directory %s: %s\n"), signdir
, strerror(errno
));
7383 fd
= open(signfile
, O_RDWR
|O_CREAT
|O_TRUNC
, 0444);
7385 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd
= -1);
7387 bam_error(_("error creating primary boot signature %s: %s\n"),
7388 signfile
, strerror(error
));
7394 INJECT_ERROR1("PRIMARY_FSYNC", ret
= -1);
7396 bam_error(_("error syncing primary boot signature %s: %s\n"),
7397 signfile
, strerror(error
));
7403 bam_print(_("created primary GRUB boot signature: %s\n"),
7406 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7412 set_primary_ufs(char *osroot
, char *sign
)
7414 const char *fcn
= "set_primary_ufs()";
7416 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, sign
));
7417 return (set_primary_common(osroot
, sign
));
7421 set_primary_zfs(char *osdev
, char *sign
)
7427 const char *fcn
= "set_primary_zfs()";
7429 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osdev
, sign
));
7431 pool
= get_pool(osdev
);
7432 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool
= NULL
);
7434 bam_error(_("failed to get pool name from %s\n"), osdev
);
7438 /* Pool name must exist in the sign */
7439 ret
= (strstr(sign
, pool
) != NULL
);
7440 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret
= 0);
7442 bam_error(_("pool name %s not present in signature %s\n"),
7448 mntpt
= mount_top_dataset(pool
, &mnted
);
7449 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt
= NULL
);
7450 if (mntpt
== NULL
) {
7451 bam_error(_("failed to mount top dataset for %s\n"), pool
);
7456 ret
= set_primary_common(mntpt
, sign
);
7458 (void) umount_top_dataset(pool
, mnted
, mntpt
);
7462 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret
= 1);
7464 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7466 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7473 set_primary(char *osroot
, char *osdev
, char *sign
, char *fstype
)
7475 const char *fcn
= "set_primary()";
7478 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype
= "foofs");
7479 if (strcmp(fstype
, "ufs") == 0) {
7480 BAM_DPRINTF(("%s: setting UFS primary sign\n", fcn
));
7481 ret
= set_primary_ufs(osroot
, sign
);
7482 } else if (strcmp(fstype
, "zfs") == 0) {
7483 BAM_DPRINTF(("%s: setting ZFS primary sign\n", fcn
));
7484 ret
= set_primary_zfs(osdev
, sign
);
7486 bam_error(_("boot signature not supported for fstype: %s\n"),
7492 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7494 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7501 ufs_add_to_sign_list(char *sign
)
7504 char signline
[MAXNAMELEN
];
7508 const char *fcn
= "ufs_add_to_sign_list()";
7510 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign
= "pool_rpool5");
7511 if (strncmp(sign
, GRUBSIGN_UFS_PREFIX
,
7512 strlen(GRUBSIGN_UFS_PREFIX
)) != 0) {
7513 bam_error(_("invalid UFS boot signature %s\n"), sign
);
7514 (void) unlink(UFS_SIGNATURE_LIST
);
7519 * most failures in this routine are not a fatal error
7520 * We simply unlink the /var/run file and continue
7523 ret
= rename(UFS_SIGNATURE_LIST
, UFS_SIGNATURE_LIST
".tmp");
7525 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret
= -1);
7527 bam_error(_("rename to file failed: %s: %s\n"),
7528 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7529 (void) unlink(UFS_SIGNATURE_LIST
);
7533 tfp
= fopen(UFS_SIGNATURE_LIST
".tmp", "a");
7535 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp
= NULL
);
7537 bam_error(_("failed to open file: %s: %s\n"),
7538 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7539 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7543 (void) snprintf(signline
, sizeof (signline
), "%s\n", sign
);
7545 ret
= fputs(signline
, tfp
);
7547 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret
= 0);
7548 if (ret
!= strlen(signline
)) {
7549 bam_error(_("failed to write signature %s to signature "
7550 "list: %s\n"), sign
, strerror(error
));
7552 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7558 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret
= EOF
);
7560 bam_error(_("failed to close file: %s: %s\n"),
7561 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7562 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7566 /* Sort the list again */
7567 (void) snprintf(cmd
, sizeof (cmd
),
7568 "/usr/bin/sort -u %s.tmp > %s.sorted",
7569 UFS_SIGNATURE_LIST
, UFS_SIGNATURE_LIST
);
7571 ret
= exec_cmd(cmd
, NULL
);
7572 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret
= 1);
7574 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7575 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
7576 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7580 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7582 ret
= rename(UFS_SIGNATURE_LIST
".sorted", UFS_SIGNATURE_LIST
);
7584 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret
= -1);
7586 bam_error(_("rename to file failed: %s: %s\n"),
7587 UFS_SIGNATURE_LIST
, strerror(error
));
7588 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
7592 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7598 set_signature(char *osroot
, char *osdev
, char *sign
, char *fstype
)
7601 const char *fcn
= "set_signature()";
7603 BAM_DPRINTF(("%s: entered. args: %s %s %s %s\n", fcn
,
7604 osroot
, osdev
, sign
, fstype
));
7606 ret
= set_backup(osroot
, osdev
, sign
, fstype
);
7607 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret
= -1);
7609 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7610 bam_error(_("failed to set backup sign (%s) for %s: %s\n"),
7611 sign
, osroot
, osdev
);
7615 ret
= set_primary(osroot
, osdev
, sign
, fstype
);
7616 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret
= -1);
7619 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7621 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7622 bam_error(_("failed to set primary sign (%s) for %s: %s\n"),
7623 sign
, osroot
, osdev
);
7630 get_grubsign(char *osroot
, char *osdev
)
7632 char *grubsign
; /* (<sign>,#,#) */
7638 const char *fcn
= "get_grubsign()";
7640 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, osdev
));
7641 fstype
= get_fstype(osroot
);
7642 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype
= NULL
);
7643 if (fstype
== NULL
) {
7644 bam_error(_("failed to get fstype for %s\n"), osroot
);
7648 sign
= find_existing_sign(osroot
, osdev
, fstype
);
7649 INJECT_ERROR1("FIND_EXISTING_SIGN", sign
= NULL
);
7651 BAM_DPRINTF(("%s: no existing grubsign for %s: %s\n",
7652 fcn
, osroot
, osdev
));
7653 sign
= create_new_sign(osdev
, fstype
);
7654 INJECT_ERROR1("CREATE_NEW_SIGN", sign
= NULL
);
7656 bam_error(_("failed to create GRUB boot signature for "
7657 "device: %s\n"), osdev
);
7663 ret
= set_signature(osroot
, osdev
, sign
, fstype
);
7664 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret
= -1);
7666 bam_error(_("failed to write GRUB boot signature for "
7667 "device: %s\n"), osdev
);
7670 (void) unlink(UFS_SIGNATURE_LIST
);
7677 bam_print(_("found or created GRUB signature %s for %s\n"),
7680 fdiskpart
= get_partition(osdev
);
7681 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart
= PARTNO_NOTFOUND
);
7682 if (fdiskpart
== PARTNO_NOTFOUND
) {
7683 bam_error(_("failed to determine fdisk partition: %s\n"),
7689 slice
= strrchr(osdev
, 's');
7691 if (fdiskpart
== PARTNO_EFI
) {
7692 fdiskpart
= atoi(&slice
[1]);
7696 grubsign
= s_calloc(1, MAXNAMELEN
+ 10);
7698 (void) snprintf(grubsign
, MAXNAMELEN
+ 10, "(%s,%d,%c)",
7699 sign
, fdiskpart
, slice
[1] + 'a' - '0');
7701 (void) snprintf(grubsign
, MAXNAMELEN
+ 10, "(%s,%d)",
7706 BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn
, grubsign
));
7712 get_title(char *rootdir
)
7714 static char title
[80];
7716 char release
[PATH_MAX
];
7718 const char *fcn
= "get_title()";
7720 /* open the /etc/release file */
7721 (void) snprintf(release
, sizeof (release
), "%s/etc/release", rootdir
);
7723 fp
= fopen(release
, "r");
7725 bam_error(_("failed to open file: %s: %s\n"), release
,
7731 /* grab first line of /etc/release */
7732 cp
= s_fgets(title
, sizeof (title
), fp
);
7734 while (isspace(*cp
)) /* remove leading spaces */
7741 cp
= cp
? cp
: "Oracle Solaris";
7743 BAM_DPRINTF(("%s: got title: %s\n", fcn
, cp
));
7749 get_special(char *mountp
)
7752 struct mnttab mp
= {0};
7753 struct mnttab mpref
= {0};
7756 const char *fcn
= "get_special()";
7758 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp
= NULL
);
7759 if (mountp
== NULL
) {
7760 bam_error(_("cannot get special file: NULL mount-point\n"));
7764 mntfp
= fopen(MNTTAB
, "r");
7766 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp
= NULL
);
7767 if (mntfp
== NULL
) {
7768 bam_error(_("failed to open file: %s: %s\n"), MNTTAB
,
7773 if (*mountp
== '\0')
7774 mpref
.mnt_mountp
= "/";
7776 mpref
.mnt_mountp
= mountp
;
7778 ret
= getmntany(mntfp
, &mp
, &mpref
);
7779 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret
= 1);
7781 (void) fclose(mntfp
);
7782 BAM_DPRINTF(("%s: Cannot get special file: mount-point %s "
7783 "not in mnttab\n", fcn
, mountp
));
7786 (void) fclose(mntfp
);
7788 BAM_DPRINTF(("%s: returning special: %s\n", fcn
, mp
.mnt_special
));
7790 return (s_strdup(mp
.mnt_special
));
7794 free_physarray(char **physarray
, int n
)
7797 const char *fcn
= "free_physarray()";
7802 BAM_DPRINTF(("%s: entering args: %d\n", fcn
, n
));
7804 for (i
= 0; i
< n
; i
++) {
7809 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7813 zfs_get_physical(char *special
, char ***physarray
, int *n
)
7815 char sdup
[PATH_MAX
];
7819 filelist_t flist
= {0};
7825 const char *fcn
= "zfs_get_physical()";
7829 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, special
));
7831 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special
= "/foo");
7832 if (special
[0] == '/') {
7833 bam_error(_("invalid device for ZFS filesystem: %s\n"),
7838 (void) strlcpy(sdup
, special
, sizeof (sdup
));
7840 pool
= strtok(sdup
, "/");
7841 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool
= NULL
);
7843 bam_error(_("cannot derive ZFS pool from special: %s\n"),
7848 (void) snprintf(cmd
, sizeof (cmd
), "/sbin/zpool status %s", pool
);
7850 ret
= exec_cmd(cmd
, &flist
);
7851 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret
= 1);
7853 bam_error(_("cannot get zpool status for pool: %s\n"), pool
);
7857 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist
.head
= NULL
);
7858 if (flist
.head
== NULL
) {
7859 bam_error(_("bad zpool status for pool=%s\n"), pool
);
7860 filelist_free(&flist
);
7864 for (lp
= flist
.head
; lp
; lp
= lp
->next
) {
7865 BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
7867 comp1
= strtok(lp
->line
, " \t");
7868 if (comp1
== NULL
) {
7872 comp1
= s_strdup(comp1
);
7878 for (lp
= flist
.head
; lp
; lp
= lp
->next
) {
7879 if (lp
->line
== NULL
)
7881 if (strcmp(lp
->line
, pool
) == 0) {
7882 BAM_DPRINTF(("%s: found pool name: %s in zpool "
7883 "status\n", fcn
, pool
));
7889 bam_error(_("no pool name %s in zpool status\n"), pool
);
7890 filelist_free(&flist
);
7895 for (i
= 0, lp
= startlp
; lp
; lp
= lp
->next
) {
7896 if (lp
->line
== NULL
)
7898 if (strcmp(lp
->line
, "mirror") == 0)
7900 if (lp
->line
[0] == '\0' || strcmp(lp
->line
, "errors:") == 0)
7903 BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
7908 bam_error(_("no physical device in zpool status for pool=%s\n"),
7910 filelist_free(&flist
);
7915 *physarray
= s_calloc(*n
, sizeof (char *));
7916 for (i
= 0, lp
= startlp
; lp
; lp
= lp
->next
) {
7917 if (lp
->line
== NULL
)
7919 if (strcmp(lp
->line
, "mirror") == 0)
7921 if (strcmp(lp
->line
, "errors:") == 0)
7923 if (strncmp(lp
->line
, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7924 strncmp(lp
->line
, "/dev/rdsk/",
7925 strlen("/dev/rdsk/")) != 0) {
7926 (void) snprintf(dsk
, sizeof (dsk
), "/dev/rdsk/%s",
7929 (void) strlcpy(dsk
, lp
->line
, sizeof (dsk
));
7931 BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
7933 (*physarray
)[i
++] = s_strdup(dsk
);
7938 filelist_free(&flist
);
7940 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7945 get_physical(char *menu_root
, char ***physarray
, int *n
)
7949 const char *fcn
= "get_physical()";
7958 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, menu_root
));
7960 /* First get the device special file from /etc/mnttab */
7961 special
= get_special(menu_root
);
7962 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special
= NULL
);
7963 if (special
== NULL
) {
7964 bam_error(_("cannot get special file for mount-point: %s\n"),
7969 /* If already a physical device nothing to do */
7970 if (strncmp(special
, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7971 strncmp(special
, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7972 BAM_DPRINTF(("%s: got physical device already directly for "
7973 "menu_root=%s special=%s\n", fcn
, menu_root
, special
));
7974 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7975 *physarray
= s_calloc(1, sizeof (char *));
7976 (*physarray
)[0] = special
;
7981 if (is_zfs(menu_root
)) {
7982 ret
= zfs_get_physical(special
, physarray
, n
);
7984 bam_error(_("cannot derive physical device for %s (%s), "
7985 "unsupported filesystem\n"), menu_root
, special
);
7991 INJECT_ERROR1("GET_PHYSICAL_RET", ret
= -1);
7993 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7997 for (i
= 0; i
< *n
; i
++) {
7998 BAM_DPRINTF(("%s: returning physical=%s\n",
7999 fcn
, (*physarray
)[i
]));
8007 is_bootdisk(char *osroot
, char *physical
)
8012 const char *fcn
= "is_bootdisk()";
8017 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, physical
));
8019 bootp
= strstr(physical
, "p0:boot");
8023 * We just want the BIOS mapping for menu disk.
8024 * Don't pass menu_root to get_grubroot() as the
8025 * check that it is used for is not relevant here.
8026 * The osroot is immaterial as well - it is only used to
8027 * to find create_diskmap script. Everything hinges on
8030 grubroot
= get_grubroot(osroot
, physical
, NULL
);
8032 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot
= NULL
);
8033 if (grubroot
== NULL
) {
8035 bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8036 "disk: %s\n"), physical
);
8039 ret
= grubroot
[3] == '0';
8042 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, ret
));
8048 * Check if menu is on the boot device
8049 * Return 0 (false) on error
8052 menu_on_bootdisk(char *osroot
, char *menu_root
)
8059 const char *fcn
= "menu_on_bootdisk()";
8061 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, menu_root
));
8063 ret
= get_physical(menu_root
, &physarray
, &n
);
8064 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret
= -1);
8066 bam_error(_("cannot get physical device special file for menu "
8067 "root: %s\n"), menu_root
);
8075 for (i
= 0; i
< n
; i
++) {
8076 assert(strncmp(physarray
[i
], "/dev/dsk/",
8077 strlen("/dev/dsk/")) == 0 ||
8078 strncmp(physarray
[i
], "/dev/rdsk/",
8079 strlen("/dev/rdsk/")) == 0);
8081 BAM_DPRINTF(("%s: checking if phys-device=%s is on bootdisk\n",
8082 fcn
, physarray
[i
]));
8083 if (is_bootdisk(osroot
, physarray
[i
])) {
8085 BAM_DPRINTF(("%s: phys-device=%s *IS* on bootdisk\n",
8086 fcn
, physarray
[i
]));
8090 free_physarray(physarray
, n
);
8092 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk
= 1);
8093 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk
= 0);
8095 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8097 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
8100 return (on_bootdisk
);
8104 bam_add_line(menu_t
*mp
, entry_t
*entry
, line_t
*prev
, line_t
*lp
)
8106 const char *fcn
= "bam_add_line()";
8113 lp
->next
= prev
->next
;
8115 BAM_DPRINTF(("%s: previous next exists\n", fcn
));
8116 prev
->next
->prev
= lp
;
8118 BAM_DPRINTF(("%s: previous next does not exist\n", fcn
));
8123 if (entry
->end
== prev
) {
8124 BAM_DPRINTF(("%s: last line in entry\n", fcn
));
8127 if (mp
->end
== prev
) {
8128 assert(lp
->next
== NULL
);
8130 BAM_DPRINTF(("%s: last line in menu\n", fcn
));
8135 * look for matching bootadm entry with specified parameters
8136 * Here are the rules (based on existing usage):
8137 * - If title is specified, match on title only
8138 * - Else, match on root/findroot, kernel, and module.
8139 * Note that, if root_opt is non-zero, the absence of
8140 * root line is considered a match.
8156 const char *fcn
= "find_boot_entry()";
8159 *entry_num
= BAM_ERROR
;
8161 /* find matching entry */
8162 for (i
= 0, ent
= mp
->entries
; ent
; i
++, ent
= ent
->next
) {
8165 /* first line of entry must be bootadm comment */
8167 if (lp
->flags
!= BAM_COMMENT
||
8168 strcmp(lp
->arg
, BAM_BOOTADM_HDR
) != 0) {
8172 /* advance to title line */
8175 if (lp
->flags
== BAM_TITLE
&& lp
->arg
&&
8176 strcmp(lp
->arg
, title
) == 0) {
8177 BAM_DPRINTF(("%s: matched title: %s\n",
8181 BAM_DPRINTF(("%s: no match title: %s, %s\n",
8182 fcn
, title
, lp
->arg
));
8183 continue; /* check title only */
8186 lp
= lp
->next
; /* advance to root line */
8189 } else if (lp
->cmd
!= NULL
&&
8190 strcmp(lp
->cmd
, menu_cmds
[FINDROOT_CMD
]) == 0) {
8191 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8193 if (findroot
== NULL
) {
8194 BAM_DPRINTF(("%s: no match line has findroot, "
8195 "we don't: %s\n", fcn
, lp
->arg
));
8198 /* findroot command found, try match */
8199 if (strcmp(lp
->arg
, findroot
) != 0) {
8200 BAM_DPRINTF(("%s: no match findroot: %s, %s\n",
8201 fcn
, findroot
, lp
->arg
));
8204 BAM_DPRINTF(("%s: matched findroot: %s\n",
8206 lp
= lp
->next
; /* advance to kernel line */
8207 } else if (lp
->cmd
!= NULL
&&
8208 strcmp(lp
->cmd
, menu_cmds
[ROOT_CMD
]) == 0) {
8209 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root
= NULL
);
8211 BAM_DPRINTF(("%s: no match, line has root, we "
8212 "don't: %s\n", fcn
, lp
->arg
));
8215 /* root cmd found, try match */
8216 if (strcmp(lp
->arg
, root
) != 0) {
8217 BAM_DPRINTF(("%s: no match root: %s, %s\n",
8218 fcn
, root
, lp
->arg
));
8221 BAM_DPRINTF(("%s: matched root: %s\n", fcn
, root
));
8222 lp
= lp
->next
; /* advance to kernel line */
8224 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8226 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8228 /* no root command, see if root is optional */
8229 if (root_opt
== 0) {
8230 BAM_DPRINTF(("%s: root NOT optional\n", fcn
));
8233 BAM_DPRINTF(("%s: root IS optional\n", fcn
));
8236 if (lp
== NULL
|| lp
->next
== NULL
) {
8241 (!check_cmd(lp
->cmd
, KERNEL_CMD
, lp
->arg
, kernel
))) {
8242 if (!(ent
->flags
& BAM_ENTRY_FAILSAFE
) ||
8243 !(ent
->flags
& BAM_ENTRY_DBOOT
) ||
8244 strcmp(kernel
, DIRECT_BOOT_FAILSAFE_LINE
) != 0)
8247 ent
->flags
|= BAM_ENTRY_UPGFSKERNEL
;
8250 BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn
,
8254 * Check for matching module entry (failsafe or normal).
8255 * If it fails to match, we go around the loop again.
8256 * For xpv entries, there are two module lines, so we
8257 * do the check twice.
8259 lp
= lp
->next
; /* advance to module line */
8260 if (check_cmd(lp
->cmd
, MODULE_CMD
, lp
->arg
, module
) ||
8261 (((lp
= lp
->next
) != NULL
) &&
8262 check_cmd(lp
->cmd
, MODULE_CMD
, lp
->arg
, module
))) {
8264 BAM_DPRINTF(("%s: module match: %s, %s\n", fcn
,
8269 if (strcmp(module
, FAILSAFE_ARCHIVE
) == 0 &&
8270 (strcmp(lp
->prev
->arg
, FAILSAFE_ARCHIVE_32
) == 0 ||
8271 strcmp(lp
->prev
->arg
, FAILSAFE_ARCHIVE_64
) == 0)) {
8272 ent
->flags
|= BAM_ENTRY_UPGFSMODULE
;
8278 if (ent
&& entry_num
) {
8283 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, i
));
8285 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, BAM_ERROR
));
8291 update_boot_entry(menu_t
*mp
, char *title
, char *findroot
, char *root
,
8292 char *kernel
, char *mod_kernel
, char *module
, int root_opt
)
8295 int change_kernel
= 0;
8299 char linebuf
[BAM_MAXLINE
];
8300 const char *fcn
= "update_boot_entry()";
8302 /* note: don't match on title, it's updated on upgrade */
8303 ent
= find_boot_entry(mp
, NULL
, kernel
, findroot
, root
, module
,
8305 if ((ent
== NULL
) && (bam_direct
== BAM_DIRECT_DBOOT
)) {
8307 * We may be upgrading a kernel from multiboot to
8308 * directboot. Look for a multiboot entry. A multiboot
8309 * entry will not have a findroot line.
8311 ent
= find_boot_entry(mp
, NULL
, "multiboot", NULL
, root
,
8312 MULTIBOOT_ARCHIVE
, root_opt
, &i
);
8314 BAM_DPRINTF(("%s: upgrading entry from dboot to "
8315 "multiboot: root = %s\n", fcn
, root
));
8319 BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8324 BAM_DPRINTF(("%s: boot entry not found in menu. Creating "
8325 "new entry, findroot = %s\n", fcn
, findroot
));
8326 return (add_boot_entry(mp
, title
, findroot
,
8327 kernel
, mod_kernel
, module
, NULL
));
8330 /* replace title of existing entry and update findroot line */
8332 lp
= lp
->next
; /* title line */
8333 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8334 menu_cmds
[TITLE_CMD
], menu_cmds
[SEP_CMD
], title
);
8337 lp
->arg
= s_strdup(title
);
8338 lp
->line
= s_strdup(linebuf
);
8339 BAM_DPRINTF(("%s: changing title to: %s\n", fcn
, title
));
8341 tlp
= lp
; /* title line */
8342 lp
= lp
->next
; /* root line */
8344 /* if no root or findroot command, create a new line_t */
8345 if ((lp
->cmd
!= NULL
) && (strcmp(lp
->cmd
, menu_cmds
[ROOT_CMD
]) != 0 &&
8346 strcmp(lp
->cmd
, menu_cmds
[FINDROOT_CMD
]) != 0)) {
8347 lp
= s_calloc(1, sizeof (line_t
));
8348 bam_add_line(mp
, ent
, tlp
, lp
);
8350 if (lp
->cmd
!= NULL
)
8358 lp
->cmd
= s_strdup(menu_cmds
[FINDROOT_CMD
]);
8359 lp
->sep
= s_strdup(menu_cmds
[SEP_CMD
]);
8360 lp
->arg
= s_strdup(findroot
);
8361 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8362 menu_cmds
[FINDROOT_CMD
], menu_cmds
[SEP_CMD
], findroot
);
8363 lp
->line
= s_strdup(linebuf
);
8364 BAM_DPRINTF(("%s: adding findroot line: %s\n", fcn
, findroot
));
8369 if (ent
->flags
& BAM_ENTRY_UPGFSKERNEL
) {
8370 char *params
= NULL
;
8372 params
= strstr(lp
->line
, "-s");
8374 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s%s",
8375 menu_cmds
[KERNEL_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8378 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8379 menu_cmds
[KERNEL_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8382 if (lp
->cmd
!= NULL
)
8387 lp
->cmd
= s_strdup(menu_cmds
[KERNEL_DOLLAR_CMD
]);
8388 lp
->arg
= s_strdup(strstr(linebuf
, "/"));
8389 lp
->line
= s_strdup(linebuf
);
8390 ent
->flags
&= ~BAM_ENTRY_UPGFSKERNEL
;
8391 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8392 fcn
, lp
->prev
->cmd
));
8395 if (change_kernel
) {
8397 * We're upgrading from multiboot to directboot.
8399 if (lp
->cmd
!= NULL
&&
8400 strcmp(lp
->cmd
, menu_cmds
[KERNEL_CMD
]) == 0) {
8401 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8402 menu_cmds
[KERNEL_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8407 lp
->cmd
= s_strdup(menu_cmds
[KERNEL_DOLLAR_CMD
]);
8408 lp
->arg
= s_strdup(kernel
);
8409 lp
->line
= s_strdup(linebuf
);
8411 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8414 if (lp
->cmd
!= NULL
&&
8415 strcmp(lp
->cmd
, menu_cmds
[MODULE_CMD
]) == 0) {
8416 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8417 menu_cmds
[MODULE_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8422 lp
->cmd
= s_strdup(menu_cmds
[MODULE_DOLLAR_CMD
]);
8423 lp
->arg
= s_strdup(module
);
8424 lp
->line
= s_strdup(linebuf
);
8426 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8434 if (ent
->flags
& BAM_ENTRY_UPGFSMODULE
) {
8435 if (lp
->cmd
!= NULL
&&
8436 strcmp(lp
->cmd
, menu_cmds
[MODULE_CMD
]) == 0) {
8437 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8438 menu_cmds
[MODULE_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8443 lp
->cmd
= s_strdup(menu_cmds
[MODULE_DOLLAR_CMD
]);
8444 lp
->arg
= s_strdup(module
);
8445 lp
->line
= s_strdup(linebuf
);
8447 ent
->flags
&= ~BAM_ENTRY_UPGFSMODULE
;
8448 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8453 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, i
));
8458 root_optional(char *osroot
, char *menu_root
)
8466 const char *fcn
= "root_optional()";
8468 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, menu_root
));
8471 * For all filesystems except ZFS, a straight compare of osroot
8472 * and menu_root will tell us if root is optional.
8473 * For ZFS, the situation is complicated by the fact that
8474 * menu_root and osroot are always different
8476 ret1
= is_zfs(osroot
);
8477 ret2
= is_zfs(menu_root
);
8478 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1
= 0);
8479 if (!ret1
|| !ret2
) {
8480 BAM_DPRINTF(("%s: one or more non-ZFS filesystems (%s, %s)\n",
8481 fcn
, osroot
, menu_root
));
8482 root_opt
= (strcmp(osroot
, menu_root
) == 0);
8486 ospecial
= get_special(osroot
);
8487 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial
= NULL
);
8488 if (ospecial
== NULL
) {
8489 bam_error(_("failed to get special file for osroot: %s\n"),
8493 BAM_DPRINTF(("%s: ospecial=%s for osroot=%s\n", fcn
, ospecial
, osroot
));
8495 mspecial
= get_special(menu_root
);
8496 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial
= NULL
);
8497 if (mspecial
== NULL
) {
8498 bam_error(_("failed to get special file for menu_root: %s\n"),
8503 BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8504 fcn
, mspecial
, menu_root
));
8506 slash
= strchr(ospecial
, '/');
8509 BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8510 fcn
, ospecial
, osroot
));
8512 root_opt
= (strcmp(ospecial
, mspecial
) == 0);
8518 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt
= 0);
8519 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt
= 1);
8521 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8523 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
8531 update_entry(menu_t
*mp
, char *menu_root
, char *osdev
)
8537 char osroot
[PATH_MAX
];
8538 char *failsafe_kernel
= NULL
;
8541 char failsafe_64
[256];
8543 const char *fcn
= "update_entry()";
8550 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn
, menu_root
, osdev
,
8553 (void) strlcpy(osroot
, bam_root
, sizeof (osroot
));
8555 title
= get_title(osroot
);
8558 grubsign
= get_grubsign(osroot
, osdev
);
8559 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign
= NULL
);
8560 if (grubsign
== NULL
) {
8561 bam_error(_("failed to get grubsign for root: %s, device %s\n"),
8567 * It is not a fatal error if get_grubroot() fails
8568 * We no longer rely on biosdev to populate the
8571 grubroot
= get_grubroot(osroot
, osdev
, menu_root
);
8572 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot
= NULL
);
8574 BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8575 "menu_root=%s\n", fcn
, osroot
, osdev
, menu_root
));
8577 BAM_DPRINTF(("%s: get_grubroot failed. osroot=%s, osdev=%s, "
8578 "menu_root=%s\n", fcn
, osroot
, osdev
, menu_root
));
8581 /* add the entry for normal Solaris */
8582 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8583 bam_direct
= BAM_DIRECT_MULTIBOOT
);
8584 if (bam_direct
== BAM_DIRECT_DBOOT
) {
8585 entry
= update_boot_entry(mp
, title
, grubsign
, grubroot
,
8586 (bam_zfs
? DIRECT_BOOT_KERNEL_ZFS
: DIRECT_BOOT_KERNEL
),
8587 NULL
, DIRECT_BOOT_ARCHIVE
,
8588 root_optional(osroot
, menu_root
));
8589 BAM_DPRINTF(("%s: updated boot entry bam_zfs=%d, "
8590 "grubsign = %s\n", fcn
, bam_zfs
, grubsign
));
8591 if ((entry
!= BAM_ERROR
) && (bam_is_hv
== BAM_HV_PRESENT
)) {
8592 (void) update_boot_entry(mp
, NEW_HV_ENTRY
, grubsign
,
8593 grubroot
, XEN_MENU
, bam_zfs
?
8594 XEN_KERNEL_MODULE_LINE_ZFS
: XEN_KERNEL_MODULE_LINE
,
8595 DIRECT_BOOT_ARCHIVE
,
8596 root_optional(osroot
, menu_root
));
8597 BAM_DPRINTF(("%s: updated HV entry bam_zfs=%d, "
8598 "grubsign = %s\n", fcn
, bam_zfs
, grubsign
));
8601 entry
= update_boot_entry(mp
, title
, grubsign
, grubroot
,
8602 MULTI_BOOT
, NULL
, MULTIBOOT_ARCHIVE
,
8603 root_optional(osroot
, menu_root
));
8605 BAM_DPRINTF(("%s: updated MULTIBOOT entry grubsign = %s\n",
8610 * Add the entry for failsafe archive. On a bfu'd system, the
8611 * failsafe may be different than the installed kernel.
8613 (void) snprintf(failsafe
, sizeof (failsafe
), "%s%s",
8614 osroot
, FAILSAFE_ARCHIVE_32
);
8615 (void) snprintf(failsafe_64
, sizeof (failsafe_64
), "%s%s",
8616 osroot
, FAILSAFE_ARCHIVE_64
);
8619 * Check if at least one of the two archives exists
8620 * Using $ISADIR as the default line, we have an entry which works
8621 * for both the cases.
8624 if (stat(failsafe
, &sbuf
) == 0 || stat(failsafe_64
, &sbuf
) == 0) {
8626 /* Figure out where the kernel line should point */
8627 (void) snprintf(failsafe
, sizeof (failsafe
), "%s%s", osroot
,
8628 DIRECT_BOOT_FAILSAFE_32
);
8629 (void) snprintf(failsafe_64
, sizeof (failsafe_64
), "%s%s",
8630 osroot
, DIRECT_BOOT_FAILSAFE_64
);
8631 if (stat(failsafe
, &sbuf
) == 0 ||
8632 stat(failsafe_64
, &sbuf
) == 0) {
8633 failsafe_kernel
= DIRECT_BOOT_FAILSAFE_LINE
;
8635 (void) snprintf(failsafe
, sizeof (failsafe
), "%s%s",
8636 osroot
, MULTI_BOOT_FAILSAFE
);
8637 if (stat(failsafe
, &sbuf
) == 0) {
8638 failsafe_kernel
= MULTI_BOOT_FAILSAFE_LINE
;
8641 if (failsafe_kernel
!= NULL
) {
8642 (void) update_boot_entry(mp
, FAILSAFE_TITLE
, grubsign
,
8643 grubroot
, failsafe_kernel
, NULL
, FAILSAFE_ARCHIVE
,
8644 root_optional(osroot
, menu_root
));
8645 BAM_DPRINTF(("%s: updated FAILSAFE entry "
8646 "failsafe_kernel = %s\n", fcn
, failsafe_kernel
));
8651 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry
= BAM_ERROR
);
8652 if (entry
== BAM_ERROR
) {
8653 bam_error(_("failed to add boot entry with title=%s, grub "
8654 "signature=%s\n"), title
, grubsign
);
8660 update_numbering(mp
);
8661 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
8662 INJECT_ERROR1("SET_DEFAULT_ERROR", ret
= BAM_ERROR
);
8663 if (ret
== BAM_ERROR
) {
8664 bam_error(_("failed to set GRUB menu default to %d\n"), entry
);
8666 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8671 save_default_entry(menu_t
*mp
, const char *which
)
8675 int entry
= 0; /* default is 0 */
8676 char linebuf
[BAM_MAXLINE
];
8677 line_t
*lp
= mp
->curdefault
;
8678 const char *fcn
= "save_default_entry()";
8681 lineNum
= mp
->end
->lineNum
;
8682 entryNum
= mp
->end
->entryNum
;
8684 lineNum
= LINE_INIT
;
8685 entryNum
= ENTRY_INIT
;
8689 entry
= s_strtol(lp
->arg
);
8691 (void) snprintf(linebuf
, sizeof (linebuf
), "#%s%d", which
, entry
);
8692 BAM_DPRINTF(("%s: saving default to: %s\n", fcn
, linebuf
));
8693 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
8694 BAM_DPRINTF(("%s: saved default to lineNum=%d, entryNum=%d\n", fcn
,
8695 lineNum
, entryNum
));
8699 restore_default_entry(menu_t
*mp
, const char *which
, line_t
*lp
)
8703 const char *fcn
= "restore_default_entry()";
8706 BAM_DPRINTF(("%s: NULL saved default\n", fcn
));
8707 return; /* nothing to restore */
8710 BAM_DPRINTF(("%s: saved default string: %s\n", fcn
, which
));
8712 str
= lp
->arg
+ strlen(which
);
8713 entry
= s_strtol(str
);
8714 (void) set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
8716 BAM_DPRINTF(("%s: restored default to entryNum: %d\n", fcn
, entry
));
8718 /* delete saved old default line */
8719 unlink_line(mp
, lp
);
8724 * This function is for supporting reboot with args.
8725 * The opt value can be:
8726 * NULL delete temp entry, if present
8727 * entry=<n> switches default entry to <n>
8728 * else treated as boot-args and setup a temperary menu entry
8729 * and make it the default
8730 * Note that we are always rebooting the current OS instance
8731 * so osroot == / always.
8733 #define REBOOT_TITLE "Solaris_reboot_transient"
8737 update_temp(menu_t
*mp
, char *dummy
, char *opt
)
8745 char kernbuf
[BUFSIZ
];
8746 char args_buf
[BUFSIZ
];
8747 char signbuf
[PATH_MAX
];
8749 const char *fcn
= "update_temp()";
8752 assert(dummy
== NULL
);
8754 /* opt can be NULL */
8755 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, opt
? opt
: "<NULL>"));
8756 BAM_DPRINTF(("%s: bam_alt_root: %d, bam_root: %s\n", fcn
,
8757 bam_alt_root
, bam_root
));
8759 if (bam_alt_root
|| bam_rootlen
!= 1 ||
8760 strcmp(bam_root
, "/") != 0 ||
8761 strcmp(rootbuf
, "/") != 0) {
8762 bam_error(_("an alternate root (%s) cannot be used with this "
8763 "sub-command\n"), bam_root
);
8767 /* If no option, delete exiting reboot menu entry */
8770 BAM_DPRINTF(("%s: opt is NULL\n", fcn
));
8771 ent
= find_boot_entry(mp
, REBOOT_TITLE
, NULL
, NULL
,
8772 NULL
, NULL
, 0, &entry
);
8773 if (ent
== NULL
) { /* not found is ok */
8774 BAM_DPRINTF(("%s: transient entry not found\n", fcn
));
8775 return (BAM_SUCCESS
);
8777 (void) delete_boot_entry(mp
, entry
, DBE_PRINTERR
);
8778 restore_default_entry(mp
, BAM_OLDDEF
, mp
->olddefault
);
8779 mp
->olddefault
= NULL
;
8780 BAM_DPRINTF(("%s: restored old default\n", fcn
));
8781 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8785 /* if entry= is specified, set the default entry */
8786 if (strncmp(opt
, "entry=", strlen("entry=")) == 0) {
8787 int entryNum
= s_strtol(opt
+ strlen("entry="));
8788 BAM_DPRINTF(("%s: opt has entry=: %s\n", fcn
, opt
));
8789 if (selector(mp
, opt
, &entry
, NULL
) == BAM_SUCCESS
) {
8790 /* this is entry=# option */
8791 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
8792 BAM_DPRINTF(("%s: default set to %d, "
8793 "set_default ret=%d\n", fcn
, entry
, ret
));
8796 bam_error(_("failed to set GRUB menu default to %d\n"),
8803 * add a new menu entry based on opt and make it the default
8806 fstype
= get_fstype("/");
8807 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype
= NULL
);
8808 if (fstype
== NULL
) {
8809 bam_error(_("failed to determine filesystem type for \"/\". "
8810 "Reboot with \narguments failed.\n"));
8814 osdev
= get_special("/");
8815 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev
= NULL
);
8816 if (osdev
== NULL
) {
8818 bam_error(_("failed to find device special file for \"/\". "
8819 "Reboot with \narguments failed.\n"));
8823 sign
= find_existing_sign("/", osdev
, fstype
);
8824 INJECT_ERROR1("REBOOT_SIGN_NULL", sign
= NULL
);
8828 bam_error(_("failed to find boot signature. Reboot with "
8829 "arguments failed.\n"));
8834 (void) strlcpy(signbuf
, sign
, sizeof (signbuf
));
8837 assert(strchr(signbuf
, '(') == NULL
&& strchr(signbuf
, ',') == NULL
&&
8838 strchr(signbuf
, ')') == NULL
);
8841 * There is no alternate root while doing reboot with args
8842 * This version of bootadm is only delivered with a DBOOT
8843 * version of Solaris.
8845 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct
= BAM_DIRECT_MULTIBOOT
);
8846 if (bam_direct
!= BAM_DIRECT_DBOOT
) {
8848 bam_error(_("the root filesystem is not a dboot Solaris "
8849 "instance. \nThis version of bootadm is not supported "
8850 "on this version of Solaris.\n"));
8854 /* add an entry for Solaris reboot */
8855 if (opt
[0] == '-') {
8856 /* It's an option - first see if boot-file is set */
8857 ret
= get_kernel(mp
, KERNEL_CMD
, kernbuf
, sizeof (kernbuf
));
8858 INJECT_ERROR1("REBOOT_GET_KERNEL", ret
= BAM_ERROR
);
8859 if (ret
!= BAM_SUCCESS
) {
8861 bam_error(_("reboot with arguments: error querying "
8862 "current boot-file settings\n"));
8865 if (kernbuf
[0] == '\0')
8866 (void) strlcpy(kernbuf
, DIRECT_BOOT_KERNEL
,
8869 * If this is a zfs file system and kernbuf does not
8870 * have "-B $ZFS-BOOTFS" string yet, add it.
8872 if (strcmp(fstype
, "zfs") == 0 && !strstr(kernbuf
, ZFS_BOOT
)) {
8873 (void) strlcat(kernbuf
, " ", sizeof (kernbuf
));
8874 (void) strlcat(kernbuf
, ZFS_BOOT
, sizeof (kernbuf
));
8876 (void) strlcat(kernbuf
, " ", sizeof (kernbuf
));
8877 (void) strlcat(kernbuf
, opt
, sizeof (kernbuf
));
8878 BAM_DPRINTF(("%s: reboot with args, option specified: "
8879 "kern=%s\n", fcn
, kernbuf
));
8880 } else if (opt
[0] == '/') {
8881 /* It's a full path, so write it out. */
8882 (void) strlcpy(kernbuf
, opt
, sizeof (kernbuf
));
8887 * # eeprom boot-args='-kd'
8888 * # reboot /platform/i86pc/kernel/unix
8890 * we want to use the boot-args as part of the boot
8891 * line. On the other hand, if someone runs:
8893 * # reboot "/platform/i86pc/kernel/unix -kd"
8895 * we don't need to mess with boot-args. If there's
8896 * no space in the options string, assume we're in the
8899 if (strchr(opt
, ' ') == NULL
) {
8900 ret
= get_kernel(mp
, ARGS_CMD
, args_buf
,
8902 INJECT_ERROR1("REBOOT_GET_ARGS", ret
= BAM_ERROR
);
8903 if (ret
!= BAM_SUCCESS
) {
8905 bam_error(_("reboot with arguments: error "
8906 "querying current boot-args settings\n"));
8910 if (args_buf
[0] != '\0') {
8911 (void) strlcat(kernbuf
, " ", sizeof (kernbuf
));
8912 (void) strlcat(kernbuf
, args_buf
,
8916 BAM_DPRINTF(("%s: reboot with args, abspath specified: "
8917 "kern=%s\n", fcn
, kernbuf
));
8920 * It may be a partial path, or it may be a partial
8921 * path followed by options. Assume that only options
8922 * follow a space. If someone sends us a kernel path
8923 * that includes a space, they deserve to be broken.
8925 opt_ptr
= strchr(opt
, ' ');
8926 if (opt_ptr
!= NULL
) {
8930 path
= expand_path(opt
);
8932 (void) strlcpy(kernbuf
, path
, sizeof (kernbuf
));
8936 * If there were options given, use those.
8937 * Otherwise, copy over the default options.
8939 if (opt_ptr
!= NULL
) {
8940 /* Restore the space in opt string */
8942 (void) strlcat(kernbuf
, opt_ptr
,
8945 ret
= get_kernel(mp
, ARGS_CMD
, args_buf
,
8947 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8949 if (ret
!= BAM_SUCCESS
) {
8951 bam_error(_("reboot with arguments: "
8952 "error querying current boot-args "
8957 if (args_buf
[0] != '\0') {
8958 (void) strlcat(kernbuf
, " ",
8960 (void) strlcat(kernbuf
,
8961 args_buf
, sizeof (kernbuf
));
8964 BAM_DPRINTF(("%s: resolved partial path: %s\n",
8968 bam_error(_("unable to expand %s to a full file"
8970 bam_print_stderr(_("Rebooting with default kernel "
8976 entry
= add_boot_entry(mp
, REBOOT_TITLE
, signbuf
, kernbuf
,
8978 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry
= BAM_ERROR
);
8979 if (entry
== BAM_ERROR
) {
8980 bam_error(_("Cannot update menu. Cannot reboot with "
8981 "requested arguments\n"));
8985 save_default_entry(mp
, BAM_OLDDEF
);
8986 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
8987 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret
= BAM_ERROR
);
8988 if (ret
== BAM_ERROR
) {
8989 bam_error(_("reboot with arguments: setting GRUB menu default "
8990 "to %d failed\n"), entry
);
8992 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8997 set_global(menu_t
*mp
, char *globalcmd
, int val
)
9004 char prefix
[BAM_MAXLINE
];
9006 const char *fcn
= "set_global()";
9011 if (strcmp(globalcmd
, menu_cmds
[DEFAULT_CMD
]) == 0) {
9012 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val
= -1);
9013 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp
->end
= NULL
);
9014 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val
= 100);
9015 if (val
< 0 || mp
->end
== NULL
|| val
> mp
->end
->entryNum
) {
9016 (void) snprintf(prefix
, sizeof (prefix
), "%d", val
);
9017 bam_error(_("invalid boot entry number: %s\n"), prefix
);
9022 found
= last
= NULL
;
9023 for (lp
= mp
->start
; lp
; lp
= lp
->next
) {
9024 if (lp
->flags
!= BAM_GLOBAL
)
9027 last
= lp
; /* track the last global found */
9029 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp
->cmd
= NULL
);
9030 if (lp
->cmd
== NULL
) {
9031 bam_error(_("no command at line %d\n"), lp
->lineNum
);
9034 if (strcmp(globalcmd
, lp
->cmd
) != 0)
9037 BAM_DPRINTF(("%s: found matching global command: %s\n",
9041 bam_error(_("duplicate command %s at line %d of "
9042 "%sboot/grub/menu.lst\n"), globalcmd
,
9043 lp
->lineNum
, bam_root
);
9048 if (found
== NULL
) {
9049 lp
= s_calloc(1, sizeof (line_t
));
9051 lp
->next
= mp
->start
;
9053 mp
->end
= (mp
->end
) ? mp
->end
: lp
;
9055 lp
->next
= last
->next
;
9057 if (lp
->next
== NULL
)
9060 lp
->flags
= BAM_GLOBAL
; /* other fields not needed for writes */
9061 len
= strlen(globalcmd
) + strlen(menu_cmds
[SEP_CMD
]);
9062 len
+= 10; /* val < 10 digits */
9063 lp
->line
= s_calloc(1, len
);
9064 (void) snprintf(lp
->line
, len
, "%s%s%d",
9065 globalcmd
, menu_cmds
[SEP_CMD
], val
);
9066 BAM_DPRINTF(("%s: wrote new global line: %s\n", fcn
, lp
->line
));
9067 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9072 * We are changing an existing entry. Retain any prefix whitespace,
9073 * but overwrite everything else. This preserves tabs added for
9078 while (*str
== ' ' || *str
== '\t')
9080 *cp
= '\0'; /* Terminate prefix */
9081 len
= strlen(prefix
) + strlen(globalcmd
);
9082 len
+= strlen(menu_cmds
[SEP_CMD
]) + 10;
9085 found
->line
= s_calloc(1, len
);
9086 (void) snprintf(found
->line
, len
,
9087 "%s%s%s%d", prefix
, globalcmd
, menu_cmds
[SEP_CMD
], val
);
9089 BAM_DPRINTF(("%s: replaced global line with: %s\n", fcn
, found
->line
));
9090 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9091 return (BAM_WRITE
); /* need a write to menu */
9095 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
9096 * expand it to a full unix path. The calling function is expected to
9097 * output a message if an error occurs and NULL is returned.
9100 expand_path(const char *partial_path
)
9104 char new_path2
[PATH_MAX
];
9106 const char *fcn
= "expand_path()";
9108 new_path_len
= strlen(partial_path
) + 64;
9109 new_path
= s_calloc(1, new_path_len
);
9111 /* First, try the simplest case - something like "kernel/unix" */
9112 (void) snprintf(new_path
, new_path_len
, "/platform/i86pc/%s",
9114 if (stat(new_path
, &sb
) == 0) {
9115 BAM_DPRINTF(("%s: expanded path: %s\n", fcn
, new_path
));
9119 if (strcmp(partial_path
, "kmdb") == 0) {
9120 (void) snprintf(new_path
, new_path_len
, "%s -k",
9121 DIRECT_BOOT_KERNEL
);
9122 BAM_DPRINTF(("%s: expanded path: %s\n", fcn
, new_path
));
9127 * We've quickly reached unsupported usage. Try once more to
9128 * see if we were just given a glom name.
9130 (void) snprintf(new_path
, new_path_len
, "/platform/i86pc/%s/unix",
9132 (void) snprintf(new_path2
, PATH_MAX
, "/platform/i86pc/%s/amd64/unix",
9134 if (stat(new_path
, &sb
) == 0) {
9135 if (stat(new_path2
, &sb
) == 0) {
9137 * We matched both, so we actually
9138 * want to write the $ISADIR version.
9140 (void) snprintf(new_path
, new_path_len
,
9141 "/platform/i86pc/kernel/%s/$ISADIR/unix",
9144 BAM_DPRINTF(("%s: expanded path: %s\n", fcn
, new_path
));
9149 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9154 * The kernel cmd and arg have been changed, so
9155 * check whether the archive line needs to change.
9158 set_archive_line(entry_t
*entryp
, line_t
*kernelp
)
9160 line_t
*lp
= entryp
->start
;
9163 const char *fcn
= "set_archive_line()";
9165 for (; lp
!= NULL
; lp
= lp
->next
) {
9166 if (lp
->cmd
!= NULL
&& strncmp(lp
->cmd
, menu_cmds
[MODULE_CMD
],
9167 sizeof (menu_cmds
[MODULE_CMD
]) - 1) == 0) {
9171 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp
= entryp
->end
);
9172 if (lp
== entryp
->end
) {
9173 BAM_DPRINTF(("%s: no module/archive line for entry: "
9174 "%d\n", fcn
, entryp
->entryNum
));
9178 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp
= NULL
);
9180 BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9181 fcn
, entryp
->entryNum
));
9185 if (strstr(kernelp
->arg
, "$ISADIR") != NULL
) {
9186 new_archive
= DIRECT_BOOT_ARCHIVE
;
9187 m_cmd
= MODULE_DOLLAR_CMD
;
9188 } else if (strstr(kernelp
->arg
, "amd64") != NULL
) {
9189 new_archive
= DIRECT_BOOT_ARCHIVE_64
;
9192 new_archive
= DIRECT_BOOT_ARCHIVE_32
;
9196 if (strcmp(lp
->arg
, new_archive
) == 0) {
9197 BAM_DPRINTF(("%s: no change for line: %s\n", fcn
, lp
->arg
));
9201 if (lp
->cmd
!= NULL
&& strcmp(lp
->cmd
, menu_cmds
[m_cmd
]) != 0) {
9203 lp
->cmd
= s_strdup(menu_cmds
[m_cmd
]);
9207 lp
->arg
= s_strdup(new_archive
);
9209 BAM_DPRINTF(("%s: replaced for line: %s\n", fcn
, lp
->line
));
9213 * Title for an entry to set properties that once went in bootenv.rc.
9215 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
9218 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9219 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
9220 * string, reset the value to the default. If path is a non-zero-length
9221 * string, set the kernel or arguments.
9232 int rv
= BAM_SUCCESS
;
9233 int free_new_path
= 0;
9242 size_t old_kernel_len
= 0;
9247 char signbuf
[PATH_MAX
];
9249 const char *fcn
= "get_set_kernel()";
9251 assert(bufsize
> 0);
9253 ptr
= kernelp
= NULL
;
9254 new_arg
= old_args
= space
= NULL
;
9258 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9259 bam_direct
= BAM_DIRECT_MULTIBOOT
);
9260 if (bam_direct
!= BAM_DIRECT_DBOOT
) {
9261 bam_error(_("bootadm set-menu %s may only be run on "
9262 "directboot kernels.\n"),
9263 optnum
== KERNEL_CMD
? "kernel" : "args");
9268 * If a user changed the default entry to a non-bootadm controlled
9269 * one, we don't want to mess with it. Just print an error and
9272 if (mp
->curdefault
) {
9273 entryNum
= s_strtol(mp
->curdefault
->arg
);
9274 for (entryp
= mp
->entries
; entryp
; entryp
= entryp
->next
) {
9275 if (entryp
->entryNum
== entryNum
)
9278 if ((entryp
!= NULL
) &&
9279 ((entryp
->flags
& (BAM_ENTRY_BOOTADM
|BAM_ENTRY_LU
)) == 0)) {
9280 bam_error(_("Default /boot/grub/menu.lst entry is not "
9281 "controlled by bootadm. Exiting\n"));
9286 entryp
= find_boot_entry(mp
, BOOTENV_RC_TITLE
, NULL
, NULL
, NULL
, NULL
,
9289 if (entryp
!= NULL
) {
9290 for (ptr
= entryp
->start
; ptr
&& ptr
!= entryp
->end
;
9292 if (strncmp(ptr
->cmd
, menu_cmds
[KERNEL_CMD
],
9293 sizeof (menu_cmds
[KERNEL_CMD
]) - 1) == 0) {
9298 if (kernelp
== NULL
) {
9299 bam_error(_("no kernel line found in entry %d\n"),
9304 old_kernel_len
= strcspn(kernelp
->arg
, " \t");
9305 space
= old_args
= kernelp
->arg
+ old_kernel_len
;
9306 while ((*old_args
== ' ') || (*old_args
== '\t'))
9311 if (entryp
== NULL
) {
9312 BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9314 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9315 return (BAM_SUCCESS
);
9318 if (optnum
== ARGS_CMD
) {
9319 if (old_args
[0] != '\0') {
9320 (void) strlcpy(buf
, old_args
, bufsize
);
9321 BAM_DPRINTF(("%s: read menu boot-args: %s\n",
9326 * We need to print the kernel, so we just turn the
9327 * first space into a '\0' and print the beginning.
9328 * We don't print anything if it's the default kernel.
9332 if (strcmp(kernelp
->arg
, DIRECT_BOOT_KERNEL
) != 0) {
9333 (void) strlcpy(buf
, kernelp
->arg
, bufsize
);
9334 BAM_DPRINTF(("%s: read menu boot-file: %s\n",
9339 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9340 return (BAM_SUCCESS
);
9344 * First, check if we're resetting an entry to the default.
9346 if ((path
[0] == '\0') ||
9347 ((optnum
== KERNEL_CMD
) &&
9348 (strcmp(path
, DIRECT_BOOT_KERNEL
) == 0))) {
9349 if ((entryp
== NULL
) || (kernelp
== NULL
)) {
9350 /* No previous entry, it's already the default */
9351 BAM_DPRINTF(("%s: no reset, already has default\n",
9353 return (BAM_SUCCESS
);
9357 * Check if we can delete the entry. If we're resetting the
9358 * kernel command, and the args is already empty, or if we're
9359 * resetting the args command, and the kernel is already the
9360 * default, we can restore the old default and delete the entry.
9362 if (((optnum
== KERNEL_CMD
) &&
9363 ((old_args
== NULL
) || (old_args
[0] == '\0'))) ||
9364 ((optnum
== ARGS_CMD
) &&
9365 (strncmp(kernelp
->arg
, DIRECT_BOOT_KERNEL
,
9366 sizeof (DIRECT_BOOT_KERNEL
) - 1) == 0))) {
9368 (void) delete_boot_entry(mp
, entryNum
, DBE_PRINTERR
);
9369 restore_default_entry(mp
, BAM_OLD_RC_DEF
,
9370 mp
->old_rc_default
);
9371 mp
->old_rc_default
= NULL
;
9373 BAM_DPRINTF(("%s: resetting to default\n", fcn
));
9377 if (optnum
== KERNEL_CMD
) {
9379 * At this point, we've already checked that old_args
9380 * and entryp are valid pointers. The "+ 2" is for
9381 * a space a the string termination character.
9383 new_str_len
= (sizeof (DIRECT_BOOT_KERNEL
) - 1) +
9384 strlen(old_args
) + 2;
9385 new_arg
= s_calloc(1, new_str_len
);
9386 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9387 DIRECT_BOOT_KERNEL
, old_args
);
9389 kernelp
->arg
= new_arg
;
9392 * We have changed the kernel line, so we may need
9393 * to update the archive line as well.
9395 set_archive_line(entryp
, kernelp
);
9396 BAM_DPRINTF(("%s: reset kernel to default, but "
9397 "retained old args: %s\n", fcn
, kernelp
->arg
));
9400 * We're resetting the boot args to nothing, so
9401 * we only need to copy the kernel. We've already
9402 * checked that the kernel is not the default.
9404 new_arg
= s_calloc(1, old_kernel_len
+ 1);
9405 (void) snprintf(new_arg
, old_kernel_len
+ 1, "%s",
9408 kernelp
->arg
= new_arg
;
9409 BAM_DPRINTF(("%s: reset args to default, but retained "
9410 "old kernel: %s\n", fcn
, kernelp
->arg
));
9417 * Expand the kernel file to a full path, if necessary
9419 if ((optnum
== KERNEL_CMD
) && (path
[0] != '/')) {
9420 new_path
= expand_path(path
);
9421 if (new_path
== NULL
) {
9422 bam_error(_("unable to expand %s to a full file "
9424 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9434 * At this point, we know we're setting a new value. First, take care
9435 * of the case where there was no previous entry.
9437 if (entryp
== NULL
) {
9439 /* Similar to code in update_temp */
9440 fstype
= get_fstype("/");
9441 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype
= NULL
);
9442 if (fstype
== NULL
) {
9443 bam_error(_("cannot determine filesystem type for "
9444 "\"/\".\nCannot generate GRUB menu entry with "
9445 "EEPROM arguments.\n"));
9450 osdev
= get_special("/");
9451 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev
= NULL
);
9452 if (osdev
== NULL
) {
9454 bam_error(_("cannot determine device special file for "
9455 "\"/\".\nCannot generate GRUB menu entry with "
9456 "EEPROM arguments.\n"));
9461 sign
= find_existing_sign("/", osdev
, fstype
);
9462 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign
= NULL
);
9466 bam_error(_("cannot determine boot signature for "
9467 "\"/\".\nCannot generate GRUB menu entry with "
9468 "EEPROM arguments.\n"));
9474 (void) strlcpy(signbuf
, sign
, sizeof (signbuf
));
9476 assert(strchr(signbuf
, '(') == NULL
&&
9477 strchr(signbuf
, ',') == NULL
&&
9478 strchr(signbuf
, ')') == NULL
);
9480 if (optnum
== KERNEL_CMD
) {
9481 if (strcmp(fstype
, "zfs") == 0) {
9482 new_str_len
= strlen(new_path
) +
9483 strlen(ZFS_BOOT
) + 8;
9484 new_arg
= s_calloc(1, new_str_len
);
9485 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9486 new_path
, ZFS_BOOT
);
9487 BAM_DPRINTF(("%s: new kernel=%s\n", fcn
,
9489 entryNum
= add_boot_entry(mp
, BOOTENV_RC_TITLE
,
9490 signbuf
, new_arg
, NULL
, NULL
, NULL
);
9493 BAM_DPRINTF(("%s: new kernel=%s\n", fcn
,
9495 entryNum
= add_boot_entry(mp
, BOOTENV_RC_TITLE
,
9496 signbuf
, new_path
, NULL
, NULL
, NULL
);
9499 new_str_len
= strlen(path
) + 8;
9500 if (strcmp(fstype
, "zfs") == 0) {
9501 new_str_len
+= strlen(DIRECT_BOOT_KERNEL_ZFS
);
9502 new_arg
= s_calloc(1, new_str_len
);
9503 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9504 DIRECT_BOOT_KERNEL_ZFS
, path
);
9506 new_str_len
+= strlen(DIRECT_BOOT_KERNEL
);
9507 new_arg
= s_calloc(1, new_str_len
);
9508 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9509 DIRECT_BOOT_KERNEL
, path
);
9512 BAM_DPRINTF(("%s: new args=%s\n", fcn
, new_arg
));
9513 entryNum
= add_boot_entry(mp
, BOOTENV_RC_TITLE
,
9514 signbuf
, new_arg
, NULL
, DIRECT_BOOT_ARCHIVE
, NULL
);
9518 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9519 entryNum
= BAM_ERROR
);
9520 if (entryNum
== BAM_ERROR
) {
9521 bam_error(_("failed to add boot entry: %s\n"),
9526 save_default_entry(mp
, BAM_OLD_RC_DEF
);
9527 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entryNum
);
9528 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret
= BAM_ERROR
);
9529 if (ret
== BAM_ERROR
) {
9530 bam_error(_("failed to set default to: %d\n"),
9538 * There was already an bootenv entry which we need to edit.
9540 if (optnum
== KERNEL_CMD
) {
9541 new_str_len
= strlen(new_path
) + strlen(old_args
) + 2;
9542 new_arg
= s_calloc(1, new_str_len
);
9543 (void) snprintf(new_arg
, new_str_len
, "%s %s", new_path
,
9546 kernelp
->arg
= new_arg
;
9549 * If we have changed the kernel line, we may need to update
9550 * the archive line as well.
9552 set_archive_line(entryp
, kernelp
);
9553 BAM_DPRINTF(("%s: rc line exists, replaced kernel, same "
9554 "args: %s\n", fcn
, kernelp
->arg
));
9556 new_str_len
= old_kernel_len
+ strlen(path
) + 8;
9557 new_arg
= s_calloc(1, new_str_len
);
9558 (void) strncpy(new_arg
, kernelp
->arg
, old_kernel_len
);
9559 (void) strlcat(new_arg
, " ", new_str_len
);
9560 (void) strlcat(new_arg
, path
, new_str_len
);
9562 kernelp
->arg
= new_arg
;
9563 BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9564 "args: %s\n", fcn
, kernelp
->arg
));
9569 if ((rv
== BAM_WRITE
) && kernelp
)
9570 update_line(kernelp
);
9573 if (rv
== BAM_WRITE
) {
9574 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9576 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9582 get_kernel(menu_t
*mp
, menu_cmd_t optnum
, char *buf
, size_t bufsize
)
9584 const char *fcn
= "get_kernel()";
9585 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, menu_cmds
[optnum
]));
9586 return (get_set_kernel(mp
, optnum
, NULL
, buf
, bufsize
));
9590 set_kernel(menu_t
*mp
, menu_cmd_t optnum
, char *path
, char *buf
, size_t bufsize
)
9592 const char *fcn
= "set_kernel()";
9593 assert(path
!= NULL
);
9594 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
,
9595 menu_cmds
[optnum
], path
));
9596 return (get_set_kernel(mp
, optnum
, path
, buf
, bufsize
));
9601 set_option(menu_t
*mp
, char *dummy
, char *opt
)
9606 char buf
[BUFSIZ
] = "";
9608 const char *fcn
= "set_option()";
9612 assert(dummy
== NULL
);
9614 /* opt is set from bam_argv[0] and is always non-NULL */
9615 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, opt
));
9617 val
= strchr(opt
, '=');
9622 if (strcmp(opt
, "default") == 0) {
9623 optnum
= DEFAULT_CMD
;
9624 } else if (strcmp(opt
, "timeout") == 0) {
9625 optnum
= TIMEOUT_CMD
;
9626 } else if (strcmp(opt
, menu_cmds
[KERNEL_CMD
]) == 0) {
9627 optnum
= KERNEL_CMD
;
9628 } else if (strcmp(opt
, menu_cmds
[ARGS_CMD
]) == 0) {
9631 bam_error(_("invalid option: %s\n"), opt
);
9636 * kernel and args are allowed without "=new_value" strings. All
9637 * others cause errors
9639 if ((val
== NULL
) && (optnum
!= KERNEL_CMD
) && (optnum
!= ARGS_CMD
)) {
9640 bam_error(_("option has no argument: %s\n"), opt
);
9642 } else if (val
!= NULL
) {
9646 if ((optnum
== KERNEL_CMD
) || (optnum
== ARGS_CMD
)) {
9647 BAM_DPRINTF(("%s: setting %s option to %s\n",
9648 fcn
, menu_cmds
[optnum
], val
? val
+ 1 : "NULL"));
9651 rv
= set_kernel(mp
, optnum
, val
+ 1, buf
, sizeof (buf
));
9653 rv
= get_kernel(mp
, optnum
, buf
, sizeof (buf
));
9654 if ((rv
== BAM_SUCCESS
) && (buf
[0] != '\0'))
9655 (void) printf("%s\n", buf
);
9657 optval
= s_strtol(val
+ 1);
9658 BAM_DPRINTF(("%s: setting %s option to %s\n", fcn
,
9659 menu_cmds
[optnum
], val
+ 1));
9660 rv
= set_global(mp
, menu_cmds
[optnum
], optval
);
9663 if (rv
== BAM_WRITE
|| rv
== BAM_SUCCESS
) {
9664 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9666 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9673 * The quiet argument suppresses messages. This is used
9674 * when invoked in the context of other commands (e.g. list_entry)
9677 read_globals(menu_t
*mp
, char *menu_path
, char *globalcmd
, int quiet
)
9681 int done
, ret
= BAM_SUCCESS
;
9687 if (mp
->start
== NULL
) {
9689 bam_error(_("menu file not found: %s\n"), menu_path
);
9694 for (lp
= mp
->start
; lp
; lp
= lp
->next
) {
9695 if (lp
->flags
!= BAM_GLOBAL
)
9698 if (lp
->cmd
== NULL
) {
9700 bam_error(_("no command at line %d\n"),
9705 if (strcmp(globalcmd
, lp
->cmd
) != 0)
9708 /* Found global. Check for duplicates */
9709 if (done
&& !quiet
) {
9710 bam_error(_("duplicate command %s at line %d of "
9711 "%sboot/grub/menu.lst\n"), globalcmd
,
9712 lp
->lineNum
, bam_root
);
9716 arg
= lp
->arg
? lp
->arg
: "";
9717 bam_print(_("%s %s\n"), globalcmd
, arg
);
9721 if (!done
&& bam_verbose
)
9722 bam_print(_("no %s entry found\n"), globalcmd
);
9728 menu_write(char *root
, menu_t
*mp
)
9730 const char *fcn
= "menu_write()";
9732 BAM_DPRINTF(("%s: entered menu_write() for root: <%s>\n", fcn
, root
));
9733 return (list2file(root
, MENU_TMP
, GRUB_MENU
, mp
->start
));
9737 line_free(line_t
*lp
)
9742 if (lp
->cmd
!= NULL
)
9754 linelist_free(line_t
*start
)
9760 start
= start
->next
;
9766 filelist_free(filelist_t
*flistp
)
9768 linelist_free(flistp
->head
);
9769 flistp
->head
= NULL
;
9770 flistp
->tail
= NULL
;
9774 menu_free(menu_t
*mp
)
9780 linelist_free(mp
->start
);
9797 * Returns 0 on success
9798 * Any other value indicates an error
9801 exec_cmd(char *cmdline
, filelist_t
*flistp
)
9811 * - only absolute paths are allowed
9812 * - set IFS to space and tab
9814 if (*cmdline
!= '/') {
9815 bam_error(_("path is not absolute: %s\n"), cmdline
);
9818 (void) putenv("IFS= \t");
9821 * We may have been exec'ed with SIGCHLD blocked
9824 (void) sigemptyset(&set
);
9825 (void) sigaddset(&set
, SIGCHLD
);
9826 if (sigprocmask(SIG_UNBLOCK
, &set
, NULL
) != 0) {
9827 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno
));
9832 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9834 disp
= sigset(SIGCHLD
, SIG_DFL
);
9835 if (disp
== SIG_ERR
) {
9836 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
9840 if (disp
== SIG_HOLD
) {
9841 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
9846 ptr
= popen(cmdline
, "r");
9848 bam_error(_("popen failed: %s: %s\n"), cmdline
,
9854 * If we simply do a pclose() following a popen(), pclose()
9855 * will close the reader end of the pipe immediately even
9856 * if the child process has not started/exited. pclose()
9857 * does wait for cmd to terminate before returning though.
9858 * When the executed command writes its output to the pipe
9859 * there is no reader process and the command dies with
9860 * SIGPIPE. To avoid this we read repeatedly until read
9861 * terminates with EOF. This indicates that the command
9862 * (writer) has closed the pipe and we can safely do a
9865 * Since pclose() does wait for the command to exit,
9866 * we can safely reap the exit status of the command
9867 * from the value returned by pclose()
9869 while (s_fgets(buf
, sizeof (buf
), ptr
) != NULL
) {
9870 if (flistp
== NULL
) {
9871 /* s_fgets strips newlines, so insert them at the end */
9872 bam_print(_("%s\n"), buf
);
9874 append_to_flist(flistp
, buf
);
9880 bam_error(_("pclose failed: %s: %s\n"), cmdline
,
9885 if (WIFEXITED(ret
)) {
9886 return (WEXITSTATUS(ret
));
9888 bam_error(_("command terminated abnormally: %s: %d\n"),
9895 * Since this function returns -1 on error
9896 * it cannot be used to convert -1. However,
9897 * that is sufficient for what we need.
9910 l
= strtol(str
, &res
, 10);
9911 if (errno
|| *res
!= '\0') {
9919 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9922 s_fputs(char *str
, FILE *fp
)
9924 char linebuf
[BAM_MAXLINE
];
9926 (void) snprintf(linebuf
, sizeof (linebuf
), "%s\n", str
);
9927 return (fputs(linebuf
, fp
));
9931 * Wrapper around fgets, that strips newlines returned by fgets
9934 s_fgets(char *buf
, int buflen
, FILE *fp
)
9938 buf
= fgets(buf
, buflen
, fp
);
9941 if (n
== buflen
- 1 && buf
[n
-1] != '\n')
9942 bam_error(_("the following line is too long "
9943 "(> %d chars)\n\t%s\n"), buflen
- 1, buf
);
9944 buf
[n
-1] = (buf
[n
-1] == '\n') ? '\0' : buf
[n
-1];
9951 s_calloc(size_t nelem
, size_t sz
)
9955 ptr
= calloc(nelem
, sz
);
9957 bam_error(_("could not allocate memory: size = %u\n"),
9965 s_realloc(void *ptr
, size_t sz
)
9967 ptr
= realloc(ptr
, sz
);
9969 bam_error(_("could not allocate memory: size = %u\n"), sz
);
9985 bam_error(_("could not allocate memory: size = %u\n"),
9993 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9994 * Returns 0 otherwise
9999 static int amd64
= -1;
10000 char isabuf
[257]; /* from sysinfo(2) manpage */
10005 if (bam_alt_platform
) {
10006 if (strcmp(bam_platform
, "i86pc") == 0) {
10007 amd64
= 1; /* diskless server */
10010 if (sysinfo(SI_ISALIST
, isabuf
, sizeof (isabuf
)) > 0 &&
10011 strncmp(isabuf
, "amd64 ", strlen("amd64 ")) == 0) {
10013 } else if (strstr(isabuf
, "i386") == NULL
) {
10014 amd64
= 1; /* diskless server */
10026 static int cached
= -1;
10027 static char mbuf
[257]; /* from sysinfo(2) manpage */
10032 if (bam_alt_platform
) {
10033 return (bam_platform
);
10035 if (sysinfo(SI_MACHINE
, mbuf
, sizeof (mbuf
)) > 0) {
10039 if (cached
== -1) {
10050 static int issparc
= -1;
10051 char mbuf
[257]; /* from sysinfo(2) manpage */
10056 if (bam_alt_platform
) {
10057 if (strncmp(bam_platform
, "sun4", 4) == 0) {
10061 if (sysinfo(SI_ARCHITECTURE
, mbuf
, sizeof (mbuf
)) > 0 &&
10062 strcmp(mbuf
, "sparc") == 0) {
10073 append_to_flist(filelist_t
*flistp
, char *s
)
10077 lp
= s_calloc(1, sizeof (line_t
));
10078 lp
->line
= s_strdup(s
);
10079 if (flistp
->head
== NULL
)
10082 flistp
->tail
->next
= lp
;
10092 ucode_install(char *root
)
10096 for (i
= 0; ucode_vendors
[i
].filestr
!= NULL
; i
++) {
10097 int cmd_len
= PATH_MAX
+ 256;
10098 char cmd
[PATH_MAX
+ 256];
10099 char file
[PATH_MAX
];
10100 char timestamp
[PATH_MAX
];
10101 struct stat fstatus
, tstatus
;
10102 struct utimbuf u_times
;
10104 (void) snprintf(file
, PATH_MAX
, "%s/%s/%s-ucode.%s",
10105 bam_root
, UCODE_INSTALL_PATH
, ucode_vendors
[i
].filestr
,
10106 ucode_vendors
[i
].extstr
);
10108 if (stat(file
, &fstatus
) != 0 || !(S_ISREG(fstatus
.st_mode
)))
10111 (void) snprintf(timestamp
, PATH_MAX
, "%s.ts", file
);
10113 if (stat(timestamp
, &tstatus
) == 0 &&
10114 fstatus
.st_mtime
<= tstatus
.st_mtime
)
10117 (void) snprintf(cmd
, cmd_len
, "/usr/sbin/ucodeadm -i -R "
10118 "%s/%s/%s %s > /dev/null 2>&1", bam_root
,
10119 UCODE_INSTALL_PATH
, ucode_vendors
[i
].vendorstr
, file
);
10120 if (system(cmd
) != 0)
10123 if (creat(timestamp
, S_IRUSR
| S_IWUSR
) == -1)
10126 u_times
.actime
= fstatus
.st_atime
;
10127 u_times
.modtime
= fstatus
.st_mtime
;
10128 (void) utime(timestamp
, &u_times
);