16615 ena assertion failure in ena_tx_intr_work()
[illumos-gate.git] / usr / src / cmd / boot / bootadm / bootadm.c
blob15a6d92d5eb4acf0fd25935f8c4db7ef558b4f62
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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)
39 /* Headers */
40 #include <stdio.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <alloca.h>
48 #include <stdarg.h>
49 #include <limits.h>
50 #include <signal.h>
51 #include <sys/wait.h>
52 #include <sys/mnttab.h>
53 #include <sys/mntent.h>
54 #include <sys/statvfs.h>
55 #include <libnvpair.h>
56 #include <ftw.h>
57 #include <fcntl.h>
58 #include <strings.h>
59 #include <utime.h>
60 #include <sys/systeminfo.h>
61 #include <sys/dktp/fdisk.h>
62 #include <sys/param.h>
63 #include <dirent.h>
64 #include <ctype.h>
65 #include <libgen.h>
66 #include <sys/sysmacros.h>
67 #include <sys/elf.h>
68 #include <libscf.h>
69 #include <zlib.h>
70 #include <sys/lockfs.h>
71 #include <sys/filio.h>
72 #include <libbe.h>
73 #include <deflt.h>
74 #ifdef i386
75 #include <libfdisk.h>
76 #endif
78 #include <pwd.h>
79 #include <grp.h>
80 #include <device_info.h>
81 #include <sys/vtoc.h>
82 #include <sys/efi_partition.h>
83 #include <regex.h>
84 #include <locale.h>
85 #include <sys/mkdev.h>
87 #include "bootadm.h"
89 #ifndef TEXT_DOMAIN
90 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
91 #endif /* TEXT_DOMAIN */
93 /* Type definitions */
95 /* Primary subcmds */
96 typedef enum {
97 BAM_MENU = 3,
98 BAM_ARCHIVE,
99 BAM_INSTALL
100 } subcmd_t;
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"
125 /* SMF */
126 #define BOOT_ARCHIVE_FMRI "system/boot-archive:default"
127 #define SCF_PG_CONFIG "config"
128 #define SCF_PROPERTY_FORMAT "format"
130 /* BE defaults */
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"
136 /* lock related */
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 */
171 * Menu related
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 */
183 " ", /* SEP_CMD */
184 "#", /* COMMENT_CMD */
185 "chainloader", /* CHAINLOADER_CMD */
186 "args", /* ARGS_CMD */
187 "findroot", /* FINDROOT_CMD */
188 "bootfs", /* BOOTFS_CMD */
189 NULL
192 char *bam_formats[] = {
193 "hsfs",
194 "ufs",
195 "cpio",
196 "ufs-nocompress",
197 NULL
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"
206 * exec_cmd related
208 typedef struct {
209 line_t *head;
210 line_t *tail;
211 } filelist_t;
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"
223 /* Globals */
224 int bam_verbose;
225 int bam_force;
226 int bam_debug;
227 int bam_skip_lock;
228 static char *prog;
229 static subcmd_t bam_cmd;
230 char *bam_root;
231 int bam_rootlen;
232 static int bam_root_readonly;
233 int bam_alt_root;
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;
240 static int bam_argc;
241 static int bam_check;
242 static int bam_saved_check;
243 static int bam_smf_check;
244 static int bam_lock_fd = -1;
245 static int bam_zfs;
246 static int bam_mbr;
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];
361 int has_dir;
362 int count;
363 int flags;
364 } dirinfo_t;
367 * Update flags:
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)
385 * struct walk_arg :
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)
390 static struct {
391 int update_flags;
392 nvlist_t *new_nvlp;
393 nvlist_t *old_nvlp;
394 FILE *sparcfile;
395 dirinfo_t dirinfo;
396 } walk_arg;
398 struct safefile {
399 char *name;
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)
421 #define MAX_IVDs 16
423 struct iso_pdesc {
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
434 * update
436 #define COUNT_MAX 50
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 */
443 static void
444 usage(void)
446 (void) fprintf(stderr, "USAGE:\n");
448 /* archive usage */
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);
454 #if defined(_OBP)
455 (void) fprintf(stderr,
456 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
457 #else
458 (void) fprintf(stderr,
459 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
460 #endif
461 #if !defined(_OBP)
462 /* x86 only */
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);
465 #endif
469 * Best effort attempt to restore the $HOME value.
471 static void
472 restore_env()
474 char home_env[PATH_MAX];
476 if (bam_home_env) {
477 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
478 bam_home_env);
479 (void) putenv(home_env);
484 #define SLEEP_TIME 5
485 #define MAX_TRIES 4
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.
493 static void
494 sanitize_env()
496 int stry = 0;
498 /* don't depend on caller umask */
499 (void) umask(0022);
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 /");
505 break;
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);
522 } else {
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) {
537 prog = argv[0];
538 } else {
539 prog++;
542 INJECT_ERROR1("ASSERT_ON", assert(0))
544 sanitize_env();
546 parse_args(argc, argv);
548 switch (bam_cmd) {
549 case BAM_MENU:
550 if (is_grub(bam_alt_root ? bam_root : "/")) {
551 ret = bam_menu(bam_subcmd, bam_opt,
552 bam_argc, bam_argv);
553 } else {
554 ret = bam_loader_menu(bam_subcmd, bam_opt,
555 bam_argc, bam_argv);
557 break;
558 case BAM_ARCHIVE:
559 ret = bam_archive(bam_subcmd, bam_opt);
560 break;
561 case BAM_INSTALL:
562 ret = bam_install(bam_subcmd, bam_opt);
563 break;
564 default:
565 usage();
566 bam_exit(1);
569 if (ret != BAM_SUCCESS)
570 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
572 bam_unlock();
573 return (0);
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 {
586 char *bam_cmdname;
587 int bam_cmd;
588 char *bam_subcmd;
589 } 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"},
596 { NULL, 0, NULL}
600 * Commands syntax published in bootadm(8) are parsed here
602 static void
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;
616 break;
618 cmp++;
620 if (cmp->bam_cmdname == NULL) {
621 usage();
622 bam_exit(1);
624 argc--;
625 argv++;
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
649 * reboot)
650 * -L -- skip locking
652 static void
653 parse_args_internal(int argc, char *argv[])
655 int c, i, error;
656 extern char *optarg;
657 extern int optind, opterr;
658 #if defined(_OBP)
659 const char *optstring = "a:d:fF:i:m:no:veQCLR:p:P:XZ";
660 #else
661 const char *optstring = "a:d:fF:i:m:no:veQCMLR:p:P:XZ";
662 #endif
664 /* Suppress error message from getopt */
665 opterr = 0;
667 error = 0;
668 while ((c = getopt(argc, argv, optstring)) != -1) {
669 switch (c) {
670 case 'a':
671 if (bam_cmd) {
672 error = 1;
673 bam_error(
674 _("multiple commands specified: -%c\n"), c);
676 bam_cmd = BAM_ARCHIVE;
677 bam_subcmd = optarg;
678 break;
679 case 'd':
680 if (bam_debug) {
681 error = 1;
682 bam_error(
683 _("duplicate options specified: -%c\n"), c);
685 bam_debug = s_strtol(optarg);
686 break;
687 case 'f':
688 bam_force = 1;
689 break;
690 case 'F':
691 if (bam_format != BAM_FORMAT_UNSET) {
692 error = 1;
693 bam_error(
694 _("multiple formats specified: -%c\n"), c);
696 for (i = 0; bam_formats[i] != NULL; i++) {
697 if (strcmp(bam_formats[i], optarg) == 0) {
698 bam_format = i;
699 break;
702 if (bam_format == BAM_FORMAT_UNSET) {
703 error = 1;
704 bam_error(
705 _("unknown format specified: -%c %s\n"),
706 c, optarg);
708 break;
709 case 'Q':
710 bam_purge = 1;
711 break;
712 case 'L':
713 bam_skip_lock = 1;
714 break;
715 case 'i':
716 if (bam_cmd) {
717 error = 1;
718 bam_error(
719 _("multiple commands specified: -%c\n"), c);
721 bam_cmd = BAM_INSTALL;
722 bam_subcmd = optarg;
723 break;
724 case 'm':
725 if (bam_cmd) {
726 error = 1;
727 bam_error(
728 _("multiple commands specified: -%c\n"), c);
730 bam_cmd = BAM_MENU;
731 bam_subcmd = optarg;
732 break;
733 #if !defined(_OBP)
734 case 'M':
735 bam_mbr = 1;
736 break;
737 #endif
738 case 'n':
739 bam_check = 1;
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.
750 bam_saved_check = 1;
751 break;
752 case 'o':
753 if (bam_opt) {
754 error = 1;
755 bam_error(
756 _("duplicate options specified: -%c\n"), c);
758 bam_opt = optarg;
759 break;
760 case 'v':
761 bam_verbose = 1;
762 break;
763 case 'C':
764 bam_smf_check = 1;
765 break;
766 case 'P':
767 if (bam_pool != NULL) {
768 error = 1;
769 bam_error(
770 _("duplicate options specified: -%c\n"), c);
772 bam_pool = optarg;
773 break;
774 case 'R':
775 if (bam_root) {
776 error = 1;
777 bam_error(
778 _("duplicate options specified: -%c\n"), c);
779 break;
780 } else if (realpath(optarg, rootbuf) == NULL) {
781 error = 1;
782 bam_error(_("cannot resolve path %s: %s\n"),
783 optarg, strerror(errno));
784 break;
786 bam_alt_root = 1;
787 bam_root = rootbuf;
788 bam_rootlen = strlen(rootbuf);
789 break;
790 case 'p':
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)) {
796 error = 1;
797 bam_error(_("invalid platform %s - must be "
798 "one of sun4u, sun4v or i86pc\n"),
799 bam_platform);
801 break;
802 case 'X':
803 bam_is_hv = BAM_HV_PRESENT;
804 break;
805 case 'Z':
806 bam_zfs = 1;
807 break;
808 case 'e':
809 bam_extend = 1;
810 break;
811 case '?':
812 error = 1;
813 bam_error(_("invalid option or missing option "
814 "argument: -%c\n"), optopt);
815 break;
816 default :
817 error = 1;
818 bam_error(_("invalid option or missing option "
819 "argument: -%c\n"), c);
820 break;
825 * An alternate platform requires an alternate root
827 if (bam_alt_platform && bam_alt_root == 0) {
828 usage();
829 bam_exit(0);
833 * A command option must be specfied
835 if (!bam_cmd) {
836 if (bam_opt && strcmp(bam_opt, "all") == 0) {
837 usage();
838 bam_exit(0);
840 bam_error(_("a command option must be specified\n"));
841 error = 1;
844 if (error) {
845 usage();
846 bam_exit(1);
849 if (optind > argc) {
850 bam_error(_("Internal error: %s\n"), "parse_args");
851 bam_exit(1);
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)) {
861 usage();
862 bam_exit(0);
866 * -n implies verbose mode
868 if (bam_check)
869 bam_verbose = 1;
872 error_t
873 check_subcmd_and_options(
874 char *subcmd,
875 char *opt,
876 subcmd_defn_t *table,
877 error_t (**fp)())
879 int i;
881 if (subcmd == NULL) {
882 bam_error(_("this command requires a sub-command\n"));
883 return (BAM_ERROR);
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"));
889 usage();
890 return (BAM_ERROR);
891 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
892 bam_error(_("invalid trailing arguments\n"));
893 usage();
894 return (BAM_ERROR);
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"));
904 usage();
905 return (BAM_ERROR);
907 if (bam_argc == 1)
908 sync_menu = 0;
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"));
916 usage();
917 return (BAM_ERROR);
920 if (bam_root == NULL) {
921 bam_root = rootbuf;
922 bam_rootlen = 1;
925 /* verify that subcmd is valid */
926 for (i = 0; table[i].subcmd != NULL; i++) {
927 if (strcmp(table[i].subcmd, subcmd) == 0)
928 break;
931 if (table[i].subcmd == NULL) {
932 bam_error(_("invalid sub-command specified: %s\n"), subcmd);
933 return (BAM_ERROR);
936 if (table[i].unpriv == 0 && geteuid() != 0) {
937 bam_error(_("you must be root to run this command\n"));
938 return (BAM_ERROR);
942 * Currently only privileged commands need a lock
944 if (table[i].unpriv == 0)
945 bam_lock();
947 /* subcmd verifies that opt is appropriate */
948 if (table[i].option != OPT_OPTIONAL) {
949 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
950 if (opt)
951 bam_error(_("this sub-command (%s) does not "
952 "take options\n"), subcmd);
953 else
954 bam_error(_("an option is required for this "
955 "sub-command: %s\n"), subcmd);
956 return (BAM_ERROR);
960 *fp = table[i].handler;
962 return (BAM_SUCCESS);
966 * NOTE: A single "/" is also considered a trailing slash and will
967 * be deleted.
969 void
970 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
972 size_t dstlen;
974 assert(src);
975 assert(dst);
977 (void) strlcpy(dst, src, dstsize);
979 dstlen = strlen(dst);
980 if (dst[dstlen - 1] == '/') {
981 dst[dstlen - 1] = '\0';
985 static int
986 is_safe_exec(char *path)
988 struct stat sb;
990 if (lstat(path, &sb) != 0) {
991 bam_error(_("stat of file failed: %s: %s\n"), path,
992 strerror(errno));
993 return (BAM_ERROR);
996 if (!S_ISREG(sb.st_mode)) {
997 bam_error(_("%s is not a regular file, skipping\n"), path);
998 return (BAM_ERROR);
1001 if (sb.st_uid != getuid()) {
1002 bam_error(_("%s is not owned by %d, skipping\n"),
1003 path, getuid());
1004 return (BAM_ERROR);
1007 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
1008 bam_error(_("%s is others or group writable, skipping\n"),
1009 path);
1010 return (BAM_ERROR);
1013 return (BAM_SUCCESS);
1016 static error_t
1017 list_setting(menu_t *mp, char *which, char *setting)
1019 line_t *lp;
1020 entry_t *ent;
1022 char *p = which;
1023 int entry;
1025 int found;
1027 assert(which);
1028 assert(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.
1036 while (*p != '\0')
1037 if (!(isdigit((int)*p++))) {
1038 setting = which;
1039 which = mp->curdefault->arg;
1040 break;
1042 } else {
1043 which = mp->curdefault->arg;
1046 entry = atoi(which);
1048 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1049 ent = ent->next)
1052 if (!ent) {
1053 bam_error(_("no matching entry found\n"));
1054 return (BAM_ERROR);
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);
1064 found = 1;
1067 if (lp == ent->end)
1068 break;
1071 if (!found) {
1072 bam_error(_("no matching entry found\n"));
1073 return (BAM_ERROR);
1076 return (BAM_SUCCESS);
1079 static error_t
1080 install_bootloader(void)
1082 nvlist_t *nvl;
1083 uint16_t flags = 0;
1084 int found = 0;
1085 struct extmnttab mnt;
1086 struct stat statbuf = {0};
1087 be_node_list_t *be_nodes, *node;
1088 FILE *fp;
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"));
1094 return (ret);
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.
1106 if (bam_alt_root) {
1107 if (stat(bam_root, &statbuf) != 0) {
1108 bam_error(_("stat of file failed: %s: %s\n"), bam_root,
1109 strerror(errno));
1110 goto done;
1112 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1113 bam_error(_("failed to open file: %s: %s\n"),
1114 MNTTAB, strerror(errno));
1115 goto done;
1117 resetmnttab(fp);
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)) {
1121 found = 1;
1122 root_ds = strdup(mnt.mnt_special);
1123 break;
1126 (void) fclose(fp);
1128 if (found == 0) {
1129 bam_error(_("alternate root %s not in mnttab\n"),
1130 bam_root);
1131 goto done;
1133 if (root_ds == NULL) {
1134 bam_error(_("out of memory\n"));
1135 goto done;
1138 if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
1139 bam_error(_("No BE's found\n"));
1140 goto done;
1142 for (node = be_nodes; node != NULL; node = node->be_next_node)
1143 if (strcmp(root_ds, node->be_root_ds) == 0)
1144 break;
1146 if (node == NULL)
1147 bam_error(_("BE (%s) does not exist\n"), root_ds);
1149 free(root_ds);
1150 root_ds = NULL;
1151 if (node == NULL) {
1152 be_free_list(be_nodes);
1153 goto done;
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,
1158 node->be_root_ds);
1159 be_free_list(be_nodes);
1160 if (ret != 0) {
1161 ret = BAM_ERROR;
1162 goto done;
1166 if (bam_force)
1167 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1168 if (bam_mbr)
1169 flags |= BE_INSTALLBOOT_FLAG_MBR;
1170 if (bam_verbose)
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"));
1175 ret = BAM_ERROR;
1176 goto done;
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);
1186 if (ret != 0) {
1187 ret = BAM_ERROR;
1188 goto done;
1190 if (found) {
1191 ret = be_installboot(nvl);
1192 if (ret != 0)
1193 ret = BAM_ERROR;
1194 goto done;
1198 if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
1199 bam_error(_("No BE's found\n"));
1200 ret = BAM_ERROR;
1201 goto done;
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)
1210 continue;
1211 if (node->be_active_on_boot)
1212 break;
1214 if (node == NULL) {
1215 bam_error(_("No active BE in %s\n"), bam_pool);
1216 be_free_list(be_nodes);
1217 ret = BAM_ERROR;
1218 goto done;
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,
1223 node->be_root_ds);
1224 be_free_list(be_nodes);
1225 if (ret != 0) {
1226 ret = BAM_ERROR;
1227 goto done;
1229 ret = be_installboot(nvl);
1230 if (ret != 0)
1231 ret = BAM_ERROR;
1232 goto done;
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));
1241 ret = BAM_ERROR;
1242 be_free_list(be_nodes);
1243 goto done;
1245 resetmnttab(fp);
1246 found = 0;
1247 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1248 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1249 found = 1;
1250 root_ds = strdup(mnt.mnt_special);
1251 break;
1254 (void) fclose(fp);
1256 if (found == 0) {
1257 bam_error(_("alternate root %s not in mnttab\n"), "/");
1258 ret = BAM_ERROR;
1259 be_free_list(be_nodes);
1260 goto done;
1262 if (root_ds == NULL) {
1263 bam_error(_("out of memory\n"));
1264 ret = BAM_ERROR;
1265 be_free_list(be_nodes);
1266 goto done;
1269 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1270 if (strcmp(root_ds, node->be_root_ds) == 0)
1271 break;
1274 if (node == NULL) {
1275 bam_error(_("No such BE: %s\n"), root_ds);
1276 free(root_ds);
1277 be_free_list(be_nodes);
1278 ret = BAM_ERROR;
1279 goto done;
1281 free(root_ds);
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);
1288 if (ret != 0)
1289 ret = BAM_ERROR;
1290 else
1291 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1292 done:
1293 nvlist_free(nvl);
1295 return (ret);
1298 static error_t
1299 bam_install(char *subcmd, char *opt)
1301 error_t (*f)(void);
1304 * Check arguments
1306 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1307 BAM_ERROR)
1308 return (BAM_ERROR);
1310 return (f());
1313 static error_t
1314 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1316 error_t ret;
1317 char menu_path[PATH_MAX];
1318 char clean_menu_root[PATH_MAX];
1319 char path[PATH_MAX];
1320 menu_t *menu;
1321 char menu_root[PATH_MAX];
1322 struct stat sb;
1323 error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1324 char *special = NULL;
1325 char *pool = NULL;
1326 zfs_mnted_t zmnted;
1327 char *zmntpt = NULL;
1328 char *osdev;
1329 char *osroot;
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"));
1338 return (BAM_ERROR);
1342 * Check arguments
1344 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1345 if (ret == BAM_ERROR) {
1346 return (BAM_ERROR);
1349 assert(bam_root);
1351 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1352 osdev = osroot = NULL;
1354 if (strcmp(subcmd, "update_entry") == 0) {
1355 assert(opt);
1357 osdev = strtok(opt, ",");
1358 assert(osdev);
1359 osroot = strtok(NULL, ",");
1360 if (osroot) {
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));
1365 return (BAM_ERROR);
1367 bam_alt_root = 1;
1368 bam_root = rootbuf;
1369 bam_rootlen = strlen(rootbuf);
1374 * We support menu on PCFS (under certain conditions), but
1375 * not the OS root
1377 if (is_pcfs(bam_root)) {
1378 bam_error(_("root <%s> on PCFS is not supported\n"), bam_root);
1379 return (BAM_ERROR);
1382 if (stat(menu_root, &sb) == -1) {
1383 bam_error(_("cannot find GRUB menu\n"));
1384 return (BAM_ERROR);
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.
1396 if (bam_alt_root) {
1397 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1398 GRUB_slice);
1399 } else {
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);
1414 return (BAM_ERROR);
1416 pool = strtok(special, "/");
1417 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1418 if (pool == NULL) {
1419 free(special);
1420 bam_error(_("cant find pool for mount-point %s\n"),
1421 menu_root);
1422 return (BAM_ERROR);
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"),
1430 pool);
1431 free(special);
1432 return (BAM_ERROR);
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"),
1455 menu_path);
1457 if ((menu = menu_read(menu_path)) == NULL) {
1458 bam_error(_("cannot find GRUB menu file: %s\n"), menu_path);
1459 free(special);
1461 return (BAM_ERROR);
1465 * We already checked the following case in
1466 * check_subcmd_and_suboptions() above. Complete the
1467 * final step now.
1469 if (strcmp(subcmd, "set_option") == 0) {
1470 assert(largc == 1 && largv[0] && largv[1] == NULL);
1471 opt = largv[0];
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));
1480 goto out;
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) {
1497 if (is_sparc()) {
1498 bam_error(_("%s operation unsupported on SPARC "
1499 "machines\n"), subcmd);
1500 ret = BAM_ERROR;
1501 } else {
1502 ret = f(menu, bam_root, NULL);
1504 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1505 if (is_sparc()) {
1506 bam_error(_("%s operation unsupported on SPARC "
1507 "machines\n"), subcmd);
1508 ret = BAM_ERROR;
1509 } else {
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
1520 * options.
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
1526 * may create.
1528 if (largc > 0) {
1529 int extra_len, i;
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);
1550 } else
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);
1559 out:
1560 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1561 assert((is_zfs(menu_root)) ^ (pool == NULL));
1562 if (pool) {
1563 (void) umount_top_dataset(pool, zmnted, zmntpt);
1564 free(special);
1566 menu_free(menu);
1567 return (ret);
1571 static error_t
1572 bam_archive(
1573 char *subcmd,
1574 char *opt)
1576 error_t ret;
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);
1588 * Check arguments
1590 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1591 if (ret != BAM_SUCCESS) {
1592 return (BAM_ERROR);
1595 ret = get_boot_cap(rootbuf);
1596 if (ret != BAM_SUCCESS) {
1597 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1598 return (ret);
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);
1609 return (BAM_ERROR);
1612 if (strcmp(subcmd, "update_all") == 0)
1613 bam_update_all = 1;
1615 ret = f(bam_root, opt);
1617 bam_update_all = 0;
1619 return (ret);
1622 /*PRINTFLIKE1*/
1623 void
1624 bam_error(char *format, ...)
1626 va_list ap;
1628 va_start(ap, format);
1629 (void) fprintf(stderr, "%s: ", prog);
1630 (void) vfprintf(stderr, format, ap);
1631 va_end(ap);
1634 /*PRINTFLIKE1*/
1635 void
1636 bam_derror(char *format, ...)
1638 va_list ap;
1640 assert(bam_debug);
1642 va_start(ap, format);
1643 (void) fprintf(stderr, "DEBUG: ");
1644 (void) vfprintf(stderr, format, ap);
1645 va_end(ap);
1648 /*PRINTFLIKE1*/
1649 void
1650 bam_print(char *format, ...)
1652 va_list ap;
1654 va_start(ap, format);
1655 (void) vfprintf(stdout, format, ap);
1656 va_end(ap);
1659 /*PRINTFLIKE1*/
1660 void
1661 bam_print_stderr(char *format, ...)
1663 va_list ap;
1665 va_start(ap, format);
1666 (void) vfprintf(stderr, format, ap);
1667 va_end(ap);
1670 void
1671 bam_exit(int excode)
1673 restore_env();
1674 bam_unlock();
1675 exit(excode);
1678 static void
1679 bam_lock(void)
1681 struct flock lock;
1682 pid_t pid;
1684 if (bam_skip_lock)
1685 return;
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;
1696 return;
1699 bam_error(_("failed to open file: %s: %s\n"),
1700 BAM_LOCK_FILE, strerror(errno));
1701 bam_exit(1);
1704 lock.l_type = F_WRLCK;
1705 lock.l_whence = SEEK_SET;
1706 lock.l_start = 0;
1707 lock.l_len = 0;
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);
1714 bam_lock_fd = -1;
1715 bam_exit(1);
1717 pid = 0;
1718 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1719 bam_print(
1720 _("another instance of bootadm (pid %lu) is running\n"),
1721 pid);
1723 lock.l_type = F_WRLCK;
1724 lock.l_whence = SEEK_SET;
1725 lock.l_start = 0;
1726 lock.l_len = 0;
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);
1731 bam_lock_fd = -1;
1732 bam_exit(1);
1736 /* We own the lock now */
1737 pid = getpid();
1738 (void) write(bam_lock_fd, &pid, sizeof (pid));
1741 static void
1742 bam_unlock(void)
1744 struct flock unlock;
1746 if (bam_skip_lock)
1747 return;
1750 * NOP if we don't hold the lock
1752 if (bam_lock_fd < 0) {
1753 return;
1756 unlock.l_type = F_UNLCK;
1757 unlock.l_whence = SEEK_SET;
1758 unlock.l_start = 0;
1759 unlock.l_len = 0;
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));
1770 bam_lock_fd = -1;
1773 static error_t
1774 list_archive(char *root, char *opt)
1776 filelist_t flist;
1777 filelist_t *flistp = &flist;
1778 line_t *lp;
1780 assert(root);
1781 assert(opt == NULL);
1783 flistp->head = flistp->tail = NULL;
1784 if (read_list(root, flistp) != BAM_SUCCESS) {
1785 return (BAM_ERROR);
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
1802 static error_t
1803 list2file(char *root, char *tmp, char *final, line_t *start)
1805 char tmpfile[PATH_MAX];
1806 char path[PATH_MAX];
1807 FILE *fp;
1808 int ret;
1809 struct stat sb;
1810 mode_t mode;
1811 uid_t root_uid;
1812 gid_t sys_gid;
1813 struct passwd *pw;
1814 struct group *gp;
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"),
1823 path);
1824 if (unlink(path) != 0) {
1825 bam_error(_("failed to unlink file: %s: %s\n"),
1826 path, strerror(errno));
1827 return (BAM_ERROR);
1828 } else {
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) {
1841 mode = sb.st_mode;
1842 root_uid = sb.st_uid;
1843 sys_gid = sb.st_gid;
1844 } else {
1845 mode = DEFAULT_DEV_MODE;
1846 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1847 root_uid = pw->pw_uid;
1848 } else {
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;
1856 } else {
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");
1868 if (fp == NULL) {
1869 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1870 strerror(errno));
1871 return (BAM_ERROR);
1873 ret = fclose(fp);
1874 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1875 if (ret == EOF) {
1876 bam_error(_("failed to close file: %s: %s\n"),
1877 tmpfile, strerror(errno));
1878 return (BAM_ERROR);
1881 /* Now open it in append mode */
1882 fp = fopen(tmpfile, "a");
1883 if (fp == NULL) {
1884 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1885 strerror(errno));
1886 return (BAM_ERROR);
1889 for (; start; start = start->next) {
1890 ret = s_fputs(start->line, fp);
1891 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1892 if (ret == EOF) {
1893 bam_error(_("write to file failed: %s: %s\n"),
1894 tmpfile, strerror(errno));
1895 (void) fclose(fp);
1896 return (BAM_ERROR);
1900 ret = fclose(fp);
1901 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1902 if (ret == EOF) {
1903 bam_error(_("failed to close file: %s: %s\n"),
1904 tmpfile, strerror(errno));
1905 return (BAM_ERROR);
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);
1914 if (ret == -1 &&
1915 errno != EINVAL && errno != ENOTSUP) {
1916 bam_error(_("chmod operation on %s failed - %s\n"),
1917 tmpfile, strerror(errno));
1918 return (BAM_ERROR);
1921 ret = chown(tmpfile, root_uid, sys_gid);
1922 if (ret == -1 &&
1923 errno != EINVAL && errno != ENOTSUP) {
1924 bam_error(_("chgrp operation on %s failed - %s\n"),
1925 tmpfile, strerror(errno));
1926 return (BAM_ERROR);
1930 * Do an atomic rename
1932 ret = rename(tmpfile, path);
1933 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1934 if (ret != 0) {
1935 bam_error(_("rename to file failed: %s: %s\n"), path,
1936 strerror(errno));
1937 return (BAM_ERROR);
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.
1949 static int
1950 setup_path(char *path)
1952 char *p;
1953 int ret;
1954 struct stat sb;
1956 p = strrchr(path, '/');
1957 if (p != NULL) {
1958 *p = '\0';
1959 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1960 /* best effort attempt, mkdirp will catch the error */
1961 (void) unlink(path);
1962 if (bam_verbose)
1963 bam_print(_("need to create directory "
1964 "path for %s\n"), path);
1965 ret = mkdirp(path, DIR_PERMS);
1966 if (ret == -1) {
1967 bam_error(_("mkdir of %s failed: %s\n"),
1968 path, strerror(errno));
1969 *p = '/';
1970 return (BAM_ERROR);
1973 *p = '/';
1974 return (BAM_SUCCESS);
1976 return (BAM_SUCCESS);
1979 typedef union {
1980 gzFile gzfile;
1981 int fdfile;
1982 } outfile;
1984 typedef struct {
1985 char path[PATH_MAX];
1986 outfile out;
1987 } cachefile;
1989 static int
1990 setup_file(char *base, const char *path, cachefile *cf)
1992 int ret;
1993 char *strip;
1995 /* init gzfile or fdfile in case we fail before opening */
1996 if (bam_direct == BAM_DIRECT_DBOOT)
1997 cf->out.gzfile = NULL;
1998 else
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);
2008 return (BAM_ERROR);
2011 /* Check if path is present in the archive cache directory */
2012 if (setup_path(cf->path) == BAM_ERROR)
2013 return (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));
2019 return (BAM_ERROR);
2021 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
2022 Z_DEFAULT_STRATEGY);
2023 } else {
2024 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
2025 == -1) {
2026 bam_error(_("failed to open file: %s: %s\n"),
2027 cf->path, strerror(errno));
2028 return (BAM_ERROR);
2032 return (BAM_SUCCESS);
2035 static int
2036 cache_write(cachefile cf, char *buf, int size)
2038 int err;
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));
2048 return (BAM_ERROR);
2050 } else {
2051 if (write(cf.out.fdfile, buf, size) < 1) {
2052 bam_error(_("write to file failed: %s: %s\n"),
2053 cf.path, strerror(errno));
2054 return (BAM_ERROR);
2057 return (BAM_SUCCESS);
2060 static int
2061 cache_close(cachefile cf)
2063 int ret;
2065 if (bam_direct == BAM_DIRECT_DBOOT) {
2066 if (cf.out.gzfile) {
2067 ret = gzclose(cf.out.gzfile);
2068 if (ret != Z_OK) {
2069 bam_error(_("failed to close file: %s: %s\n"),
2070 cf.path, strerror(errno));
2071 return (BAM_ERROR);
2074 } else {
2075 if (cf.out.fdfile != -1) {
2076 ret = close(cf.out.fdfile);
2077 if (ret != 0) {
2078 bam_error(_("failed to close file: %s: %s\n"),
2079 cf.path, strerror(errno));
2080 return (BAM_ERROR);
2085 return (BAM_SUCCESS);
2088 static int
2089 dircache_updatefile(const char *path)
2091 int ret, exitcode;
2092 char buf[4096 * 4];
2093 FILE *infile;
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,
2106 strerror(errno));
2107 return (BAM_ERROR);
2110 ret = setup_file(get_cachedir(), path, &outfile);
2111 if (ret == BAM_ERROR) {
2112 exitcode = BAM_ERROR;
2113 goto out;
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;
2124 goto out;
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);
2132 get_count()++;
2133 if (get_count() > COUNT_MAX)
2134 set_dir_flag(NO_EXTEND);
2135 exitcode = BAM_SUCCESS;
2136 out:
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);
2145 return (exitcode);
2148 static int
2149 dircache_updatedir(const char *path, int updt)
2151 int ret;
2152 char dpath[PATH_MAX];
2153 char *strip;
2154 struct stat sb;
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);
2165 return (BAM_ERROR);
2168 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2169 return (BAM_SUCCESS);
2171 if (updt) {
2172 if (!is_dir_flag_on(NO_EXTEND))
2173 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2174 set_dir_flag(NO_EXTEND);
2175 } else {
2176 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2177 set_flag(UPDATE_ERROR);
2178 return (BAM_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;
2191 #else
2192 typedef Elf32_Ehdr _elfhdr;
2193 #endif
2196 * This routine updates the contents of the cache directory
2198 static int
2199 update_dircache(const char *path, int flags)
2201 int rc = BAM_SUCCESS;
2203 switch (flags) {
2204 case FTW_F:
2206 int fd;
2207 _elfhdr elf;
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);
2213 rc = BAM_ERROR;
2214 break;
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);
2226 (void) close(fd);
2227 rc = BAM_ERROR;
2228 break;
2230 (void) close(fd);
2232 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2233 /* Not an ELF file, include in archive */
2234 rc = dircache_updatefile(path);
2235 } else {
2236 /* Include 64-bit ELF files only */
2237 switch (elf.e_ident[EI_CLASS]) {
2238 case ELFCLASS32:
2239 bam_print(_("WARNING: ELF file %s is 32-bit "
2240 "and will be excluded\n"), path);
2241 break;
2242 case ELFCLASS64:
2243 rc = dircache_updatefile(path);
2244 break;
2245 default:
2246 bam_print(_("WARNING: ELF file %s is neither "
2247 "32-bit nor 64-bit\n"), path);
2248 break;
2251 break;
2253 case FTW_D:
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,
2259 DO_CACHE_DIR);
2262 break;
2263 default:
2264 rc = BAM_ERROR;
2265 break;
2268 return (rc);
2271 /*ARGSUSED*/
2272 static int
2273 cmpstat(
2274 const char *file,
2275 const struct stat *st,
2276 int flags,
2277 struct FTW *ftw)
2279 uint_t sz;
2280 uint64_t *value;
2281 uint64_t filestat[2];
2282 int error, ret, status;
2284 struct safefile *safefilep;
2285 FILE *fp;
2286 struct stat sb;
2287 regex_t re;
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)))
2294 return (0);
2297 * Ignore broken links
2299 if (flags == FTW_SL && stat(file, &sb) < 0)
2300 return (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);
2311 if (error)
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)
2323 return (0);
2325 /* read in list of safe files */
2326 if (safefiles == NULL) {
2327 fp = fopen("/boot/solaris/filelist.safe", "r");
2328 if (fp != NULL) {
2329 safefiles = s_calloc(1,
2330 sizeof (struct safefile));
2331 safefilep = safefiles;
2332 safefilep->name = s_calloc(1, MAXPATHLEN +
2333 MAXNAMELEN);
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;
2344 (void) fclose(fp);
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) {
2354 char *strip;
2356 strip = (char *)file + strlen(rootbuf);
2357 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2358 file);
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)) {
2367 if (bam_verbose)
2368 bam_print(_(" new %s\n"), file);
2370 if (is_flag_on(IS_SPARC_TARGET)) {
2371 set_dir_flag(NEED_UPDATE);
2372 return (0);
2375 ret = update_dircache(file, flags);
2376 if (ret == BAM_ERROR) {
2377 bam_error(_("directory cache update failed for %s\n"),
2378 file);
2379 return (-1);
2382 return (0);
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 */
2392 return (0);
2394 if (is_flag_on(IS_SPARC_TARGET)) {
2395 set_dir_flag(NEED_UPDATE);
2396 } else {
2397 ret = update_dircache(file, flags);
2398 if (ret == BAM_ERROR) {
2399 bam_error(_("directory cache update "
2400 "failed for %s\n"), file);
2401 return (-1);
2405 if (bam_verbose)
2406 bam_print(_(" new %s\n"), file);
2407 return (0);
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())
2416 return (0);
2418 * File exists in old archive. Check if file has changed
2420 assert(sz == 2);
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);
2433 regfree(&re);
2434 if (status == 0) {
2435 (void) creat(
2436 NEED_UPDATE_SAFE_FILE,
2437 0644);
2438 return (0);
2441 safefilep = safefilep->next;
2445 if (is_flag_on(IS_SPARC_TARGET)) {
2446 set_dir_flag(NEED_UPDATE);
2447 } else {
2448 ret = update_dircache(file, flags);
2449 if (ret == BAM_ERROR) {
2450 bam_error(_("directory cache update failed "
2451 "for %s\n"), file);
2452 return (-1);
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 "
2464 "for %s\n"), file);
2465 return (-1);
2469 if (bam_verbose) {
2470 if (bam_smf_check)
2471 bam_print(" %s\n", file);
2472 else
2473 bam_print(_(" changed %s\n"), file);
2477 return (0);
2481 * Remove a directory path recursively
2483 static int
2484 rmdir_r(char *path)
2486 struct dirent *d = NULL;
2487 DIR *dir = NULL;
2488 char tpath[PATH_MAX];
2489 struct stat sb;
2491 if ((dir = opendir(path)) == NULL)
2492 return (-1);
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",
2498 path, d->d_name);
2499 if (stat(tpath, &sb) == 0) {
2500 if (sb.st_mode & S_IFDIR)
2501 (void) rmdir_r(tpath);
2502 else
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.
2516 static int
2517 set_cache_dir(char *root)
2519 struct stat sb;
2520 int ret = 0;
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);
2528 return (BAM_ERROR);
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());
2538 if (bam_verbose)
2539 bam_print(_("archive cache directory not found: %s\n"),
2540 get_cachedir());
2541 ret = mkdir(get_cachedir(), DIR_PERMS);
2542 if (ret < 0) {
2543 bam_error(_("mkdir of %s failed: %s\n"),
2544 get_cachedir(), strerror(errno));
2545 get_cachedir()[0] = '\0';
2546 return (ret);
2548 set_flag(NEED_CACHE_DIR);
2549 set_dir_flag(NO_EXTEND);
2552 return (BAM_SUCCESS);
2555 static int
2556 set_update_dir(char *root)
2558 struct stat sb;
2559 int ret;
2561 if (is_dir_flag_on(NO_EXTEND))
2562 return (BAM_SUCCESS);
2564 if (!bam_extend) {
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);
2575 return (BAM_ERROR);
2578 if (stat(get_updatedir(), &sb) == 0) {
2579 if (S_ISDIR(sb.st_mode))
2580 ret = rmdir_r(get_updatedir());
2581 else
2582 ret = unlink(get_updatedir());
2584 if (ret != 0)
2585 set_dir_flag(NO_EXTEND);
2588 if (mkdir(get_updatedir(), DIR_PERMS) < 0)
2589 set_dir_flag(NO_EXTEND);
2591 return (BAM_SUCCESS);
2594 static int
2595 is_valid_archive(char *root)
2597 char archive_path[PATH_MAX];
2598 char timestamp_path[PATH_MAX];
2599 struct stat sb, timestamp;
2600 int ret;
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);
2608 return (BAM_ERROR);
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
2620 * cache.
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);
2635 return (BAM_ERROR);
2638 if (stat(timestamp_path, &timestamp) != 0 ||
2639 sb.st_mtime > timestamp.st_mtime) {
2640 if (bam_verbose && !bam_check)
2641 bam_print(
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.)
2678 static int
2679 check_flags_and_files(char *root)
2682 struct stat sb;
2683 int ret;
2686 * If archive is missing, create archive
2688 ret = is_valid_archive(root);
2689 if (ret == BAM_ERROR)
2690 return (BAM_ERROR);
2692 if (bam_nowrite())
2693 return (BAM_SUCCESS);
2696 * check if cache directories exist on x86.
2697 * check (and always open) the cache file on SPARC.
2699 if (is_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);
2707 return (BAM_ERROR);
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));
2719 return (BAM_ERROR);
2722 set_dir_present();
2723 } else {
2724 if (set_cache_dir(root) != 0)
2725 return (BAM_ERROR);
2727 set_dir_present();
2729 if (set_update_dir(root) != 0)
2730 return (BAM_ERROR);
2734 * if force, create archive unconditionally
2736 if (bam_force) {
2737 set_dir_flag(NEED_UPDATE);
2738 if (bam_verbose)
2739 bam_print(_("forced update of archive requested\n"));
2740 return (BAM_SUCCESS);
2743 return (BAM_SUCCESS);
2746 static error_t
2747 read_one_list(char *root, filelist_t *flistp, char *filelist)
2749 char path[PATH_MAX];
2750 FILE *fp;
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");
2757 if (fp == NULL) {
2758 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2759 fcn, path, strerror(errno)));
2760 return (BAM_ERROR);
2762 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2763 /* skip blank lines */
2764 if (strspn(buf, " \t") == strlen(buf))
2765 continue;
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));
2771 return (BAM_ERROR);
2773 return (BAM_SUCCESS);
2776 static error_t
2777 read_list(char *root, filelist_t *flistp)
2779 char path[PATH_MAX];
2780 char cmd[PATH_MAX];
2781 struct stat sb;
2782 int n, rval;
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"));
2793 return (BAM_ERROR);
2796 if (is_safe_exec(path) == BAM_ERROR)
2797 return (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);
2815 *platarg = 0;
2816 *rootarg = 0;
2818 if (strlen(root) > 1) {
2819 (void) snprintf(rootarg, rootarglen,
2820 "-R %s", root);
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);
2828 free(platarg);
2829 free(rootarg);
2830 if (n >= sizeof (cmd)) {
2831 bam_error(_("archive filelist is empty\n"));
2832 return (BAM_ERROR);
2834 if (exec_cmd(cmd, flistp) != 0) {
2835 BAM_DPRINTF(("%s: failed to open archive "
2836 "filelist: %s: %s\n", fcn, path, strerror(errno)));
2837 return (BAM_ERROR);
2839 } else {
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)
2845 return (rval);
2846 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2849 if (flistp->head == NULL) {
2850 bam_error(_("archive filelist is empty\n"));
2851 return (BAM_ERROR);
2854 return (BAM_SUCCESS);
2857 static void
2858 getoldstat(char *root)
2860 char path[PATH_MAX];
2861 int fd, error;
2862 struct stat sb;
2863 char *ostat;
2865 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2866 fd = open(path, O_RDONLY);
2867 if (fd == -1) {
2868 if (bam_verbose)
2869 bam_print(_("failed to open file: %s: %s\n"),
2870 path, strerror(errno));
2871 goto out_err;
2874 if (fstat(fd, &sb) != 0) {
2875 bam_error(_("stat of file failed: %s: %s\n"), path,
2876 strerror(errno));
2877 goto out_err;
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,
2884 strerror(errno));
2885 free(ostat);
2886 goto out_err;
2889 (void) close(fd);
2890 fd = -1;
2892 walk_arg.old_nvlp = NULL;
2893 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2895 free(ostat);
2897 if (error) {
2898 bam_error(_("failed to unpack stat data: %s: %s\n"),
2899 path, strerror(error));
2900 walk_arg.old_nvlp = NULL;
2901 goto out_err;
2902 } else {
2903 return;
2906 out_err:
2907 if (fd != -1)
2908 (void) close(fd);
2909 set_dir_flag(NEED_UPDATE);
2912 /* Best effort stale entry removal */
2913 static void
2914 delete_stale(char *file)
2916 char path[PATH_MAX];
2917 struct stat sb;
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);
2923 else
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.
2936 static void
2937 check4stale(char *root)
2939 nvpair_t *nvp;
2940 nvlist_t *nvlp;
2941 char *file;
2942 char path[PATH_MAX];
2945 * Skip stale file check during smf check
2947 if (bam_smf_check)
2948 return;
2951 * If we need to (re)create the cache, there's no need to check for
2952 * stale files
2954 if (is_flag_on(NEED_CACHE_DIR))
2955 return;
2957 /* Nothing to do if no old stats */
2958 if ((nvlp = walk_arg.old_nvlp) == NULL)
2959 return;
2961 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2962 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2963 file = nvpair_name(nvp);
2964 if (file == NULL)
2965 continue;
2966 (void) snprintf(path, sizeof (path), "%s/%s",
2967 root, file);
2968 if (access(path, F_OK) < 0) {
2969 if (bam_verbose)
2970 bam_print(_(" stale %s\n"), path);
2972 if (is_flag_on(IS_SPARC_TARGET)) {
2973 set_dir_flag(NEED_UPDATE);
2974 } else {
2975 if (has_cachedir())
2976 delete_stale(file);
2982 static void
2983 create_newstat(void)
2985 int error;
2987 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2988 if (error) {
2990 * Not fatal - we can still create archive
2992 walk_arg.new_nvlp = NULL;
2993 bam_error(_("failed to create stat data: %s\n"),
2994 strerror(error));
2998 static int
2999 walk_list(char *root, filelist_t *flistp)
3001 char path[PATH_MAX];
3002 line_t *lp;
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))
3014 return (BAM_ERROR);
3016 * Some files may not exist.
3017 * For example: etc/rtc_config on a x86 diskless system
3018 * Emit verbose message only
3020 if (bam_verbose)
3021 bam_print(_("cannot find: %s: %s\n"),
3022 path, strerror(errno));
3026 return (BAM_SUCCESS);
3030 * Update the timestamp file.
3032 static void
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,
3048 strerror(errno));
3049 bam_error(_("failed to update the timestamp file, next"
3050 " archive update may experience reduced performance\n"));
3055 static void
3056 savenew(char *root)
3058 char path[PATH_MAX];
3059 char path2[PATH_MAX];
3060 size_t sz;
3061 char *nstat;
3062 int fd, wrote, error;
3064 nstat = NULL;
3065 sz = 0;
3066 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3067 NV_ENCODE_XDR, 0);
3068 if (error) {
3069 bam_error(_("failed to pack stat data: %s\n"),
3070 strerror(error));
3071 return;
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);
3076 if (fd == -1) {
3077 bam_error(_("failed to open file: %s: %s\n"), path,
3078 strerror(errno));
3079 free(nstat);
3080 return;
3082 wrote = write(fd, nstat, sz);
3083 if (wrote != sz) {
3084 bam_error(_("write to file failed: %s: %s\n"), path,
3085 strerror(errno));
3086 (void) close(fd);
3087 free(nstat);
3088 return;
3090 (void) close(fd);
3091 free(nstat);
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,
3096 strerror(errno));
3100 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3102 static void
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;
3115 * Returns:
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.
3128 static int
3129 update_required(char *root)
3131 struct stat sb;
3132 char path[PATH_MAX];
3133 filelist_t flist;
3134 filelist_t *flistp = &flist;
3135 int ret;
3137 flistp->head = flistp->tail = NULL;
3139 if (is_sparc())
3140 set_flag(IS_SPARC_TARGET);
3143 * Check if cache directories and archives are present
3146 ret = check_flags_and_files(root);
3147 if (ret < 0)
3148 return (BAM_ERROR);
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);
3159 return (0);
3163 getoldstat(root);
3166 * Check if the archive contains files that are no longer
3167 * present on the root filesystem.
3169 check4stale(root);
3172 * read list of files
3174 if (read_list(root, flistp) != BAM_SUCCESS) {
3175 clear_walk_args();
3176 return (BAM_ERROR);
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
3186 create_newstat();
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"));
3202 return (BAM_ERROR);
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)) {
3214 clear_walk_args();
3215 return (0);
3218 if (walk_arg.sparcfile != NULL)
3219 (void) fclose(walk_arg.sparcfile);
3221 return (1);
3224 static int
3225 flushfs(char *root)
3227 char cmd[PATH_MAX + 30];
3229 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3230 LOCKFS_PATH, root);
3232 return (exec_cmd(cmd, NULL));
3235 static int
3236 do_archive_copy(char *source, char *dest)
3239 sync();
3241 /* the equivalent of mv archive-new-$pid boot_archive */
3242 if (rename(source, dest) != 0) {
3243 (void) unlink(source);
3244 return (BAM_ERROR);
3247 if (flushfs(bam_root) != 0)
3248 sync();
3250 return (BAM_SUCCESS);
3253 static int
3254 check_cmdline(filelist_t flist)
3256 line_t *lp;
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);
3262 return (BAM_ERROR);
3266 return (BAM_SUCCESS);
3269 static void
3270 dump_errormsg(filelist_t flist)
3272 line_t *lp;
3274 for (lp = flist.head; lp; lp = lp->next)
3275 (void) fprintf(stderr, "%s\n", lp->line);
3278 static int
3279 check_archive(char *dest)
3281 struct stat sb;
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);
3287 return (BAM_ERROR);
3290 return (BAM_SUCCESS);
3293 static boolean_t
3294 is_be(char *root)
3296 zfs_handle_t *zhp;
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];
3303 if (!is_zfs(root))
3304 return (B_FALSE);
3306 * Get dataset for mountpoint
3308 if ((hdl = libzfs_init()) == NULL)
3309 return (B_FALSE);
3311 if ((zhp = zfs_path_to_zhandle(hdl, root,
3312 ZFS_TYPE_FILESYSTEM)) == NULL) {
3313 libzfs_fini(hdl);
3314 return (B_FALSE);
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) {
3332 be_exist = B_TRUE;
3333 break;
3336 be_free_list(be_nodes);
3338 zfs_close(zhp);
3339 libzfs_fini(hdl);
3341 return (be_exist);
3345 * Returns B_TRUE if mkiso is in the expected PATH and should be used,
3346 * B_FALSE otherwise
3348 static boolean_t
3349 use_mkisofs()
3351 scf_simple_prop_t *prop;
3352 char *format = NULL;
3353 boolean_t ret;
3355 /* Check whether the mkisofs binary is in the expected location */
3356 if (access(MKISOFS_PATH, X_OK) != 0) {
3357 if (bam_verbose)
3358 bam_print("mkisofs not found\n");
3359 return (B_FALSE);
3362 if (bam_format == BAM_FORMAT_HSFS) {
3363 if (bam_verbose)
3364 bam_print("-F specified HSFS");
3365 return (B_TRUE);
3368 /* If working on an alt-root, do not use HSFS unless asked via -F */
3369 if (bam_alt_root)
3370 return (B_FALSE);
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 */
3379 if (bam_verbose) {
3380 bam_print(
3381 "%s does not have %s/%s property, using mkisofs\n",
3382 BOOT_ARCHIVE_FMRI, SCF_PG_CONFIG,
3383 SCF_PROPERTY_FORMAT);
3385 return (B_TRUE);
3387 if (scf_simple_prop_numvalues(prop) < 0 ||
3388 (format = scf_simple_prop_next_astring(prop)) == NULL)
3389 ret = B_TRUE;
3390 else
3391 ret = strcmp(format, "hsfs") == 0 ? B_TRUE : B_FALSE;
3392 if (bam_verbose) {
3393 if (ret)
3394 bam_print("Creating hsfs boot archive\n");
3395 else
3396 bam_print("Creating %s boot archive\n", format);
3398 scf_simple_prop_free(prop);
3399 return (ret);
3402 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3404 static int
3405 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3407 int ret;
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);
3414 return (BAM_ERROR);
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,
3422 tempname, list);
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);
3429 goto out_err;
3432 filelist_free(&flist);
3435 * Prepare dd command line to copy the bootblk on the new archive and
3436 * execute it
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,
3440 bootblk, tempname);
3442 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3444 ret = exec_cmd(cmdline, &flist);
3445 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3446 goto out_err;
3448 filelist_free(&flist);
3450 /* Did we get a valid archive ? */
3451 if (check_archive(tempname) == BAM_ERROR)
3452 return (BAM_ERROR);
3454 return (do_archive_copy(tempname, archive));
3456 out_err:
3457 filelist_free(&flist);
3458 bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline);
3459 (void) unlink(tempname);
3460 return (BAM_ERROR);
3463 static unsigned int
3464 from_733(unsigned char *s)
3466 int i;
3467 unsigned int ret = 0;
3469 for (i = 0; i < 4; i++)
3470 ret |= s[i] << (8 * i);
3472 return (ret);
3475 static void
3476 to_733(unsigned char *s, unsigned int val)
3478 int i;
3480 for (i = 0; i < 4; i++)
3481 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3485 * creates sha1 hash of archive
3487 static int
3488 digest_archive(const char *archive)
3490 char *archive_hash;
3491 char *hash;
3492 int ret;
3493 FILE *fp;
3495 (void) asprintf(&archive_hash, "%s.hash", archive);
3496 if (archive_hash == NULL)
3497 return (BAM_ERROR);
3499 if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
3500 free(archive_hash);
3501 return (ret);
3504 fp = fopen(archive_hash, "w");
3505 if (fp == NULL) {
3506 free(archive_hash);
3507 free(hash);
3508 return (BAM_ERROR);
3511 (void) fprintf(fp, "%s\n", hash);
3512 (void) fclose(fp);
3513 free(hash);
3514 free(archive_hash);
3515 return (BAM_SUCCESS);
3519 * Extends the current boot archive without recreating it from scratch
3521 static int
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);
3532 if (fd == -1) {
3533 if (bam_verbose)
3534 bam_error(_("failed to open file: %s: %s\n"),
3535 archive, strerror(errno));
3536 goto out_err;
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)) {
3545 if (bam_verbose)
3546 bam_error(_("read failed for file: %s: %s\n"),
3547 archive, strerror(errno));
3548 goto out_err;
3551 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3552 if (bam_verbose)
3553 bam_error(_("iso descriptor signature for %s is "
3554 "invalid\n"), archive);
3555 goto out_err;
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) {
3572 if (bam_verbose) {
3573 bam_error(_("Command '%s' failed while generating "
3574 "multisession archive\n"), cmdline);
3575 dump_errormsg(flist);
3577 goto out_flist_err;
3579 filelist_free(&flist);
3581 newfd = open(tempname, O_RDONLY);
3582 if (newfd == -1) {
3583 if (bam_verbose)
3584 bam_error(_("failed to open file: %s: %s\n"),
3585 archive, strerror(errno));
3586 goto out_err;
3589 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3590 VOLDESC_OFF * CD_BLOCK);
3591 if (ret != sizeof (saved_desc)) {
3592 if (bam_verbose)
3593 bam_error(_("read failed for file: %s: %s\n"),
3594 archive, strerror(errno));
3595 goto out_err;
3598 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3599 if (bam_verbose)
3600 bam_error(_("iso descriptor signature for %s is "
3601 "invalid\n"), archive);
3602 goto out_err;
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)
3610 break;
3611 if (memcmp(saved_desc[i].id, "CD001", 5))
3612 break;
3614 if (bam_verbose)
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) {
3623 if (bam_verbose)
3624 bam_error(_("write to file failed: %s: %s\n"),
3625 archive, strerror(errno));
3626 goto out_err;
3628 (void) close(newfd);
3629 newfd = -1;
3631 ret = fsync(fd);
3632 if (ret != 0)
3633 sync();
3635 ret = close(fd);
3636 if (ret != 0) {
3637 if (bam_verbose)
3638 bam_error(_("failed to close file: %s: %s\n"),
3639 archive, strerror(errno));
3640 return (BAM_ERROR);
3642 fd = -1;
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,
3646 (next_session/16));
3648 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3650 ret = exec_cmd(cmdline, &flist);
3651 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3652 if (bam_verbose)
3653 bam_error(_("Command '%s' failed while generating "
3654 "multisession archive\n"), cmdline);
3655 goto out_flist_err;
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)
3665 sync();
3667 if (bam_verbose)
3668 bam_print("boot archive updated successfully\n");
3670 return (BAM_SUCCESS);
3672 out_flist_err:
3673 filelist_free(&flist);
3674 out_err:
3675 if (fd != -1)
3676 (void) close(fd);
3677 if (newfd != -1)
3678 (void) close(newfd);
3679 return (BAM_ERROR);
3682 static int
3683 create_x86_archive(char *archive, char *tempname, char *update_dir)
3685 int ret;
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"),
3698 cmdline);
3699 dump_errormsg(flist);
3700 filelist_free(&flist);
3701 (void) unlink(tempname);
3702 return (BAM_ERROR);
3705 filelist_free(&flist);
3707 if (check_archive(tempname) == BAM_ERROR)
3708 return (BAM_ERROR);
3710 return (do_archive_copy(tempname, archive));
3713 static int
3714 mkisofs_archive(char *root)
3716 int ret;
3717 char suffix[20];
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))
3724 goto out_path_err;
3726 ret = build_path(temp, sizeof (temp), root, ARCHIVE_PREFIX, suffix);
3728 if (ret >= sizeof (temp))
3729 goto out_path_err;
3731 ret = build_path(boot_archive, sizeof (boot_archive), root,
3732 ARCHIVE_PREFIX, ARCHIVE_SUFFIX);
3734 if (ret >= sizeof (boot_archive))
3735 goto out_path_err;
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))
3744 goto out_path_err;
3746 ret = create_sparc_archive(boot_archive, temp, bootblk,
3747 get_cachedir());
3748 } else {
3749 if (!is_dir_flag_on(NO_EXTEND)) {
3750 if (bam_verbose)
3751 bam_print("Attempting to extend x86 archive: "
3752 "%s\n", boot_archive);
3754 ret = extend_iso_archive(boot_archive, temp,
3755 get_updatedir());
3756 if (ret == BAM_SUCCESS) {
3757 if (bam_verbose)
3758 bam_print("Successfully extended %s\n",
3759 boot_archive);
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
3775 if (bam_verbose)
3776 bam_print("Unable to extend %s... rebuilding archive\n",
3777 boot_archive);
3779 if (get_updatedir()[0] != '\0')
3780 (void) rmdir_r(get_updatedir());
3783 ret = create_x86_archive(boot_archive, temp,
3784 get_cachedir());
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);
3793 return (ret);
3795 out_path_err:
3796 bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3797 root);
3798 return (BAM_ERROR);
3801 static int
3802 assemble_systemfile(char *infilename, char *outfilename)
3804 char buf[BUFSIZ];
3805 FILE *infile, *outfile;
3806 size_t n;
3808 if ((infile = fopen(infilename, "r")) == NULL) {
3809 bam_error(_("failed to open file: %s: %s\n"), infilename,
3810 strerror(errno));
3811 return (BAM_ERROR);
3814 if ((outfile = fopen(outfilename, "a")) == NULL) {
3815 bam_error(_("failed to open file: %s: %s\n"), outfilename,
3816 strerror(errno));
3817 (void) fclose(infile);
3818 return (BAM_ERROR);
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);
3827 return (BAM_ERROR);
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.
3842 static error_t
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;
3849 struct stat st;
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;
3860 } else {
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));
3869 return (BAM_ERROR);
3872 (void) unlink(tmpfile);
3874 for (i = 0; i < files; i++) {
3875 char filepath[PATH_MAX];
3876 char *fname;
3878 fname = filelist[i]->d_name;
3880 /* skip anything that starts with a dot */
3881 if (strncmp(fname, ".", 1) == 0) {
3882 free(filelist[i]);
3883 continue;
3886 if (bam_verbose)
3887 bam_print(_("/etc/system.d adding %s/%s\n"),
3888 path, fname);
3890 (void) snprintf(filepath, sizeof (filepath), "%s/%s",
3891 path, fname);
3893 if ((assemble_systemfile(filepath, tmpfile)) < 0) {
3894 bam_error(_("failed to append file: %s: %s\n"),
3895 filepath, strerror(errno));
3896 ret = BAM_ERROR;
3897 break;
3899 sysfiles++;
3902 if (sysfiles > 0) {
3903 if (rename(tmpfile, self_assembly) < 0) {
3904 bam_error(_("failed to rename file: %s: %s\n"), tmpfile,
3905 strerror(errno));
3906 return (BAM_ERROR);
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"),
3916 strerror(errno));
3917 return (BAM_ERROR);
3919 } else {
3920 (void) unlink(tmpfile);
3921 (void) unlink(self_assembly);
3923 return (ret);
3926 static error_t
3927 create_ramdisk(char *root)
3929 char *cmdline, path[PATH_MAX];
3930 size_t len;
3931 struct stat sb;
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);
3938 if (ret != 0)
3939 status = BAM_ERROR;
3941 return (status);
3942 } else if (bam_format == BAM_FORMAT_HSFS) {
3943 bam_error(_("cannot create hsfs archive\n"));
3944 return (BAM_ERROR);
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));
3956 return (BAM_ERROR);
3959 if (is_safe_exec(path) == BAM_ERROR)
3960 return (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';
3979 } else
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"));
3986 free(cmdline);
3987 return (BAM_ERROR);
3991 if (exec_cmd(cmdline, NULL) != 0) {
3992 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3993 cmdline);
3994 free(cmdline);
3995 return (BAM_ERROR);
3997 free(cmdline);
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
4008 * 1 - is miniroot
4009 * 0 - is not
4010 * When in doubt assume it is not a ramdisk.
4012 static int
4013 is_ramdisk(char *root)
4015 struct extmnttab mnt;
4016 FILE *fp;
4017 int found;
4018 char mntpt[PATH_MAX];
4019 char *cp;
4022 * There are 3 situations where creating archive is
4023 * of dubious value:
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
4028 * worth it.
4029 * The other two conditions are handled here
4031 fp = fopen(MNTTAB, "r");
4032 if (fp == NULL) {
4033 bam_error(_("failed to open file: %s: %s\n"),
4034 MNTTAB, strerror(errno));
4035 return (0);
4038 resetmnttab(fp);
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;
4046 if (*cp == '/')
4047 *cp = '\0';
4049 found = 0;
4050 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4051 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
4052 found = 1;
4053 break;
4057 if (!found) {
4058 if (bam_verbose)
4059 bam_error(_("alternate root %s not in mnttab\n"),
4060 mntpt);
4061 (void) fclose(fp);
4062 return (0);
4065 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
4066 strlen(RAMDISK_SPECIAL)) == 0) {
4067 if (bam_verbose)
4068 bam_error(_("%s is on a ramdisk device\n"), bam_root);
4069 (void) fclose(fp);
4070 return (1);
4073 (void) fclose(fp);
4075 return (0);
4078 static int
4079 is_boot_archive(char *root)
4081 char path[PATH_MAX];
4082 struct stat sb;
4083 int error;
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);
4092 if (error == -1) {
4093 if (bam_verbose)
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));
4097 return (0);
4100 BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
4101 fcn, root));
4102 return (1);
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];
4118 struct stat sb;
4119 void *defp;
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) {
4126 return (0);
4127 } else {
4128 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
4129 if (res != NULL && res[0] != '\0') {
4130 if (strcasecmp(res, "true") == 0)
4131 grub = B_TRUE;
4133 defclose_r(defp);
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",
4140 fcn, path));
4141 return (0);
4142 } else
4143 return (1);
4146 return (0);
4150 is_zfs(char *root)
4152 struct statvfs vfs;
4153 int ret;
4154 const char *fcn = "is_zfs()";
4156 ret = statvfs(root, &vfs);
4157 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
4158 if (ret != 0) {
4159 bam_error(_("statvfs failed for %s: %s\n"), root,
4160 strerror(errno));
4161 return (0);
4164 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
4165 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
4166 return (1);
4167 } else {
4168 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
4169 return (0);
4174 is_pcfs(char *root)
4176 struct statvfs vfs;
4177 int ret;
4178 const char *fcn = "is_pcfs()";
4180 ret = statvfs(root, &vfs);
4181 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
4182 if (ret != 0) {
4183 bam_error(_("statvfs failed for %s: %s\n"), root,
4184 strerror(errno));
4185 return (0);
4188 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
4189 BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn, root));
4190 return (1);
4191 } else {
4192 BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4193 fcn, root));
4194 return (0);
4198 static int
4199 is_readonly(char *root)
4201 int fd;
4202 int error;
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
4209 * create a file
4211 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
4212 root, BOOTADM_RDONLY_TEST, getpid());
4214 (void) unlink(testfile);
4216 errno = 0;
4217 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
4218 error = errno;
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));
4222 return (1);
4223 } else if (fd == -1) {
4224 bam_error(_("error during read-only test on %s: %s\n"),
4225 root, strerror(error));
4228 (void) close(fd);
4229 (void) unlink(testfile);
4231 BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
4232 return (0);
4235 static error_t
4236 update_archive(char *root, char *opt)
4238 error_t ret;
4240 assert(root);
4241 assert(opt == NULL);
4243 init_walk_args();
4244 (void) umask(022);
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);
4262 return (BAM_ERROR);
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);
4286 bam_check = 1;
4290 * Process the /etc/system.d/.self-assembly file.
4292 if (build_etc_system_dir(bam_root) == BAM_ERROR)
4293 return (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
4304 * is required.
4306 if (bam_nowrite()) {
4307 if (is_flag_on(RDONLY_FSCHK)) {
4308 bam_check = bam_saved_check;
4309 if (ret > 0)
4310 bam_error(_("%s filesystem is read-only, "
4311 "skipping archives update\n"), root);
4312 if (bam_update_all)
4313 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4316 bam_exit((ret != 0) ? 1 : 0);
4319 if (ret == 1) {
4320 /* create the ramdisk */
4321 ret = create_ramdisk(root);
4325 * if the archive is updated, save the new stat data and update the
4326 * timestamp file
4328 if (ret == 0 && walk_arg.new_nvlp != NULL) {
4329 savenew(root);
4330 update_timestamp(root);
4333 clear_walk_args();
4335 return (ret);
4338 static char *
4339 find_root_pool()
4341 char *special = get_special("/");
4342 char *p;
4344 if (special == NULL)
4345 return (NULL);
4347 if (*special == '/') {
4348 free(special);
4349 return (NULL);
4352 if ((p = strchr(special, '/')) != NULL)
4353 *p = '\0';
4355 return (special);
4358 static error_t
4359 synchronize_BE_menu(void)
4361 struct stat sb;
4362 char cmdline[PATH_MAX];
4363 char cksum_line[PATH_MAX];
4364 filelist_t flist = {0};
4365 char *old_cksum_str;
4366 char *old_size_str;
4367 char *old_file;
4368 char *curr_cksum_str;
4369 char *curr_size_str;
4370 char *curr_file;
4371 char *pool = NULL;
4372 char *mntpt = NULL;
4373 zfs_mnted_t mnted;
4374 FILE *cfp;
4375 int found;
4376 int ret;
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));
4390 goto menu_sync;
4393 cfp = fopen(LU_MENU_CKSUM, "r");
4394 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4395 if (cfp == NULL) {
4396 bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4397 LU_MENU_CKSUM);
4398 goto menu_sync;
4400 BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn, LU_MENU_CKSUM));
4402 found = 0;
4403 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4404 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4405 if (found) {
4406 bam_error(_("multiple checksums for GRUB menu in "
4407 "checksum file: %s\n"), LU_MENU_CKSUM);
4408 (void) fclose(cfp);
4409 goto menu_sync;
4411 found = 1;
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"),
4425 LU_MENU_CKSUM);
4426 goto menu_sync;
4428 BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn, LU_MENU_CKSUM));
4430 /* Get checksum of current menu */
4431 pool = find_root_pool();
4432 if (pool) {
4433 mntpt = mount_top_dataset(pool, &mnted);
4434 if (mntpt == NULL) {
4435 bam_error(_("failed to mount top dataset for %s\n"),
4436 pool);
4437 free(pool);
4438 return (BAM_ERROR);
4440 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4441 CKSUM, mntpt, GRUB_MENU);
4442 } else {
4443 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4444 CKSUM, GRUB_MENU);
4446 ret = exec_cmd(cmdline, &flist);
4447 if (pool) {
4448 (void) umount_top_dataset(pool, mnted, mntpt);
4449 free(pool);
4451 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4452 if (ret != 0) {
4453 bam_error(_("error generating checksum of GRUB menu\n"));
4454 return (BAM_ERROR);
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);
4462 return (BAM_ERROR);
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);
4478 return (BAM_ERROR);
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));
4495 menu_sync:
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);
4503 if (ret != 0) {
4504 bam_error(_("error propagating updated GRUB menu\n"));
4505 return (BAM_ERROR);
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);
4513 if (ret != 0) {
4514 bam_error(_("failed to create backup for GRUB menu: %s\n"),
4515 GRUB_BACKUP_MENU);
4516 return (BAM_ERROR);
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);
4526 if (ret != 0) {
4527 bam_error(_("error propagating backup GRUB menu: %s\n"),
4528 GRUB_BACKUP_MENU);
4529 return (BAM_ERROR);
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);
4538 if (ret != 0) {
4539 bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4540 LU_MENU_CKSUM);
4541 return (BAM_ERROR);
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);
4551 if (ret != 0) {
4552 bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4553 LU_MENU_CKSUM);
4554 return (BAM_ERROR);
4556 BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4557 fcn, LU_MENU_CKSUM));
4559 return (BAM_SUCCESS);
4562 static error_t
4563 update_all(char *root, char *opt)
4565 struct extmnttab mnt;
4566 struct stat sb;
4567 FILE *fp;
4568 char multibt[PATH_MAX];
4569 char creatram[PATH_MAX];
4570 error_t ret = BAM_SUCCESS;
4572 assert(root);
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);
4579 return (BAM_ERROR);
4583 * First update archive for current root
4585 if (update_archive(root, opt) != BAM_SUCCESS)
4586 ret = BAM_ERROR;
4588 if (ret == BAM_ERROR)
4589 goto out;
4592 * Now walk the mount table, performing archive update
4593 * for all mounted Newboot root filesystems
4595 fp = fopen(MNTTAB, "r");
4596 if (fp == NULL) {
4597 bam_error(_("failed to open file: %s: %s\n"),
4598 MNTTAB, strerror(errno));
4599 ret = BAM_ERROR;
4600 goto out;
4603 resetmnttab(fp);
4605 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4606 if (mnt.mnt_special == NULL)
4607 continue;
4608 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4609 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4610 continue;
4611 if (strcmp(mnt.mnt_mountp, "/") == 0)
4612 continue;
4614 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4615 mnt.mnt_mountp, CREATE_RAMDISK);
4617 if (stat(creatram, &sb) == -1)
4618 continue;
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/",
4625 mnt.mnt_mountp);
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))
4634 ret = BAM_ERROR;
4637 (void) fclose(fp);
4639 out:
4641 * We no longer use biosdev for Live Upgrade. Hence
4642 * there is no need to defer (to shutdown time) any fdisk
4643 * updates
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)
4655 ret = BAM_ERROR;
4657 return (ret);
4660 static void
4661 append_line(menu_t *mp, line_t *lp)
4663 if (mp->start == NULL) {
4664 mp->start = lp;
4665 } else {
4666 mp->end->next = lp;
4667 lp->prev = mp->end;
4669 mp->end = lp;
4672 void
4673 unlink_line(menu_t *mp, line_t *lp)
4675 /* unlink from list */
4676 if (lp->prev)
4677 lp->prev->next = lp->next;
4678 else
4679 mp->start = lp->next;
4680 if (lp->next)
4681 lp->next->prev = lp->prev;
4682 else
4683 mp->end = lp->prev;
4686 static entry_t *
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()";
4692 assert(mp);
4693 assert(start);
4694 assert(end);
4696 ent = s_calloc(1, sizeof (entry_t));
4697 BAM_DPRINTF(("%s: new boot entry alloced\n", fcn));
4698 ent->start = start;
4699 ent->end = end;
4701 if (mp->entries == NULL) {
4702 mp->entries = ent;
4703 BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn));
4704 return (ent);
4707 prev = mp->entries;
4708 while (prev->next)
4709 prev = prev->next;
4710 prev->next = ent;
4711 ent->prev = prev;
4712 BAM_DPRINTF(("%s: new boot entry linked in\n", fcn));
4713 return (ent);
4716 static void
4717 boot_entry_addline(entry_t *ent, line_t *lp)
4719 if (ent)
4720 ent->end = 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.
4729 static int
4730 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4732 int ret;
4733 const char *fcn = "check_cmd()";
4735 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, arg, str));
4737 if (cmd != NULL) {
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]));
4742 return (0);
4744 ret = (strstr(arg, str) != NULL);
4745 } else
4746 ret = 0;
4748 if (ret) {
4749 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
4750 } else {
4751 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
4754 return (ret);
4757 static error_t
4758 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4760 const char *fcn = "kernel_parser()";
4762 assert(entry);
4763 assert(cmd);
4764 assert(arg);
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));
4769 return (BAM_ERROR);
4772 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4773 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4774 fcn, arg));
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",
4783 fcn, arg));
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",
4788 fcn, arg));
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
4795 | BAM_ENTRY_32BIT;
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
4801 | BAM_ENTRY_64BIT;
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));
4815 return (BAM_ERROR);
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;
4822 } else {
4823 BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn, arg));
4824 bam_error(_("kernel command on line %d not recognized.\n"),
4825 linenum);
4826 return (BAM_ERROR);
4829 return (BAM_SUCCESS);
4832 static error_t
4833 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4835 const char *fcn = "module_parser()";
4837 assert(entry);
4838 assert(cmd);
4839 assert(arg);
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));
4844 return (BAM_ERROR);
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));
4862 return (BAM_ERROR);
4863 } else {
4864 BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn, arg));
4865 bam_error(_("module command on line %d not recognized.\n"),
4866 linenum);
4867 return (BAM_ERROR);
4872 * A line in menu.lst looks like
4873 * [ ]*<cmd>[ \t=]*<arg>*
4875 static void
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
4881 * been processed
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;
4888 line_t *lp;
4889 char *cmd, *sep, *arg;
4890 char save, *cp, *line;
4891 menu_flag_t flag = BAM_INVALID;
4892 const char *fcn = "line_parser()";
4894 cmd = NULL;
4895 if (str == NULL) {
4896 return;
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')
4907 str++;
4909 if (*str == '#') { /* comment */
4910 cmd = s_strdup("#");
4911 sep = NULL;
4912 arg = s_strdup(str + 1);
4913 flag = BAM_COMMENT;
4914 if (strstr(arg, BAM_LU_HDR) != NULL) {
4915 in_liveupgrade = 1;
4916 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4917 in_liveupgrade = 0;
4918 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4919 is_libbe_ent = 1;
4921 } else if (*str == '\0') { /* blank line */
4922 cmd = sep = arg = NULL;
4923 flag = BAM_EMPTY;
4924 } else {
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.
4931 cp = str;
4932 while (*str != ' ' && *str != '\t' && *str != '=') {
4933 if (*str == '\0') {
4934 cmd = s_strdup(cp);
4935 sep = arg = NULL;
4936 break;
4938 str++;
4941 if (*str != '\0') {
4942 save = *str;
4943 *str = '\0';
4944 cmd = s_strdup(cp);
4945 *str = save;
4947 str++;
4948 save = *str;
4949 *str = '\0';
4950 sep = s_strdup(str - 1);
4951 *str = save;
4953 while (*str == ' ' || *str == '\t')
4954 str++;
4955 if (*str == '\0')
4956 arg = NULL;
4957 else
4958 arg = s_strdup(str);
4962 lp = s_calloc(1, sizeof (line_t));
4964 lp->cmd = cmd;
4965 lp->sep = sep;
4966 lp->arg = arg;
4967 lp->line = line;
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",
4978 fcn, arg));
4979 } else {
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",
4984 fcn, arg));
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;
4994 lp->flags = flag;
4995 } else {
4996 lp->entryNum = *entryNum;
4998 if (*entryNum == ENTRY_INIT) {
4999 lp->flags = BAM_GLOBAL;
5000 } else {
5001 lp->flags = BAM_ENTRY;
5003 if (cmd && arg) {
5004 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
5005 BAM_DPRINTF(("%s: setting ROOT: %s\n",
5006 fcn, arg));
5007 curr_ent->flags |= BAM_ENTRY_ROOT;
5008 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
5009 == 0) {
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));
5017 curr_ent->flags |=
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,
5022 arg, lp->lineNum);
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))) {
5041 if (is_libbe_ent) {
5042 curr_ent->flags |= BAM_ENTRY_LIBBE;
5043 is_libbe_ent = 0;
5046 boot_entry_addline(curr_ent, lp);
5048 append_line(mp, lp);
5050 prev = lp;
5053 void
5054 update_numbering(menu_t *mp)
5056 int lineNum;
5057 int entryNum;
5058 int old_default_value;
5059 line_t *lp, *prev, *default_lp, *default_entry;
5060 char buf[PATH_MAX];
5062 if (mp->start == NULL) {
5063 return;
5066 lineNum = LINE_INIT;
5067 entryNum = ENTRY_INIT;
5068 old_default_value = ENTRY_INIT;
5069 lp = default_lp = default_entry = NULL;
5071 prev = 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 &&
5080 lp->arg) {
5081 old_default_value = atoi(lp->arg);
5082 default_lp = lp;
5086 * If not a booting entry, nothing else to fix for this
5087 * entry
5089 if (lp->entryNum == ENTRY_INIT)
5090 continue;
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) {
5102 default_entry = lp;
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 &&
5113 prev->arg &&
5114 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
5115 prev->entryNum = lp->entryNum;
5117 } else {
5118 lp->entryNum = entryNum;
5123 * No default command in menu, simply return
5125 if (default_lp == NULL) {
5126 return;
5129 free(default_lp->arg);
5130 free(default_lp->line);
5132 if (default_entry == NULL) {
5133 default_lp->arg = s_strdup("0");
5134 } else {
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);
5150 static menu_t *
5151 menu_read(char *menu_path)
5153 FILE *fp;
5154 char buf[BAM_MAXLINE], *cp;
5155 menu_t *mp;
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 */
5162 free(mp);
5163 return (NULL);
5166 /* Note: GRUB boot entry number starts with 0 */
5167 line = LINE_INIT;
5168 entry = ENTRY_INIT;
5169 cp = buf;
5170 len = sizeof (buf);
5171 while (s_fgets(cp, len, fp) != NULL) {
5172 n = strlen(cp);
5173 if (cp[n - 1] == '\\') {
5174 len -= n - 1;
5175 assert(len >= 2);
5176 cp += n - 1;
5177 continue;
5179 line_parser(mp, buf, &line, &entry);
5180 cp = buf;
5181 len = sizeof (buf);
5184 if (fclose(fp) == EOF) {
5185 bam_error(_("failed to close file: %s: %s\n"), menu_path,
5186 strerror(errno));
5189 return (mp);
5192 static error_t
5193 selector(menu_t *mp, char *opt, int *entry, char **title)
5195 char *eq;
5196 char *opt_dup;
5197 int entryNum;
5199 assert(mp);
5200 assert(mp->start);
5201 assert(opt);
5203 opt_dup = s_strdup(opt);
5205 if (entry)
5206 *entry = ENTRY_INIT;
5207 if (title)
5208 *title = NULL;
5210 eq = strchr(opt_dup, '=');
5211 if (eq == NULL) {
5212 bam_error(_("invalid option: %s\n"), opt);
5213 free(opt_dup);
5214 return (BAM_ERROR);
5217 *eq = '\0';
5218 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
5219 assert(mp->end);
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);
5223 free(opt_dup);
5224 return (BAM_ERROR);
5226 *entry = entryNum;
5227 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
5228 *title = opt + (eq - opt_dup) + 1;
5229 } else {
5230 bam_error(_("invalid option: %s\n"), opt);
5231 free(opt_dup);
5232 return (BAM_ERROR);
5235 free(opt_dup);
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
5246 static error_t
5247 list_entry(menu_t *mp, char *menu_path, char *opt)
5249 line_t *lp;
5250 int entry = ENTRY_INIT;
5251 int found;
5252 char *title = NULL;
5254 assert(mp);
5255 assert(menu_path);
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);
5263 return (BAM_ERROR);
5266 if (opt != NULL) {
5267 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
5268 return (BAM_ERROR);
5270 assert((entry != ENTRY_INIT) ^ (title != NULL));
5271 } else {
5272 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
5273 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
5276 found = 0;
5277 for (lp = mp->start; lp; lp = lp->next) {
5278 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
5279 continue;
5280 if (opt == NULL && lp->flags == BAM_TITLE) {
5281 bam_print(_("%d %s\n"), lp->entryNum,
5282 lp->arg);
5283 found = 1;
5284 continue;
5286 if (entry != ENTRY_INIT && lp->entryNum == entry) {
5287 bam_print(_("%s\n"), lp->line);
5288 found = 1;
5289 continue;
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;
5302 found = 1;
5303 continue;
5307 if (!found) {
5308 bam_error(_("no matching entry found\n"));
5309 return (BAM_ERROR);
5312 return (BAM_SUCCESS);
5316 add_boot_entry(menu_t *mp,
5317 char *title,
5318 char *findroot,
5319 char *kernel,
5320 char *mod_kernel,
5321 char *module,
5322 char *bootfs)
5324 int lineNum;
5325 int entryNum;
5326 char linebuf[BAM_MAXLINE];
5327 menu_cmd_t k_cmd;
5328 menu_cmd_t m_cmd;
5329 const char *fcn = "add_boot_entry()";
5331 assert(mp);
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"));
5336 return (BAM_ERROR);
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]);
5344 return (BAM_ERROR);
5346 if (module == NULL) {
5347 if (bam_direct != BAM_DIRECT_DBOOT) {
5348 bam_error(_("missing suboption: %s\n"),
5349 menu_cmds[MODULE_CMD]);
5350 return (BAM_ERROR);
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;
5358 } else {
5359 module = DIRECT_BOOT_ARCHIVE_32;
5363 k_cmd = KERNEL_DOLLAR_CMD;
5364 m_cmd = MODULE_DOLLAR_CMD;
5366 if (mp->start) {
5367 lineNum = mp->end->lineNum;
5368 entryNum = mp->end->entryNum;
5369 } else {
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);
5416 return (entryNum);
5419 error_t
5420 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5422 line_t *lp;
5423 line_t *freed;
5424 entry_t *ent;
5425 entry_t *tmp;
5426 int deleted = 0;
5427 const char *fcn = "delete_boot_entry()";
5429 assert(entryNum != ENTRY_INIT);
5431 tmp = NULL;
5433 ent = mp->entries;
5434 while (ent) {
5435 lp = ent->start;
5438 * Check entry number and make sure it's a modifiable entry.
5440 * Guidelines:
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)) {
5448 ent = ent->next;
5449 continue;
5452 /* free the entry content */
5453 do {
5454 freed = lp;
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);
5459 line_free(freed);
5460 } while (freed != ent->end);
5462 /* free the entry_t structure */
5463 assert(tmp == NULL);
5464 tmp = ent;
5465 ent = ent->next;
5466 if (tmp->prev)
5467 tmp->prev->next = ent;
5468 else
5469 mp->entries = ent;
5470 if (ent)
5471 ent->prev = tmp->prev;
5472 BAM_DPRINTF(("%s: freeing entry: %d\n", fcn, tmp->entryNum));
5473 free(tmp);
5474 tmp = NULL;
5475 deleted = 1;
5478 assert(tmp == NULL);
5480 if (!deleted && entryNum != ALL_ENTRIES) {
5481 if (quiet == DBE_PRINTERR)
5482 bam_error(_("no matching bootadm entry found\n"));
5483 return (BAM_ERROR);
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);
5495 static error_t
5496 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5498 assert(mp);
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) {
5510 return (BAM_ERROR);
5513 return (BAM_WRITE);
5516 static FILE *
5517 create_diskmap(char *osroot)
5519 FILE *fp;
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");
5526 if (fp == NULL) {
5527 int ret;
5529 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5530 CREATE_DISKMAP);
5531 if (ret >= sizeof (path)) {
5532 bam_error(_("unable to create path on mountpoint %s, "
5533 "path too long\n"), osroot);
5534 return (NULL);
5536 if (is_safe_exec(path) == BAM_ERROR)
5537 return (NULL);
5539 (void) snprintf(cmd, sizeof (cmd),
5540 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5541 if (exec_cmd(cmd, NULL) != 0)
5542 return (NULL);
5543 fp = fopen(GRUBDISK_MAP, "r");
5544 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5545 if (fp) {
5546 BAM_DPRINTF(("%s: created diskmap file: %s\n",
5547 fcn, GRUBDISK_MAP));
5548 } else {
5549 BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5550 fcn, GRUBDISK_MAP));
5553 return (fp);
5556 #define SECTOR_SIZE 512
5558 static int
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;
5565 #ifdef i386
5566 ext_part_t *epp;
5567 uint32_t secnum, numsec;
5568 int rval, pno, ext_partno = PARTNO_NOTFOUND;
5569 #endif
5571 /* form whole disk (p0) */
5572 slice = device + strlen(device) - 2;
5573 is_pcfs = (*slice != 's');
5574 if (!is_pcfs)
5575 *slice = '\0';
5576 wholedisk = s_calloc(1, strlen(device) + 3);
5577 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5578 if (!is_pcfs)
5579 *slice = 's';
5581 /* read boot sector */
5582 fd = open(wholedisk, O_RDONLY);
5583 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5584 return (partno);
5586 (void) close(fd);
5588 #ifdef i386
5589 /* Read/Initialize extended partition information */
5590 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5591 != FDISK_SUCCESS) {
5592 switch (rval) {
5594 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5595 * be considered as soft errors and hence
5596 * we do not return
5598 case FDISK_EBADLOGDRIVE:
5599 break;
5600 case FDISK_ENOLOGDRIVE:
5601 break;
5602 case FDISK_EBADMAGIC:
5603 /*FALLTHROUGH*/
5604 default:
5605 free(wholedisk);
5606 libfdisk_fini(&epp);
5607 return (partno);
5610 #endif
5611 free(wholedisk);
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) {
5620 partno = i;
5621 break;
5623 } else { /* look for solaris partition, old and new */
5624 if (part->systid == EFI_PMBR) {
5625 partno = PARTNO_EFI;
5626 break;
5629 #ifdef i386
5630 if ((part->systid == SUNIXOS &&
5631 (fdisk_is_linux_swap(epp, part->relsect,
5632 NULL) != 0)) || part->systid == SUNIXOS2) {
5633 #else
5634 if (part->systid == SUNIXOS ||
5635 part->systid == SUNIXOS2) {
5636 #endif
5637 partno = i;
5638 break;
5641 #ifdef i386
5642 if (fdisk_is_dos_extended(part->systid))
5643 ext_partno = i;
5644 #endif
5647 #ifdef i386
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) {
5652 partno = pno - 1;
5655 libfdisk_fini(&epp);
5656 #endif
5657 return (partno);
5660 char *
5661 get_grubroot(char *osroot, char *osdev, char *menu_root)
5663 char *grubroot; /* (hd#,#,#) */
5664 char *slice;
5665 char *grubhd = NULL;
5666 int fdiskpart;
5667 int found = 0;
5668 char *devname;
5669 char *ctdname = strstr(osdev, "dsk/");
5670 char linebuf[PATH_MAX];
5671 FILE *fp;
5673 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5674 if (ctdname == NULL) {
5675 bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev);
5676 return (NULL);
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);
5683 return (NULL);
5686 ctdname += strlen("dsk/");
5687 slice = strrchr(ctdname, 's');
5688 if (slice)
5689 *slice = '\0';
5691 fp = create_diskmap(osroot);
5692 if (fp == NULL) {
5693 bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5694 osroot);
5695 return (NULL);
5698 rewind(fp);
5699 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5700 grubhd = strtok(linebuf, " \t\n");
5701 if (grubhd)
5702 devname = strtok(NULL, " \t\n");
5703 else
5704 devname = NULL;
5705 if (devname && strcmp(devname, ctdname) == 0) {
5706 found = 1;
5707 break;
5711 if (slice)
5712 *slice = 's';
5714 (void) fclose(fp);
5715 fp = NULL;
5717 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5718 if (found == 0) {
5719 bam_error(_("not using biosdev command for disk: %s.\n"),
5720 osdev);
5721 return (NULL);
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"),
5728 osdev);
5729 return (NULL);
5732 grubroot = s_calloc(1, 10);
5733 if (fdiskpart == PARTNO_EFI) {
5734 fdiskpart = atoi(&slice[1]);
5735 slice = NULL;
5738 if (slice) {
5739 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5740 grubhd, fdiskpart, slice[1] + 'a' - '0');
5741 } else
5742 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5743 grubhd, fdiskpart);
5745 assert(fp == NULL);
5746 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5747 return (grubroot);
5750 static char *
5751 find_primary_common(char *mntpt, char *fstype)
5753 char signdir[PATH_MAX];
5754 char tmpsign[MAXNAMELEN + 1];
5755 char *lu;
5756 char *ufs;
5757 char *zfs;
5758 DIR *dirp = NULL;
5759 struct dirent *entp;
5760 struct stat sb;
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));
5768 return (NULL);
5771 dirp = opendir(signdir);
5772 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5773 if (dirp == NULL) {
5774 bam_error(_("opendir of %s failed: %s\n"), signdir,
5775 strerror(errno));
5776 return (NULL);
5779 ufs = zfs = lu = NULL;
5781 while ((entp = readdir(dirp)) != NULL) {
5782 if (strcmp(entp->d_name, ".") == 0 ||
5783 strcmp(entp->d_name, "..") == 0)
5784 continue;
5786 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5788 if (lu == NULL &&
5789 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5790 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5791 lu = s_strdup(tmpsign);
5794 if (ufs == NULL &&
5795 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5796 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5797 ufs = s_strdup(tmpsign);
5800 if (zfs == NULL &&
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,
5808 zfs ? zfs : "NULL",
5809 ufs ? ufs : "NULL",
5810 lu ? lu : "NULL"));
5812 if (dirp) {
5813 (void) closedir(dirp);
5814 dirp = NULL;
5817 if (strcmp(fstype, "ufs") == 0 && zfs) {
5818 bam_error(_("found mismatched boot signature %s for "
5819 "filesystem type: %s.\n"), zfs, "ufs");
5820 free(zfs);
5821 zfs = NULL;
5822 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5823 bam_error(_("found mismatched boot signature %s for "
5824 "filesystem type: %s.\n"), ufs, "zfs");
5825 free(ufs);
5826 ufs = NULL;
5829 assert(dirp == NULL);
5831 /* For now, we let Live Upgrade take care of its signature itself */
5832 if (lu) {
5833 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5834 free(lu);
5835 lu = NULL;
5838 return (zfs ? zfs : ufs);
5841 static char *
5842 find_backup_common(char *mntpt, char *fstype)
5844 FILE *bfp = NULL;
5845 char tmpsign[MAXNAMELEN + 1];
5846 char backup[PATH_MAX];
5847 char *ufs;
5848 char *zfs;
5849 char *lu;
5850 int error;
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");
5861 if (bfp == NULL) {
5862 error = errno;
5863 if (bam_verbose) {
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)));
5869 return (NULL);
5872 ufs = zfs = lu = NULL;
5874 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5876 if (lu == NULL &&
5877 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5878 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5879 lu = s_strdup(tmpsign);
5882 if (ufs == NULL &&
5883 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5884 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5885 ufs = s_strdup(tmpsign);
5888 if (zfs == NULL &&
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,
5896 zfs ? zfs : "NULL",
5897 ufs ? ufs : "NULL",
5898 lu ? lu : "NULL"));
5900 if (bfp) {
5901 (void) fclose(bfp);
5902 bfp = NULL;
5905 if (strcmp(fstype, "ufs") == 0 && zfs) {
5906 bam_error(_("found mismatched boot signature %s for "
5907 "filesystem type: %s.\n"), zfs, "ufs");
5908 free(zfs);
5909 zfs = NULL;
5910 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5911 bam_error(_("found mismatched boot signature %s for "
5912 "filesystem type: %s.\n"), ufs, "zfs");
5913 free(ufs);
5914 ufs = NULL;
5917 assert(bfp == NULL);
5919 /* For now, we let Live Upgrade take care of its signature itself */
5920 if (lu) {
5921 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5922 free(lu);
5923 lu = NULL;
5926 return (zfs ? zfs : ufs);
5929 static char *
5930 find_ufs_existing(char *osroot)
5932 char *sign;
5933 const char *fcn = "find_ufs_existing()";
5935 sign = find_primary_common(osroot, "ufs");
5936 if (sign == NULL) {
5937 sign = find_backup_common(osroot, "ufs");
5938 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
5939 sign ? sign : "NULL"));
5940 } else {
5941 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
5944 return (sign);
5947 char *
5948 get_mountpoint(char *special, char *fstype)
5950 FILE *mntfp;
5951 struct mnttab mp = {0};
5952 struct mnttab mpref = {0};
5953 int error;
5954 int ret;
5955 const char *fcn = "get_mountpoint()";
5957 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
5959 mntfp = fopen(MNTTAB, "r");
5960 error = errno;
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));
5965 return (NULL);
5968 mpref.mnt_special = special;
5969 mpref.mnt_fstype = fstype;
5971 ret = getmntany(mntfp, &mp, &mpref);
5972 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5973 if (ret != 0) {
5974 (void) fclose(mntfp);
5975 BAM_DPRINTF(("%s: no mount-point for special=%s and "
5976 "fstype=%s\n", fcn, special, fstype));
5977 return (NULL);
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
5994 static char *
5995 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5997 char cmd[PATH_MAX];
5998 char tmpmnt[PATH_MAX];
5999 filelist_t flist = {0};
6000 char *is_mounted;
6001 struct stat sb;
6002 int ret;
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",
6011 pool);
6013 ret = exec_cmd(cmd, &flist);
6014 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
6015 if (ret != 0) {
6016 bam_error(_("failed to determine mount status of ZFS "
6017 "pool %s\n"), pool);
6018 return (NULL);
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);
6025 return (NULL);
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",
6036 fcn, pool));
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);
6050 if (ret == -1) {
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);
6055 if (ret == -1) {
6056 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
6057 strerror(errno));
6058 return (NULL);
6060 } else {
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",
6067 pool, tmpmnt);
6069 ret = exec_cmd(cmd, NULL);
6070 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
6071 if (ret != 0) {
6072 bam_error(_("mount of ZFS pool %s failed\n"), pool);
6073 (void) rmdir(tmpmnt);
6074 return (NULL);
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
6088 char *
6089 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
6091 char cmd[PATH_MAX];
6092 filelist_t flist = {0};
6093 char *is_mounted;
6094 char *mntpt;
6095 char *zmntpt;
6096 int ret;
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",
6108 pool);
6109 ret = exec_cmd(cmd, &flist);
6110 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
6111 if (ret != 0) {
6112 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6113 pool);
6114 return (NULL);
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",
6132 pool);
6134 ret = exec_cmd(cmd, &flist);
6135 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
6136 if (ret != 0) {
6137 bam_error(_("failed to determine mount status of ZFS "
6138 "pool %s\n"), pool);
6139 return (NULL);
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);
6146 return (NULL);
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",
6156 fcn, pool));
6157 goto mounted;
6160 filelist_free(&flist);
6161 BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
6162 fcn, pool));
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);
6169 if (ret != 0) {
6170 bam_error(_("mount of ZFS pool %s failed\n"), pool);
6171 return (NULL);
6173 *mnted = ZFS_MOUNTED;
6174 BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
6175 /*FALLTHRU*/
6176 mounted:
6178 * Now get the mountpoint
6180 (void) snprintf(cmd, sizeof (cmd),
6181 "/sbin/zfs get -Ho value mountpoint %s",
6182 pool);
6184 ret = exec_cmd(cmd, &flist);
6185 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
6186 if (ret != 0) {
6187 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6188 pool);
6189 goto error;
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);
6195 goto error;
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"),
6202 pool, mntpt);
6203 goto error;
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));
6212 return (zmntpt);
6214 error:
6215 filelist_free(&flist);
6216 (void) umount_top_dataset(pool, *mnted, NULL);
6217 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6218 return (NULL);
6222 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
6224 char cmd[PATH_MAX];
6225 int ret;
6226 const char *fcn = "umount_top_dataset()";
6228 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
6229 switch (mnted) {
6230 case LEGACY_ALREADY:
6231 case ZFS_ALREADY:
6232 /* nothing to do */
6233 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6234 "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
6235 free(mntpt);
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);
6242 if (ret != 0) {
6243 bam_error(_("umount of %s failed\n"), pool);
6244 free(mntpt);
6245 return (BAM_ERROR);
6247 if (mntpt)
6248 (void) rmdir(mntpt);
6249 free(mntpt);
6250 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6251 "successfully unmounted\n", fcn, pool));
6252 return (BAM_SUCCESS);
6253 case ZFS_MOUNTED:
6254 free(mntpt);
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);
6259 if (ret != 0) {
6260 bam_error(_("umount of %s failed\n"), pool);
6261 return (BAM_ERROR);
6263 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6264 "successfully unmounted\n", fcn, pool));
6265 return (BAM_SUCCESS);
6266 default:
6267 bam_error(_("Internal error: bad saved mount state for "
6268 "pool %s\n"), pool);
6269 return (BAM_ERROR);
6271 /*NOTREACHED*/
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
6279 static char *
6280 get_pool(char *osdev)
6282 char cmd[PATH_MAX];
6283 char buf[PATH_MAX];
6284 filelist_t flist = {0};
6285 char *pool;
6286 char *cp;
6287 char *slash;
6288 int ret;
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"));
6294 return (NULL);
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, '/');
6302 if (slash)
6303 *slash = '\0';
6304 pool = s_strdup(buf);
6305 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6306 return (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"),
6310 osdev);
6311 return (NULL);
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:'",
6320 osdev);
6322 ret = exec_cmd(cmd, &flist);
6323 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
6324 if (ret != 0) {
6325 bam_error(_("fstyp -a on device %s failed\n"), osdev);
6326 return (NULL);
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);
6333 return (NULL);
6336 (void) strtok(flist.head->line, "'");
6337 cp = strtok(NULL, "'");
6338 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6339 if (cp == NULL) {
6340 bam_error(_("bad fstyp -a output for device %s\n"), osdev);
6341 filelist_free(&flist);
6342 return (NULL);
6345 pool = s_strdup(cp);
6347 filelist_free(&flist);
6349 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6351 return (pool);
6354 static char *
6355 find_zfs_existing(char *osdev)
6357 char *pool;
6358 zfs_mnted_t mnted;
6359 char *mntpt;
6360 char *sign;
6361 const char *fcn = "find_zfs_existing()";
6363 pool = get_pool(osdev);
6364 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6365 if (pool == NULL) {
6366 bam_error(_("failed to get pool for device: %s\n"), osdev);
6367 return (NULL);
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"),
6374 pool);
6375 free(pool);
6376 return (NULL);
6379 sign = find_primary_common(mntpt, "zfs");
6380 if (sign == NULL) {
6381 sign = find_backup_common(mntpt, "zfs");
6382 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
6383 sign ? sign : "NULL"));
6384 } else {
6385 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
6388 (void) umount_top_dataset(pool, mnted, mntpt);
6390 free(pool);
6392 return (sign);
6395 static char *
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));
6407 } else {
6408 bam_error(_("boot signature not supported for fstype: %s\n"),
6409 fstype);
6410 return (NULL);
6414 #define MH_HASH_SZ 16
6416 typedef enum {
6417 MH_ERROR = -1,
6418 MH_NOMATCH,
6419 MH_MATCH
6420 } mh_search_t;
6422 typedef struct mcache {
6423 char *mc_special;
6424 char *mc_mntpt;
6425 char *mc_fstype;
6426 struct mcache *mc_next;
6427 } mcache_t;
6429 typedef struct mhash {
6430 mcache_t *mh_hash[MH_HASH_SZ];
6431 } mhash_t;
6433 static int
6434 mhash_fcn(char *key)
6436 int i;
6437 uint64_t sum = 0;
6439 for (i = 0; key[i] != '\0'; i++) {
6440 sum += (uchar_t)key[i];
6443 sum %= MH_HASH_SZ;
6445 assert(sum < MH_HASH_SZ);
6447 return (sum);
6450 static mhash_t *
6451 cache_mnttab(void)
6453 FILE *mfp;
6454 struct extmnttab mnt;
6455 mcache_t *mcp;
6456 mhash_t *mhp;
6457 char *ctds;
6458 int idx;
6459 int error;
6460 char *special_dup;
6461 const char *fcn = "cache_mnttab()";
6463 mfp = fopen(MNTTAB, "r");
6464 error = errno;
6465 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6466 if (mfp == NULL) {
6467 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
6468 strerror(error));
6469 return (NULL);
6472 mhp = s_calloc(1, sizeof (mhash_t));
6474 resetmnttab(mfp);
6476 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6477 /* only cache ufs */
6478 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6479 continue;
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;
6494 free(special_dup);
6497 (void) fclose(mfp);
6499 return (mhp);
6502 static void
6503 free_mnttab(mhash_t *mhp)
6505 mcache_t *mcp;
6506 int i;
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);
6514 free(mcp);
6518 for (i = 0; i < MH_HASH_SZ; i++) {
6519 assert(mhp->mh_hash[i] == NULL);
6521 free(mhp);
6524 static mh_search_t
6525 search_hash(mhash_t *mhp, char *special, char **mntpt)
6527 int idx;
6528 mcache_t *mcp;
6529 const char *fcn = "search_hash()";
6531 assert(mntpt);
6533 *mntpt = NULL;
6535 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6536 if (strchr(special, '/')) {
6537 bam_error(_("invalid key for mnttab hash: %s\n"), special);
6538 return (MH_ERROR);
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)
6545 break;
6548 if (mcp == NULL) {
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));
6556 return (MH_MATCH);
6559 static int
6560 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6562 char *sign;
6563 char *signline;
6564 char signbuf[MAXNAMELEN];
6565 int len;
6566 int error;
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");
6571 if (sign == NULL) {
6572 /* No existing signature, nothing to add to list */
6573 BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6574 fcn, mntpt));
6575 return (0);
6578 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6579 signline = signbuf;
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);
6585 free(sign);
6586 /* ignore invalid signatures */
6587 return (0);
6590 len = fputs(signline, tfp);
6591 error = errno;
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));
6596 free(sign);
6597 return (-1);
6600 free(sign);
6602 BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6603 fcn, mntpt));
6604 return (0);
6608 * slice is a basename not a full pathname
6610 static int
6611 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6613 int ret;
6614 char cmd[PATH_MAX];
6615 char path[PATH_MAX];
6616 struct stat sbuf;
6617 char *mntpt;
6618 filelist_t flist = {0};
6619 char *fstype;
6620 char blkslice[PATH_MAX];
6621 const char *fcn = "process_slice_common()";
6624 ret = search_hash(mhp, slice, &mntpt);
6625 switch (ret) {
6626 case MH_MATCH:
6627 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6628 return (-1);
6629 else
6630 return (0);
6631 case MH_NOMATCH:
6632 break;
6633 case MH_ERROR:
6634 default:
6635 return (-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));
6641 return (0);
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",
6647 slice);
6649 if (exec_cmd(cmd, &flist) != 0) {
6650 if (bam_verbose)
6651 bam_print(_("fstyp failed for slice: %s\n"), slice);
6652 return (0);
6655 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6656 if (bam_verbose)
6657 bam_print(_("bad output from fstyp for slice: %s\n"),
6658 slice);
6659 filelist_free(&flist);
6660 return (0);
6663 fstype = strtok(flist.head->line, " \t\n");
6664 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6665 if (bam_verbose)
6666 bam_print(_("%s is not a ufs slice: %s\n"),
6667 slice, fstype);
6668 filelist_free(&flist);
6669 return (0);
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",
6681 slice);
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) {
6688 if (bam_verbose)
6689 bam_print(_("mount of %s (fstype %s) failed\n"),
6690 blkslice, "ufs");
6691 return (0);
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",
6698 tmpmnt);
6700 if (exec_cmd(cmd, NULL) != 0) {
6701 bam_print(_("umount of %s failed\n"), slice);
6702 return (0);
6705 return (ret);
6708 static int
6709 process_vtoc_slices(
6710 char *s0,
6711 struct vtoc *vtoc,
6712 FILE *tfp,
6713 mhash_t *mhp,
6714 char *tmpmnt)
6716 int idx;
6717 char slice[PATH_MAX];
6718 size_t len;
6719 char *cp;
6720 const char *fcn = "process_vtoc_slices()";
6722 len = strlen(s0);
6724 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6726 s0[len - 1] = '\0';
6728 (void) strlcpy(slice, s0, sizeof (slice));
6730 s0[len - 1] = '0';
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",
6740 fcn, slice));
6741 continue;
6744 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6745 switch (vtoc->v_part[idx].p_tag) {
6746 case V_SWAP:
6747 case V_USR:
6748 case V_BACKUP:
6749 case V_VAR:
6750 case V_HOME:
6751 case V_ALTSCTR:
6752 BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6753 "skipping: %s\n", fcn, slice));
6754 continue;
6755 default:
6756 BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6757 fcn, slice));
6758 break;
6761 /* skip unmountable and readonly slices */
6762 switch (vtoc->v_part[idx].p_flag) {
6763 case V_UNMNT:
6764 case V_RONLY:
6765 BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6766 fcn, slice));
6767 continue;
6768 default:
6769 BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6770 fcn, slice));
6771 break;
6774 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6775 return (-1);
6779 return (0);
6782 static int
6783 process_efi_slices(
6784 char *s0,
6785 struct dk_gpt *efi,
6786 FILE *tfp,
6787 mhash_t *mhp,
6788 char *tmpmnt)
6790 int idx;
6791 char slice[PATH_MAX];
6792 size_t len;
6793 char *cp;
6794 const char *fcn = "process_efi_slices()";
6796 len = strlen(s0);
6798 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6800 s0[len - 1] = '\0';
6802 (void) strlcpy(slice, s0, sizeof (slice));
6804 s0[len - 1] = '0';
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",
6814 fcn, slice));
6815 continue;
6818 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6819 switch (efi->efi_parts[idx].p_tag) {
6820 case V_SWAP:
6821 case V_USR:
6822 case V_BACKUP:
6823 case V_VAR:
6824 case V_HOME:
6825 case V_ALTSCTR:
6826 BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6827 fcn, slice));
6828 continue;
6829 default:
6830 BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6831 fcn, slice));
6832 break;
6835 /* skip unmountable and readonly slices */
6836 switch (efi->efi_parts[idx].p_flag) {
6837 case V_UNMNT:
6838 case V_RONLY:
6839 BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6840 fcn, slice));
6841 continue;
6842 default:
6843 BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6844 fcn, slice));
6845 break;
6848 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6849 return (-1);
6853 return (0);
6857 * s0 is a basename not a full path
6859 static int
6860 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6862 struct vtoc vtoc;
6863 struct dk_gpt *efi;
6864 char s0path[PATH_MAX];
6865 struct stat sbuf;
6866 int e_flag;
6867 int v_flag;
6868 int retval;
6869 int err;
6870 int fd;
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));
6877 return (0);
6880 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6881 if (fd == -1) {
6882 bam_error(_("failed to open file: %s: %s\n"), s0path,
6883 strerror(errno));
6884 return (0);
6887 e_flag = v_flag = 0;
6888 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6889 switch (retval) {
6890 case VT_EIO:
6891 BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6892 fcn, s0path));
6893 break;
6894 case VT_EINVAL:
6895 BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6896 fcn, s0path));
6897 break;
6898 case VT_ERROR:
6899 BAM_DPRINTF(("%s: VTOC: unknown error while "
6900 "reading: %s\n", fcn, s0path));
6901 break;
6902 case VT_ENOTSUP:
6903 e_flag = 1;
6904 BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6905 fcn, s0path));
6906 break;
6907 case 0:
6908 v_flag = 1;
6909 BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6910 fcn, s0path));
6911 break;
6912 default:
6913 BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6914 "code: %s\n", fcn, s0path));
6915 break;
6919 if (e_flag) {
6920 e_flag = 0;
6921 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6922 switch (retval) {
6923 case VT_EIO:
6924 BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6925 fcn, s0path));
6926 break;
6927 case VT_EINVAL:
6928 BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn, s0path));
6929 break;
6930 case VT_ERROR:
6931 BAM_DPRINTF(("%s: EFI: unknown error while "
6932 "reading: %s\n", fcn, s0path));
6933 break;
6934 case VT_ENOTSUP:
6935 BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6936 fcn, s0path));
6937 break;
6938 case 0:
6939 e_flag = 1;
6940 BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6941 fcn, s0path));
6942 break;
6943 default:
6944 BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6945 fcn, s0path));
6946 break;
6950 (void) close(fd);
6952 if (v_flag) {
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);
6958 } else {
6959 BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6960 fcn, s0path));
6961 return (0);
6964 return (retval);
6968 * Find and create a list of all existing UFS boot signatures
6970 static int
6971 FindAllUfsSignatures(void)
6973 mhash_t *mnttab_hash;
6974 DIR *dirp = NULL;
6975 struct dirent *dp;
6976 char tmpmnt[PATH_MAX];
6977 char cmd[PATH_MAX];
6978 struct stat sb;
6979 int fd;
6980 FILE *tfp;
6981 size_t len;
6982 int ret;
6983 int error;
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);
6989 return (0);
6992 fd = open(UFS_SIGNATURE_LIST".tmp",
6993 O_RDWR|O_CREAT|O_TRUNC, 0644);
6994 error = errno;
6995 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6996 if (fd == -1) {
6997 bam_error(_("failed to open file: %s: %s\n"),
6998 UFS_SIGNATURE_LIST".tmp", strerror(error));
6999 return (-1);
7002 ret = close(fd);
7003 error = errno;
7004 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
7005 if (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");
7009 return (-1);
7012 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7013 error = errno;
7014 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
7015 if (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");
7019 return (-1);
7022 mnttab_hash = cache_mnttab();
7023 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
7024 if (mnttab_hash == NULL) {
7025 (void) fclose(tfp);
7026 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7027 bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn);
7028 return (-1);
7031 (void) snprintf(tmpmnt, sizeof (tmpmnt),
7032 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
7033 (void) unlink(tmpmnt);
7035 ret = mkdirp(tmpmnt, DIR_PERMS);
7036 error = errno;
7037 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
7038 if (ret == -1) {
7039 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
7040 strerror(error));
7041 free_mnttab(mnttab_hash);
7042 (void) fclose(tfp);
7043 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7044 return (-1);
7047 dirp = opendir("/dev/rdsk");
7048 error = errno;
7049 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
7050 if (dirp == NULL) {
7051 bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
7052 strerror(error));
7053 goto fail;
7056 while ((dp = readdir(dirp)) != NULL) {
7057 if (strcmp(dp->d_name, ".") == 0 ||
7058 strcmp(dp->d_name, "..") == 0)
7059 continue;
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",
7068 fcn, dp->d_name));
7069 continue;
7072 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
7073 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
7074 if (ret == -1)
7075 goto fail;
7078 (void) closedir(dirp);
7079 free_mnttab(mnttab_hash);
7080 (void) rmdir(tmpmnt);
7082 ret = fclose(tfp);
7083 error = errno;
7084 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
7085 if (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");
7089 return (-1);
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);
7099 if (ret != 0) {
7100 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7101 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7102 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7103 return (-1);
7106 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7108 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7109 error = errno;
7110 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
7111 if (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");
7115 return (-1);
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));
7124 return (0);
7126 fail:
7127 if (dirp)
7128 (void) closedir(dirp);
7129 free_mnttab(mnttab_hash);
7130 (void) rmdir(tmpmnt);
7131 (void) fclose(tfp);
7132 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7133 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7134 return (-1);
7137 static char *
7138 create_ufs_sign(void)
7140 struct stat sb;
7141 int signnum = -1;
7142 char tmpsign[MAXNAMELEN + 1];
7143 char *numstr;
7144 int i;
7145 FILE *tfp;
7146 int ret;
7147 int error;
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);
7154 if (ret == -1) {
7155 bam_error(_("search for UFS boot signatures failed\n"));
7156 return (NULL);
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);
7166 return (NULL);
7169 if (sb.st_size == 0) {
7170 bam_print(_(" - no existing UFS boot signatures\n"));
7171 i = 0;
7172 goto found;
7175 /* The signature list was sorted when it was created */
7176 tfp = fopen(UFS_SIGNATURE_LIST, "r");
7177 error = errno;
7178 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
7179 if (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);
7183 return (NULL);
7186 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
7188 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
7189 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7190 (void) fclose(tfp);
7191 (void) unlink(UFS_SIGNATURE_LIST);
7192 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7193 return (NULL);
7195 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
7197 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
7198 (void) fclose(tfp);
7199 (void) unlink(UFS_SIGNATURE_LIST);
7200 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7201 return (NULL);
7204 signnum = atoi(numstr);
7205 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
7206 if (signnum < 0) {
7207 (void) fclose(tfp);
7208 (void) unlink(UFS_SIGNATURE_LIST);
7209 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7210 return (NULL);
7213 if (i != signnum) {
7214 BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7215 fcn, i));
7216 break;
7220 (void) fclose(tfp);
7222 found:
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);
7228 if (ret == -1) {
7229 (void) unlink(UFS_SIGNATURE_LIST);
7230 bam_error(_("failed to add sign %s to signlist.\n"), tmpsign);
7231 return (NULL);
7234 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7236 return (s_strdup(tmpsign));
7239 static char *
7240 get_fstype(char *osroot)
7242 FILE *mntfp;
7243 struct mnttab mp = {0};
7244 struct mnttab mpref = {0};
7245 int error;
7246 int ret;
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"));
7252 return (NULL);
7255 mntfp = fopen(MNTTAB, "r");
7256 error = errno;
7257 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
7258 if (mntfp == NULL) {
7259 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7260 strerror(error));
7261 return (NULL);
7264 if (*osroot == '\0')
7265 mpref.mnt_mountp = "/";
7266 else
7267 mpref.mnt_mountp = osroot;
7269 ret = getmntany(mntfp, &mp, &mpref);
7270 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
7271 if (ret != 0) {
7272 bam_error(_("failed to find OS mountpoint %s in %s\n"),
7273 osroot, MNTTAB);
7274 (void) fclose(mntfp);
7275 return (NULL);
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);
7282 return (NULL);
7285 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7287 return (s_strdup(mp.mnt_fstype));
7290 static char *
7291 create_zfs_sign(char *osdev)
7293 char tmpsign[PATH_MAX];
7294 char *pool;
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);
7304 if (pool == NULL) {
7305 bam_error(_("failed to get pool name from %s\n"), osdev);
7306 return (NULL);
7309 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
7311 BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn, tmpsign));
7313 free(pool);
7315 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7317 return (s_strdup(tmpsign));
7320 static char *
7321 create_new_sign(char *osdev, char *fstype)
7323 char *sign;
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();
7334 } else {
7335 bam_error(_("boot signature not supported for fstype: %s\n"),
7336 fstype);
7337 sign = NULL;
7340 BAM_DPRINTF(("%s: created new sign: %s\n", fcn,
7341 sign ? sign : "<NULL>"));
7342 return (sign);
7345 static int
7346 set_backup_common(char *mntpt, char *sign)
7348 FILE *bfp;
7349 char backup[PATH_MAX];
7350 char tmpsign[PATH_MAX];
7351 int error;
7352 char *bdir;
7353 char *backup_dup;
7354 struct stat sb;
7355 int ret;
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");
7363 if (bfp != NULL) {
7364 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
7365 if (strcmp(tmpsign, sign) == 0) {
7366 BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7367 fcn, sign));
7368 (void) fclose(bfp);
7369 return (0);
7372 (void) fclose(bfp);
7373 BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7374 fcn, sign));
7375 } else {
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);
7387 assert(bdir);
7389 ret = stat(bdir, &sb);
7390 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
7391 if (ret == -1) {
7392 BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7393 fcn, bdir));
7394 ret = mkdirp(bdir, DIR_PERMS);
7395 error = errno;
7396 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
7397 if (ret == -1) {
7398 bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7399 GRUBSIGN_BACKUP, strerror(error));
7400 free(backup_dup);
7401 return (-1);
7404 free(backup_dup);
7407 * Open the backup in append mode to add the correct
7408 * signature;
7410 bfp = fopen(backup, "a");
7411 error = errno;
7412 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
7413 if (bfp == NULL) {
7414 bam_error(_("error opening boot signature backup "
7415 "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7416 return (-1);
7419 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
7421 ret = fputs(tmpsign, bfp);
7422 error = errno;
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));
7427 (void) fclose(bfp);
7428 return (-1);
7431 (void) fclose(bfp);
7433 if (bam_verbose)
7434 bam_print(_("updated boot signature backup file %s\n"),
7435 GRUBSIGN_BACKUP);
7437 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7439 return (0);
7442 static int
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));
7451 static int
7452 set_backup_zfs(char *osdev, char *sign)
7454 char *pool;
7455 char *mntpt;
7456 zfs_mnted_t mnted;
7457 int ret;
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);
7464 if (pool == NULL) {
7465 bam_error(_("failed to get pool name from %s\n"), osdev);
7466 return (-1);
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);
7473 free(pool);
7474 return (-1);
7477 ret = set_backup_common(mntpt, sign);
7479 (void) umount_top_dataset(pool, mnted, mntpt);
7481 free(pool);
7483 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7484 if (ret == 0) {
7485 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7486 } else {
7487 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7490 return (ret);
7493 static int
7494 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7496 const char *fcn = "set_backup()";
7497 int ret;
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);
7507 } else {
7508 bam_error(_("boot signature not supported for fstype: %s\n"),
7509 fstype);
7510 ret = -1;
7513 if (ret == 0) {
7514 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7515 } else {
7516 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7519 return (ret);
7522 static int
7523 set_primary_common(char *mntpt, char *sign)
7525 char signfile[PATH_MAX];
7526 char signdir[PATH_MAX];
7527 struct stat sb;
7528 int fd;
7529 int error;
7530 int ret;
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) {
7537 if (bam_verbose)
7538 bam_print(_("primary sign %s exists\n"), sign);
7539 return (0);
7540 } else {
7541 BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7542 fcn, signfile));
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",
7550 fcn, signdir));
7551 ret = mkdirp(signdir, DIR_PERMS);
7552 error = errno;
7553 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7554 if (ret == -1) {
7555 bam_error(_("error creating boot signature "
7556 "directory %s: %s\n"), signdir, strerror(errno));
7557 return (-1);
7561 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7562 error = errno;
7563 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7564 if (fd == -1) {
7565 bam_error(_("error creating primary boot signature %s: %s\n"),
7566 signfile, strerror(error));
7567 return (-1);
7570 ret = fsync(fd);
7571 error = errno;
7572 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7573 if (ret != 0) {
7574 bam_error(_("error syncing primary boot signature %s: %s\n"),
7575 signfile, strerror(error));
7578 (void) close(fd);
7580 if (bam_verbose)
7581 bam_print(_("created primary GRUB boot signature: %s\n"),
7582 signfile);
7584 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7586 return (0);
7589 static int
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));
7598 static int
7599 set_primary_zfs(char *osdev, char *sign)
7601 char *pool;
7602 char *mntpt;
7603 zfs_mnted_t mnted;
7604 int ret;
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);
7611 if (pool == NULL) {
7612 bam_error(_("failed to get pool name from %s\n"), osdev);
7613 return (-1);
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);
7619 if (ret == 0) {
7620 bam_error(_("pool name %s not present in signature %s\n"),
7621 pool, sign);
7622 free(pool);
7623 return (-1);
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);
7630 free(pool);
7631 return (-1);
7634 ret = set_primary_common(mntpt, sign);
7636 (void) umount_top_dataset(pool, mnted, mntpt);
7638 free(pool);
7640 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7641 if (ret == 0) {
7642 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7643 } else {
7644 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7647 return (ret);
7650 static int
7651 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7653 const char *fcn = "set_primary()";
7654 int ret;
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);
7663 } else {
7664 bam_error(_("boot signature not supported for fstype: %s\n"),
7665 fstype);
7666 ret = -1;
7669 if (ret == 0) {
7670 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7671 } else {
7672 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7675 return (ret);
7678 static int
7679 ufs_add_to_sign_list(char *sign)
7681 FILE *tfp;
7682 char signline[MAXNAMELEN];
7683 char cmd[PATH_MAX];
7684 int ret;
7685 int error;
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);
7693 return (-1);
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");
7702 error = errno;
7703 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7704 if (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);
7708 return (0);
7711 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7712 error = errno;
7713 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7714 if (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");
7718 return (0);
7721 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7723 ret = fputs(signline, tfp);
7724 error = errno;
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));
7729 (void) fclose(tfp);
7730 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7731 return (0);
7734 ret = fclose(tfp);
7735 error = errno;
7736 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7737 if (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");
7741 return (0);
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);
7751 if (ret != 0) {
7752 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7753 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7754 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7755 return (0);
7758 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7760 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7761 error = errno;
7762 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7763 if (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");
7767 return (0);
7770 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7772 return (0);
7775 static int
7776 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7778 int ret;
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);
7786 if (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);
7790 return (-1);
7793 ret = set_primary(osroot, osdev, sign, fstype);
7794 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7796 if (ret == 0) {
7797 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7798 } else {
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);
7804 return (ret);
7807 char *
7808 get_grubsign(char *osroot, char *osdev)
7810 char *grubsign; /* (<sign>,#,#) */
7811 char *slice;
7812 int fdiskpart;
7813 char *sign;
7814 char *fstype;
7815 int ret;
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);
7823 return (NULL);
7826 sign = find_existing_sign(osroot, osdev, fstype);
7827 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7828 if (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);
7833 if (sign == NULL) {
7834 bam_error(_("failed to create GRUB boot signature for "
7835 "device: %s\n"), osdev);
7836 free(fstype);
7837 return (NULL);
7841 ret = set_signature(osroot, osdev, sign, fstype);
7842 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7843 if (ret == -1) {
7844 bam_error(_("failed to write GRUB boot signature for "
7845 "device: %s\n"), osdev);
7846 free(sign);
7847 free(fstype);
7848 (void) unlink(UFS_SIGNATURE_LIST);
7849 return (NULL);
7852 free(fstype);
7854 if (bam_verbose)
7855 bam_print(_("found or created GRUB signature %s for %s\n"),
7856 sign, osdev);
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"),
7862 osdev);
7863 free(sign);
7864 return (NULL);
7867 slice = strrchr(osdev, 's');
7869 if (fdiskpart == PARTNO_EFI) {
7870 fdiskpart = atoi(&slice[1]);
7871 slice = NULL;
7874 grubsign = s_calloc(1, MAXNAMELEN + 10);
7875 if (slice) {
7876 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7877 sign, fdiskpart, slice[1] + 'a' - '0');
7878 } else
7879 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7880 sign, fdiskpart);
7882 free(sign);
7884 BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn, grubsign));
7886 return (grubsign);
7889 static char *
7890 get_title(char *rootdir)
7892 static char title[80];
7893 char *cp = NULL;
7894 char release[PATH_MAX];
7895 FILE *fp;
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");
7902 if (fp == NULL) {
7903 bam_error(_("failed to open file: %s: %s\n"), release,
7904 strerror(errno));
7905 cp = NULL;
7906 goto out;
7909 /* grab first line of /etc/release */
7910 cp = s_fgets(title, sizeof (title), fp);
7911 if (cp) {
7912 while (isspace(*cp)) /* remove leading spaces */
7913 cp++;
7916 (void) fclose(fp);
7918 out:
7919 cp = cp ? cp : "Oracle Solaris";
7921 BAM_DPRINTF(("%s: got title: %s\n", fcn, cp));
7923 return (cp);
7926 char *
7927 get_special(char *mountp)
7929 FILE *mntfp;
7930 struct mnttab mp = {0};
7931 struct mnttab mpref = {0};
7932 int error;
7933 int ret;
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"));
7939 return (NULL);
7942 mntfp = fopen(MNTTAB, "r");
7943 error = errno;
7944 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7945 if (mntfp == NULL) {
7946 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7947 strerror(error));
7948 return (NULL);
7951 if (*mountp == '\0')
7952 mpref.mnt_mountp = "/";
7953 else
7954 mpref.mnt_mountp = mountp;
7956 ret = getmntany(mntfp, &mp, &mpref);
7957 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7958 if (ret != 0) {
7959 (void) fclose(mntfp);
7960 BAM_DPRINTF(("%s: Cannot get special file: mount-point %s "
7961 "not in mnttab\n", fcn, mountp));
7962 return (NULL);
7964 (void) fclose(mntfp);
7966 BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
7968 return (s_strdup(mp.mnt_special));
7971 static void
7972 free_physarray(char **physarray, int n)
7974 int i;
7975 const char *fcn = "free_physarray()";
7977 assert(physarray);
7978 assert(n);
7980 BAM_DPRINTF(("%s: entering args: %d\n", fcn, n));
7982 for (i = 0; i < n; i++) {
7983 free(physarray[i]);
7985 free(physarray);
7987 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7990 static int
7991 zfs_get_physical(char *special, char ***physarray, int *n)
7993 char sdup[PATH_MAX];
7994 char cmd[PATH_MAX];
7995 char dsk[PATH_MAX];
7996 char *pool;
7997 filelist_t flist = {0};
7998 line_t *lp;
7999 line_t *startlp;
8000 char *comp1;
8001 int i;
8002 int ret;
8003 const char *fcn = "zfs_get_physical()";
8005 assert(special);
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"),
8012 special);
8013 return (-1);
8016 (void) strlcpy(sdup, special, sizeof (sdup));
8018 pool = strtok(sdup, "/");
8019 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
8020 if (pool == NULL) {
8021 bam_error(_("cannot derive ZFS pool from special: %s\n"),
8022 special);
8023 return (-1);
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);
8030 if (ret != 0) {
8031 bam_error(_("cannot get zpool status for pool: %s\n"), pool);
8032 return (-1);
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);
8039 return (-1);
8042 for (lp = flist.head; lp; lp = lp->next) {
8043 BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
8044 fcn, lp->line));
8045 comp1 = strtok(lp->line, " \t");
8046 if (comp1 == NULL) {
8047 free(lp->line);
8048 lp->line = NULL;
8049 } else {
8050 comp1 = s_strdup(comp1);
8051 free(lp->line);
8052 lp->line = comp1;
8056 for (lp = flist.head; lp; lp = lp->next) {
8057 if (lp->line == NULL)
8058 continue;
8059 if (strcmp(lp->line, pool) == 0) {
8060 BAM_DPRINTF(("%s: found pool name: %s in zpool "
8061 "status\n", fcn, pool));
8062 break;
8066 if (lp == NULL) {
8067 bam_error(_("no pool name %s in zpool status\n"), pool);
8068 filelist_free(&flist);
8069 return (-1);
8072 startlp = lp->next;
8073 for (i = 0, lp = startlp; lp; lp = lp->next) {
8074 if (lp->line == NULL)
8075 continue;
8076 if (strcmp(lp->line, "mirror") == 0)
8077 continue;
8078 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
8079 break;
8080 i++;
8081 BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
8082 fcn, i));
8085 if (i == 0) {
8086 bam_error(_("no physical device in zpool status for pool=%s\n"),
8087 pool);
8088 filelist_free(&flist);
8089 return (-1);
8092 *n = i;
8093 *physarray = s_calloc(*n, sizeof (char *));
8094 for (i = 0, lp = startlp; lp; lp = lp->next) {
8095 if (lp->line == NULL)
8096 continue;
8097 if (strcmp(lp->line, "mirror") == 0)
8098 continue;
8099 if (strcmp(lp->line, "errors:") == 0)
8100 break;
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",
8105 lp->line);
8106 } else {
8107 (void) strlcpy(dsk, lp->line, sizeof (dsk));
8109 BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
8110 fcn, dsk, pool));
8111 (*physarray)[i++] = s_strdup(dsk);
8114 assert(i == *n);
8116 filelist_free(&flist);
8118 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8119 return (0);
8122 static int
8123 get_physical(char *menu_root, char ***physarray, int *n)
8125 char *special;
8126 int ret;
8127 const char *fcn = "get_physical()";
8129 assert(menu_root);
8130 assert(physarray);
8131 assert(n);
8133 *physarray = NULL;
8134 *n = 0;
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"),
8143 menu_root);
8144 return (-1);
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;
8155 *n = 1;
8156 return (0);
8159 if (is_zfs(menu_root)) {
8160 ret = zfs_get_physical(special, physarray, n);
8161 } else {
8162 bam_error(_("cannot derive physical device for %s (%s), "
8163 "unsupported filesystem\n"), menu_root, special);
8164 ret = -1;
8167 free(special);
8169 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
8170 if (ret == -1) {
8171 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8172 } else {
8173 int i;
8174 assert (*n > 0);
8175 for (i = 0; i < *n; i++) {
8176 BAM_DPRINTF(("%s: returning physical=%s\n",
8177 fcn, (*physarray)[i]));
8181 return (ret);
8184 static int
8185 is_bootdisk(char *osroot, char *physical)
8187 int ret;
8188 char *grubroot;
8189 char *bootp;
8190 const char *fcn = "is_bootdisk()";
8192 assert(osroot);
8193 assert(physical);
8195 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, physical));
8197 bootp = strstr(physical, "p0:boot");
8198 if (bootp)
8199 *bootp = '\0';
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
8206 * "physical"
8208 grubroot = get_grubroot(osroot, physical, NULL);
8210 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
8211 if (grubroot == NULL) {
8212 if (bam_verbose)
8213 bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8214 "disk: %s\n"), physical);
8215 return (0);
8217 ret = grubroot[3] == '0';
8218 free(grubroot);
8220 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
8222 return (ret);
8226 * Check if menu is on the boot device
8227 * Return 0 (false) on error
8229 static int
8230 menu_on_bootdisk(char *osroot, char *menu_root)
8232 char **physarray;
8233 int ret;
8234 int n;
8235 int i;
8236 int on_bootdisk;
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);
8243 if (ret != 0) {
8244 bam_error(_("cannot get physical device special file for menu "
8245 "root: %s\n"), menu_root);
8246 return (0);
8249 assert(physarray);
8250 assert(n > 0);
8252 on_bootdisk = 0;
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])) {
8262 on_bootdisk = 1;
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);
8272 if (on_bootdisk) {
8273 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8274 } else {
8275 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8278 return (on_bootdisk);
8281 void
8282 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8284 const char *fcn = "bam_add_line()";
8286 assert(mp);
8287 assert(entry);
8288 assert(prev);
8289 assert(lp);
8291 lp->next = prev->next;
8292 if (prev->next) {
8293 BAM_DPRINTF(("%s: previous next exists\n", fcn));
8294 prev->next->prev = lp;
8295 } else {
8296 BAM_DPRINTF(("%s: previous next does not exist\n", fcn));
8298 prev->next = lp;
8299 lp->prev = prev;
8301 if (entry->end == prev) {
8302 BAM_DPRINTF(("%s: last line in entry\n", fcn));
8303 entry->end = lp;
8305 if (mp->end == prev) {
8306 assert(lp->next == NULL);
8307 mp->end = lp;
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.
8320 static entry_t *
8321 find_boot_entry(
8322 menu_t *mp,
8323 char *title,
8324 char *kernel,
8325 char *findroot,
8326 char *root,
8327 char *module,
8328 int root_opt,
8329 int *entry_num)
8331 int i;
8332 line_t *lp;
8333 entry_t *ent;
8334 const char *fcn = "find_boot_entry()";
8336 if (entry_num)
8337 *entry_num = BAM_ERROR;
8339 /* find matching entry */
8340 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8341 lp = ent->start;
8343 /* first line of entry must be bootadm comment */
8344 lp = ent->start;
8345 if (lp->flags != BAM_COMMENT ||
8346 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8347 continue;
8350 /* advance to title line */
8351 lp = lp->next;
8352 if (title) {
8353 if (lp->flags == BAM_TITLE && lp->arg &&
8354 strcmp(lp->arg, title) == 0) {
8355 BAM_DPRINTF(("%s: matched title: %s\n",
8356 fcn, title));
8357 break;
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 */
8365 if (lp == NULL) {
8366 continue;
8367 } else if (lp->cmd != NULL &&
8368 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8369 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8370 findroot = NULL);
8371 if (findroot == NULL) {
8372 BAM_DPRINTF(("%s: no match line has findroot, "
8373 "we don't: %s\n", fcn, lp->arg));
8374 continue;
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));
8380 continue;
8382 BAM_DPRINTF(("%s: matched findroot: %s\n",
8383 fcn, findroot));
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);
8388 if (root == NULL) {
8389 BAM_DPRINTF(("%s: no match, line has root, we "
8390 "don't: %s\n", fcn, lp->arg));
8391 continue;
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));
8397 continue;
8399 BAM_DPRINTF(("%s: matched root: %s\n", fcn, root));
8400 lp = lp->next; /* advance to kernel line */
8401 } else {
8402 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8403 root_opt = 0);
8404 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8405 root_opt = 1);
8406 /* no root command, see if root is optional */
8407 if (root_opt == 0) {
8408 BAM_DPRINTF(("%s: root NOT optional\n", fcn));
8409 continue;
8411 BAM_DPRINTF(("%s: root IS optional\n", fcn));
8414 if (lp == NULL || lp->next == NULL) {
8415 continue;
8418 if (kernel &&
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)
8423 continue;
8425 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8428 BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn,
8429 kernel, lp->arg));
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))) {
8441 /* match found */
8442 BAM_DPRINTF(("%s: module match: %s, %s\n", fcn,
8443 module, lp->arg));
8444 break;
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;
8451 break;
8456 if (ent && entry_num) {
8457 *entry_num = i;
8460 if (ent) {
8461 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8462 } else {
8463 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_ERROR));
8465 return (ent);
8468 static int
8469 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8470 char *kernel, char *mod_kernel, char *module, int root_opt)
8472 int i;
8473 int change_kernel = 0;
8474 entry_t *ent;
8475 line_t *lp;
8476 line_t *tlp;
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,
8482 root_opt, &i);
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);
8491 if (ent != NULL) {
8492 BAM_DPRINTF(("%s: upgrading entry from dboot to "
8493 "multiboot: root = %s\n", fcn, root));
8494 change_kernel = 1;
8496 } else if (ent) {
8497 BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8498 fcn, findroot));
8501 if (ent == NULL) {
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 */
8509 lp = ent->start;
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);
8513 free(lp->arg);
8514 free(lp->line);
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);
8527 } else {
8528 if (lp->cmd != NULL)
8529 free(lp->cmd);
8531 free(lp->sep);
8532 free(lp->arg);
8533 free(lp->line);
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));
8544 /* kernel line */
8545 lp = lp->next;
8547 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8548 char *params = NULL;
8550 params = strstr(lp->line, "-s");
8551 if (params != NULL)
8552 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8553 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8554 kernel, params+2);
8555 else
8556 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8557 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8558 kernel);
8560 if (lp->cmd != NULL)
8561 free(lp->cmd);
8563 free(lp->arg);
8564 free(lp->line);
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],
8581 kernel);
8582 free(lp->cmd);
8583 free(lp->arg);
8584 free(lp->line);
8585 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8586 lp->arg = s_strdup(kernel);
8587 lp->line = s_strdup(linebuf);
8588 lp = lp->next;
8589 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8590 fcn, kernel));
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],
8596 module);
8597 free(lp->cmd);
8598 free(lp->arg);
8599 free(lp->line);
8600 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8601 lp->arg = s_strdup(module);
8602 lp->line = s_strdup(linebuf);
8603 lp = lp->next;
8604 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8605 fcn, module));
8609 /* module line */
8610 lp = lp->next;
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],
8617 module);
8618 free(lp->cmd);
8619 free(lp->arg);
8620 free(lp->line);
8621 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8622 lp->arg = s_strdup(module);
8623 lp->line = s_strdup(linebuf);
8624 lp = lp->next;
8625 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8626 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8627 fcn, module));
8631 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8632 return (i);
8636 root_optional(char *osroot, char *menu_root)
8638 char *ospecial;
8639 char *mspecial;
8640 char *slash;
8641 int root_opt;
8642 int ret1;
8643 int ret2;
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);
8661 goto out;
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"),
8668 osroot);
8669 return (0);
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"),
8677 menu_root);
8678 free(ospecial);
8679 return (0);
8681 BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8682 fcn, mspecial, menu_root));
8684 slash = strchr(ospecial, '/');
8685 if (slash)
8686 *slash = '\0';
8687 BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8688 fcn, ospecial, osroot));
8690 root_opt = (strcmp(ospecial, mspecial) == 0);
8692 free(ospecial);
8693 free(mspecial);
8695 out:
8696 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8697 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8698 if (root_opt) {
8699 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8700 } else {
8701 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8704 return (root_opt);
8707 /*ARGSUSED*/
8708 static error_t
8709 update_entry(menu_t *mp, char *menu_root, char *osdev)
8711 int entry;
8712 char *grubsign;
8713 char *grubroot;
8714 char *title;
8715 char osroot[PATH_MAX];
8716 char *failsafe_kernel = NULL;
8717 struct stat sbuf;
8718 char failsafe[256];
8719 char failsafe_64[256];
8720 int ret;
8721 const char *fcn = "update_entry()";
8723 assert(mp);
8724 assert(menu_root);
8725 assert(osdev);
8726 assert(bam_root);
8728 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, menu_root, osdev,
8729 bam_root));
8731 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8733 title = get_title(osroot);
8734 assert(title);
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"),
8740 osroot, osdev);
8741 return (BAM_ERROR);
8745 * It is not a fatal error if get_grubroot() fails
8746 * We no longer rely on biosdev to populate the
8747 * menu
8749 grubroot = get_grubroot(osroot, osdev, menu_root);
8750 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8751 if (grubroot) {
8752 BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8753 "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8754 } else {
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));
8778 } else {
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",
8784 fcn, grubsign));
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;
8812 } else {
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));
8827 free(grubroot);
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);
8833 free(grubsign);
8834 return (BAM_ERROR);
8836 free(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));
8845 return (BAM_WRITE);
8848 static void
8849 save_default_entry(menu_t *mp, const char *which)
8851 int lineNum;
8852 int entryNum;
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()";
8858 if (mp->start) {
8859 lineNum = mp->end->lineNum;
8860 entryNum = mp->end->entryNum;
8861 } else {
8862 lineNum = LINE_INIT;
8863 entryNum = ENTRY_INIT;
8866 if (lp)
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));
8876 static void
8877 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8879 int entry;
8880 char *str;
8881 const char *fcn = "restore_default_entry()";
8883 if (lp == NULL) {
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);
8898 line_free(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"
8913 /*ARGSUSED*/
8914 static error_t
8915 update_temp(menu_t *mp, char *dummy, char *opt)
8917 int entry;
8918 char *osdev;
8919 char *fstype;
8920 char *sign;
8921 char *opt_ptr;
8922 char *path;
8923 char kernbuf[BUFSIZ];
8924 char args_buf[BUFSIZ];
8925 char signbuf[PATH_MAX];
8926 int ret;
8927 const char *fcn = "update_temp()";
8929 assert(mp);
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);
8942 return (BAM_ERROR);
8945 /* If no option, delete exiting reboot menu entry */
8946 if (opt == NULL) {
8947 entry_t *ent;
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));
8960 return (BAM_WRITE);
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));
8972 return (ret);
8973 } else {
8974 bam_error(_("failed to set GRUB menu default to %d\n"),
8975 entryNum);
8976 return (BAM_ERROR);
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"));
8989 return (BAM_ERROR);
8992 osdev = get_special("/");
8993 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8994 if (osdev == NULL) {
8995 free(fstype);
8996 bam_error(_("failed to find device special file for \"/\". "
8997 "Reboot with \narguments failed.\n"));
8998 return (BAM_ERROR);
9001 sign = find_existing_sign("/", osdev, fstype);
9002 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
9003 if (sign == NULL) {
9004 free(fstype);
9005 free(osdev);
9006 bam_error(_("failed to find boot signature. Reboot with "
9007 "arguments failed.\n"));
9008 return (BAM_ERROR);
9011 free(osdev);
9012 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9013 free(sign);
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) {
9025 free(fstype);
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"));
9029 return (BAM_ERROR);
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) {
9038 free(fstype);
9039 bam_error(_("reboot with arguments: error querying "
9040 "current boot-file settings\n"));
9041 return (BAM_ERROR);
9043 if (kernbuf[0] == '\0')
9044 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
9045 sizeof (kernbuf));
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));
9063 * If someone runs:
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
9075 * first case.
9077 if (strchr(opt, ' ') == NULL) {
9078 ret = get_kernel(mp, ARGS_CMD, args_buf,
9079 sizeof (args_buf));
9080 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
9081 if (ret != BAM_SUCCESS) {
9082 free(fstype);
9083 bam_error(_("reboot with arguments: error "
9084 "querying current boot-args settings\n"));
9085 return (BAM_ERROR);
9088 if (args_buf[0] != '\0') {
9089 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
9090 (void) strlcat(kernbuf, args_buf,
9091 sizeof (kernbuf));
9094 BAM_DPRINTF(("%s: reboot with args, abspath specified: "
9095 "kern=%s\n", fcn, kernbuf));
9096 } else {
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) {
9105 *opt_ptr = '\0';
9108 path = expand_path(opt);
9109 if (path != NULL) {
9110 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
9111 free(path);
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 */
9119 *opt_ptr = ' ';
9120 (void) strlcat(kernbuf, opt_ptr,
9121 sizeof (kernbuf));
9122 } else {
9123 ret = get_kernel(mp, ARGS_CMD, args_buf,
9124 sizeof (args_buf));
9125 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
9126 ret = BAM_ERROR);
9127 if (ret != BAM_SUCCESS) {
9128 free(fstype);
9129 bam_error(_("reboot with arguments: "
9130 "error querying current boot-args "
9131 "settings\n"));
9132 return (BAM_ERROR);
9135 if (args_buf[0] != '\0') {
9136 (void) strlcat(kernbuf, " ",
9137 sizeof (kernbuf));
9138 (void) strlcat(kernbuf,
9139 args_buf, sizeof (kernbuf));
9142 BAM_DPRINTF(("%s: resolved partial path: %s\n",
9143 fcn, kernbuf));
9144 } else {
9145 free(fstype);
9146 bam_error(_("unable to expand %s to a full file"
9147 " path.\n"), opt);
9148 bam_print_stderr(_("Rebooting with default kernel "
9149 "and options.\n"));
9150 return (BAM_ERROR);
9153 free(fstype);
9154 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
9155 NULL, NULL, NULL);
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"));
9160 return (BAM_ERROR);
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));
9171 return (BAM_WRITE);
9174 error_t
9175 set_global(menu_t *mp, char *globalcmd, int val)
9177 line_t *lp;
9178 line_t *found;
9179 line_t *last;
9180 char *cp;
9181 char *str;
9182 char prefix[BAM_MAXLINE];
9183 size_t len;
9184 const char *fcn = "set_global()";
9186 assert(mp);
9187 assert(globalcmd);
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);
9196 return (BAM_ERROR);
9200 found = last = NULL;
9201 for (lp = mp->start; lp; lp = lp->next) {
9202 if (lp->flags != BAM_GLOBAL)
9203 continue;
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);
9210 continue;
9212 if (strcmp(globalcmd, lp->cmd) != 0)
9213 continue;
9215 BAM_DPRINTF(("%s: found matching global command: %s\n",
9216 fcn, globalcmd));
9218 if (found) {
9219 bam_error(_("duplicate command %s at line %d of "
9220 "%sboot/grub/menu.lst\n"), globalcmd,
9221 lp->lineNum, bam_root);
9223 found = lp;
9226 if (found == NULL) {
9227 lp = s_calloc(1, sizeof (line_t));
9228 if (last == NULL) {
9229 lp->next = mp->start;
9230 mp->start = lp;
9231 mp->end = (mp->end) ? mp->end : lp;
9232 } else {
9233 lp->next = last->next;
9234 last->next = lp;
9235 if (lp->next == NULL)
9236 mp->end = lp;
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));
9246 return (BAM_WRITE);
9250 * We are changing an existing entry. Retain any prefix whitespace,
9251 * but overwrite everything else. This preserves tabs added for
9252 * readability.
9254 str = found->line;
9255 cp = prefix;
9256 while (*str == ' ' || *str == '\t')
9257 *(cp++) = *(str++);
9258 *cp = '\0'; /* Terminate prefix */
9259 len = strlen(prefix) + strlen(globalcmd);
9260 len += strlen(menu_cmds[SEP_CMD]) + 10;
9262 free(found->line);
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.
9277 static char *
9278 expand_path(const char *partial_path)
9280 int new_path_len;
9281 char *new_path;
9282 char new_path2[PATH_MAX];
9283 struct stat sb;
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",
9291 partial_path);
9292 if (stat(new_path, &sb) == 0) {
9293 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9294 return (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));
9301 return (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",
9309 partial_path);
9310 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9311 partial_path);
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",
9320 partial_path);
9322 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9323 return (new_path);
9326 free(new_path);
9327 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9328 return (NULL);
9332 * The kernel cmd and arg have been changed, so
9333 * check whether the archive line needs to change.
9335 static void
9336 set_archive_line(entry_t *entryp, line_t *kernelp)
9338 line_t *lp = entryp->start;
9339 char *new_archive;
9340 menu_cmd_t m_cmd;
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) {
9346 break;
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));
9353 return;
9356 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9357 if (lp == NULL) {
9358 BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9359 fcn, entryp->entryNum));
9360 return;
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;
9368 m_cmd = MODULE_CMD;
9369 } else {
9370 new_archive = DIRECT_BOOT_ARCHIVE_32;
9371 m_cmd = MODULE_CMD;
9374 if (strcmp(lp->arg, new_archive) == 0) {
9375 BAM_DPRINTF(("%s: no change for line: %s\n", fcn, lp->arg));
9376 return;
9379 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9380 free(lp->cmd);
9381 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9384 free(lp->arg);
9385 lp->arg = s_strdup(new_archive);
9386 update_line(lp);
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.
9401 static error_t
9402 get_set_kernel(
9403 menu_t *mp,
9404 menu_cmd_t optnum,
9405 char *path,
9406 char *buf,
9407 size_t bufsize)
9409 int entryNum;
9410 int rv = BAM_SUCCESS;
9411 int free_new_path = 0;
9412 entry_t *entryp;
9413 line_t *ptr;
9414 line_t *kernelp;
9415 char *new_arg;
9416 char *old_args;
9417 char *space;
9418 char *new_path;
9419 char old_space;
9420 size_t old_kernel_len = 0;
9421 size_t new_str_len;
9422 char *fstype;
9423 char *osdev;
9424 char *sign;
9425 char signbuf[PATH_MAX];
9426 int ret;
9427 const char *fcn = "get_set_kernel()";
9429 assert(bufsize > 0);
9431 ptr = kernelp = NULL;
9432 new_arg = old_args = space = NULL;
9433 new_path = NULL;
9434 buf[0] = '\0';
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");
9442 return (BAM_ERROR);
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
9448 * return.
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)
9454 break;
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"));
9460 return (BAM_ERROR);
9464 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9465 0, &entryNum);
9467 if (entryp != NULL) {
9468 for (ptr = entryp->start; ptr && ptr != entryp->end;
9469 ptr = ptr->next) {
9470 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9471 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9472 kernelp = ptr;
9473 break;
9476 if (kernelp == NULL) {
9477 bam_error(_("no kernel line found in entry %d\n"),
9478 entryNum);
9479 return (BAM_ERROR);
9482 old_kernel_len = strcspn(kernelp->arg, " \t");
9483 space = old_args = kernelp->arg + old_kernel_len;
9484 while ((*old_args == ' ') || (*old_args == '\t'))
9485 old_args++;
9488 if (path == NULL) {
9489 if (entryp == NULL) {
9490 BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9491 fcn));
9492 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9493 return (BAM_SUCCESS);
9495 assert(kernelp);
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",
9500 fcn, buf));
9502 } else {
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.
9508 old_space = *space;
9509 *space = '\0';
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",
9513 fcn, buf));
9515 *space = old_space;
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",
9530 fcn));
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))) {
9545 kernelp = NULL;
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;
9550 rv = BAM_WRITE;
9551 BAM_DPRINTF(("%s: resetting to default\n", fcn));
9552 goto done;
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);
9566 free(kernelp->arg);
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));
9576 } else {
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",
9584 kernelp->arg);
9585 free(kernelp->arg);
9586 kernelp->arg = new_arg;
9587 BAM_DPRINTF(("%s: reset args to default, but retained "
9588 "old kernel: %s\n", fcn, kernelp->arg));
9590 rv = BAM_WRITE;
9591 goto done;
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 "
9601 "path.\n"), path);
9602 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9603 return (BAM_ERROR);
9605 free_new_path = 1;
9606 } else {
9607 new_path = path;
9608 free_new_path = 0;
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"));
9624 rv = BAM_ERROR;
9625 goto done;
9628 osdev = get_special("/");
9629 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9630 if (osdev == NULL) {
9631 free(fstype);
9632 bam_error(_("cannot determine device special file for "
9633 "\"/\".\nCannot generate GRUB menu entry with "
9634 "EEPROM arguments.\n"));
9635 rv = BAM_ERROR;
9636 goto done;
9639 sign = find_existing_sign("/", osdev, fstype);
9640 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9641 if (sign == NULL) {
9642 free(fstype);
9643 free(osdev);
9644 bam_error(_("cannot determine boot signature for "
9645 "\"/\".\nCannot generate GRUB menu entry with "
9646 "EEPROM arguments.\n"));
9647 rv = BAM_ERROR;
9648 goto done;
9651 free(osdev);
9652 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9653 free(sign);
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,
9666 new_arg));
9667 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9668 signbuf, new_arg, NULL, NULL, NULL);
9669 free(new_arg);
9670 } else {
9671 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9672 new_path));
9673 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9674 signbuf, new_path, NULL, NULL, NULL);
9676 } else {
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);
9683 } else {
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);
9693 free(new_arg);
9695 free(fstype);
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"),
9700 BOOTENV_RC_TITLE);
9701 rv = BAM_ERROR;
9702 goto done;
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"),
9709 entryNum);
9711 rv = BAM_WRITE;
9712 goto done;
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,
9722 old_args);
9723 free(kernelp->arg);
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));
9733 } else {
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);
9739 free(kernelp->arg);
9740 kernelp->arg = new_arg;
9741 BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9742 "args: %s\n", fcn, kernelp->arg));
9744 rv = BAM_WRITE;
9746 done:
9747 if ((rv == BAM_WRITE) && kernelp)
9748 update_line(kernelp);
9749 if (free_new_path)
9750 free(new_path);
9751 if (rv == BAM_WRITE) {
9752 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9753 } else {
9754 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9756 return (rv);
9759 static error_t
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));
9767 static error_t
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));
9777 /*ARGSUSED*/
9778 static error_t
9779 set_option(menu_t *mp, char *dummy, char *opt)
9781 int optnum;
9782 int optval;
9783 char *val;
9784 char buf[BUFSIZ] = "";
9785 error_t rv;
9786 const char *fcn = "set_option()";
9788 assert(mp);
9789 assert(opt);
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, '=');
9796 if (val != NULL) {
9797 *val = '\0';
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) {
9807 optnum = ARGS_CMD;
9808 } else {
9809 bam_error(_("invalid option: %s\n"), opt);
9810 return (BAM_ERROR);
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);
9819 return (BAM_ERROR);
9820 } else if (val != NULL) {
9821 *val = '=';
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"));
9828 if (val)
9829 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9830 else
9831 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9832 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9833 (void) printf("%s\n", buf);
9834 } else {
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));
9843 } else {
9844 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9847 return (rv);
9851 * The quiet argument suppresses messages. This is used
9852 * when invoked in the context of other commands (e.g. list_entry)
9854 static error_t
9855 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9857 line_t *lp;
9858 char *arg;
9859 int done, ret = BAM_SUCCESS;
9861 assert(mp);
9862 assert(menu_path);
9863 assert(globalcmd);
9865 if (mp->start == NULL) {
9866 if (!quiet)
9867 bam_error(_("menu file not found: %s\n"), menu_path);
9868 return (BAM_ERROR);
9871 done = 0;
9872 for (lp = mp->start; lp; lp = lp->next) {
9873 if (lp->flags != BAM_GLOBAL)
9874 continue;
9876 if (lp->cmd == NULL) {
9877 if (!quiet)
9878 bam_error(_("no command at line %d\n"),
9879 lp->lineNum);
9880 continue;
9883 if (strcmp(globalcmd, lp->cmd) != 0)
9884 continue;
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);
9891 ret = BAM_ERROR;
9894 arg = lp->arg ? lp->arg : "";
9895 bam_print(_("%s %s\n"), globalcmd, arg);
9896 done = 1;
9899 if (!done && bam_verbose)
9900 bam_print(_("no %s entry found\n"), globalcmd);
9902 return (ret);
9905 static error_t
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));
9914 void
9915 line_free(line_t *lp)
9917 if (lp == NULL)
9918 return;
9920 if (lp->cmd != NULL)
9921 free(lp->cmd);
9922 if (lp->sep)
9923 free(lp->sep);
9924 if (lp->arg)
9925 free(lp->arg);
9926 if (lp->line)
9927 free(lp->line);
9928 free(lp);
9931 static void
9932 linelist_free(line_t *start)
9934 line_t *lp;
9936 while (start) {
9937 lp = start;
9938 start = start->next;
9939 line_free(lp);
9943 static void
9944 filelist_free(filelist_t *flistp)
9946 linelist_free(flistp->head);
9947 flistp->head = NULL;
9948 flistp->tail = NULL;
9951 static void
9952 menu_free(menu_t *mp)
9954 entry_t *ent, *tmp;
9955 assert(mp);
9957 if (mp->start)
9958 linelist_free(mp->start);
9959 ent = mp->entries;
9960 while (ent) {
9961 tmp = ent;
9962 ent = tmp->next;
9963 free(tmp);
9966 free(mp);
9970 * Utility routines
9975 * Returns 0 on success
9976 * Any other value indicates an error
9978 static int
9979 exec_cmd(char *cmdline, filelist_t *flistp)
9981 char buf[BUFSIZ];
9982 int ret;
9983 FILE *ptr;
9984 sigset_t set;
9985 void (*disp)(int);
9988 * For security
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);
9994 return (-1);
9996 (void) putenv("IFS= \t");
9999 * We may have been exec'ed with SIGCHLD blocked
10000 * unblock it here
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));
10006 return (-1);
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"),
10015 strerror(errno));
10016 return (-1);
10018 if (disp == SIG_HOLD) {
10019 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
10020 cmdline);
10021 return (-1);
10024 ptr = popen(cmdline, "r");
10025 if (ptr == NULL) {
10026 bam_error(_("popen failed: %s: %s\n"), cmdline,
10027 strerror(errno));
10028 return (-1);
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
10041 * pclose().
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);
10051 } else {
10052 append_to_flist(flistp, buf);
10056 ret = pclose(ptr);
10057 if (ret == -1) {
10058 bam_error(_("pclose failed: %s: %s\n"), cmdline,
10059 strerror(errno));
10060 return (-1);
10063 if (WIFEXITED(ret)) {
10064 return (WEXITSTATUS(ret));
10065 } else {
10066 bam_error(_("command terminated abnormally: %s: %d\n"),
10067 cmdline, ret);
10068 return (-1);
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.
10077 static long
10078 s_strtol(char *str)
10080 long l;
10081 char *res = NULL;
10083 if (str == NULL) {
10084 return (-1);
10087 errno = 0;
10088 l = strtol(str, &res, 10);
10089 if (errno || *res != '\0') {
10090 return (-1);
10093 return (l);
10097 * Wrapper around fputs, that adds a newline (since fputs doesn't)
10099 static int
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
10111 char *
10112 s_fgets(char *buf, int buflen, FILE *fp)
10114 int n;
10116 buf = fgets(buf, buflen, fp);
10117 if (buf) {
10118 n = strlen(buf);
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];
10125 return (buf);
10128 void *
10129 s_calloc(size_t nelem, size_t sz)
10131 void *ptr;
10133 ptr = calloc(nelem, sz);
10134 if (ptr == NULL) {
10135 bam_error(_("could not allocate memory: size = %u\n"),
10136 nelem*sz);
10137 bam_exit(1);
10139 return (ptr);
10142 void *
10143 s_realloc(void *ptr, size_t sz)
10145 ptr = realloc(ptr, sz);
10146 if (ptr == NULL) {
10147 bam_error(_("could not allocate memory: size = %u\n"), sz);
10148 bam_exit(1);
10150 return (ptr);
10153 char *
10154 s_strdup(char *str)
10156 char *ptr;
10158 if (str == NULL)
10159 return (NULL);
10161 ptr = strdup(str);
10162 if (ptr == NULL) {
10163 bam_error(_("could not allocate memory: size = %u\n"),
10164 strlen(str) + 1);
10165 bam_exit(1);
10167 return (ptr);
10171 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
10172 * Returns 0 otherwise
10174 static int
10175 is_amd64(void)
10177 static int amd64 = -1;
10178 char isabuf[257]; /* from sysinfo(2) manpage */
10180 if (amd64 != -1)
10181 return (amd64);
10183 if (bam_alt_platform) {
10184 if (strcmp(bam_platform, "i86pc") == 0) {
10185 amd64 = 1; /* diskless server */
10187 } else {
10188 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
10189 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
10190 amd64 = 1;
10191 } else if (strstr(isabuf, "i386") == NULL) {
10192 amd64 = 1; /* diskless server */
10195 if (amd64 == -1)
10196 amd64 = 0;
10198 return (amd64);
10201 static char *
10202 get_machine(void)
10204 static int cached = -1;
10205 static char mbuf[257]; /* from sysinfo(2) manpage */
10207 if (cached == 0)
10208 return (mbuf);
10210 if (bam_alt_platform) {
10211 return (bam_platform);
10212 } else {
10213 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
10214 cached = 1;
10217 if (cached == -1) {
10218 mbuf[0] = '\0';
10219 cached = 0;
10222 return (mbuf);
10226 is_sparc(void)
10228 static int issparc = -1;
10229 char mbuf[257]; /* from sysinfo(2) manpage */
10231 if (issparc != -1)
10232 return (issparc);
10234 if (bam_alt_platform) {
10235 if (strncmp(bam_platform, "sun4", 4) == 0) {
10236 issparc = 1;
10238 } else {
10239 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
10240 strcmp(mbuf, "sparc") == 0) {
10241 issparc = 1;
10244 if (issparc == -1)
10245 issparc = 0;
10247 return (issparc);
10250 static void
10251 append_to_flist(filelist_t *flistp, char *s)
10253 line_t *lp;
10255 lp = s_calloc(1, sizeof (line_t));
10256 lp->line = s_strdup(s);
10257 if (flistp->head == NULL)
10258 flistp->head = lp;
10259 else
10260 flistp->tail->next = lp;
10261 flistp->tail = lp;