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 2017 Nexenta Systems, Inc.
28 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
29 * Copyright 2023 Oxide Computer Company
33 * bootadm(8) is a new utility for managing bootability of
34 * Solaris *Newboot* environments. It has two primary tasks:
35 * - Allow end users to manage bootability of Newboot Solaris instances
36 * - Provide services to other subsystems in Solaris (primarily Install)
45 #include <sys/types.h>
52 #include <sys/mnttab.h>
53 #include <sys/mntent.h>
54 #include <sys/statvfs.h>
55 #include <libnvpair.h>
60 #include <sys/systeminfo.h>
61 #include <sys/dktp/fdisk.h>
62 #include <sys/param.h>
66 #include <sys/sysmacros.h>
70 #include <sys/lockfs.h>
71 #include <sys/filio.h>
80 #include <device_info.h>
82 #include <sys/efi_partition.h>
85 #include <sys/mkdev.h>
90 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
91 #endif /* TEXT_DOMAIN */
93 /* Type definitions */
102 #define LINE_INIT 0 /* lineNum initial value */
103 #define ENTRY_INIT -1 /* entryNum initial value */
104 #define ALL_ENTRIES -2 /* selects all boot entries */
106 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
107 #define PARTNO_EFI -2 /* EFI partition table found */
109 #define GRUB_DIR "/boot/grub"
110 #define GRUB_STAGE2 GRUB_DIR "/stage2"
111 #define GRUB_MENU "/boot/grub/menu.lst"
112 #define MENU_TMP "/boot/grub/menu.lst.tmp"
113 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
114 #define RAMDISK_SPECIAL "/devices/ramdisk"
115 #define STUBBOOT "/stubboot"
116 #define MULTIBOOT "/platform/i86pc/multiboot"
117 #define GRUBSIGN_DIR "/boot/grub/bootsign"
118 #define GRUBSIGN_BACKUP "/etc/bootsign"
119 #define GRUBSIGN_UFS_PREFIX "rootfs"
120 #define GRUBSIGN_ZFS_PREFIX "pool_"
121 #define GRUBSIGN_LU_PREFIX "BE_"
122 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
123 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
126 #define BOOT_ARCHIVE_FMRI "system/boot-archive:default"
127 #define SCF_PG_CONFIG "config"
128 #define SCF_PROPERTY_FORMAT "format"
131 #define BE_DEFAULTS "/etc/default/be"
132 #define BE_DFLT_BE_HAS_GRUB "BE_HAS_GRUB="
134 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
137 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
138 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
140 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
141 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
142 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
143 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
145 #define GRUB_slice "/etc/lu/GRUB_slice"
146 #define GRUB_root "/etc/lu/GRUB_root"
147 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
148 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
149 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
150 #define LULIB "/usr/lib/lu/lulib"
151 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
152 #define CKSUM "/usr/bin/cksum"
153 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
154 #define BOOTADM "/sbin/bootadm"
156 #define INSTALLGRUB "/sbin/installgrub"
157 #define STAGE1 "/boot/grub/stage1"
158 #define STAGE2 "/boot/grub/stage2"
160 #define ETC_SYSTEM_DIR "etc/system.d"
161 #define SELF_ASSEMBLY "etc/system.d/.self-assembly"
164 * Default file attributes
166 #define DEFAULT_DEV_MODE 0644 /* default permissions */
167 #define DEFAULT_DEV_UID 0 /* user root */
168 #define DEFAULT_DEV_GID 3 /* group sys */
172 * menu_cmd_t and menu_cmds must be kept in sync
174 char *menu_cmds
[] = {
175 "default", /* DEFAULT_CMD */
176 "timeout", /* TIMEOUT_CMD */
177 "title", /* TITLE_CMD */
178 "root", /* ROOT_CMD */
179 "kernel", /* KERNEL_CMD */
180 "kernel$", /* KERNEL_DOLLAR_CMD */
181 "module", /* MODULE_CMD */
182 "module$", /* MODULE_DOLLAR_CMD */
184 "#", /* COMMENT_CMD */
185 "chainloader", /* CHAINLOADER_CMD */
186 "args", /* ARGS_CMD */
187 "findroot", /* FINDROOT_CMD */
188 "bootfs", /* BOOTFS_CMD */
192 char *bam_formats
[] = {
199 #define BAM_FORMAT_UNSET -1
200 #define BAM_FORMAT_HSFS 0
201 short bam_format
= BAM_FORMAT_UNSET
;
203 #define OPT_ENTRY_NUM "entry"
213 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
214 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
216 #define FILE_STAT "boot/solaris/filestat.ramdisk"
217 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
218 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
219 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
221 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
229 static subcmd_t bam_cmd
;
232 static int bam_root_readonly
;
234 static int bam_extend
= 0;
235 static int bam_purge
= 0;
236 static char *bam_subcmd
;
237 static char *bam_opt
;
238 static char **bam_argv
;
239 static char *bam_pool
;
241 static int bam_check
;
242 static int bam_saved_check
;
243 static int bam_smf_check
;
244 static int bam_lock_fd
= -1;
247 char rootbuf
[PATH_MAX
] = "/";
248 static char self_assembly
[PATH_MAX
];
249 static int bam_update_all
;
250 static int bam_alt_platform
;
251 static char *bam_platform
;
252 static char *bam_home_env
= NULL
;
254 /* function prototypes */
255 static void parse_args_internal(int, char *[]);
256 static void parse_args(int, char *argv
[]);
257 static error_t
bam_menu(char *, char *, int, char *[]);
258 static error_t
bam_install(char *, char *);
259 static error_t
bam_archive(char *, char *);
261 static void bam_lock(void);
262 static void bam_unlock(void);
264 static int exec_cmd(char *, filelist_t
*);
265 static error_t
read_globals(menu_t
*, char *, char *, int);
266 static int menu_on_bootdisk(char *os_root
, char *menu_root
);
267 static menu_t
*menu_read(char *);
268 static error_t
menu_write(char *, menu_t
*);
269 static void linelist_free(line_t
*);
270 static void menu_free(menu_t
*);
271 static void filelist_free(filelist_t
*);
272 static error_t
list2file(char *, char *, char *, line_t
*);
273 static error_t
list_entry(menu_t
*, char *, char *);
274 static error_t
list_setting(menu_t
*, char *, char *);
275 static error_t
delete_all_entries(menu_t
*, char *, char *);
276 static error_t
update_entry(menu_t
*mp
, char *menu_root
, char *opt
);
277 static error_t
update_temp(menu_t
*mp
, char *dummy
, char *opt
);
279 static error_t
install_bootloader(void);
280 static error_t
update_archive(char *, char *);
281 static error_t
list_archive(char *, char *);
282 static error_t
update_all(char *, char *);
283 static error_t
read_list(char *, filelist_t
*);
284 static error_t
set_option(menu_t
*, char *, char *);
285 static error_t
set_kernel(menu_t
*, menu_cmd_t
, char *, char *, size_t);
286 static error_t
get_kernel(menu_t
*, menu_cmd_t
, char *, size_t);
287 static error_t
build_etc_system_dir(char *);
288 static char *expand_path(const char *);
290 static long s_strtol(char *);
291 static int s_fputs(char *, FILE *);
293 static int is_amd64(void);
294 static char *get_machine(void);
295 static void append_to_flist(filelist_t
*, char *);
296 static int ufs_add_to_sign_list(char *sign
);
297 static error_t
synchronize_BE_menu(void);
299 /* Menu related sub commands */
300 static subcmd_defn_t menu_subcmds
[] = {
301 "set_option", OPT_ABSENT
, set_option
, 0, /* PUB */
302 "list_entry", OPT_OPTIONAL
, list_entry
, 1, /* PUB */
303 "delete_all_entries", OPT_ABSENT
, delete_all_entries
, 0, /* PVT */
304 "update_entry", OPT_REQ
, update_entry
, 0, /* menu */
305 "update_temp", OPT_OPTIONAL
, update_temp
, 0, /* reboot */
306 "upgrade", OPT_ABSENT
, upgrade_menu
, 0, /* menu */
307 "list_setting", OPT_OPTIONAL
, list_setting
, 1, /* menu */
308 "disable_hypervisor", OPT_ABSENT
, cvt_to_metal
, 0, /* menu */
309 "enable_hypervisor", OPT_ABSENT
, cvt_to_hyper
, 0, /* menu */
310 NULL
, 0, NULL
, 0 /* must be last */
313 /* Archive related sub commands */
314 static subcmd_defn_t arch_subcmds
[] = {
315 "update", OPT_ABSENT
, update_archive
, 0, /* PUB */
316 "update_all", OPT_ABSENT
, update_all
, 0, /* PVT */
317 "list", OPT_OPTIONAL
, list_archive
, 1, /* PUB */
318 NULL
, 0, NULL
, 0 /* must be last */
321 /* Install related sub commands */
322 static subcmd_defn_t inst_subcmds
[] = {
323 "install_bootloader", OPT_ABSENT
, install_bootloader
, 0, /* PUB */
324 NULL
, 0, NULL
, 0 /* must be last */
327 #define build_path(buf, len, root, prefix, suffix) \
328 snprintf((buf), (len), "%s%s%s%s%s", (root), (prefix), get_machine(), \
329 is_flag_on(IS_SPARC_TARGET) ? "" : "/amd64", (suffix))
332 * Directory specific flags:
333 * NEED_UPDATE : the specified archive needs to be updated
334 * NO_EXTEND : don't extend the specified archive, but recreate it
336 #define NEED_UPDATE 0x00000001
337 #define NO_EXTEND 0x00000002
339 #define set_dir_flag(f) (walk_arg.dirinfo.flags |= (f))
340 #define unset_dir_flag(f) (walk_arg.dirinfo.flags &= ~(f))
341 #define is_dir_flag_on(f) (walk_arg.dirinfo.flags & (f) ? 1 : 0)
343 #define get_cachedir() (walk_arg.dirinfo.cdir_path)
344 #define get_updatedir() (walk_arg.dirinfo.update_path)
345 #define get_count() (walk_arg.dirinfo.count)
346 #define has_cachedir() (walk_arg.dirinfo.has_dir)
347 #define set_dir_present() (walk_arg.dirinfo.has_dir = 1)
350 * dirinfo_t (specific cache directory information):
351 * cdir_path: path to the archive cache directory
352 * update_path: path to the update directory (contains the files that will be
353 * used to extend the archive)
354 * has_dir: the specified cache directory is active
355 * count: the number of files to update
356 * flags: directory specific flags
358 typedef struct _dirinfo
{
359 char cdir_path
[PATH_MAX
];
360 char update_path
[PATH_MAX
];
368 * NEED_CACHE_DIR : cache directory is missing and needs to be created
369 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
370 * UPDATE_ERROR : an error occourred while traversing the list of files
371 * RDONLY_FSCHK : the target filesystem is read-only
372 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
374 #define NEED_CACHE_DIR 0x00000001
375 #define IS_SPARC_TARGET 0x00000002
376 #define UPDATE_ERROR 0x00000004
377 #define RDONLY_FSCHK 0x00000008
378 #define INVALIDATE_CACHE 0x00000010
380 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
381 #define set_flag(flag) (walk_arg.update_flags |= flag)
382 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
386 * update_flags: flags related to the current updating process
387 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
388 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
400 struct safefile
*next
;
403 static struct safefile
*safefiles
= NULL
;
406 * svc:/system/filesystem/usr:default service checks for this file and
407 * does a boot archive update and then reboot the system.
409 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
412 * svc:/system/boot-archive-update:default checks for this file and
413 * updates the boot archive.
415 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
417 /* Thanks growisofs */
418 #define CD_BLOCK ((off64_t)2048)
419 #define VOLDESC_OFF 16
420 #define DVD_BLOCK (32*1024)
424 unsigned char type
[1];
425 unsigned char id
[5];
426 unsigned char void1
[80-5-1];
427 unsigned char volume_space_size
[8];
428 unsigned char void2
[2048-80-8];
432 * COUNT_MAX: maximum number of changed files to justify a multisession update
433 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
437 #define BA_SIZE_MAX (50 * 1024 * 1024)
439 #define bam_nowrite() (bam_check || bam_smf_check)
441 static int sync_menu
= 1; /* whether we need to sync the BE menus */
446 (void) fprintf(stderr
, "USAGE:\n");
449 (void) fprintf(stderr
,
450 "\t%s update-archive [-vnf] [-R altroot [-p platform]] "
451 "[-F format]\n", prog
);
452 (void) fprintf(stderr
,
453 "\t%s list-archive [-R altroot [-p platform]]\n", prog
);
455 (void) fprintf(stderr
,
456 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog
);
458 (void) fprintf(stderr
,
459 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog
);
463 (void) fprintf(stderr
, "\t%s set-menu [-R altroot] key=value\n", prog
);
464 (void) fprintf(stderr
, "\t%s list-menu [-R altroot]\n", prog
);
469 * Best effort attempt to restore the $HOME value.
474 char home_env
[PATH_MAX
];
477 (void) snprintf(home_env
, sizeof (home_env
), "HOME=%s",
479 (void) putenv(home_env
);
488 * Sanitize the environment in which bootadm will execute its sub-processes
489 * (ex. mkisofs). This is done to prevent those processes from attempting
490 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
491 * or, potentially, insecure.
498 /* don't depend on caller umask */
501 /* move away from a potential unsafe current working directory */
502 while (chdir("/") == -1) {
503 if (errno
!= EINTR
) {
504 bam_print("WARNING: unable to chdir to /");
509 bam_home_env
= getenv("HOME");
510 while (bam_home_env
!= NULL
&& putenv("HOME=/") == -1) {
511 if (errno
== ENOMEM
) {
512 /* retry no more than MAX_TRIES times */
513 if (++stry
> MAX_TRIES
) {
514 bam_print("WARNING: unable to recover from "
515 "system memory pressure... aborting \n");
516 bam_exit(EXIT_FAILURE
);
518 /* memory is tight, try to sleep */
519 bam_print("Attempting to recover from memory pressure: "
520 "sleeping for %d seconds\n", SLEEP_TIME
* stry
);
521 (void) sleep(SLEEP_TIME
* stry
);
523 bam_print("WARNING: unable to sanitize HOME\n");
529 main(int argc
, char *argv
[])
531 error_t ret
= BAM_SUCCESS
;
533 (void) setlocale(LC_ALL
, "");
534 (void) textdomain(TEXT_DOMAIN
);
536 if ((prog
= strrchr(argv
[0], '/')) == NULL
) {
542 INJECT_ERROR1("ASSERT_ON", assert(0))
546 parse_args(argc
, argv
);
550 if (is_grub(bam_alt_root
? bam_root
: "/")) {
551 ret
= bam_menu(bam_subcmd
, bam_opt
,
554 ret
= bam_loader_menu(bam_subcmd
, bam_opt
,
559 ret
= bam_archive(bam_subcmd
, bam_opt
);
562 ret
= bam_install(bam_subcmd
, bam_opt
);
569 if (ret
!= BAM_SUCCESS
)
570 bam_exit((ret
== BAM_NOCHANGE
) ? 2 : 1);
577 * Equivalence of public and internal commands:
578 * update-archive -- -a update
579 * list-archive -- -a list
580 * set-menu -- -m set_option
581 * list-menu -- -m list_entry
582 * update-menu -- -m update_entry
583 * install-bootloader -- -i install_bootloader
585 static struct cmd_map
{
590 { "update-archive", BAM_ARCHIVE
, "update"},
591 { "list-archive", BAM_ARCHIVE
, "list"},
592 { "set-menu", BAM_MENU
, "set_option"},
593 { "list-menu", BAM_MENU
, "list_entry"},
594 { "update-menu", BAM_MENU
, "update_entry"},
595 { "install-bootloader", BAM_INSTALL
, "install_bootloader"},
600 * Commands syntax published in bootadm(8) are parsed here
603 parse_args(int argc
, char *argv
[])
605 struct cmd_map
*cmp
= cmd_map
;
607 /* command conforming to the final spec */
608 if (argc
> 1 && argv
[1][0] != '-') {
610 * Map commands to internal table.
612 while (cmp
->bam_cmdname
) {
613 if (strcmp(argv
[1], cmp
->bam_cmdname
) == 0) {
614 bam_cmd
= cmp
->bam_cmd
;
615 bam_subcmd
= cmp
->bam_subcmd
;
620 if (cmp
->bam_cmdname
== NULL
) {
628 parse_args_internal(argc
, argv
);
632 * A combination of public and private commands are parsed here.
633 * The internal syntax and the corresponding functionality are:
634 * -a update -- update-archive
635 * -a list -- list-archive
636 * -a update_all -- (reboot to sync all mnted OS archive)
637 * -i install_bootloader -- install-bootloader
638 * -m update_entry -- update-menu
639 * -m list_entry -- list-menu
640 * -m update_temp -- (reboot -- [boot-args])
641 * -m delete_all_entries -- (called from install)
642 * -m enable_hypervisor [args] -- cvt_to_hyper
643 * -m disable_hypervisor -- cvt_to_metal
644 * -m list_setting [entry] [value] -- list_setting
646 * A set of private flags is there too:
647 * -Q -- purge the cache directories and rebuild them
648 * -e -- use the (faster) archive update approach (used by
653 parse_args_internal(int argc
, char *argv
[])
657 extern int optind
, opterr
;
659 const char *optstring
= "a:d:fF:i:m:no:veQCLR:p:P:XZ";
661 const char *optstring
= "a:d:fF:i:m:no:veQCMLR:p:P:XZ";
664 /* Suppress error message from getopt */
668 while ((c
= getopt(argc
, argv
, optstring
)) != -1) {
674 _("multiple commands specified: -%c\n"), c
);
676 bam_cmd
= BAM_ARCHIVE
;
683 _("duplicate options specified: -%c\n"), c
);
685 bam_debug
= s_strtol(optarg
);
691 if (bam_format
!= BAM_FORMAT_UNSET
) {
694 _("multiple formats specified: -%c\n"), c
);
696 for (i
= 0; bam_formats
[i
] != NULL
; i
++) {
697 if (strcmp(bam_formats
[i
], optarg
) == 0) {
702 if (bam_format
== BAM_FORMAT_UNSET
) {
705 _("unknown format specified: -%c %s\n"),
719 _("multiple commands specified: -%c\n"), c
);
721 bam_cmd
= BAM_INSTALL
;
728 _("multiple commands specified: -%c\n"), c
);
741 * We save the original value of bam_check. The new
742 * approach in case of a read-only filesystem is to
743 * behave as a check, so we need a way to restore the
744 * original value after the evaluation of the read-only
745 * filesystem has been done.
746 * Even if we don't allow at the moment a check with
747 * update_all, this approach is more robust than
748 * simply resetting bam_check to zero.
756 _("duplicate options specified: -%c\n"), c
);
767 if (bam_pool
!= NULL
) {
770 _("duplicate options specified: -%c\n"), c
);
778 _("duplicate options specified: -%c\n"), c
);
780 } else if (realpath(optarg
, rootbuf
) == NULL
) {
782 bam_error(_("cannot resolve path %s: %s\n"),
783 optarg
, strerror(errno
));
788 bam_rootlen
= strlen(rootbuf
);
791 bam_alt_platform
= 1;
792 bam_platform
= optarg
;
793 if ((strcmp(bam_platform
, "i86pc") != 0) &&
794 (strcmp(bam_platform
, "sun4u") != 0) &&
795 (strcmp(bam_platform
, "sun4v") != 0)) {
797 bam_error(_("invalid platform %s - must be "
798 "one of sun4u, sun4v or i86pc\n"),
803 bam_is_hv
= BAM_HV_PRESENT
;
813 bam_error(_("invalid option or missing option "
814 "argument: -%c\n"), optopt
);
818 bam_error(_("invalid option or missing option "
819 "argument: -%c\n"), c
);
825 * An alternate platform requires an alternate root
827 if (bam_alt_platform
&& bam_alt_root
== 0) {
833 * A command option must be specfied
836 if (bam_opt
&& strcmp(bam_opt
, "all") == 0) {
840 bam_error(_("a command option must be specified\n"));
850 bam_error(_("Internal error: %s\n"), "parse_args");
852 } else if (optind
< argc
) {
853 bam_argv
= &argv
[optind
];
854 bam_argc
= argc
- optind
;
858 * mbr and pool are options for install_bootloader
860 if (bam_cmd
!= BAM_INSTALL
&& (bam_mbr
|| bam_pool
!= NULL
)) {
866 * -n implies verbose mode
873 check_subcmd_and_options(
876 subcmd_defn_t
*table
,
881 if (subcmd
== NULL
) {
882 bam_error(_("this command requires a sub-command\n"));
886 if (strcmp(subcmd
, "set_option") == 0) {
887 if (bam_argc
== 0 || bam_argv
== NULL
|| bam_argv
[0] == NULL
) {
888 bam_error(_("missing argument for sub-command\n"));
891 } else if (bam_argc
> 1 || bam_argv
[1] != NULL
) {
892 bam_error(_("invalid trailing arguments\n"));
896 } else if (strcmp(subcmd
, "update_all") == 0) {
898 * The only option we accept for the "update_all"
899 * subcmd is "fastboot".
901 if (bam_argc
> 1 || (bam_argc
== 1 &&
902 strcmp(bam_argv
[0], "fastboot") != 0)) {
903 bam_error(_("invalid trailing arguments\n"));
909 } else if (((strcmp(subcmd
, "enable_hypervisor") != 0) &&
910 (strcmp(subcmd
, "list_setting") != 0)) && (bam_argc
|| bam_argv
)) {
912 * Of the remaining subcommands, only "enable_hypervisor" and
913 * "list_setting" take trailing arguments.
915 bam_error(_("invalid trailing arguments\n"));
920 if (bam_root
== NULL
) {
925 /* verify that subcmd is valid */
926 for (i
= 0; table
[i
].subcmd
!= NULL
; i
++) {
927 if (strcmp(table
[i
].subcmd
, subcmd
) == 0)
931 if (table
[i
].subcmd
== NULL
) {
932 bam_error(_("invalid sub-command specified: %s\n"), subcmd
);
936 if (table
[i
].unpriv
== 0 && geteuid() != 0) {
937 bam_error(_("you must be root to run this command\n"));
942 * Currently only privileged commands need a lock
944 if (table
[i
].unpriv
== 0)
947 /* subcmd verifies that opt is appropriate */
948 if (table
[i
].option
!= OPT_OPTIONAL
) {
949 if ((table
[i
].option
== OPT_REQ
) ^ (opt
!= NULL
)) {
951 bam_error(_("this sub-command (%s) does not "
952 "take options\n"), subcmd
);
954 bam_error(_("an option is required for this "
955 "sub-command: %s\n"), subcmd
);
960 *fp
= table
[i
].handler
;
962 return (BAM_SUCCESS
);
966 * NOTE: A single "/" is also considered a trailing slash and will
970 elide_trailing_slash(const char *src
, char *dst
, size_t dstsize
)
977 (void) strlcpy(dst
, src
, dstsize
);
979 dstlen
= strlen(dst
);
980 if (dst
[dstlen
- 1] == '/') {
981 dst
[dstlen
- 1] = '\0';
986 is_safe_exec(char *path
)
990 if (lstat(path
, &sb
) != 0) {
991 bam_error(_("stat of file failed: %s: %s\n"), path
,
996 if (!S_ISREG(sb
.st_mode
)) {
997 bam_error(_("%s is not a regular file, skipping\n"), path
);
1001 if (sb
.st_uid
!= getuid()) {
1002 bam_error(_("%s is not owned by %d, skipping\n"),
1007 if (sb
.st_mode
& S_IWOTH
|| sb
.st_mode
& S_IWGRP
) {
1008 bam_error(_("%s is others or group writable, skipping\n"),
1013 return (BAM_SUCCESS
);
1017 list_setting(menu_t
*mp
, char *which
, char *setting
)
1030 if (*which
!= '\0') {
1032 * If "which" is not a number, assume it's a setting we want
1033 * to look for and so set up the routine to look for "which"
1034 * in the default entry.
1037 if (!(isdigit((int)*p
++))) {
1039 which
= mp
->curdefault
->arg
;
1043 which
= mp
->curdefault
->arg
;
1046 entry
= atoi(which
);
1048 for (ent
= mp
->entries
; ((ent
!= NULL
) && (ent
->entryNum
!= entry
));
1053 bam_error(_("no matching entry found\n"));
1057 found
= (*setting
== '\0');
1059 for (lp
= ent
->start
; lp
!= NULL
; lp
= lp
->next
) {
1060 if ((*setting
== '\0') && (lp
->flags
!= BAM_COMMENT
))
1061 bam_print("%s\n", lp
->line
);
1062 else if (lp
->cmd
!= NULL
&& strcmp(setting
, lp
->cmd
) == 0) {
1063 bam_print("%s\n", lp
->arg
);
1072 bam_error(_("no matching entry found\n"));
1076 return (BAM_SUCCESS
);
1080 install_bootloader(void)
1085 struct extmnttab mnt
;
1086 struct stat statbuf
= {0};
1087 be_node_list_t
*be_nodes
, *node
;
1089 char *root_ds
= NULL
;
1090 int ret
= BAM_ERROR
;
1092 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0) {
1093 bam_error(_("out of memory\n"));
1098 * if bam_alt_root is set, the stage files are used from alt root.
1099 * if pool is set, the target devices are pool devices, stage files
1100 * are read from pool bootfs unless alt root is set.
1102 * use arguments as targets, stage files are from alt or current root
1103 * if no arguments and no pool, install on current boot pool.
1107 if (stat(bam_root
, &statbuf
) != 0) {
1108 bam_error(_("stat of file failed: %s: %s\n"), bam_root
,
1112 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
1113 bam_error(_("failed to open file: %s: %s\n"),
1114 MNTTAB
, strerror(errno
));
1118 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
1119 if (mnt
.mnt_major
== major(statbuf
.st_dev
) &&
1120 mnt
.mnt_minor
== minor(statbuf
.st_dev
)) {
1122 root_ds
= strdup(mnt
.mnt_special
);
1129 bam_error(_("alternate root %s not in mnttab\n"),
1133 if (root_ds
== NULL
) {
1134 bam_error(_("out of memory\n"));
1138 if (be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
) != BE_SUCCESS
) {
1139 bam_error(_("No BE's found\n"));
1142 for (node
= be_nodes
; node
!= NULL
; node
= node
->be_next_node
)
1143 if (strcmp(root_ds
, node
->be_root_ds
) == 0)
1147 bam_error(_("BE (%s) does not exist\n"), root_ds
);
1152 be_free_list(be_nodes
);
1155 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_NAME
,
1156 node
->be_node_name
);
1157 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_ROOT
,
1159 be_free_list(be_nodes
);
1167 flags
|= BE_INSTALLBOOT_FLAG_FORCE
;
1169 flags
|= BE_INSTALLBOOT_FLAG_MBR
;
1171 flags
|= BE_INSTALLBOOT_FLAG_VERBOSE
;
1173 if (nvlist_add_uint16(nvl
, BE_ATTR_INSTALL_FLAGS
, flags
) != 0) {
1174 bam_error(_("out of memory\n"));
1180 * if altroot was set, we got be name and be root, only need
1181 * to set pool name as target.
1182 * if no altroot, need to find be name and root from pool.
1184 if (bam_pool
!= NULL
) {
1185 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_POOL
, bam_pool
);
1191 ret
= be_installboot(nvl
);
1198 if (be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
) != BE_SUCCESS
) {
1199 bam_error(_("No BE's found\n"));
1204 if (bam_pool
!= NULL
) {
1206 * find active be_node in bam_pool
1208 for (node
= be_nodes
; node
!= NULL
; node
= node
->be_next_node
) {
1209 if (strcmp(bam_pool
, node
->be_rpool
) != 0)
1211 if (node
->be_active_on_boot
)
1215 bam_error(_("No active BE in %s\n"), bam_pool
);
1216 be_free_list(be_nodes
);
1220 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_NAME
,
1221 node
->be_node_name
);
1222 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_ROOT
,
1224 be_free_list(be_nodes
);
1229 ret
= be_installboot(nvl
);
1236 * get dataset for "/" and fill up the args.
1238 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
1239 bam_error(_("failed to open file: %s: %s\n"),
1240 MNTTAB
, strerror(errno
));
1242 be_free_list(be_nodes
);
1247 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
1248 if (strcmp(mnt
.mnt_mountp
, "/") == 0) {
1250 root_ds
= strdup(mnt
.mnt_special
);
1257 bam_error(_("alternate root %s not in mnttab\n"), "/");
1259 be_free_list(be_nodes
);
1262 if (root_ds
== NULL
) {
1263 bam_error(_("out of memory\n"));
1265 be_free_list(be_nodes
);
1269 for (node
= be_nodes
; node
!= NULL
; node
= node
->be_next_node
) {
1270 if (strcmp(root_ds
, node
->be_root_ds
) == 0)
1275 bam_error(_("No such BE: %s\n"), root_ds
);
1277 be_free_list(be_nodes
);
1283 ret
= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_NAME
, node
->be_node_name
);
1284 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_ROOT
, node
->be_root_ds
);
1285 ret
|= nvlist_add_string(nvl
, BE_ATTR_ORIG_BE_POOL
, node
->be_rpool
);
1286 be_free_list(be_nodes
);
1291 ret
= be_installboot(nvl
) ? BAM_ERROR
: 0;
1299 bam_install(char *subcmd
, char *opt
)
1306 if (check_subcmd_and_options(subcmd
, opt
, inst_subcmds
, &f
) ==
1314 bam_menu(char *subcmd
, char *opt
, int largc
, char *largv
[])
1317 char menu_path
[PATH_MAX
];
1318 char clean_menu_root
[PATH_MAX
];
1319 char path
[PATH_MAX
];
1321 char menu_root
[PATH_MAX
];
1323 error_t (*f
)(menu_t
*mp
, char *menu_path
, char *opt
);
1324 char *special
= NULL
;
1327 char *zmntpt
= NULL
;
1330 const char *fcn
= "bam_menu()";
1333 * Menu sub-command only applies to GRUB (i.e. x86)
1335 if (!is_grub(bam_alt_root
? bam_root
: "/")) {
1336 bam_error(_("not a GRUB 0.97 based Illumos instance. "
1337 "Operation not supported\n"));
1344 ret
= check_subcmd_and_options(subcmd
, opt
, menu_subcmds
, &f
);
1345 if (ret
== BAM_ERROR
) {
1351 (void) strlcpy(menu_root
, bam_root
, sizeof (menu_root
));
1352 osdev
= osroot
= NULL
;
1354 if (strcmp(subcmd
, "update_entry") == 0) {
1357 osdev
= strtok(opt
, ",");
1359 osroot
= strtok(NULL
, ",");
1361 /* fixup bam_root so that it points at osroot */
1362 if (realpath(osroot
, rootbuf
) == NULL
) {
1363 bam_error(_("cannot resolve path %s: %s\n"),
1364 osroot
, strerror(errno
));
1369 bam_rootlen
= strlen(rootbuf
);
1374 * We support menu on PCFS (under certain conditions), but
1377 if (is_pcfs(bam_root
)) {
1378 bam_error(_("root <%s> on PCFS is not supported\n"), bam_root
);
1382 if (stat(menu_root
, &sb
) == -1) {
1383 bam_error(_("cannot find GRUB menu\n"));
1387 BAM_DPRINTF(("%s: menu root is %s\n", fcn
, menu_root
));
1390 * We no longer use the GRUB slice file. If it exists, then
1391 * the user is doing something that is unsupported (such as
1392 * standard upgrading an old Live Upgrade BE). If that
1393 * happens, mimic existing behavior i.e. pretend that it is
1394 * not a BE. Emit a warning though.
1397 (void) snprintf(path
, sizeof (path
), "%s%s", bam_root
,
1400 (void) snprintf(path
, sizeof (path
), "%s", GRUB_slice
);
1403 if (bam_verbose
&& stat(path
, &sb
) == 0)
1404 bam_error(_("unsupported GRUB slice file (%s) exists - "
1405 "ignoring.\n"), path
);
1407 if (is_zfs(menu_root
)) {
1408 assert(strcmp(menu_root
, bam_root
) == 0);
1409 special
= get_special(menu_root
);
1410 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special
= NULL
);
1411 if (special
== NULL
) {
1412 bam_error(_("cant find special file for "
1413 "mount-point %s\n"), menu_root
);
1416 pool
= strtok(special
, "/");
1417 INJECT_ERROR1("Z_MENU_GET_POOL", pool
= NULL
);
1420 bam_error(_("cant find pool for mount-point %s\n"),
1424 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn
, pool
));
1426 zmntpt
= mount_top_dataset(pool
, &zmnted
);
1427 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt
= NULL
);
1428 if (zmntpt
== NULL
) {
1429 bam_error(_("cannot mount pool dataset for pool: %s\n"),
1434 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn
, zmntpt
));
1436 (void) strlcpy(menu_root
, zmntpt
, sizeof (menu_root
));
1437 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn
, menu_root
));
1440 elide_trailing_slash(menu_root
, clean_menu_root
,
1441 sizeof (clean_menu_root
));
1443 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn
, clean_menu_root
));
1445 (void) strlcpy(menu_path
, clean_menu_root
, sizeof (menu_path
));
1446 (void) strlcat(menu_path
, GRUB_MENU
, sizeof (menu_path
));
1448 BAM_DPRINTF(("%s: menu path is: %s\n", fcn
, menu_path
));
1451 * If listing the menu, display the menu location
1453 if (strcmp(subcmd
, "list_entry") == 0)
1454 bam_print(_("the location for the active GRUB menu is: %s\n"),
1457 if ((menu
= menu_read(menu_path
)) == NULL
) {
1458 bam_error(_("cannot find GRUB menu file: %s\n"), menu_path
);
1465 * We already checked the following case in
1466 * check_subcmd_and_suboptions() above. Complete the
1469 if (strcmp(subcmd
, "set_option") == 0) {
1470 assert(largc
== 1 && largv
[0] && largv
[1] == NULL
);
1472 } else if ((strcmp(subcmd
, "enable_hypervisor") != 0) &&
1473 (strcmp(subcmd
, "list_setting") != 0)) {
1474 assert(largc
== 0 && largv
== NULL
);
1477 ret
= get_boot_cap(bam_root
);
1478 if (ret
!= BAM_SUCCESS
) {
1479 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn
));
1484 * Once the sub-cmd handler has run
1485 * only the line field is guaranteed to have valid values
1487 if (strcmp(subcmd
, "update_entry") == 0) {
1488 ret
= f(menu
, menu_root
, osdev
);
1489 } else if (strcmp(subcmd
, "upgrade") == 0) {
1490 ret
= f(menu
, bam_root
, menu_root
);
1491 } else if (strcmp(subcmd
, "list_entry") == 0) {
1492 ret
= f(menu
, menu_path
, opt
);
1493 } else if (strcmp(subcmd
, "list_setting") == 0) {
1494 ret
= f(menu
, ((largc
> 0) ? largv
[0] : ""),
1495 ((largc
> 1) ? largv
[1] : ""));
1496 } else if (strcmp(subcmd
, "disable_hypervisor") == 0) {
1498 bam_error(_("%s operation unsupported on SPARC "
1499 "machines\n"), subcmd
);
1502 ret
= f(menu
, bam_root
, NULL
);
1504 } else if (strcmp(subcmd
, "enable_hypervisor") == 0) {
1506 bam_error(_("%s operation unsupported on SPARC "
1507 "machines\n"), subcmd
);
1510 char *extra_args
= NULL
;
1513 * Compress all arguments passed in the largv[] array
1514 * into one string that can then be appended to the
1515 * end of the kernel$ string the routine to enable the
1516 * hypervisor will build.
1518 * This allows the caller to supply arbitrary unparsed
1519 * arguments, such as dom0 memory settings or APIC
1522 * This concatenation will be done without ANY syntax
1523 * checking whatsoever, so it's the responsibility of
1524 * the caller to make sure the arguments are valid and
1525 * do not duplicate arguments the conversion routines
1531 for (extra_len
= 0, i
= 0; i
< largc
; i
++)
1532 extra_len
+= strlen(largv
[i
]);
1535 * Allocate space for argument strings,
1536 * intervening spaces and terminating NULL.
1538 extra_args
= alloca(extra_len
+ largc
);
1540 (void) strcpy(extra_args
, largv
[0]);
1542 for (i
= 1; i
< largc
; i
++) {
1543 (void) strcat(extra_args
, " ");
1544 (void) strcat(extra_args
, largv
[i
]);
1548 ret
= f(menu
, bam_root
, extra_args
);
1551 ret
= f(menu
, NULL
, opt
);
1553 if (ret
== BAM_WRITE
) {
1554 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
1555 fcn
, clean_menu_root
));
1556 ret
= menu_write(clean_menu_root
, menu
);
1560 INJECT_ERROR1("POOL_SET", pool
= "/pooldata");
1561 assert((is_zfs(menu_root
)) ^ (pool
== NULL
));
1563 (void) umount_top_dataset(pool
, zmnted
, zmntpt
);
1577 error_t (*f
)(char *root
, char *opt
);
1578 const char *fcn
= "bam_archive()";
1581 * Add trailing / for archive subcommands
1583 if (rootbuf
[strlen(rootbuf
) - 1] != '/')
1584 (void) strcat(rootbuf
, "/");
1585 bam_rootlen
= strlen(rootbuf
);
1590 ret
= check_subcmd_and_options(subcmd
, opt
, arch_subcmds
, &f
);
1591 if (ret
!= BAM_SUCCESS
) {
1595 ret
= get_boot_cap(rootbuf
);
1596 if (ret
!= BAM_SUCCESS
) {
1597 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn
));
1602 * Check archive not supported with update_all
1603 * since it is awkward to display out-of-sync
1604 * information for each BE.
1606 if (bam_check
&& strcmp(subcmd
, "update_all") == 0) {
1607 bam_error(_("the check option is not supported with "
1608 "subcmd: %s\n"), subcmd
);
1612 if (strcmp(subcmd
, "update_all") == 0)
1615 ret
= f(bam_root
, opt
);
1624 bam_error(char *format
, ...)
1628 va_start(ap
, format
);
1629 (void) fprintf(stderr
, "%s: ", prog
);
1630 (void) vfprintf(stderr
, format
, ap
);
1636 bam_derror(char *format
, ...)
1642 va_start(ap
, format
);
1643 (void) fprintf(stderr
, "DEBUG: ");
1644 (void) vfprintf(stderr
, format
, ap
);
1650 bam_print(char *format
, ...)
1654 va_start(ap
, format
);
1655 (void) vfprintf(stdout
, format
, ap
);
1661 bam_print_stderr(char *format
, ...)
1665 va_start(ap
, format
);
1666 (void) vfprintf(stderr
, format
, ap
);
1671 bam_exit(int excode
)
1687 bam_lock_fd
= open(BAM_LOCK_FILE
, O_CREAT
|O_RDWR
, LOCK_FILE_PERMS
);
1688 if (bam_lock_fd
< 0) {
1690 * We may be invoked early in boot for archive verification.
1691 * In this case, root is readonly and /var/run may not exist.
1692 * Proceed without the lock
1694 if (errno
== EROFS
|| errno
== ENOENT
) {
1695 bam_root_readonly
= 1;
1699 bam_error(_("failed to open file: %s: %s\n"),
1700 BAM_LOCK_FILE
, strerror(errno
));
1704 lock
.l_type
= F_WRLCK
;
1705 lock
.l_whence
= SEEK_SET
;
1709 if (fcntl(bam_lock_fd
, F_SETLK
, &lock
) == -1) {
1710 if (errno
!= EACCES
&& errno
!= EAGAIN
) {
1711 bam_error(_("failed to lock file: %s: %s\n"),
1712 BAM_LOCK_FILE
, strerror(errno
));
1713 (void) close(bam_lock_fd
);
1718 (void) pread(bam_lock_fd
, &pid
, sizeof (pid_t
), 0);
1720 _("another instance of bootadm (pid %lu) is running\n"),
1723 lock
.l_type
= F_WRLCK
;
1724 lock
.l_whence
= SEEK_SET
;
1727 if (fcntl(bam_lock_fd
, F_SETLKW
, &lock
) == -1) {
1728 bam_error(_("failed to lock file: %s: %s\n"),
1729 BAM_LOCK_FILE
, strerror(errno
));
1730 (void) close(bam_lock_fd
);
1736 /* We own the lock now */
1738 (void) write(bam_lock_fd
, &pid
, sizeof (pid
));
1744 struct flock unlock
;
1750 * NOP if we don't hold the lock
1752 if (bam_lock_fd
< 0) {
1756 unlock
.l_type
= F_UNLCK
;
1757 unlock
.l_whence
= SEEK_SET
;
1761 if (fcntl(bam_lock_fd
, F_SETLK
, &unlock
) == -1) {
1762 bam_error(_("failed to unlock file: %s: %s\n"),
1763 BAM_LOCK_FILE
, strerror(errno
));
1766 if (close(bam_lock_fd
) == -1) {
1767 bam_error(_("failed to close file: %s: %s\n"),
1768 BAM_LOCK_FILE
, strerror(errno
));
1774 list_archive(char *root
, char *opt
)
1777 filelist_t
*flistp
= &flist
;
1781 assert(opt
== NULL
);
1783 flistp
->head
= flistp
->tail
= NULL
;
1784 if (read_list(root
, flistp
) != BAM_SUCCESS
) {
1787 assert(flistp
->head
&& flistp
->tail
);
1789 for (lp
= flistp
->head
; lp
; lp
= lp
->next
) {
1790 bam_print(_("%s\n"), lp
->line
);
1793 filelist_free(flistp
);
1795 return (BAM_SUCCESS
);
1799 * This routine writes a list of lines to a file.
1800 * The list is *not* freed
1803 list2file(char *root
, char *tmp
, char *final
, line_t
*start
)
1805 char tmpfile
[PATH_MAX
];
1806 char path
[PATH_MAX
];
1815 const char *fcn
= "list2file()";
1817 (void) snprintf(path
, sizeof (path
), "%s%s", root
, final
);
1819 if (start
== NULL
) {
1820 /* Empty GRUB menu */
1821 if (stat(path
, &sb
) != -1) {
1822 bam_print(_("file is empty, deleting file: %s\n"),
1824 if (unlink(path
) != 0) {
1825 bam_error(_("failed to unlink file: %s: %s\n"),
1826 path
, strerror(errno
));
1829 return (BAM_SUCCESS
);
1832 return (BAM_SUCCESS
);
1836 * Preserve attributes of existing file if possible,
1837 * otherwise ask the system for uid/gid of root/sys.
1838 * If all fails, fall back on hard-coded defaults.
1840 if (stat(path
, &sb
) != -1) {
1842 root_uid
= sb
.st_uid
;
1843 sys_gid
= sb
.st_gid
;
1845 mode
= DEFAULT_DEV_MODE
;
1846 if ((pw
= getpwnam(DEFAULT_DEV_USER
)) != NULL
) {
1847 root_uid
= pw
->pw_uid
;
1849 bam_error(_("getpwnam: uid for %s failed, "
1850 "defaulting to %d\n"),
1851 DEFAULT_DEV_USER
, DEFAULT_DEV_UID
);
1852 root_uid
= (uid_t
)DEFAULT_DEV_UID
;
1854 if ((gp
= getgrnam(DEFAULT_DEV_GROUP
)) != NULL
) {
1855 sys_gid
= gp
->gr_gid
;
1857 bam_error(_("getgrnam: gid for %s failed, "
1858 "defaulting to %d\n"),
1859 DEFAULT_DEV_GROUP
, DEFAULT_DEV_GID
);
1860 sys_gid
= (gid_t
)DEFAULT_DEV_GID
;
1864 (void) snprintf(tmpfile
, sizeof (tmpfile
), "%s%s", root
, tmp
);
1866 /* Truncate tmpfile first */
1867 fp
= fopen(tmpfile
, "w");
1869 bam_error(_("failed to open file: %s: %s\n"), tmpfile
,
1874 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret
= EOF
);
1876 bam_error(_("failed to close file: %s: %s\n"),
1877 tmpfile
, strerror(errno
));
1881 /* Now open it in append mode */
1882 fp
= fopen(tmpfile
, "a");
1884 bam_error(_("failed to open file: %s: %s\n"), tmpfile
,
1889 for (; start
; start
= start
->next
) {
1890 ret
= s_fputs(start
->line
, fp
);
1891 INJECT_ERROR1("LIST2FILE_FPUTS", ret
= EOF
);
1893 bam_error(_("write to file failed: %s: %s\n"),
1894 tmpfile
, strerror(errno
));
1901 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret
= EOF
);
1903 bam_error(_("failed to close file: %s: %s\n"),
1904 tmpfile
, strerror(errno
));
1909 * Set up desired attributes. Ignore failures on filesystems
1910 * not supporting these operations - pcfs reports unsupported
1911 * operations as EINVAL.
1913 ret
= chmod(tmpfile
, mode
);
1915 errno
!= EINVAL
&& errno
!= ENOTSUP
) {
1916 bam_error(_("chmod operation on %s failed - %s\n"),
1917 tmpfile
, strerror(errno
));
1921 ret
= chown(tmpfile
, root_uid
, sys_gid
);
1923 errno
!= EINVAL
&& errno
!= ENOTSUP
) {
1924 bam_error(_("chgrp operation on %s failed - %s\n"),
1925 tmpfile
, strerror(errno
));
1930 * Do an atomic rename
1932 ret
= rename(tmpfile
, path
);
1933 INJECT_ERROR1("LIST2FILE_RENAME", ret
= -1);
1935 bam_error(_("rename to file failed: %s: %s\n"), path
,
1940 BAM_DPRINTF(("%s: wrote file successfully: %s\n", fcn
, path
));
1941 return (BAM_SUCCESS
);
1945 * Checks if the path specified (without the file name at the end) exists
1946 * and creates it if not. If the path exists and is not a directory, an attempt
1947 * to unlink is made.
1950 setup_path(char *path
)
1956 p
= strrchr(path
, '/');
1959 if (stat(path
, &sb
) != 0 || !(S_ISDIR(sb
.st_mode
))) {
1960 /* best effort attempt, mkdirp will catch the error */
1961 (void) unlink(path
);
1963 bam_print(_("need to create directory "
1964 "path for %s\n"), path
);
1965 ret
= mkdirp(path
, DIR_PERMS
);
1967 bam_error(_("mkdir of %s failed: %s\n"),
1968 path
, strerror(errno
));
1974 return (BAM_SUCCESS
);
1976 return (BAM_SUCCESS
);
1985 char path
[PATH_MAX
];
1990 setup_file(char *base
, const char *path
, cachefile
*cf
)
1995 /* init gzfile or fdfile in case we fail before opening */
1996 if (bam_direct
== BAM_DIRECT_DBOOT
)
1997 cf
->out
.gzfile
= NULL
;
1999 cf
->out
.fdfile
= -1;
2001 /* strip the trailing altroot path */
2002 strip
= (char *)path
+ strlen(rootbuf
);
2004 ret
= snprintf(cf
->path
, sizeof (cf
->path
), "%s/%s", base
, strip
);
2005 if (ret
>= sizeof (cf
->path
)) {
2006 bam_error(_("unable to create path on mountpoint %s, "
2007 "path too long\n"), rootbuf
);
2011 /* Check if path is present in the archive cache directory */
2012 if (setup_path(cf
->path
) == BAM_ERROR
)
2015 if (bam_direct
== BAM_DIRECT_DBOOT
) {
2016 if ((cf
->out
.gzfile
= gzopen(cf
->path
, "wb")) == NULL
) {
2017 bam_error(_("failed to open file: %s: %s\n"),
2018 cf
->path
, strerror(errno
));
2021 (void) gzsetparams(cf
->out
.gzfile
, Z_BEST_SPEED
,
2022 Z_DEFAULT_STRATEGY
);
2024 if ((cf
->out
.fdfile
= open(cf
->path
, O_WRONLY
| O_CREAT
, 0644))
2026 bam_error(_("failed to open file: %s: %s\n"),
2027 cf
->path
, strerror(errno
));
2032 return (BAM_SUCCESS
);
2036 cache_write(cachefile cf
, char *buf
, int size
)
2040 if (bam_direct
== BAM_DIRECT_DBOOT
) {
2041 if (gzwrite(cf
.out
.gzfile
, buf
, size
) < 1) {
2042 bam_error(_("failed to write to %s\n"),
2043 gzerror(cf
.out
.gzfile
, &err
));
2044 if (err
== Z_ERRNO
&& bam_verbose
) {
2045 bam_error(_("write to file failed: %s: %s\n"),
2046 cf
.path
, strerror(errno
));
2051 if (write(cf
.out
.fdfile
, buf
, size
) < 1) {
2052 bam_error(_("write to file failed: %s: %s\n"),
2053 cf
.path
, strerror(errno
));
2057 return (BAM_SUCCESS
);
2061 cache_close(cachefile cf
)
2065 if (bam_direct
== BAM_DIRECT_DBOOT
) {
2066 if (cf
.out
.gzfile
) {
2067 ret
= gzclose(cf
.out
.gzfile
);
2069 bam_error(_("failed to close file: %s: %s\n"),
2070 cf
.path
, strerror(errno
));
2075 if (cf
.out
.fdfile
!= -1) {
2076 ret
= close(cf
.out
.fdfile
);
2078 bam_error(_("failed to close file: %s: %s\n"),
2079 cf
.path
, strerror(errno
));
2085 return (BAM_SUCCESS
);
2089 dircache_updatefile(const char *path
)
2094 cachefile outfile
, outupdt
;
2096 if (bam_nowrite()) {
2097 set_dir_flag(NEED_UPDATE
);
2098 return (BAM_SUCCESS
);
2101 if (!has_cachedir())
2102 return (BAM_SUCCESS
);
2104 if ((infile
= fopen(path
, "rb")) == NULL
) {
2105 bam_error(_("failed to open file: %s: %s\n"), path
,
2110 ret
= setup_file(get_cachedir(), path
, &outfile
);
2111 if (ret
== BAM_ERROR
) {
2112 exitcode
= BAM_ERROR
;
2115 if (!is_dir_flag_on(NO_EXTEND
)) {
2116 ret
= setup_file(get_updatedir(), path
, &outupdt
);
2117 if (ret
== BAM_ERROR
)
2118 set_dir_flag(NO_EXTEND
);
2121 while ((ret
= fread(buf
, 1, sizeof (buf
), infile
)) > 0) {
2122 if (cache_write(outfile
, buf
, ret
) == BAM_ERROR
) {
2123 exitcode
= BAM_ERROR
;
2126 if (!is_dir_flag_on(NO_EXTEND
))
2127 if (cache_write(outupdt
, buf
, ret
) == BAM_ERROR
)
2128 set_dir_flag(NO_EXTEND
);
2131 set_dir_flag(NEED_UPDATE
);
2133 if (get_count() > COUNT_MAX
)
2134 set_dir_flag(NO_EXTEND
);
2135 exitcode
= BAM_SUCCESS
;
2137 (void) fclose(infile
);
2138 if (cache_close(outfile
) == BAM_ERROR
)
2139 exitcode
= BAM_ERROR
;
2140 if (!is_dir_flag_on(NO_EXTEND
) &&
2141 cache_close(outupdt
) == BAM_ERROR
)
2142 exitcode
= BAM_ERROR
;
2143 if (exitcode
== BAM_ERROR
)
2144 set_flag(UPDATE_ERROR
);
2149 dircache_updatedir(const char *path
, int updt
)
2152 char dpath
[PATH_MAX
];
2156 strip
= (char *)path
+ strlen(rootbuf
);
2158 ret
= snprintf(dpath
, sizeof (dpath
), "%s/%s", updt
?
2159 get_updatedir() : get_cachedir(), strip
);
2161 if (ret
>= sizeof (dpath
)) {
2162 bam_error(_("unable to create path on mountpoint %s, "
2163 "path too long\n"), rootbuf
);
2164 set_flag(UPDATE_ERROR
);
2168 if (stat(dpath
, &sb
) == 0 && S_ISDIR(sb
.st_mode
))
2169 return (BAM_SUCCESS
);
2172 if (!is_dir_flag_on(NO_EXTEND
))
2173 if (!bam_nowrite() && mkdirp(dpath
, DIR_PERMS
) == -1)
2174 set_dir_flag(NO_EXTEND
);
2176 if (!bam_nowrite() && mkdirp(dpath
, DIR_PERMS
) == -1) {
2177 set_flag(UPDATE_ERROR
);
2182 set_dir_flag(NEED_UPDATE
);
2183 return (BAM_SUCCESS
);
2186 #define DO_CACHE_DIR 0
2187 #define DO_UPDATE_DIR 1
2189 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2190 typedef Elf64_Ehdr _elfhdr
;
2192 typedef Elf32_Ehdr _elfhdr
;
2196 * This routine updates the contents of the cache directory
2199 update_dircache(const char *path
, int flags
)
2201 int rc
= BAM_SUCCESS
;
2209 if ((fd
= open(path
, O_RDONLY
)) < 0) {
2210 bam_error(_("failed to open file: %s: %s\n"),
2211 path
, strerror(errno
));
2212 set_flag(UPDATE_ERROR
);
2218 * libelf and gelf would be a cleaner and easier way to handle
2219 * this, but libelf fails compilation if _ILP32 is defined &&
2220 * _FILE_OFFSET_BITS is != 32 ...
2222 if (read(fd
, (void *)&elf
, sizeof (_elfhdr
)) < 0) {
2223 bam_error(_("read failed for file: %s: %s\n"),
2224 path
, strerror(errno
));
2225 set_flag(UPDATE_ERROR
);
2232 if (memcmp(elf
.e_ident
, ELFMAG
, 4) != 0) {
2233 /* Not an ELF file, include in archive */
2234 rc
= dircache_updatefile(path
);
2236 /* Include 64-bit ELF files only */
2237 switch (elf
.e_ident
[EI_CLASS
]) {
2239 bam_print(_("WARNING: ELF file %s is 32-bit "
2240 "and will be excluded\n"), path
);
2243 rc
= dircache_updatefile(path
);
2246 bam_print(_("WARNING: ELF file %s is neither "
2247 "32-bit nor 64-bit\n"), path
);
2254 if (strstr(path
, "/amd64") != NULL
) {
2255 if (has_cachedir()) {
2256 rc
= dircache_updatedir(path
, DO_UPDATE_DIR
);
2257 if (rc
== BAM_SUCCESS
)
2258 rc
= dircache_updatedir(path
,
2275 const struct stat
*st
,
2281 uint64_t filestat
[2];
2282 int error
, ret
, status
;
2284 struct safefile
*safefilep
;
2290 * On SPARC we create/update links too.
2292 if (flags
!= FTW_F
&& flags
!= FTW_D
&& (flags
== FTW_SL
&&
2293 !is_flag_on(IS_SPARC_TARGET
)))
2297 * Ignore broken links
2299 if (flags
== FTW_SL
&& stat(file
, &sb
) < 0)
2303 * new_nvlp may be NULL if there were errors earlier
2304 * but this is not fatal to update determination.
2306 if (walk_arg
.new_nvlp
) {
2307 filestat
[0] = st
->st_size
;
2308 filestat
[1] = st
->st_mtime
;
2309 error
= nvlist_add_uint64_array(walk_arg
.new_nvlp
,
2310 file
+ bam_rootlen
, filestat
, 2);
2312 bam_error(_("failed to update stat data for: %s: %s\n"),
2313 file
, strerror(error
));
2317 * If we are invoked as part of system/filesystem/boot-archive, then
2318 * there are a number of things we should not worry about
2320 if (bam_smf_check
) {
2321 /* ignore amd64 modules unless we are booted amd64. */
2322 if (!is_amd64() && strstr(file
, "/amd64/") != 0)
2325 /* read in list of safe files */
2326 if (safefiles
== NULL
) {
2327 fp
= fopen("/boot/solaris/filelist.safe", "r");
2329 safefiles
= s_calloc(1,
2330 sizeof (struct safefile
));
2331 safefilep
= safefiles
;
2332 safefilep
->name
= s_calloc(1, MAXPATHLEN
+
2334 safefilep
->next
= NULL
;
2335 while (s_fgets(safefilep
->name
, MAXPATHLEN
+
2336 MAXNAMELEN
, fp
) != NULL
) {
2337 safefilep
->next
= s_calloc(1,
2338 sizeof (struct safefile
));
2339 safefilep
= safefilep
->next
;
2340 safefilep
->name
= s_calloc(1,
2341 MAXPATHLEN
+ MAXNAMELEN
);
2342 safefilep
->next
= NULL
;
2350 * On SPARC we create a -path-list file for mkisofs
2352 if (is_flag_on(IS_SPARC_TARGET
) && !bam_nowrite()) {
2353 if (flags
!= FTW_D
) {
2356 strip
= (char *)file
+ strlen(rootbuf
);
2357 (void) fprintf(walk_arg
.sparcfile
, "/%s=%s\n", strip
,
2363 * We are transitioning from the old model to the dircache or the cache
2364 * directory was removed: create the entry without further checkings.
2366 if (is_flag_on(NEED_CACHE_DIR
)) {
2368 bam_print(_(" new %s\n"), file
);
2370 if (is_flag_on(IS_SPARC_TARGET
)) {
2371 set_dir_flag(NEED_UPDATE
);
2375 ret
= update_dircache(file
, flags
);
2376 if (ret
== BAM_ERROR
) {
2377 bam_error(_("directory cache update failed for %s\n"),
2386 * We need an update if file doesn't exist in old archive
2388 if (walk_arg
.old_nvlp
== NULL
||
2389 nvlist_lookup_uint64_array(walk_arg
.old_nvlp
,
2390 file
+ bam_rootlen
, &value
, &sz
) != 0) {
2391 if (bam_smf_check
) /* ignore new during smf check */
2394 if (is_flag_on(IS_SPARC_TARGET
)) {
2395 set_dir_flag(NEED_UPDATE
);
2397 ret
= update_dircache(file
, flags
);
2398 if (ret
== BAM_ERROR
) {
2399 bam_error(_("directory cache update "
2400 "failed for %s\n"), file
);
2406 bam_print(_(" new %s\n"), file
);
2411 * If we got there, the file is already listed as to be included in the
2412 * iso image. We just need to know if we are going to rebuild it or not
2414 if (is_flag_on(IS_SPARC_TARGET
) &&
2415 is_dir_flag_on(NEED_UPDATE
) && !bam_nowrite())
2418 * File exists in old archive. Check if file has changed
2421 bcopy(value
, filestat
, sizeof (filestat
));
2423 if (flags
!= FTW_D
&& (filestat
[0] != st
->st_size
||
2424 filestat
[1] != st
->st_mtime
)) {
2425 if (bam_smf_check
) {
2426 safefilep
= safefiles
;
2427 while (safefilep
!= NULL
&&
2428 safefilep
->name
[0] != '\0') {
2429 if (regcomp(&re
, safefilep
->name
,
2430 REG_EXTENDED
|REG_NOSUB
) == 0) {
2431 status
= regexec(&re
,
2432 file
+ bam_rootlen
, 0, NULL
, 0);
2436 NEED_UPDATE_SAFE_FILE
,
2441 safefilep
= safefilep
->next
;
2445 if (is_flag_on(IS_SPARC_TARGET
)) {
2446 set_dir_flag(NEED_UPDATE
);
2448 ret
= update_dircache(file
, flags
);
2449 if (ret
== BAM_ERROR
) {
2450 bam_error(_("directory cache update failed "
2457 * Update self-assembly file if there are changes in
2458 * /etc/system.d directory
2460 if (strstr(file
, ETC_SYSTEM_DIR
)) {
2461 ret
= update_dircache(self_assembly
, flags
);
2462 if (ret
== BAM_ERROR
) {
2463 bam_error(_("directory cache update failed "
2471 bam_print(" %s\n", file
);
2473 bam_print(_(" changed %s\n"), file
);
2481 * Remove a directory path recursively
2486 struct dirent
*d
= NULL
;
2488 char tpath
[PATH_MAX
];
2491 if ((dir
= opendir(path
)) == NULL
)
2494 while ((d
= readdir(dir
)) != NULL
) {
2495 if ((strcmp(d
->d_name
, ".") != 0) &&
2496 (strcmp(d
->d_name
, "..") != 0)) {
2497 (void) snprintf(tpath
, sizeof (tpath
), "%s/%s",
2499 if (stat(tpath
, &sb
) == 0) {
2500 if (sb
.st_mode
& S_IFDIR
)
2501 (void) rmdir_r(tpath
);
2503 (void) remove(tpath
);
2507 return (remove(path
));
2511 * Check if cache directory exists and, if not, create it and update flags
2512 * accordingly. If the path exists, but it's not a directory, a best effort
2513 * attempt to remove and recreate it is made.
2514 * If the user requested a 'purge', always recreate the directory from scratch.
2517 set_cache_dir(char *root
)
2522 ret
= build_path(get_cachedir(), sizeof (get_cachedir()),
2523 root
, ARCHIVE_PREFIX
, CACHEDIR_SUFFIX
);
2525 if (ret
>= sizeof (get_cachedir())) {
2526 bam_error(_("unable to create path on mountpoint %s, "
2527 "path too long\n"), rootbuf
);
2531 if (bam_purge
|| is_flag_on(INVALIDATE_CACHE
))
2532 (void) rmdir_r(get_cachedir());
2534 if (stat(get_cachedir(), &sb
) != 0 || !(S_ISDIR(sb
.st_mode
))) {
2535 /* best effort unlink attempt, mkdir will catch errors */
2536 (void) unlink(get_cachedir());
2539 bam_print(_("archive cache directory not found: %s\n"),
2541 ret
= mkdir(get_cachedir(), DIR_PERMS
);
2543 bam_error(_("mkdir of %s failed: %s\n"),
2544 get_cachedir(), strerror(errno
));
2545 get_cachedir()[0] = '\0';
2548 set_flag(NEED_CACHE_DIR
);
2549 set_dir_flag(NO_EXTEND
);
2552 return (BAM_SUCCESS
);
2556 set_update_dir(char *root
)
2561 if (is_dir_flag_on(NO_EXTEND
))
2562 return (BAM_SUCCESS
);
2565 set_dir_flag(NO_EXTEND
);
2566 return (BAM_SUCCESS
);
2569 ret
= build_path(get_updatedir(), sizeof (get_updatedir()),
2570 root
, ARCHIVE_PREFIX
, UPDATEDIR_SUFFIX
);
2572 if (ret
>= sizeof (get_updatedir())) {
2573 bam_error(_("unable to create path on mountpoint %s, "
2574 "path too long\n"), rootbuf
);
2578 if (stat(get_updatedir(), &sb
) == 0) {
2579 if (S_ISDIR(sb
.st_mode
))
2580 ret
= rmdir_r(get_updatedir());
2582 ret
= unlink(get_updatedir());
2585 set_dir_flag(NO_EXTEND
);
2588 if (mkdir(get_updatedir(), DIR_PERMS
) < 0)
2589 set_dir_flag(NO_EXTEND
);
2591 return (BAM_SUCCESS
);
2595 is_valid_archive(char *root
)
2597 char archive_path
[PATH_MAX
];
2598 char timestamp_path
[PATH_MAX
];
2599 struct stat sb
, timestamp
;
2602 ret
= build_path(archive_path
, sizeof (archive_path
),
2603 root
, ARCHIVE_PREFIX
, ARCHIVE_SUFFIX
);
2605 if (ret
>= sizeof (archive_path
)) {
2606 bam_error(_("unable to create path on mountpoint %s, "
2607 "path too long\n"), rootbuf
);
2611 if (stat(archive_path
, &sb
) != 0) {
2612 if (bam_verbose
&& !bam_check
)
2613 bam_print(_("archive not found: %s\n"), archive_path
);
2614 set_dir_flag(NEED_UPDATE
| NO_EXTEND
);
2615 return (BAM_SUCCESS
);
2619 * The timestamp file is used to prevent stale files in the archive
2621 * Stale files can happen if the system is booted back and forth across
2622 * the transition from bootadm-before-the-cache to
2623 * bootadm-after-the-cache, since older versions of bootadm don't know
2624 * about the existence of the archive cache.
2626 * Since only bootadm-after-the-cache versions know about about this
2627 * file, we require that the boot archive be older than this file.
2629 ret
= snprintf(timestamp_path
, sizeof (timestamp_path
), "%s%s", root
,
2630 FILE_STAT_TIMESTAMP
);
2632 if (ret
>= sizeof (timestamp_path
)) {
2633 bam_error(_("unable to create path on mountpoint %s, "
2634 "path too long\n"), rootbuf
);
2638 if (stat(timestamp_path
, ×tamp
) != 0 ||
2639 sb
.st_mtime
> timestamp
.st_mtime
) {
2640 if (bam_verbose
&& !bam_check
)
2642 _("archive cache is out of sync. Rebuilding.\n"));
2644 * Don't generate a false positive for the boot-archive service
2645 * but trigger an update of the archive cache in
2646 * boot-archive-update.
2648 if (bam_smf_check
) {
2649 (void) creat(NEED_UPDATE_FILE
, 0644);
2650 return (BAM_SUCCESS
);
2653 set_flag(INVALIDATE_CACHE
);
2654 set_dir_flag(NEED_UPDATE
| NO_EXTEND
);
2655 return (BAM_SUCCESS
);
2658 if (is_flag_on(IS_SPARC_TARGET
))
2659 return (BAM_SUCCESS
);
2661 if (bam_extend
&& sb
.st_size
> BA_SIZE_MAX
) {
2662 if (bam_verbose
&& !bam_check
)
2663 bam_print(_("archive %s is bigger than %d bytes and "
2664 "will be rebuilt\n"), archive_path
, BA_SIZE_MAX
);
2665 set_dir_flag(NO_EXTEND
);
2668 return (BAM_SUCCESS
);
2672 * Check flags and presence of required files and directories.
2673 * The force flag and/or absence of files should
2674 * trigger an update.
2675 * Suppress stdout output if check (-n) option is set
2676 * (as -n should only produce parseable output.)
2679 check_flags_and_files(char *root
)
2686 * If archive is missing, create archive
2688 ret
= is_valid_archive(root
);
2689 if (ret
== BAM_ERROR
)
2693 return (BAM_SUCCESS
);
2696 * check if cache directories exist on x86.
2697 * check (and always open) the cache file on SPARC.
2700 ret
= snprintf(get_cachedir(),
2701 sizeof (get_cachedir()), "%s%s%s/%s", root
,
2702 ARCHIVE_PREFIX
, get_machine(), CACHEDIR_SUFFIX
);
2704 if (ret
>= sizeof (get_cachedir())) {
2705 bam_error(_("unable to create path on mountpoint %s, "
2706 "path too long\n"), rootbuf
);
2710 if (stat(get_cachedir(), &sb
) != 0) {
2711 set_flag(NEED_CACHE_DIR
);
2712 set_dir_flag(NEED_UPDATE
);
2715 walk_arg
.sparcfile
= fopen(get_cachedir(), "w");
2716 if (walk_arg
.sparcfile
== NULL
) {
2717 bam_error(_("failed to open file: %s: %s\n"),
2718 get_cachedir(), strerror(errno
));
2724 if (set_cache_dir(root
) != 0)
2729 if (set_update_dir(root
) != 0)
2734 * if force, create archive unconditionally
2737 set_dir_flag(NEED_UPDATE
);
2739 bam_print(_("forced update of archive requested\n"));
2740 return (BAM_SUCCESS
);
2743 return (BAM_SUCCESS
);
2747 read_one_list(char *root
, filelist_t
*flistp
, char *filelist
)
2749 char path
[PATH_MAX
];
2751 char buf
[BAM_MAXLINE
];
2752 const char *fcn
= "read_one_list()";
2754 (void) snprintf(path
, sizeof (path
), "%s%s", root
, filelist
);
2756 fp
= fopen(path
, "r");
2758 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2759 fcn
, path
, strerror(errno
)));
2762 while (s_fgets(buf
, sizeof (buf
), fp
) != NULL
) {
2763 /* skip blank lines */
2764 if (strspn(buf
, " \t") == strlen(buf
))
2766 append_to_flist(flistp
, buf
);
2768 if (fclose(fp
) != 0) {
2769 bam_error(_("failed to close file: %s: %s\n"),
2770 path
, strerror(errno
));
2773 return (BAM_SUCCESS
);
2777 read_list(char *root
, filelist_t
*flistp
)
2779 char path
[PATH_MAX
];
2783 const char *fcn
= "read_list()";
2785 flistp
->head
= flistp
->tail
= NULL
;
2788 * build and check path to extract_boot_filelist.ksh
2790 n
= snprintf(path
, sizeof (path
), "%s%s", root
, EXTRACT_BOOT_FILELIST
);
2791 if (n
>= sizeof (path
)) {
2792 bam_error(_("archive filelist is empty\n"));
2796 if (is_safe_exec(path
) == BAM_ERROR
)
2800 * If extract_boot_filelist is present, exec it, otherwise read
2801 * the filelists directly, for compatibility with older images.
2803 if (stat(path
, &sb
) == 0) {
2805 * build arguments to exec extract_boot_filelist.ksh
2807 char *rootarg
, *platarg
;
2808 int platarglen
= 1, rootarglen
= 1;
2809 if (strlen(root
) > 1)
2810 rootarglen
+= strlen(root
) + strlen("-R ");
2811 if (bam_alt_platform
)
2812 platarglen
+= strlen(bam_platform
) + strlen("-p ");
2813 platarg
= s_calloc(1, platarglen
);
2814 rootarg
= s_calloc(1, rootarglen
);
2818 if (strlen(root
) > 1) {
2819 (void) snprintf(rootarg
, rootarglen
,
2822 if (bam_alt_platform
) {
2823 (void) snprintf(platarg
, platarglen
,
2824 "-p %s", bam_platform
);
2826 n
= snprintf(cmd
, sizeof (cmd
), "%s %s %s /%s /%s",
2827 path
, rootarg
, platarg
, BOOT_FILE_LIST
, ETC_FILE_LIST
);
2830 if (n
>= sizeof (cmd
)) {
2831 bam_error(_("archive filelist is empty\n"));
2834 if (exec_cmd(cmd
, flistp
) != 0) {
2835 BAM_DPRINTF(("%s: failed to open archive "
2836 "filelist: %s: %s\n", fcn
, path
, strerror(errno
)));
2841 * Read current lists of files - only the first is mandatory
2843 rval
= read_one_list(root
, flistp
, BOOT_FILE_LIST
);
2844 if (rval
!= BAM_SUCCESS
)
2846 (void) read_one_list(root
, flistp
, ETC_FILE_LIST
);
2849 if (flistp
->head
== NULL
) {
2850 bam_error(_("archive filelist is empty\n"));
2854 return (BAM_SUCCESS
);
2858 getoldstat(char *root
)
2860 char path
[PATH_MAX
];
2865 (void) snprintf(path
, sizeof (path
), "%s%s", root
, FILE_STAT
);
2866 fd
= open(path
, O_RDONLY
);
2869 bam_print(_("failed to open file: %s: %s\n"),
2870 path
, strerror(errno
));
2874 if (fstat(fd
, &sb
) != 0) {
2875 bam_error(_("stat of file failed: %s: %s\n"), path
,
2880 ostat
= s_calloc(1, sb
.st_size
);
2882 if (read(fd
, ostat
, sb
.st_size
) != sb
.st_size
) {
2883 bam_error(_("read failed for file: %s: %s\n"), path
,
2892 walk_arg
.old_nvlp
= NULL
;
2893 error
= nvlist_unpack(ostat
, sb
.st_size
, &walk_arg
.old_nvlp
, 0);
2898 bam_error(_("failed to unpack stat data: %s: %s\n"),
2899 path
, strerror(error
));
2900 walk_arg
.old_nvlp
= NULL
;
2909 set_dir_flag(NEED_UPDATE
);
2912 /* Best effort stale entry removal */
2914 delete_stale(char *file
)
2916 char path
[PATH_MAX
];
2919 (void) snprintf(path
, sizeof (path
), "%s/%s", get_cachedir(), file
);
2920 if (!bam_check
&& stat(path
, &sb
) == 0) {
2921 if (sb
.st_mode
& S_IFDIR
)
2922 (void) rmdir_r(path
);
2924 (void) unlink(path
);
2926 set_dir_flag(NEED_UPDATE
| NO_EXTEND
);
2931 * Checks if a file in the current (old) archive has
2932 * been deleted from the root filesystem. This is needed for
2933 * software like Trusted Extensions (TX) that switch early
2934 * in boot based on presence/absence of a kernel module.
2937 check4stale(char *root
)
2942 char path
[PATH_MAX
];
2945 * Skip stale file check during smf check
2951 * If we need to (re)create the cache, there's no need to check for
2954 if (is_flag_on(NEED_CACHE_DIR
))
2957 /* Nothing to do if no old stats */
2958 if ((nvlp
= walk_arg
.old_nvlp
) == NULL
)
2961 for (nvp
= nvlist_next_nvpair(nvlp
, NULL
); nvp
;
2962 nvp
= nvlist_next_nvpair(nvlp
, nvp
)) {
2963 file
= nvpair_name(nvp
);
2966 (void) snprintf(path
, sizeof (path
), "%s/%s",
2968 if (access(path
, F_OK
) < 0) {
2970 bam_print(_(" stale %s\n"), path
);
2972 if (is_flag_on(IS_SPARC_TARGET
)) {
2973 set_dir_flag(NEED_UPDATE
);
2983 create_newstat(void)
2987 error
= nvlist_alloc(&walk_arg
.new_nvlp
, NV_UNIQUE_NAME
, 0);
2990 * Not fatal - we can still create archive
2992 walk_arg
.new_nvlp
= NULL
;
2993 bam_error(_("failed to create stat data: %s\n"),
2999 walk_list(char *root
, filelist_t
*flistp
)
3001 char path
[PATH_MAX
];
3004 for (lp
= flistp
->head
; lp
; lp
= lp
->next
) {
3006 * Don't follow symlinks. A symlink must refer to
3007 * a file that would appear in the archive through
3008 * a direct reference. This matches the archive
3009 * construction behavior.
3011 (void) snprintf(path
, sizeof (path
), "%s%s", root
, lp
->line
);
3012 if (nftw(path
, cmpstat
, 20, FTW_PHYS
) == -1) {
3013 if (is_flag_on(UPDATE_ERROR
))
3016 * Some files may not exist.
3017 * For example: etc/rtc_config on a x86 diskless system
3018 * Emit verbose message only
3021 bam_print(_("cannot find: %s: %s\n"),
3022 path
, strerror(errno
));
3026 return (BAM_SUCCESS
);
3030 * Update the timestamp file.
3033 update_timestamp(char *root
)
3035 char timestamp_path
[PATH_MAX
];
3037 /* this path length has already been checked in check_flags_and_files */
3038 (void) snprintf(timestamp_path
, sizeof (timestamp_path
), "%s%s", root
,
3039 FILE_STAT_TIMESTAMP
);
3042 * recreate the timestamp file. Since an outdated or absent timestamp
3043 * file translates in a complete rebuild of the archive cache, notify
3044 * the user of the performance issue.
3046 if (creat(timestamp_path
, FILE_STAT_MODE
) < 0) {
3047 bam_error(_("failed to open file: %s: %s\n"), timestamp_path
,
3049 bam_error(_("failed to update the timestamp file, next"
3050 " archive update may experience reduced performance\n"));
3058 char path
[PATH_MAX
];
3059 char path2
[PATH_MAX
];
3062 int fd
, wrote
, error
;
3066 error
= nvlist_pack(walk_arg
.new_nvlp
, &nstat
, &sz
,
3069 bam_error(_("failed to pack stat data: %s\n"),
3074 (void) snprintf(path
, sizeof (path
), "%s%s", root
, FILE_STAT_TMP
);
3075 fd
= open(path
, O_RDWR
|O_CREAT
|O_TRUNC
, FILE_STAT_MODE
);
3077 bam_error(_("failed to open file: %s: %s\n"), path
,
3082 wrote
= write(fd
, nstat
, sz
);
3084 bam_error(_("write to file failed: %s: %s\n"), path
,
3093 (void) snprintf(path2
, sizeof (path2
), "%s%s", root
, FILE_STAT
);
3094 if (rename(path
, path2
) != 0) {
3095 bam_error(_("rename to file failed: %s: %s\n"), path2
,
3100 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3103 clear_walk_args(void)
3105 nvlist_free(walk_arg
.old_nvlp
);
3106 nvlist_free(walk_arg
.new_nvlp
);
3107 if (walk_arg
.sparcfile
)
3108 (void) fclose(walk_arg
.sparcfile
);
3109 walk_arg
.old_nvlp
= NULL
;
3110 walk_arg
.new_nvlp
= NULL
;
3111 walk_arg
.sparcfile
= NULL
;
3116 * 0 - no update necessary
3117 * 1 - update required.
3118 * BAM_ERROR (-1) - An error occurred
3120 * Special handling for check (-n):
3121 * ================================
3122 * The check (-n) option produces parseable output.
3123 * To do this, we suppress all stdout messages unrelated
3124 * to out of sync files.
3125 * All stderr messages are still printed though.
3129 update_required(char *root
)
3132 char path
[PATH_MAX
];
3134 filelist_t
*flistp
= &flist
;
3137 flistp
->head
= flistp
->tail
= NULL
;
3140 set_flag(IS_SPARC_TARGET
);
3143 * Check if cache directories and archives are present
3146 ret
= check_flags_and_files(root
);
3151 * In certain deployment scenarios, filestat may not
3152 * exist. Do not stop the boot process, but trigger an update
3153 * of the archives (which will recreate filestat.ramdisk).
3155 if (bam_smf_check
) {
3156 (void) snprintf(path
, sizeof (path
), "%s%s", root
, FILE_STAT
);
3157 if (stat(path
, &sb
) != 0) {
3158 (void) creat(NEED_UPDATE_FILE
, 0644);
3166 * Check if the archive contains files that are no longer
3167 * present on the root filesystem.
3172 * read list of files
3174 if (read_list(root
, flistp
) != BAM_SUCCESS
) {
3179 assert(flistp
->head
&& flistp
->tail
);
3182 * At this point either the update is required
3183 * or the decision is pending. In either case
3184 * we need to create new stat nvlist
3188 * This walk does 2 things:
3189 * - gets new stat data for every file
3190 * - (optional) compare old and new stat data
3192 ret
= walk_list(root
, &flist
);
3194 /* done with the file list */
3195 filelist_free(flistp
);
3197 /* something went wrong */
3199 if (ret
== BAM_ERROR
) {
3200 bam_error(_("Failed to gather cache files, archives "
3201 "generation aborted\n"));
3205 if (walk_arg
.new_nvlp
== NULL
) {
3206 if (walk_arg
.sparcfile
!= NULL
)
3207 (void) fclose(walk_arg
.sparcfile
);
3208 bam_error(_("cannot create new stat data\n"));
3211 /* If nothing was updated, discard newstat. */
3213 if (!is_dir_flag_on(NEED_UPDATE
)) {
3218 if (walk_arg
.sparcfile
!= NULL
)
3219 (void) fclose(walk_arg
.sparcfile
);
3227 char cmd
[PATH_MAX
+ 30];
3229 (void) snprintf(cmd
, sizeof (cmd
), "%s -f \"%s\" 2>/dev/null",
3232 return (exec_cmd(cmd
, NULL
));
3236 do_archive_copy(char *source
, char *dest
)
3241 /* the equivalent of mv archive-new-$pid boot_archive */
3242 if (rename(source
, dest
) != 0) {
3243 (void) unlink(source
);
3247 if (flushfs(bam_root
) != 0)
3250 return (BAM_SUCCESS
);
3254 check_cmdline(filelist_t flist
)
3258 for (lp
= flist
.head
; lp
; lp
= lp
->next
) {
3259 if (strstr(lp
->line
, "Error:") != NULL
||
3260 strstr(lp
->line
, "Inode number overflow") != NULL
) {
3261 (void) fprintf(stderr
, "%s\n", lp
->line
);
3266 return (BAM_SUCCESS
);
3270 dump_errormsg(filelist_t flist
)
3274 for (lp
= flist
.head
; lp
; lp
= lp
->next
)
3275 (void) fprintf(stderr
, "%s\n", lp
->line
);
3279 check_archive(char *dest
)
3283 if (stat(dest
, &sb
) != 0 || !S_ISREG(sb
.st_mode
) ||
3284 sb
.st_size
< 10000) {
3285 bam_error(_("archive file %s not generated correctly\n"), dest
);
3286 (void) unlink(dest
);
3290 return (BAM_SUCCESS
);
3297 libzfs_handle_t
*hdl
;
3298 be_node_list_t
*be_nodes
= NULL
;
3299 be_node_list_t
*cur_be
;
3300 boolean_t be_exist
= B_FALSE
;
3301 char ds_path
[ZFS_MAX_DATASET_NAME_LEN
];
3306 * Get dataset for mountpoint
3308 if ((hdl
= libzfs_init()) == NULL
)
3311 if ((zhp
= zfs_path_to_zhandle(hdl
, root
,
3312 ZFS_TYPE_FILESYSTEM
)) == NULL
) {
3317 (void) strlcpy(ds_path
, zfs_get_name(zhp
), sizeof (ds_path
));
3320 * Check if the current dataset is BE
3322 if (be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
) == BE_SUCCESS
) {
3323 for (cur_be
= be_nodes
; cur_be
!= NULL
;
3324 cur_be
= cur_be
->be_next_node
) {
3327 * Because we guarantee that cur_be->be_root_ds
3328 * is null-terminated by internal data structure,
3329 * we can safely use strcmp()
3331 if (strcmp(ds_path
, cur_be
->be_root_ds
) == 0) {
3336 be_free_list(be_nodes
);
3345 * Returns B_TRUE if mkiso is in the expected PATH and should be used,
3351 scf_simple_prop_t
*prop
;
3352 char *format
= NULL
;
3355 /* Check whether the mkisofs binary is in the expected location */
3356 if (access(MKISOFS_PATH
, X_OK
) != 0) {
3358 bam_print("mkisofs not found\n");
3362 if (bam_format
== BAM_FORMAT_HSFS
) {
3364 bam_print("-F specified HSFS");
3368 /* If working on an alt-root, do not use HSFS unless asked via -F */
3373 * Then check that the system/boot-archive config/format property
3374 * is "hsfs" or empty.
3376 if ((prop
= scf_simple_prop_get(NULL
, BOOT_ARCHIVE_FMRI
, SCF_PG_CONFIG
,
3377 SCF_PROPERTY_FORMAT
)) == NULL
) {
3378 /* Could not find property, use mkisofs */
3381 "%s does not have %s/%s property, using mkisofs\n",
3382 BOOT_ARCHIVE_FMRI
, SCF_PG_CONFIG
,
3383 SCF_PROPERTY_FORMAT
);
3387 if (scf_simple_prop_numvalues(prop
) < 0 ||
3388 (format
= scf_simple_prop_next_astring(prop
)) == NULL
)
3391 ret
= strcmp(format
, "hsfs") == 0 ? B_TRUE
: B_FALSE
;
3394 bam_print("Creating hsfs boot archive\n");
3396 bam_print("Creating %s boot archive\n", format
);
3398 scf_simple_prop_free(prop
);
3402 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3405 create_sparc_archive(char *archive
, char *tempname
, char *bootblk
, char *list
)
3408 char cmdline
[3 * PATH_MAX
+ 64];
3409 filelist_t flist
= {0};
3410 const char *func
= "create_sparc_archive()";
3412 if (access(bootblk
, R_OK
) == 1) {
3413 bam_error(_("unable to access bootblk file : %s\n"), bootblk
);
3418 * Prepare mkisofs command line and execute it
3420 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s -G %s -o \"%s\" "
3421 "-path-list \"%s\" 2>&1", MKISOFS_PATH
, MKISO_PARAMS
, bootblk
,
3424 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3426 ret
= exec_cmd(cmdline
, &flist
);
3427 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3428 dump_errormsg(flist
);
3432 filelist_free(&flist
);
3435 * Prepare dd command line to copy the bootblk on the new archive and
3438 (void) snprintf(cmdline
, sizeof (cmdline
), "%s if=\"%s\" of=\"%s\""
3439 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR
,
3442 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3444 ret
= exec_cmd(cmdline
, &flist
);
3445 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
)
3448 filelist_free(&flist
);
3450 /* Did we get a valid archive ? */
3451 if (check_archive(tempname
) == BAM_ERROR
)
3454 return (do_archive_copy(tempname
, archive
));
3457 filelist_free(&flist
);
3458 bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline
);
3459 (void) unlink(tempname
);
3464 from_733(unsigned char *s
)
3467 unsigned int ret
= 0;
3469 for (i
= 0; i
< 4; i
++)
3470 ret
|= s
[i
] << (8 * i
);
3476 to_733(unsigned char *s
, unsigned int val
)
3480 for (i
= 0; i
< 4; i
++)
3481 s
[i
] = s
[7-i
] = (val
>> (8 * i
)) & 0xFF;
3485 * creates sha1 hash of archive
3488 digest_archive(const char *archive
)
3495 (void) asprintf(&archive_hash
, "%s.hash", archive
);
3496 if (archive_hash
== NULL
)
3499 if ((ret
= bootadm_digest(archive
, &hash
)) == BAM_ERROR
) {
3504 fp
= fopen(archive_hash
, "w");
3511 (void) fprintf(fp
, "%s\n", hash
);
3515 return (BAM_SUCCESS
);
3519 * Extends the current boot archive without recreating it from scratch
3522 extend_iso_archive(char *archive
, char *tempname
, char *update_dir
)
3524 int fd
= -1, newfd
= -1, ret
, i
;
3525 int next_session
= 0, new_size
= 0;
3526 char cmdline
[3 * PATH_MAX
+ 64];
3527 const char *func
= "extend_iso_archive()";
3528 filelist_t flist
= {0};
3529 struct iso_pdesc saved_desc
[MAX_IVDs
];
3531 fd
= open(archive
, O_RDWR
);
3534 bam_error(_("failed to open file: %s: %s\n"),
3535 archive
, strerror(errno
));
3540 * A partial read is likely due to a corrupted file
3542 ret
= pread64(fd
, saved_desc
, sizeof (saved_desc
),
3543 VOLDESC_OFF
* CD_BLOCK
);
3544 if (ret
!= sizeof (saved_desc
)) {
3546 bam_error(_("read failed for file: %s: %s\n"),
3547 archive
, strerror(errno
));
3551 if (memcmp(saved_desc
[0].type
, "\1CD001", 6)) {
3553 bam_error(_("iso descriptor signature for %s is "
3554 "invalid\n"), archive
);
3559 * Read primary descriptor and locate next_session offset (it should
3560 * point to the end of the archive)
3562 next_session
= P2ROUNDUP(from_733(saved_desc
[0].volume_space_size
), 16);
3564 (void) snprintf(cmdline
, sizeof (cmdline
), "%s -C 16,%d -M %s %s -o \""
3565 "%s\" \"%s\" 2>&1", MKISOFS_PATH
, next_session
, archive
,
3566 MKISO_PARAMS
, tempname
, update_dir
);
3568 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3570 ret
= exec_cmd(cmdline
, &flist
);
3571 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3573 bam_error(_("Command '%s' failed while generating "
3574 "multisession archive\n"), cmdline
);
3575 dump_errormsg(flist
);
3579 filelist_free(&flist
);
3581 newfd
= open(tempname
, O_RDONLY
);
3584 bam_error(_("failed to open file: %s: %s\n"),
3585 archive
, strerror(errno
));
3589 ret
= pread64(newfd
, saved_desc
, sizeof (saved_desc
),
3590 VOLDESC_OFF
* CD_BLOCK
);
3591 if (ret
!= sizeof (saved_desc
)) {
3593 bam_error(_("read failed for file: %s: %s\n"),
3594 archive
, strerror(errno
));
3598 if (memcmp(saved_desc
[0].type
, "\1CD001", 6)) {
3600 bam_error(_("iso descriptor signature for %s is "
3601 "invalid\n"), archive
);
3605 new_size
= from_733(saved_desc
[0].volume_space_size
) + next_session
;
3606 to_733(saved_desc
[0].volume_space_size
, new_size
);
3608 for (i
= 1; i
< MAX_IVDs
; i
++) {
3609 if (saved_desc
[i
].type
[0] == (unsigned char)255)
3611 if (memcmp(saved_desc
[i
].id
, "CD001", 5))
3615 bam_print("%s: Updating descriptor entry [%d]\n", func
,
3618 to_733(saved_desc
[i
].volume_space_size
, new_size
);
3621 ret
= pwrite64(fd
, saved_desc
, DVD_BLOCK
, VOLDESC_OFF
*CD_BLOCK
);
3622 if (ret
!= DVD_BLOCK
) {
3624 bam_error(_("write to file failed: %s: %s\n"),
3625 archive
, strerror(errno
));
3628 (void) close(newfd
);
3638 bam_error(_("failed to close file: %s: %s\n"),
3639 archive
, strerror(errno
));
3644 (void) snprintf(cmdline
, sizeof (cmdline
), "%s if=%s of=%s bs=32k "
3645 "seek=%d conv=sync 2>&1", DD_PATH_USR
, tempname
, archive
,
3648 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3650 ret
= exec_cmd(cmdline
, &flist
);
3651 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3653 bam_error(_("Command '%s' failed while generating "
3654 "multisession archive\n"), cmdline
);
3657 filelist_free(&flist
);
3659 (void) unlink(tempname
);
3661 if (digest_archive(archive
) == BAM_ERROR
&& bam_verbose
)
3662 bam_print("boot archive hashing failed\n");
3664 if (flushfs(bam_root
) != 0)
3668 bam_print("boot archive updated successfully\n");
3670 return (BAM_SUCCESS
);
3673 filelist_free(&flist
);
3678 (void) close(newfd
);
3683 create_x86_archive(char *archive
, char *tempname
, char *update_dir
)
3686 char cmdline
[3 * PATH_MAX
+ 64];
3687 filelist_t flist
= {0};
3688 const char *func
= "create_x86_archive()";
3690 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s -o \"%s\" \"%s\" "
3691 "2>&1", MKISOFS_PATH
, MKISO_PARAMS
, tempname
, update_dir
);
3693 BAM_DPRINTF(("%s: executing: %s\n", func
, cmdline
));
3695 ret
= exec_cmd(cmdline
, &flist
);
3696 if (ret
!= 0 || check_cmdline(flist
) == BAM_ERROR
) {
3697 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3699 dump_errormsg(flist
);
3700 filelist_free(&flist
);
3701 (void) unlink(tempname
);
3705 filelist_free(&flist
);
3707 if (check_archive(tempname
) == BAM_ERROR
)
3710 return (do_archive_copy(tempname
, archive
));
3714 mkisofs_archive(char *root
)
3718 char temp
[PATH_MAX
];
3719 char bootblk
[PATH_MAX
];
3720 char boot_archive
[PATH_MAX
];
3722 ret
= snprintf(suffix
, sizeof (suffix
), "/archive-new-%d", getpid());
3723 if (ret
>= sizeof (suffix
))
3726 ret
= build_path(temp
, sizeof (temp
), root
, ARCHIVE_PREFIX
, suffix
);
3728 if (ret
>= sizeof (temp
))
3731 ret
= build_path(boot_archive
, sizeof (boot_archive
), root
,
3732 ARCHIVE_PREFIX
, ARCHIVE_SUFFIX
);
3734 if (ret
>= sizeof (boot_archive
))
3737 bam_print("updating %s (HSFS)\n",
3738 boot_archive
[1] == '/' ? boot_archive
+ 1 : boot_archive
);
3740 if (is_flag_on(IS_SPARC_TARGET
)) {
3741 ret
= snprintf(bootblk
, sizeof (bootblk
),
3742 "%s/platform/%s/lib/fs/hsfs/bootblk", root
, get_machine());
3743 if (ret
>= sizeof (bootblk
))
3746 ret
= create_sparc_archive(boot_archive
, temp
, bootblk
,
3749 if (!is_dir_flag_on(NO_EXTEND
)) {
3751 bam_print("Attempting to extend x86 archive: "
3752 "%s\n", boot_archive
);
3754 ret
= extend_iso_archive(boot_archive
, temp
,
3756 if (ret
== BAM_SUCCESS
) {
3758 bam_print("Successfully extended %s\n",
3761 (void) rmdir_r(get_updatedir());
3762 return (BAM_SUCCESS
);
3766 * The boot archive will be recreated from scratch. We get here
3767 * if at least one of these conditions is true:
3768 * - bootadm was called without the -e switch
3769 * - the archive (or the archive cache) doesn't exist
3770 * - archive size is bigger than BA_SIZE_MAX
3771 * - more than COUNT_MAX files need to be updated
3772 * - an error occourred either populating the /updates directory
3773 * or extend_iso_archive() failed
3776 bam_print("Unable to extend %s... rebuilding archive\n",
3779 if (get_updatedir()[0] != '\0')
3780 (void) rmdir_r(get_updatedir());
3783 ret
= create_x86_archive(boot_archive
, temp
,
3787 if (digest_archive(boot_archive
) == BAM_ERROR
&& bam_verbose
)
3788 bam_print("boot archive hashing failed\n");
3790 if (ret
== BAM_SUCCESS
&& bam_verbose
)
3791 bam_print("Successfully created %s\n", boot_archive
);
3796 bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3802 assemble_systemfile(char *infilename
, char *outfilename
)
3805 FILE *infile
, *outfile
;
3808 if ((infile
= fopen(infilename
, "r")) == NULL
) {
3809 bam_error(_("failed to open file: %s: %s\n"), infilename
,
3814 if ((outfile
= fopen(outfilename
, "a")) == NULL
) {
3815 bam_error(_("failed to open file: %s: %s\n"), outfilename
,
3817 (void) fclose(infile
);
3821 while ((n
= fread(buf
, 1, sizeof (buf
), infile
)) > 0) {
3822 if (fwrite(buf
, 1, n
, outfile
) != n
) {
3823 bam_error(_("failed to write file: %s: %s\n"),
3824 outfilename
, strerror(errno
));
3825 (void) fclose(infile
);
3826 (void) fclose(outfile
);
3831 (void) fclose(infile
);
3832 (void) fclose(outfile
);
3834 return (BAM_SUCCESS
);
3838 * Concatenate all files (except those starting with a dot)
3839 * from /etc/system.d directory into a single /etc/system.d/.self-assembly
3840 * file. The kernel reads it before /etc/system file.
3843 build_etc_system_dir(char *root
)
3845 struct dirent
**filelist
;
3846 char path
[PATH_MAX
], tmpfile
[PATH_MAX
];
3847 int i
, files
, sysfiles
= 0;
3848 int ret
= BAM_SUCCESS
;
3850 timespec_t times
[2];
3852 (void) snprintf(path
, sizeof (path
), "%s/%s", root
, ETC_SYSTEM_DIR
);
3853 (void) snprintf(self_assembly
, sizeof (self_assembly
),
3854 "%s%s", root
, SELF_ASSEMBLY
);
3855 (void) snprintf(tmpfile
, sizeof (tmpfile
), "%s.%ld",
3856 self_assembly
, (long)getpid());
3858 if (stat(self_assembly
, &st
) >= 0 && (st
.st_mode
& S_IFMT
) == S_IFREG
) {
3859 times
[0] = times
[1] = st
.st_mtim
;
3861 times
[1].tv_nsec
= 0;
3864 if ((files
= scandir(path
, &filelist
, NULL
, alphasort
)) < 0) {
3865 /* Don't fail the update if <ROOT>/etc/system.d doesn't exist */
3866 if (errno
== ENOENT
)
3867 return (BAM_SUCCESS
);
3868 bam_error(_("can't read %s: %s\n"), path
, strerror(errno
));
3872 (void) unlink(tmpfile
);
3874 for (i
= 0; i
< files
; i
++) {
3875 char filepath
[PATH_MAX
];
3878 fname
= filelist
[i
]->d_name
;
3880 /* skip anything that starts with a dot */
3881 if (strncmp(fname
, ".", 1) == 0) {
3887 bam_print(_("/etc/system.d adding %s/%s\n"),
3890 (void) snprintf(filepath
, sizeof (filepath
), "%s/%s",
3893 if ((assemble_systemfile(filepath
, tmpfile
)) < 0) {
3894 bam_error(_("failed to append file: %s: %s\n"),
3895 filepath
, strerror(errno
));
3903 if (rename(tmpfile
, self_assembly
) < 0) {
3904 bam_error(_("failed to rename file: %s: %s\n"), tmpfile
,
3910 * Use previous attribute times to avoid
3911 * boot archive recreation.
3913 if (times
[1].tv_nsec
!= 0 &&
3914 utimensat(AT_FDCWD
, self_assembly
, times
, 0) != 0) {
3915 bam_error(_("failed to change times: %s\n"),
3920 (void) unlink(tmpfile
);
3921 (void) unlink(self_assembly
);
3927 create_ramdisk(char *root
)
3929 char *cmdline
, path
[PATH_MAX
];
3932 int ret
, status
= BAM_SUCCESS
;
3934 /* If mkisofs should be used, use it to create the required archives */
3935 if (use_mkisofs()) {
3936 if (has_cachedir() && is_dir_flag_on(NEED_UPDATE
)) {
3937 ret
= mkisofs_archive(root
);
3942 } else if (bam_format
== BAM_FORMAT_HSFS
) {
3943 bam_error(_("cannot create hsfs archive\n"));
3948 * Else setup command args for create_ramdisk.ksh for the archive
3949 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3952 (void) snprintf(path
, sizeof (path
), "%s/%s", root
, CREATE_RAMDISK
);
3953 if (stat(path
, &sb
) != 0) {
3954 bam_error(_("archive creation file not found: %s: %s\n"),
3955 path
, strerror(errno
));
3959 if (is_safe_exec(path
) == BAM_ERROR
)
3962 len
= strlen(path
) + strlen(root
) + 10; /* room for space + -R */
3963 if (bam_alt_platform
)
3964 len
+= strlen(bam_platform
) + strlen(" -p ");
3965 if (bam_format
!= BAM_FORMAT_UNSET
)
3966 len
+= strlen(bam_formats
[bam_format
]) + strlen(" -f ");
3967 cmdline
= s_calloc(1, len
);
3969 if (bam_alt_platform
) {
3970 assert(strlen(root
) > 1);
3971 (void) snprintf(cmdline
, len
, "%s -p %s -R %s",
3972 path
, bam_platform
, root
);
3973 /* chop off / at the end */
3974 cmdline
[strlen(cmdline
) - 1] = '\0';
3975 } else if (strlen(root
) > 1) {
3976 (void) snprintf(cmdline
, len
, "%s -R %s", path
, root
);
3977 /* chop off / at the end */
3978 cmdline
[strlen(cmdline
) - 1] = '\0';
3980 (void) snprintf(cmdline
, len
, "%s", path
);
3982 if (bam_format
!= BAM_FORMAT_UNSET
) {
3983 if (strlcat(cmdline
, " -f ", len
) >= len
||
3984 strlcat(cmdline
, bam_formats
[bam_format
], len
) >= len
) {
3985 bam_error(_("boot-archive command line too long\n"));
3991 if (exec_cmd(cmdline
, NULL
) != 0) {
3992 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3999 * The existence of the expected archives used to be
4000 * verified here. This check is done in create_ramdisk as
4001 * it needs to be in sync with the altroot operated upon.
4003 return (BAM_SUCCESS
);
4007 * Checks if target filesystem is on a ramdisk
4010 * When in doubt assume it is not a ramdisk.
4013 is_ramdisk(char *root
)
4015 struct extmnttab mnt
;
4018 char mntpt
[PATH_MAX
];
4022 * There are 3 situations where creating archive is
4024 * - create boot_archive on a lofi-mounted boot_archive
4025 * - create it on a ramdisk which is the root filesystem
4026 * - create it on a ramdisk mounted somewhere else
4027 * The first is not easy to detect and checking for it is not
4029 * The other two conditions are handled here
4031 fp
= fopen(MNTTAB
, "r");
4033 bam_error(_("failed to open file: %s: %s\n"),
4034 MNTTAB
, strerror(errno
));
4041 * Remove any trailing / from the mount point
4043 (void) strlcpy(mntpt
, root
, sizeof (mntpt
));
4044 if (strcmp(root
, "/") != 0) {
4045 cp
= mntpt
+ strlen(mntpt
) - 1;
4050 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
4051 if (strcmp(mnt
.mnt_mountp
, mntpt
) == 0) {
4059 bam_error(_("alternate root %s not in mnttab\n"),
4065 if (strncmp(mnt
.mnt_special
, RAMDISK_SPECIAL
,
4066 strlen(RAMDISK_SPECIAL
)) == 0) {
4068 bam_error(_("%s is on a ramdisk device\n"), bam_root
);
4079 is_boot_archive(char *root
)
4081 char path
[PATH_MAX
];
4084 const char *fcn
= "is_boot_archive()";
4087 * We can't create an archive without the create_ramdisk script
4089 (void) snprintf(path
, sizeof (path
), "%s/%s", root
, CREATE_RAMDISK
);
4090 error
= stat(path
, &sb
);
4091 INJECT_ERROR1("NOT_ARCHIVE_BASED", error
= -1);
4094 bam_print(_("file not found: %s\n"), path
);
4095 BAM_DPRINTF(("%s: not a boot archive based Solaris "
4096 "instance: %s\n", fcn
, root
));
4100 BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
4106 * Need to call this for anything that operates on the GRUB menu
4107 * In the x86 live upgrade case the directory /boot/grub may be present
4108 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
4109 * is to check for the presence of the stage2 binary which is present
4110 * only on GRUB targets (even on x86 boot partitions). Checking for the
4111 * presence of the multiboot binary is not correct as it is not present
4112 * on x86 boot partitions.
4115 is_grub(const char *root
)
4117 char path
[PATH_MAX
];
4120 boolean_t grub
= B_FALSE
;
4121 const char *res
= NULL
;
4122 const char *fcn
= "is_grub()";
4124 /* grub is disabled by default */
4125 if ((defp
= defopen_r(BE_DEFAULTS
)) == NULL
) {
4128 res
= defread_r(BE_DFLT_BE_HAS_GRUB
, defp
);
4129 if (res
!= NULL
&& res
[0] != '\0') {
4130 if (strcasecmp(res
, "true") == 0)
4136 if (grub
== B_TRUE
) {
4137 (void) snprintf(path
, sizeof (path
), "%s%s", root
, GRUB_STAGE2
);
4138 if (stat(path
, &sb
) == -1) {
4139 BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
4154 const char *fcn
= "is_zfs()";
4156 ret
= statvfs(root
, &vfs
);
4157 INJECT_ERROR1("STATVFS_ZFS", ret
= 1);
4159 bam_error(_("statvfs failed for %s: %s\n"), root
,
4164 if (strncmp(vfs
.f_basetype
, "zfs", strlen("zfs")) == 0) {
4165 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn
, root
));
4168 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn
, root
));
4178 const char *fcn
= "is_pcfs()";
4180 ret
= statvfs(root
, &vfs
);
4181 INJECT_ERROR1("STATVFS_PCFS", ret
= 1);
4183 bam_error(_("statvfs failed for %s: %s\n"), root
,
4188 if (strncmp(vfs
.f_basetype
, "pcfs", strlen("pcfs")) == 0) {
4189 BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn
, root
));
4192 BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4199 is_readonly(char *root
)
4203 char testfile
[PATH_MAX
];
4204 const char *fcn
= "is_readonly()";
4207 * Using statvfs() to check for a read-only filesystem is not
4208 * reliable. The only way to reliably test is to attempt to
4211 (void) snprintf(testfile
, sizeof (testfile
), "%s/%s.%d",
4212 root
, BOOTADM_RDONLY_TEST
, getpid());
4214 (void) unlink(testfile
);
4217 fd
= open(testfile
, O_RDWR
|O_CREAT
|O_EXCL
, 0644);
4219 INJECT_ERROR2("RDONLY_TEST_ERROR", fd
= -1, error
= EACCES
);
4220 if (fd
== -1 && error
== EROFS
) {
4221 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn
, root
));
4223 } else if (fd
== -1) {
4224 bam_error(_("error during read-only test on %s: %s\n"),
4225 root
, strerror(error
));
4229 (void) unlink(testfile
);
4231 BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn
, root
));
4236 update_archive(char *root
, char *opt
)
4241 assert(opt
== NULL
);
4247 * Never update non-BE root in update_all
4249 if (bam_update_all
&& !is_be(root
))
4250 return (BAM_SUCCESS
);
4252 * root must belong to a boot archive based OS,
4254 if (!is_boot_archive(root
)) {
4256 * Emit message only if not in context of update_all.
4257 * If in update_all, emit only if verbose flag is set.
4259 if (!bam_update_all
|| bam_verbose
)
4260 bam_print(_("%s: not a boot archive based Solaris "
4261 "instance\n"), root
);
4266 * If smf check is requested when / is writable (can happen
4267 * on first reboot following an upgrade because service
4268 * dependency is messed up), skip the check.
4270 if (bam_smf_check
&& !bam_root_readonly
&& !is_zfs(root
))
4271 return (BAM_SUCCESS
);
4274 * Don't generate archive on ramdisk.
4276 if (is_ramdisk(root
))
4277 return (BAM_SUCCESS
);
4280 * root must be writable. This check applies to alternate
4281 * root (-R option); bam_root_readonly applies to '/' only.
4282 * The behaviour translates into being the one of a 'check'.
4284 if (!bam_smf_check
&& !bam_check
&& is_readonly(root
)) {
4285 set_flag(RDONLY_FSCHK
);
4290 * Process the /etc/system.d/.self-assembly file.
4292 if (build_etc_system_dir(bam_root
) == BAM_ERROR
)
4296 * Now check if an update is really needed.
4298 ret
= update_required(root
);
4301 * The check command (-n) is *not* a dry run.
4302 * It only checks if the archive is in sync.
4303 * A readonly filesystem has to be considered an error only if an update
4306 if (bam_nowrite()) {
4307 if (is_flag_on(RDONLY_FSCHK
)) {
4308 bam_check
= bam_saved_check
;
4310 bam_error(_("%s filesystem is read-only, "
4311 "skipping archives update\n"), root
);
4313 return ((ret
!= 0) ? BAM_ERROR
: BAM_SUCCESS
);
4316 bam_exit((ret
!= 0) ? 1 : 0);
4320 /* create the ramdisk */
4321 ret
= create_ramdisk(root
);
4325 * if the archive is updated, save the new stat data and update the
4328 if (ret
== 0 && walk_arg
.new_nvlp
!= NULL
) {
4330 update_timestamp(root
);
4341 char *special
= get_special("/");
4344 if (special
== NULL
)
4347 if (*special
== '/') {
4352 if ((p
= strchr(special
, '/')) != NULL
)
4359 synchronize_BE_menu(void)
4362 char cmdline
[PATH_MAX
];
4363 char cksum_line
[PATH_MAX
];
4364 filelist_t flist
= {0};
4365 char *old_cksum_str
;
4368 char *curr_cksum_str
;
4369 char *curr_size_str
;
4377 const char *fcn
= "synchronize_BE_menu()";
4379 BAM_DPRINTF(("%s: entered. No args\n", fcn
));
4381 /* Check if findroot enabled LU BE */
4382 if (stat(FINDROOT_INSTALLGRUB
, &sb
) != 0) {
4383 BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn
));
4384 return (BAM_SUCCESS
);
4387 if (stat(LU_MENU_CKSUM
, &sb
) != 0) {
4388 BAM_DPRINTF(("%s: checksum file absent: %s\n",
4389 fcn
, LU_MENU_CKSUM
));
4393 cfp
= fopen(LU_MENU_CKSUM
, "r");
4394 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp
= NULL
);
4396 bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4400 BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn
, LU_MENU_CKSUM
));
4403 while (s_fgets(cksum_line
, sizeof (cksum_line
), cfp
) != NULL
) {
4404 INJECT_ERROR1("MULTIPLE_CKSUM", found
= 1);
4406 bam_error(_("multiple checksums for GRUB menu in "
4407 "checksum file: %s\n"), LU_MENU_CKSUM
);
4413 BAM_DPRINTF(("%s: read checksum file: %s\n", fcn
, LU_MENU_CKSUM
));
4416 old_cksum_str
= strtok(cksum_line
, " \t");
4417 old_size_str
= strtok(NULL
, " \t");
4418 old_file
= strtok(NULL
, " \t");
4420 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str
= NULL
);
4421 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str
= NULL
);
4422 INJECT_ERROR1("OLD_FILE_NULL", old_file
= NULL
);
4423 if (old_cksum_str
== NULL
|| old_size_str
== NULL
|| old_file
== NULL
) {
4424 bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4428 BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn
, LU_MENU_CKSUM
));
4430 /* Get checksum of current menu */
4431 pool
= find_root_pool();
4433 mntpt
= mount_top_dataset(pool
, &mnted
);
4434 if (mntpt
== NULL
) {
4435 bam_error(_("failed to mount top dataset for %s\n"),
4440 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s%s",
4441 CKSUM
, mntpt
, GRUB_MENU
);
4443 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s",
4446 ret
= exec_cmd(cmdline
, &flist
);
4448 (void) umount_top_dataset(pool
, mnted
, mntpt
);
4451 INJECT_ERROR1("GET_CURR_CKSUM", ret
= 1);
4453 bam_error(_("error generating checksum of GRUB menu\n"));
4456 BAM_DPRINTF(("%s: successfully generated checksum\n", fcn
));
4458 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist
.head
= NULL
);
4459 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
4460 bam_error(_("bad checksum generated for GRUB menu\n"));
4461 filelist_free(&flist
);
4464 BAM_DPRINTF(("%s: generated checksum output valid\n", fcn
));
4466 curr_cksum_str
= strtok(flist
.head
->line
, " \t");
4467 curr_size_str
= strtok(NULL
, " \t");
4468 curr_file
= strtok(NULL
, " \t");
4470 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str
= NULL
);
4471 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str
= NULL
);
4472 INJECT_ERROR1("CURR_FILE_NULL", curr_file
= NULL
);
4473 if (curr_cksum_str
== NULL
|| curr_size_str
== NULL
||
4474 curr_file
== NULL
) {
4475 bam_error(_("error parsing checksum generated "
4476 "for GRUB menu\n"));
4477 filelist_free(&flist
);
4480 BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn
));
4482 if (strcmp(old_cksum_str
, curr_cksum_str
) == 0 &&
4483 strcmp(old_size_str
, curr_size_str
) == 0 &&
4484 strcmp(old_file
, curr_file
) == 0) {
4485 filelist_free(&flist
);
4486 BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn
));
4487 return (BAM_SUCCESS
);
4490 filelist_free(&flist
);
4492 /* cksum doesn't match - the menu has changed */
4493 BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn
));
4496 bam_print(_("propagating updated GRUB menu\n"));
4498 (void) snprintf(cmdline
, sizeof (cmdline
),
4499 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4500 LULIB
, LULIB_PROPAGATE_FILE
, GRUB_MENU
);
4501 ret
= exec_cmd(cmdline
, NULL
);
4502 INJECT_ERROR1("PROPAGATE_MENU", ret
= 1);
4504 bam_error(_("error propagating updated GRUB menu\n"));
4507 BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn
));
4509 (void) snprintf(cmdline
, sizeof (cmdline
), "/bin/cp %s %s > /dev/null",
4510 GRUB_MENU
, GRUB_BACKUP_MENU
);
4511 ret
= exec_cmd(cmdline
, NULL
);
4512 INJECT_ERROR1("CREATE_BACKUP", ret
= 1);
4514 bam_error(_("failed to create backup for GRUB menu: %s\n"),
4518 BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4519 fcn
, GRUB_BACKUP_MENU
));
4521 (void) snprintf(cmdline
, sizeof (cmdline
),
4522 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4523 LULIB
, LULIB_PROPAGATE_FILE
, GRUB_BACKUP_MENU
);
4524 ret
= exec_cmd(cmdline
, NULL
);
4525 INJECT_ERROR1("PROPAGATE_BACKUP", ret
= 1);
4527 bam_error(_("error propagating backup GRUB menu: %s\n"),
4531 BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4532 fcn
, GRUB_BACKUP_MENU
));
4534 (void) snprintf(cmdline
, sizeof (cmdline
), "%s %s > %s",
4535 CKSUM
, GRUB_MENU
, LU_MENU_CKSUM
);
4536 ret
= exec_cmd(cmdline
, NULL
);
4537 INJECT_ERROR1("CREATE_CKSUM_FILE", ret
= 1);
4539 bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4543 BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4544 fcn
, LU_MENU_CKSUM
));
4546 (void) snprintf(cmdline
, sizeof (cmdline
),
4547 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4548 LULIB
, LULIB_PROPAGATE_FILE
, LU_MENU_CKSUM
);
4549 ret
= exec_cmd(cmdline
, NULL
);
4550 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret
= 1);
4552 bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4556 BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4557 fcn
, LU_MENU_CKSUM
));
4559 return (BAM_SUCCESS
);
4563 update_all(char *root
, char *opt
)
4565 struct extmnttab mnt
;
4568 char multibt
[PATH_MAX
];
4569 char creatram
[PATH_MAX
];
4570 error_t ret
= BAM_SUCCESS
;
4573 assert(opt
== NULL
);
4575 if (bam_rootlen
!= 1 || *root
!= '/') {
4576 elide_trailing_slash(root
, multibt
, sizeof (multibt
));
4577 bam_error(_("an alternate root (%s) cannot be used with this "
4578 "sub-command\n"), multibt
);
4583 * First update archive for current root
4585 if (update_archive(root
, opt
) != BAM_SUCCESS
)
4588 if (ret
== BAM_ERROR
)
4592 * Now walk the mount table, performing archive update
4593 * for all mounted Newboot root filesystems
4595 fp
= fopen(MNTTAB
, "r");
4597 bam_error(_("failed to open file: %s: %s\n"),
4598 MNTTAB
, strerror(errno
));
4605 while (getextmntent(fp
, &mnt
, sizeof (mnt
)) == 0) {
4606 if (mnt
.mnt_special
== NULL
)
4608 if ((strcmp(mnt
.mnt_fstype
, MNTTYPE_ZFS
) != 0) &&
4609 (strncmp(mnt
.mnt_special
, "/dev/", strlen("/dev/")) != 0))
4611 if (strcmp(mnt
.mnt_mountp
, "/") == 0)
4614 (void) snprintf(creatram
, sizeof (creatram
), "%s/%s",
4615 mnt
.mnt_mountp
, CREATE_RAMDISK
);
4617 if (stat(creatram
, &sb
) == -1)
4621 * We put a trailing slash to be consistent with root = "/"
4622 * case, such that we don't have to print // in some cases.
4624 (void) snprintf(rootbuf
, sizeof (rootbuf
), "%s/",
4626 bam_rootlen
= strlen(rootbuf
);
4629 * It's possible that other mounts may be an alternate boot
4630 * architecture, so check it again.
4632 if ((get_boot_cap(rootbuf
) != BAM_SUCCESS
) ||
4633 (update_archive(rootbuf
, opt
) != BAM_SUCCESS
))
4641 * We no longer use biosdev for Live Upgrade. Hence
4642 * there is no need to defer (to shutdown time) any fdisk
4645 if (stat(GRUB_fdisk
, &sb
) == 0 || stat(GRUB_fdisk_target
, &sb
) == 0) {
4646 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4647 "Not supported.\n"), GRUB_fdisk
, GRUB_fdisk_target
);
4651 * If user has updated menu in current BE, propagate the
4652 * updates to all BEs.
4654 if (sync_menu
&& synchronize_BE_menu() != BAM_SUCCESS
)
4661 append_line(menu_t
*mp
, line_t
*lp
)
4663 if (mp
->start
== NULL
) {
4673 unlink_line(menu_t
*mp
, line_t
*lp
)
4675 /* unlink from list */
4677 lp
->prev
->next
= lp
->next
;
4679 mp
->start
= lp
->next
;
4681 lp
->next
->prev
= lp
->prev
;
4687 boot_entry_new(menu_t
*mp
, line_t
*start
, line_t
*end
)
4689 entry_t
*ent
, *prev
;
4690 const char *fcn
= "boot_entry_new()";
4696 ent
= s_calloc(1, sizeof (entry_t
));
4697 BAM_DPRINTF(("%s: new boot entry alloced\n", fcn
));
4701 if (mp
->entries
== NULL
) {
4703 BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn
));
4712 BAM_DPRINTF(("%s: new boot entry linked in\n", fcn
));
4717 boot_entry_addline(entry_t
*ent
, line_t
*lp
)
4724 * Check whether cmd matches the one indexed by which, and whether arg matches
4725 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4726 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4727 * strstr(), so it can be a partial match.
4730 check_cmd(const char *cmd
, const int which
, const char *arg
, const char *str
)
4733 const char *fcn
= "check_cmd()";
4735 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, arg
, str
));
4738 if ((strcmp(cmd
, menu_cmds
[which
]) != 0) &&
4739 (strcmp(cmd
, menu_cmds
[which
+ 1]) != 0)) {
4740 BAM_DPRINTF(("%s: command %s does not match %s\n",
4741 fcn
, cmd
, menu_cmds
[which
]));
4744 ret
= (strstr(arg
, str
) != NULL
);
4749 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
4751 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
4758 kernel_parser(entry_t
*entry
, char *cmd
, char *arg
, int linenum
)
4760 const char *fcn
= "kernel_parser()";
4766 if (strcmp(cmd
, menu_cmds
[KERNEL_CMD
]) != 0 &&
4767 strcmp(cmd
, menu_cmds
[KERNEL_DOLLAR_CMD
]) != 0) {
4768 BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn
, cmd
));
4772 if (strncmp(arg
, DIRECT_BOOT_32
, sizeof (DIRECT_BOOT_32
) - 1) == 0) {
4773 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4775 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_32BIT
;
4776 } else if (strncmp(arg
, DIRECT_BOOT_KERNEL
,
4777 sizeof (DIRECT_BOOT_KERNEL
) - 1) == 0) {
4778 BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn
, arg
));
4779 entry
->flags
|= BAM_ENTRY_DBOOT
;
4780 } else if (strncmp(arg
, DIRECT_BOOT_64
,
4781 sizeof (DIRECT_BOOT_64
) - 1) == 0) {
4782 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4784 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_64BIT
;
4785 } else if (strncmp(arg
, DIRECT_BOOT_FAILSAFE_KERNEL
,
4786 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL
) - 1) == 0) {
4787 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4789 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_FAILSAFE
;
4790 } else if (strncmp(arg
, DIRECT_BOOT_FAILSAFE_32
,
4791 sizeof (DIRECT_BOOT_FAILSAFE_32
) - 1) == 0) {
4792 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4793 "flag: %s\n", fcn
, arg
));
4794 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_FAILSAFE
4796 } else if (strncmp(arg
, DIRECT_BOOT_FAILSAFE_64
,
4797 sizeof (DIRECT_BOOT_FAILSAFE_64
) - 1) == 0) {
4798 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4799 "flag: %s\n", fcn
, arg
));
4800 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_FAILSAFE
4802 } else if (strncmp(arg
, MULTI_BOOT
, sizeof (MULTI_BOOT
) - 1) == 0) {
4803 BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn
, arg
));
4804 entry
->flags
|= BAM_ENTRY_MULTIBOOT
;
4805 } else if (strncmp(arg
, MULTI_BOOT_FAILSAFE
,
4806 sizeof (MULTI_BOOT_FAILSAFE
) - 1) == 0) {
4807 BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4808 "flag: %s\n", fcn
, arg
));
4809 entry
->flags
|= BAM_ENTRY_MULTIBOOT
| BAM_ENTRY_FAILSAFE
;
4810 } else if (strstr(arg
, XEN_KERNEL_SUBSTR
)) {
4811 BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn
, arg
));
4812 entry
->flags
|= BAM_ENTRY_HV
;
4813 } else if (!(entry
->flags
& (BAM_ENTRY_BOOTADM
|BAM_ENTRY_LU
))) {
4814 BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn
, arg
));
4816 } else if (strncmp(arg
, KERNEL_PREFIX
, strlen(KERNEL_PREFIX
)) == 0 &&
4817 strstr(arg
, UNIX_SPACE
)) {
4818 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_32BIT
;
4819 } else if (strncmp(arg
, KERNEL_PREFIX
, strlen(KERNEL_PREFIX
)) == 0 &&
4820 strstr(arg
, AMD_UNIX_SPACE
)) {
4821 entry
->flags
|= BAM_ENTRY_DBOOT
| BAM_ENTRY_64BIT
;
4823 BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn
, arg
));
4824 bam_error(_("kernel command on line %d not recognized.\n"),
4829 return (BAM_SUCCESS
);
4833 module_parser(entry_t
*entry
, char *cmd
, char *arg
, int linenum
)
4835 const char *fcn
= "module_parser()";
4841 if (strcmp(cmd
, menu_cmds
[MODULE_CMD
]) != 0 &&
4842 strcmp(cmd
, menu_cmds
[MODULE_DOLLAR_CMD
]) != 0) {
4843 BAM_DPRINTF(("%s: not module cmd: %s\n", fcn
, cmd
));
4847 if (strcmp(arg
, DIRECT_BOOT_ARCHIVE
) == 0 ||
4848 strcmp(arg
, DIRECT_BOOT_ARCHIVE_32
) == 0 ||
4849 strcmp(arg
, DIRECT_BOOT_ARCHIVE_64
) == 0 ||
4850 strcmp(arg
, MULTIBOOT_ARCHIVE
) == 0 ||
4851 strcmp(arg
, FAILSAFE_ARCHIVE
) == 0 ||
4852 strcmp(arg
, FAILSAFE_ARCHIVE_32
) == 0 ||
4853 strcmp(arg
, FAILSAFE_ARCHIVE_64
) == 0 ||
4854 strcmp(arg
, XEN_KERNEL_MODULE_LINE
) == 0 ||
4855 strcmp(arg
, XEN_KERNEL_MODULE_LINE_ZFS
) == 0) {
4856 BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn
, arg
));
4857 return (BAM_SUCCESS
);
4858 } else if (!(entry
->flags
& BAM_ENTRY_BOOTADM
) &&
4859 !(entry
->flags
& BAM_ENTRY_LU
)) {
4860 /* don't emit warning for hand entries */
4861 BAM_DPRINTF(("%s: is HAND module: %s\n", fcn
, arg
));
4864 BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn
, arg
));
4865 bam_error(_("module command on line %d not recognized.\n"),
4872 * A line in menu.lst looks like
4873 * [ ]*<cmd>[ \t=]*<arg>*
4876 line_parser(menu_t
*mp
, char *str
, int *lineNum
, int *entryNum
)
4879 * save state across calls. This is so that
4880 * header gets the right entry# after title has
4883 static line_t
*prev
= NULL
;
4884 static entry_t
*curr_ent
= NULL
;
4885 static int in_liveupgrade
= 0;
4886 static int is_libbe_ent
= 0;
4889 char *cmd
, *sep
, *arg
;
4890 char save
, *cp
, *line
;
4891 menu_flag_t flag
= BAM_INVALID
;
4892 const char *fcn
= "line_parser()";
4900 * First save a copy of the entire line.
4901 * We use this later to set the line field.
4903 line
= s_strdup(str
);
4905 /* Eat up leading whitespace */
4906 while (*str
== ' ' || *str
== '\t')
4909 if (*str
== '#') { /* comment */
4910 cmd
= s_strdup("#");
4912 arg
= s_strdup(str
+ 1);
4914 if (strstr(arg
, BAM_LU_HDR
) != NULL
) {
4916 } else if (strstr(arg
, BAM_LU_FTR
) != NULL
) {
4918 } else if (strstr(arg
, BAM_LIBBE_FTR
) != NULL
) {
4921 } else if (*str
== '\0') { /* blank line */
4922 cmd
= sep
= arg
= NULL
;
4926 * '=' is not a documented separator in grub syntax.
4927 * However various development bits use '=' as a
4928 * separator. In addition, external users also
4929 * use = as a separator. So we will allow that usage.
4932 while (*str
!= ' ' && *str
!= '\t' && *str
!= '=') {
4950 sep
= s_strdup(str
- 1);
4953 while (*str
== ' ' || *str
== '\t')
4958 arg
= s_strdup(str
);
4962 lp
= s_calloc(1, sizeof (line_t
));
4968 lp
->lineNum
= ++(*lineNum
);
4969 if (cmd
&& strcmp(cmd
, menu_cmds
[TITLE_CMD
]) == 0) {
4970 lp
->entryNum
= ++(*entryNum
);
4971 lp
->flags
= BAM_TITLE
;
4972 if (prev
&& prev
->flags
== BAM_COMMENT
&&
4973 prev
->arg
&& strcmp(prev
->arg
, BAM_BOOTADM_HDR
) == 0) {
4974 prev
->entryNum
= lp
->entryNum
;
4975 curr_ent
= boot_entry_new(mp
, prev
, lp
);
4976 curr_ent
->flags
|= BAM_ENTRY_BOOTADM
;
4977 BAM_DPRINTF(("%s: is bootadm(8) entry: %s\n",
4980 curr_ent
= boot_entry_new(mp
, lp
, lp
);
4981 if (in_liveupgrade
) {
4982 curr_ent
->flags
|= BAM_ENTRY_LU
;
4983 BAM_DPRINTF(("%s: is LU entry: %s\n",
4987 curr_ent
->entryNum
= *entryNum
;
4988 } else if (flag
!= BAM_INVALID
) {
4990 * For header comments, the entry# is "fixed up"
4991 * by the subsequent title
4993 lp
->entryNum
= *entryNum
;
4996 lp
->entryNum
= *entryNum
;
4998 if (*entryNum
== ENTRY_INIT
) {
4999 lp
->flags
= BAM_GLOBAL
;
5001 lp
->flags
= BAM_ENTRY
;
5004 if (strcmp(cmd
, menu_cmds
[ROOT_CMD
]) == 0) {
5005 BAM_DPRINTF(("%s: setting ROOT: %s\n",
5007 curr_ent
->flags
|= BAM_ENTRY_ROOT
;
5008 } else if (strcmp(cmd
, menu_cmds
[FINDROOT_CMD
])
5010 BAM_DPRINTF(("%s: setting "
5011 "FINDROOT: %s\n", fcn
, arg
));
5012 curr_ent
->flags
|= BAM_ENTRY_FINDROOT
;
5013 } else if (strcmp(cmd
,
5014 menu_cmds
[CHAINLOADER_CMD
]) == 0) {
5015 BAM_DPRINTF(("%s: setting "
5016 "CHAINLOADER: %s\n", fcn
, arg
));
5018 BAM_ENTRY_CHAINLOADER
;
5019 } else if (kernel_parser(curr_ent
, cmd
, arg
,
5020 lp
->lineNum
) != BAM_SUCCESS
) {
5021 (void) module_parser(curr_ent
, cmd
,
5028 /* record default, old default, and entry line ranges */
5029 if (lp
->flags
== BAM_GLOBAL
&& lp
->cmd
!= NULL
&&
5030 strcmp(lp
->cmd
, menu_cmds
[DEFAULT_CMD
]) == 0) {
5031 mp
->curdefault
= lp
;
5032 } else if (lp
->flags
== BAM_COMMENT
&&
5033 strncmp(lp
->arg
, BAM_OLDDEF
, strlen(BAM_OLDDEF
)) == 0) {
5034 mp
->olddefault
= lp
;
5035 } else if (lp
->flags
== BAM_COMMENT
&&
5036 strncmp(lp
->arg
, BAM_OLD_RC_DEF
, strlen(BAM_OLD_RC_DEF
)) == 0) {
5037 mp
->old_rc_default
= lp
;
5038 } else if (lp
->flags
== BAM_ENTRY
||
5039 (lp
->flags
== BAM_COMMENT
&&
5040 ((strcmp(lp
->arg
, BAM_BOOTADM_FTR
) == 0) || is_libbe_ent
))) {
5042 curr_ent
->flags
|= BAM_ENTRY_LIBBE
;
5046 boot_entry_addline(curr_ent
, lp
);
5048 append_line(mp
, lp
);
5054 update_numbering(menu_t
*mp
)
5058 int old_default_value
;
5059 line_t
*lp
, *prev
, *default_lp
, *default_entry
;
5062 if (mp
->start
== NULL
) {
5066 lineNum
= LINE_INIT
;
5067 entryNum
= ENTRY_INIT
;
5068 old_default_value
= ENTRY_INIT
;
5069 lp
= default_lp
= default_entry
= NULL
;
5072 for (lp
= mp
->start
; lp
; prev
= lp
, lp
= lp
->next
) {
5073 lp
->lineNum
= ++lineNum
;
5076 * Get the value of the default command
5078 if (lp
->entryNum
== ENTRY_INIT
&& lp
->cmd
!= NULL
&&
5079 strcmp(lp
->cmd
, menu_cmds
[DEFAULT_CMD
]) == 0 &&
5081 old_default_value
= atoi(lp
->arg
);
5086 * If not a booting entry, nothing else to fix for this
5089 if (lp
->entryNum
== ENTRY_INIT
)
5093 * Record the position of the default entry.
5094 * The following works because global
5095 * commands like default and timeout should precede
5096 * actual boot entries, so old_default_value
5097 * is already known (or default cmd is missing).
5099 if (default_entry
== NULL
&&
5100 old_default_value
!= ENTRY_INIT
&&
5101 lp
->entryNum
== old_default_value
) {
5106 * Now fixup the entry number
5108 if (lp
->cmd
!= NULL
&&
5109 strcmp(lp
->cmd
, menu_cmds
[TITLE_CMD
]) == 0) {
5110 lp
->entryNum
= ++entryNum
;
5111 /* fixup the bootadm header */
5112 if (prev
&& prev
->flags
== BAM_COMMENT
&&
5114 strcmp(prev
->arg
, BAM_BOOTADM_HDR
) == 0) {
5115 prev
->entryNum
= lp
->entryNum
;
5118 lp
->entryNum
= entryNum
;
5123 * No default command in menu, simply return
5125 if (default_lp
== NULL
) {
5129 free(default_lp
->arg
);
5130 free(default_lp
->line
);
5132 if (default_entry
== NULL
) {
5133 default_lp
->arg
= s_strdup("0");
5135 (void) snprintf(buf
, sizeof (buf
), "%d",
5136 default_entry
->entryNum
);
5137 default_lp
->arg
= s_strdup(buf
);
5141 * The following is required since only the line field gets
5142 * written back to menu.lst
5144 (void) snprintf(buf
, sizeof (buf
), "%s%s%s",
5145 menu_cmds
[DEFAULT_CMD
], menu_cmds
[SEP_CMD
], default_lp
->arg
);
5146 default_lp
->line
= s_strdup(buf
);
5151 menu_read(char *menu_path
)
5154 char buf
[BAM_MAXLINE
], *cp
;
5156 int line
, entry
, len
, n
;
5158 mp
= s_calloc(1, sizeof (menu_t
));
5160 fp
= fopen(menu_path
, "r");
5161 if (fp
== NULL
) { /* Let the caller handle this error */
5166 /* Note: GRUB boot entry number starts with 0 */
5171 while (s_fgets(cp
, len
, fp
) != NULL
) {
5173 if (cp
[n
- 1] == '\\') {
5179 line_parser(mp
, buf
, &line
, &entry
);
5184 if (fclose(fp
) == EOF
) {
5185 bam_error(_("failed to close file: %s: %s\n"), menu_path
,
5193 selector(menu_t
*mp
, char *opt
, int *entry
, char **title
)
5203 opt_dup
= s_strdup(opt
);
5206 *entry
= ENTRY_INIT
;
5210 eq
= strchr(opt_dup
, '=');
5212 bam_error(_("invalid option: %s\n"), opt
);
5218 if (entry
&& strcmp(opt_dup
, OPT_ENTRY_NUM
) == 0) {
5220 entryNum
= s_strtol(eq
+ 1);
5221 if (entryNum
< 0 || entryNum
> mp
->end
->entryNum
) {
5222 bam_error(_("invalid boot entry number: %s\n"), eq
+ 1);
5227 } else if (title
&& strcmp(opt_dup
, menu_cmds
[TITLE_CMD
]) == 0) {
5228 *title
= opt
+ (eq
- opt_dup
) + 1;
5230 bam_error(_("invalid option: %s\n"), opt
);
5236 return (BAM_SUCCESS
);
5240 * If invoked with no titles/entries (opt == NULL)
5241 * only title lines in file are printed.
5243 * If invoked with a title or entry #, all
5244 * lines in *every* matching entry are listed
5247 list_entry(menu_t
*mp
, char *menu_path
, char *opt
)
5250 int entry
= ENTRY_INIT
;
5257 /* opt is optional */
5258 BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path
,
5259 opt
? opt
: "<NULL>"));
5261 if (mp
->start
== NULL
) {
5262 bam_error(_("menu file not found: %s\n"), menu_path
);
5267 if (selector(mp
, opt
, &entry
, &title
) != BAM_SUCCESS
) {
5270 assert((entry
!= ENTRY_INIT
) ^ (title
!= NULL
));
5272 (void) read_globals(mp
, menu_path
, menu_cmds
[DEFAULT_CMD
], 0);
5273 (void) read_globals(mp
, menu_path
, menu_cmds
[TIMEOUT_CMD
], 0);
5277 for (lp
= mp
->start
; lp
; lp
= lp
->next
) {
5278 if (lp
->flags
== BAM_COMMENT
|| lp
->flags
== BAM_EMPTY
)
5280 if (opt
== NULL
&& lp
->flags
== BAM_TITLE
) {
5281 bam_print(_("%d %s\n"), lp
->entryNum
,
5286 if (entry
!= ENTRY_INIT
&& lp
->entryNum
== entry
) {
5287 bam_print(_("%s\n"), lp
->line
);
5293 * We set the entry value here so that all lines
5294 * in entry get printed. If we subsequently match
5295 * title in other entries, all lines in those
5296 * entries get printed as well.
5298 if (title
&& lp
->flags
== BAM_TITLE
&& lp
->arg
&&
5299 strncmp(title
, lp
->arg
, strlen(title
)) == 0) {
5300 bam_print(_("%s\n"), lp
->line
);
5301 entry
= lp
->entryNum
;
5308 bam_error(_("no matching entry found\n"));
5312 return (BAM_SUCCESS
);
5316 add_boot_entry(menu_t
*mp
,
5326 char linebuf
[BAM_MAXLINE
];
5329 const char *fcn
= "add_boot_entry()";
5333 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot
= NULL
);
5334 if (findroot
== NULL
) {
5335 bam_error(_("can't find argument for findroot command\n"));
5339 if (title
== NULL
) {
5340 title
= "Solaris"; /* default to Solaris */
5342 if (kernel
== NULL
) {
5343 bam_error(_("missing suboption: %s\n"), menu_cmds
[KERNEL_CMD
]);
5346 if (module
== NULL
) {
5347 if (bam_direct
!= BAM_DIRECT_DBOOT
) {
5348 bam_error(_("missing suboption: %s\n"),
5349 menu_cmds
[MODULE_CMD
]);
5353 /* Figure the commands out from the kernel line */
5354 if (strstr(kernel
, "$ISADIR") != NULL
) {
5355 module
= DIRECT_BOOT_ARCHIVE
;
5356 } else if (strstr(kernel
, "amd64") != NULL
) {
5357 module
= DIRECT_BOOT_ARCHIVE_64
;
5359 module
= DIRECT_BOOT_ARCHIVE_32
;
5363 k_cmd
= KERNEL_DOLLAR_CMD
;
5364 m_cmd
= MODULE_DOLLAR_CMD
;
5367 lineNum
= mp
->end
->lineNum
;
5368 entryNum
= mp
->end
->entryNum
;
5370 lineNum
= LINE_INIT
;
5371 entryNum
= ENTRY_INIT
;
5375 * No separator for comment (HDR/FTR) commands
5376 * The syntax for comments is #<comment>
5378 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s",
5379 menu_cmds
[COMMENT_CMD
], BAM_BOOTADM_HDR
);
5380 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5382 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5383 menu_cmds
[TITLE_CMD
], menu_cmds
[SEP_CMD
], title
);
5384 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5386 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5387 menu_cmds
[FINDROOT_CMD
], menu_cmds
[SEP_CMD
], findroot
);
5388 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5389 BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5390 fcn
, lineNum
, entryNum
));
5392 if (bootfs
!= NULL
) {
5393 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5394 menu_cmds
[BOOTFS_CMD
], menu_cmds
[SEP_CMD
], bootfs
);
5395 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5398 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5399 menu_cmds
[k_cmd
], menu_cmds
[SEP_CMD
], kernel
);
5400 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5402 if (mod_kernel
!= NULL
) {
5403 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5404 menu_cmds
[m_cmd
], menu_cmds
[SEP_CMD
], mod_kernel
);
5405 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5408 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
5409 menu_cmds
[m_cmd
], menu_cmds
[SEP_CMD
], module
);
5410 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5412 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s",
5413 menu_cmds
[COMMENT_CMD
], BAM_BOOTADM_FTR
);
5414 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
5420 delete_boot_entry(menu_t
*mp
, int entryNum
, int quiet
)
5427 const char *fcn
= "delete_boot_entry()";
5429 assert(entryNum
!= ENTRY_INIT
);
5438 * Check entry number and make sure it's a modifiable entry.
5441 * + We can modify a bootadm-created entry
5442 * + We can modify a libbe-created entry
5444 if ((lp
->flags
!= BAM_COMMENT
&&
5445 (((ent
->flags
& BAM_ENTRY_LIBBE
) == 0) &&
5446 strcmp(lp
->arg
, BAM_BOOTADM_HDR
) != 0)) ||
5447 (entryNum
!= ALL_ENTRIES
&& lp
->entryNum
!= entryNum
)) {
5452 /* free the entry content */
5455 lp
= lp
->next
; /* prev stays the same */
5456 BAM_DPRINTF(("%s: freeing line: %d\n",
5457 fcn
, freed
->lineNum
));
5458 unlink_line(mp
, freed
);
5460 } while (freed
!= ent
->end
);
5462 /* free the entry_t structure */
5463 assert(tmp
== NULL
);
5467 tmp
->prev
->next
= ent
;
5471 ent
->prev
= tmp
->prev
;
5472 BAM_DPRINTF(("%s: freeing entry: %d\n", fcn
, tmp
->entryNum
));
5478 assert(tmp
== NULL
);
5480 if (!deleted
&& entryNum
!= ALL_ENTRIES
) {
5481 if (quiet
== DBE_PRINTERR
)
5482 bam_error(_("no matching bootadm entry found\n"));
5487 * Now that we have deleted an entry, update
5488 * the entry numbering and the default cmd.
5490 update_numbering(mp
);
5492 return (BAM_SUCCESS
);
5496 delete_all_entries(menu_t
*mp
, char *dummy
, char *opt
)
5499 assert(dummy
== NULL
);
5500 assert(opt
== NULL
);
5502 BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5504 if (mp
->start
== NULL
) {
5505 bam_print(_("the GRUB menu is empty\n"));
5506 return (BAM_SUCCESS
);
5509 if (delete_boot_entry(mp
, ALL_ENTRIES
, DBE_PRINTERR
) != BAM_SUCCESS
) {
5517 create_diskmap(char *osroot
)
5520 char cmd
[PATH_MAX
+ 16];
5521 char path
[PATH_MAX
];
5522 const char *fcn
= "create_diskmap()";
5524 /* make sure we have a map file */
5525 fp
= fopen(GRUBDISK_MAP
, "r");
5529 ret
= snprintf(path
, sizeof (path
), "%s/%s", osroot
,
5531 if (ret
>= sizeof (path
)) {
5532 bam_error(_("unable to create path on mountpoint %s, "
5533 "path too long\n"), osroot
);
5536 if (is_safe_exec(path
) == BAM_ERROR
)
5539 (void) snprintf(cmd
, sizeof (cmd
),
5540 "%s/%s > /dev/null", osroot
, CREATE_DISKMAP
);
5541 if (exec_cmd(cmd
, NULL
) != 0)
5543 fp
= fopen(GRUBDISK_MAP
, "r");
5544 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp
= NULL
);
5546 BAM_DPRINTF(("%s: created diskmap file: %s\n",
5547 fcn
, GRUBDISK_MAP
));
5549 BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5550 fcn
, GRUBDISK_MAP
));
5556 #define SECTOR_SIZE 512
5559 get_partition(char *device
)
5561 int i
, fd
, is_pcfs
, partno
= PARTNO_NOTFOUND
;
5562 struct mboot
*mboot
;
5563 char boot_sect
[SECTOR_SIZE
];
5564 char *wholedisk
, *slice
;
5567 uint32_t secnum
, numsec
;
5568 int rval
, pno
, ext_partno
= PARTNO_NOTFOUND
;
5571 /* form whole disk (p0) */
5572 slice
= device
+ strlen(device
) - 2;
5573 is_pcfs
= (*slice
!= 's');
5576 wholedisk
= s_calloc(1, strlen(device
) + 3);
5577 (void) snprintf(wholedisk
, strlen(device
) + 3, "%sp0", device
);
5581 /* read boot sector */
5582 fd
= open(wholedisk
, O_RDONLY
);
5583 if (fd
== -1 || read(fd
, boot_sect
, SECTOR_SIZE
) != SECTOR_SIZE
) {
5589 /* Read/Initialize extended partition information */
5590 if ((rval
= libfdisk_init(&epp
, wholedisk
, NULL
, FDISK_READ_DISK
))
5594 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5595 * be considered as soft errors and hence
5598 case FDISK_EBADLOGDRIVE
:
5600 case FDISK_ENOLOGDRIVE
:
5602 case FDISK_EBADMAGIC
:
5606 libfdisk_fini(&epp
);
5613 /* parse fdisk table */
5614 mboot
= (struct mboot
*)((void *)boot_sect
);
5615 for (i
= 0; i
< FD_NUMPART
; i
++) {
5616 struct ipart
*part
=
5617 (struct ipart
*)(uintptr_t)mboot
->parts
+ i
;
5618 if (is_pcfs
) { /* looking for solaris boot part */
5619 if (part
->systid
== 0xbe) {
5623 } else { /* look for solaris partition, old and new */
5624 if (part
->systid
== EFI_PMBR
) {
5625 partno
= PARTNO_EFI
;
5630 if ((part
->systid
== SUNIXOS
&&
5631 (fdisk_is_linux_swap(epp
, part
->relsect
,
5632 NULL
) != 0)) || part
->systid
== SUNIXOS2
) {
5634 if (part
->systid
== SUNIXOS
||
5635 part
->systid
== SUNIXOS2
) {
5642 if (fdisk_is_dos_extended(part
->systid
))
5648 /* If no primary solaris partition, check extended partition */
5649 if ((partno
== PARTNO_NOTFOUND
) && (ext_partno
!= PARTNO_NOTFOUND
)) {
5650 rval
= fdisk_get_solaris_part(epp
, &pno
, &secnum
, &numsec
);
5651 if (rval
== FDISK_SUCCESS
) {
5655 libfdisk_fini(&epp
);
5661 get_grubroot(char *osroot
, char *osdev
, char *menu_root
)
5663 char *grubroot
; /* (hd#,#,#) */
5665 char *grubhd
= NULL
;
5669 char *ctdname
= strstr(osdev
, "dsk/");
5670 char linebuf
[PATH_MAX
];
5673 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname
= NULL
);
5674 if (ctdname
== NULL
) {
5675 bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev
);
5679 if (menu_root
&& !menu_on_bootdisk(osroot
, menu_root
)) {
5680 /* menu bears no resemblance to our reality */
5681 bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5682 "bootdisk: %s\n"), osdev
);
5686 ctdname
+= strlen("dsk/");
5687 slice
= strrchr(ctdname
, 's');
5691 fp
= create_diskmap(osroot
);
5693 bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5699 while (s_fgets(linebuf
, sizeof (linebuf
), fp
) != NULL
) {
5700 grubhd
= strtok(linebuf
, " \t\n");
5702 devname
= strtok(NULL
, " \t\n");
5705 if (devname
&& strcmp(devname
, ctdname
) == 0) {
5717 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found
= 0);
5719 bam_error(_("not using biosdev command for disk: %s.\n"),
5724 fdiskpart
= get_partition(osdev
);
5725 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart
= PARTNO_NOTFOUND
);
5726 if (fdiskpart
== PARTNO_NOTFOUND
) {
5727 bam_error(_("failed to determine fdisk partition: %s\n"),
5732 grubroot
= s_calloc(1, 10);
5733 if (fdiskpart
== PARTNO_EFI
) {
5734 fdiskpart
= atoi(&slice
[1]);
5739 (void) snprintf(grubroot
, 10, "(hd%s,%d,%c)",
5740 grubhd
, fdiskpart
, slice
[1] + 'a' - '0');
5742 (void) snprintf(grubroot
, 10, "(hd%s,%d)",
5746 assert(strncmp(grubroot
, "(hd", strlen("(hd")) == 0);
5751 find_primary_common(char *mntpt
, char *fstype
)
5753 char signdir
[PATH_MAX
];
5754 char tmpsign
[MAXNAMELEN
+ 1];
5759 struct dirent
*entp
;
5761 const char *fcn
= "find_primary_common()";
5763 (void) snprintf(signdir
, sizeof (signdir
), "%s/%s",
5764 mntpt
, GRUBSIGN_DIR
);
5766 if (stat(signdir
, &sb
) == -1) {
5767 BAM_DPRINTF(("%s: no sign dir: %s\n", fcn
, signdir
));
5771 dirp
= opendir(signdir
);
5772 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp
= NULL
);
5774 bam_error(_("opendir of %s failed: %s\n"), signdir
,
5779 ufs
= zfs
= lu
= NULL
;
5781 while ((entp
= readdir(dirp
)) != NULL
) {
5782 if (strcmp(entp
->d_name
, ".") == 0 ||
5783 strcmp(entp
->d_name
, "..") == 0)
5786 (void) snprintf(tmpsign
, sizeof (tmpsign
), "%s", entp
->d_name
);
5789 strncmp(tmpsign
, GRUBSIGN_LU_PREFIX
,
5790 strlen(GRUBSIGN_LU_PREFIX
)) == 0) {
5791 lu
= s_strdup(tmpsign
);
5795 strncmp(tmpsign
, GRUBSIGN_UFS_PREFIX
,
5796 strlen(GRUBSIGN_UFS_PREFIX
)) == 0) {
5797 ufs
= s_strdup(tmpsign
);
5801 strncmp(tmpsign
, GRUBSIGN_ZFS_PREFIX
,
5802 strlen(GRUBSIGN_ZFS_PREFIX
)) == 0) {
5803 zfs
= s_strdup(tmpsign
);
5807 BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn
,
5813 (void) closedir(dirp
);
5817 if (strcmp(fstype
, "ufs") == 0 && zfs
) {
5818 bam_error(_("found mismatched boot signature %s for "
5819 "filesystem type: %s.\n"), zfs
, "ufs");
5822 } else if (strcmp(fstype
, "zfs") == 0 && ufs
) {
5823 bam_error(_("found mismatched boot signature %s for "
5824 "filesystem type: %s.\n"), ufs
, "zfs");
5829 assert(dirp
== NULL
);
5831 /* For now, we let Live Upgrade take care of its signature itself */
5833 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn
, lu
));
5838 return (zfs
? zfs
: ufs
);
5842 find_backup_common(char *mntpt
, char *fstype
)
5845 char tmpsign
[MAXNAMELEN
+ 1];
5846 char backup
[PATH_MAX
];
5851 const char *fcn
= "find_backup_common()";
5854 * We didn't find it in the primary directory.
5855 * Look at the backup
5857 (void) snprintf(backup
, sizeof (backup
), "%s%s",
5858 mntpt
, GRUBSIGN_BACKUP
);
5860 bfp
= fopen(backup
, "r");
5864 bam_error(_("failed to open file: %s: %s\n"),
5865 backup
, strerror(error
));
5867 BAM_DPRINTF(("%s: failed to open %s: %s\n",
5868 fcn
, backup
, strerror(error
)));
5872 ufs
= zfs
= lu
= NULL
;
5874 while (s_fgets(tmpsign
, sizeof (tmpsign
), bfp
) != NULL
) {
5877 strncmp(tmpsign
, GRUBSIGN_LU_PREFIX
,
5878 strlen(GRUBSIGN_LU_PREFIX
)) == 0) {
5879 lu
= s_strdup(tmpsign
);
5883 strncmp(tmpsign
, GRUBSIGN_UFS_PREFIX
,
5884 strlen(GRUBSIGN_UFS_PREFIX
)) == 0) {
5885 ufs
= s_strdup(tmpsign
);
5889 strncmp(tmpsign
, GRUBSIGN_ZFS_PREFIX
,
5890 strlen(GRUBSIGN_ZFS_PREFIX
)) == 0) {
5891 zfs
= s_strdup(tmpsign
);
5895 BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn
,
5905 if (strcmp(fstype
, "ufs") == 0 && zfs
) {
5906 bam_error(_("found mismatched boot signature %s for "
5907 "filesystem type: %s.\n"), zfs
, "ufs");
5910 } else if (strcmp(fstype
, "zfs") == 0 && ufs
) {
5911 bam_error(_("found mismatched boot signature %s for "
5912 "filesystem type: %s.\n"), ufs
, "zfs");
5917 assert(bfp
== NULL
);
5919 /* For now, we let Live Upgrade take care of its signature itself */
5921 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn
, lu
));
5926 return (zfs
? zfs
: ufs
);
5930 find_ufs_existing(char *osroot
)
5933 const char *fcn
= "find_ufs_existing()";
5935 sign
= find_primary_common(osroot
, "ufs");
5937 sign
= find_backup_common(osroot
, "ufs");
5938 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn
,
5939 sign
? sign
: "NULL"));
5941 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn
, sign
));
5948 get_mountpoint(char *special
, char *fstype
)
5951 struct mnttab mp
= {0};
5952 struct mnttab mpref
= {0};
5955 const char *fcn
= "get_mountpoint()";
5957 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, special
, fstype
));
5959 mntfp
= fopen(MNTTAB
, "r");
5961 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp
= NULL
);
5962 if (mntfp
== NULL
) {
5963 bam_error(_("failed to open file: %s: %s\n"),
5964 MNTTAB
, strerror(error
));
5968 mpref
.mnt_special
= special
;
5969 mpref
.mnt_fstype
= fstype
;
5971 ret
= getmntany(mntfp
, &mp
, &mpref
);
5972 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret
= 1);
5974 (void) fclose(mntfp
);
5975 BAM_DPRINTF(("%s: no mount-point for special=%s and "
5976 "fstype=%s\n", fcn
, special
, fstype
));
5979 (void) fclose(mntfp
);
5981 assert(mp
.mnt_mountp
);
5983 BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5984 fcn
, special
, mp
.mnt_mountp
));
5986 return (s_strdup(mp
.mnt_mountp
));
5990 * Mounts a "legacy" top dataset (if needed)
5991 * Returns: The mountpoint of the legacy top dataset or NULL on error
5992 * mnted returns one of the above values defined for zfs_mnted_t
5995 mount_legacy_dataset(char *pool
, zfs_mnted_t
*mnted
)
5998 char tmpmnt
[PATH_MAX
];
5999 filelist_t flist
= {0};
6003 const char *fcn
= "mount_legacy_dataset()";
6005 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, pool
));
6007 *mnted
= ZFS_MNT_ERROR
;
6009 (void) snprintf(cmd
, sizeof (cmd
),
6010 "/sbin/zfs get -Ho value mounted %s",
6013 ret
= exec_cmd(cmd
, &flist
);
6014 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret
= 1);
6016 bam_error(_("failed to determine mount status of ZFS "
6017 "pool %s\n"), pool
);
6021 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist
.head
= NULL
);
6022 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6023 bam_error(_("ZFS pool %s has bad mount status\n"), pool
);
6024 filelist_free(&flist
);
6028 is_mounted
= strtok(flist
.head
->line
, " \t\n");
6029 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted
= "yes");
6030 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted
= "no");
6031 if (strcmp(is_mounted
, "no") != 0) {
6032 filelist_free(&flist
);
6033 *mnted
= LEGACY_ALREADY
;
6034 /* get_mountpoint returns a strdup'ed string */
6035 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
6037 return (get_mountpoint(pool
, "zfs"));
6040 filelist_free(&flist
);
6043 * legacy top dataset is not mounted. Mount it now
6044 * First create a mountpoint.
6046 (void) snprintf(tmpmnt
, sizeof (tmpmnt
), "%s.%d",
6047 ZFS_LEGACY_MNTPT
, getpid());
6049 ret
= stat(tmpmnt
, &sb
);
6051 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
6052 fcn
, pool
, tmpmnt
));
6053 ret
= mkdirp(tmpmnt
, DIR_PERMS
);
6054 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret
= -1);
6056 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt
,
6061 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
6062 "present\n", fcn
, pool
, tmpmnt
));
6065 (void) snprintf(cmd
, sizeof (cmd
),
6066 "/sbin/mount -F zfs %s %s",
6069 ret
= exec_cmd(cmd
, NULL
);
6070 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret
= 1);
6072 bam_error(_("mount of ZFS pool %s failed\n"), pool
);
6073 (void) rmdir(tmpmnt
);
6077 *mnted
= LEGACY_MOUNTED
;
6078 BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
6079 fcn
, pool
, tmpmnt
));
6080 return (s_strdup(tmpmnt
));
6084 * Mounts the top dataset (if needed)
6085 * Returns: The mountpoint of the top dataset or NULL on error
6086 * mnted returns one of the above values defined for zfs_mnted_t
6089 mount_top_dataset(char *pool
, zfs_mnted_t
*mnted
)
6092 filelist_t flist
= {0};
6097 const char *fcn
= "mount_top_dataset()";
6099 *mnted
= ZFS_MNT_ERROR
;
6101 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, pool
));
6104 * First check if the top dataset is a "legacy" dataset
6106 (void) snprintf(cmd
, sizeof (cmd
),
6107 "/sbin/zfs get -Ho value mountpoint %s",
6109 ret
= exec_cmd(cmd
, &flist
);
6110 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret
= 1);
6112 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6117 if (flist
.head
&& (flist
.head
== flist
.tail
)) {
6118 char *legacy
= strtok(flist
.head
->line
, " \t\n");
6119 if (legacy
&& strcmp(legacy
, "legacy") == 0) {
6120 filelist_free(&flist
);
6121 BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn
, pool
));
6122 return (mount_legacy_dataset(pool
, mnted
));
6126 filelist_free(&flist
);
6128 BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn
, pool
));
6130 (void) snprintf(cmd
, sizeof (cmd
),
6131 "/sbin/zfs get -Ho value mounted %s",
6134 ret
= exec_cmd(cmd
, &flist
);
6135 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret
= 1);
6137 bam_error(_("failed to determine mount status of ZFS "
6138 "pool %s\n"), pool
);
6142 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist
.head
= NULL
);
6143 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6144 bam_error(_("ZFS pool %s has bad mount status\n"), pool
);
6145 filelist_free(&flist
);
6149 is_mounted
= strtok(flist
.head
->line
, " \t\n");
6150 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted
= "yes");
6151 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted
= "no");
6152 if (strcmp(is_mounted
, "no") != 0) {
6153 filelist_free(&flist
);
6154 *mnted
= ZFS_ALREADY
;
6155 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
6160 filelist_free(&flist
);
6161 BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
6164 /* top dataset is not mounted. Mount it now */
6165 (void) snprintf(cmd
, sizeof (cmd
),
6166 "/sbin/zfs mount %s", pool
);
6167 ret
= exec_cmd(cmd
, NULL
);
6168 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret
= 1);
6170 bam_error(_("mount of ZFS pool %s failed\n"), pool
);
6173 *mnted
= ZFS_MOUNTED
;
6174 BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn
, pool
));
6178 * Now get the mountpoint
6180 (void) snprintf(cmd
, sizeof (cmd
),
6181 "/sbin/zfs get -Ho value mountpoint %s",
6184 ret
= exec_cmd(cmd
, &flist
);
6185 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret
= 1);
6187 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6192 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist
.head
= NULL
);
6193 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6194 bam_error(_("ZFS pool %s has no mount-point\n"), pool
);
6198 mntpt
= strtok(flist
.head
->line
, " \t\n");
6199 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt
= "foo");
6200 if (*mntpt
!= '/') {
6201 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6205 zmntpt
= s_strdup(mntpt
);
6207 filelist_free(&flist
);
6209 BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6210 fcn
, pool
, zmntpt
));
6215 filelist_free(&flist
);
6216 (void) umount_top_dataset(pool
, *mnted
, NULL
);
6217 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
6222 umount_top_dataset(char *pool
, zfs_mnted_t mnted
, char *mntpt
)
6226 const char *fcn
= "umount_top_dataset()";
6228 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted
= ZFS_MNT_ERROR
);
6230 case LEGACY_ALREADY
:
6233 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6234 "to umount\n", fcn
, pool
, mntpt
? mntpt
: "NULL"));
6236 return (BAM_SUCCESS
);
6237 case LEGACY_MOUNTED
:
6238 (void) snprintf(cmd
, sizeof (cmd
),
6239 "/sbin/umount %s", pool
);
6240 ret
= exec_cmd(cmd
, NULL
);
6241 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret
= 1);
6243 bam_error(_("umount of %s failed\n"), pool
);
6248 (void) rmdir(mntpt
);
6250 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6251 "successfully unmounted\n", fcn
, pool
));
6252 return (BAM_SUCCESS
);
6255 (void) snprintf(cmd
, sizeof (cmd
),
6256 "/sbin/zfs unmount %s", pool
);
6257 ret
= exec_cmd(cmd
, NULL
);
6258 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret
= 1);
6260 bam_error(_("umount of %s failed\n"), pool
);
6263 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6264 "successfully unmounted\n", fcn
, pool
));
6265 return (BAM_SUCCESS
);
6267 bam_error(_("Internal error: bad saved mount state for "
6268 "pool %s\n"), pool
);
6275 * For ZFS, osdev can be one of two forms
6276 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6277 * It can be a /dev/[r]dsk special file. We handle both instances
6280 get_pool(char *osdev
)
6284 filelist_t flist
= {0};
6289 const char *fcn
= "get_pool()";
6291 INJECT_ERROR1("GET_POOL_OSDEV", osdev
= NULL
);
6292 if (osdev
== NULL
) {
6293 bam_error(_("NULL device: cannot determine pool name\n"));
6297 BAM_DPRINTF(("%s: osdev arg = %s\n", fcn
, osdev
));
6299 if (osdev
[0] != '/') {
6300 (void) strlcpy(buf
, osdev
, sizeof (buf
));
6301 slash
= strchr(buf
, '/');
6304 pool
= s_strdup(buf
);
6305 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn
, pool
));
6307 } else if (strncmp(osdev
, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6308 strncmp(osdev
, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6309 bam_error(_("invalid device %s: cannot determine pool name\n"),
6315 * Call the zfs fstyp directly since this is a zpool. This avoids
6316 * potential pcfs conflicts if the first block wasn't cleared.
6318 (void) snprintf(cmd
, sizeof (cmd
),
6319 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6322 ret
= exec_cmd(cmd
, &flist
);
6323 INJECT_ERROR1("GET_POOL_FSTYP", ret
= 1);
6325 bam_error(_("fstyp -a on device %s failed\n"), osdev
);
6329 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist
.head
= NULL
);
6330 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6331 bam_error(_("NULL fstyp -a output for device %s\n"), osdev
);
6332 filelist_free(&flist
);
6336 (void) strtok(flist
.head
->line
, "'");
6337 cp
= strtok(NULL
, "'");
6338 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp
= NULL
);
6340 bam_error(_("bad fstyp -a output for device %s\n"), osdev
);
6341 filelist_free(&flist
);
6345 pool
= s_strdup(cp
);
6347 filelist_free(&flist
);
6349 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn
, pool
));
6355 find_zfs_existing(char *osdev
)
6361 const char *fcn
= "find_zfs_existing()";
6363 pool
= get_pool(osdev
);
6364 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool
= NULL
);
6366 bam_error(_("failed to get pool for device: %s\n"), osdev
);
6370 mntpt
= mount_top_dataset(pool
, &mnted
);
6371 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt
= NULL
);
6372 if (mntpt
== NULL
) {
6373 bam_error(_("failed to mount top dataset for pool: %s\n"),
6379 sign
= find_primary_common(mntpt
, "zfs");
6381 sign
= find_backup_common(mntpt
, "zfs");
6382 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn
,
6383 sign
? sign
: "NULL"));
6385 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn
, sign
));
6388 (void) umount_top_dataset(pool
, mnted
, mntpt
);
6396 find_existing_sign(char *osroot
, char *osdev
, char *fstype
)
6398 const char *fcn
= "find_existing_sign()";
6400 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype
= "foofs");
6401 if (strcmp(fstype
, "ufs") == 0) {
6402 BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn
));
6403 return (find_ufs_existing(osroot
));
6404 } else if (strcmp(fstype
, "zfs") == 0) {
6405 BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn
));
6406 return (find_zfs_existing(osdev
));
6408 bam_error(_("boot signature not supported for fstype: %s\n"),
6414 #define MH_HASH_SZ 16
6422 typedef struct mcache
{
6426 struct mcache
*mc_next
;
6429 typedef struct mhash
{
6430 mcache_t
*mh_hash
[MH_HASH_SZ
];
6434 mhash_fcn(char *key
)
6439 for (i
= 0; key
[i
] != '\0'; i
++) {
6440 sum
+= (uchar_t
)key
[i
];
6445 assert(sum
< MH_HASH_SZ
);
6454 struct extmnttab mnt
;
6461 const char *fcn
= "cache_mnttab()";
6463 mfp
= fopen(MNTTAB
, "r");
6465 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp
= NULL
);
6467 bam_error(_("failed to open file: %s: %s\n"), MNTTAB
,
6472 mhp
= s_calloc(1, sizeof (mhash_t
));
6476 while (getextmntent(mfp
, &mnt
, sizeof (mnt
)) == 0) {
6477 /* only cache ufs */
6478 if (strcmp(mnt
.mnt_fstype
, "ufs") != 0)
6481 /* basename() modifies its arg, so dup it */
6482 special_dup
= s_strdup(mnt
.mnt_special
);
6483 ctds
= basename(special_dup
);
6485 mcp
= s_calloc(1, sizeof (mcache_t
));
6486 mcp
->mc_special
= s_strdup(ctds
);
6487 mcp
->mc_mntpt
= s_strdup(mnt
.mnt_mountp
);
6488 mcp
->mc_fstype
= s_strdup(mnt
.mnt_fstype
);
6489 BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6490 "fstype=%s\n", fcn
, ctds
, mnt
.mnt_mountp
, mnt
.mnt_fstype
));
6491 idx
= mhash_fcn(ctds
);
6492 mcp
->mc_next
= mhp
->mh_hash
[idx
];
6493 mhp
->mh_hash
[idx
] = mcp
;
6503 free_mnttab(mhash_t
*mhp
)
6508 for (i
= 0; i
< MH_HASH_SZ
; i
++) {
6509 while ((mcp
= mhp
->mh_hash
[i
]) != NULL
) {
6510 mhp
->mh_hash
[i
] = mcp
->mc_next
;
6511 free(mcp
->mc_special
);
6512 free(mcp
->mc_mntpt
);
6513 free(mcp
->mc_fstype
);
6518 for (i
= 0; i
< MH_HASH_SZ
; i
++) {
6519 assert(mhp
->mh_hash
[i
] == NULL
);
6525 search_hash(mhash_t
*mhp
, char *special
, char **mntpt
)
6529 const char *fcn
= "search_hash()";
6535 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special
= "/foo");
6536 if (strchr(special
, '/')) {
6537 bam_error(_("invalid key for mnttab hash: %s\n"), special
);
6541 idx
= mhash_fcn(special
);
6543 for (mcp
= mhp
->mh_hash
[idx
]; mcp
; mcp
= mcp
->mc_next
) {
6544 if (strcmp(mcp
->mc_special
, special
) == 0)
6549 BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn
, special
));
6550 return (MH_NOMATCH
);
6553 assert(strcmp(mcp
->mc_fstype
, "ufs") == 0);
6554 *mntpt
= mcp
->mc_mntpt
;
6555 BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn
, special
));
6560 check_add_ufs_sign_to_list(FILE *tfp
, char *mntpt
)
6564 char signbuf
[MAXNAMELEN
];
6567 const char *fcn
= "check_add_ufs_sign_to_list()";
6569 /* safe to specify NULL as "osdev" arg for UFS */
6570 sign
= find_existing_sign(mntpt
, NULL
, "ufs");
6572 /* No existing signature, nothing to add to list */
6573 BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6578 (void) snprintf(signbuf
, sizeof (signbuf
), "%s\n", sign
);
6581 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline
= "pool_rpool10\n");
6582 if (strncmp(signline
, GRUBSIGN_UFS_PREFIX
,
6583 strlen(GRUBSIGN_UFS_PREFIX
))) {
6584 bam_error(_("invalid UFS boot signature %s\n"), sign
);
6586 /* ignore invalid signatures */
6590 len
= fputs(signline
, tfp
);
6592 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len
= 0);
6593 if (len
!= strlen(signline
)) {
6594 bam_error(_("failed to write signature %s to signature "
6595 "list: %s\n"), sign
, strerror(error
));
6602 BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6608 * slice is a basename not a full pathname
6611 process_slice_common(char *slice
, FILE *tfp
, mhash_t
*mhp
, char *tmpmnt
)
6615 char path
[PATH_MAX
];
6618 filelist_t flist
= {0};
6620 char blkslice
[PATH_MAX
];
6621 const char *fcn
= "process_slice_common()";
6624 ret
= search_hash(mhp
, slice
, &mntpt
);
6627 if (check_add_ufs_sign_to_list(tfp
, mntpt
) == -1)
6638 (void) snprintf(path
, sizeof (path
), "/dev/rdsk/%s", slice
);
6639 if (stat(path
, &sbuf
) == -1) {
6640 BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn
, path
));
6644 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6645 (void) snprintf(cmd
, sizeof (cmd
),
6646 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6649 if (exec_cmd(cmd
, &flist
) != 0) {
6651 bam_print(_("fstyp failed for slice: %s\n"), slice
);
6655 if ((flist
.head
== NULL
) || (flist
.head
!= flist
.tail
)) {
6657 bam_print(_("bad output from fstyp for slice: %s\n"),
6659 filelist_free(&flist
);
6663 fstype
= strtok(flist
.head
->line
, " \t\n");
6664 if (fstype
== NULL
|| strcmp(fstype
, "ufs") != 0) {
6666 bam_print(_("%s is not a ufs slice: %s\n"),
6668 filelist_free(&flist
);
6672 filelist_free(&flist
);
6675 * Since we are mounting the filesystem read-only, the
6676 * the last mount field of the superblock is unchanged
6677 * and does not need to be fixed up post-mount;
6680 (void) snprintf(blkslice
, sizeof (blkslice
), "/dev/dsk/%s",
6683 (void) snprintf(cmd
, sizeof (cmd
),
6684 "/usr/sbin/mount -F ufs -o ro %s %s "
6685 "> /dev/null 2>&1", blkslice
, tmpmnt
);
6687 if (exec_cmd(cmd
, NULL
) != 0) {
6689 bam_print(_("mount of %s (fstype %s) failed\n"),
6694 ret
= check_add_ufs_sign_to_list(tfp
, tmpmnt
);
6696 (void) snprintf(cmd
, sizeof (cmd
),
6697 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6700 if (exec_cmd(cmd
, NULL
) != 0) {
6701 bam_print(_("umount of %s failed\n"), slice
);
6709 process_vtoc_slices(
6717 char slice
[PATH_MAX
];
6720 const char *fcn
= "process_vtoc_slices()";
6724 assert(s0
[len
- 2] == 's' && s0
[len
- 1] == '0');
6728 (void) strlcpy(slice
, s0
, sizeof (slice
));
6732 cp
= slice
+ len
- 1;
6734 for (idx
= 0; idx
< vtoc
->v_nparts
; idx
++) {
6736 (void) snprintf(cp
, sizeof (slice
) - (len
- 1), "%u", idx
);
6738 if (vtoc
->v_part
[idx
].p_size
== 0) {
6739 BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6744 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6745 switch (vtoc
->v_part
[idx
].p_tag
) {
6752 BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6753 "skipping: %s\n", fcn
, slice
));
6756 BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6761 /* skip unmountable and readonly slices */
6762 switch (vtoc
->v_part
[idx
].p_flag
) {
6765 BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6769 BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6774 if (process_slice_common(slice
, tfp
, mhp
, tmpmnt
) == -1) {
6791 char slice
[PATH_MAX
];
6794 const char *fcn
= "process_efi_slices()";
6798 assert(s0
[len
- 2] == 's' && s0
[len
- 1] == '0');
6802 (void) strlcpy(slice
, s0
, sizeof (slice
));
6806 cp
= slice
+ len
- 1;
6808 for (idx
= 0; idx
< efi
->efi_nparts
; idx
++) {
6810 (void) snprintf(cp
, sizeof (slice
) - (len
- 1), "%u", idx
);
6812 if (efi
->efi_parts
[idx
].p_size
== 0) {
6813 BAM_DPRINTF(("%s: EFI: skipping 0-length slice: %s\n",
6818 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6819 switch (efi
->efi_parts
[idx
].p_tag
) {
6826 BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6830 BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6835 /* skip unmountable and readonly slices */
6836 switch (efi
->efi_parts
[idx
].p_flag
) {
6839 BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6843 BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6848 if (process_slice_common(slice
, tfp
, mhp
, tmpmnt
) == -1) {
6857 * s0 is a basename not a full path
6860 process_slice0(char *s0
, FILE *tfp
, mhash_t
*mhp
, char *tmpmnt
)
6864 char s0path
[PATH_MAX
];
6871 const char *fcn
= "process_slice0()";
6873 (void) snprintf(s0path
, sizeof (s0path
), "/dev/rdsk/%s", s0
);
6875 if (stat(s0path
, &sbuf
) == -1) {
6876 BAM_DPRINTF(("%s: slice 0 does not exist: %s\n", fcn
, s0path
));
6880 fd
= open(s0path
, O_NONBLOCK
|O_RDONLY
);
6882 bam_error(_("failed to open file: %s: %s\n"), s0path
,
6887 e_flag
= v_flag
= 0;
6888 retval
= ((err
= read_vtoc(fd
, &vtoc
)) >= 0) ? 0 : err
;
6891 BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6895 BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6899 BAM_DPRINTF(("%s: VTOC: unknown error while "
6900 "reading: %s\n", fcn
, s0path
));
6904 BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6909 BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6913 BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6914 "code: %s\n", fcn
, s0path
));
6921 retval
= ((err
= efi_alloc_and_read(fd
, &efi
)) >= 0) ? 0 : err
;
6924 BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6928 BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn
, s0path
));
6931 BAM_DPRINTF(("%s: EFI: unknown error while "
6932 "reading: %s\n", fcn
, s0path
));
6935 BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6940 BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6944 BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6953 retval
= process_vtoc_slices(s0
,
6954 &vtoc
, tfp
, mhp
, tmpmnt
);
6955 } else if (e_flag
) {
6956 retval
= process_efi_slices(s0
,
6957 efi
, tfp
, mhp
, tmpmnt
);
6959 BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6968 * Find and create a list of all existing UFS boot signatures
6971 FindAllUfsSignatures(void)
6973 mhash_t
*mnttab_hash
;
6976 char tmpmnt
[PATH_MAX
];
6984 const char *fcn
= "FindAllUfsSignatures()";
6986 if (stat(UFS_SIGNATURE_LIST
, &sb
) != -1) {
6987 bam_print(_(" - signature list %s exists\n"),
6988 UFS_SIGNATURE_LIST
);
6992 fd
= open(UFS_SIGNATURE_LIST
".tmp",
6993 O_RDWR
|O_CREAT
|O_TRUNC
, 0644);
6995 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd
= -1);
6997 bam_error(_("failed to open file: %s: %s\n"),
6998 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7004 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret
= -1);
7006 bam_error(_("failed to close file: %s: %s\n"),
7007 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7008 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7012 tfp
= fopen(UFS_SIGNATURE_LIST
".tmp", "a");
7014 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp
= NULL
);
7016 bam_error(_("failed to open file: %s: %s\n"),
7017 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7018 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7022 mnttab_hash
= cache_mnttab();
7023 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash
= NULL
);
7024 if (mnttab_hash
== NULL
) {
7026 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7027 bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn
);
7031 (void) snprintf(tmpmnt
, sizeof (tmpmnt
),
7032 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
7033 (void) unlink(tmpmnt
);
7035 ret
= mkdirp(tmpmnt
, DIR_PERMS
);
7037 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret
= -1);
7039 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt
,
7041 free_mnttab(mnttab_hash
);
7043 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7047 dirp
= opendir("/dev/rdsk");
7049 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp
= NULL
);
7051 bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
7056 while ((dp
= readdir(dirp
)) != NULL
) {
7057 if (strcmp(dp
->d_name
, ".") == 0 ||
7058 strcmp(dp
->d_name
, "..") == 0)
7062 * we only look for the s0 slice. This is guranteed to
7063 * have 's' at len - 2.
7065 len
= strlen(dp
->d_name
);
7066 if (dp
->d_name
[len
- 2 ] != 's' || dp
->d_name
[len
- 1] != '0') {
7067 BAM_DPRINTF(("%s: skipping non-s0 slice: %s\n",
7072 ret
= process_slice0(dp
->d_name
, tfp
, mnttab_hash
, tmpmnt
);
7073 INJECT_ERROR1("PROCESS_S0_FAIL", ret
= -1);
7078 (void) closedir(dirp
);
7079 free_mnttab(mnttab_hash
);
7080 (void) rmdir(tmpmnt
);
7084 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret
= EOF
);
7086 bam_error(_("failed to close file: %s: %s\n"),
7087 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7088 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7092 /* We have a list of existing GRUB signatures. Sort it first */
7093 (void) snprintf(cmd
, sizeof (cmd
),
7094 "/usr/bin/sort -u %s.tmp > %s.sorted",
7095 UFS_SIGNATURE_LIST
, UFS_SIGNATURE_LIST
);
7097 ret
= exec_cmd(cmd
, NULL
);
7098 INJECT_ERROR1("SORT_SIGN_LIST", ret
= 1);
7100 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7101 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
7102 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7106 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7108 ret
= rename(UFS_SIGNATURE_LIST
".sorted", UFS_SIGNATURE_LIST
);
7110 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret
= -1);
7112 bam_error(_("rename to file failed: %s: %s\n"),
7113 UFS_SIGNATURE_LIST
, strerror(error
));
7114 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
7118 if (stat(UFS_SIGNATURE_LIST
, &sb
) == 0 && sb
.st_size
== 0) {
7119 BAM_DPRINTF(("%s: generated zero length signlist: %s.\n",
7120 fcn
, UFS_SIGNATURE_LIST
));
7123 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7128 (void) closedir(dirp
);
7129 free_mnttab(mnttab_hash
);
7130 (void) rmdir(tmpmnt
);
7132 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7133 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7138 create_ufs_sign(void)
7142 char tmpsign
[MAXNAMELEN
+ 1];
7148 const char *fcn
= "create_ufs_sign()";
7150 bam_print(_(" - searching for UFS boot signatures\n"));
7152 ret
= FindAllUfsSignatures();
7153 INJECT_ERROR1("FIND_ALL_UFS", ret
= -1);
7155 bam_error(_("search for UFS boot signatures failed\n"));
7159 /* Make sure the list exists and is owned by root */
7160 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
7161 (void) unlink(UFS_SIGNATURE_LIST
));
7162 if (stat(UFS_SIGNATURE_LIST
, &sb
) == -1 || sb
.st_uid
!= 0) {
7163 (void) unlink(UFS_SIGNATURE_LIST
);
7164 bam_error(_("missing UFS signature list file: %s\n"),
7165 UFS_SIGNATURE_LIST
);
7169 if (sb
.st_size
== 0) {
7170 bam_print(_(" - no existing UFS boot signatures\n"));
7175 /* The signature list was sorted when it was created */
7176 tfp
= fopen(UFS_SIGNATURE_LIST
, "r");
7178 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp
= NULL
);
7180 bam_error(_("error opening UFS boot signature list "
7181 "file %s: %s\n"), UFS_SIGNATURE_LIST
, strerror(error
));
7182 (void) unlink(UFS_SIGNATURE_LIST
);
7186 for (i
= 0; s_fgets(tmpsign
, sizeof (tmpsign
), tfp
); i
++) {
7188 if (strncmp(tmpsign
, GRUBSIGN_UFS_PREFIX
,
7189 strlen(GRUBSIGN_UFS_PREFIX
)) != 0) {
7191 (void) unlink(UFS_SIGNATURE_LIST
);
7192 bam_error(_("bad UFS boot signature: %s\n"), tmpsign
);
7195 numstr
= tmpsign
+ strlen(GRUBSIGN_UFS_PREFIX
);
7197 if (numstr
[0] == '\0' || !isdigit(numstr
[0])) {
7199 (void) unlink(UFS_SIGNATURE_LIST
);
7200 bam_error(_("bad UFS boot signature: %s\n"), tmpsign
);
7204 signnum
= atoi(numstr
);
7205 INJECT_ERROR1("NEGATIVE_SIGN", signnum
= -1);
7208 (void) unlink(UFS_SIGNATURE_LIST
);
7209 bam_error(_("bad UFS boot signature: %s\n"), tmpsign
);
7214 BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7223 (void) snprintf(tmpsign
, sizeof (tmpsign
), "rootfs%d", i
);
7225 /* add the ufs signature to the /var/run list of signatures */
7226 ret
= ufs_add_to_sign_list(tmpsign
);
7227 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret
= -1);
7229 (void) unlink(UFS_SIGNATURE_LIST
);
7230 bam_error(_("failed to add sign %s to signlist.\n"), tmpsign
);
7234 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7236 return (s_strdup(tmpsign
));
7240 get_fstype(char *osroot
)
7243 struct mnttab mp
= {0};
7244 struct mnttab mpref
= {0};
7247 const char *fcn
= "get_fstype()";
7249 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot
= NULL
);
7250 if (osroot
== NULL
) {
7251 bam_error(_("no OS mountpoint. Cannot determine fstype\n"));
7255 mntfp
= fopen(MNTTAB
, "r");
7257 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp
= NULL
);
7258 if (mntfp
== NULL
) {
7259 bam_error(_("failed to open file: %s: %s\n"), MNTTAB
,
7264 if (*osroot
== '\0')
7265 mpref
.mnt_mountp
= "/";
7267 mpref
.mnt_mountp
= osroot
;
7269 ret
= getmntany(mntfp
, &mp
, &mpref
);
7270 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret
= 1);
7272 bam_error(_("failed to find OS mountpoint %s in %s\n"),
7274 (void) fclose(mntfp
);
7277 (void) fclose(mntfp
);
7279 INJECT_ERROR1("GET_FSTYPE_NULL", mp
.mnt_fstype
= NULL
);
7280 if (mp
.mnt_fstype
== NULL
) {
7281 bam_error(_("NULL fstype found for OS root %s\n"), osroot
);
7285 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7287 return (s_strdup(mp
.mnt_fstype
));
7291 create_zfs_sign(char *osdev
)
7293 char tmpsign
[PATH_MAX
];
7295 const char *fcn
= "create_zfs_sign()";
7297 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, osdev
));
7300 * First find the pool name
7302 pool
= get_pool(osdev
);
7303 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool
= NULL
);
7305 bam_error(_("failed to get pool name from %s\n"), osdev
);
7309 (void) snprintf(tmpsign
, sizeof (tmpsign
), "pool_%s", pool
);
7311 BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn
, tmpsign
));
7315 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7317 return (s_strdup(tmpsign
));
7321 create_new_sign(char *osdev
, char *fstype
)
7324 const char *fcn
= "create_new_sign()";
7326 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype
= "foofs");
7328 if (strcmp(fstype
, "zfs") == 0) {
7329 BAM_DPRINTF(("%s: created new ZFS sign\n", fcn
));
7330 sign
= create_zfs_sign(osdev
);
7331 } else if (strcmp(fstype
, "ufs") == 0) {
7332 BAM_DPRINTF(("%s: created new UFS sign\n", fcn
));
7333 sign
= create_ufs_sign();
7335 bam_error(_("boot signature not supported for fstype: %s\n"),
7340 BAM_DPRINTF(("%s: created new sign: %s\n", fcn
,
7341 sign
? sign
: "<NULL>"));
7346 set_backup_common(char *mntpt
, char *sign
)
7349 char backup
[PATH_MAX
];
7350 char tmpsign
[PATH_MAX
];
7356 const char *fcn
= "set_backup_common()";
7358 (void) snprintf(backup
, sizeof (backup
), "%s%s",
7359 mntpt
, GRUBSIGN_BACKUP
);
7361 /* First read the backup */
7362 bfp
= fopen(backup
, "r");
7364 while (s_fgets(tmpsign
, sizeof (tmpsign
), bfp
)) {
7365 if (strcmp(tmpsign
, sign
) == 0) {
7366 BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7373 BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7376 BAM_DPRINTF(("%s: no backup file (%s) found.\n", fcn
, backup
));
7380 * Didn't find the correct signature. First create
7381 * the directory if necessary.
7384 /* dirname() modifies its argument so dup it */
7385 backup_dup
= s_strdup(backup
);
7386 bdir
= dirname(backup_dup
);
7389 ret
= stat(bdir
, &sb
);
7390 INJECT_ERROR1("SET_BACKUP_STAT", ret
= -1);
7392 BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7394 ret
= mkdirp(bdir
, DIR_PERMS
);
7396 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret
= -1);
7398 bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7399 GRUBSIGN_BACKUP
, strerror(error
));
7407 * Open the backup in append mode to add the correct
7410 bfp
= fopen(backup
, "a");
7412 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp
= NULL
);
7414 bam_error(_("error opening boot signature backup "
7415 "file %s: %s\n"), GRUBSIGN_BACKUP
, strerror(error
));
7419 (void) snprintf(tmpsign
, sizeof (tmpsign
), "%s\n", sign
);
7421 ret
= fputs(tmpsign
, bfp
);
7423 INJECT_ERROR1("SET_BACKUP_FPUTS", ret
= 0);
7424 if (ret
!= strlen(tmpsign
)) {
7425 bam_error(_("error writing boot signature backup "
7426 "file %s: %s\n"), GRUBSIGN_BACKUP
, strerror(error
));
7434 bam_print(_("updated boot signature backup file %s\n"),
7437 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7443 set_backup_ufs(char *osroot
, char *sign
)
7445 const char *fcn
= "set_backup_ufs()";
7447 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, sign
));
7448 return (set_backup_common(osroot
, sign
));
7452 set_backup_zfs(char *osdev
, char *sign
)
7458 const char *fcn
= "set_backup_zfs()";
7460 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osdev
, sign
));
7462 pool
= get_pool(osdev
);
7463 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool
= NULL
);
7465 bam_error(_("failed to get pool name from %s\n"), osdev
);
7469 mntpt
= mount_top_dataset(pool
, &mnted
);
7470 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt
= NULL
);
7471 if (mntpt
== NULL
) {
7472 bam_error(_("failed to mount top dataset for %s\n"), pool
);
7477 ret
= set_backup_common(mntpt
, sign
);
7479 (void) umount_top_dataset(pool
, mnted
, mntpt
);
7483 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret
= 1);
7485 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7487 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7494 set_backup(char *osroot
, char *osdev
, char *sign
, char *fstype
)
7496 const char *fcn
= "set_backup()";
7499 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype
= "foofs");
7501 if (strcmp(fstype
, "ufs") == 0) {
7502 BAM_DPRINTF(("%s: setting UFS backup sign\n", fcn
));
7503 ret
= set_backup_ufs(osroot
, sign
);
7504 } else if (strcmp(fstype
, "zfs") == 0) {
7505 BAM_DPRINTF(("%s: setting ZFS backup sign\n", fcn
));
7506 ret
= set_backup_zfs(osdev
, sign
);
7508 bam_error(_("boot signature not supported for fstype: %s\n"),
7514 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7516 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7523 set_primary_common(char *mntpt
, char *sign
)
7525 char signfile
[PATH_MAX
];
7526 char signdir
[PATH_MAX
];
7531 const char *fcn
= "set_primary_common()";
7533 (void) snprintf(signfile
, sizeof (signfile
), "%s/%s/%s",
7534 mntpt
, GRUBSIGN_DIR
, sign
);
7536 if (stat(signfile
, &sb
) != -1) {
7538 bam_print(_("primary sign %s exists\n"), sign
);
7541 BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7545 (void) snprintf(signdir
, sizeof (signdir
), "%s/%s",
7546 mntpt
, GRUBSIGN_DIR
);
7548 if (stat(signdir
, &sb
) == -1) {
7549 BAM_DPRINTF(("%s: primary signdir (%s) does not exist\n",
7551 ret
= mkdirp(signdir
, DIR_PERMS
);
7553 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret
= -1);
7555 bam_error(_("error creating boot signature "
7556 "directory %s: %s\n"), signdir
, strerror(errno
));
7561 fd
= open(signfile
, O_RDWR
|O_CREAT
|O_TRUNC
, 0444);
7563 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd
= -1);
7565 bam_error(_("error creating primary boot signature %s: %s\n"),
7566 signfile
, strerror(error
));
7572 INJECT_ERROR1("PRIMARY_FSYNC", ret
= -1);
7574 bam_error(_("error syncing primary boot signature %s: %s\n"),
7575 signfile
, strerror(error
));
7581 bam_print(_("created primary GRUB boot signature: %s\n"),
7584 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7590 set_primary_ufs(char *osroot
, char *sign
)
7592 const char *fcn
= "set_primary_ufs()";
7594 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, sign
));
7595 return (set_primary_common(osroot
, sign
));
7599 set_primary_zfs(char *osdev
, char *sign
)
7605 const char *fcn
= "set_primary_zfs()";
7607 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osdev
, sign
));
7609 pool
= get_pool(osdev
);
7610 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool
= NULL
);
7612 bam_error(_("failed to get pool name from %s\n"), osdev
);
7616 /* Pool name must exist in the sign */
7617 ret
= (strstr(sign
, pool
) != NULL
);
7618 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret
= 0);
7620 bam_error(_("pool name %s not present in signature %s\n"),
7626 mntpt
= mount_top_dataset(pool
, &mnted
);
7627 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt
= NULL
);
7628 if (mntpt
== NULL
) {
7629 bam_error(_("failed to mount top dataset for %s\n"), pool
);
7634 ret
= set_primary_common(mntpt
, sign
);
7636 (void) umount_top_dataset(pool
, mnted
, mntpt
);
7640 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret
= 1);
7642 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7644 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7651 set_primary(char *osroot
, char *osdev
, char *sign
, char *fstype
)
7653 const char *fcn
= "set_primary()";
7656 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype
= "foofs");
7657 if (strcmp(fstype
, "ufs") == 0) {
7658 BAM_DPRINTF(("%s: setting UFS primary sign\n", fcn
));
7659 ret
= set_primary_ufs(osroot
, sign
);
7660 } else if (strcmp(fstype
, "zfs") == 0) {
7661 BAM_DPRINTF(("%s: setting ZFS primary sign\n", fcn
));
7662 ret
= set_primary_zfs(osdev
, sign
);
7664 bam_error(_("boot signature not supported for fstype: %s\n"),
7670 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7672 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7679 ufs_add_to_sign_list(char *sign
)
7682 char signline
[MAXNAMELEN
];
7686 const char *fcn
= "ufs_add_to_sign_list()";
7688 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign
= "pool_rpool5");
7689 if (strncmp(sign
, GRUBSIGN_UFS_PREFIX
,
7690 strlen(GRUBSIGN_UFS_PREFIX
)) != 0) {
7691 bam_error(_("invalid UFS boot signature %s\n"), sign
);
7692 (void) unlink(UFS_SIGNATURE_LIST
);
7697 * most failures in this routine are not a fatal error
7698 * We simply unlink the /var/run file and continue
7701 ret
= rename(UFS_SIGNATURE_LIST
, UFS_SIGNATURE_LIST
".tmp");
7703 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret
= -1);
7705 bam_error(_("rename to file failed: %s: %s\n"),
7706 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7707 (void) unlink(UFS_SIGNATURE_LIST
);
7711 tfp
= fopen(UFS_SIGNATURE_LIST
".tmp", "a");
7713 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp
= NULL
);
7715 bam_error(_("failed to open file: %s: %s\n"),
7716 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7717 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7721 (void) snprintf(signline
, sizeof (signline
), "%s\n", sign
);
7723 ret
= fputs(signline
, tfp
);
7725 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret
= 0);
7726 if (ret
!= strlen(signline
)) {
7727 bam_error(_("failed to write signature %s to signature "
7728 "list: %s\n"), sign
, strerror(error
));
7730 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7736 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret
= EOF
);
7738 bam_error(_("failed to close file: %s: %s\n"),
7739 UFS_SIGNATURE_LIST
".tmp", strerror(error
));
7740 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7744 /* Sort the list again */
7745 (void) snprintf(cmd
, sizeof (cmd
),
7746 "/usr/bin/sort -u %s.tmp > %s.sorted",
7747 UFS_SIGNATURE_LIST
, UFS_SIGNATURE_LIST
);
7749 ret
= exec_cmd(cmd
, NULL
);
7750 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret
= 1);
7752 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7753 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
7754 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7758 (void) unlink(UFS_SIGNATURE_LIST
".tmp");
7760 ret
= rename(UFS_SIGNATURE_LIST
".sorted", UFS_SIGNATURE_LIST
);
7762 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret
= -1);
7764 bam_error(_("rename to file failed: %s: %s\n"),
7765 UFS_SIGNATURE_LIST
, strerror(error
));
7766 (void) unlink(UFS_SIGNATURE_LIST
".sorted");
7770 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7776 set_signature(char *osroot
, char *osdev
, char *sign
, char *fstype
)
7779 const char *fcn
= "set_signature()";
7781 BAM_DPRINTF(("%s: entered. args: %s %s %s %s\n", fcn
,
7782 osroot
, osdev
, sign
, fstype
));
7784 ret
= set_backup(osroot
, osdev
, sign
, fstype
);
7785 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret
= -1);
7787 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7788 bam_error(_("failed to set backup sign (%s) for %s: %s\n"),
7789 sign
, osroot
, osdev
);
7793 ret
= set_primary(osroot
, osdev
, sign
, fstype
);
7794 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret
= -1);
7797 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7799 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
7800 bam_error(_("failed to set primary sign (%s) for %s: %s\n"),
7801 sign
, osroot
, osdev
);
7808 get_grubsign(char *osroot
, char *osdev
)
7810 char *grubsign
; /* (<sign>,#,#) */
7816 const char *fcn
= "get_grubsign()";
7818 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, osdev
));
7819 fstype
= get_fstype(osroot
);
7820 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype
= NULL
);
7821 if (fstype
== NULL
) {
7822 bam_error(_("failed to get fstype for %s\n"), osroot
);
7826 sign
= find_existing_sign(osroot
, osdev
, fstype
);
7827 INJECT_ERROR1("FIND_EXISTING_SIGN", sign
= NULL
);
7829 BAM_DPRINTF(("%s: no existing grubsign for %s: %s\n",
7830 fcn
, osroot
, osdev
));
7831 sign
= create_new_sign(osdev
, fstype
);
7832 INJECT_ERROR1("CREATE_NEW_SIGN", sign
= NULL
);
7834 bam_error(_("failed to create GRUB boot signature for "
7835 "device: %s\n"), osdev
);
7841 ret
= set_signature(osroot
, osdev
, sign
, fstype
);
7842 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret
= -1);
7844 bam_error(_("failed to write GRUB boot signature for "
7845 "device: %s\n"), osdev
);
7848 (void) unlink(UFS_SIGNATURE_LIST
);
7855 bam_print(_("found or created GRUB signature %s for %s\n"),
7858 fdiskpart
= get_partition(osdev
);
7859 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart
= PARTNO_NOTFOUND
);
7860 if (fdiskpart
== PARTNO_NOTFOUND
) {
7861 bam_error(_("failed to determine fdisk partition: %s\n"),
7867 slice
= strrchr(osdev
, 's');
7869 if (fdiskpart
== PARTNO_EFI
) {
7870 fdiskpart
= atoi(&slice
[1]);
7874 grubsign
= s_calloc(1, MAXNAMELEN
+ 10);
7876 (void) snprintf(grubsign
, MAXNAMELEN
+ 10, "(%s,%d,%c)",
7877 sign
, fdiskpart
, slice
[1] + 'a' - '0');
7879 (void) snprintf(grubsign
, MAXNAMELEN
+ 10, "(%s,%d)",
7884 BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn
, grubsign
));
7890 get_title(char *rootdir
)
7892 static char title
[80];
7894 char release
[PATH_MAX
];
7896 const char *fcn
= "get_title()";
7898 /* open the /etc/release file */
7899 (void) snprintf(release
, sizeof (release
), "%s/etc/release", rootdir
);
7901 fp
= fopen(release
, "r");
7903 bam_error(_("failed to open file: %s: %s\n"), release
,
7909 /* grab first line of /etc/release */
7910 cp
= s_fgets(title
, sizeof (title
), fp
);
7912 while (isspace(*cp
)) /* remove leading spaces */
7919 cp
= cp
? cp
: "Oracle Solaris";
7921 BAM_DPRINTF(("%s: got title: %s\n", fcn
, cp
));
7927 get_special(char *mountp
)
7930 struct mnttab mp
= {0};
7931 struct mnttab mpref
= {0};
7934 const char *fcn
= "get_special()";
7936 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp
= NULL
);
7937 if (mountp
== NULL
) {
7938 bam_error(_("cannot get special file: NULL mount-point\n"));
7942 mntfp
= fopen(MNTTAB
, "r");
7944 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp
= NULL
);
7945 if (mntfp
== NULL
) {
7946 bam_error(_("failed to open file: %s: %s\n"), MNTTAB
,
7951 if (*mountp
== '\0')
7952 mpref
.mnt_mountp
= "/";
7954 mpref
.mnt_mountp
= mountp
;
7956 ret
= getmntany(mntfp
, &mp
, &mpref
);
7957 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret
= 1);
7959 (void) fclose(mntfp
);
7960 BAM_DPRINTF(("%s: Cannot get special file: mount-point %s "
7961 "not in mnttab\n", fcn
, mountp
));
7964 (void) fclose(mntfp
);
7966 BAM_DPRINTF(("%s: returning special: %s\n", fcn
, mp
.mnt_special
));
7968 return (s_strdup(mp
.mnt_special
));
7972 free_physarray(char **physarray
, int n
)
7975 const char *fcn
= "free_physarray()";
7980 BAM_DPRINTF(("%s: entering args: %d\n", fcn
, n
));
7982 for (i
= 0; i
< n
; i
++) {
7987 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
7991 zfs_get_physical(char *special
, char ***physarray
, int *n
)
7993 char sdup
[PATH_MAX
];
7997 filelist_t flist
= {0};
8003 const char *fcn
= "zfs_get_physical()";
8007 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, special
));
8009 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special
= "/foo");
8010 if (special
[0] == '/') {
8011 bam_error(_("invalid device for ZFS filesystem: %s\n"),
8016 (void) strlcpy(sdup
, special
, sizeof (sdup
));
8018 pool
= strtok(sdup
, "/");
8019 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool
= NULL
);
8021 bam_error(_("cannot derive ZFS pool from special: %s\n"),
8026 (void) snprintf(cmd
, sizeof (cmd
), "/sbin/zpool status %s", pool
);
8028 ret
= exec_cmd(cmd
, &flist
);
8029 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret
= 1);
8031 bam_error(_("cannot get zpool status for pool: %s\n"), pool
);
8035 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist
.head
= NULL
);
8036 if (flist
.head
== NULL
) {
8037 bam_error(_("bad zpool status for pool=%s\n"), pool
);
8038 filelist_free(&flist
);
8042 for (lp
= flist
.head
; lp
; lp
= lp
->next
) {
8043 BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
8045 comp1
= strtok(lp
->line
, " \t");
8046 if (comp1
== NULL
) {
8050 comp1
= s_strdup(comp1
);
8056 for (lp
= flist
.head
; lp
; lp
= lp
->next
) {
8057 if (lp
->line
== NULL
)
8059 if (strcmp(lp
->line
, pool
) == 0) {
8060 BAM_DPRINTF(("%s: found pool name: %s in zpool "
8061 "status\n", fcn
, pool
));
8067 bam_error(_("no pool name %s in zpool status\n"), pool
);
8068 filelist_free(&flist
);
8073 for (i
= 0, lp
= startlp
; lp
; lp
= lp
->next
) {
8074 if (lp
->line
== NULL
)
8076 if (strcmp(lp
->line
, "mirror") == 0)
8078 if (lp
->line
[0] == '\0' || strcmp(lp
->line
, "errors:") == 0)
8081 BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
8086 bam_error(_("no physical device in zpool status for pool=%s\n"),
8088 filelist_free(&flist
);
8093 *physarray
= s_calloc(*n
, sizeof (char *));
8094 for (i
= 0, lp
= startlp
; lp
; lp
= lp
->next
) {
8095 if (lp
->line
== NULL
)
8097 if (strcmp(lp
->line
, "mirror") == 0)
8099 if (strcmp(lp
->line
, "errors:") == 0)
8101 if (strncmp(lp
->line
, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
8102 strncmp(lp
->line
, "/dev/rdsk/",
8103 strlen("/dev/rdsk/")) != 0) {
8104 (void) snprintf(dsk
, sizeof (dsk
), "/dev/rdsk/%s",
8107 (void) strlcpy(dsk
, lp
->line
, sizeof (dsk
));
8109 BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
8111 (*physarray
)[i
++] = s_strdup(dsk
);
8116 filelist_free(&flist
);
8118 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8123 get_physical(char *menu_root
, char ***physarray
, int *n
)
8127 const char *fcn
= "get_physical()";
8136 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, menu_root
));
8138 /* First get the device special file from /etc/mnttab */
8139 special
= get_special(menu_root
);
8140 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special
= NULL
);
8141 if (special
== NULL
) {
8142 bam_error(_("cannot get special file for mount-point: %s\n"),
8147 /* If already a physical device nothing to do */
8148 if (strncmp(special
, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
8149 strncmp(special
, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
8150 BAM_DPRINTF(("%s: got physical device already directly for "
8151 "menu_root=%s special=%s\n", fcn
, menu_root
, special
));
8152 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8153 *physarray
= s_calloc(1, sizeof (char *));
8154 (*physarray
)[0] = special
;
8159 if (is_zfs(menu_root
)) {
8160 ret
= zfs_get_physical(special
, physarray
, n
);
8162 bam_error(_("cannot derive physical device for %s (%s), "
8163 "unsupported filesystem\n"), menu_root
, special
);
8169 INJECT_ERROR1("GET_PHYSICAL_RET", ret
= -1);
8171 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
8175 for (i
= 0; i
< *n
; i
++) {
8176 BAM_DPRINTF(("%s: returning physical=%s\n",
8177 fcn
, (*physarray
)[i
]));
8185 is_bootdisk(char *osroot
, char *physical
)
8190 const char *fcn
= "is_bootdisk()";
8195 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, physical
));
8197 bootp
= strstr(physical
, "p0:boot");
8201 * We just want the BIOS mapping for menu disk.
8202 * Don't pass menu_root to get_grubroot() as the
8203 * check that it is used for is not relevant here.
8204 * The osroot is immaterial as well - it is only used to
8205 * to find create_diskmap script. Everything hinges on
8208 grubroot
= get_grubroot(osroot
, physical
, NULL
);
8210 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot
= NULL
);
8211 if (grubroot
== NULL
) {
8213 bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8214 "disk: %s\n"), physical
);
8217 ret
= grubroot
[3] == '0';
8220 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, ret
));
8226 * Check if menu is on the boot device
8227 * Return 0 (false) on error
8230 menu_on_bootdisk(char *osroot
, char *menu_root
)
8237 const char *fcn
= "menu_on_bootdisk()";
8239 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, menu_root
));
8241 ret
= get_physical(menu_root
, &physarray
, &n
);
8242 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret
= -1);
8244 bam_error(_("cannot get physical device special file for menu "
8245 "root: %s\n"), menu_root
);
8253 for (i
= 0; i
< n
; i
++) {
8254 assert(strncmp(physarray
[i
], "/dev/dsk/",
8255 strlen("/dev/dsk/")) == 0 ||
8256 strncmp(physarray
[i
], "/dev/rdsk/",
8257 strlen("/dev/rdsk/")) == 0);
8259 BAM_DPRINTF(("%s: checking if phys-device=%s is on bootdisk\n",
8260 fcn
, physarray
[i
]));
8261 if (is_bootdisk(osroot
, physarray
[i
])) {
8263 BAM_DPRINTF(("%s: phys-device=%s *IS* on bootdisk\n",
8264 fcn
, physarray
[i
]));
8268 free_physarray(physarray
, n
);
8270 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk
= 1);
8271 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk
= 0);
8273 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8275 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
8278 return (on_bootdisk
);
8282 bam_add_line(menu_t
*mp
, entry_t
*entry
, line_t
*prev
, line_t
*lp
)
8284 const char *fcn
= "bam_add_line()";
8291 lp
->next
= prev
->next
;
8293 BAM_DPRINTF(("%s: previous next exists\n", fcn
));
8294 prev
->next
->prev
= lp
;
8296 BAM_DPRINTF(("%s: previous next does not exist\n", fcn
));
8301 if (entry
->end
== prev
) {
8302 BAM_DPRINTF(("%s: last line in entry\n", fcn
));
8305 if (mp
->end
== prev
) {
8306 assert(lp
->next
== NULL
);
8308 BAM_DPRINTF(("%s: last line in menu\n", fcn
));
8313 * look for matching bootadm entry with specified parameters
8314 * Here are the rules (based on existing usage):
8315 * - If title is specified, match on title only
8316 * - Else, match on root/findroot, kernel, and module.
8317 * Note that, if root_opt is non-zero, the absence of
8318 * root line is considered a match.
8334 const char *fcn
= "find_boot_entry()";
8337 *entry_num
= BAM_ERROR
;
8339 /* find matching entry */
8340 for (i
= 0, ent
= mp
->entries
; ent
; i
++, ent
= ent
->next
) {
8343 /* first line of entry must be bootadm comment */
8345 if (lp
->flags
!= BAM_COMMENT
||
8346 strcmp(lp
->arg
, BAM_BOOTADM_HDR
) != 0) {
8350 /* advance to title line */
8353 if (lp
->flags
== BAM_TITLE
&& lp
->arg
&&
8354 strcmp(lp
->arg
, title
) == 0) {
8355 BAM_DPRINTF(("%s: matched title: %s\n",
8359 BAM_DPRINTF(("%s: no match title: %s, %s\n",
8360 fcn
, title
, lp
->arg
));
8361 continue; /* check title only */
8364 lp
= lp
->next
; /* advance to root line */
8367 } else if (lp
->cmd
!= NULL
&&
8368 strcmp(lp
->cmd
, menu_cmds
[FINDROOT_CMD
]) == 0) {
8369 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8371 if (findroot
== NULL
) {
8372 BAM_DPRINTF(("%s: no match line has findroot, "
8373 "we don't: %s\n", fcn
, lp
->arg
));
8376 /* findroot command found, try match */
8377 if (strcmp(lp
->arg
, findroot
) != 0) {
8378 BAM_DPRINTF(("%s: no match findroot: %s, %s\n",
8379 fcn
, findroot
, lp
->arg
));
8382 BAM_DPRINTF(("%s: matched findroot: %s\n",
8384 lp
= lp
->next
; /* advance to kernel line */
8385 } else if (lp
->cmd
!= NULL
&&
8386 strcmp(lp
->cmd
, menu_cmds
[ROOT_CMD
]) == 0) {
8387 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root
= NULL
);
8389 BAM_DPRINTF(("%s: no match, line has root, we "
8390 "don't: %s\n", fcn
, lp
->arg
));
8393 /* root cmd found, try match */
8394 if (strcmp(lp
->arg
, root
) != 0) {
8395 BAM_DPRINTF(("%s: no match root: %s, %s\n",
8396 fcn
, root
, lp
->arg
));
8399 BAM_DPRINTF(("%s: matched root: %s\n", fcn
, root
));
8400 lp
= lp
->next
; /* advance to kernel line */
8402 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8404 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8406 /* no root command, see if root is optional */
8407 if (root_opt
== 0) {
8408 BAM_DPRINTF(("%s: root NOT optional\n", fcn
));
8411 BAM_DPRINTF(("%s: root IS optional\n", fcn
));
8414 if (lp
== NULL
|| lp
->next
== NULL
) {
8419 (!check_cmd(lp
->cmd
, KERNEL_CMD
, lp
->arg
, kernel
))) {
8420 if (!(ent
->flags
& BAM_ENTRY_FAILSAFE
) ||
8421 !(ent
->flags
& BAM_ENTRY_DBOOT
) ||
8422 strcmp(kernel
, DIRECT_BOOT_FAILSAFE_LINE
) != 0)
8425 ent
->flags
|= BAM_ENTRY_UPGFSKERNEL
;
8428 BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn
,
8432 * Check for matching module entry (failsafe or normal).
8433 * If it fails to match, we go around the loop again.
8434 * For xpv entries, there are two module lines, so we
8435 * do the check twice.
8437 lp
= lp
->next
; /* advance to module line */
8438 if (check_cmd(lp
->cmd
, MODULE_CMD
, lp
->arg
, module
) ||
8439 (((lp
= lp
->next
) != NULL
) &&
8440 check_cmd(lp
->cmd
, MODULE_CMD
, lp
->arg
, module
))) {
8442 BAM_DPRINTF(("%s: module match: %s, %s\n", fcn
,
8447 if (strcmp(module
, FAILSAFE_ARCHIVE
) == 0 &&
8448 (strcmp(lp
->prev
->arg
, FAILSAFE_ARCHIVE_32
) == 0 ||
8449 strcmp(lp
->prev
->arg
, FAILSAFE_ARCHIVE_64
) == 0)) {
8450 ent
->flags
|= BAM_ENTRY_UPGFSMODULE
;
8456 if (ent
&& entry_num
) {
8461 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, i
));
8463 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, BAM_ERROR
));
8469 update_boot_entry(menu_t
*mp
, char *title
, char *findroot
, char *root
,
8470 char *kernel
, char *mod_kernel
, char *module
, int root_opt
)
8473 int change_kernel
= 0;
8477 char linebuf
[BAM_MAXLINE
];
8478 const char *fcn
= "update_boot_entry()";
8480 /* note: don't match on title, it's updated on upgrade */
8481 ent
= find_boot_entry(mp
, NULL
, kernel
, findroot
, root
, module
,
8483 if ((ent
== NULL
) && (bam_direct
== BAM_DIRECT_DBOOT
)) {
8485 * We may be upgrading a kernel from multiboot to
8486 * directboot. Look for a multiboot entry. A multiboot
8487 * entry will not have a findroot line.
8489 ent
= find_boot_entry(mp
, NULL
, "multiboot", NULL
, root
,
8490 MULTIBOOT_ARCHIVE
, root_opt
, &i
);
8492 BAM_DPRINTF(("%s: upgrading entry from dboot to "
8493 "multiboot: root = %s\n", fcn
, root
));
8497 BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8502 BAM_DPRINTF(("%s: boot entry not found in menu. Creating "
8503 "new entry, findroot = %s\n", fcn
, findroot
));
8504 return (add_boot_entry(mp
, title
, findroot
,
8505 kernel
, mod_kernel
, module
, NULL
));
8508 /* replace title of existing entry and update findroot line */
8510 lp
= lp
->next
; /* title line */
8511 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8512 menu_cmds
[TITLE_CMD
], menu_cmds
[SEP_CMD
], title
);
8515 lp
->arg
= s_strdup(title
);
8516 lp
->line
= s_strdup(linebuf
);
8517 BAM_DPRINTF(("%s: changing title to: %s\n", fcn
, title
));
8519 tlp
= lp
; /* title line */
8520 lp
= lp
->next
; /* root line */
8522 /* if no root or findroot command, create a new line_t */
8523 if ((lp
->cmd
!= NULL
) && (strcmp(lp
->cmd
, menu_cmds
[ROOT_CMD
]) != 0 &&
8524 strcmp(lp
->cmd
, menu_cmds
[FINDROOT_CMD
]) != 0)) {
8525 lp
= s_calloc(1, sizeof (line_t
));
8526 bam_add_line(mp
, ent
, tlp
, lp
);
8528 if (lp
->cmd
!= NULL
)
8536 lp
->cmd
= s_strdup(menu_cmds
[FINDROOT_CMD
]);
8537 lp
->sep
= s_strdup(menu_cmds
[SEP_CMD
]);
8538 lp
->arg
= s_strdup(findroot
);
8539 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8540 menu_cmds
[FINDROOT_CMD
], menu_cmds
[SEP_CMD
], findroot
);
8541 lp
->line
= s_strdup(linebuf
);
8542 BAM_DPRINTF(("%s: adding findroot line: %s\n", fcn
, findroot
));
8547 if (ent
->flags
& BAM_ENTRY_UPGFSKERNEL
) {
8548 char *params
= NULL
;
8550 params
= strstr(lp
->line
, "-s");
8552 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s%s",
8553 menu_cmds
[KERNEL_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8556 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8557 menu_cmds
[KERNEL_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8560 if (lp
->cmd
!= NULL
)
8565 lp
->cmd
= s_strdup(menu_cmds
[KERNEL_DOLLAR_CMD
]);
8566 lp
->arg
= s_strdup(strstr(linebuf
, "/"));
8567 lp
->line
= s_strdup(linebuf
);
8568 ent
->flags
&= ~BAM_ENTRY_UPGFSKERNEL
;
8569 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8570 fcn
, lp
->prev
->cmd
));
8573 if (change_kernel
) {
8575 * We're upgrading from multiboot to directboot.
8577 if (lp
->cmd
!= NULL
&&
8578 strcmp(lp
->cmd
, menu_cmds
[KERNEL_CMD
]) == 0) {
8579 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8580 menu_cmds
[KERNEL_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8585 lp
->cmd
= s_strdup(menu_cmds
[KERNEL_DOLLAR_CMD
]);
8586 lp
->arg
= s_strdup(kernel
);
8587 lp
->line
= s_strdup(linebuf
);
8589 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8592 if (lp
->cmd
!= NULL
&&
8593 strcmp(lp
->cmd
, menu_cmds
[MODULE_CMD
]) == 0) {
8594 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8595 menu_cmds
[MODULE_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8600 lp
->cmd
= s_strdup(menu_cmds
[MODULE_DOLLAR_CMD
]);
8601 lp
->arg
= s_strdup(module
);
8602 lp
->line
= s_strdup(linebuf
);
8604 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8612 if (ent
->flags
& BAM_ENTRY_UPGFSMODULE
) {
8613 if (lp
->cmd
!= NULL
&&
8614 strcmp(lp
->cmd
, menu_cmds
[MODULE_CMD
]) == 0) {
8615 (void) snprintf(linebuf
, sizeof (linebuf
), "%s%s%s",
8616 menu_cmds
[MODULE_DOLLAR_CMD
], menu_cmds
[SEP_CMD
],
8621 lp
->cmd
= s_strdup(menu_cmds
[MODULE_DOLLAR_CMD
]);
8622 lp
->arg
= s_strdup(module
);
8623 lp
->line
= s_strdup(linebuf
);
8625 ent
->flags
&= ~BAM_ENTRY_UPGFSMODULE
;
8626 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8631 BAM_DPRINTF(("%s: returning ret = %d\n", fcn
, i
));
8636 root_optional(char *osroot
, char *menu_root
)
8644 const char *fcn
= "root_optional()";
8646 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
, osroot
, menu_root
));
8649 * For all filesystems except ZFS, a straight compare of osroot
8650 * and menu_root will tell us if root is optional.
8651 * For ZFS, the situation is complicated by the fact that
8652 * menu_root and osroot are always different
8654 ret1
= is_zfs(osroot
);
8655 ret2
= is_zfs(menu_root
);
8656 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1
= 0);
8657 if (!ret1
|| !ret2
) {
8658 BAM_DPRINTF(("%s: one or more non-ZFS filesystems (%s, %s)\n",
8659 fcn
, osroot
, menu_root
));
8660 root_opt
= (strcmp(osroot
, menu_root
) == 0);
8664 ospecial
= get_special(osroot
);
8665 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial
= NULL
);
8666 if (ospecial
== NULL
) {
8667 bam_error(_("failed to get special file for osroot: %s\n"),
8671 BAM_DPRINTF(("%s: ospecial=%s for osroot=%s\n", fcn
, ospecial
, osroot
));
8673 mspecial
= get_special(menu_root
);
8674 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial
= NULL
);
8675 if (mspecial
== NULL
) {
8676 bam_error(_("failed to get special file for menu_root: %s\n"),
8681 BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8682 fcn
, mspecial
, menu_root
));
8684 slash
= strchr(ospecial
, '/');
8687 BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8688 fcn
, ospecial
, osroot
));
8690 root_opt
= (strcmp(ospecial
, mspecial
) == 0);
8696 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt
= 0);
8697 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt
= 1);
8699 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8701 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
8709 update_entry(menu_t
*mp
, char *menu_root
, char *osdev
)
8715 char osroot
[PATH_MAX
];
8716 char *failsafe_kernel
= NULL
;
8719 char failsafe_64
[256];
8721 const char *fcn
= "update_entry()";
8728 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn
, menu_root
, osdev
,
8731 (void) strlcpy(osroot
, bam_root
, sizeof (osroot
));
8733 title
= get_title(osroot
);
8736 grubsign
= get_grubsign(osroot
, osdev
);
8737 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign
= NULL
);
8738 if (grubsign
== NULL
) {
8739 bam_error(_("failed to get grubsign for root: %s, device %s\n"),
8745 * It is not a fatal error if get_grubroot() fails
8746 * We no longer rely on biosdev to populate the
8749 grubroot
= get_grubroot(osroot
, osdev
, menu_root
);
8750 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot
= NULL
);
8752 BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8753 "menu_root=%s\n", fcn
, osroot
, osdev
, menu_root
));
8755 BAM_DPRINTF(("%s: get_grubroot failed. osroot=%s, osdev=%s, "
8756 "menu_root=%s\n", fcn
, osroot
, osdev
, menu_root
));
8759 /* add the entry for normal Solaris */
8760 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8761 bam_direct
= BAM_DIRECT_MULTIBOOT
);
8762 if (bam_direct
== BAM_DIRECT_DBOOT
) {
8763 entry
= update_boot_entry(mp
, title
, grubsign
, grubroot
,
8764 (bam_zfs
? DIRECT_BOOT_KERNEL_ZFS
: DIRECT_BOOT_KERNEL
),
8765 NULL
, DIRECT_BOOT_ARCHIVE
,
8766 root_optional(osroot
, menu_root
));
8767 BAM_DPRINTF(("%s: updated boot entry bam_zfs=%d, "
8768 "grubsign = %s\n", fcn
, bam_zfs
, grubsign
));
8769 if ((entry
!= BAM_ERROR
) && (bam_is_hv
== BAM_HV_PRESENT
)) {
8770 (void) update_boot_entry(mp
, NEW_HV_ENTRY
, grubsign
,
8771 grubroot
, XEN_MENU
, bam_zfs
?
8772 XEN_KERNEL_MODULE_LINE_ZFS
: XEN_KERNEL_MODULE_LINE
,
8773 DIRECT_BOOT_ARCHIVE
,
8774 root_optional(osroot
, menu_root
));
8775 BAM_DPRINTF(("%s: updated HV entry bam_zfs=%d, "
8776 "grubsign = %s\n", fcn
, bam_zfs
, grubsign
));
8779 entry
= update_boot_entry(mp
, title
, grubsign
, grubroot
,
8780 MULTI_BOOT
, NULL
, MULTIBOOT_ARCHIVE
,
8781 root_optional(osroot
, menu_root
));
8783 BAM_DPRINTF(("%s: updated MULTIBOOT entry grubsign = %s\n",
8788 * Add the entry for failsafe archive. On a bfu'd system, the
8789 * failsafe may be different than the installed kernel.
8791 (void) snprintf(failsafe
, sizeof (failsafe
), "%s%s",
8792 osroot
, FAILSAFE_ARCHIVE_32
);
8793 (void) snprintf(failsafe_64
, sizeof (failsafe_64
), "%s%s",
8794 osroot
, FAILSAFE_ARCHIVE_64
);
8797 * Check if at least one of the two archives exists
8798 * Using $ISADIR as the default line, we have an entry which works
8799 * for both the cases.
8802 if (stat(failsafe
, &sbuf
) == 0 || stat(failsafe_64
, &sbuf
) == 0) {
8804 /* Figure out where the kernel line should point */
8805 (void) snprintf(failsafe
, sizeof (failsafe
), "%s%s", osroot
,
8806 DIRECT_BOOT_FAILSAFE_32
);
8807 (void) snprintf(failsafe_64
, sizeof (failsafe_64
), "%s%s",
8808 osroot
, DIRECT_BOOT_FAILSAFE_64
);
8809 if (stat(failsafe
, &sbuf
) == 0 ||
8810 stat(failsafe_64
, &sbuf
) == 0) {
8811 failsafe_kernel
= DIRECT_BOOT_FAILSAFE_LINE
;
8813 (void) snprintf(failsafe
, sizeof (failsafe
), "%s%s",
8814 osroot
, MULTI_BOOT_FAILSAFE
);
8815 if (stat(failsafe
, &sbuf
) == 0) {
8816 failsafe_kernel
= MULTI_BOOT_FAILSAFE_LINE
;
8819 if (failsafe_kernel
!= NULL
) {
8820 (void) update_boot_entry(mp
, FAILSAFE_TITLE
, grubsign
,
8821 grubroot
, failsafe_kernel
, NULL
, FAILSAFE_ARCHIVE
,
8822 root_optional(osroot
, menu_root
));
8823 BAM_DPRINTF(("%s: updated FAILSAFE entry "
8824 "failsafe_kernel = %s\n", fcn
, failsafe_kernel
));
8829 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry
= BAM_ERROR
);
8830 if (entry
== BAM_ERROR
) {
8831 bam_error(_("failed to add boot entry with title=%s, grub "
8832 "signature=%s\n"), title
, grubsign
);
8838 update_numbering(mp
);
8839 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
8840 INJECT_ERROR1("SET_DEFAULT_ERROR", ret
= BAM_ERROR
);
8841 if (ret
== BAM_ERROR
) {
8842 bam_error(_("failed to set GRUB menu default to %d\n"), entry
);
8844 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8849 save_default_entry(menu_t
*mp
, const char *which
)
8853 int entry
= 0; /* default is 0 */
8854 char linebuf
[BAM_MAXLINE
];
8855 line_t
*lp
= mp
->curdefault
;
8856 const char *fcn
= "save_default_entry()";
8859 lineNum
= mp
->end
->lineNum
;
8860 entryNum
= mp
->end
->entryNum
;
8862 lineNum
= LINE_INIT
;
8863 entryNum
= ENTRY_INIT
;
8867 entry
= s_strtol(lp
->arg
);
8869 (void) snprintf(linebuf
, sizeof (linebuf
), "#%s%d", which
, entry
);
8870 BAM_DPRINTF(("%s: saving default to: %s\n", fcn
, linebuf
));
8871 line_parser(mp
, linebuf
, &lineNum
, &entryNum
);
8872 BAM_DPRINTF(("%s: saved default to lineNum=%d, entryNum=%d\n", fcn
,
8873 lineNum
, entryNum
));
8877 restore_default_entry(menu_t
*mp
, const char *which
, line_t
*lp
)
8881 const char *fcn
= "restore_default_entry()";
8884 BAM_DPRINTF(("%s: NULL saved default\n", fcn
));
8885 return; /* nothing to restore */
8888 BAM_DPRINTF(("%s: saved default string: %s\n", fcn
, which
));
8890 str
= lp
->arg
+ strlen(which
);
8891 entry
= s_strtol(str
);
8892 (void) set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
8894 BAM_DPRINTF(("%s: restored default to entryNum: %d\n", fcn
, entry
));
8896 /* delete saved old default line */
8897 unlink_line(mp
, lp
);
8902 * This function is for supporting reboot with args.
8903 * The opt value can be:
8904 * NULL delete temp entry, if present
8905 * entry=<n> switches default entry to <n>
8906 * else treated as boot-args and setup a temperary menu entry
8907 * and make it the default
8908 * Note that we are always rebooting the current OS instance
8909 * so osroot == / always.
8911 #define REBOOT_TITLE "Solaris_reboot_transient"
8915 update_temp(menu_t
*mp
, char *dummy
, char *opt
)
8923 char kernbuf
[BUFSIZ
];
8924 char args_buf
[BUFSIZ
];
8925 char signbuf
[PATH_MAX
];
8927 const char *fcn
= "update_temp()";
8930 assert(dummy
== NULL
);
8932 /* opt can be NULL */
8933 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, opt
? opt
: "<NULL>"));
8934 BAM_DPRINTF(("%s: bam_alt_root: %d, bam_root: %s\n", fcn
,
8935 bam_alt_root
, bam_root
));
8937 if (bam_alt_root
|| bam_rootlen
!= 1 ||
8938 strcmp(bam_root
, "/") != 0 ||
8939 strcmp(rootbuf
, "/") != 0) {
8940 bam_error(_("an alternate root (%s) cannot be used with this "
8941 "sub-command\n"), bam_root
);
8945 /* If no option, delete exiting reboot menu entry */
8948 BAM_DPRINTF(("%s: opt is NULL\n", fcn
));
8949 ent
= find_boot_entry(mp
, REBOOT_TITLE
, NULL
, NULL
,
8950 NULL
, NULL
, 0, &entry
);
8951 if (ent
== NULL
) { /* not found is ok */
8952 BAM_DPRINTF(("%s: transient entry not found\n", fcn
));
8953 return (BAM_SUCCESS
);
8955 (void) delete_boot_entry(mp
, entry
, DBE_PRINTERR
);
8956 restore_default_entry(mp
, BAM_OLDDEF
, mp
->olddefault
);
8957 mp
->olddefault
= NULL
;
8958 BAM_DPRINTF(("%s: restored old default\n", fcn
));
8959 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
8963 /* if entry= is specified, set the default entry */
8964 if (strncmp(opt
, "entry=", strlen("entry=")) == 0) {
8965 int entryNum
= s_strtol(opt
+ strlen("entry="));
8966 BAM_DPRINTF(("%s: opt has entry=: %s\n", fcn
, opt
));
8967 if (selector(mp
, opt
, &entry
, NULL
) == BAM_SUCCESS
) {
8968 /* this is entry=# option */
8969 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
8970 BAM_DPRINTF(("%s: default set to %d, "
8971 "set_default ret=%d\n", fcn
, entry
, ret
));
8974 bam_error(_("failed to set GRUB menu default to %d\n"),
8981 * add a new menu entry based on opt and make it the default
8984 fstype
= get_fstype("/");
8985 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype
= NULL
);
8986 if (fstype
== NULL
) {
8987 bam_error(_("failed to determine filesystem type for \"/\". "
8988 "Reboot with \narguments failed.\n"));
8992 osdev
= get_special("/");
8993 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev
= NULL
);
8994 if (osdev
== NULL
) {
8996 bam_error(_("failed to find device special file for \"/\". "
8997 "Reboot with \narguments failed.\n"));
9001 sign
= find_existing_sign("/", osdev
, fstype
);
9002 INJECT_ERROR1("REBOOT_SIGN_NULL", sign
= NULL
);
9006 bam_error(_("failed to find boot signature. Reboot with "
9007 "arguments failed.\n"));
9012 (void) strlcpy(signbuf
, sign
, sizeof (signbuf
));
9015 assert(strchr(signbuf
, '(') == NULL
&& strchr(signbuf
, ',') == NULL
&&
9016 strchr(signbuf
, ')') == NULL
);
9019 * There is no alternate root while doing reboot with args
9020 * This version of bootadm is only delivered with a DBOOT
9021 * version of Solaris.
9023 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct
= BAM_DIRECT_MULTIBOOT
);
9024 if (bam_direct
!= BAM_DIRECT_DBOOT
) {
9026 bam_error(_("the root filesystem is not a dboot Solaris "
9027 "instance. \nThis version of bootadm is not supported "
9028 "on this version of Solaris.\n"));
9032 /* add an entry for Solaris reboot */
9033 if (opt
[0] == '-') {
9034 /* It's an option - first see if boot-file is set */
9035 ret
= get_kernel(mp
, KERNEL_CMD
, kernbuf
, sizeof (kernbuf
));
9036 INJECT_ERROR1("REBOOT_GET_KERNEL", ret
= BAM_ERROR
);
9037 if (ret
!= BAM_SUCCESS
) {
9039 bam_error(_("reboot with arguments: error querying "
9040 "current boot-file settings\n"));
9043 if (kernbuf
[0] == '\0')
9044 (void) strlcpy(kernbuf
, DIRECT_BOOT_KERNEL
,
9047 * If this is a zfs file system and kernbuf does not
9048 * have "-B $ZFS-BOOTFS" string yet, add it.
9050 if (strcmp(fstype
, "zfs") == 0 && !strstr(kernbuf
, ZFS_BOOT
)) {
9051 (void) strlcat(kernbuf
, " ", sizeof (kernbuf
));
9052 (void) strlcat(kernbuf
, ZFS_BOOT
, sizeof (kernbuf
));
9054 (void) strlcat(kernbuf
, " ", sizeof (kernbuf
));
9055 (void) strlcat(kernbuf
, opt
, sizeof (kernbuf
));
9056 BAM_DPRINTF(("%s: reboot with args, option specified: "
9057 "kern=%s\n", fcn
, kernbuf
));
9058 } else if (opt
[0] == '/') {
9059 /* It's a full path, so write it out. */
9060 (void) strlcpy(kernbuf
, opt
, sizeof (kernbuf
));
9065 * # eeprom boot-args='-kd'
9066 * # reboot /platform/i86pc/kernel/unix
9068 * we want to use the boot-args as part of the boot
9069 * line. On the other hand, if someone runs:
9071 * # reboot "/platform/i86pc/kernel/unix -kd"
9073 * we don't need to mess with boot-args. If there's
9074 * no space in the options string, assume we're in the
9077 if (strchr(opt
, ' ') == NULL
) {
9078 ret
= get_kernel(mp
, ARGS_CMD
, args_buf
,
9080 INJECT_ERROR1("REBOOT_GET_ARGS", ret
= BAM_ERROR
);
9081 if (ret
!= BAM_SUCCESS
) {
9083 bam_error(_("reboot with arguments: error "
9084 "querying current boot-args settings\n"));
9088 if (args_buf
[0] != '\0') {
9089 (void) strlcat(kernbuf
, " ", sizeof (kernbuf
));
9090 (void) strlcat(kernbuf
, args_buf
,
9094 BAM_DPRINTF(("%s: reboot with args, abspath specified: "
9095 "kern=%s\n", fcn
, kernbuf
));
9098 * It may be a partial path, or it may be a partial
9099 * path followed by options. Assume that only options
9100 * follow a space. If someone sends us a kernel path
9101 * that includes a space, they deserve to be broken.
9103 opt_ptr
= strchr(opt
, ' ');
9104 if (opt_ptr
!= NULL
) {
9108 path
= expand_path(opt
);
9110 (void) strlcpy(kernbuf
, path
, sizeof (kernbuf
));
9114 * If there were options given, use those.
9115 * Otherwise, copy over the default options.
9117 if (opt_ptr
!= NULL
) {
9118 /* Restore the space in opt string */
9120 (void) strlcat(kernbuf
, opt_ptr
,
9123 ret
= get_kernel(mp
, ARGS_CMD
, args_buf
,
9125 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
9127 if (ret
!= BAM_SUCCESS
) {
9129 bam_error(_("reboot with arguments: "
9130 "error querying current boot-args "
9135 if (args_buf
[0] != '\0') {
9136 (void) strlcat(kernbuf
, " ",
9138 (void) strlcat(kernbuf
,
9139 args_buf
, sizeof (kernbuf
));
9142 BAM_DPRINTF(("%s: resolved partial path: %s\n",
9146 bam_error(_("unable to expand %s to a full file"
9148 bam_print_stderr(_("Rebooting with default kernel "
9154 entry
= add_boot_entry(mp
, REBOOT_TITLE
, signbuf
, kernbuf
,
9156 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry
= BAM_ERROR
);
9157 if (entry
== BAM_ERROR
) {
9158 bam_error(_("Cannot update menu. Cannot reboot with "
9159 "requested arguments\n"));
9163 save_default_entry(mp
, BAM_OLDDEF
);
9164 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entry
);
9165 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret
= BAM_ERROR
);
9166 if (ret
== BAM_ERROR
) {
9167 bam_error(_("reboot with arguments: setting GRUB menu default "
9168 "to %d failed\n"), entry
);
9170 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9175 set_global(menu_t
*mp
, char *globalcmd
, int val
)
9182 char prefix
[BAM_MAXLINE
];
9184 const char *fcn
= "set_global()";
9189 if (strcmp(globalcmd
, menu_cmds
[DEFAULT_CMD
]) == 0) {
9190 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val
= -1);
9191 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp
->end
= NULL
);
9192 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val
= 100);
9193 if (val
< 0 || mp
->end
== NULL
|| val
> mp
->end
->entryNum
) {
9194 (void) snprintf(prefix
, sizeof (prefix
), "%d", val
);
9195 bam_error(_("invalid boot entry number: %s\n"), prefix
);
9200 found
= last
= NULL
;
9201 for (lp
= mp
->start
; lp
; lp
= lp
->next
) {
9202 if (lp
->flags
!= BAM_GLOBAL
)
9205 last
= lp
; /* track the last global found */
9207 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp
->cmd
= NULL
);
9208 if (lp
->cmd
== NULL
) {
9209 bam_error(_("no command at line %d\n"), lp
->lineNum
);
9212 if (strcmp(globalcmd
, lp
->cmd
) != 0)
9215 BAM_DPRINTF(("%s: found matching global command: %s\n",
9219 bam_error(_("duplicate command %s at line %d of "
9220 "%sboot/grub/menu.lst\n"), globalcmd
,
9221 lp
->lineNum
, bam_root
);
9226 if (found
== NULL
) {
9227 lp
= s_calloc(1, sizeof (line_t
));
9229 lp
->next
= mp
->start
;
9231 mp
->end
= (mp
->end
) ? mp
->end
: lp
;
9233 lp
->next
= last
->next
;
9235 if (lp
->next
== NULL
)
9238 lp
->flags
= BAM_GLOBAL
; /* other fields not needed for writes */
9239 len
= strlen(globalcmd
) + strlen(menu_cmds
[SEP_CMD
]);
9240 len
+= 10; /* val < 10 digits */
9241 lp
->line
= s_calloc(1, len
);
9242 (void) snprintf(lp
->line
, len
, "%s%s%d",
9243 globalcmd
, menu_cmds
[SEP_CMD
], val
);
9244 BAM_DPRINTF(("%s: wrote new global line: %s\n", fcn
, lp
->line
));
9245 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9250 * We are changing an existing entry. Retain any prefix whitespace,
9251 * but overwrite everything else. This preserves tabs added for
9256 while (*str
== ' ' || *str
== '\t')
9258 *cp
= '\0'; /* Terminate prefix */
9259 len
= strlen(prefix
) + strlen(globalcmd
);
9260 len
+= strlen(menu_cmds
[SEP_CMD
]) + 10;
9263 found
->line
= s_calloc(1, len
);
9264 (void) snprintf(found
->line
, len
,
9265 "%s%s%s%d", prefix
, globalcmd
, menu_cmds
[SEP_CMD
], val
);
9267 BAM_DPRINTF(("%s: replaced global line with: %s\n", fcn
, found
->line
));
9268 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9269 return (BAM_WRITE
); /* need a write to menu */
9273 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
9274 * expand it to a full unix path. The calling function is expected to
9275 * output a message if an error occurs and NULL is returned.
9278 expand_path(const char *partial_path
)
9282 char new_path2
[PATH_MAX
];
9284 const char *fcn
= "expand_path()";
9286 new_path_len
= strlen(partial_path
) + 64;
9287 new_path
= s_calloc(1, new_path_len
);
9289 /* First, try the simplest case - something like "kernel/unix" */
9290 (void) snprintf(new_path
, new_path_len
, "/platform/i86pc/%s",
9292 if (stat(new_path
, &sb
) == 0) {
9293 BAM_DPRINTF(("%s: expanded path: %s\n", fcn
, new_path
));
9297 if (strcmp(partial_path
, "kmdb") == 0) {
9298 (void) snprintf(new_path
, new_path_len
, "%s -k",
9299 DIRECT_BOOT_KERNEL
);
9300 BAM_DPRINTF(("%s: expanded path: %s\n", fcn
, new_path
));
9305 * We've quickly reached unsupported usage. Try once more to
9306 * see if we were just given a glom name.
9308 (void) snprintf(new_path
, new_path_len
, "/platform/i86pc/%s/unix",
9310 (void) snprintf(new_path2
, PATH_MAX
, "/platform/i86pc/%s/amd64/unix",
9312 if (stat(new_path
, &sb
) == 0) {
9313 if (stat(new_path2
, &sb
) == 0) {
9315 * We matched both, so we actually
9316 * want to write the $ISADIR version.
9318 (void) snprintf(new_path
, new_path_len
,
9319 "/platform/i86pc/kernel/%s/$ISADIR/unix",
9322 BAM_DPRINTF(("%s: expanded path: %s\n", fcn
, new_path
));
9327 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9332 * The kernel cmd and arg have been changed, so
9333 * check whether the archive line needs to change.
9336 set_archive_line(entry_t
*entryp
, line_t
*kernelp
)
9338 line_t
*lp
= entryp
->start
;
9341 const char *fcn
= "set_archive_line()";
9343 for (; lp
!= NULL
; lp
= lp
->next
) {
9344 if (lp
->cmd
!= NULL
&& strncmp(lp
->cmd
, menu_cmds
[MODULE_CMD
],
9345 sizeof (menu_cmds
[MODULE_CMD
]) - 1) == 0) {
9349 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp
= entryp
->end
);
9350 if (lp
== entryp
->end
) {
9351 BAM_DPRINTF(("%s: no module/archive line for entry: "
9352 "%d\n", fcn
, entryp
->entryNum
));
9356 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp
= NULL
);
9358 BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9359 fcn
, entryp
->entryNum
));
9363 if (strstr(kernelp
->arg
, "$ISADIR") != NULL
) {
9364 new_archive
= DIRECT_BOOT_ARCHIVE
;
9365 m_cmd
= MODULE_DOLLAR_CMD
;
9366 } else if (strstr(kernelp
->arg
, "amd64") != NULL
) {
9367 new_archive
= DIRECT_BOOT_ARCHIVE_64
;
9370 new_archive
= DIRECT_BOOT_ARCHIVE_32
;
9374 if (strcmp(lp
->arg
, new_archive
) == 0) {
9375 BAM_DPRINTF(("%s: no change for line: %s\n", fcn
, lp
->arg
));
9379 if (lp
->cmd
!= NULL
&& strcmp(lp
->cmd
, menu_cmds
[m_cmd
]) != 0) {
9381 lp
->cmd
= s_strdup(menu_cmds
[m_cmd
]);
9385 lp
->arg
= s_strdup(new_archive
);
9387 BAM_DPRINTF(("%s: replaced for line: %s\n", fcn
, lp
->line
));
9391 * Title for an entry to set properties that once went in bootenv.rc.
9393 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
9396 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9397 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
9398 * string, reset the value to the default. If path is a non-zero-length
9399 * string, set the kernel or arguments.
9410 int rv
= BAM_SUCCESS
;
9411 int free_new_path
= 0;
9420 size_t old_kernel_len
= 0;
9425 char signbuf
[PATH_MAX
];
9427 const char *fcn
= "get_set_kernel()";
9429 assert(bufsize
> 0);
9431 ptr
= kernelp
= NULL
;
9432 new_arg
= old_args
= space
= NULL
;
9436 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9437 bam_direct
= BAM_DIRECT_MULTIBOOT
);
9438 if (bam_direct
!= BAM_DIRECT_DBOOT
) {
9439 bam_error(_("bootadm set-menu %s may only be run on "
9440 "directboot kernels.\n"),
9441 optnum
== KERNEL_CMD
? "kernel" : "args");
9446 * If a user changed the default entry to a non-bootadm controlled
9447 * one, we don't want to mess with it. Just print an error and
9450 if (mp
->curdefault
) {
9451 entryNum
= s_strtol(mp
->curdefault
->arg
);
9452 for (entryp
= mp
->entries
; entryp
; entryp
= entryp
->next
) {
9453 if (entryp
->entryNum
== entryNum
)
9456 if ((entryp
!= NULL
) &&
9457 ((entryp
->flags
& (BAM_ENTRY_BOOTADM
|BAM_ENTRY_LU
)) == 0)) {
9458 bam_error(_("Default /boot/grub/menu.lst entry is not "
9459 "controlled by bootadm. Exiting\n"));
9464 entryp
= find_boot_entry(mp
, BOOTENV_RC_TITLE
, NULL
, NULL
, NULL
, NULL
,
9467 if (entryp
!= NULL
) {
9468 for (ptr
= entryp
->start
; ptr
&& ptr
!= entryp
->end
;
9470 if (strncmp(ptr
->cmd
, menu_cmds
[KERNEL_CMD
],
9471 sizeof (menu_cmds
[KERNEL_CMD
]) - 1) == 0) {
9476 if (kernelp
== NULL
) {
9477 bam_error(_("no kernel line found in entry %d\n"),
9482 old_kernel_len
= strcspn(kernelp
->arg
, " \t");
9483 space
= old_args
= kernelp
->arg
+ old_kernel_len
;
9484 while ((*old_args
== ' ') || (*old_args
== '\t'))
9489 if (entryp
== NULL
) {
9490 BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9492 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9493 return (BAM_SUCCESS
);
9496 if (optnum
== ARGS_CMD
) {
9497 if (old_args
[0] != '\0') {
9498 (void) strlcpy(buf
, old_args
, bufsize
);
9499 BAM_DPRINTF(("%s: read menu boot-args: %s\n",
9504 * We need to print the kernel, so we just turn the
9505 * first space into a '\0' and print the beginning.
9506 * We don't print anything if it's the default kernel.
9510 if (strcmp(kernelp
->arg
, DIRECT_BOOT_KERNEL
) != 0) {
9511 (void) strlcpy(buf
, kernelp
->arg
, bufsize
);
9512 BAM_DPRINTF(("%s: read menu boot-file: %s\n",
9517 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9518 return (BAM_SUCCESS
);
9522 * First, check if we're resetting an entry to the default.
9524 if ((path
[0] == '\0') ||
9525 ((optnum
== KERNEL_CMD
) &&
9526 (strcmp(path
, DIRECT_BOOT_KERNEL
) == 0))) {
9527 if ((entryp
== NULL
) || (kernelp
== NULL
)) {
9528 /* No previous entry, it's already the default */
9529 BAM_DPRINTF(("%s: no reset, already has default\n",
9531 return (BAM_SUCCESS
);
9535 * Check if we can delete the entry. If we're resetting the
9536 * kernel command, and the args is already empty, or if we're
9537 * resetting the args command, and the kernel is already the
9538 * default, we can restore the old default and delete the entry.
9540 if (((optnum
== KERNEL_CMD
) &&
9541 ((old_args
== NULL
) || (old_args
[0] == '\0'))) ||
9542 ((optnum
== ARGS_CMD
) &&
9543 (strncmp(kernelp
->arg
, DIRECT_BOOT_KERNEL
,
9544 sizeof (DIRECT_BOOT_KERNEL
) - 1) == 0))) {
9546 (void) delete_boot_entry(mp
, entryNum
, DBE_PRINTERR
);
9547 restore_default_entry(mp
, BAM_OLD_RC_DEF
,
9548 mp
->old_rc_default
);
9549 mp
->old_rc_default
= NULL
;
9551 BAM_DPRINTF(("%s: resetting to default\n", fcn
));
9555 if (optnum
== KERNEL_CMD
) {
9557 * At this point, we've already checked that old_args
9558 * and entryp are valid pointers. The "+ 2" is for
9559 * a space a the string termination character.
9561 new_str_len
= (sizeof (DIRECT_BOOT_KERNEL
) - 1) +
9562 strlen(old_args
) + 2;
9563 new_arg
= s_calloc(1, new_str_len
);
9564 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9565 DIRECT_BOOT_KERNEL
, old_args
);
9567 kernelp
->arg
= new_arg
;
9570 * We have changed the kernel line, so we may need
9571 * to update the archive line as well.
9573 set_archive_line(entryp
, kernelp
);
9574 BAM_DPRINTF(("%s: reset kernel to default, but "
9575 "retained old args: %s\n", fcn
, kernelp
->arg
));
9578 * We're resetting the boot args to nothing, so
9579 * we only need to copy the kernel. We've already
9580 * checked that the kernel is not the default.
9582 new_arg
= s_calloc(1, old_kernel_len
+ 1);
9583 (void) snprintf(new_arg
, old_kernel_len
+ 1, "%s",
9586 kernelp
->arg
= new_arg
;
9587 BAM_DPRINTF(("%s: reset args to default, but retained "
9588 "old kernel: %s\n", fcn
, kernelp
->arg
));
9595 * Expand the kernel file to a full path, if necessary
9597 if ((optnum
== KERNEL_CMD
) && (path
[0] != '/')) {
9598 new_path
= expand_path(path
);
9599 if (new_path
== NULL
) {
9600 bam_error(_("unable to expand %s to a full file "
9602 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9612 * At this point, we know we're setting a new value. First, take care
9613 * of the case where there was no previous entry.
9615 if (entryp
== NULL
) {
9617 /* Similar to code in update_temp */
9618 fstype
= get_fstype("/");
9619 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype
= NULL
);
9620 if (fstype
== NULL
) {
9621 bam_error(_("cannot determine filesystem type for "
9622 "\"/\".\nCannot generate GRUB menu entry with "
9623 "EEPROM arguments.\n"));
9628 osdev
= get_special("/");
9629 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev
= NULL
);
9630 if (osdev
== NULL
) {
9632 bam_error(_("cannot determine device special file for "
9633 "\"/\".\nCannot generate GRUB menu entry with "
9634 "EEPROM arguments.\n"));
9639 sign
= find_existing_sign("/", osdev
, fstype
);
9640 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign
= NULL
);
9644 bam_error(_("cannot determine boot signature for "
9645 "\"/\".\nCannot generate GRUB menu entry with "
9646 "EEPROM arguments.\n"));
9652 (void) strlcpy(signbuf
, sign
, sizeof (signbuf
));
9654 assert(strchr(signbuf
, '(') == NULL
&&
9655 strchr(signbuf
, ',') == NULL
&&
9656 strchr(signbuf
, ')') == NULL
);
9658 if (optnum
== KERNEL_CMD
) {
9659 if (strcmp(fstype
, "zfs") == 0) {
9660 new_str_len
= strlen(new_path
) +
9661 strlen(ZFS_BOOT
) + 8;
9662 new_arg
= s_calloc(1, new_str_len
);
9663 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9664 new_path
, ZFS_BOOT
);
9665 BAM_DPRINTF(("%s: new kernel=%s\n", fcn
,
9667 entryNum
= add_boot_entry(mp
, BOOTENV_RC_TITLE
,
9668 signbuf
, new_arg
, NULL
, NULL
, NULL
);
9671 BAM_DPRINTF(("%s: new kernel=%s\n", fcn
,
9673 entryNum
= add_boot_entry(mp
, BOOTENV_RC_TITLE
,
9674 signbuf
, new_path
, NULL
, NULL
, NULL
);
9677 new_str_len
= strlen(path
) + 8;
9678 if (strcmp(fstype
, "zfs") == 0) {
9679 new_str_len
+= strlen(DIRECT_BOOT_KERNEL_ZFS
);
9680 new_arg
= s_calloc(1, new_str_len
);
9681 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9682 DIRECT_BOOT_KERNEL_ZFS
, path
);
9684 new_str_len
+= strlen(DIRECT_BOOT_KERNEL
);
9685 new_arg
= s_calloc(1, new_str_len
);
9686 (void) snprintf(new_arg
, new_str_len
, "%s %s",
9687 DIRECT_BOOT_KERNEL
, path
);
9690 BAM_DPRINTF(("%s: new args=%s\n", fcn
, new_arg
));
9691 entryNum
= add_boot_entry(mp
, BOOTENV_RC_TITLE
,
9692 signbuf
, new_arg
, NULL
, DIRECT_BOOT_ARCHIVE
, NULL
);
9696 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9697 entryNum
= BAM_ERROR
);
9698 if (entryNum
== BAM_ERROR
) {
9699 bam_error(_("failed to add boot entry: %s\n"),
9704 save_default_entry(mp
, BAM_OLD_RC_DEF
);
9705 ret
= set_global(mp
, menu_cmds
[DEFAULT_CMD
], entryNum
);
9706 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret
= BAM_ERROR
);
9707 if (ret
== BAM_ERROR
) {
9708 bam_error(_("failed to set default to: %d\n"),
9716 * There was already an bootenv entry which we need to edit.
9718 if (optnum
== KERNEL_CMD
) {
9719 new_str_len
= strlen(new_path
) + strlen(old_args
) + 2;
9720 new_arg
= s_calloc(1, new_str_len
);
9721 (void) snprintf(new_arg
, new_str_len
, "%s %s", new_path
,
9724 kernelp
->arg
= new_arg
;
9727 * If we have changed the kernel line, we may need to update
9728 * the archive line as well.
9730 set_archive_line(entryp
, kernelp
);
9731 BAM_DPRINTF(("%s: rc line exists, replaced kernel, same "
9732 "args: %s\n", fcn
, kernelp
->arg
));
9734 new_str_len
= old_kernel_len
+ strlen(path
) + 8;
9735 new_arg
= s_calloc(1, new_str_len
);
9736 (void) strncpy(new_arg
, kernelp
->arg
, old_kernel_len
);
9737 (void) strlcat(new_arg
, " ", new_str_len
);
9738 (void) strlcat(new_arg
, path
, new_str_len
);
9740 kernelp
->arg
= new_arg
;
9741 BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9742 "args: %s\n", fcn
, kernelp
->arg
));
9747 if ((rv
== BAM_WRITE
) && kernelp
)
9748 update_line(kernelp
);
9751 if (rv
== BAM_WRITE
) {
9752 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9754 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9760 get_kernel(menu_t
*mp
, menu_cmd_t optnum
, char *buf
, size_t bufsize
)
9762 const char *fcn
= "get_kernel()";
9763 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, menu_cmds
[optnum
]));
9764 return (get_set_kernel(mp
, optnum
, NULL
, buf
, bufsize
));
9768 set_kernel(menu_t
*mp
, menu_cmd_t optnum
, char *path
, char *buf
, size_t bufsize
)
9770 const char *fcn
= "set_kernel()";
9771 assert(path
!= NULL
);
9772 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn
,
9773 menu_cmds
[optnum
], path
));
9774 return (get_set_kernel(mp
, optnum
, path
, buf
, bufsize
));
9779 set_option(menu_t
*mp
, char *dummy
, char *opt
)
9784 char buf
[BUFSIZ
] = "";
9786 const char *fcn
= "set_option()";
9790 assert(dummy
== NULL
);
9792 /* opt is set from bam_argv[0] and is always non-NULL */
9793 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn
, opt
));
9795 val
= strchr(opt
, '=');
9800 if (strcmp(opt
, "default") == 0) {
9801 optnum
= DEFAULT_CMD
;
9802 } else if (strcmp(opt
, "timeout") == 0) {
9803 optnum
= TIMEOUT_CMD
;
9804 } else if (strcmp(opt
, menu_cmds
[KERNEL_CMD
]) == 0) {
9805 optnum
= KERNEL_CMD
;
9806 } else if (strcmp(opt
, menu_cmds
[ARGS_CMD
]) == 0) {
9809 bam_error(_("invalid option: %s\n"), opt
);
9814 * kernel and args are allowed without "=new_value" strings. All
9815 * others cause errors
9817 if ((val
== NULL
) && (optnum
!= KERNEL_CMD
) && (optnum
!= ARGS_CMD
)) {
9818 bam_error(_("option has no argument: %s\n"), opt
);
9820 } else if (val
!= NULL
) {
9824 if ((optnum
== KERNEL_CMD
) || (optnum
== ARGS_CMD
)) {
9825 BAM_DPRINTF(("%s: setting %s option to %s\n",
9826 fcn
, menu_cmds
[optnum
], val
? val
+ 1 : "NULL"));
9829 rv
= set_kernel(mp
, optnum
, val
+ 1, buf
, sizeof (buf
));
9831 rv
= get_kernel(mp
, optnum
, buf
, sizeof (buf
));
9832 if ((rv
== BAM_SUCCESS
) && (buf
[0] != '\0'))
9833 (void) printf("%s\n", buf
);
9835 optval
= s_strtol(val
+ 1);
9836 BAM_DPRINTF(("%s: setting %s option to %s\n", fcn
,
9837 menu_cmds
[optnum
], val
+ 1));
9838 rv
= set_global(mp
, menu_cmds
[optnum
], optval
);
9841 if (rv
== BAM_WRITE
|| rv
== BAM_SUCCESS
) {
9842 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn
));
9844 BAM_DPRINTF(("%s: returning FAILURE\n", fcn
));
9851 * The quiet argument suppresses messages. This is used
9852 * when invoked in the context of other commands (e.g. list_entry)
9855 read_globals(menu_t
*mp
, char *menu_path
, char *globalcmd
, int quiet
)
9859 int done
, ret
= BAM_SUCCESS
;
9865 if (mp
->start
== NULL
) {
9867 bam_error(_("menu file not found: %s\n"), menu_path
);
9872 for (lp
= mp
->start
; lp
; lp
= lp
->next
) {
9873 if (lp
->flags
!= BAM_GLOBAL
)
9876 if (lp
->cmd
== NULL
) {
9878 bam_error(_("no command at line %d\n"),
9883 if (strcmp(globalcmd
, lp
->cmd
) != 0)
9886 /* Found global. Check for duplicates */
9887 if (done
&& !quiet
) {
9888 bam_error(_("duplicate command %s at line %d of "
9889 "%sboot/grub/menu.lst\n"), globalcmd
,
9890 lp
->lineNum
, bam_root
);
9894 arg
= lp
->arg
? lp
->arg
: "";
9895 bam_print(_("%s %s\n"), globalcmd
, arg
);
9899 if (!done
&& bam_verbose
)
9900 bam_print(_("no %s entry found\n"), globalcmd
);
9906 menu_write(char *root
, menu_t
*mp
)
9908 const char *fcn
= "menu_write()";
9910 BAM_DPRINTF(("%s: entered menu_write() for root: <%s>\n", fcn
, root
));
9911 return (list2file(root
, MENU_TMP
, GRUB_MENU
, mp
->start
));
9915 line_free(line_t
*lp
)
9920 if (lp
->cmd
!= NULL
)
9932 linelist_free(line_t
*start
)
9938 start
= start
->next
;
9944 filelist_free(filelist_t
*flistp
)
9946 linelist_free(flistp
->head
);
9947 flistp
->head
= NULL
;
9948 flistp
->tail
= NULL
;
9952 menu_free(menu_t
*mp
)
9958 linelist_free(mp
->start
);
9975 * Returns 0 on success
9976 * Any other value indicates an error
9979 exec_cmd(char *cmdline
, filelist_t
*flistp
)
9989 * - only absolute paths are allowed
9990 * - set IFS to space and tab
9992 if (*cmdline
!= '/') {
9993 bam_error(_("path is not absolute: %s\n"), cmdline
);
9996 (void) putenv("IFS= \t");
9999 * We may have been exec'ed with SIGCHLD blocked
10002 (void) sigemptyset(&set
);
10003 (void) sigaddset(&set
, SIGCHLD
);
10004 if (sigprocmask(SIG_UNBLOCK
, &set
, NULL
) != 0) {
10005 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno
));
10010 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
10012 disp
= sigset(SIGCHLD
, SIG_DFL
);
10013 if (disp
== SIG_ERR
) {
10014 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
10018 if (disp
== SIG_HOLD
) {
10019 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
10024 ptr
= popen(cmdline
, "r");
10026 bam_error(_("popen failed: %s: %s\n"), cmdline
,
10032 * If we simply do a pclose() following a popen(), pclose()
10033 * will close the reader end of the pipe immediately even
10034 * if the child process has not started/exited. pclose()
10035 * does wait for cmd to terminate before returning though.
10036 * When the executed command writes its output to the pipe
10037 * there is no reader process and the command dies with
10038 * SIGPIPE. To avoid this we read repeatedly until read
10039 * terminates with EOF. This indicates that the command
10040 * (writer) has closed the pipe and we can safely do a
10043 * Since pclose() does wait for the command to exit,
10044 * we can safely reap the exit status of the command
10045 * from the value returned by pclose()
10047 while (s_fgets(buf
, sizeof (buf
), ptr
) != NULL
) {
10048 if (flistp
== NULL
) {
10049 /* s_fgets strips newlines, so insert them at the end */
10050 bam_print(_("%s\n"), buf
);
10052 append_to_flist(flistp
, buf
);
10058 bam_error(_("pclose failed: %s: %s\n"), cmdline
,
10063 if (WIFEXITED(ret
)) {
10064 return (WEXITSTATUS(ret
));
10066 bam_error(_("command terminated abnormally: %s: %d\n"),
10073 * Since this function returns -1 on error
10074 * it cannot be used to convert -1. However,
10075 * that is sufficient for what we need.
10078 s_strtol(char *str
)
10088 l
= strtol(str
, &res
, 10);
10089 if (errno
|| *res
!= '\0') {
10097 * Wrapper around fputs, that adds a newline (since fputs doesn't)
10100 s_fputs(char *str
, FILE *fp
)
10102 char linebuf
[BAM_MAXLINE
];
10104 (void) snprintf(linebuf
, sizeof (linebuf
), "%s\n", str
);
10105 return (fputs(linebuf
, fp
));
10109 * Wrapper around fgets, that strips newlines returned by fgets
10112 s_fgets(char *buf
, int buflen
, FILE *fp
)
10116 buf
= fgets(buf
, buflen
, fp
);
10119 if (n
== buflen
- 1 && buf
[n
-1] != '\n')
10120 bam_error(_("the following line is too long "
10121 "(> %d chars)\n\t%s\n"), buflen
- 1, buf
);
10122 buf
[n
-1] = (buf
[n
-1] == '\n') ? '\0' : buf
[n
-1];
10129 s_calloc(size_t nelem
, size_t sz
)
10133 ptr
= calloc(nelem
, sz
);
10135 bam_error(_("could not allocate memory: size = %u\n"),
10143 s_realloc(void *ptr
, size_t sz
)
10145 ptr
= realloc(ptr
, sz
);
10147 bam_error(_("could not allocate memory: size = %u\n"), sz
);
10154 s_strdup(char *str
)
10163 bam_error(_("could not allocate memory: size = %u\n"),
10171 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
10172 * Returns 0 otherwise
10177 static int amd64
= -1;
10178 char isabuf
[257]; /* from sysinfo(2) manpage */
10183 if (bam_alt_platform
) {
10184 if (strcmp(bam_platform
, "i86pc") == 0) {
10185 amd64
= 1; /* diskless server */
10188 if (sysinfo(SI_ISALIST
, isabuf
, sizeof (isabuf
)) > 0 &&
10189 strncmp(isabuf
, "amd64 ", strlen("amd64 ")) == 0) {
10191 } else if (strstr(isabuf
, "i386") == NULL
) {
10192 amd64
= 1; /* diskless server */
10204 static int cached
= -1;
10205 static char mbuf
[257]; /* from sysinfo(2) manpage */
10210 if (bam_alt_platform
) {
10211 return (bam_platform
);
10213 if (sysinfo(SI_MACHINE
, mbuf
, sizeof (mbuf
)) > 0) {
10217 if (cached
== -1) {
10228 static int issparc
= -1;
10229 char mbuf
[257]; /* from sysinfo(2) manpage */
10234 if (bam_alt_platform
) {
10235 if (strncmp(bam_platform
, "sun4", 4) == 0) {
10239 if (sysinfo(SI_ARCHITECTURE
, mbuf
, sizeof (mbuf
)) > 0 &&
10240 strcmp(mbuf
, "sparc") == 0) {
10251 append_to_flist(filelist_t
*flistp
, char *s
)
10255 lp
= s_calloc(1, sizeof (line_t
));
10256 lp
->line
= s_strdup(s
);
10257 if (flistp
->head
== NULL
)
10260 flistp
->tail
->next
= lp
;