7242 usr/src/cmd/boot build needs cleanup
[unleashed.git] / usr / src / cmd / boot / bootadm / bootadm.c
blobba62ba3d7a096d5109bcc975f97eb18442d0a3c0
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
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright 2012 Milan Jurik. All rights reserved.
27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28 * Copyright (c) 2015 by Delphix. All rights reserved.
32 * bootadm(1M) is a new utility for managing bootability of
33 * Solaris *Newboot* environments. It has two primary tasks:
34 * - Allow end users to manage bootability of Newboot Solaris instances
35 * - Provide services to other subsystems in Solaris (primarily Install)
38 /* Headers */
39 #include <stdio.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <alloca.h>
47 #include <stdarg.h>
48 #include <limits.h>
49 #include <signal.h>
50 #include <sys/wait.h>
51 #include <sys/mnttab.h>
52 #include <sys/mntent.h>
53 #include <sys/statvfs.h>
54 #include <libnvpair.h>
55 #include <ftw.h>
56 #include <fcntl.h>
57 #include <strings.h>
58 #include <utime.h>
59 #include <sys/systeminfo.h>
60 #include <sys/dktp/fdisk.h>
61 #include <sys/param.h>
62 #include <dirent.h>
63 #include <ctype.h>
64 #include <libgen.h>
65 #include <sys/sysmacros.h>
66 #include <sys/elf.h>
67 #include <libscf.h>
68 #include <zlib.h>
69 #include <sys/lockfs.h>
70 #include <sys/filio.h>
71 #include <libbe.h>
72 #ifdef i386
73 #include <libfdisk.h>
74 #endif
76 #if !defined(_OBP)
77 #include <sys/ucode.h>
78 #endif
80 #include <pwd.h>
81 #include <grp.h>
82 #include <device_info.h>
83 #include <sys/vtoc.h>
84 #include <sys/efi_partition.h>
85 #include <regex.h>
86 #include <locale.h>
87 #include <sys/mkdev.h>
89 #include "message.h"
90 #include "bootadm.h"
92 #ifndef TEXT_DOMAIN
93 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
94 #endif /* TEXT_DOMAIN */
96 /* Type definitions */
98 /* Primary subcmds */
99 typedef enum {
100 BAM_MENU = 3,
101 BAM_ARCHIVE,
102 BAM_INSTALL
103 } subcmd_t;
105 typedef enum {
106 OPT_ABSENT = 0, /* No option */
107 OPT_REQ, /* option required */
108 OPT_OPTIONAL /* option may or may not be present */
109 } option_t;
111 typedef struct {
112 char *subcmd;
113 option_t option;
114 error_t (*handler)();
115 int unpriv; /* is this an unprivileged command */
116 } subcmd_defn_t;
118 #define LINE_INIT 0 /* lineNum initial value */
119 #define ENTRY_INIT -1 /* entryNum initial value */
120 #define ALL_ENTRIES -2 /* selects all boot entries */
122 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
123 #define PARTNO_EFI -2 /* EFI partition table found */
125 #define GRUB_DIR "/boot/grub"
126 #define GRUB_STAGE2 GRUB_DIR "/stage2"
127 #define GRUB_MENU "/boot/grub/menu.lst"
128 #define MENU_TMP "/boot/grub/menu.lst.tmp"
129 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
130 #define RAMDISK_SPECIAL "/dev/ramdisk/"
131 #define STUBBOOT "/stubboot"
132 #define MULTIBOOT "/platform/i86pc/multiboot"
133 #define GRUBSIGN_DIR "/boot/grub/bootsign"
134 #define GRUBSIGN_BACKUP "/etc/bootsign"
135 #define GRUBSIGN_UFS_PREFIX "rootfs"
136 #define GRUBSIGN_ZFS_PREFIX "pool_"
137 #define GRUBSIGN_LU_PREFIX "BE_"
138 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
139 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
141 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
143 /* lock related */
144 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
145 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
147 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
148 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
149 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
150 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
152 #define GRUB_slice "/etc/lu/GRUB_slice"
153 #define GRUB_root "/etc/lu/GRUB_root"
154 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
155 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
156 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
157 #define LULIB "/usr/lib/lu/lulib"
158 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
159 #define CKSUM "/usr/bin/cksum"
160 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
161 #define BOOTADM "/sbin/bootadm"
163 #define INSTALLGRUB "/sbin/installgrub"
164 #define STAGE1 "/boot/grub/stage1"
165 #define STAGE2 "/boot/grub/stage2"
167 typedef enum zfs_mnted {
168 ZFS_MNT_ERROR = -1,
169 LEGACY_MOUNTED = 1,
170 LEGACY_ALREADY,
171 ZFS_MOUNTED,
172 ZFS_ALREADY
173 } zfs_mnted_t;
176 * Default file attributes
178 #define DEFAULT_DEV_MODE 0644 /* default permissions */
179 #define DEFAULT_DEV_UID 0 /* user root */
180 #define DEFAULT_DEV_GID 3 /* group sys */
183 * Menu related
184 * menu_cmd_t and menu_cmds must be kept in sync
186 char *menu_cmds[] = {
187 "default", /* DEFAULT_CMD */
188 "timeout", /* TIMEOUT_CMD */
189 "title", /* TITLE_CMD */
190 "root", /* ROOT_CMD */
191 "kernel", /* KERNEL_CMD */
192 "kernel$", /* KERNEL_DOLLAR_CMD */
193 "module", /* MODULE_CMD */
194 "module$", /* MODULE_DOLLAR_CMD */
195 " ", /* SEP_CMD */
196 "#", /* COMMENT_CMD */
197 "chainloader", /* CHAINLOADER_CMD */
198 "args", /* ARGS_CMD */
199 "findroot", /* FINDROOT_CMD */
200 "bootfs", /* BOOTFS_CMD */
201 NULL
204 #define OPT_ENTRY_NUM "entry"
207 * exec_cmd related
209 typedef struct {
210 line_t *head;
211 line_t *tail;
212 } filelist_t;
214 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
215 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
217 #define FILE_STAT "boot/solaris/filestat.ramdisk"
218 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
219 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
220 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
222 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
224 /* Globals */
225 int bam_verbose;
226 int bam_force;
227 int bam_debug;
228 static char *prog;
229 static subcmd_t bam_cmd;
230 static char *bam_root;
231 static int bam_rootlen;
232 static int bam_root_readonly;
233 static 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 static char rootbuf[PATH_MAX] = "/";
248 static int bam_update_all;
249 static int bam_alt_platform;
250 static char *bam_platform;
251 static char *bam_home_env = NULL;
253 /* function prototypes */
254 static void parse_args_internal(int, char *[]);
255 static void parse_args(int, char *argv[]);
256 static error_t bam_menu(char *, char *, int, char *[]);
257 static error_t bam_install(char *, char *);
258 static error_t bam_archive(char *, char *);
260 static void bam_lock(void);
261 static void bam_unlock(void);
263 static int exec_cmd(char *, filelist_t *);
264 static error_t read_globals(menu_t *, char *, char *, int);
265 static int menu_on_bootdisk(char *os_root, char *menu_root);
266 static menu_t *menu_read(char *);
267 static error_t menu_write(char *, menu_t *);
268 static void linelist_free(line_t *);
269 static void menu_free(menu_t *);
270 static void filelist_free(filelist_t *);
271 static error_t list2file(char *, char *, char *, line_t *);
272 static error_t list_entry(menu_t *, char *, char *);
273 static error_t list_setting(menu_t *, char *, char *);
274 static error_t delete_all_entries(menu_t *, char *, char *);
275 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
276 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
278 static error_t install_bootloader(void);
279 static error_t update_archive(char *, char *);
280 static error_t list_archive(char *, char *);
281 static error_t update_all(char *, char *);
282 static error_t read_list(char *, filelist_t *);
283 static error_t set_option(menu_t *, char *, char *);
284 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
285 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
286 static char *expand_path(const char *);
288 static long s_strtol(char *);
289 static int s_fputs(char *, FILE *);
291 static int is_zfs(char *root);
292 static int is_ufs(char *root);
293 static int is_pcfs(char *root);
294 static int is_amd64(void);
295 static char *get_machine(void);
296 static void append_to_flist(filelist_t *, char *);
297 static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted);
298 static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt);
299 static int ufs_add_to_sign_list(char *sign);
300 static error_t synchronize_BE_menu(void);
302 #if !defined(_OBP)
303 static void ucode_install();
304 #endif
306 /* Menu related sub commands */
307 static subcmd_defn_t menu_subcmds[] = {
308 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
309 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
310 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
311 "update_entry", OPT_REQ, update_entry, 0, /* menu */
312 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
313 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
314 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
315 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
316 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
317 NULL, 0, NULL, 0 /* must be last */
320 /* Archive related sub commands */
321 static subcmd_defn_t arch_subcmds[] = {
322 "update", OPT_ABSENT, update_archive, 0, /* PUB */
323 "update_all", OPT_ABSENT, update_all, 0, /* PVT */
324 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */
325 NULL, 0, NULL, 0 /* must be last */
328 /* Install related sub commands */
329 static subcmd_defn_t inst_subcmds[] = {
330 "install_bootloader", OPT_ABSENT, install_bootloader, 0, /* PUB */
331 NULL, 0, NULL, 0 /* must be last */
334 enum dircache_copy_opt {
335 FILE32 = 0,
336 FILE64,
337 CACHEDIR_NUM
341 * Directory specific flags:
342 * NEED_UPDATE : the specified archive needs to be updated
343 * NO_MULTI : don't extend the specified archive, but recreate it
345 #define NEED_UPDATE 0x00000001
346 #define NO_MULTI 0x00000002
348 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
349 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
350 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
352 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
353 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
354 #define get_count(id) (walk_arg.dirinfo[id].count)
355 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
356 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
359 * dirinfo_t (specific cache directory information):
360 * cdir_path: path to the archive cache directory
361 * update_path: path to the update directory (contains the files that will be
362 * used to extend the archive)
363 * has_dir: the specified cache directory is active
364 * count: the number of files to update
365 * flags: directory specific flags
367 typedef struct _dirinfo {
368 char cdir_path[PATH_MAX];
369 char update_path[PATH_MAX];
370 int has_dir;
371 int count;
372 int flags;
373 } dirinfo_t;
376 * Update flags:
377 * NEED_CACHE_DIR : cache directory is missing and needs to be created
378 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
379 * UPDATE_ERROR : an error occourred while traversing the list of files
380 * RDONLY_FSCHK : the target filesystem is read-only
381 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
383 #define NEED_CACHE_DIR 0x00000001
384 #define IS_SPARC_TARGET 0x00000002
385 #define UPDATE_ERROR 0x00000004
386 #define RDONLY_FSCHK 0x00000008
387 #define INVALIDATE_CACHE 0x00000010
389 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
390 #define set_flag(flag) (walk_arg.update_flags |= flag)
391 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
394 * struct walk_arg :
395 * update_flags: flags related to the current updating process
396 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
397 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
399 static struct {
400 int update_flags;
401 nvlist_t *new_nvlp;
402 nvlist_t *old_nvlp;
403 FILE *sparcfile;
404 dirinfo_t dirinfo[CACHEDIR_NUM];
405 } walk_arg;
407 struct safefile {
408 char *name;
409 struct safefile *next;
412 static struct safefile *safefiles = NULL;
415 * svc:/system/filesystem/usr:default service checks for this file and
416 * does a boot archive update and then reboot the system.
418 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
421 * svc:/system/boot-archive-update:default checks for this file and
422 * updates the boot archive.
424 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
426 /* Thanks growisofs */
427 #define CD_BLOCK ((off64_t)2048)
428 #define VOLDESC_OFF 16
429 #define DVD_BLOCK (32*1024)
430 #define MAX_IVDs 16
432 struct iso_pdesc {
433 unsigned char type [1];
434 unsigned char id [5];
435 unsigned char void1 [80-5-1];
436 unsigned char volume_space_size [8];
437 unsigned char void2 [2048-80-8];
441 * COUNT_MAX: maximum number of changed files to justify a multisession update
442 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
443 * update
445 #define COUNT_MAX 50
446 #define BA_SIZE_MAX (50 * 1024 * 1024)
448 #define bam_nowrite() (bam_check || bam_smf_check)
450 static int sync_menu = 1; /* whether we need to sync the BE menus */
452 static void
453 usage(void)
455 (void) fprintf(stderr, "USAGE:\n");
457 /* archive usage */
458 (void) fprintf(stderr,
459 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
460 (void) fprintf(stderr,
461 "\t%s list-archive [-R altroot [-p platform]]\n", prog);
462 #if defined(_OBP)
463 (void) fprintf(stderr,
464 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
465 #else
466 (void) fprintf(stderr,
467 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
468 #endif
469 #if !defined(_OBP)
470 /* x86 only */
471 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
472 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
473 #endif
477 * Best effort attempt to restore the $HOME value.
479 static void
480 restore_env()
482 char home_env[PATH_MAX];
484 if (bam_home_env) {
485 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
486 bam_home_env);
487 (void) putenv(home_env);
492 #define SLEEP_TIME 5
493 #define MAX_TRIES 4
496 * Sanitize the environment in which bootadm will execute its sub-processes
497 * (ex. mkisofs). This is done to prevent those processes from attempting
498 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
499 * or, potentially, insecure.
501 static void
502 sanitize_env()
504 int stry = 0;
506 /* don't depend on caller umask */
507 (void) umask(0022);
509 /* move away from a potential unsafe current working directory */
510 while (chdir("/") == -1) {
511 if (errno != EINTR) {
512 bam_print("WARNING: unable to chdir to /");
513 break;
517 bam_home_env = getenv("HOME");
518 while (bam_home_env != NULL && putenv("HOME=/") == -1) {
519 if (errno == ENOMEM) {
520 /* retry no more than MAX_TRIES times */
521 if (++stry > MAX_TRIES) {
522 bam_print("WARNING: unable to recover from "
523 "system memory pressure... aborting \n");
524 bam_exit(EXIT_FAILURE);
526 /* memory is tight, try to sleep */
527 bam_print("Attempting to recover from memory pressure: "
528 "sleeping for %d seconds\n", SLEEP_TIME * stry);
529 (void) sleep(SLEEP_TIME * stry);
530 } else {
531 bam_print("WARNING: unable to sanitize HOME\n");
537 main(int argc, char *argv[])
539 error_t ret = BAM_SUCCESS;
541 (void) setlocale(LC_ALL, "");
542 (void) textdomain(TEXT_DOMAIN);
544 if ((prog = strrchr(argv[0], '/')) == NULL) {
545 prog = argv[0];
546 } else {
547 prog++;
550 INJECT_ERROR1("ASSERT_ON", assert(0))
552 sanitize_env();
554 parse_args(argc, argv);
556 switch (bam_cmd) {
557 case BAM_MENU:
558 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
559 break;
560 case BAM_ARCHIVE:
561 ret = bam_archive(bam_subcmd, bam_opt);
562 break;
563 case BAM_INSTALL:
564 ret = bam_install(bam_subcmd, bam_opt);
565 break;
566 default:
567 usage();
568 bam_exit(1);
571 if (ret != BAM_SUCCESS)
572 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
574 bam_unlock();
575 return (0);
579 * Equivalence of public and internal commands:
580 * update-archive -- -a update
581 * list-archive -- -a list
582 * set-menu -- -m set_option
583 * list-menu -- -m list_entry
584 * update-menu -- -m update_entry
585 * install-bootloader -- -i install_bootloader
587 static struct cmd_map {
588 char *bam_cmdname;
589 int bam_cmd;
590 char *bam_subcmd;
591 } cmd_map[] = {
592 { "update-archive", BAM_ARCHIVE, "update"},
593 { "list-archive", BAM_ARCHIVE, "list"},
594 { "set-menu", BAM_MENU, "set_option"},
595 { "list-menu", BAM_MENU, "list_entry"},
596 { "update-menu", BAM_MENU, "update_entry"},
597 { "install-bootloader", BAM_INSTALL, "install_bootloader"},
598 { NULL, 0, NULL}
602 * Commands syntax published in bootadm(1M) are parsed here
604 static void
605 parse_args(int argc, char *argv[])
607 struct cmd_map *cmp = cmd_map;
609 /* command conforming to the final spec */
610 if (argc > 1 && argv[1][0] != '-') {
612 * Map commands to internal table.
614 while (cmp->bam_cmdname) {
615 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
616 bam_cmd = cmp->bam_cmd;
617 bam_subcmd = cmp->bam_subcmd;
618 break;
620 cmp++;
622 if (cmp->bam_cmdname == NULL) {
623 usage();
624 bam_exit(1);
626 argc--;
627 argv++;
630 parse_args_internal(argc, argv);
634 * A combination of public and private commands are parsed here.
635 * The internal syntax and the corresponding functionality are:
636 * -a update -- update-archive
637 * -a list -- list-archive
638 * -a update-all -- (reboot to sync all mnted OS archive)
639 * -i install_bootloader -- install-bootloader
640 * -m update_entry -- update-menu
641 * -m list_entry -- list-menu
642 * -m update_temp -- (reboot -- [boot-args])
643 * -m delete_all_entries -- (called from install)
644 * -m enable_hypervisor [args] -- cvt_to_hyper
645 * -m disable_hypervisor -- cvt_to_metal
646 * -m list_setting [entry] [value] -- list_setting
648 * A set of private flags is there too:
649 * -F -- purge the cache directories and rebuild them
650 * -e -- use the (faster) archive update approach (used by
651 * reboot)
653 static void
654 parse_args_internal(int argc, char *argv[])
656 int c, error;
657 extern char *optarg;
658 extern int optind, opterr;
659 #if defined(_OBP)
660 const char *optstring = "a:d:fi:m:no:veFCR:p:P:XZ";
661 #else
662 const char *optstring = "a:d:fi:m:no:veFCMR:p:P:XZ";
663 #endif
665 /* Suppress error message from getopt */
666 opterr = 0;
668 error = 0;
669 while ((c = getopt(argc, argv, optstring)) != -1) {
670 switch (c) {
671 case 'a':
672 if (bam_cmd) {
673 error = 1;
674 bam_error(MULT_CMDS, 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(DUP_OPT, c);
684 bam_debug = s_strtol(optarg);
685 break;
686 case 'f':
687 bam_force = 1;
688 break;
689 case 'F':
690 bam_purge = 1;
691 break;
692 case 'i':
693 if (bam_cmd) {
694 error = 1;
695 bam_error(MULT_CMDS, c);
697 bam_cmd = BAM_INSTALL;
698 bam_subcmd = optarg;
699 break;
700 case 'm':
701 if (bam_cmd) {
702 error = 1;
703 bam_error(MULT_CMDS, c);
705 bam_cmd = BAM_MENU;
706 bam_subcmd = optarg;
707 break;
708 #if !defined(_OBP)
709 case 'M':
710 bam_mbr = 1;
711 break;
712 #endif
713 case 'n':
714 bam_check = 1;
716 * We save the original value of bam_check. The new
717 * approach in case of a read-only filesystem is to
718 * behave as a check, so we need a way to restore the
719 * original value after the evaluation of the read-only
720 * filesystem has been done.
721 * Even if we don't allow at the moment a check with
722 * update_all, this approach is more robust than
723 * simply resetting bam_check to zero.
725 bam_saved_check = 1;
726 break;
727 case 'o':
728 if (bam_opt) {
729 error = 1;
730 bam_error(DUP_OPT, c);
732 bam_opt = optarg;
733 break;
734 case 'v':
735 bam_verbose = 1;
736 break;
737 case 'C':
738 bam_smf_check = 1;
739 break;
740 case 'P':
741 if (bam_pool != NULL) {
742 error = 1;
743 bam_error(DUP_OPT, c);
745 bam_pool = optarg;
746 break;
747 case 'R':
748 if (bam_root) {
749 error = 1;
750 bam_error(DUP_OPT, c);
751 break;
752 } else if (realpath(optarg, rootbuf) == NULL) {
753 error = 1;
754 bam_error(CANT_RESOLVE, optarg,
755 strerror(errno));
756 break;
758 bam_alt_root = 1;
759 bam_root = rootbuf;
760 bam_rootlen = strlen(rootbuf);
761 break;
762 case 'p':
763 bam_alt_platform = 1;
764 bam_platform = optarg;
765 if ((strcmp(bam_platform, "i86pc") != 0) &&
766 (strcmp(bam_platform, "sun4u") != 0) &&
767 (strcmp(bam_platform, "sun4v") != 0)) {
768 error = 1;
769 bam_error(INVALID_PLAT, bam_platform);
771 break;
772 case 'X':
773 bam_is_hv = BAM_HV_PRESENT;
774 break;
775 case 'Z':
776 bam_zfs = 1;
777 break;
778 case 'e':
779 bam_extend = 1;
780 break;
781 case '?':
782 error = 1;
783 bam_error(BAD_OPT, optopt);
784 break;
785 default :
786 error = 1;
787 bam_error(BAD_OPT, c);
788 break;
793 * An alternate platform requires an alternate root
795 if (bam_alt_platform && bam_alt_root == 0) {
796 usage();
797 bam_exit(0);
801 * A command option must be specfied
803 if (!bam_cmd) {
804 if (bam_opt && strcmp(bam_opt, "all") == 0) {
805 usage();
806 bam_exit(0);
808 bam_error(NEED_CMD);
809 error = 1;
812 if (error) {
813 usage();
814 bam_exit(1);
817 if (optind > argc) {
818 bam_error(INT_ERROR, "parse_args");
819 bam_exit(1);
820 } else if (optind < argc) {
821 bam_argv = &argv[optind];
822 bam_argc = argc - optind;
826 * mbr and pool are options for install_bootloader
828 if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
829 usage();
830 bam_exit(0);
834 * -n implies verbose mode
836 if (bam_check)
837 bam_verbose = 1;
840 static error_t
841 check_subcmd_and_options(
842 char *subcmd,
843 char *opt,
844 subcmd_defn_t *table,
845 error_t (**fp)())
847 int i;
849 if (subcmd == NULL) {
850 bam_error(NEED_SUBCMD);
851 return (BAM_ERROR);
854 if (strcmp(subcmd, "set_option") == 0) {
855 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
856 bam_error(MISSING_ARG);
857 usage();
858 return (BAM_ERROR);
859 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
860 bam_error(TRAILING_ARGS);
861 usage();
862 return (BAM_ERROR);
864 } else if (strcmp(subcmd, "update_all") == 0) {
866 * The only option we accept for the "update_all"
867 * subcmd is "fastboot".
869 if (bam_argc > 1 || (bam_argc == 1 &&
870 strcmp(bam_argv[0], "fastboot") != 0)) {
871 bam_error(TRAILING_ARGS);
872 usage();
873 return (BAM_ERROR);
875 if (bam_argc == 1)
876 sync_menu = 0;
877 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
878 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
880 * Of the remaining subcommands, only "enable_hypervisor" and
881 * "list_setting" take trailing arguments.
883 bam_error(TRAILING_ARGS);
884 usage();
885 return (BAM_ERROR);
888 if (bam_root == NULL) {
889 bam_root = rootbuf;
890 bam_rootlen = 1;
893 /* verify that subcmd is valid */
894 for (i = 0; table[i].subcmd != NULL; i++) {
895 if (strcmp(table[i].subcmd, subcmd) == 0)
896 break;
899 if (table[i].subcmd == NULL) {
900 bam_error(INVALID_SUBCMD, subcmd);
901 return (BAM_ERROR);
904 if (table[i].unpriv == 0 && geteuid() != 0) {
905 bam_error(MUST_BE_ROOT);
906 return (BAM_ERROR);
910 * Currently only privileged commands need a lock
912 if (table[i].unpriv == 0)
913 bam_lock();
915 /* subcmd verifies that opt is appropriate */
916 if (table[i].option != OPT_OPTIONAL) {
917 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
918 if (opt)
919 bam_error(NO_OPT_REQ, subcmd);
920 else
921 bam_error(MISS_OPT, subcmd);
922 return (BAM_ERROR);
926 *fp = table[i].handler;
928 return (BAM_SUCCESS);
932 * NOTE: A single "/" is also considered a trailing slash and will
933 * be deleted.
935 static void
936 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
938 size_t dstlen;
940 assert(src);
941 assert(dst);
943 (void) strlcpy(dst, src, dstsize);
945 dstlen = strlen(dst);
946 if (dst[dstlen - 1] == '/') {
947 dst[dstlen - 1] = '\0';
951 static int
952 is_safe_exec(char *path)
954 struct stat sb;
956 if (lstat(path, &sb) != 0) {
957 bam_error(STAT_FAIL, path, strerror(errno));
958 return (BAM_ERROR);
961 if (!S_ISREG(sb.st_mode)) {
962 bam_error(PATH_EXEC_LINK, path);
963 return (BAM_ERROR);
966 if (sb.st_uid != getuid()) {
967 bam_error(PATH_EXEC_OWNER, path, getuid());
968 return (BAM_ERROR);
971 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
972 bam_error(PATH_EXEC_PERMS, path);
973 return (BAM_ERROR);
976 return (BAM_SUCCESS);
979 static error_t
980 list_setting(menu_t *mp, char *which, char *setting)
982 line_t *lp;
983 entry_t *ent;
985 char *p = which;
986 int entry;
988 int found;
990 assert(which);
991 assert(setting);
993 if (*which != NULL) {
995 * If "which" is not a number, assume it's a setting we want
996 * to look for and so set up the routine to look for "which"
997 * in the default entry.
999 while (*p != NULL)
1000 if (!(isdigit((int)*p++))) {
1001 setting = which;
1002 which = mp->curdefault->arg;
1003 break;
1005 } else {
1006 which = mp->curdefault->arg;
1009 entry = atoi(which);
1011 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1012 ent = ent->next)
1015 if (!ent) {
1016 bam_error(NO_MATCH_ENTRY);
1017 return (BAM_ERROR);
1020 found = (*setting == NULL);
1022 for (lp = ent->start; lp != NULL; lp = lp->next) {
1023 if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
1024 bam_print(PRINT, lp->line);
1025 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1026 bam_print(PRINT, lp->arg);
1027 found = 1;
1030 if (lp == ent->end)
1031 break;
1034 if (!found) {
1035 bam_error(NO_MATCH_ENTRY);
1036 return (BAM_ERROR);
1039 return (BAM_SUCCESS);
1042 static error_t
1043 install_bootloader(void)
1045 nvlist_t *nvl;
1046 uint16_t flags = 0;
1047 int found = 0;
1048 struct extmnttab mnt;
1049 struct stat statbuf = {0};
1050 be_node_list_t *be_nodes, *node;
1051 FILE *fp;
1052 char *root_ds = NULL;
1053 int ret = BAM_ERROR;
1055 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1056 bam_error(_("out of memory\n"));
1057 return (ret);
1061 * if bam_alt_root is set, the stage files are used from alt root.
1062 * if pool is set, the target devices are pool devices, stage files
1063 * are read from pool bootfs unless alt root is set.
1065 * use arguments as targets, stage files are from alt or current root
1066 * if no arguments and no pool, install on current boot pool.
1069 if (bam_alt_root) {
1070 if (stat(bam_root, &statbuf) != 0) {
1071 bam_error(STAT_FAIL, bam_root, strerror(errno));
1072 goto done;
1074 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1075 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1076 goto done;
1078 resetmnttab(fp);
1079 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1080 if (mnt.mnt_major == major(statbuf.st_dev) &&
1081 mnt.mnt_minor == minor(statbuf.st_dev)) {
1082 found = 1;
1083 root_ds = strdup(mnt.mnt_special);
1084 break;
1087 (void) fclose(fp);
1089 if (found == 0) {
1090 bam_error(NOT_IN_MNTTAB, bam_root);
1091 goto done;
1093 if (root_ds == NULL) {
1094 bam_error(_("out of memory\n"));
1095 goto done;
1098 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1099 bam_error(_("No BE's found\n"));
1100 goto done;
1102 for (node = be_nodes; node != NULL; node = node->be_next_node)
1103 if (strcmp(root_ds, node->be_root_ds) == 0)
1104 break;
1106 if (node == NULL)
1107 bam_error(_("BE (%s) does not exist\n"), root_ds);
1109 free(root_ds);
1110 root_ds = NULL;
1111 if (node == NULL) {
1112 be_free_list(be_nodes);
1113 goto done;
1115 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1116 node->be_node_name);
1117 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1118 node->be_root_ds);
1119 be_free_list(be_nodes);
1120 if (ret != 0) {
1121 ret = BAM_ERROR;
1122 goto done;
1126 if (bam_force)
1127 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1128 if (bam_mbr)
1129 flags |= BE_INSTALLBOOT_FLAG_MBR;
1130 if (bam_verbose)
1131 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1133 if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1134 bam_error(_("out of memory\n"));
1135 ret = BAM_ERROR;
1136 goto done;
1140 * if altroot was set, we got be name and be root, only need
1141 * to set pool name as target.
1142 * if no altroot, need to find be name and root from pool.
1144 if (bam_pool != NULL) {
1145 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1146 if (ret != 0) {
1147 ret = BAM_ERROR;
1148 goto done;
1150 if (found) {
1151 ret = be_installboot(nvl);
1152 if (ret != 0)
1153 ret = BAM_ERROR;
1154 goto done;
1158 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1159 bam_error(_("No BE's found\n"));
1160 ret = BAM_ERROR;
1161 goto done;
1164 if (bam_pool != NULL) {
1166 * find active be_node in bam_pool
1168 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1169 if (strcmp(bam_pool, node->be_rpool) != 0)
1170 continue;
1171 if (node->be_active_on_boot)
1172 break;
1174 if (node == NULL) {
1175 bam_error(_("No active BE in %s\n"), bam_pool);
1176 be_free_list(be_nodes);
1177 ret = BAM_ERROR;
1178 goto done;
1180 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1181 node->be_node_name);
1182 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1183 node->be_root_ds);
1184 be_free_list(be_nodes);
1185 if (ret != 0) {
1186 ret = BAM_ERROR;
1187 goto done;
1189 ret = be_installboot(nvl);
1190 if (ret != 0)
1191 ret = BAM_ERROR;
1192 goto done;
1196 * get dataset for "/" and fill up the args.
1198 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1199 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1200 ret = BAM_ERROR;
1201 be_free_list(be_nodes);
1202 goto done;
1204 resetmnttab(fp);
1205 found = 0;
1206 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1207 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1208 found = 1;
1209 root_ds = strdup(mnt.mnt_special);
1210 break;
1213 (void) fclose(fp);
1215 if (found == 0) {
1216 bam_error(NOT_IN_MNTTAB, "/");
1217 ret = BAM_ERROR;
1218 be_free_list(be_nodes);
1219 goto done;
1221 if (root_ds == NULL) {
1222 bam_error(_("out of memory\n"));
1223 ret = BAM_ERROR;
1224 be_free_list(be_nodes);
1225 goto done;
1228 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1229 if (strcmp(root_ds, node->be_root_ds) == 0)
1230 break;
1233 if (node == NULL) {
1234 bam_error(_("No such BE: %s\n"), root_ds);
1235 free(root_ds);
1236 be_free_list(be_nodes);
1237 ret = BAM_ERROR;
1238 goto done;
1240 free(root_ds);
1242 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1243 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1244 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1245 be_free_list(be_nodes);
1247 if (ret != 0)
1248 ret = BAM_ERROR;
1249 else
1250 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1251 done:
1252 nvlist_free(nvl);
1254 return (ret);
1257 static error_t
1258 bam_install(char *subcmd, char *opt)
1260 error_t (*f)(void);
1263 * Check arguments
1265 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1266 BAM_ERROR)
1267 return (BAM_ERROR);
1269 return (f());
1272 static error_t
1273 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1275 error_t ret;
1276 char menu_path[PATH_MAX];
1277 char clean_menu_root[PATH_MAX];
1278 char path[PATH_MAX];
1279 menu_t *menu;
1280 char menu_root[PATH_MAX];
1281 struct stat sb;
1282 error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1283 char *special = NULL;
1284 char *pool = NULL;
1285 zfs_mnted_t zmnted;
1286 char *zmntpt = NULL;
1287 char *osdev;
1288 char *osroot;
1289 const char *fcn = "bam_menu()";
1292 * Menu sub-command only applies to GRUB (i.e. x86)
1294 if (!is_grub(bam_alt_root ? bam_root : "/")) {
1295 bam_error(NOT_GRUB_BOOT);
1296 return (BAM_ERROR);
1300 * Check arguments
1302 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1303 if (ret == BAM_ERROR) {
1304 return (BAM_ERROR);
1307 assert(bam_root);
1309 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1310 osdev = osroot = NULL;
1312 if (strcmp(subcmd, "update_entry") == 0) {
1313 assert(opt);
1315 osdev = strtok(opt, ",");
1316 assert(osdev);
1317 osroot = strtok(NULL, ",");
1318 if (osroot) {
1319 /* fixup bam_root so that it points at osroot */
1320 if (realpath(osroot, rootbuf) == NULL) {
1321 bam_error(CANT_RESOLVE, osroot,
1322 strerror(errno));
1323 return (BAM_ERROR);
1325 bam_alt_root = 1;
1326 bam_root = rootbuf;
1327 bam_rootlen = strlen(rootbuf);
1332 * We support menu on PCFS (under certain conditions), but
1333 * not the OS root
1335 if (is_pcfs(bam_root)) {
1336 bam_error(PCFS_ROOT_NOTSUP, bam_root);
1337 return (BAM_ERROR);
1340 if (stat(menu_root, &sb) == -1) {
1341 bam_error(CANNOT_LOCATE_GRUB_MENU);
1342 return (BAM_ERROR);
1345 BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root));
1348 * We no longer use the GRUB slice file. If it exists, then
1349 * the user is doing something that is unsupported (such as
1350 * standard upgrading an old Live Upgrade BE). If that
1351 * happens, mimic existing behavior i.e. pretend that it is
1352 * not a BE. Emit a warning though.
1354 if (bam_alt_root) {
1355 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1356 GRUB_slice);
1357 } else {
1358 (void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1361 if (bam_verbose && stat(path, &sb) == 0)
1362 bam_error(GRUB_SLICE_FILE_EXISTS, path);
1364 if (is_zfs(menu_root)) {
1365 assert(strcmp(menu_root, bam_root) == 0);
1366 special = get_special(menu_root);
1367 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1368 if (special == NULL) {
1369 bam_error(CANT_FIND_SPECIAL, menu_root);
1370 return (BAM_ERROR);
1372 pool = strtok(special, "/");
1373 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1374 if (pool == NULL) {
1375 free(special);
1376 bam_error(CANT_FIND_POOL, menu_root);
1377 return (BAM_ERROR);
1379 BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool));
1381 zmntpt = mount_top_dataset(pool, &zmnted);
1382 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1383 if (zmntpt == NULL) {
1384 bam_error(CANT_MOUNT_POOL_DATASET, pool);
1385 free(special);
1386 return (BAM_ERROR);
1388 BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt));
1390 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1391 BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root));
1394 elide_trailing_slash(menu_root, clean_menu_root,
1395 sizeof (clean_menu_root));
1397 BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root));
1399 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1400 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1402 BAM_DPRINTF((D_MENU_PATH, fcn, menu_path));
1405 * If listing the menu, display the menu location
1407 if (strcmp(subcmd, "list_entry") == 0)
1408 bam_print(GRUB_MENU_PATH, menu_path);
1410 if ((menu = menu_read(menu_path)) == NULL) {
1411 bam_error(CANNOT_LOCATE_GRUB_MENU_FILE, menu_path);
1412 free(special);
1414 return (BAM_ERROR);
1418 * We already checked the following case in
1419 * check_subcmd_and_suboptions() above. Complete the
1420 * final step now.
1422 if (strcmp(subcmd, "set_option") == 0) {
1423 assert(largc == 1 && largv[0] && largv[1] == NULL);
1424 opt = largv[0];
1425 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1426 (strcmp(subcmd, "list_setting") != 0)) {
1427 assert(largc == 0 && largv == NULL);
1430 ret = get_boot_cap(bam_root);
1431 if (ret != BAM_SUCCESS) {
1432 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1433 goto out;
1437 * Once the sub-cmd handler has run
1438 * only the line field is guaranteed to have valid values
1440 if (strcmp(subcmd, "update_entry") == 0) {
1441 ret = f(menu, menu_root, osdev);
1442 } else if (strcmp(subcmd, "upgrade") == 0) {
1443 ret = f(menu, bam_root, menu_root);
1444 } else if (strcmp(subcmd, "list_entry") == 0) {
1445 ret = f(menu, menu_path, opt);
1446 } else if (strcmp(subcmd, "list_setting") == 0) {
1447 ret = f(menu, ((largc > 0) ? largv[0] : ""),
1448 ((largc > 1) ? largv[1] : ""));
1449 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1450 if (is_sparc()) {
1451 bam_error(NO_SPARC, subcmd);
1452 ret = BAM_ERROR;
1453 } else {
1454 ret = f(menu, bam_root, NULL);
1456 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1457 if (is_sparc()) {
1458 bam_error(NO_SPARC, subcmd);
1459 ret = BAM_ERROR;
1460 } else {
1461 char *extra_args = NULL;
1464 * Compress all arguments passed in the largv[] array
1465 * into one string that can then be appended to the
1466 * end of the kernel$ string the routine to enable the
1467 * hypervisor will build.
1469 * This allows the caller to supply arbitrary unparsed
1470 * arguments, such as dom0 memory settings or APIC
1471 * options.
1473 * This concatenation will be done without ANY syntax
1474 * checking whatsoever, so it's the responsibility of
1475 * the caller to make sure the arguments are valid and
1476 * do not duplicate arguments the conversion routines
1477 * may create.
1479 if (largc > 0) {
1480 int extra_len, i;
1482 for (extra_len = 0, i = 0; i < largc; i++)
1483 extra_len += strlen(largv[i]);
1486 * Allocate space for argument strings,
1487 * intervening spaces and terminating NULL.
1489 extra_args = alloca(extra_len + largc);
1491 (void) strcpy(extra_args, largv[0]);
1493 for (i = 1; i < largc; i++) {
1494 (void) strcat(extra_args, " ");
1495 (void) strcat(extra_args, largv[i]);
1499 ret = f(menu, bam_root, extra_args);
1501 } else
1502 ret = f(menu, NULL, opt);
1504 if (ret == BAM_WRITE) {
1505 BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root));
1506 ret = menu_write(clean_menu_root, menu);
1509 out:
1510 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1511 assert((is_zfs(menu_root)) ^ (pool == NULL));
1512 if (pool) {
1513 (void) umount_top_dataset(pool, zmnted, zmntpt);
1514 free(special);
1516 menu_free(menu);
1517 return (ret);
1521 static error_t
1522 bam_archive(
1523 char *subcmd,
1524 char *opt)
1526 error_t ret;
1527 error_t (*f)(char *root, char *opt);
1528 const char *fcn = "bam_archive()";
1531 * Add trailing / for archive subcommands
1533 if (rootbuf[strlen(rootbuf) - 1] != '/')
1534 (void) strcat(rootbuf, "/");
1535 bam_rootlen = strlen(rootbuf);
1538 * Check arguments
1540 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1541 if (ret != BAM_SUCCESS) {
1542 return (BAM_ERROR);
1545 ret = get_boot_cap(rootbuf);
1546 if (ret != BAM_SUCCESS) {
1547 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1548 return (ret);
1552 * Check archive not supported with update_all
1553 * since it is awkward to display out-of-sync
1554 * information for each BE.
1556 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1557 bam_error(CHECK_NOT_SUPPORTED, subcmd);
1558 return (BAM_ERROR);
1561 if (strcmp(subcmd, "update_all") == 0)
1562 bam_update_all = 1;
1564 #if !defined(_OBP)
1565 ucode_install(bam_root);
1566 #endif
1568 ret = f(bam_root, opt);
1570 bam_update_all = 0;
1572 return (ret);
1575 /*PRINTFLIKE1*/
1576 void
1577 bam_error(char *format, ...)
1579 va_list ap;
1581 va_start(ap, format);
1582 (void) fprintf(stderr, "%s: ", prog);
1583 (void) vfprintf(stderr, format, ap);
1584 va_end(ap);
1587 /*PRINTFLIKE1*/
1588 void
1589 bam_derror(char *format, ...)
1591 va_list ap;
1593 assert(bam_debug);
1595 va_start(ap, format);
1596 (void) fprintf(stderr, "DEBUG: ");
1597 (void) vfprintf(stderr, format, ap);
1598 va_end(ap);
1601 /*PRINTFLIKE1*/
1602 void
1603 bam_print(char *format, ...)
1605 va_list ap;
1607 va_start(ap, format);
1608 (void) vfprintf(stdout, format, ap);
1609 va_end(ap);
1612 /*PRINTFLIKE1*/
1613 void
1614 bam_print_stderr(char *format, ...)
1616 va_list ap;
1618 va_start(ap, format);
1619 (void) vfprintf(stderr, format, ap);
1620 va_end(ap);
1623 void
1624 bam_exit(int excode)
1626 restore_env();
1627 bam_unlock();
1628 exit(excode);
1631 static void
1632 bam_lock(void)
1634 struct flock lock;
1635 pid_t pid;
1637 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1638 if (bam_lock_fd < 0) {
1640 * We may be invoked early in boot for archive verification.
1641 * In this case, root is readonly and /var/run may not exist.
1642 * Proceed without the lock
1644 if (errno == EROFS || errno == ENOENT) {
1645 bam_root_readonly = 1;
1646 return;
1649 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
1650 bam_exit(1);
1653 lock.l_type = F_WRLCK;
1654 lock.l_whence = SEEK_SET;
1655 lock.l_start = 0;
1656 lock.l_len = 0;
1658 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1659 if (errno != EACCES && errno != EAGAIN) {
1660 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1661 (void) close(bam_lock_fd);
1662 bam_lock_fd = -1;
1663 bam_exit(1);
1665 pid = 0;
1666 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1667 bam_print(FILE_LOCKED, pid);
1669 lock.l_type = F_WRLCK;
1670 lock.l_whence = SEEK_SET;
1671 lock.l_start = 0;
1672 lock.l_len = 0;
1673 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1674 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1675 (void) close(bam_lock_fd);
1676 bam_lock_fd = -1;
1677 bam_exit(1);
1681 /* We own the lock now */
1682 pid = getpid();
1683 (void) write(bam_lock_fd, &pid, sizeof (pid));
1686 static void
1687 bam_unlock(void)
1689 struct flock unlock;
1692 * NOP if we don't hold the lock
1694 if (bam_lock_fd < 0) {
1695 return;
1698 unlock.l_type = F_UNLCK;
1699 unlock.l_whence = SEEK_SET;
1700 unlock.l_start = 0;
1701 unlock.l_len = 0;
1703 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1704 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1707 if (close(bam_lock_fd) == -1) {
1708 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
1710 bam_lock_fd = -1;
1713 static error_t
1714 list_archive(char *root, char *opt)
1716 filelist_t flist;
1717 filelist_t *flistp = &flist;
1718 line_t *lp;
1720 assert(root);
1721 assert(opt == NULL);
1723 flistp->head = flistp->tail = NULL;
1724 if (read_list(root, flistp) != BAM_SUCCESS) {
1725 return (BAM_ERROR);
1727 assert(flistp->head && flistp->tail);
1729 for (lp = flistp->head; lp; lp = lp->next) {
1730 bam_print(PRINT, lp->line);
1733 filelist_free(flistp);
1735 return (BAM_SUCCESS);
1739 * This routine writes a list of lines to a file.
1740 * The list is *not* freed
1742 static error_t
1743 list2file(char *root, char *tmp, char *final, line_t *start)
1745 char tmpfile[PATH_MAX];
1746 char path[PATH_MAX];
1747 FILE *fp;
1748 int ret;
1749 struct stat sb;
1750 mode_t mode;
1751 uid_t root_uid;
1752 gid_t sys_gid;
1753 struct passwd *pw;
1754 struct group *gp;
1755 const char *fcn = "list2file()";
1757 (void) snprintf(path, sizeof (path), "%s%s", root, final);
1759 if (start == NULL) {
1760 /* Empty GRUB menu */
1761 if (stat(path, &sb) != -1) {
1762 bam_print(UNLINK_EMPTY, path);
1763 if (unlink(path) != 0) {
1764 bam_error(UNLINK_FAIL, path, strerror(errno));
1765 return (BAM_ERROR);
1766 } else {
1767 return (BAM_SUCCESS);
1770 return (BAM_SUCCESS);
1774 * Preserve attributes of existing file if possible,
1775 * otherwise ask the system for uid/gid of root/sys.
1776 * If all fails, fall back on hard-coded defaults.
1778 if (stat(path, &sb) != -1) {
1779 mode = sb.st_mode;
1780 root_uid = sb.st_uid;
1781 sys_gid = sb.st_gid;
1782 } else {
1783 mode = DEFAULT_DEV_MODE;
1784 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1785 root_uid = pw->pw_uid;
1786 } else {
1787 bam_error(CANT_FIND_USER,
1788 DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1789 root_uid = (uid_t)DEFAULT_DEV_UID;
1791 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1792 sys_gid = gp->gr_gid;
1793 } else {
1794 bam_error(CANT_FIND_GROUP,
1795 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1796 sys_gid = (gid_t)DEFAULT_DEV_GID;
1800 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1802 /* Truncate tmpfile first */
1803 fp = fopen(tmpfile, "w");
1804 if (fp == NULL) {
1805 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1806 return (BAM_ERROR);
1808 ret = fclose(fp);
1809 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1810 if (ret == EOF) {
1811 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1812 return (BAM_ERROR);
1815 /* Now open it in append mode */
1816 fp = fopen(tmpfile, "a");
1817 if (fp == NULL) {
1818 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1819 return (BAM_ERROR);
1822 for (; start; start = start->next) {
1823 ret = s_fputs(start->line, fp);
1824 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1825 if (ret == EOF) {
1826 bam_error(WRITE_FAIL, tmpfile, strerror(errno));
1827 (void) fclose(fp);
1828 return (BAM_ERROR);
1832 ret = fclose(fp);
1833 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1834 if (ret == EOF) {
1835 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1836 return (BAM_ERROR);
1840 * Set up desired attributes. Ignore failures on filesystems
1841 * not supporting these operations - pcfs reports unsupported
1842 * operations as EINVAL.
1844 ret = chmod(tmpfile, mode);
1845 if (ret == -1 &&
1846 errno != EINVAL && errno != ENOTSUP) {
1847 bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
1848 return (BAM_ERROR);
1851 ret = chown(tmpfile, root_uid, sys_gid);
1852 if (ret == -1 &&
1853 errno != EINVAL && errno != ENOTSUP) {
1854 bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
1855 return (BAM_ERROR);
1859 * Do an atomic rename
1861 ret = rename(tmpfile, path);
1862 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1863 if (ret != 0) {
1864 bam_error(RENAME_FAIL, path, strerror(errno));
1865 return (BAM_ERROR);
1868 BAM_DPRINTF((D_WROTE_FILE, fcn, path));
1869 return (BAM_SUCCESS);
1873 * Checks if the path specified (without the file name at the end) exists
1874 * and creates it if not. If the path exists and is not a directory, an attempt
1875 * to unlink is made.
1877 static int
1878 setup_path(char *path)
1880 char *p;
1881 int ret;
1882 struct stat sb;
1884 p = strrchr(path, '/');
1885 if (p != NULL) {
1886 *p = '\0';
1887 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1888 /* best effort attempt, mkdirp will catch the error */
1889 (void) unlink(path);
1890 if (bam_verbose)
1891 bam_print(NEED_DIRPATH, path);
1892 ret = mkdirp(path, DIR_PERMS);
1893 if (ret == -1) {
1894 bam_error(MKDIR_FAILED, path, strerror(errno));
1895 *p = '/';
1896 return (BAM_ERROR);
1899 *p = '/';
1900 return (BAM_SUCCESS);
1902 return (BAM_SUCCESS);
1905 typedef union {
1906 gzFile gzfile;
1907 int fdfile;
1908 } outfile;
1910 typedef struct {
1911 char path[PATH_MAX];
1912 outfile out;
1913 } cachefile;
1915 static int
1916 setup_file(char *base, const char *path, cachefile *cf)
1918 int ret;
1919 char *strip;
1921 /* init gzfile or fdfile in case we fail before opening */
1922 if (bam_direct == BAM_DIRECT_DBOOT)
1923 cf->out.gzfile = NULL;
1924 else
1925 cf->out.fdfile = -1;
1927 /* strip the trailing altroot path */
1928 strip = (char *)path + strlen(rootbuf);
1930 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1931 if (ret >= sizeof (cf->path)) {
1932 bam_error(PATH_TOO_LONG, rootbuf);
1933 return (BAM_ERROR);
1936 /* Check if path is present in the archive cache directory */
1937 if (setup_path(cf->path) == BAM_ERROR)
1938 return (BAM_ERROR);
1940 if (bam_direct == BAM_DIRECT_DBOOT) {
1941 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1942 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1943 return (BAM_ERROR);
1945 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1946 Z_DEFAULT_STRATEGY);
1947 } else {
1948 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1949 == -1) {
1950 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1951 return (BAM_ERROR);
1955 return (BAM_SUCCESS);
1958 static int
1959 cache_write(cachefile cf, char *buf, int size)
1961 int err;
1963 if (bam_direct == BAM_DIRECT_DBOOT) {
1964 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
1965 bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err));
1966 if (err == Z_ERRNO && bam_verbose) {
1967 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1969 return (BAM_ERROR);
1971 } else {
1972 if (write(cf.out.fdfile, buf, size) < 1) {
1973 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1974 return (BAM_ERROR);
1977 return (BAM_SUCCESS);
1980 static int
1981 cache_close(cachefile cf)
1983 int ret;
1985 if (bam_direct == BAM_DIRECT_DBOOT) {
1986 if (cf.out.gzfile) {
1987 ret = gzclose(cf.out.gzfile);
1988 if (ret != Z_OK) {
1989 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
1990 return (BAM_ERROR);
1993 } else {
1994 if (cf.out.fdfile != -1) {
1995 ret = close(cf.out.fdfile);
1996 if (ret != 0) {
1997 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
1998 return (BAM_ERROR);
2003 return (BAM_SUCCESS);
2006 static int
2007 dircache_updatefile(const char *path, int what)
2009 int ret, exitcode;
2010 char buf[4096 * 4];
2011 FILE *infile;
2012 cachefile outfile, outupdt;
2014 if (bam_nowrite()) {
2015 set_dir_flag(what, NEED_UPDATE);
2016 return (BAM_SUCCESS);
2019 if (!has_cachedir(what))
2020 return (BAM_SUCCESS);
2022 if ((infile = fopen(path, "rb")) == NULL) {
2023 bam_error(OPEN_FAIL, path, strerror(errno));
2024 return (BAM_ERROR);
2027 ret = setup_file(get_cachedir(what), path, &outfile);
2028 if (ret == BAM_ERROR) {
2029 exitcode = BAM_ERROR;
2030 goto out;
2032 if (!is_dir_flag_on(what, NO_MULTI)) {
2033 ret = setup_file(get_updatedir(what), path, &outupdt);
2034 if (ret == BAM_ERROR)
2035 set_dir_flag(what, NO_MULTI);
2038 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2039 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2040 exitcode = BAM_ERROR;
2041 goto out;
2043 if (!is_dir_flag_on(what, NO_MULTI))
2044 if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2045 set_dir_flag(what, NO_MULTI);
2048 set_dir_flag(what, NEED_UPDATE);
2049 get_count(what)++;
2050 if (get_count(what) > COUNT_MAX)
2051 set_dir_flag(what, NO_MULTI);
2052 exitcode = BAM_SUCCESS;
2053 out:
2054 (void) fclose(infile);
2055 if (cache_close(outfile) == BAM_ERROR)
2056 exitcode = BAM_ERROR;
2057 if (!is_dir_flag_on(what, NO_MULTI) &&
2058 cache_close(outupdt) == BAM_ERROR)
2059 exitcode = BAM_ERROR;
2060 if (exitcode == BAM_ERROR)
2061 set_flag(UPDATE_ERROR);
2062 return (exitcode);
2065 static int
2066 dircache_updatedir(const char *path, int what, int updt)
2068 int ret;
2069 char dpath[PATH_MAX];
2070 char *strip;
2071 struct stat sb;
2073 strip = (char *)path + strlen(rootbuf);
2075 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2076 get_updatedir(what) : get_cachedir(what), strip);
2078 if (ret >= sizeof (dpath)) {
2079 bam_error(PATH_TOO_LONG, rootbuf);
2080 set_flag(UPDATE_ERROR);
2081 return (BAM_ERROR);
2084 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2085 return (BAM_SUCCESS);
2087 if (updt) {
2088 if (!is_dir_flag_on(what, NO_MULTI))
2089 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2090 set_dir_flag(what, NO_MULTI);
2091 } else {
2092 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2093 set_flag(UPDATE_ERROR);
2094 return (BAM_ERROR);
2098 set_dir_flag(what, NEED_UPDATE);
2099 return (BAM_SUCCESS);
2102 #define DO_CACHE_DIR 0
2103 #define DO_UPDATE_DIR 1
2105 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2106 typedef Elf64_Ehdr _elfhdr;
2107 #else
2108 typedef Elf32_Ehdr _elfhdr;
2109 #endif
2112 * This routine updates the contents of the cache directory
2114 static int
2115 update_dircache(const char *path, int flags)
2117 int rc = BAM_SUCCESS;
2119 switch (flags) {
2120 case FTW_F:
2122 int fd;
2123 _elfhdr elf;
2125 if ((fd = open(path, O_RDONLY)) < 0) {
2126 bam_error(OPEN_FAIL, path, strerror(errno));
2127 set_flag(UPDATE_ERROR);
2128 rc = BAM_ERROR;
2129 break;
2133 * libelf and gelf would be a cleaner and easier way to handle
2134 * this, but libelf fails compilation if _ILP32 is defined &&
2135 * _FILE_OFFSET_BITS is != 32 ...
2137 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2138 bam_error(READ_FAIL, path, strerror(errno));
2139 set_flag(UPDATE_ERROR);
2140 (void) close(fd);
2141 rc = BAM_ERROR;
2142 break;
2144 (void) close(fd);
2147 * If the file is not an executable and is not inside an amd64
2148 * directory, we copy it in both the cache directories,
2149 * otherwise, we only copy it inside the 64-bit one.
2151 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2152 if (strstr(path, "/amd64")) {
2153 rc = dircache_updatefile(path, FILE64);
2154 } else {
2155 rc = dircache_updatefile(path, FILE32);
2156 if (rc == BAM_SUCCESS)
2157 rc = dircache_updatefile(path, FILE64);
2159 } else {
2161 * Based on the ELF class we copy the file in the 32-bit
2162 * or the 64-bit cache directory.
2164 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
2165 rc = dircache_updatefile(path, FILE32);
2166 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
2167 rc = dircache_updatefile(path, FILE64);
2168 } else {
2169 bam_print(NO3264ELF, path);
2170 /* paranoid */
2171 rc = dircache_updatefile(path, FILE32);
2172 if (rc == BAM_SUCCESS)
2173 rc = dircache_updatefile(path, FILE64);
2176 break;
2178 case FTW_D:
2179 if (strstr(path, "/amd64") == NULL) {
2180 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
2181 if (rc == BAM_SUCCESS)
2182 rc = dircache_updatedir(path, FILE32,
2183 DO_CACHE_DIR);
2184 } else {
2185 if (has_cachedir(FILE64)) {
2186 rc = dircache_updatedir(path, FILE64,
2187 DO_UPDATE_DIR);
2188 if (rc == BAM_SUCCESS)
2189 rc = dircache_updatedir(path, FILE64,
2190 DO_CACHE_DIR);
2193 break;
2194 default:
2195 rc = BAM_ERROR;
2196 break;
2199 return (rc);
2202 /*ARGSUSED*/
2203 static int
2204 cmpstat(
2205 const char *file,
2206 const struct stat *st,
2207 int flags,
2208 struct FTW *ftw)
2210 uint_t sz;
2211 uint64_t *value;
2212 uint64_t filestat[2];
2213 int error, ret, status;
2215 struct safefile *safefilep;
2216 FILE *fp;
2217 struct stat sb;
2218 regex_t re;
2221 * On SPARC we create/update links too.
2223 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2224 !is_flag_on(IS_SPARC_TARGET)))
2225 return (0);
2228 * Ignore broken links
2230 if (flags == FTW_SL && stat(file, &sb) < 0)
2231 return (0);
2234 * new_nvlp may be NULL if there were errors earlier
2235 * but this is not fatal to update determination.
2237 if (walk_arg.new_nvlp) {
2238 filestat[0] = st->st_size;
2239 filestat[1] = st->st_mtime;
2240 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2241 file + bam_rootlen, filestat, 2);
2242 if (error)
2243 bam_error(NVADD_FAIL, file, strerror(error));
2247 * If we are invoked as part of system/filesystem/boot-archive, then
2248 * there are a number of things we should not worry about
2250 if (bam_smf_check) {
2251 /* ignore amd64 modules unless we are booted amd64. */
2252 if (!is_amd64() && strstr(file, "/amd64/") != 0)
2253 return (0);
2255 /* read in list of safe files */
2256 if (safefiles == NULL) {
2257 fp = fopen("/boot/solaris/filelist.safe", "r");
2258 if (fp != NULL) {
2259 safefiles = s_calloc(1,
2260 sizeof (struct safefile));
2261 safefilep = safefiles;
2262 safefilep->name = s_calloc(1, MAXPATHLEN +
2263 MAXNAMELEN);
2264 safefilep->next = NULL;
2265 while (s_fgets(safefilep->name, MAXPATHLEN +
2266 MAXNAMELEN, fp) != NULL) {
2267 safefilep->next = s_calloc(1,
2268 sizeof (struct safefile));
2269 safefilep = safefilep->next;
2270 safefilep->name = s_calloc(1,
2271 MAXPATHLEN + MAXNAMELEN);
2272 safefilep->next = NULL;
2274 (void) fclose(fp);
2280 * On SPARC we create a -path-list file for mkisofs
2282 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2283 if (flags != FTW_D) {
2284 char *strip;
2286 strip = (char *)file + strlen(rootbuf);
2287 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2288 file);
2293 * We are transitioning from the old model to the dircache or the cache
2294 * directory was removed: create the entry without further checkings.
2296 if (is_flag_on(NEED_CACHE_DIR)) {
2297 if (bam_verbose)
2298 bam_print(PARSEABLE_NEW_FILE, file);
2300 if (is_flag_on(IS_SPARC_TARGET)) {
2301 set_dir_flag(FILE64, NEED_UPDATE);
2302 return (0);
2305 ret = update_dircache(file, flags);
2306 if (ret == BAM_ERROR) {
2307 bam_error(UPDT_CACHE_FAIL, file);
2308 return (-1);
2311 return (0);
2315 * We need an update if file doesn't exist in old archive
2317 if (walk_arg.old_nvlp == NULL ||
2318 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2319 file + bam_rootlen, &value, &sz) != 0) {
2320 if (bam_smf_check) /* ignore new during smf check */
2321 return (0);
2323 if (is_flag_on(IS_SPARC_TARGET)) {
2324 set_dir_flag(FILE64, NEED_UPDATE);
2325 } else {
2326 ret = update_dircache(file, flags);
2327 if (ret == BAM_ERROR) {
2328 bam_error(UPDT_CACHE_FAIL, file);
2329 return (-1);
2333 if (bam_verbose)
2334 bam_print(PARSEABLE_NEW_FILE, file);
2335 return (0);
2339 * If we got there, the file is already listed as to be included in the
2340 * iso image. We just need to know if we are going to rebuild it or not
2342 if (is_flag_on(IS_SPARC_TARGET) &&
2343 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
2344 return (0);
2346 * File exists in old archive. Check if file has changed
2348 assert(sz == 2);
2349 bcopy(value, filestat, sizeof (filestat));
2351 if (flags != FTW_D && (filestat[0] != st->st_size ||
2352 filestat[1] != st->st_mtime)) {
2353 if (bam_smf_check) {
2354 safefilep = safefiles;
2355 while (safefilep != NULL &&
2356 safefilep->name[0] != '\0') {
2357 if (regcomp(&re, safefilep->name,
2358 REG_EXTENDED|REG_NOSUB) == 0) {
2359 status = regexec(&re,
2360 file + bam_rootlen, 0, NULL, 0);
2361 regfree(&re);
2362 if (status == 0) {
2363 (void) creat(
2364 NEED_UPDATE_SAFE_FILE,
2365 0644);
2366 return (0);
2369 safefilep = safefilep->next;
2373 if (is_flag_on(IS_SPARC_TARGET)) {
2374 set_dir_flag(FILE64, NEED_UPDATE);
2375 } else {
2376 ret = update_dircache(file, flags);
2377 if (ret == BAM_ERROR) {
2378 bam_error(UPDT_CACHE_FAIL, file);
2379 return (-1);
2383 if (bam_verbose) {
2384 if (bam_smf_check)
2385 bam_print(" %s\n", file);
2386 else
2387 bam_print(PARSEABLE_OUT_DATE, file);
2391 return (0);
2395 * Remove a directory path recursively
2397 static int
2398 rmdir_r(char *path)
2400 struct dirent *d = NULL;
2401 DIR *dir = NULL;
2402 char tpath[PATH_MAX];
2403 struct stat sb;
2405 if ((dir = opendir(path)) == NULL)
2406 return (-1);
2408 while ((d = readdir(dir)) != NULL) {
2409 if ((strcmp(d->d_name, ".") != 0) &&
2410 (strcmp(d->d_name, "..") != 0)) {
2411 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
2412 path, d->d_name);
2413 if (stat(tpath, &sb) == 0) {
2414 if (sb.st_mode & S_IFDIR)
2415 (void) rmdir_r(tpath);
2416 else
2417 (void) remove(tpath);
2421 return (remove(path));
2425 * Check if cache directory exists and, if not, create it and update flags
2426 * accordingly. If the path exists, but it's not a directory, a best effort
2427 * attempt to remove and recreate it is made.
2428 * If the user requested a 'purge', always recreate the directory from scratch.
2430 static int
2431 set_cache_dir(char *root, int what)
2433 struct stat sb;
2434 int ret = 0;
2436 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
2437 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
2438 "/amd64" : "", CACHEDIR_SUFFIX);
2440 if (ret >= sizeof (get_cachedir(what))) {
2441 bam_error(PATH_TOO_LONG, rootbuf);
2442 return (BAM_ERROR);
2445 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2446 (void) rmdir_r(get_cachedir(what));
2448 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2449 /* best effort unlink attempt, mkdir will catch errors */
2450 (void) unlink(get_cachedir(what));
2452 if (bam_verbose)
2453 bam_print(UPDATE_CDIR_MISS, get_cachedir(what));
2454 ret = mkdir(get_cachedir(what), DIR_PERMS);
2455 if (ret < 0) {
2456 bam_error(MKDIR_FAILED, get_cachedir(what),
2457 strerror(errno));
2458 get_cachedir(what)[0] = '\0';
2459 return (ret);
2461 set_flag(NEED_CACHE_DIR);
2462 set_dir_flag(what, NO_MULTI);
2465 return (BAM_SUCCESS);
2468 static int
2469 set_update_dir(char *root, int what)
2471 struct stat sb;
2472 int ret;
2474 if (is_dir_flag_on(what, NO_MULTI))
2475 return (BAM_SUCCESS);
2477 if (!bam_extend) {
2478 set_dir_flag(what, NO_MULTI);
2479 return (BAM_SUCCESS);
2482 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2483 ret = snprintf(get_updatedir(what),
2484 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2485 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2486 else
2487 ret = snprintf(get_updatedir(what),
2488 sizeof (get_updatedir(what)), "%s%s%s%s", root,
2489 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2491 if (ret >= sizeof (get_updatedir(what))) {
2492 bam_error(PATH_TOO_LONG, rootbuf);
2493 return (BAM_ERROR);
2496 if (stat(get_updatedir(what), &sb) == 0) {
2497 if (S_ISDIR(sb.st_mode))
2498 ret = rmdir_r(get_updatedir(what));
2499 else
2500 ret = unlink(get_updatedir(what));
2502 if (ret != 0)
2503 set_dir_flag(what, NO_MULTI);
2506 if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2507 set_dir_flag(what, NO_MULTI);
2509 return (BAM_SUCCESS);
2512 static int
2513 is_valid_archive(char *root, int what)
2515 char archive_path[PATH_MAX];
2516 char timestamp_path[PATH_MAX];
2517 struct stat sb, timestamp;
2518 int ret;
2520 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2521 ret = snprintf(archive_path, sizeof (archive_path),
2522 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2523 ARCHIVE_SUFFIX);
2524 else
2525 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2526 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2528 if (ret >= sizeof (archive_path)) {
2529 bam_error(PATH_TOO_LONG, rootbuf);
2530 return (BAM_ERROR);
2533 if (stat(archive_path, &sb) != 0) {
2534 if (bam_verbose && !bam_check)
2535 bam_print(UPDATE_ARCH_MISS, archive_path);
2536 set_dir_flag(what, NEED_UPDATE);
2537 set_dir_flag(what, NO_MULTI);
2538 return (BAM_SUCCESS);
2542 * The timestamp file is used to prevent stale files in the archive
2543 * cache.
2544 * Stale files can happen if the system is booted back and forth across
2545 * the transition from bootadm-before-the-cache to
2546 * bootadm-after-the-cache, since older versions of bootadm don't know
2547 * about the existence of the archive cache.
2549 * Since only bootadm-after-the-cache versions know about about this
2550 * file, we require that the boot archive be older than this file.
2552 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2553 FILE_STAT_TIMESTAMP);
2555 if (ret >= sizeof (timestamp_path)) {
2556 bam_error(PATH_TOO_LONG, rootbuf);
2557 return (BAM_ERROR);
2560 if (stat(timestamp_path, &timestamp) != 0 ||
2561 sb.st_mtime > timestamp.st_mtime) {
2562 if (bam_verbose && !bam_check)
2563 bam_print(UPDATE_CACHE_OLD);
2565 * Don't generate a false positive for the boot-archive service
2566 * but trigger an update of the archive cache in
2567 * boot-archive-update.
2569 if (bam_smf_check) {
2570 (void) creat(NEED_UPDATE_FILE, 0644);
2571 return (BAM_SUCCESS);
2574 set_flag(INVALIDATE_CACHE);
2575 set_dir_flag(what, NEED_UPDATE);
2576 set_dir_flag(what, NO_MULTI);
2577 return (BAM_SUCCESS);
2580 if (is_flag_on(IS_SPARC_TARGET))
2581 return (BAM_SUCCESS);
2583 if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2584 if (bam_verbose && !bam_check)
2585 bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX);
2586 set_dir_flag(what, NO_MULTI);
2589 return (BAM_SUCCESS);
2593 * Check flags and presence of required files and directories.
2594 * The force flag and/or absence of files should
2595 * trigger an update.
2596 * Suppress stdout output if check (-n) option is set
2597 * (as -n should only produce parseable output.)
2599 static int
2600 check_flags_and_files(char *root)
2603 struct stat sb;
2604 int ret;
2607 * If archive is missing, create archive
2609 if (is_flag_on(IS_SPARC_TARGET)) {
2610 ret = is_valid_archive(root, FILE64);
2611 if (ret == BAM_ERROR)
2612 return (BAM_ERROR);
2613 } else {
2614 int what = FILE32;
2615 do {
2616 ret = is_valid_archive(root, what);
2617 if (ret == BAM_ERROR)
2618 return (BAM_ERROR);
2619 what++;
2620 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2623 if (bam_nowrite())
2624 return (BAM_SUCCESS);
2628 * check if cache directories exist on x86.
2629 * check (and always open) the cache file on SPARC.
2631 if (is_sparc()) {
2632 ret = snprintf(get_cachedir(FILE64),
2633 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2634 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2636 if (ret >= sizeof (get_cachedir(FILE64))) {
2637 bam_error(PATH_TOO_LONG, rootbuf);
2638 return (BAM_ERROR);
2641 if (stat(get_cachedir(FILE64), &sb) != 0) {
2642 set_flag(NEED_CACHE_DIR);
2643 set_dir_flag(FILE64, NEED_UPDATE);
2646 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2647 if (walk_arg.sparcfile == NULL) {
2648 bam_error(OPEN_FAIL, get_cachedir(FILE64),
2649 strerror(errno));
2650 return (BAM_ERROR);
2653 set_dir_present(FILE64);
2654 } else {
2655 int what = FILE32;
2657 do {
2658 if (set_cache_dir(root, what) != 0)
2659 return (BAM_ERROR);
2661 set_dir_present(what);
2663 if (set_update_dir(root, what) != 0)
2664 return (BAM_ERROR);
2665 what++;
2666 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2670 * if force, create archive unconditionally
2672 if (bam_force) {
2673 if (!is_sparc())
2674 set_dir_flag(FILE32, NEED_UPDATE);
2675 set_dir_flag(FILE64, NEED_UPDATE);
2676 if (bam_verbose)
2677 bam_print(UPDATE_FORCE);
2678 return (BAM_SUCCESS);
2681 return (BAM_SUCCESS);
2684 static error_t
2685 read_one_list(char *root, filelist_t *flistp, char *filelist)
2687 char path[PATH_MAX];
2688 FILE *fp;
2689 char buf[BAM_MAXLINE];
2690 const char *fcn = "read_one_list()";
2692 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2694 fp = fopen(path, "r");
2695 if (fp == NULL) {
2696 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2697 return (BAM_ERROR);
2699 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2700 /* skip blank lines */
2701 if (strspn(buf, " \t") == strlen(buf))
2702 continue;
2703 append_to_flist(flistp, buf);
2705 if (fclose(fp) != 0) {
2706 bam_error(CLOSE_FAIL, path, strerror(errno));
2707 return (BAM_ERROR);
2709 return (BAM_SUCCESS);
2712 static error_t
2713 read_list(char *root, filelist_t *flistp)
2715 char path[PATH_MAX];
2716 char cmd[PATH_MAX];
2717 struct stat sb;
2718 int n, rval;
2719 const char *fcn = "read_list()";
2721 flistp->head = flistp->tail = NULL;
2724 * build and check path to extract_boot_filelist.ksh
2726 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2727 if (n >= sizeof (path)) {
2728 bam_error(NO_FLIST);
2729 return (BAM_ERROR);
2732 if (is_safe_exec(path) == BAM_ERROR)
2733 return (BAM_ERROR);
2736 * If extract_boot_filelist is present, exec it, otherwise read
2737 * the filelists directly, for compatibility with older images.
2739 if (stat(path, &sb) == 0) {
2741 * build arguments to exec extract_boot_filelist.ksh
2743 char *rootarg, *platarg;
2744 int platarglen = 1, rootarglen = 1;
2745 if (strlen(root) > 1)
2746 rootarglen += strlen(root) + strlen("-R ");
2747 if (bam_alt_platform)
2748 platarglen += strlen(bam_platform) + strlen("-p ");
2749 platarg = s_calloc(1, platarglen);
2750 rootarg = s_calloc(1, rootarglen);
2751 *platarg = 0;
2752 *rootarg = 0;
2754 if (strlen(root) > 1) {
2755 (void) snprintf(rootarg, rootarglen,
2756 "-R %s", root);
2758 if (bam_alt_platform) {
2759 (void) snprintf(platarg, platarglen,
2760 "-p %s", bam_platform);
2762 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2763 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2764 free(platarg);
2765 free(rootarg);
2766 if (n >= sizeof (cmd)) {
2767 bam_error(NO_FLIST);
2768 return (BAM_ERROR);
2770 if (exec_cmd(cmd, flistp) != 0) {
2771 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2772 return (BAM_ERROR);
2774 } else {
2776 * Read current lists of files - only the first is mandatory
2778 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2779 if (rval != BAM_SUCCESS)
2780 return (rval);
2781 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2784 if (flistp->head == NULL) {
2785 bam_error(NO_FLIST);
2786 return (BAM_ERROR);
2789 return (BAM_SUCCESS);
2792 static void
2793 getoldstat(char *root)
2795 char path[PATH_MAX];
2796 int fd, error;
2797 struct stat sb;
2798 char *ostat;
2800 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2801 fd = open(path, O_RDONLY);
2802 if (fd == -1) {
2803 if (bam_verbose)
2804 bam_print(OPEN_FAIL, path, strerror(errno));
2805 goto out_err;
2808 if (fstat(fd, &sb) != 0) {
2809 bam_error(STAT_FAIL, path, strerror(errno));
2810 goto out_err;
2813 ostat = s_calloc(1, sb.st_size);
2815 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2816 bam_error(READ_FAIL, path, strerror(errno));
2817 free(ostat);
2818 goto out_err;
2821 (void) close(fd);
2822 fd = -1;
2824 walk_arg.old_nvlp = NULL;
2825 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2827 free(ostat);
2829 if (error) {
2830 bam_error(UNPACK_FAIL, path, strerror(error));
2831 walk_arg.old_nvlp = NULL;
2832 goto out_err;
2833 } else {
2834 return;
2837 out_err:
2838 if (fd != -1)
2839 (void) close(fd);
2840 if (!is_flag_on(IS_SPARC_TARGET))
2841 set_dir_flag(FILE32, NEED_UPDATE);
2842 set_dir_flag(FILE64, NEED_UPDATE);
2845 /* Best effort stale entry removal */
2846 static void
2847 delete_stale(char *file, int what)
2849 char path[PATH_MAX];
2850 struct stat sb;
2852 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2853 if (!bam_check && stat(path, &sb) == 0) {
2854 if (sb.st_mode & S_IFDIR)
2855 (void) rmdir_r(path);
2856 else
2857 (void) unlink(path);
2859 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2864 * Checks if a file in the current (old) archive has
2865 * been deleted from the root filesystem. This is needed for
2866 * software like Trusted Extensions (TX) that switch early
2867 * in boot based on presence/absence of a kernel module.
2869 static void
2870 check4stale(char *root)
2872 nvpair_t *nvp;
2873 nvlist_t *nvlp;
2874 char *file;
2875 char path[PATH_MAX];
2878 * Skip stale file check during smf check
2880 if (bam_smf_check)
2881 return;
2884 * If we need to (re)create the cache, there's no need to check for
2885 * stale files
2887 if (is_flag_on(NEED_CACHE_DIR))
2888 return;
2890 /* Nothing to do if no old stats */
2891 if ((nvlp = walk_arg.old_nvlp) == NULL)
2892 return;
2894 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2895 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2896 file = nvpair_name(nvp);
2897 if (file == NULL)
2898 continue;
2899 (void) snprintf(path, sizeof (path), "%s/%s",
2900 root, file);
2901 if (access(path, F_OK) < 0) {
2902 int what;
2904 if (bam_verbose)
2905 bam_print(PARSEABLE_STALE_FILE, path);
2907 if (is_flag_on(IS_SPARC_TARGET)) {
2908 set_dir_flag(FILE64, NEED_UPDATE);
2909 } else {
2910 for (what = FILE32; what < CACHEDIR_NUM; what++)
2911 if (has_cachedir(what))
2912 delete_stale(file, what);
2918 static void
2919 create_newstat(void)
2921 int error;
2923 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2924 if (error) {
2926 * Not fatal - we can still create archive
2928 walk_arg.new_nvlp = NULL;
2929 bam_error(NVALLOC_FAIL, strerror(error));
2933 static int
2934 walk_list(char *root, filelist_t *flistp)
2936 char path[PATH_MAX];
2937 line_t *lp;
2939 for (lp = flistp->head; lp; lp = lp->next) {
2941 * Don't follow symlinks. A symlink must refer to
2942 * a file that would appear in the archive through
2943 * a direct reference. This matches the archive
2944 * construction behavior.
2946 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
2947 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
2948 if (is_flag_on(UPDATE_ERROR))
2949 return (BAM_ERROR);
2951 * Some files may not exist.
2952 * For example: etc/rtc_config on a x86 diskless system
2953 * Emit verbose message only
2955 if (bam_verbose)
2956 bam_print(NFTW_FAIL, path, strerror(errno));
2960 return (BAM_SUCCESS);
2964 * Update the timestamp file.
2966 static void
2967 update_timestamp(char *root)
2969 char timestamp_path[PATH_MAX];
2971 /* this path length has already been checked in check_flags_and_files */
2972 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2973 FILE_STAT_TIMESTAMP);
2976 * recreate the timestamp file. Since an outdated or absent timestamp
2977 * file translates in a complete rebuild of the archive cache, notify
2978 * the user of the performance issue.
2980 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
2981 bam_error(OPEN_FAIL, timestamp_path, strerror(errno));
2982 bam_error(TIMESTAMP_FAIL);
2987 static void
2988 savenew(char *root)
2990 char path[PATH_MAX];
2991 char path2[PATH_MAX];
2992 size_t sz;
2993 char *nstat;
2994 int fd, wrote, error;
2996 nstat = NULL;
2997 sz = 0;
2998 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
2999 NV_ENCODE_XDR, 0);
3000 if (error) {
3001 bam_error(PACK_FAIL, strerror(error));
3002 return;
3005 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3006 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3007 if (fd == -1) {
3008 bam_error(OPEN_FAIL, path, strerror(errno));
3009 free(nstat);
3010 return;
3012 wrote = write(fd, nstat, sz);
3013 if (wrote != sz) {
3014 bam_error(WRITE_FAIL, path, strerror(errno));
3015 (void) close(fd);
3016 free(nstat);
3017 return;
3019 (void) close(fd);
3020 free(nstat);
3022 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3023 if (rename(path, path2) != 0) {
3024 bam_error(RENAME_FAIL, path2, strerror(errno));
3028 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3030 static void
3031 clear_walk_args(void)
3033 nvlist_free(walk_arg.old_nvlp);
3034 nvlist_free(walk_arg.new_nvlp);
3035 if (walk_arg.sparcfile)
3036 (void) fclose(walk_arg.sparcfile);
3037 walk_arg.old_nvlp = NULL;
3038 walk_arg.new_nvlp = NULL;
3039 walk_arg.sparcfile = NULL;
3043 * Returns:
3044 * 0 - no update necessary
3045 * 1 - update required.
3046 * BAM_ERROR (-1) - An error occurred
3048 * Special handling for check (-n):
3049 * ================================
3050 * The check (-n) option produces parseable output.
3051 * To do this, we suppress all stdout messages unrelated
3052 * to out of sync files.
3053 * All stderr messages are still printed though.
3056 static int
3057 update_required(char *root)
3059 struct stat sb;
3060 char path[PATH_MAX];
3061 filelist_t flist;
3062 filelist_t *flistp = &flist;
3063 int ret;
3065 flistp->head = flistp->tail = NULL;
3067 if (is_sparc())
3068 set_flag(IS_SPARC_TARGET);
3071 * Check if cache directories and archives are present
3074 ret = check_flags_and_files(root);
3075 if (ret < 0)
3076 return (BAM_ERROR);
3079 * In certain deployment scenarios, filestat may not
3080 * exist. Do not stop the boot process, but trigger an update
3081 * of the archives (which will recreate filestat.ramdisk).
3083 if (bam_smf_check) {
3084 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3085 if (stat(path, &sb) != 0) {
3086 (void) creat(NEED_UPDATE_FILE, 0644);
3087 return (0);
3091 getoldstat(root);
3094 * Check if the archive contains files that are no longer
3095 * present on the root filesystem.
3097 check4stale(root);
3100 * read list of files
3102 if (read_list(root, flistp) != BAM_SUCCESS) {
3103 clear_walk_args();
3104 return (BAM_ERROR);
3107 assert(flistp->head && flistp->tail);
3110 * At this point either the update is required
3111 * or the decision is pending. In either case
3112 * we need to create new stat nvlist
3114 create_newstat();
3116 * This walk does 2 things:
3117 * - gets new stat data for every file
3118 * - (optional) compare old and new stat data
3120 ret = walk_list(root, &flist);
3122 /* done with the file list */
3123 filelist_free(flistp);
3125 /* something went wrong */
3127 if (ret == BAM_ERROR) {
3128 bam_error(CACHE_FAIL);
3129 return (BAM_ERROR);
3132 if (walk_arg.new_nvlp == NULL) {
3133 if (walk_arg.sparcfile != NULL)
3134 (void) fclose(walk_arg.sparcfile);
3135 bam_error(NO_NEW_STAT);
3138 /* If nothing was updated, discard newstat. */
3140 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
3141 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
3142 clear_walk_args();
3143 return (0);
3146 if (walk_arg.sparcfile != NULL)
3147 (void) fclose(walk_arg.sparcfile);
3149 return (1);
3152 static int
3153 flushfs(char *root)
3155 char cmd[PATH_MAX + 30];
3157 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3158 LOCKFS_PATH, root);
3160 return (exec_cmd(cmd, NULL));
3163 static int
3164 do_archive_copy(char *source, char *dest)
3167 sync();
3169 /* the equivalent of mv archive-new-$pid boot_archive */
3170 if (rename(source, dest) != 0) {
3171 (void) unlink(source);
3172 return (BAM_ERROR);
3175 if (flushfs(bam_root) != 0)
3176 sync();
3178 return (BAM_SUCCESS);
3181 static int
3182 check_cmdline(filelist_t flist)
3184 line_t *lp;
3186 for (lp = flist.head; lp; lp = lp->next) {
3187 if (strstr(lp->line, "Error:") != NULL ||
3188 strstr(lp->line, "Inode number overflow") != NULL) {
3189 (void) fprintf(stderr, "%s\n", lp->line);
3190 return (BAM_ERROR);
3194 return (BAM_SUCCESS);
3197 static void
3198 dump_errormsg(filelist_t flist)
3200 line_t *lp;
3202 for (lp = flist.head; lp; lp = lp->next)
3203 (void) fprintf(stderr, "%s\n", lp->line);
3206 static int
3207 check_archive(char *dest)
3209 struct stat sb;
3211 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3212 sb.st_size < 10000) {
3213 bam_error(ARCHIVE_BAD, dest);
3214 (void) unlink(dest);
3215 return (BAM_ERROR);
3218 return (BAM_SUCCESS);
3221 static boolean_t
3222 is_be(char *root)
3224 zfs_handle_t *zhp;
3225 libzfs_handle_t *hdl;
3226 be_node_list_t *be_nodes = NULL;
3227 be_node_list_t *cur_be;
3228 boolean_t be_exist = B_FALSE;
3229 char ds_path[ZFS_MAX_DATASET_NAME_LEN];
3231 if (!is_zfs(root))
3232 return (B_FALSE);
3234 * Get dataset for mountpoint
3236 if ((hdl = libzfs_init()) == NULL)
3237 return (B_FALSE);
3239 if ((zhp = zfs_path_to_zhandle(hdl, root,
3240 ZFS_TYPE_FILESYSTEM)) == NULL) {
3241 libzfs_fini(hdl);
3242 return (B_FALSE);
3245 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3248 * Check if the current dataset is BE
3250 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
3251 for (cur_be = be_nodes; cur_be != NULL;
3252 cur_be = cur_be->be_next_node) {
3255 * Because we guarantee that cur_be->be_root_ds
3256 * is null-terminated by internal data structure,
3257 * we can safely use strcmp()
3259 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3260 be_exist = B_TRUE;
3261 break;
3264 be_free_list(be_nodes);
3266 zfs_close(zhp);
3267 libzfs_fini(hdl);
3269 return (be_exist);
3273 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
3275 static int
3276 is_mkisofs()
3278 if (access(MKISOFS_PATH, X_OK) == 0)
3279 return (1);
3280 return (0);
3283 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3285 static int
3286 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3288 int ret;
3289 char cmdline[3 * PATH_MAX + 64];
3290 filelist_t flist = {0};
3291 const char *func = "create_sparc_archive()";
3293 if (access(bootblk, R_OK) == 1) {
3294 bam_error(BOOTBLK_FAIL, bootblk);
3295 return (BAM_ERROR);
3299 * Prepare mkisofs command line and execute it
3301 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3302 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3303 tempname, list);
3305 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3307 ret = exec_cmd(cmdline, &flist);
3308 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3309 dump_errormsg(flist);
3310 goto out_err;
3313 filelist_free(&flist);
3316 * Prepare dd command line to copy the bootblk on the new archive and
3317 * execute it
3319 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3320 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3321 bootblk, tempname);
3323 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3325 ret = exec_cmd(cmdline, &flist);
3326 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3327 goto out_err;
3329 filelist_free(&flist);
3331 /* Did we get a valid archive ? */
3332 if (check_archive(tempname) == BAM_ERROR)
3333 return (BAM_ERROR);
3335 return (do_archive_copy(tempname, archive));
3337 out_err:
3338 filelist_free(&flist);
3339 bam_error(ARCHIVE_FAIL, cmdline);
3340 (void) unlink(tempname);
3341 return (BAM_ERROR);
3344 static unsigned int
3345 from_733(unsigned char *s)
3347 int i;
3348 unsigned int ret = 0;
3350 for (i = 0; i < 4; i++)
3351 ret |= s[i] << (8 * i);
3353 return (ret);
3356 static void
3357 to_733(unsigned char *s, unsigned int val)
3359 int i;
3361 for (i = 0; i < 4; i++)
3362 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3366 * Extends the current boot archive without recreating it from scratch
3368 static int
3369 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3371 int fd = -1, newfd = -1, ret, i;
3372 int next_session = 0, new_size = 0;
3373 char cmdline[3 * PATH_MAX + 64];
3374 const char *func = "extend_iso_archive()";
3375 filelist_t flist = {0};
3376 struct iso_pdesc saved_desc[MAX_IVDs];
3378 fd = open(archive, O_RDWR);
3379 if (fd == -1) {
3380 if (bam_verbose)
3381 bam_error(OPEN_FAIL, archive, strerror(errno));
3382 goto out_err;
3386 * A partial read is likely due to a corrupted file
3388 ret = pread64(fd, saved_desc, sizeof (saved_desc),
3389 VOLDESC_OFF * CD_BLOCK);
3390 if (ret != sizeof (saved_desc)) {
3391 if (bam_verbose)
3392 bam_error(READ_FAIL, archive, strerror(errno));
3393 goto out_err;
3396 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3397 if (bam_verbose)
3398 bam_error(SIGN_FAIL, archive);
3399 goto out_err;
3403 * Read primary descriptor and locate next_session offset (it should
3404 * point to the end of the archive)
3406 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3408 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3409 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3410 MKISO_PARAMS, tempname, update_dir);
3412 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3414 ret = exec_cmd(cmdline, &flist);
3415 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3416 if (bam_verbose) {
3417 bam_error(MULTI_FAIL, cmdline);
3418 dump_errormsg(flist);
3420 goto out_flist_err;
3422 filelist_free(&flist);
3424 newfd = open(tempname, O_RDONLY);
3425 if (newfd == -1) {
3426 if (bam_verbose)
3427 bam_error(OPEN_FAIL, archive, strerror(errno));
3428 goto out_err;
3431 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3432 VOLDESC_OFF * CD_BLOCK);
3433 if (ret != sizeof (saved_desc)) {
3434 if (bam_verbose)
3435 bam_error(READ_FAIL, archive, strerror(errno));
3436 goto out_err;
3439 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3440 if (bam_verbose)
3441 bam_error(SIGN_FAIL, archive);
3442 goto out_err;
3445 new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3446 to_733(saved_desc[0].volume_space_size, new_size);
3448 for (i = 1; i < MAX_IVDs; i++) {
3449 if (saved_desc[i].type[0] == (unsigned char)255)
3450 break;
3451 if (memcmp(saved_desc[i].id, "CD001", 5))
3452 break;
3454 if (bam_verbose)
3455 bam_print("%s: Updating descriptor entry [%d]\n", func,
3458 to_733(saved_desc[i].volume_space_size, new_size);
3461 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3462 if (ret != DVD_BLOCK) {
3463 if (bam_verbose)
3464 bam_error(WRITE_FAIL, archive, strerror(errno));
3465 goto out_err;
3467 (void) close(newfd);
3468 newfd = -1;
3470 ret = fsync(fd);
3471 if (ret != 0)
3472 sync();
3474 ret = close(fd);
3475 if (ret != 0) {
3476 if (bam_verbose)
3477 bam_error(CLOSE_FAIL, archive, strerror(errno));
3478 return (BAM_ERROR);
3480 fd = -1;
3482 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3483 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3484 (next_session/16));
3486 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3488 ret = exec_cmd(cmdline, &flist);
3489 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3490 if (bam_verbose)
3491 bam_error(MULTI_FAIL, cmdline);
3492 goto out_flist_err;
3494 filelist_free(&flist);
3496 (void) unlink(tempname);
3498 if (flushfs(bam_root) != 0)
3499 sync();
3501 if (bam_verbose)
3502 bam_print("boot archive updated successfully\n");
3504 return (BAM_SUCCESS);
3506 out_flist_err:
3507 filelist_free(&flist);
3508 out_err:
3509 if (fd != -1)
3510 (void) close(fd);
3511 if (newfd != -1)
3512 (void) close(newfd);
3513 return (BAM_ERROR);
3516 static int
3517 create_x86_archive(char *archive, char *tempname, char *update_dir)
3519 int ret;
3520 char cmdline[3 * PATH_MAX + 64];
3521 filelist_t flist = {0};
3522 const char *func = "create_x86_archive()";
3524 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3525 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3527 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3529 ret = exec_cmd(cmdline, &flist);
3530 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3531 bam_error(ARCHIVE_FAIL, cmdline);
3532 dump_errormsg(flist);
3533 filelist_free(&flist);
3534 (void) unlink(tempname);
3535 return (BAM_ERROR);
3538 filelist_free(&flist);
3540 if (check_archive(tempname) == BAM_ERROR)
3541 return (BAM_ERROR);
3543 return (do_archive_copy(tempname, archive));
3546 static int
3547 mkisofs_archive(char *root, int what)
3549 int ret;
3550 char temp[PATH_MAX];
3551 char bootblk[PATH_MAX];
3552 char boot_archive[PATH_MAX];
3554 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3555 ret = snprintf(temp, sizeof (temp),
3556 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3557 get_machine(), getpid());
3558 else
3559 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3560 root, ARCHIVE_PREFIX, get_machine(), getpid());
3562 if (ret >= sizeof (temp))
3563 goto out_path_err;
3565 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3566 ret = snprintf(boot_archive, sizeof (boot_archive),
3567 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3568 ARCHIVE_SUFFIX);
3569 else
3570 ret = snprintf(boot_archive, sizeof (boot_archive),
3571 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3572 ARCHIVE_SUFFIX);
3574 if (ret >= sizeof (boot_archive))
3575 goto out_path_err;
3577 bam_print("updating %s\n", boot_archive);
3579 if (is_flag_on(IS_SPARC_TARGET)) {
3580 ret = snprintf(bootblk, sizeof (bootblk),
3581 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3582 if (ret >= sizeof (bootblk))
3583 goto out_path_err;
3585 ret = create_sparc_archive(boot_archive, temp, bootblk,
3586 get_cachedir(what));
3587 } else {
3588 if (!is_dir_flag_on(what, NO_MULTI)) {
3589 if (bam_verbose)
3590 bam_print("Attempting to extend x86 archive: "
3591 "%s\n", boot_archive);
3593 ret = extend_iso_archive(boot_archive, temp,
3594 get_updatedir(what));
3595 if (ret == BAM_SUCCESS) {
3596 if (bam_verbose)
3597 bam_print("Successfully extended %s\n",
3598 boot_archive);
3600 (void) rmdir_r(get_updatedir(what));
3601 return (BAM_SUCCESS);
3605 * The boot archive will be recreated from scratch. We get here
3606 * if at least one of these conditions is true:
3607 * - bootadm was called without the -e switch
3608 * - the archive (or the archive cache) doesn't exist
3609 * - archive size is bigger than BA_SIZE_MAX
3610 * - more than COUNT_MAX files need to be updated
3611 * - an error occourred either populating the /updates directory
3612 * or extend_iso_archive() failed
3614 if (bam_verbose)
3615 bam_print("Unable to extend %s... rebuilding archive\n",
3616 boot_archive);
3618 if (get_updatedir(what)[0] != '\0')
3619 (void) rmdir_r(get_updatedir(what));
3622 ret = create_x86_archive(boot_archive, temp,
3623 get_cachedir(what));
3626 if (ret == BAM_SUCCESS && bam_verbose)
3627 bam_print("Successfully created %s\n", boot_archive);
3629 return (ret);
3631 out_path_err:
3632 bam_error(PATH_TOO_LONG, root);
3633 return (BAM_ERROR);
3636 static error_t
3637 create_ramdisk(char *root)
3639 char *cmdline, path[PATH_MAX];
3640 size_t len;
3641 struct stat sb;
3642 int ret, what, status = BAM_SUCCESS;
3644 /* If there is mkisofs, use it to create the required archives */
3645 if (is_mkisofs()) {
3646 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3647 if (has_cachedir(what) && is_dir_flag_on(what,
3648 NEED_UPDATE)) {
3649 ret = mkisofs_archive(root, what);
3650 if (ret != 0)
3651 status = BAM_ERROR;
3654 return (status);
3658 * Else setup command args for create_ramdisk.ksh for the UFS archives
3660 if (bam_verbose)
3661 bam_print("mkisofs not found, creating UFS archive\n");
3663 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3664 if (stat(path, &sb) != 0) {
3665 bam_error(ARCH_EXEC_MISS, path, strerror(errno));
3666 return (BAM_ERROR);
3669 if (is_safe_exec(path) == BAM_ERROR)
3670 return (BAM_ERROR);
3672 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3673 if (bam_alt_platform)
3674 len += strlen(bam_platform) + strlen("-p ");
3675 cmdline = s_calloc(1, len);
3677 if (bam_alt_platform) {
3678 assert(strlen(root) > 1);
3679 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3680 path, bam_platform, root);
3681 /* chop off / at the end */
3682 cmdline[strlen(cmdline) - 1] = '\0';
3683 } else if (strlen(root) > 1) {
3684 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3685 /* chop off / at the end */
3686 cmdline[strlen(cmdline) - 1] = '\0';
3687 } else
3688 (void) snprintf(cmdline, len, "%s", path);
3690 if (exec_cmd(cmdline, NULL) != 0) {
3691 bam_error(ARCHIVE_FAIL, cmdline);
3692 free(cmdline);
3693 return (BAM_ERROR);
3695 free(cmdline);
3697 * The existence of the expected archives used to be
3698 * verified here. This check is done in create_ramdisk as
3699 * it needs to be in sync with the altroot operated upon.
3701 return (BAM_SUCCESS);
3705 * Checks if target filesystem is on a ramdisk
3706 * 1 - is miniroot
3707 * 0 - is not
3708 * When in doubt assume it is not a ramdisk.
3710 static int
3711 is_ramdisk(char *root)
3713 struct extmnttab mnt;
3714 FILE *fp;
3715 int found;
3716 char mntpt[PATH_MAX];
3717 char *cp;
3720 * There are 3 situations where creating archive is
3721 * of dubious value:
3722 * - create boot_archive on a lofi-mounted boot_archive
3723 * - create it on a ramdisk which is the root filesystem
3724 * - create it on a ramdisk mounted somewhere else
3725 * The first is not easy to detect and checking for it is not
3726 * worth it.
3727 * The other two conditions are handled here
3729 fp = fopen(MNTTAB, "r");
3730 if (fp == NULL) {
3731 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3732 return (0);
3735 resetmnttab(fp);
3738 * Remove any trailing / from the mount point
3740 (void) strlcpy(mntpt, root, sizeof (mntpt));
3741 if (strcmp(root, "/") != 0) {
3742 cp = mntpt + strlen(mntpt) - 1;
3743 if (*cp == '/')
3744 *cp = '\0';
3746 found = 0;
3747 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3748 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3749 found = 1;
3750 break;
3754 if (!found) {
3755 if (bam_verbose)
3756 bam_error(NOT_IN_MNTTAB, mntpt);
3757 (void) fclose(fp);
3758 return (0);
3761 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3762 strlen(RAMDISK_SPECIAL)) == 0) {
3763 if (bam_verbose)
3764 bam_error(IS_RAMDISK, bam_root);
3765 (void) fclose(fp);
3766 return (1);
3769 (void) fclose(fp);
3771 return (0);
3774 static int
3775 is_boot_archive(char *root)
3777 char path[PATH_MAX];
3778 struct stat sb;
3779 int error;
3780 const char *fcn = "is_boot_archive()";
3783 * We can't create an archive without the create_ramdisk script
3785 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3786 error = stat(path, &sb);
3787 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3788 if (error == -1) {
3789 if (bam_verbose)
3790 bam_print(FILE_MISS, path);
3791 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
3792 return (0);
3795 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
3796 return (1);
3800 * Need to call this for anything that operates on the GRUB menu
3801 * In the x86 live upgrade case the directory /boot/grub may be present
3802 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3803 * is to check for the presence of the stage2 binary which is present
3804 * only on GRUB targets (even on x86 boot partitions). Checking for the
3805 * presence of the multiboot binary is not correct as it is not present
3806 * on x86 boot partitions.
3809 is_grub(const char *root)
3811 char path[PATH_MAX];
3812 struct stat sb;
3813 const char *fcn = "is_grub()";
3815 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3816 if (stat(path, &sb) == -1) {
3817 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
3818 return (0);
3821 return (1);
3824 static int
3825 is_zfs(char *root)
3827 struct statvfs vfs;
3828 int ret;
3829 const char *fcn = "is_zfs()";
3831 ret = statvfs(root, &vfs);
3832 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3833 if (ret != 0) {
3834 bam_error(STATVFS_FAIL, root, strerror(errno));
3835 return (0);
3838 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3839 BAM_DPRINTF((D_IS_ZFS, fcn, root));
3840 return (1);
3841 } else {
3842 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
3843 return (0);
3847 static int
3848 is_ufs(char *root)
3850 struct statvfs vfs;
3851 int ret;
3852 const char *fcn = "is_ufs()";
3854 ret = statvfs(root, &vfs);
3855 INJECT_ERROR1("STATVFS_UFS", ret = 1);
3856 if (ret != 0) {
3857 bam_error(STATVFS_FAIL, root, strerror(errno));
3858 return (0);
3861 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
3862 BAM_DPRINTF((D_IS_UFS, fcn, root));
3863 return (1);
3864 } else {
3865 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
3866 return (0);
3870 static int
3871 is_pcfs(char *root)
3873 struct statvfs vfs;
3874 int ret;
3875 const char *fcn = "is_pcfs()";
3877 ret = statvfs(root, &vfs);
3878 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
3879 if (ret != 0) {
3880 bam_error(STATVFS_FAIL, root, strerror(errno));
3881 return (0);
3884 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
3885 BAM_DPRINTF((D_IS_PCFS, fcn, root));
3886 return (1);
3887 } else {
3888 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
3889 return (0);
3893 static int
3894 is_readonly(char *root)
3896 int fd;
3897 int error;
3898 char testfile[PATH_MAX];
3899 const char *fcn = "is_readonly()";
3902 * Using statvfs() to check for a read-only filesystem is not
3903 * reliable. The only way to reliably test is to attempt to
3904 * create a file
3906 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
3907 root, BOOTADM_RDONLY_TEST, getpid());
3909 (void) unlink(testfile);
3911 errno = 0;
3912 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
3913 error = errno;
3914 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
3915 if (fd == -1 && error == EROFS) {
3916 BAM_DPRINTF((D_RDONLY_FS, fcn, root));
3917 return (1);
3918 } else if (fd == -1) {
3919 bam_error(RDONLY_TEST_ERROR, root, strerror(error));
3922 (void) close(fd);
3923 (void) unlink(testfile);
3925 BAM_DPRINTF((D_RDWR_FS, fcn, root));
3926 return (0);
3929 static error_t
3930 update_archive(char *root, char *opt)
3932 error_t ret;
3934 assert(root);
3935 assert(opt == NULL);
3937 init_walk_args();
3938 (void) umask(022);
3941 * Never update non-BE root in update_all
3943 if (!is_be(root) && bam_update_all)
3944 return (BAM_SUCCESS);
3946 * root must belong to a boot archive based OS,
3948 if (!is_boot_archive(root)) {
3950 * Emit message only if not in context of update_all.
3951 * If in update_all, emit only if verbose flag is set.
3953 if (!bam_update_all || bam_verbose)
3954 bam_print(NOT_ARCHIVE_BOOT, root);
3955 return (BAM_ERROR);
3959 * If smf check is requested when / is writable (can happen
3960 * on first reboot following an upgrade because service
3961 * dependency is messed up), skip the check.
3963 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
3964 return (BAM_SUCCESS);
3967 * Don't generate archive on ramdisk.
3969 if (is_ramdisk(root))
3970 return (BAM_SUCCESS);
3973 * root must be writable. This check applies to alternate
3974 * root (-R option); bam_root_readonly applies to '/' only.
3975 * The behaviour translates into being the one of a 'check'.
3977 if (!bam_smf_check && !bam_check && is_readonly(root)) {
3978 set_flag(RDONLY_FSCHK);
3979 bam_check = 1;
3983 * Now check if an update is really needed.
3985 ret = update_required(root);
3988 * The check command (-n) is *not* a dry run.
3989 * It only checks if the archive is in sync.
3990 * A readonly filesystem has to be considered an error only if an update
3991 * is required.
3993 if (bam_nowrite()) {
3994 if (is_flag_on(RDONLY_FSCHK)) {
3995 bam_check = bam_saved_check;
3996 if (ret > 0)
3997 bam_error(RDONLY_FS, root);
3998 if (bam_update_all)
3999 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4002 bam_exit((ret != 0) ? 1 : 0);
4005 if (ret == 1) {
4006 /* create the ramdisk */
4007 ret = create_ramdisk(root);
4011 * if the archive is updated, save the new stat data and update the
4012 * timestamp file
4014 if (ret == 0 && walk_arg.new_nvlp != NULL) {
4015 savenew(root);
4016 update_timestamp(root);
4019 clear_walk_args();
4021 return (ret);
4024 static char *
4025 find_root_pool()
4027 char *special = get_special("/");
4028 char *p;
4030 if (special == NULL)
4031 return (NULL);
4033 if (*special == '/') {
4034 free(special);
4035 return (NULL);
4038 if ((p = strchr(special, '/')) != NULL)
4039 *p = '\0';
4041 return (special);
4044 static error_t
4045 synchronize_BE_menu(void)
4047 struct stat sb;
4048 char cmdline[PATH_MAX];
4049 char cksum_line[PATH_MAX];
4050 filelist_t flist = {0};
4051 char *old_cksum_str;
4052 char *old_size_str;
4053 char *old_file;
4054 char *curr_cksum_str;
4055 char *curr_size_str;
4056 char *curr_file;
4057 char *pool = NULL;
4058 char *mntpt = NULL;
4059 zfs_mnted_t mnted;
4060 FILE *cfp;
4061 int found;
4062 int ret;
4063 const char *fcn = "synchronize_BE_menu()";
4065 BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
4067 /* Check if findroot enabled LU BE */
4068 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4069 BAM_DPRINTF((D_NOT_LU_BE, fcn));
4070 return (BAM_SUCCESS);
4073 if (stat(LU_MENU_CKSUM, &sb) != 0) {
4074 BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4075 goto menu_sync;
4078 cfp = fopen(LU_MENU_CKSUM, "r");
4079 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4080 if (cfp == NULL) {
4081 bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
4082 goto menu_sync;
4084 BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
4086 found = 0;
4087 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4088 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4089 if (found) {
4090 bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
4091 (void) fclose(cfp);
4092 goto menu_sync;
4094 found = 1;
4096 BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
4099 old_cksum_str = strtok(cksum_line, " \t");
4100 old_size_str = strtok(NULL, " \t");
4101 old_file = strtok(NULL, " \t");
4103 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4104 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4105 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4106 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4107 bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
4108 goto menu_sync;
4110 BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
4112 /* Get checksum of current menu */
4113 pool = find_root_pool();
4114 if (pool) {
4115 mntpt = mount_top_dataset(pool, &mnted);
4116 if (mntpt == NULL) {
4117 bam_error(FAIL_MNT_TOP_DATASET, pool);
4118 free(pool);
4119 return (BAM_ERROR);
4121 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4122 CKSUM, mntpt, GRUB_MENU);
4123 } else {
4124 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4125 CKSUM, GRUB_MENU);
4127 ret = exec_cmd(cmdline, &flist);
4128 if (pool) {
4129 (void) umount_top_dataset(pool, mnted, mntpt);
4130 free(pool);
4132 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4133 if (ret != 0) {
4134 bam_error(MENU_CKSUM_FAIL);
4135 return (BAM_ERROR);
4137 BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
4139 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4140 if ((flist.head == NULL) || (flist.head != flist.tail)) {
4141 bam_error(BAD_CKSUM);
4142 filelist_free(&flist);
4143 return (BAM_ERROR);
4145 BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
4147 curr_cksum_str = strtok(flist.head->line, " \t");
4148 curr_size_str = strtok(NULL, " \t");
4149 curr_file = strtok(NULL, " \t");
4151 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4152 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4153 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4154 if (curr_cksum_str == NULL || curr_size_str == NULL ||
4155 curr_file == NULL) {
4156 bam_error(BAD_CKSUM_PARSE);
4157 filelist_free(&flist);
4158 return (BAM_ERROR);
4160 BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
4162 if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4163 strcmp(old_size_str, curr_size_str) == 0 &&
4164 strcmp(old_file, curr_file) == 0) {
4165 filelist_free(&flist);
4166 BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
4167 return (BAM_SUCCESS);
4170 filelist_free(&flist);
4172 /* cksum doesn't match - the menu has changed */
4173 BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
4175 menu_sync:
4176 bam_print(PROP_GRUB_MENU);
4178 (void) snprintf(cmdline, sizeof (cmdline),
4179 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4180 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4181 ret = exec_cmd(cmdline, NULL);
4182 INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4183 if (ret != 0) {
4184 bam_error(MENU_PROP_FAIL);
4185 return (BAM_ERROR);
4187 BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
4189 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4190 GRUB_MENU, GRUB_BACKUP_MENU);
4191 ret = exec_cmd(cmdline, NULL);
4192 INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4193 if (ret != 0) {
4194 bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
4195 return (BAM_ERROR);
4197 BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
4199 (void) snprintf(cmdline, sizeof (cmdline),
4200 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4201 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4202 ret = exec_cmd(cmdline, NULL);
4203 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4204 if (ret != 0) {
4205 bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
4206 return (BAM_ERROR);
4208 BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
4210 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4211 CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4212 ret = exec_cmd(cmdline, NULL);
4213 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4214 if (ret != 0) {
4215 bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
4216 return (BAM_ERROR);
4218 BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4220 (void) snprintf(cmdline, sizeof (cmdline),
4221 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4222 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4223 ret = exec_cmd(cmdline, NULL);
4224 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4225 if (ret != 0) {
4226 bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
4227 return (BAM_ERROR);
4229 BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4231 return (BAM_SUCCESS);
4234 static error_t
4235 update_all(char *root, char *opt)
4237 struct extmnttab mnt;
4238 struct stat sb;
4239 FILE *fp;
4240 char multibt[PATH_MAX];
4241 char creatram[PATH_MAX];
4242 error_t ret = BAM_SUCCESS;
4244 assert(root);
4245 assert(opt == NULL);
4247 if (bam_rootlen != 1 || *root != '/') {
4248 elide_trailing_slash(root, multibt, sizeof (multibt));
4249 bam_error(ALT_ROOT_INVALID, multibt);
4250 return (BAM_ERROR);
4254 * First update archive for current root
4256 if (update_archive(root, opt) != BAM_SUCCESS)
4257 ret = BAM_ERROR;
4259 if (ret == BAM_ERROR)
4260 goto out;
4263 * Now walk the mount table, performing archive update
4264 * for all mounted Newboot root filesystems
4266 fp = fopen(MNTTAB, "r");
4267 if (fp == NULL) {
4268 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
4269 ret = BAM_ERROR;
4270 goto out;
4273 resetmnttab(fp);
4275 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4276 if (mnt.mnt_special == NULL)
4277 continue;
4278 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4279 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4280 continue;
4281 if (strcmp(mnt.mnt_mountp, "/") == 0)
4282 continue;
4284 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4285 mnt.mnt_mountp, CREATE_RAMDISK);
4287 if (stat(creatram, &sb) == -1)
4288 continue;
4291 * We put a trailing slash to be consistent with root = "/"
4292 * case, such that we don't have to print // in some cases.
4294 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4295 mnt.mnt_mountp);
4296 bam_rootlen = strlen(rootbuf);
4299 * It's possible that other mounts may be an alternate boot
4300 * architecture, so check it again.
4302 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4303 (update_archive(rootbuf, opt) != BAM_SUCCESS))
4304 ret = BAM_ERROR;
4307 (void) fclose(fp);
4309 out:
4311 * We no longer use biosdev for Live Upgrade. Hence
4312 * there is no need to defer (to shutdown time) any fdisk
4313 * updates
4315 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4316 bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
4320 * If user has updated menu in current BE, propagate the
4321 * updates to all BEs.
4323 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4324 ret = BAM_ERROR;
4326 return (ret);
4329 static void
4330 append_line(menu_t *mp, line_t *lp)
4332 if (mp->start == NULL) {
4333 mp->start = lp;
4334 } else {
4335 mp->end->next = lp;
4336 lp->prev = mp->end;
4338 mp->end = lp;
4341 void
4342 unlink_line(menu_t *mp, line_t *lp)
4344 /* unlink from list */
4345 if (lp->prev)
4346 lp->prev->next = lp->next;
4347 else
4348 mp->start = lp->next;
4349 if (lp->next)
4350 lp->next->prev = lp->prev;
4351 else
4352 mp->end = lp->prev;
4355 static entry_t *
4356 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4358 entry_t *ent, *prev;
4359 const char *fcn = "boot_entry_new()";
4361 assert(mp);
4362 assert(start);
4363 assert(end);
4365 ent = s_calloc(1, sizeof (entry_t));
4366 BAM_DPRINTF((D_ENTRY_NEW, fcn));
4367 ent->start = start;
4368 ent->end = end;
4370 if (mp->entries == NULL) {
4371 mp->entries = ent;
4372 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
4373 return (ent);
4376 prev = mp->entries;
4377 while (prev->next)
4378 prev = prev->next;
4379 prev->next = ent;
4380 ent->prev = prev;
4381 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
4382 return (ent);
4385 static void
4386 boot_entry_addline(entry_t *ent, line_t *lp)
4388 if (ent)
4389 ent->end = lp;
4393 * Check whether cmd matches the one indexed by which, and whether arg matches
4394 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4395 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4396 * strstr(), so it can be a partial match.
4398 static int
4399 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4401 int ret;
4402 const char *fcn = "check_cmd()";
4404 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
4406 if (cmd != NULL) {
4407 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4408 (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4409 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
4410 fcn, cmd, menu_cmds[which]));
4411 return (0);
4413 ret = (strstr(arg, str) != NULL);
4414 } else
4415 ret = 0;
4417 if (ret) {
4418 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
4419 } else {
4420 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
4423 return (ret);
4426 static error_t
4427 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4429 const char *fcn = "kernel_parser()";
4431 assert(entry);
4432 assert(cmd);
4433 assert(arg);
4435 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4436 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4437 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
4438 return (BAM_ERROR);
4441 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4442 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
4443 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4444 } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4445 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4446 BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
4447 entry->flags |= BAM_ENTRY_DBOOT;
4448 } else if (strncmp(arg, DIRECT_BOOT_64,
4449 sizeof (DIRECT_BOOT_64) - 1) == 0) {
4450 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
4451 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4452 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4453 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4454 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
4455 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4456 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4457 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4458 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg));
4459 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4460 | BAM_ENTRY_32BIT;
4461 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4462 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4463 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg));
4464 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4465 | BAM_ENTRY_64BIT;
4466 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4467 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
4468 entry->flags |= BAM_ENTRY_MULTIBOOT;
4469 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4470 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4471 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
4472 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4473 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4474 BAM_DPRINTF((D_SET_HV, fcn, arg));
4475 entry->flags |= BAM_ENTRY_HV;
4476 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4477 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
4478 return (BAM_ERROR);
4479 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4480 strstr(arg, UNIX_SPACE)) {
4481 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4482 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4483 strstr(arg, AMD_UNIX_SPACE)) {
4484 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4485 } else {
4486 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
4487 bam_error(UNKNOWN_KERNEL_LINE, linenum);
4488 return (BAM_ERROR);
4491 return (BAM_SUCCESS);
4494 static error_t
4495 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4497 const char *fcn = "module_parser()";
4499 assert(entry);
4500 assert(cmd);
4501 assert(arg);
4503 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4504 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4505 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
4506 return (BAM_ERROR);
4509 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4510 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4511 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4512 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4513 strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4514 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4515 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4516 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4517 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4518 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
4519 return (BAM_SUCCESS);
4520 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4521 !(entry->flags & BAM_ENTRY_LU)) {
4522 /* don't emit warning for hand entries */
4523 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
4524 return (BAM_ERROR);
4525 } else {
4526 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
4527 bam_error(UNKNOWN_MODULE_LINE, linenum);
4528 return (BAM_ERROR);
4533 * A line in menu.lst looks like
4534 * [ ]*<cmd>[ \t=]*<arg>*
4536 static void
4537 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4540 * save state across calls. This is so that
4541 * header gets the right entry# after title has
4542 * been processed
4544 static line_t *prev = NULL;
4545 static entry_t *curr_ent = NULL;
4546 static int in_liveupgrade = 0;
4547 static int is_libbe_ent = 0;
4549 line_t *lp;
4550 char *cmd, *sep, *arg;
4551 char save, *cp, *line;
4552 menu_flag_t flag = BAM_INVALID;
4553 const char *fcn = "line_parser()";
4555 cmd = NULL;
4556 if (str == NULL) {
4557 return;
4561 * First save a copy of the entire line.
4562 * We use this later to set the line field.
4564 line = s_strdup(str);
4566 /* Eat up leading whitespace */
4567 while (*str == ' ' || *str == '\t')
4568 str++;
4570 if (*str == '#') { /* comment */
4571 cmd = s_strdup("#");
4572 sep = NULL;
4573 arg = s_strdup(str + 1);
4574 flag = BAM_COMMENT;
4575 if (strstr(arg, BAM_LU_HDR) != NULL) {
4576 in_liveupgrade = 1;
4577 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4578 in_liveupgrade = 0;
4579 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4580 is_libbe_ent = 1;
4582 } else if (*str == '\0') { /* blank line */
4583 cmd = sep = arg = NULL;
4584 flag = BAM_EMPTY;
4585 } else {
4587 * '=' is not a documented separator in grub syntax.
4588 * However various development bits use '=' as a
4589 * separator. In addition, external users also
4590 * use = as a separator. So we will allow that usage.
4592 cp = str;
4593 while (*str != ' ' && *str != '\t' && *str != '=') {
4594 if (*str == '\0') {
4595 cmd = s_strdup(cp);
4596 sep = arg = NULL;
4597 break;
4599 str++;
4602 if (*str != '\0') {
4603 save = *str;
4604 *str = '\0';
4605 cmd = s_strdup(cp);
4606 *str = save;
4608 str++;
4609 save = *str;
4610 *str = '\0';
4611 sep = s_strdup(str - 1);
4612 *str = save;
4614 while (*str == ' ' || *str == '\t')
4615 str++;
4616 if (*str == '\0')
4617 arg = NULL;
4618 else
4619 arg = s_strdup(str);
4623 lp = s_calloc(1, sizeof (line_t));
4625 lp->cmd = cmd;
4626 lp->sep = sep;
4627 lp->arg = arg;
4628 lp->line = line;
4629 lp->lineNum = ++(*lineNum);
4630 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4631 lp->entryNum = ++(*entryNum);
4632 lp->flags = BAM_TITLE;
4633 if (prev && prev->flags == BAM_COMMENT &&
4634 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4635 prev->entryNum = lp->entryNum;
4636 curr_ent = boot_entry_new(mp, prev, lp);
4637 curr_ent->flags |= BAM_ENTRY_BOOTADM;
4638 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
4639 } else {
4640 curr_ent = boot_entry_new(mp, lp, lp);
4641 if (in_liveupgrade) {
4642 curr_ent->flags |= BAM_ENTRY_LU;
4643 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
4646 curr_ent->entryNum = *entryNum;
4647 } else if (flag != BAM_INVALID) {
4649 * For header comments, the entry# is "fixed up"
4650 * by the subsequent title
4652 lp->entryNum = *entryNum;
4653 lp->flags = flag;
4654 } else {
4655 lp->entryNum = *entryNum;
4657 if (*entryNum == ENTRY_INIT) {
4658 lp->flags = BAM_GLOBAL;
4659 } else {
4660 lp->flags = BAM_ENTRY;
4662 if (cmd && arg) {
4663 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4664 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
4665 curr_ent->flags |= BAM_ENTRY_ROOT;
4666 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4667 == 0) {
4668 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
4669 arg));
4670 curr_ent->flags |= BAM_ENTRY_FINDROOT;
4671 } else if (strcmp(cmd,
4672 menu_cmds[CHAINLOADER_CMD]) == 0) {
4673 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
4674 arg));
4675 curr_ent->flags |=
4676 BAM_ENTRY_CHAINLOADER;
4677 } else if (kernel_parser(curr_ent, cmd, arg,
4678 lp->lineNum) != BAM_SUCCESS) {
4679 (void) module_parser(curr_ent, cmd,
4680 arg, lp->lineNum);
4686 /* record default, old default, and entry line ranges */
4687 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4688 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4689 mp->curdefault = lp;
4690 } else if (lp->flags == BAM_COMMENT &&
4691 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4692 mp->olddefault = lp;
4693 } else if (lp->flags == BAM_COMMENT &&
4694 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4695 mp->old_rc_default = lp;
4696 } else if (lp->flags == BAM_ENTRY ||
4697 (lp->flags == BAM_COMMENT &&
4698 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4699 if (is_libbe_ent) {
4700 curr_ent->flags |= BAM_ENTRY_LIBBE;
4701 is_libbe_ent = 0;
4704 boot_entry_addline(curr_ent, lp);
4706 append_line(mp, lp);
4708 prev = lp;
4711 void
4712 update_numbering(menu_t *mp)
4714 int lineNum;
4715 int entryNum;
4716 int old_default_value;
4717 line_t *lp, *prev, *default_lp, *default_entry;
4718 char buf[PATH_MAX];
4720 if (mp->start == NULL) {
4721 return;
4724 lineNum = LINE_INIT;
4725 entryNum = ENTRY_INIT;
4726 old_default_value = ENTRY_INIT;
4727 lp = default_lp = default_entry = NULL;
4729 prev = NULL;
4730 for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4731 lp->lineNum = ++lineNum;
4734 * Get the value of the default command
4736 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4737 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4738 lp->arg) {
4739 old_default_value = atoi(lp->arg);
4740 default_lp = lp;
4744 * If not a booting entry, nothing else to fix for this
4745 * entry
4747 if (lp->entryNum == ENTRY_INIT)
4748 continue;
4751 * Record the position of the default entry.
4752 * The following works because global
4753 * commands like default and timeout should precede
4754 * actual boot entries, so old_default_value
4755 * is already known (or default cmd is missing).
4757 if (default_entry == NULL &&
4758 old_default_value != ENTRY_INIT &&
4759 lp->entryNum == old_default_value) {
4760 default_entry = lp;
4764 * Now fixup the entry number
4766 if (lp->cmd != NULL &&
4767 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4768 lp->entryNum = ++entryNum;
4769 /* fixup the bootadm header */
4770 if (prev && prev->flags == BAM_COMMENT &&
4771 prev->arg &&
4772 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4773 prev->entryNum = lp->entryNum;
4775 } else {
4776 lp->entryNum = entryNum;
4781 * No default command in menu, simply return
4783 if (default_lp == NULL) {
4784 return;
4787 free(default_lp->arg);
4788 free(default_lp->line);
4790 if (default_entry == NULL) {
4791 default_lp->arg = s_strdup("0");
4792 } else {
4793 (void) snprintf(buf, sizeof (buf), "%d",
4794 default_entry->entryNum);
4795 default_lp->arg = s_strdup(buf);
4799 * The following is required since only the line field gets
4800 * written back to menu.lst
4802 (void) snprintf(buf, sizeof (buf), "%s%s%s",
4803 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4804 default_lp->line = s_strdup(buf);
4808 static menu_t *
4809 menu_read(char *menu_path)
4811 FILE *fp;
4812 char buf[BAM_MAXLINE], *cp;
4813 menu_t *mp;
4814 int line, entry, len, n;
4816 mp = s_calloc(1, sizeof (menu_t));
4818 fp = fopen(menu_path, "r");
4819 if (fp == NULL) { /* Let the caller handle this error */
4820 free(mp);
4821 return (NULL);
4824 /* Note: GRUB boot entry number starts with 0 */
4825 line = LINE_INIT;
4826 entry = ENTRY_INIT;
4827 cp = buf;
4828 len = sizeof (buf);
4829 while (s_fgets(cp, len, fp) != NULL) {
4830 n = strlen(cp);
4831 if (cp[n - 1] == '\\') {
4832 len -= n - 1;
4833 assert(len >= 2);
4834 cp += n - 1;
4835 continue;
4837 line_parser(mp, buf, &line, &entry);
4838 cp = buf;
4839 len = sizeof (buf);
4842 if (fclose(fp) == EOF) {
4843 bam_error(CLOSE_FAIL, menu_path, strerror(errno));
4846 return (mp);
4849 static error_t
4850 selector(menu_t *mp, char *opt, int *entry, char **title)
4852 char *eq;
4853 char *opt_dup;
4854 int entryNum;
4856 assert(mp);
4857 assert(mp->start);
4858 assert(opt);
4860 opt_dup = s_strdup(opt);
4862 if (entry)
4863 *entry = ENTRY_INIT;
4864 if (title)
4865 *title = NULL;
4867 eq = strchr(opt_dup, '=');
4868 if (eq == NULL) {
4869 bam_error(INVALID_OPT, opt);
4870 free(opt_dup);
4871 return (BAM_ERROR);
4874 *eq = '\0';
4875 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
4876 assert(mp->end);
4877 entryNum = s_strtol(eq + 1);
4878 if (entryNum < 0 || entryNum > mp->end->entryNum) {
4879 bam_error(INVALID_ENTRY, eq + 1);
4880 free(opt_dup);
4881 return (BAM_ERROR);
4883 *entry = entryNum;
4884 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
4885 *title = opt + (eq - opt_dup) + 1;
4886 } else {
4887 bam_error(INVALID_OPT, opt);
4888 free(opt_dup);
4889 return (BAM_ERROR);
4892 free(opt_dup);
4893 return (BAM_SUCCESS);
4897 * If invoked with no titles/entries (opt == NULL)
4898 * only title lines in file are printed.
4900 * If invoked with a title or entry #, all
4901 * lines in *every* matching entry are listed
4903 static error_t
4904 list_entry(menu_t *mp, char *menu_path, char *opt)
4906 line_t *lp;
4907 int entry = ENTRY_INIT;
4908 int found;
4909 char *title = NULL;
4911 assert(mp);
4912 assert(menu_path);
4914 /* opt is optional */
4915 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
4916 opt ? opt : "<NULL>"));
4918 if (mp->start == NULL) {
4919 bam_error(NO_MENU, menu_path);
4920 return (BAM_ERROR);
4923 if (opt != NULL) {
4924 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
4925 return (BAM_ERROR);
4927 assert((entry != ENTRY_INIT) ^ (title != NULL));
4928 } else {
4929 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
4930 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
4933 found = 0;
4934 for (lp = mp->start; lp; lp = lp->next) {
4935 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
4936 continue;
4937 if (opt == NULL && lp->flags == BAM_TITLE) {
4938 bam_print(PRINT_TITLE, lp->entryNum,
4939 lp->arg);
4940 found = 1;
4941 continue;
4943 if (entry != ENTRY_INIT && lp->entryNum == entry) {
4944 bam_print(PRINT, lp->line);
4945 found = 1;
4946 continue;
4950 * We set the entry value here so that all lines
4951 * in entry get printed. If we subsequently match
4952 * title in other entries, all lines in those
4953 * entries get printed as well.
4955 if (title && lp->flags == BAM_TITLE && lp->arg &&
4956 strncmp(title, lp->arg, strlen(title)) == 0) {
4957 bam_print(PRINT, lp->line);
4958 entry = lp->entryNum;
4959 found = 1;
4960 continue;
4964 if (!found) {
4965 bam_error(NO_MATCH_ENTRY);
4966 return (BAM_ERROR);
4969 return (BAM_SUCCESS);
4973 add_boot_entry(menu_t *mp,
4974 char *title,
4975 char *findroot,
4976 char *kernel,
4977 char *mod_kernel,
4978 char *module,
4979 char *bootfs)
4981 int lineNum;
4982 int entryNum;
4983 char linebuf[BAM_MAXLINE];
4984 menu_cmd_t k_cmd;
4985 menu_cmd_t m_cmd;
4986 const char *fcn = "add_boot_entry()";
4988 assert(mp);
4990 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
4991 if (findroot == NULL) {
4992 bam_error(NULL_FINDROOT);
4993 return (BAM_ERROR);
4996 if (title == NULL) {
4997 title = "Solaris"; /* default to Solaris */
4999 if (kernel == NULL) {
5000 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
5001 return (BAM_ERROR);
5003 if (module == NULL) {
5004 if (bam_direct != BAM_DIRECT_DBOOT) {
5005 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
5006 return (BAM_ERROR);
5009 /* Figure the commands out from the kernel line */
5010 if (strstr(kernel, "$ISADIR") != NULL) {
5011 module = DIRECT_BOOT_ARCHIVE;
5012 } else if (strstr(kernel, "amd64") != NULL) {
5013 module = DIRECT_BOOT_ARCHIVE_64;
5014 } else {
5015 module = DIRECT_BOOT_ARCHIVE_32;
5019 k_cmd = KERNEL_DOLLAR_CMD;
5020 m_cmd = MODULE_DOLLAR_CMD;
5022 if (mp->start) {
5023 lineNum = mp->end->lineNum;
5024 entryNum = mp->end->entryNum;
5025 } else {
5026 lineNum = LINE_INIT;
5027 entryNum = ENTRY_INIT;
5031 * No separator for comment (HDR/FTR) commands
5032 * The syntax for comments is #<comment>
5034 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5035 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5036 line_parser(mp, linebuf, &lineNum, &entryNum);
5038 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5039 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5040 line_parser(mp, linebuf, &lineNum, &entryNum);
5042 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5043 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5044 line_parser(mp, linebuf, &lineNum, &entryNum);
5045 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
5047 if (bootfs != NULL) {
5048 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5049 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5050 line_parser(mp, linebuf, &lineNum, &entryNum);
5053 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5054 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5055 line_parser(mp, linebuf, &lineNum, &entryNum);
5057 if (mod_kernel != NULL) {
5058 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5059 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5060 line_parser(mp, linebuf, &lineNum, &entryNum);
5063 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5064 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5065 line_parser(mp, linebuf, &lineNum, &entryNum);
5067 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5068 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5069 line_parser(mp, linebuf, &lineNum, &entryNum);
5071 return (entryNum);
5074 error_t
5075 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5077 line_t *lp;
5078 line_t *freed;
5079 entry_t *ent;
5080 entry_t *tmp;
5081 int deleted = 0;
5082 const char *fcn = "delete_boot_entry()";
5084 assert(entryNum != ENTRY_INIT);
5086 tmp = NULL;
5088 ent = mp->entries;
5089 while (ent) {
5090 lp = ent->start;
5093 * Check entry number and make sure it's a modifiable entry.
5095 * Guidelines:
5096 * + We can modify a bootadm-created entry
5097 * + We can modify a libbe-created entry
5099 if ((lp->flags != BAM_COMMENT &&
5100 (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5101 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5102 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5103 ent = ent->next;
5104 continue;
5107 /* free the entry content */
5108 do {
5109 freed = lp;
5110 lp = lp->next; /* prev stays the same */
5111 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
5112 unlink_line(mp, freed);
5113 line_free(freed);
5114 } while (freed != ent->end);
5116 /* free the entry_t structure */
5117 assert(tmp == NULL);
5118 tmp = ent;
5119 ent = ent->next;
5120 if (tmp->prev)
5121 tmp->prev->next = ent;
5122 else
5123 mp->entries = ent;
5124 if (ent)
5125 ent->prev = tmp->prev;
5126 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
5127 free(tmp);
5128 tmp = NULL;
5129 deleted = 1;
5132 assert(tmp == NULL);
5134 if (!deleted && entryNum != ALL_ENTRIES) {
5135 if (quiet == DBE_PRINTERR)
5136 bam_error(NO_BOOTADM_MATCH);
5137 return (BAM_ERROR);
5141 * Now that we have deleted an entry, update
5142 * the entry numbering and the default cmd.
5144 update_numbering(mp);
5146 return (BAM_SUCCESS);
5149 static error_t
5150 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5152 assert(mp);
5153 assert(dummy == NULL);
5154 assert(opt == NULL);
5156 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
5158 if (mp->start == NULL) {
5159 bam_print(EMPTY_MENU);
5160 return (BAM_SUCCESS);
5163 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5164 return (BAM_ERROR);
5167 return (BAM_WRITE);
5170 static FILE *
5171 create_diskmap(char *osroot)
5173 FILE *fp;
5174 char cmd[PATH_MAX + 16];
5175 char path[PATH_MAX];
5176 const char *fcn = "create_diskmap()";
5178 /* make sure we have a map file */
5179 fp = fopen(GRUBDISK_MAP, "r");
5180 if (fp == NULL) {
5181 int ret;
5183 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5184 CREATE_DISKMAP);
5185 if (ret >= sizeof (path)) {
5186 bam_error(PATH_TOO_LONG, osroot);
5187 return (NULL);
5189 if (is_safe_exec(path) == BAM_ERROR)
5190 return (NULL);
5192 (void) snprintf(cmd, sizeof (cmd),
5193 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5194 if (exec_cmd(cmd, NULL) != 0)
5195 return (NULL);
5196 fp = fopen(GRUBDISK_MAP, "r");
5197 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5198 if (fp) {
5199 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
5200 } else {
5201 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
5204 return (fp);
5207 #define SECTOR_SIZE 512
5209 static int
5210 get_partition(char *device)
5212 int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5213 struct mboot *mboot;
5214 char boot_sect[SECTOR_SIZE];
5215 char *wholedisk, *slice;
5216 #ifdef i386
5217 ext_part_t *epp;
5218 uint32_t secnum, numsec;
5219 int rval, pno, ext_partno = PARTNO_NOTFOUND;
5220 #endif
5222 /* form whole disk (p0) */
5223 slice = device + strlen(device) - 2;
5224 is_pcfs = (*slice != 's');
5225 if (!is_pcfs)
5226 *slice = '\0';
5227 wholedisk = s_calloc(1, strlen(device) + 3);
5228 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5229 if (!is_pcfs)
5230 *slice = 's';
5232 /* read boot sector */
5233 fd = open(wholedisk, O_RDONLY);
5234 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5235 return (partno);
5237 (void) close(fd);
5239 #ifdef i386
5240 /* Read/Initialize extended partition information */
5241 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5242 != FDISK_SUCCESS) {
5243 switch (rval) {
5245 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5246 * be considered as soft errors and hence
5247 * we do not return
5249 case FDISK_EBADLOGDRIVE:
5250 break;
5251 case FDISK_ENOLOGDRIVE:
5252 break;
5253 case FDISK_EBADMAGIC:
5254 /*FALLTHROUGH*/
5255 default:
5256 free(wholedisk);
5257 libfdisk_fini(&epp);
5258 return (partno);
5261 #endif
5262 free(wholedisk);
5264 /* parse fdisk table */
5265 mboot = (struct mboot *)((void *)boot_sect);
5266 for (i = 0; i < FD_NUMPART; i++) {
5267 struct ipart *part =
5268 (struct ipart *)(uintptr_t)mboot->parts + i;
5269 if (is_pcfs) { /* looking for solaris boot part */
5270 if (part->systid == 0xbe) {
5271 partno = i;
5272 break;
5274 } else { /* look for solaris partition, old and new */
5275 if (part->systid == EFI_PMBR) {
5276 partno = PARTNO_EFI;
5277 break;
5280 #ifdef i386
5281 if ((part->systid == SUNIXOS &&
5282 (fdisk_is_linux_swap(epp, part->relsect,
5283 NULL) != 0)) || part->systid == SUNIXOS2) {
5284 #else
5285 if (part->systid == SUNIXOS ||
5286 part->systid == SUNIXOS2) {
5287 #endif
5288 partno = i;
5289 break;
5292 #ifdef i386
5293 if (fdisk_is_dos_extended(part->systid))
5294 ext_partno = i;
5295 #endif
5298 #ifdef i386
5299 /* If no primary solaris partition, check extended partition */
5300 if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5301 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5302 if (rval == FDISK_SUCCESS) {
5303 partno = pno - 1;
5306 libfdisk_fini(&epp);
5307 #endif
5308 return (partno);
5311 char *
5312 get_grubroot(char *osroot, char *osdev, char *menu_root)
5314 char *grubroot; /* (hd#,#,#) */
5315 char *slice;
5316 char *grubhd = NULL;
5317 int fdiskpart;
5318 int found = 0;
5319 char *devname;
5320 char *ctdname = strstr(osdev, "dsk/");
5321 char linebuf[PATH_MAX];
5322 FILE *fp;
5324 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5325 if (ctdname == NULL) {
5326 bam_error(INVALID_DEV_DSK, osdev);
5327 return (NULL);
5330 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5331 /* menu bears no resemblance to our reality */
5332 bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev);
5333 return (NULL);
5336 ctdname += strlen("dsk/");
5337 slice = strrchr(ctdname, 's');
5338 if (slice)
5339 *slice = '\0';
5341 fp = create_diskmap(osroot);
5342 if (fp == NULL) {
5343 bam_error(DISKMAP_FAIL, osroot);
5344 return (NULL);
5347 rewind(fp);
5348 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5349 grubhd = strtok(linebuf, " \t\n");
5350 if (grubhd)
5351 devname = strtok(NULL, " \t\n");
5352 else
5353 devname = NULL;
5354 if (devname && strcmp(devname, ctdname) == 0) {
5355 found = 1;
5356 break;
5360 if (slice)
5361 *slice = 's';
5363 (void) fclose(fp);
5364 fp = NULL;
5366 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5367 if (found == 0) {
5368 bam_error(BIOSDEV_SKIP, osdev);
5369 return (NULL);
5372 fdiskpart = get_partition(osdev);
5373 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5374 if (fdiskpart == PARTNO_NOTFOUND) {
5375 bam_error(FDISKPART_FAIL, osdev);
5376 return (NULL);
5379 grubroot = s_calloc(1, 10);
5380 if (fdiskpart == PARTNO_EFI) {
5381 fdiskpart = atoi(&slice[1]);
5382 slice = NULL;
5385 if (slice) {
5386 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5387 grubhd, fdiskpart, slice[1] + 'a' - '0');
5388 } else
5389 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5390 grubhd, fdiskpart);
5392 assert(fp == NULL);
5393 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5394 return (grubroot);
5397 static char *
5398 find_primary_common(char *mntpt, char *fstype)
5400 char signdir[PATH_MAX];
5401 char tmpsign[MAXNAMELEN + 1];
5402 char *lu;
5403 char *ufs;
5404 char *zfs;
5405 DIR *dirp = NULL;
5406 struct dirent *entp;
5407 struct stat sb;
5408 const char *fcn = "find_primary_common()";
5410 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5411 mntpt, GRUBSIGN_DIR);
5413 if (stat(signdir, &sb) == -1) {
5414 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
5415 return (NULL);
5418 dirp = opendir(signdir);
5419 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5420 if (dirp == NULL) {
5421 bam_error(OPENDIR_FAILED, signdir, strerror(errno));
5422 return (NULL);
5425 ufs = zfs = lu = NULL;
5427 while ((entp = readdir(dirp)) != NULL) {
5428 if (strcmp(entp->d_name, ".") == 0 ||
5429 strcmp(entp->d_name, "..") == 0)
5430 continue;
5432 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5434 if (lu == NULL &&
5435 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5436 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5437 lu = s_strdup(tmpsign);
5440 if (ufs == NULL &&
5441 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5442 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5443 ufs = s_strdup(tmpsign);
5446 if (zfs == NULL &&
5447 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5448 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5449 zfs = s_strdup(tmpsign);
5453 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
5454 zfs ? zfs : "NULL",
5455 ufs ? ufs : "NULL",
5456 lu ? lu : "NULL"));
5458 if (dirp) {
5459 (void) closedir(dirp);
5460 dirp = NULL;
5463 if (strcmp(fstype, "ufs") == 0 && zfs) {
5464 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5465 free(zfs);
5466 zfs = NULL;
5467 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5468 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5469 free(ufs);
5470 ufs = NULL;
5473 assert(dirp == NULL);
5475 /* For now, we let Live Upgrade take care of its signature itself */
5476 if (lu) {
5477 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5478 free(lu);
5479 lu = NULL;
5482 return (zfs ? zfs : ufs);
5485 static char *
5486 find_backup_common(char *mntpt, char *fstype)
5488 FILE *bfp = NULL;
5489 char tmpsign[MAXNAMELEN + 1];
5490 char backup[PATH_MAX];
5491 char *ufs;
5492 char *zfs;
5493 char *lu;
5494 int error;
5495 const char *fcn = "find_backup_common()";
5498 * We didn't find it in the primary directory.
5499 * Look at the backup
5501 (void) snprintf(backup, sizeof (backup), "%s%s",
5502 mntpt, GRUBSIGN_BACKUP);
5504 bfp = fopen(backup, "r");
5505 if (bfp == NULL) {
5506 error = errno;
5507 if (bam_verbose) {
5508 bam_error(OPEN_FAIL, backup, strerror(error));
5510 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
5511 return (NULL);
5514 ufs = zfs = lu = NULL;
5516 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5518 if (lu == NULL &&
5519 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5520 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5521 lu = s_strdup(tmpsign);
5524 if (ufs == NULL &&
5525 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5526 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5527 ufs = s_strdup(tmpsign);
5530 if (zfs == NULL &&
5531 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5532 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5533 zfs = s_strdup(tmpsign);
5537 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
5538 zfs ? zfs : "NULL",
5539 ufs ? ufs : "NULL",
5540 lu ? lu : "NULL"));
5542 if (bfp) {
5543 (void) fclose(bfp);
5544 bfp = NULL;
5547 if (strcmp(fstype, "ufs") == 0 && zfs) {
5548 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5549 free(zfs);
5550 zfs = NULL;
5551 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5552 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5553 free(ufs);
5554 ufs = NULL;
5557 assert(bfp == NULL);
5559 /* For now, we let Live Upgrade take care of its signature itself */
5560 if (lu) {
5561 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5562 free(lu);
5563 lu = NULL;
5566 return (zfs ? zfs : ufs);
5569 static char *
5570 find_ufs_existing(char *osroot)
5572 char *sign;
5573 const char *fcn = "find_ufs_existing()";
5575 sign = find_primary_common(osroot, "ufs");
5576 if (sign == NULL) {
5577 sign = find_backup_common(osroot, "ufs");
5578 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
5579 } else {
5580 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
5583 return (sign);
5586 char *
5587 get_mountpoint(char *special, char *fstype)
5589 FILE *mntfp;
5590 struct mnttab mp = {0};
5591 struct mnttab mpref = {0};
5592 int error;
5593 int ret;
5594 const char *fcn = "get_mountpoint()";
5596 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
5598 mntfp = fopen(MNTTAB, "r");
5599 error = errno;
5600 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5601 if (mntfp == NULL) {
5602 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
5603 return (NULL);
5606 mpref.mnt_special = special;
5607 mpref.mnt_fstype = fstype;
5609 ret = getmntany(mntfp, &mp, &mpref);
5610 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5611 if (ret != 0) {
5612 (void) fclose(mntfp);
5613 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
5614 return (NULL);
5616 (void) fclose(mntfp);
5618 assert(mp.mnt_mountp);
5620 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
5622 return (s_strdup(mp.mnt_mountp));
5626 * Mounts a "legacy" top dataset (if needed)
5627 * Returns: The mountpoint of the legacy top dataset or NULL on error
5628 * mnted returns one of the above values defined for zfs_mnted_t
5630 static char *
5631 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5633 char cmd[PATH_MAX];
5634 char tmpmnt[PATH_MAX];
5635 filelist_t flist = {0};
5636 char *is_mounted;
5637 struct stat sb;
5638 int ret;
5639 const char *fcn = "mount_legacy_dataset()";
5641 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5643 *mnted = ZFS_MNT_ERROR;
5645 (void) snprintf(cmd, sizeof (cmd),
5646 "/sbin/zfs get -Ho value mounted %s",
5647 pool);
5649 ret = exec_cmd(cmd, &flist);
5650 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5651 if (ret != 0) {
5652 bam_error(ZFS_MNTED_FAILED, pool);
5653 return (NULL);
5656 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5657 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5658 bam_error(BAD_ZFS_MNTED, pool);
5659 filelist_free(&flist);
5660 return (NULL);
5663 is_mounted = strtok(flist.head->line, " \t\n");
5664 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5665 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5666 if (strcmp(is_mounted, "no") != 0) {
5667 filelist_free(&flist);
5668 *mnted = LEGACY_ALREADY;
5669 /* get_mountpoint returns a strdup'ed string */
5670 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
5671 return (get_mountpoint(pool, "zfs"));
5674 filelist_free(&flist);
5677 * legacy top dataset is not mounted. Mount it now
5678 * First create a mountpoint.
5680 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5681 ZFS_LEGACY_MNTPT, getpid());
5683 ret = stat(tmpmnt, &sb);
5684 if (ret == -1) {
5685 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
5686 ret = mkdirp(tmpmnt, DIR_PERMS);
5687 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5688 if (ret == -1) {
5689 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
5690 return (NULL);
5692 } else {
5693 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
5696 (void) snprintf(cmd, sizeof (cmd),
5697 "/sbin/mount -F zfs %s %s",
5698 pool, tmpmnt);
5700 ret = exec_cmd(cmd, NULL);
5701 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5702 if (ret != 0) {
5703 bam_error(ZFS_MOUNT_FAILED, pool);
5704 (void) rmdir(tmpmnt);
5705 return (NULL);
5708 *mnted = LEGACY_MOUNTED;
5709 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
5710 return (s_strdup(tmpmnt));
5714 * Mounts the top dataset (if needed)
5715 * Returns: The mountpoint of the top dataset or NULL on error
5716 * mnted returns one of the above values defined for zfs_mnted_t
5718 static char *
5719 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5721 char cmd[PATH_MAX];
5722 filelist_t flist = {0};
5723 char *is_mounted;
5724 char *mntpt;
5725 char *zmntpt;
5726 int ret;
5727 const char *fcn = "mount_top_dataset()";
5729 *mnted = ZFS_MNT_ERROR;
5731 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5734 * First check if the top dataset is a "legacy" dataset
5736 (void) snprintf(cmd, sizeof (cmd),
5737 "/sbin/zfs get -Ho value mountpoint %s",
5738 pool);
5739 ret = exec_cmd(cmd, &flist);
5740 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5741 if (ret != 0) {
5742 bam_error(ZFS_MNTPT_FAILED, pool);
5743 return (NULL);
5746 if (flist.head && (flist.head == flist.tail)) {
5747 char *legacy = strtok(flist.head->line, " \t\n");
5748 if (legacy && strcmp(legacy, "legacy") == 0) {
5749 filelist_free(&flist);
5750 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
5751 return (mount_legacy_dataset(pool, mnted));
5755 filelist_free(&flist);
5757 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
5759 (void) snprintf(cmd, sizeof (cmd),
5760 "/sbin/zfs get -Ho value mounted %s",
5761 pool);
5763 ret = exec_cmd(cmd, &flist);
5764 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5765 if (ret != 0) {
5766 bam_error(ZFS_MNTED_FAILED, pool);
5767 return (NULL);
5770 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5771 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5772 bam_error(BAD_ZFS_MNTED, pool);
5773 filelist_free(&flist);
5774 return (NULL);
5777 is_mounted = strtok(flist.head->line, " \t\n");
5778 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5779 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5780 if (strcmp(is_mounted, "no") != 0) {
5781 filelist_free(&flist);
5782 *mnted = ZFS_ALREADY;
5783 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
5784 goto mounted;
5787 filelist_free(&flist);
5788 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
5790 /* top dataset is not mounted. Mount it now */
5791 (void) snprintf(cmd, sizeof (cmd),
5792 "/sbin/zfs mount %s", pool);
5793 ret = exec_cmd(cmd, NULL);
5794 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5795 if (ret != 0) {
5796 bam_error(ZFS_MOUNT_FAILED, pool);
5797 return (NULL);
5799 *mnted = ZFS_MOUNTED;
5800 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
5801 /*FALLTHRU*/
5802 mounted:
5804 * Now get the mountpoint
5806 (void) snprintf(cmd, sizeof (cmd),
5807 "/sbin/zfs get -Ho value mountpoint %s",
5808 pool);
5810 ret = exec_cmd(cmd, &flist);
5811 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
5812 if (ret != 0) {
5813 bam_error(ZFS_MNTPT_FAILED, pool);
5814 goto error;
5817 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
5818 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5819 bam_error(NULL_ZFS_MNTPT, pool);
5820 goto error;
5823 mntpt = strtok(flist.head->line, " \t\n");
5824 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
5825 if (*mntpt != '/') {
5826 bam_error(BAD_ZFS_MNTPT, pool, mntpt);
5827 goto error;
5829 zmntpt = s_strdup(mntpt);
5831 filelist_free(&flist);
5833 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
5835 return (zmntpt);
5837 error:
5838 filelist_free(&flist);
5839 (void) umount_top_dataset(pool, *mnted, NULL);
5840 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
5841 return (NULL);
5844 static int
5845 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
5847 char cmd[PATH_MAX];
5848 int ret;
5849 const char *fcn = "umount_top_dataset()";
5851 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
5852 switch (mnted) {
5853 case LEGACY_ALREADY:
5854 case ZFS_ALREADY:
5855 /* nothing to do */
5856 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
5857 mntpt ? mntpt : "NULL"));
5858 free(mntpt);
5859 return (BAM_SUCCESS);
5860 case LEGACY_MOUNTED:
5861 (void) snprintf(cmd, sizeof (cmd),
5862 "/sbin/umount %s", pool);
5863 ret = exec_cmd(cmd, NULL);
5864 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
5865 if (ret != 0) {
5866 bam_error(UMOUNT_FAILED, pool);
5867 free(mntpt);
5868 return (BAM_ERROR);
5870 if (mntpt)
5871 (void) rmdir(mntpt);
5872 free(mntpt);
5873 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
5874 return (BAM_SUCCESS);
5875 case ZFS_MOUNTED:
5876 free(mntpt);
5877 (void) snprintf(cmd, sizeof (cmd),
5878 "/sbin/zfs unmount %s", pool);
5879 ret = exec_cmd(cmd, NULL);
5880 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
5881 if (ret != 0) {
5882 bam_error(UMOUNT_FAILED, pool);
5883 return (BAM_ERROR);
5885 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
5886 return (BAM_SUCCESS);
5887 default:
5888 bam_error(INT_BAD_MNTSTATE, pool);
5889 return (BAM_ERROR);
5891 /*NOTREACHED*/
5895 * For ZFS, osdev can be one of two forms
5896 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
5897 * It can be a /dev/[r]dsk special file. We handle both instances
5899 static char *
5900 get_pool(char *osdev)
5902 char cmd[PATH_MAX];
5903 char buf[PATH_MAX];
5904 filelist_t flist = {0};
5905 char *pool;
5906 char *cp;
5907 char *slash;
5908 int ret;
5909 const char *fcn = "get_pool()";
5911 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
5912 if (osdev == NULL) {
5913 bam_error(GET_POOL_OSDEV_NULL);
5914 return (NULL);
5917 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
5919 if (osdev[0] != '/') {
5920 (void) strlcpy(buf, osdev, sizeof (buf));
5921 slash = strchr(buf, '/');
5922 if (slash)
5923 *slash = '\0';
5924 pool = s_strdup(buf);
5925 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5926 return (pool);
5927 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
5928 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
5929 bam_error(GET_POOL_BAD_OSDEV, osdev);
5930 return (NULL);
5934 * Call the zfs fstyp directly since this is a zpool. This avoids
5935 * potential pcfs conflicts if the first block wasn't cleared.
5937 (void) snprintf(cmd, sizeof (cmd),
5938 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
5939 osdev);
5941 ret = exec_cmd(cmd, &flist);
5942 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
5943 if (ret != 0) {
5944 bam_error(FSTYP_A_FAILED, osdev);
5945 return (NULL);
5948 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
5949 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5950 bam_error(NULL_FSTYP_A, osdev);
5951 filelist_free(&flist);
5952 return (NULL);
5955 (void) strtok(flist.head->line, "'");
5956 cp = strtok(NULL, "'");
5957 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
5958 if (cp == NULL) {
5959 bam_error(BAD_FSTYP_A, osdev);
5960 filelist_free(&flist);
5961 return (NULL);
5964 pool = s_strdup(cp);
5966 filelist_free(&flist);
5968 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5970 return (pool);
5973 static char *
5974 find_zfs_existing(char *osdev)
5976 char *pool;
5977 zfs_mnted_t mnted;
5978 char *mntpt;
5979 char *sign;
5980 const char *fcn = "find_zfs_existing()";
5982 pool = get_pool(osdev);
5983 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
5984 if (pool == NULL) {
5985 bam_error(ZFS_GET_POOL_FAILED, osdev);
5986 return (NULL);
5989 mntpt = mount_top_dataset(pool, &mnted);
5990 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
5991 if (mntpt == NULL) {
5992 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
5993 free(pool);
5994 return (NULL);
5997 sign = find_primary_common(mntpt, "zfs");
5998 if (sign == NULL) {
5999 sign = find_backup_common(mntpt, "zfs");
6000 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
6001 } else {
6002 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
6005 (void) umount_top_dataset(pool, mnted, mntpt);
6007 free(pool);
6009 return (sign);
6012 static char *
6013 find_existing_sign(char *osroot, char *osdev, char *fstype)
6015 const char *fcn = "find_existing_sign()";
6017 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6018 if (strcmp(fstype, "ufs") == 0) {
6019 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
6020 return (find_ufs_existing(osroot));
6021 } else if (strcmp(fstype, "zfs") == 0) {
6022 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
6023 return (find_zfs_existing(osdev));
6024 } else {
6025 bam_error(GRUBSIGN_NOTSUP, fstype);
6026 return (NULL);
6030 #define MH_HASH_SZ 16
6032 typedef enum {
6033 MH_ERROR = -1,
6034 MH_NOMATCH,
6035 MH_MATCH
6036 } mh_search_t;
6038 typedef struct mcache {
6039 char *mc_special;
6040 char *mc_mntpt;
6041 char *mc_fstype;
6042 struct mcache *mc_next;
6043 } mcache_t;
6045 typedef struct mhash {
6046 mcache_t *mh_hash[MH_HASH_SZ];
6047 } mhash_t;
6049 static int
6050 mhash_fcn(char *key)
6052 int i;
6053 uint64_t sum = 0;
6055 for (i = 0; key[i] != '\0'; i++) {
6056 sum += (uchar_t)key[i];
6059 sum %= MH_HASH_SZ;
6061 assert(sum < MH_HASH_SZ);
6063 return (sum);
6066 static mhash_t *
6067 cache_mnttab(void)
6069 FILE *mfp;
6070 struct extmnttab mnt;
6071 mcache_t *mcp;
6072 mhash_t *mhp;
6073 char *ctds;
6074 int idx;
6075 int error;
6076 char *special_dup;
6077 const char *fcn = "cache_mnttab()";
6079 mfp = fopen(MNTTAB, "r");
6080 error = errno;
6081 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6082 if (mfp == NULL) {
6083 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6084 return (NULL);
6087 mhp = s_calloc(1, sizeof (mhash_t));
6089 resetmnttab(mfp);
6091 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6092 /* only cache ufs */
6093 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6094 continue;
6096 /* basename() modifies its arg, so dup it */
6097 special_dup = s_strdup(mnt.mnt_special);
6098 ctds = basename(special_dup);
6100 mcp = s_calloc(1, sizeof (mcache_t));
6101 mcp->mc_special = s_strdup(ctds);
6102 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6103 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6104 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
6105 mnt.mnt_mountp, mnt.mnt_fstype));
6106 idx = mhash_fcn(ctds);
6107 mcp->mc_next = mhp->mh_hash[idx];
6108 mhp->mh_hash[idx] = mcp;
6109 free(special_dup);
6112 (void) fclose(mfp);
6114 return (mhp);
6117 static void
6118 free_mnttab(mhash_t *mhp)
6120 mcache_t *mcp;
6121 int i;
6123 for (i = 0; i < MH_HASH_SZ; i++) {
6124 while ((mcp = mhp->mh_hash[i]) != NULL) {
6125 mhp->mh_hash[i] = mcp->mc_next;
6126 free(mcp->mc_special);
6127 free(mcp->mc_mntpt);
6128 free(mcp->mc_fstype);
6129 free(mcp);
6133 for (i = 0; i < MH_HASH_SZ; i++) {
6134 assert(mhp->mh_hash[i] == NULL);
6136 free(mhp);
6139 static mh_search_t
6140 search_hash(mhash_t *mhp, char *special, char **mntpt)
6142 int idx;
6143 mcache_t *mcp;
6144 const char *fcn = "search_hash()";
6146 assert(mntpt);
6148 *mntpt = NULL;
6150 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6151 if (strchr(special, '/')) {
6152 bam_error(INVALID_MHASH_KEY, special);
6153 return (MH_ERROR);
6156 idx = mhash_fcn(special);
6158 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6159 if (strcmp(mcp->mc_special, special) == 0)
6160 break;
6163 if (mcp == NULL) {
6164 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
6165 return (MH_NOMATCH);
6168 assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6169 *mntpt = mcp->mc_mntpt;
6170 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
6171 return (MH_MATCH);
6174 static int
6175 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6177 char *sign;
6178 char *signline;
6179 char signbuf[MAXNAMELEN];
6180 int len;
6181 int error;
6182 const char *fcn = "check_add_ufs_sign_to_list()";
6184 /* safe to specify NULL as "osdev" arg for UFS */
6185 sign = find_existing_sign(mntpt, NULL, "ufs");
6186 if (sign == NULL) {
6187 /* No existing signature, nothing to add to list */
6188 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
6189 return (0);
6192 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6193 signline = signbuf;
6195 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6196 if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6197 strlen(GRUBSIGN_UFS_PREFIX))) {
6198 bam_error(INVALID_UFS_SIGNATURE, sign);
6199 free(sign);
6200 /* ignore invalid signatures */
6201 return (0);
6204 len = fputs(signline, tfp);
6205 error = errno;
6206 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6207 if (len != strlen(signline)) {
6208 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
6209 free(sign);
6210 return (-1);
6213 free(sign);
6215 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
6216 return (0);
6220 * slice is a basename not a full pathname
6222 static int
6223 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6225 int ret;
6226 char cmd[PATH_MAX];
6227 char path[PATH_MAX];
6228 struct stat sbuf;
6229 char *mntpt;
6230 filelist_t flist = {0};
6231 char *fstype;
6232 char blkslice[PATH_MAX];
6233 const char *fcn = "process_slice_common()";
6236 ret = search_hash(mhp, slice, &mntpt);
6237 switch (ret) {
6238 case MH_MATCH:
6239 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6240 return (-1);
6241 else
6242 return (0);
6243 case MH_NOMATCH:
6244 break;
6245 case MH_ERROR:
6246 default:
6247 return (-1);
6250 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6251 if (stat(path, &sbuf) == -1) {
6252 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
6253 return (0);
6256 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6257 (void) snprintf(cmd, sizeof (cmd),
6258 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6259 slice);
6261 if (exec_cmd(cmd, &flist) != 0) {
6262 if (bam_verbose)
6263 bam_print(FSTYP_FAILED, slice);
6264 return (0);
6267 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6268 if (bam_verbose)
6269 bam_print(FSTYP_BAD, slice);
6270 filelist_free(&flist);
6271 return (0);
6274 fstype = strtok(flist.head->line, " \t\n");
6275 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6276 if (bam_verbose)
6277 bam_print(NOT_UFS_SLICE, slice, fstype);
6278 filelist_free(&flist);
6279 return (0);
6282 filelist_free(&flist);
6285 * Since we are mounting the filesystem read-only, the
6286 * the last mount field of the superblock is unchanged
6287 * and does not need to be fixed up post-mount;
6290 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6291 slice);
6293 (void) snprintf(cmd, sizeof (cmd),
6294 "/usr/sbin/mount -F ufs -o ro %s %s "
6295 "> /dev/null 2>&1", blkslice, tmpmnt);
6297 if (exec_cmd(cmd, NULL) != 0) {
6298 if (bam_verbose)
6299 bam_print(MOUNT_FAILED, blkslice, "ufs");
6300 return (0);
6303 ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6305 (void) snprintf(cmd, sizeof (cmd),
6306 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6307 tmpmnt);
6309 if (exec_cmd(cmd, NULL) != 0) {
6310 bam_print(UMOUNT_FAILED, slice);
6311 return (0);
6314 return (ret);
6317 static int
6318 process_vtoc_slices(
6319 char *s0,
6320 struct vtoc *vtoc,
6321 FILE *tfp,
6322 mhash_t *mhp,
6323 char *tmpmnt)
6325 int idx;
6326 char slice[PATH_MAX];
6327 size_t len;
6328 char *cp;
6329 const char *fcn = "process_vtoc_slices()";
6331 len = strlen(s0);
6333 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6335 s0[len - 1] = '\0';
6337 (void) strlcpy(slice, s0, sizeof (slice));
6339 s0[len - 1] = '0';
6341 cp = slice + len - 1;
6343 for (idx = 0; idx < vtoc->v_nparts; idx++) {
6345 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6347 if (vtoc->v_part[idx].p_size == 0) {
6348 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
6349 continue;
6352 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6353 switch (vtoc->v_part[idx].p_tag) {
6354 case V_SWAP:
6355 case V_USR:
6356 case V_BACKUP:
6357 case V_VAR:
6358 case V_HOME:
6359 case V_ALTSCTR:
6360 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
6361 continue;
6362 default:
6363 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
6364 break;
6367 /* skip unmountable and readonly slices */
6368 switch (vtoc->v_part[idx].p_flag) {
6369 case V_UNMNT:
6370 case V_RONLY:
6371 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
6372 continue;
6373 default:
6374 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
6375 break;
6378 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6379 return (-1);
6383 return (0);
6386 static int
6387 process_efi_slices(
6388 char *s0,
6389 struct dk_gpt *efi,
6390 FILE *tfp,
6391 mhash_t *mhp,
6392 char *tmpmnt)
6394 int idx;
6395 char slice[PATH_MAX];
6396 size_t len;
6397 char *cp;
6398 const char *fcn = "process_efi_slices()";
6400 len = strlen(s0);
6402 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6404 s0[len - 1] = '\0';
6406 (void) strlcpy(slice, s0, sizeof (slice));
6408 s0[len - 1] = '0';
6410 cp = slice + len - 1;
6412 for (idx = 0; idx < efi->efi_nparts; idx++) {
6414 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6416 if (efi->efi_parts[idx].p_size == 0) {
6417 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
6418 continue;
6421 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6422 switch (efi->efi_parts[idx].p_tag) {
6423 case V_SWAP:
6424 case V_USR:
6425 case V_BACKUP:
6426 case V_VAR:
6427 case V_HOME:
6428 case V_ALTSCTR:
6429 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
6430 continue;
6431 default:
6432 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
6433 break;
6436 /* skip unmountable and readonly slices */
6437 switch (efi->efi_parts[idx].p_flag) {
6438 case V_UNMNT:
6439 case V_RONLY:
6440 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
6441 continue;
6442 default:
6443 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
6444 break;
6447 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6448 return (-1);
6452 return (0);
6456 * s0 is a basename not a full path
6458 static int
6459 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6461 struct vtoc vtoc;
6462 struct dk_gpt *efi;
6463 char s0path[PATH_MAX];
6464 struct stat sbuf;
6465 int e_flag;
6466 int v_flag;
6467 int retval;
6468 int err;
6469 int fd;
6470 const char *fcn = "process_slice0()";
6472 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6474 if (stat(s0path, &sbuf) == -1) {
6475 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
6476 return (0);
6479 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6480 if (fd == -1) {
6481 bam_error(OPEN_FAIL, s0path, strerror(errno));
6482 return (0);
6485 e_flag = v_flag = 0;
6486 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6487 switch (retval) {
6488 case VT_EIO:
6489 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
6490 break;
6491 case VT_EINVAL:
6492 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
6493 break;
6494 case VT_ERROR:
6495 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
6496 break;
6497 case VT_ENOTSUP:
6498 e_flag = 1;
6499 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
6500 break;
6501 case 0:
6502 v_flag = 1;
6503 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
6504 break;
6505 default:
6506 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
6507 break;
6511 if (e_flag) {
6512 e_flag = 0;
6513 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6514 switch (retval) {
6515 case VT_EIO:
6516 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
6517 break;
6518 case VT_EINVAL:
6519 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
6520 break;
6521 case VT_ERROR:
6522 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
6523 break;
6524 case VT_ENOTSUP:
6525 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
6526 break;
6527 case 0:
6528 e_flag = 1;
6529 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
6530 break;
6531 default:
6532 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
6533 break;
6537 (void) close(fd);
6539 if (v_flag) {
6540 retval = process_vtoc_slices(s0,
6541 &vtoc, tfp, mhp, tmpmnt);
6542 } else if (e_flag) {
6543 retval = process_efi_slices(s0,
6544 efi, tfp, mhp, tmpmnt);
6545 } else {
6546 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
6547 return (0);
6550 return (retval);
6554 * Find and create a list of all existing UFS boot signatures
6556 static int
6557 FindAllUfsSignatures(void)
6559 mhash_t *mnttab_hash;
6560 DIR *dirp = NULL;
6561 struct dirent *dp;
6562 char tmpmnt[PATH_MAX];
6563 char cmd[PATH_MAX];
6564 struct stat sb;
6565 int fd;
6566 FILE *tfp;
6567 size_t len;
6568 int ret;
6569 int error;
6570 const char *fcn = "FindAllUfsSignatures()";
6572 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) {
6573 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
6574 return (0);
6577 fd = open(UFS_SIGNATURE_LIST".tmp",
6578 O_RDWR|O_CREAT|O_TRUNC, 0644);
6579 error = errno;
6580 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6581 if (fd == -1) {
6582 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6583 return (-1);
6586 ret = close(fd);
6587 error = errno;
6588 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6589 if (ret == -1) {
6590 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6591 strerror(error));
6592 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6593 return (-1);
6596 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6597 error = errno;
6598 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6599 if (tfp == NULL) {
6600 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6601 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6602 return (-1);
6605 mnttab_hash = cache_mnttab();
6606 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6607 if (mnttab_hash == NULL) {
6608 (void) fclose(tfp);
6609 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6610 bam_error(CACHE_MNTTAB_FAIL, fcn);
6611 return (-1);
6614 (void) snprintf(tmpmnt, sizeof (tmpmnt),
6615 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6616 (void) unlink(tmpmnt);
6618 ret = mkdirp(tmpmnt, DIR_PERMS);
6619 error = errno;
6620 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6621 if (ret == -1) {
6622 bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
6623 free_mnttab(mnttab_hash);
6624 (void) fclose(tfp);
6625 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6626 return (-1);
6629 dirp = opendir("/dev/rdsk");
6630 error = errno;
6631 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6632 if (dirp == NULL) {
6633 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
6634 goto fail;
6637 while ((dp = readdir(dirp)) != NULL) {
6638 if (strcmp(dp->d_name, ".") == 0 ||
6639 strcmp(dp->d_name, "..") == 0)
6640 continue;
6643 * we only look for the s0 slice. This is guranteed to
6644 * have 's' at len - 2.
6646 len = strlen(dp->d_name);
6647 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6648 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
6649 continue;
6652 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6653 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6654 if (ret == -1)
6655 goto fail;
6658 (void) closedir(dirp);
6659 free_mnttab(mnttab_hash);
6660 (void) rmdir(tmpmnt);
6662 ret = fclose(tfp);
6663 error = errno;
6664 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6665 if (ret == EOF) {
6666 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6667 strerror(error));
6668 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6669 return (-1);
6672 /* We have a list of existing GRUB signatures. Sort it first */
6673 (void) snprintf(cmd, sizeof (cmd),
6674 "/usr/bin/sort -u %s.tmp > %s.sorted",
6675 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6677 ret = exec_cmd(cmd, NULL);
6678 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6679 if (ret != 0) {
6680 bam_error(GRUBSIGN_SORT_FAILED);
6681 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6682 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6683 return (-1);
6686 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6688 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6689 error = errno;
6690 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6691 if (ret == -1) {
6692 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
6693 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6694 return (-1);
6697 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6698 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
6701 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6702 return (0);
6704 fail:
6705 if (dirp)
6706 (void) closedir(dirp);
6707 free_mnttab(mnttab_hash);
6708 (void) rmdir(tmpmnt);
6709 (void) fclose(tfp);
6710 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6711 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6712 return (-1);
6715 static char *
6716 create_ufs_sign(void)
6718 struct stat sb;
6719 int signnum = -1;
6720 char tmpsign[MAXNAMELEN + 1];
6721 char *numstr;
6722 int i;
6723 FILE *tfp;
6724 int ret;
6725 int error;
6726 const char *fcn = "create_ufs_sign()";
6728 bam_print(SEARCHING_UFS_SIGN);
6730 ret = FindAllUfsSignatures();
6731 INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6732 if (ret == -1) {
6733 bam_error(ERR_FIND_UFS_SIGN);
6734 return (NULL);
6737 /* Make sure the list exists and is owned by root */
6738 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6739 (void) unlink(UFS_SIGNATURE_LIST));
6740 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6741 (void) unlink(UFS_SIGNATURE_LIST);
6742 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
6743 return (NULL);
6746 if (sb.st_size == 0) {
6747 bam_print(GRUBSIGN_UFS_NONE);
6748 i = 0;
6749 goto found;
6752 /* The signature list was sorted when it was created */
6753 tfp = fopen(UFS_SIGNATURE_LIST, "r");
6754 error = errno;
6755 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
6756 if (tfp == NULL) {
6757 bam_error(UFS_SIGNATURE_LIST_OPENERR,
6758 UFS_SIGNATURE_LIST, strerror(error));
6759 (void) unlink(UFS_SIGNATURE_LIST);
6760 return (NULL);
6763 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
6765 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
6766 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
6767 (void) fclose(tfp);
6768 (void) unlink(UFS_SIGNATURE_LIST);
6769 bam_error(UFS_BADSIGN, tmpsign);
6770 return (NULL);
6772 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
6774 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
6775 (void) fclose(tfp);
6776 (void) unlink(UFS_SIGNATURE_LIST);
6777 bam_error(UFS_BADSIGN, tmpsign);
6778 return (NULL);
6781 signnum = atoi(numstr);
6782 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
6783 if (signnum < 0) {
6784 (void) fclose(tfp);
6785 (void) unlink(UFS_SIGNATURE_LIST);
6786 bam_error(UFS_BADSIGN, tmpsign);
6787 return (NULL);
6790 if (i != signnum) {
6791 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
6792 break;
6796 (void) fclose(tfp);
6798 found:
6799 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
6801 /* add the ufs signature to the /var/run list of signatures */
6802 ret = ufs_add_to_sign_list(tmpsign);
6803 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
6804 if (ret == -1) {
6805 (void) unlink(UFS_SIGNATURE_LIST);
6806 bam_error(FAILED_ADD_SIGNLIST, tmpsign);
6807 return (NULL);
6810 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6812 return (s_strdup(tmpsign));
6815 static char *
6816 get_fstype(char *osroot)
6818 FILE *mntfp;
6819 struct mnttab mp = {0};
6820 struct mnttab mpref = {0};
6821 int error;
6822 int ret;
6823 const char *fcn = "get_fstype()";
6825 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
6826 if (osroot == NULL) {
6827 bam_error(GET_FSTYPE_ARGS);
6828 return (NULL);
6831 mntfp = fopen(MNTTAB, "r");
6832 error = errno;
6833 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
6834 if (mntfp == NULL) {
6835 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6836 return (NULL);
6839 if (*osroot == '\0')
6840 mpref.mnt_mountp = "/";
6841 else
6842 mpref.mnt_mountp = osroot;
6844 ret = getmntany(mntfp, &mp, &mpref);
6845 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
6846 if (ret != 0) {
6847 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
6848 (void) fclose(mntfp);
6849 return (NULL);
6851 (void) fclose(mntfp);
6853 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
6854 if (mp.mnt_fstype == NULL) {
6855 bam_error(MNTTAB_FSTYPE_NULL, osroot);
6856 return (NULL);
6859 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6861 return (s_strdup(mp.mnt_fstype));
6864 static char *
6865 create_zfs_sign(char *osdev)
6867 char tmpsign[PATH_MAX];
6868 char *pool;
6869 const char *fcn = "create_zfs_sign()";
6871 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
6874 * First find the pool name
6876 pool = get_pool(osdev);
6877 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
6878 if (pool == NULL) {
6879 bam_error(GET_POOL_FAILED, osdev);
6880 return (NULL);
6883 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
6885 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
6887 free(pool);
6889 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6891 return (s_strdup(tmpsign));
6894 static char *
6895 create_new_sign(char *osdev, char *fstype)
6897 char *sign;
6898 const char *fcn = "create_new_sign()";
6900 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
6902 if (strcmp(fstype, "zfs") == 0) {
6903 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
6904 sign = create_zfs_sign(osdev);
6905 } else if (strcmp(fstype, "ufs") == 0) {
6906 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
6907 sign = create_ufs_sign();
6908 } else {
6909 bam_error(GRUBSIGN_NOTSUP, fstype);
6910 sign = NULL;
6913 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
6914 return (sign);
6917 static int
6918 set_backup_common(char *mntpt, char *sign)
6920 FILE *bfp;
6921 char backup[PATH_MAX];
6922 char tmpsign[PATH_MAX];
6923 int error;
6924 char *bdir;
6925 char *backup_dup;
6926 struct stat sb;
6927 int ret;
6928 const char *fcn = "set_backup_common()";
6930 (void) snprintf(backup, sizeof (backup), "%s%s",
6931 mntpt, GRUBSIGN_BACKUP);
6933 /* First read the backup */
6934 bfp = fopen(backup, "r");
6935 if (bfp != NULL) {
6936 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
6937 if (strcmp(tmpsign, sign) == 0) {
6938 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
6939 (void) fclose(bfp);
6940 return (0);
6943 (void) fclose(bfp);
6944 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
6945 } else {
6946 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
6950 * Didn't find the correct signature. First create
6951 * the directory if necessary.
6954 /* dirname() modifies its argument so dup it */
6955 backup_dup = s_strdup(backup);
6956 bdir = dirname(backup_dup);
6957 assert(bdir);
6959 ret = stat(bdir, &sb);
6960 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
6961 if (ret == -1) {
6962 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
6963 ret = mkdirp(bdir, DIR_PERMS);
6964 error = errno;
6965 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
6966 if (ret == -1) {
6967 bam_error(GRUBSIGN_BACKUP_MKDIRERR,
6968 GRUBSIGN_BACKUP, strerror(error));
6969 free(backup_dup);
6970 return (-1);
6973 free(backup_dup);
6976 * Open the backup in append mode to add the correct
6977 * signature;
6979 bfp = fopen(backup, "a");
6980 error = errno;
6981 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
6982 if (bfp == NULL) {
6983 bam_error(GRUBSIGN_BACKUP_OPENERR,
6984 GRUBSIGN_BACKUP, strerror(error));
6985 return (-1);
6988 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
6990 ret = fputs(tmpsign, bfp);
6991 error = errno;
6992 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
6993 if (ret != strlen(tmpsign)) {
6994 bam_error(GRUBSIGN_BACKUP_WRITEERR,
6995 GRUBSIGN_BACKUP, strerror(error));
6996 (void) fclose(bfp);
6997 return (-1);
7000 (void) fclose(bfp);
7002 if (bam_verbose)
7003 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
7005 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7007 return (0);
7010 static int
7011 set_backup_ufs(char *osroot, char *sign)
7013 const char *fcn = "set_backup_ufs()";
7015 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
7016 return (set_backup_common(osroot, sign));
7019 static int
7020 set_backup_zfs(char *osdev, char *sign)
7022 char *pool;
7023 char *mntpt;
7024 zfs_mnted_t mnted;
7025 int ret;
7026 const char *fcn = "set_backup_zfs()";
7028 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
7030 pool = get_pool(osdev);
7031 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7032 if (pool == NULL) {
7033 bam_error(GET_POOL_FAILED, osdev);
7034 return (-1);
7037 mntpt = mount_top_dataset(pool, &mnted);
7038 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7039 if (mntpt == NULL) {
7040 bam_error(FAIL_MNT_TOP_DATASET, pool);
7041 free(pool);
7042 return (-1);
7045 ret = set_backup_common(mntpt, sign);
7047 (void) umount_top_dataset(pool, mnted, mntpt);
7049 free(pool);
7051 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7052 if (ret == 0) {
7053 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7054 } else {
7055 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7058 return (ret);
7061 static int
7062 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7064 const char *fcn = "set_backup()";
7065 int ret;
7067 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7069 if (strcmp(fstype, "ufs") == 0) {
7070 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
7071 ret = set_backup_ufs(osroot, sign);
7072 } else if (strcmp(fstype, "zfs") == 0) {
7073 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
7074 ret = set_backup_zfs(osdev, sign);
7075 } else {
7076 bam_error(GRUBSIGN_NOTSUP, fstype);
7077 ret = -1;
7080 if (ret == 0) {
7081 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7082 } else {
7083 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7086 return (ret);
7089 static int
7090 set_primary_common(char *mntpt, char *sign)
7092 char signfile[PATH_MAX];
7093 char signdir[PATH_MAX];
7094 struct stat sb;
7095 int fd;
7096 int error;
7097 int ret;
7098 const char *fcn = "set_primary_common()";
7100 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7101 mntpt, GRUBSIGN_DIR, sign);
7103 if (stat(signfile, &sb) != -1) {
7104 if (bam_verbose)
7105 bam_print(PRIMARY_SIGN_EXISTS, sign);
7106 return (0);
7107 } else {
7108 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
7111 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
7112 mntpt, GRUBSIGN_DIR);
7114 if (stat(signdir, &sb) == -1) {
7115 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
7116 ret = mkdirp(signdir, DIR_PERMS);
7117 error = errno;
7118 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7119 if (ret == -1) {
7120 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
7121 return (-1);
7125 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7126 error = errno;
7127 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7128 if (fd == -1) {
7129 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
7130 return (-1);
7133 ret = fsync(fd);
7134 error = errno;
7135 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7136 if (ret != 0) {
7137 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
7140 (void) close(fd);
7142 if (bam_verbose)
7143 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
7145 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7147 return (0);
7150 static int
7151 set_primary_ufs(char *osroot, char *sign)
7153 const char *fcn = "set_primary_ufs()";
7155 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
7156 return (set_primary_common(osroot, sign));
7159 static int
7160 set_primary_zfs(char *osdev, char *sign)
7162 char *pool;
7163 char *mntpt;
7164 zfs_mnted_t mnted;
7165 int ret;
7166 const char *fcn = "set_primary_zfs()";
7168 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
7170 pool = get_pool(osdev);
7171 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7172 if (pool == NULL) {
7173 bam_error(GET_POOL_FAILED, osdev);
7174 return (-1);
7177 /* Pool name must exist in the sign */
7178 ret = (strstr(sign, pool) != NULL);
7179 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7180 if (ret == 0) {
7181 bam_error(POOL_SIGN_INCOMPAT, pool, sign);
7182 free(pool);
7183 return (-1);
7186 mntpt = mount_top_dataset(pool, &mnted);
7187 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7188 if (mntpt == NULL) {
7189 bam_error(FAIL_MNT_TOP_DATASET, pool);
7190 free(pool);
7191 return (-1);
7194 ret = set_primary_common(mntpt, sign);
7196 (void) umount_top_dataset(pool, mnted, mntpt);
7198 free(pool);
7200 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7201 if (ret == 0) {
7202 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7203 } else {
7204 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7207 return (ret);
7210 static int
7211 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7213 const char *fcn = "set_primary()";
7214 int ret;
7216 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7217 if (strcmp(fstype, "ufs") == 0) {
7218 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
7219 ret = set_primary_ufs(osroot, sign);
7220 } else if (strcmp(fstype, "zfs") == 0) {
7221 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
7222 ret = set_primary_zfs(osdev, sign);
7223 } else {
7224 bam_error(GRUBSIGN_NOTSUP, fstype);
7225 ret = -1;
7228 if (ret == 0) {
7229 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7230 } else {
7231 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7234 return (ret);
7237 static int
7238 ufs_add_to_sign_list(char *sign)
7240 FILE *tfp;
7241 char signline[MAXNAMELEN];
7242 char cmd[PATH_MAX];
7243 int ret;
7244 int error;
7245 const char *fcn = "ufs_add_to_sign_list()";
7247 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7248 if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7249 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7250 bam_error(INVALID_UFS_SIGN, sign);
7251 (void) unlink(UFS_SIGNATURE_LIST);
7252 return (-1);
7256 * most failures in this routine are not a fatal error
7257 * We simply unlink the /var/run file and continue
7260 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7261 error = errno;
7262 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7263 if (ret == -1) {
7264 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
7265 strerror(error));
7266 (void) unlink(UFS_SIGNATURE_LIST);
7267 return (0);
7270 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7271 error = errno;
7272 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7273 if (tfp == NULL) {
7274 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
7275 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7276 return (0);
7279 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7281 ret = fputs(signline, tfp);
7282 error = errno;
7283 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7284 if (ret != strlen(signline)) {
7285 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
7286 (void) fclose(tfp);
7287 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7288 return (0);
7291 ret = fclose(tfp);
7292 error = errno;
7293 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7294 if (ret == EOF) {
7295 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
7296 strerror(error));
7297 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7298 return (0);
7301 /* Sort the list again */
7302 (void) snprintf(cmd, sizeof (cmd),
7303 "/usr/bin/sort -u %s.tmp > %s.sorted",
7304 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7306 ret = exec_cmd(cmd, NULL);
7307 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7308 if (ret != 0) {
7309 bam_error(GRUBSIGN_SORT_FAILED);
7310 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7311 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7312 return (0);
7315 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7317 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7318 error = errno;
7319 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7320 if (ret == -1) {
7321 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
7322 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7323 return (0);
7326 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7328 return (0);
7331 static int
7332 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7334 int ret;
7335 const char *fcn = "set_signature()";
7337 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
7339 ret = set_backup(osroot, osdev, sign, fstype);
7340 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7341 if (ret == -1) {
7342 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7343 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
7344 return (-1);
7347 ret = set_primary(osroot, osdev, sign, fstype);
7348 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7350 if (ret == 0) {
7351 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7352 } else {
7353 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7354 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
7357 return (ret);
7360 char *
7361 get_grubsign(char *osroot, char *osdev)
7363 char *grubsign; /* (<sign>,#,#) */
7364 char *slice;
7365 int fdiskpart;
7366 char *sign;
7367 char *fstype;
7368 int ret;
7369 const char *fcn = "get_grubsign()";
7371 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
7372 fstype = get_fstype(osroot);
7373 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7374 if (fstype == NULL) {
7375 bam_error(GET_FSTYPE_FAILED, osroot);
7376 return (NULL);
7379 sign = find_existing_sign(osroot, osdev, fstype);
7380 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7381 if (sign == NULL) {
7382 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
7383 sign = create_new_sign(osdev, fstype);
7384 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7385 if (sign == NULL) {
7386 bam_error(GRUBSIGN_CREATE_FAIL, osdev);
7387 free(fstype);
7388 return (NULL);
7392 ret = set_signature(osroot, osdev, sign, fstype);
7393 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7394 if (ret == -1) {
7395 bam_error(GRUBSIGN_WRITE_FAIL, osdev);
7396 free(sign);
7397 free(fstype);
7398 (void) unlink(UFS_SIGNATURE_LIST);
7399 return (NULL);
7402 free(fstype);
7404 if (bam_verbose)
7405 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
7407 fdiskpart = get_partition(osdev);
7408 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7409 if (fdiskpart == PARTNO_NOTFOUND) {
7410 bam_error(FDISKPART_FAIL, osdev);
7411 free(sign);
7412 return (NULL);
7415 slice = strrchr(osdev, 's');
7417 if (fdiskpart == PARTNO_EFI) {
7418 fdiskpart = atoi(&slice[1]);
7419 slice = NULL;
7422 grubsign = s_calloc(1, MAXNAMELEN + 10);
7423 if (slice) {
7424 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7425 sign, fdiskpart, slice[1] + 'a' - '0');
7426 } else
7427 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7428 sign, fdiskpart);
7430 free(sign);
7432 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
7434 return (grubsign);
7437 static char *
7438 get_title(char *rootdir)
7440 static char title[80];
7441 char *cp = NULL;
7442 char release[PATH_MAX];
7443 FILE *fp;
7444 const char *fcn = "get_title()";
7446 /* open the /etc/release file */
7447 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7449 fp = fopen(release, "r");
7450 if (fp == NULL) {
7451 bam_error(OPEN_FAIL, release, strerror(errno));
7452 cp = NULL;
7453 goto out;
7456 /* grab first line of /etc/release */
7457 cp = s_fgets(title, sizeof (title), fp);
7458 if (cp) {
7459 while (isspace(*cp)) /* remove leading spaces */
7460 cp++;
7463 (void) fclose(fp);
7465 out:
7466 cp = cp ? cp : "Oracle Solaris";
7468 BAM_DPRINTF((D_GET_TITLE, fcn, cp));
7470 return (cp);
7473 char *
7474 get_special(char *mountp)
7476 FILE *mntfp;
7477 struct mnttab mp = {0};
7478 struct mnttab mpref = {0};
7479 int error;
7480 int ret;
7481 const char *fcn = "get_special()";
7483 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7484 if (mountp == NULL) {
7485 bam_error(GET_SPECIAL_NULL_MNTPT);
7486 return (NULL);
7489 mntfp = fopen(MNTTAB, "r");
7490 error = errno;
7491 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7492 if (mntfp == NULL) {
7493 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
7494 return (NULL);
7497 if (*mountp == '\0')
7498 mpref.mnt_mountp = "/";
7499 else
7500 mpref.mnt_mountp = mountp;
7502 ret = getmntany(mntfp, &mp, &mpref);
7503 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7504 if (ret != 0) {
7505 (void) fclose(mntfp);
7506 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
7507 return (NULL);
7509 (void) fclose(mntfp);
7511 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
7513 return (s_strdup(mp.mnt_special));
7516 static void
7517 free_physarray(char **physarray, int n)
7519 int i;
7520 const char *fcn = "free_physarray()";
7522 assert(physarray);
7523 assert(n);
7525 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
7527 for (i = 0; i < n; i++) {
7528 free(physarray[i]);
7530 free(physarray);
7532 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7535 static int
7536 zfs_get_physical(char *special, char ***physarray, int *n)
7538 char sdup[PATH_MAX];
7539 char cmd[PATH_MAX];
7540 char dsk[PATH_MAX];
7541 char *pool;
7542 filelist_t flist = {0};
7543 line_t *lp;
7544 line_t *startlp;
7545 char *comp1;
7546 int i;
7547 int ret;
7548 const char *fcn = "zfs_get_physical()";
7550 assert(special);
7552 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7554 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7555 if (special[0] == '/') {
7556 bam_error(INVALID_ZFS_SPECIAL, special);
7557 return (-1);
7560 (void) strlcpy(sdup, special, sizeof (sdup));
7562 pool = strtok(sdup, "/");
7563 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7564 if (pool == NULL) {
7565 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
7566 return (-1);
7569 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7571 ret = exec_cmd(cmd, &flist);
7572 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7573 if (ret != 0) {
7574 bam_error(ZFS_GET_POOL_STATUS, pool);
7575 return (-1);
7578 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7579 if (flist.head == NULL) {
7580 bam_error(BAD_ZPOOL_STATUS, pool);
7581 filelist_free(&flist);
7582 return (-1);
7585 for (lp = flist.head; lp; lp = lp->next) {
7586 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
7587 comp1 = strtok(lp->line, " \t");
7588 if (comp1 == NULL) {
7589 free(lp->line);
7590 lp->line = NULL;
7591 } else {
7592 comp1 = s_strdup(comp1);
7593 free(lp->line);
7594 lp->line = comp1;
7598 for (lp = flist.head; lp; lp = lp->next) {
7599 if (lp->line == NULL)
7600 continue;
7601 if (strcmp(lp->line, pool) == 0) {
7602 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
7603 break;
7607 if (lp == NULL) {
7608 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
7609 filelist_free(&flist);
7610 return (-1);
7613 startlp = lp->next;
7614 for (i = 0, lp = startlp; lp; lp = lp->next) {
7615 if (lp->line == NULL)
7616 continue;
7617 if (strcmp(lp->line, "mirror") == 0)
7618 continue;
7619 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7620 break;
7621 i++;
7622 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
7625 if (i == 0) {
7626 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
7627 filelist_free(&flist);
7628 return (-1);
7631 *n = i;
7632 *physarray = s_calloc(*n, sizeof (char *));
7633 for (i = 0, lp = startlp; lp; lp = lp->next) {
7634 if (lp->line == NULL)
7635 continue;
7636 if (strcmp(lp->line, "mirror") == 0)
7637 continue;
7638 if (strcmp(lp->line, "errors:") == 0)
7639 break;
7640 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7641 strncmp(lp->line, "/dev/rdsk/",
7642 strlen("/dev/rdsk/")) != 0) {
7643 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7644 lp->line);
7645 } else {
7646 (void) strlcpy(dsk, lp->line, sizeof (dsk));
7648 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
7649 (*physarray)[i++] = s_strdup(dsk);
7652 assert(i == *n);
7654 filelist_free(&flist);
7656 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7657 return (0);
7661 * Certain services needed to run metastat successfully may not
7662 * be enabled. Enable them now.
7665 * Checks if the specified service is online
7666 * Returns: 1 if the service is online
7667 * 0 if the service is not online
7668 * -1 on error
7670 static int
7671 is_svc_online(char *svc)
7673 char *state;
7674 const char *fcn = "is_svc_online()";
7676 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
7678 state = smf_get_state(svc);
7679 INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
7680 if (state == NULL) {
7681 bam_error(GET_SVC_STATE_ERR, svc);
7682 return (-1);
7684 BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
7686 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
7687 BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
7688 free(state);
7689 return (1);
7692 BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
7694 free(state);
7696 return (0);
7699 static int
7700 enable_svc(char *svc)
7702 int ret;
7703 int sleeptime;
7704 const char *fcn = "enable_svc()";
7706 ret = is_svc_online(svc);
7707 if (ret == -1) {
7708 bam_error(SVC_IS_ONLINE_FAILED, svc);
7709 return (-1);
7710 } else if (ret == 1) {
7711 BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
7712 return (0);
7715 /* Service is not enabled. Enable it now. */
7716 ret = smf_enable_instance(svc, 0);
7717 INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
7718 if (ret != 0) {
7719 bam_error(ENABLE_SVC_FAILED, svc);
7720 return (-1);
7723 BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
7725 sleeptime = 0;
7726 do {
7727 ret = is_svc_online(svc);
7728 INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
7729 INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
7730 INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
7731 if (ret == -1) {
7732 bam_error(ERR_SVC_GET_ONLINE, svc);
7733 return (-1);
7734 } else if (ret == 1) {
7735 BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
7736 return (1);
7738 (void) sleep(1);
7739 } while (++sleeptime < 60);
7741 bam_error(TIMEOUT_ENABLE_SVC, svc);
7743 return (-1);
7746 static int
7747 ufs_get_physical(char *special, char ***physarray, int *n)
7749 char cmd[PATH_MAX];
7750 char *shortname;
7751 filelist_t flist = {0};
7752 char *meta;
7753 char *type;
7754 char *comp1;
7755 char *comp2;
7756 char *comp3;
7757 char *comp4;
7758 int i;
7759 line_t *lp;
7760 int ret;
7761 char *svc;
7762 const char *fcn = "ufs_get_physical()";
7764 assert(special);
7766 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7768 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
7769 bam_error(UFS_GET_PHYS_NOT_SVM, special);
7770 return (-1);
7773 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
7774 shortname = special + strlen("/dev/md/dsk/");
7775 } else if (strncmp(special, "/dev/md/rdsk/",
7776 strlen("/dev/md/rdsk/")) == 0) {
7777 shortname = special + strlen("/dev/md/rdsk");
7778 } else {
7779 bam_error(UFS_GET_PHYS_INVALID_SVM, special);
7780 return (-1);
7783 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
7785 svc = "network/rpc/meta:default";
7786 if (enable_svc(svc) == -1) {
7787 bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
7790 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
7792 ret = exec_cmd(cmd, &flist);
7793 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
7794 if (ret != 0) {
7795 bam_error(UFS_SVM_METASTAT_ERR, shortname);
7796 return (-1);
7799 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
7800 if (flist.head == NULL) {
7801 bam_error(BAD_UFS_SVM_METASTAT, shortname);
7802 filelist_free(&flist);
7803 return (-1);
7807 * Check if not a mirror. We only parse a single metadevice
7808 * if not a mirror
7810 meta = strtok(flist.head->line, " \t");
7811 type = strtok(NULL, " \t");
7812 if (meta == NULL || type == NULL) {
7813 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
7814 filelist_free(&flist);
7815 return (-1);
7817 if (strcmp(type, "-m") != 0) {
7818 comp1 = strtok(NULL, " \t");
7819 comp2 = strtok(NULL, " \t");
7820 if (comp1 == NULL || comp2 != NULL) {
7821 bam_error(INVALID_UFS_SVM_METASTAT, shortname);
7822 filelist_free(&flist);
7823 return (-1);
7825 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
7826 *physarray = s_calloc(1, sizeof (char *));
7827 (*physarray)[0] = s_strdup(comp1);
7828 *n = 1;
7829 filelist_free(&flist);
7830 return (0);
7834 * Okay we have a mirror. Everything after the first line
7835 * is a submirror
7837 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7838 if (strstr(lp->line, "/dev/dsk/") == NULL &&
7839 strstr(lp->line, "/dev/rdsk/") == NULL) {
7840 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
7841 filelist_free(&flist);
7842 return (-1);
7844 i++;
7847 *physarray = s_calloc(i, sizeof (char *));
7848 *n = i;
7850 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7851 comp1 = strtok(lp->line, " \t");
7852 comp2 = strtok(NULL, " \t");
7853 comp3 = strtok(NULL, " \t");
7854 comp4 = strtok(NULL, " \t");
7856 if (comp3 == NULL || comp4 == NULL ||
7857 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7858 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
7859 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
7860 filelist_free(&flist);
7861 free_physarray(*physarray, *n);
7862 return (-1);
7865 (*physarray)[i++] = s_strdup(comp4);
7868 assert(i == *n);
7870 filelist_free(&flist);
7872 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7873 return (0);
7876 static int
7877 get_physical(char *menu_root, char ***physarray, int *n)
7879 char *special;
7880 int ret;
7881 const char *fcn = "get_physical()";
7883 assert(menu_root);
7884 assert(physarray);
7885 assert(n);
7887 *physarray = NULL;
7888 *n = 0;
7890 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
7892 /* First get the device special file from /etc/mnttab */
7893 special = get_special(menu_root);
7894 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7895 if (special == NULL) {
7896 bam_error(GET_SPECIAL_NULL, menu_root);
7897 return (-1);
7900 /* If already a physical device nothing to do */
7901 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7902 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7903 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
7904 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7905 *physarray = s_calloc(1, sizeof (char *));
7906 (*physarray)[0] = special;
7907 *n = 1;
7908 return (0);
7911 if (is_zfs(menu_root)) {
7912 ret = zfs_get_physical(special, physarray, n);
7913 } else if (is_ufs(menu_root)) {
7914 ret = ufs_get_physical(special, physarray, n);
7915 } else {
7916 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
7917 ret = -1;
7920 free(special);
7922 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7923 if (ret == -1) {
7924 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7925 } else {
7926 int i;
7927 assert (*n > 0);
7928 for (i = 0; i < *n; i++) {
7929 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
7933 return (ret);
7936 static int
7937 is_bootdisk(char *osroot, char *physical)
7939 int ret;
7940 char *grubroot;
7941 char *bootp;
7942 const char *fcn = "is_bootdisk()";
7944 assert(osroot);
7945 assert(physical);
7947 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
7949 bootp = strstr(physical, "p0:boot");
7950 if (bootp)
7951 *bootp = '\0';
7953 * We just want the BIOS mapping for menu disk.
7954 * Don't pass menu_root to get_grubroot() as the
7955 * check that it is used for is not relevant here.
7956 * The osroot is immaterial as well - it is only used to
7957 * to find create_diskmap script. Everything hinges on
7958 * "physical"
7960 grubroot = get_grubroot(osroot, physical, NULL);
7962 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
7963 if (grubroot == NULL) {
7964 if (bam_verbose)
7965 bam_error(NO_GRUBROOT_FOR_DISK, physical);
7966 return (0);
7968 ret = grubroot[3] == '0';
7969 free(grubroot);
7971 BAM_DPRINTF((D_RETURN_RET, fcn, ret));
7973 return (ret);
7977 * Check if menu is on the boot device
7978 * Return 0 (false) on error
7980 static int
7981 menu_on_bootdisk(char *osroot, char *menu_root)
7983 char **physarray;
7984 int ret;
7985 int n;
7986 int i;
7987 int on_bootdisk;
7988 const char *fcn = "menu_on_bootdisk()";
7990 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
7992 ret = get_physical(menu_root, &physarray, &n);
7993 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
7994 if (ret != 0) {
7995 bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
7996 return (0);
7999 assert(physarray);
8000 assert(n > 0);
8002 on_bootdisk = 0;
8003 for (i = 0; i < n; i++) {
8004 assert(strncmp(physarray[i], "/dev/dsk/",
8005 strlen("/dev/dsk/")) == 0 ||
8006 strncmp(physarray[i], "/dev/rdsk/",
8007 strlen("/dev/rdsk/")) == 0);
8009 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
8010 if (is_bootdisk(osroot, physarray[i])) {
8011 on_bootdisk = 1;
8012 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
8016 free_physarray(physarray, n);
8018 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8019 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8020 if (on_bootdisk) {
8021 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8022 } else {
8023 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8026 return (on_bootdisk);
8029 void
8030 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8032 const char *fcn = "bam_add_line()";
8034 assert(mp);
8035 assert(entry);
8036 assert(prev);
8037 assert(lp);
8039 lp->next = prev->next;
8040 if (prev->next) {
8041 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
8042 prev->next->prev = lp;
8043 } else {
8044 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
8046 prev->next = lp;
8047 lp->prev = prev;
8049 if (entry->end == prev) {
8050 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
8051 entry->end = lp;
8053 if (mp->end == prev) {
8054 assert(lp->next == NULL);
8055 mp->end = lp;
8056 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
8061 * look for matching bootadm entry with specified parameters
8062 * Here are the rules (based on existing usage):
8063 * - If title is specified, match on title only
8064 * - Else, match on root/findroot, kernel, and module.
8065 * Note that, if root_opt is non-zero, the absence of
8066 * root line is considered a match.
8068 static entry_t *
8069 find_boot_entry(
8070 menu_t *mp,
8071 char *title,
8072 char *kernel,
8073 char *findroot,
8074 char *root,
8075 char *module,
8076 int root_opt,
8077 int *entry_num)
8079 int i;
8080 line_t *lp;
8081 entry_t *ent;
8082 const char *fcn = "find_boot_entry()";
8084 if (entry_num)
8085 *entry_num = BAM_ERROR;
8087 /* find matching entry */
8088 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8089 lp = ent->start;
8091 /* first line of entry must be bootadm comment */
8092 lp = ent->start;
8093 if (lp->flags != BAM_COMMENT ||
8094 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8095 continue;
8098 /* advance to title line */
8099 lp = lp->next;
8100 if (title) {
8101 if (lp->flags == BAM_TITLE && lp->arg &&
8102 strcmp(lp->arg, title) == 0) {
8103 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
8104 break;
8106 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
8107 continue; /* check title only */
8110 lp = lp->next; /* advance to root line */
8111 if (lp == NULL) {
8112 continue;
8113 } else if (lp->cmd != NULL &&
8114 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8115 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8116 findroot = NULL);
8117 if (findroot == NULL) {
8118 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
8119 fcn, lp->arg));
8120 continue;
8122 /* findroot command found, try match */
8123 if (strcmp(lp->arg, findroot) != 0) {
8124 BAM_DPRINTF((D_NOMATCH_FINDROOT,
8125 fcn, findroot, lp->arg));
8126 continue;
8128 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
8129 lp = lp->next; /* advance to kernel line */
8130 } else if (lp->cmd != NULL &&
8131 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8132 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8133 if (root == NULL) {
8134 BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
8135 fcn, lp->arg));
8136 continue;
8138 /* root cmd found, try match */
8139 if (strcmp(lp->arg, root) != 0) {
8140 BAM_DPRINTF((D_NOMATCH_ROOT,
8141 fcn, root, lp->arg));
8142 continue;
8144 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
8145 lp = lp->next; /* advance to kernel line */
8146 } else {
8147 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8148 root_opt = 0);
8149 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8150 root_opt = 1);
8151 /* no root command, see if root is optional */
8152 if (root_opt == 0) {
8153 BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
8154 continue;
8156 BAM_DPRINTF((D_ROOT_OPT, fcn));
8159 if (lp == NULL || lp->next == NULL) {
8160 continue;
8163 if (kernel &&
8164 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8165 if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8166 !(ent->flags & BAM_ENTRY_DBOOT) ||
8167 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8168 continue;
8170 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8173 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
8176 * Check for matching module entry (failsafe or normal).
8177 * If it fails to match, we go around the loop again.
8178 * For xpv entries, there are two module lines, so we
8179 * do the check twice.
8181 lp = lp->next; /* advance to module line */
8182 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8183 (((lp = lp->next) != NULL) &&
8184 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8185 /* match found */
8186 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
8187 break;
8190 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8191 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8192 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8193 ent->flags |= BAM_ENTRY_UPGFSMODULE;
8194 break;
8199 if (ent && entry_num) {
8200 *entry_num = i;
8203 if (ent) {
8204 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8205 } else {
8206 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
8208 return (ent);
8211 static int
8212 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8213 char *kernel, char *mod_kernel, char *module, int root_opt)
8215 int i;
8216 int change_kernel = 0;
8217 entry_t *ent;
8218 line_t *lp;
8219 line_t *tlp;
8220 char linebuf[BAM_MAXLINE];
8221 const char *fcn = "update_boot_entry()";
8223 /* note: don't match on title, it's updated on upgrade */
8224 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8225 root_opt, &i);
8226 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8228 * We may be upgrading a kernel from multiboot to
8229 * directboot. Look for a multiboot entry. A multiboot
8230 * entry will not have a findroot line.
8232 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8233 MULTIBOOT_ARCHIVE, root_opt, &i);
8234 if (ent != NULL) {
8235 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
8236 change_kernel = 1;
8238 } else if (ent) {
8239 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
8242 if (ent == NULL) {
8243 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
8244 return (add_boot_entry(mp, title, findroot,
8245 kernel, mod_kernel, module, NULL));
8248 /* replace title of existing entry and update findroot line */
8249 lp = ent->start;
8250 lp = lp->next; /* title line */
8251 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8252 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8253 free(lp->arg);
8254 free(lp->line);
8255 lp->arg = s_strdup(title);
8256 lp->line = s_strdup(linebuf);
8257 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
8259 tlp = lp; /* title line */
8260 lp = lp->next; /* root line */
8262 /* if no root or findroot command, create a new line_t */
8263 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8264 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8265 lp = s_calloc(1, sizeof (line_t));
8266 bam_add_line(mp, ent, tlp, lp);
8267 } else {
8268 if (lp->cmd != NULL)
8269 free(lp->cmd);
8271 free(lp->sep);
8272 free(lp->arg);
8273 free(lp->line);
8276 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8277 lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8278 lp->arg = s_strdup(findroot);
8279 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8280 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8281 lp->line = s_strdup(linebuf);
8282 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
8284 /* kernel line */
8285 lp = lp->next;
8287 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8288 char *params = NULL;
8290 params = strstr(lp->line, "-s");
8291 if (params != NULL)
8292 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8293 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8294 kernel, params+2);
8295 else
8296 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8297 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8298 kernel);
8300 if (lp->cmd != NULL)
8301 free(lp->cmd);
8303 free(lp->arg);
8304 free(lp->line);
8305 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8306 lp->arg = s_strdup(strstr(linebuf, "/"));
8307 lp->line = s_strdup(linebuf);
8308 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8309 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd));
8312 if (change_kernel) {
8314 * We're upgrading from multiboot to directboot.
8316 if (lp->cmd != NULL &&
8317 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8318 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8319 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8320 kernel);
8321 free(lp->cmd);
8322 free(lp->arg);
8323 free(lp->line);
8324 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8325 lp->arg = s_strdup(kernel);
8326 lp->line = s_strdup(linebuf);
8327 lp = lp->next;
8328 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
8330 if (lp->cmd != NULL &&
8331 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8332 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8333 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8334 module);
8335 free(lp->cmd);
8336 free(lp->arg);
8337 free(lp->line);
8338 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8339 lp->arg = s_strdup(module);
8340 lp->line = s_strdup(linebuf);
8341 lp = lp->next;
8342 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8346 /* module line */
8347 lp = lp->next;
8349 if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8350 if (lp->cmd != NULL &&
8351 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8352 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8353 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8354 module);
8355 free(lp->cmd);
8356 free(lp->arg);
8357 free(lp->line);
8358 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8359 lp->arg = s_strdup(module);
8360 lp->line = s_strdup(linebuf);
8361 lp = lp->next;
8362 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8363 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8367 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8368 return (i);
8372 root_optional(char *osroot, char *menu_root)
8374 char *ospecial;
8375 char *mspecial;
8376 char *slash;
8377 int root_opt;
8378 int ret1;
8379 int ret2;
8380 const char *fcn = "root_optional()";
8382 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
8385 * For all filesystems except ZFS, a straight compare of osroot
8386 * and menu_root will tell us if root is optional.
8387 * For ZFS, the situation is complicated by the fact that
8388 * menu_root and osroot are always different
8390 ret1 = is_zfs(osroot);
8391 ret2 = is_zfs(menu_root);
8392 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8393 if (!ret1 || !ret2) {
8394 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
8395 root_opt = (strcmp(osroot, menu_root) == 0);
8396 goto out;
8399 ospecial = get_special(osroot);
8400 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8401 if (ospecial == NULL) {
8402 bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
8403 return (0);
8405 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
8407 mspecial = get_special(menu_root);
8408 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8409 if (mspecial == NULL) {
8410 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
8411 free(ospecial);
8412 return (0);
8414 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
8416 slash = strchr(ospecial, '/');
8417 if (slash)
8418 *slash = '\0';
8419 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
8421 root_opt = (strcmp(ospecial, mspecial) == 0);
8423 free(ospecial);
8424 free(mspecial);
8426 out:
8427 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8428 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8429 if (root_opt) {
8430 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8431 } else {
8432 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8435 return (root_opt);
8438 /*ARGSUSED*/
8439 static error_t
8440 update_entry(menu_t *mp, char *menu_root, char *osdev)
8442 int entry;
8443 char *grubsign;
8444 char *grubroot;
8445 char *title;
8446 char osroot[PATH_MAX];
8447 char *failsafe_kernel = NULL;
8448 struct stat sbuf;
8449 char failsafe[256];
8450 char failsafe_64[256];
8451 int ret;
8452 const char *fcn = "update_entry()";
8454 assert(mp);
8455 assert(menu_root);
8456 assert(osdev);
8457 assert(bam_root);
8459 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
8461 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8463 title = get_title(osroot);
8464 assert(title);
8466 grubsign = get_grubsign(osroot, osdev);
8467 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8468 if (grubsign == NULL) {
8469 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
8470 return (BAM_ERROR);
8474 * It is not a fatal error if get_grubroot() fails
8475 * We no longer rely on biosdev to populate the
8476 * menu
8478 grubroot = get_grubroot(osroot, osdev, menu_root);
8479 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8480 if (grubroot) {
8481 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
8482 fcn, osroot, osdev, menu_root));
8483 } else {
8484 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
8485 fcn, osroot, osdev, menu_root));
8488 /* add the entry for normal Solaris */
8489 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8490 bam_direct = BAM_DIRECT_MULTIBOOT);
8491 if (bam_direct == BAM_DIRECT_DBOOT) {
8492 entry = update_boot_entry(mp, title, grubsign, grubroot,
8493 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8494 NULL, DIRECT_BOOT_ARCHIVE,
8495 root_optional(osroot, menu_root));
8496 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
8497 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8498 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8499 grubroot, XEN_MENU, bam_zfs ?
8500 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8501 DIRECT_BOOT_ARCHIVE,
8502 root_optional(osroot, menu_root));
8503 BAM_DPRINTF((D_UPDATED_HV_ENTRY,
8504 fcn, bam_zfs, grubsign));
8506 } else {
8507 entry = update_boot_entry(mp, title, grubsign, grubroot,
8508 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8509 root_optional(osroot, menu_root));
8511 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
8515 * Add the entry for failsafe archive. On a bfu'd system, the
8516 * failsafe may be different than the installed kernel.
8518 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8519 osroot, FAILSAFE_ARCHIVE_32);
8520 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8521 osroot, FAILSAFE_ARCHIVE_64);
8524 * Check if at least one of the two archives exists
8525 * Using $ISADIR as the default line, we have an entry which works
8526 * for both the cases.
8529 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8531 /* Figure out where the kernel line should point */
8532 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8533 DIRECT_BOOT_FAILSAFE_32);
8534 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8535 osroot, DIRECT_BOOT_FAILSAFE_64);
8536 if (stat(failsafe, &sbuf) == 0 ||
8537 stat(failsafe_64, &sbuf) == 0) {
8538 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8539 } else {
8540 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8541 osroot, MULTI_BOOT_FAILSAFE);
8542 if (stat(failsafe, &sbuf) == 0) {
8543 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8546 if (failsafe_kernel != NULL) {
8547 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8548 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8549 root_optional(osroot, menu_root));
8550 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
8551 failsafe_kernel));
8554 free(grubroot);
8556 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8557 if (entry == BAM_ERROR) {
8558 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
8559 free(grubsign);
8560 return (BAM_ERROR);
8562 free(grubsign);
8564 update_numbering(mp);
8565 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8566 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8567 if (ret == BAM_ERROR) {
8568 bam_error(SET_DEFAULT_FAILED, entry);
8570 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8571 return (BAM_WRITE);
8574 static void
8575 save_default_entry(menu_t *mp, const char *which)
8577 int lineNum;
8578 int entryNum;
8579 int entry = 0; /* default is 0 */
8580 char linebuf[BAM_MAXLINE];
8581 line_t *lp = mp->curdefault;
8582 const char *fcn = "save_default_entry()";
8584 if (mp->start) {
8585 lineNum = mp->end->lineNum;
8586 entryNum = mp->end->entryNum;
8587 } else {
8588 lineNum = LINE_INIT;
8589 entryNum = ENTRY_INIT;
8592 if (lp)
8593 entry = s_strtol(lp->arg);
8595 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8596 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
8597 line_parser(mp, linebuf, &lineNum, &entryNum);
8598 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
8601 static void
8602 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8604 int entry;
8605 char *str;
8606 const char *fcn = "restore_default_entry()";
8608 if (lp == NULL) {
8609 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
8610 return; /* nothing to restore */
8613 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
8615 str = lp->arg + strlen(which);
8616 entry = s_strtol(str);
8617 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8619 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
8621 /* delete saved old default line */
8622 unlink_line(mp, lp);
8623 line_free(lp);
8627 * This function is for supporting reboot with args.
8628 * The opt value can be:
8629 * NULL delete temp entry, if present
8630 * entry=<n> switches default entry to <n>
8631 * else treated as boot-args and setup a temperary menu entry
8632 * and make it the default
8633 * Note that we are always rebooting the current OS instance
8634 * so osroot == / always.
8636 #define REBOOT_TITLE "Solaris_reboot_transient"
8638 /*ARGSUSED*/
8639 static error_t
8640 update_temp(menu_t *mp, char *dummy, char *opt)
8642 int entry;
8643 char *osdev;
8644 char *fstype;
8645 char *sign;
8646 char *opt_ptr;
8647 char *path;
8648 char kernbuf[BUFSIZ];
8649 char args_buf[BUFSIZ];
8650 char signbuf[PATH_MAX];
8651 int ret;
8652 const char *fcn = "update_temp()";
8654 assert(mp);
8655 assert(dummy == NULL);
8657 /* opt can be NULL */
8658 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
8659 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
8661 if (bam_alt_root || bam_rootlen != 1 ||
8662 strcmp(bam_root, "/") != 0 ||
8663 strcmp(rootbuf, "/") != 0) {
8664 bam_error(ALT_ROOT_INVALID, bam_root);
8665 return (BAM_ERROR);
8668 /* If no option, delete exiting reboot menu entry */
8669 if (opt == NULL) {
8670 entry_t *ent;
8671 BAM_DPRINTF((D_OPT_NULL, fcn));
8672 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8673 NULL, NULL, 0, &entry);
8674 if (ent == NULL) { /* not found is ok */
8675 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
8676 return (BAM_SUCCESS);
8678 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8679 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8680 mp->olddefault = NULL;
8681 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
8682 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8683 return (BAM_WRITE);
8686 /* if entry= is specified, set the default entry */
8687 if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8688 int entryNum = s_strtol(opt + strlen("entry="));
8689 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
8690 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8691 /* this is entry=# option */
8692 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8693 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
8694 return (ret);
8695 } else {
8696 bam_error(SET_DEFAULT_FAILED, entryNum);
8697 return (BAM_ERROR);
8702 * add a new menu entry based on opt and make it the default
8705 fstype = get_fstype("/");
8706 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8707 if (fstype == NULL) {
8708 bam_error(REBOOT_FSTYPE_FAILED);
8709 return (BAM_ERROR);
8712 osdev = get_special("/");
8713 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8714 if (osdev == NULL) {
8715 free(fstype);
8716 bam_error(REBOOT_SPECIAL_FAILED);
8717 return (BAM_ERROR);
8720 sign = find_existing_sign("/", osdev, fstype);
8721 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8722 if (sign == NULL) {
8723 free(fstype);
8724 free(osdev);
8725 bam_error(REBOOT_SIGN_FAILED);
8726 return (BAM_ERROR);
8729 free(osdev);
8730 (void) strlcpy(signbuf, sign, sizeof (signbuf));
8731 free(sign);
8733 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8734 strchr(signbuf, ')') == NULL);
8737 * There is no alternate root while doing reboot with args
8738 * This version of bootadm is only delivered with a DBOOT
8739 * version of Solaris.
8741 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8742 if (bam_direct != BAM_DIRECT_DBOOT) {
8743 free(fstype);
8744 bam_error(REBOOT_DIRECT_FAILED);
8745 return (BAM_ERROR);
8748 /* add an entry for Solaris reboot */
8749 if (opt[0] == '-') {
8750 /* It's an option - first see if boot-file is set */
8751 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8752 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8753 if (ret != BAM_SUCCESS) {
8754 free(fstype);
8755 bam_error(REBOOT_GET_KERNEL_FAILED);
8756 return (BAM_ERROR);
8758 if (kernbuf[0] == '\0')
8759 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8760 sizeof (kernbuf));
8762 * If this is a zfs file system and kernbuf does not
8763 * have "-B $ZFS-BOOTFS" string yet, add it.
8765 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8766 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8767 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8769 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8770 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8771 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
8772 } else if (opt[0] == '/') {
8773 /* It's a full path, so write it out. */
8774 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8777 * If someone runs:
8779 * # eeprom boot-args='-kd'
8780 * # reboot /platform/i86pc/kernel/unix
8782 * we want to use the boot-args as part of the boot
8783 * line. On the other hand, if someone runs:
8785 * # reboot "/platform/i86pc/kernel/unix -kd"
8787 * we don't need to mess with boot-args. If there's
8788 * no space in the options string, assume we're in the
8789 * first case.
8791 if (strchr(opt, ' ') == NULL) {
8792 ret = get_kernel(mp, ARGS_CMD, args_buf,
8793 sizeof (args_buf));
8794 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8795 if (ret != BAM_SUCCESS) {
8796 free(fstype);
8797 bam_error(REBOOT_GET_ARGS_FAILED);
8798 return (BAM_ERROR);
8801 if (args_buf[0] != '\0') {
8802 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8803 (void) strlcat(kernbuf, args_buf,
8804 sizeof (kernbuf));
8807 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
8808 } else {
8810 * It may be a partial path, or it may be a partial
8811 * path followed by options. Assume that only options
8812 * follow a space. If someone sends us a kernel path
8813 * that includes a space, they deserve to be broken.
8815 opt_ptr = strchr(opt, ' ');
8816 if (opt_ptr != NULL) {
8817 *opt_ptr = '\0';
8820 path = expand_path(opt);
8821 if (path != NULL) {
8822 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8823 free(path);
8826 * If there were options given, use those.
8827 * Otherwise, copy over the default options.
8829 if (opt_ptr != NULL) {
8830 /* Restore the space in opt string */
8831 *opt_ptr = ' ';
8832 (void) strlcat(kernbuf, opt_ptr,
8833 sizeof (kernbuf));
8834 } else {
8835 ret = get_kernel(mp, ARGS_CMD, args_buf,
8836 sizeof (args_buf));
8837 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8838 ret = BAM_ERROR);
8839 if (ret != BAM_SUCCESS) {
8840 free(fstype);
8841 bam_error(REBOOT_GET_ARGS_FAILED);
8842 return (BAM_ERROR);
8845 if (args_buf[0] != '\0') {
8846 (void) strlcat(kernbuf, " ",
8847 sizeof (kernbuf));
8848 (void) strlcat(kernbuf,
8849 args_buf, sizeof (kernbuf));
8852 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
8853 } else {
8854 free(fstype);
8855 bam_error(UNKNOWN_KERNEL, opt);
8856 bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
8857 return (BAM_ERROR);
8860 free(fstype);
8861 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8862 NULL, NULL, NULL);
8863 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8864 if (entry == BAM_ERROR) {
8865 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
8866 return (BAM_ERROR);
8869 save_default_entry(mp, BAM_OLDDEF);
8870 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8871 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8872 if (ret == BAM_ERROR) {
8873 bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
8875 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8876 return (BAM_WRITE);
8879 error_t
8880 set_global(menu_t *mp, char *globalcmd, int val)
8882 line_t *lp;
8883 line_t *found;
8884 line_t *last;
8885 char *cp;
8886 char *str;
8887 char prefix[BAM_MAXLINE];
8888 size_t len;
8889 const char *fcn = "set_global()";
8891 assert(mp);
8892 assert(globalcmd);
8894 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
8895 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
8896 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
8897 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
8898 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
8899 (void) snprintf(prefix, sizeof (prefix), "%d", val);
8900 bam_error(INVALID_ENTRY, prefix);
8901 return (BAM_ERROR);
8905 found = last = NULL;
8906 for (lp = mp->start; lp; lp = lp->next) {
8907 if (lp->flags != BAM_GLOBAL)
8908 continue;
8910 last = lp; /* track the last global found */
8912 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
8913 if (lp->cmd == NULL) {
8914 bam_error(NO_CMD, lp->lineNum);
8915 continue;
8917 if (strcmp(globalcmd, lp->cmd) != 0)
8918 continue;
8920 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
8922 if (found) {
8923 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
8925 found = lp;
8928 if (found == NULL) {
8929 lp = s_calloc(1, sizeof (line_t));
8930 if (last == NULL) {
8931 lp->next = mp->start;
8932 mp->start = lp;
8933 mp->end = (mp->end) ? mp->end : lp;
8934 } else {
8935 lp->next = last->next;
8936 last->next = lp;
8937 if (lp->next == NULL)
8938 mp->end = lp;
8940 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
8941 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
8942 len += 10; /* val < 10 digits */
8943 lp->line = s_calloc(1, len);
8944 (void) snprintf(lp->line, len, "%s%s%d",
8945 globalcmd, menu_cmds[SEP_CMD], val);
8946 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
8947 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8948 return (BAM_WRITE);
8952 * We are changing an existing entry. Retain any prefix whitespace,
8953 * but overwrite everything else. This preserves tabs added for
8954 * readability.
8956 str = found->line;
8957 cp = prefix;
8958 while (*str == ' ' || *str == '\t')
8959 *(cp++) = *(str++);
8960 *cp = '\0'; /* Terminate prefix */
8961 len = strlen(prefix) + strlen(globalcmd);
8962 len += strlen(menu_cmds[SEP_CMD]) + 10;
8964 free(found->line);
8965 found->line = s_calloc(1, len);
8966 (void) snprintf(found->line, len,
8967 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
8969 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
8970 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8971 return (BAM_WRITE); /* need a write to menu */
8975 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
8976 * expand it to a full unix path. The calling function is expected to
8977 * output a message if an error occurs and NULL is returned.
8979 static char *
8980 expand_path(const char *partial_path)
8982 int new_path_len;
8983 char *new_path;
8984 char new_path2[PATH_MAX];
8985 struct stat sb;
8986 const char *fcn = "expand_path()";
8988 new_path_len = strlen(partial_path) + 64;
8989 new_path = s_calloc(1, new_path_len);
8991 /* First, try the simplest case - something like "kernel/unix" */
8992 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
8993 partial_path);
8994 if (stat(new_path, &sb) == 0) {
8995 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8996 return (new_path);
8999 if (strcmp(partial_path, "kmdb") == 0) {
9000 (void) snprintf(new_path, new_path_len, "%s -k",
9001 DIRECT_BOOT_KERNEL);
9002 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
9003 return (new_path);
9007 * We've quickly reached unsupported usage. Try once more to
9008 * see if we were just given a glom name.
9010 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9011 partial_path);
9012 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9013 partial_path);
9014 if (stat(new_path, &sb) == 0) {
9015 if (stat(new_path2, &sb) == 0) {
9017 * We matched both, so we actually
9018 * want to write the $ISADIR version.
9020 (void) snprintf(new_path, new_path_len,
9021 "/platform/i86pc/kernel/%s/$ISADIR/unix",
9022 partial_path);
9024 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
9025 return (new_path);
9028 free(new_path);
9029 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9030 return (NULL);
9034 * The kernel cmd and arg have been changed, so
9035 * check whether the archive line needs to change.
9037 static void
9038 set_archive_line(entry_t *entryp, line_t *kernelp)
9040 line_t *lp = entryp->start;
9041 char *new_archive;
9042 menu_cmd_t m_cmd;
9043 const char *fcn = "set_archive_line()";
9045 for (; lp != NULL; lp = lp->next) {
9046 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9047 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9048 break;
9051 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9052 if (lp == entryp->end) {
9053 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
9054 entryp->entryNum));
9055 return;
9058 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9059 if (lp == NULL) {
9060 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
9061 return;
9064 if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9065 new_archive = DIRECT_BOOT_ARCHIVE;
9066 m_cmd = MODULE_DOLLAR_CMD;
9067 } else if (strstr(kernelp->arg, "amd64") != NULL) {
9068 new_archive = DIRECT_BOOT_ARCHIVE_64;
9069 m_cmd = MODULE_CMD;
9070 } else {
9071 new_archive = DIRECT_BOOT_ARCHIVE_32;
9072 m_cmd = MODULE_CMD;
9075 if (strcmp(lp->arg, new_archive) == 0) {
9076 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
9077 return;
9080 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9081 free(lp->cmd);
9082 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9085 free(lp->arg);
9086 lp->arg = s_strdup(new_archive);
9087 update_line(lp);
9088 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
9092 * Title for an entry to set properties that once went in bootenv.rc.
9094 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
9097 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9098 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
9099 * string, reset the value to the default. If path is a non-zero-length
9100 * string, set the kernel or arguments.
9102 static error_t
9103 get_set_kernel(
9104 menu_t *mp,
9105 menu_cmd_t optnum,
9106 char *path,
9107 char *buf,
9108 size_t bufsize)
9110 int entryNum;
9111 int rv = BAM_SUCCESS;
9112 int free_new_path = 0;
9113 entry_t *entryp;
9114 line_t *ptr;
9115 line_t *kernelp;
9116 char *new_arg;
9117 char *old_args;
9118 char *space;
9119 char *new_path;
9120 char old_space;
9121 size_t old_kernel_len = 0;
9122 size_t new_str_len;
9123 char *fstype;
9124 char *osdev;
9125 char *sign;
9126 char signbuf[PATH_MAX];
9127 int ret;
9128 const char *fcn = "get_set_kernel()";
9130 assert(bufsize > 0);
9132 ptr = kernelp = NULL;
9133 new_arg = old_args = space = NULL;
9134 new_path = NULL;
9135 buf[0] = '\0';
9137 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9138 bam_direct = BAM_DIRECT_MULTIBOOT);
9139 if (bam_direct != BAM_DIRECT_DBOOT) {
9140 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
9141 return (BAM_ERROR);
9145 * If a user changed the default entry to a non-bootadm controlled
9146 * one, we don't want to mess with it. Just print an error and
9147 * return.
9149 if (mp->curdefault) {
9150 entryNum = s_strtol(mp->curdefault->arg);
9151 for (entryp = mp->entries; entryp; entryp = entryp->next) {
9152 if (entryp->entryNum == entryNum)
9153 break;
9155 if ((entryp != NULL) &&
9156 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9157 bam_error(DEFAULT_NOT_BAM);
9158 return (BAM_ERROR);
9162 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9163 0, &entryNum);
9165 if (entryp != NULL) {
9166 for (ptr = entryp->start; ptr && ptr != entryp->end;
9167 ptr = ptr->next) {
9168 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9169 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9170 kernelp = ptr;
9171 break;
9174 if (kernelp == NULL) {
9175 bam_error(NO_KERNEL, entryNum);
9176 return (BAM_ERROR);
9179 old_kernel_len = strcspn(kernelp->arg, " \t");
9180 space = old_args = kernelp->arg + old_kernel_len;
9181 while ((*old_args == ' ') || (*old_args == '\t'))
9182 old_args++;
9185 if (path == NULL) {
9186 if (entryp == NULL) {
9187 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
9188 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9189 return (BAM_SUCCESS);
9191 assert(kernelp);
9192 if (optnum == ARGS_CMD) {
9193 if (old_args[0] != '\0') {
9194 (void) strlcpy(buf, old_args, bufsize);
9195 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
9197 } else {
9199 * We need to print the kernel, so we just turn the
9200 * first space into a '\0' and print the beginning.
9201 * We don't print anything if it's the default kernel.
9203 old_space = *space;
9204 *space = '\0';
9205 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9206 (void) strlcpy(buf, kernelp->arg, bufsize);
9207 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
9209 *space = old_space;
9211 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9212 return (BAM_SUCCESS);
9216 * First, check if we're resetting an entry to the default.
9218 if ((path[0] == '\0') ||
9219 ((optnum == KERNEL_CMD) &&
9220 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9221 if ((entryp == NULL) || (kernelp == NULL)) {
9222 /* No previous entry, it's already the default */
9223 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
9224 return (BAM_SUCCESS);
9228 * Check if we can delete the entry. If we're resetting the
9229 * kernel command, and the args is already empty, or if we're
9230 * resetting the args command, and the kernel is already the
9231 * default, we can restore the old default and delete the entry.
9233 if (((optnum == KERNEL_CMD) &&
9234 ((old_args == NULL) || (old_args[0] == '\0'))) ||
9235 ((optnum == ARGS_CMD) &&
9236 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9237 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9238 kernelp = NULL;
9239 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9240 restore_default_entry(mp, BAM_OLD_RC_DEF,
9241 mp->old_rc_default);
9242 mp->old_rc_default = NULL;
9243 rv = BAM_WRITE;
9244 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
9245 goto done;
9248 if (optnum == KERNEL_CMD) {
9250 * At this point, we've already checked that old_args
9251 * and entryp are valid pointers. The "+ 2" is for
9252 * a space a the string termination character.
9254 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9255 strlen(old_args) + 2;
9256 new_arg = s_calloc(1, new_str_len);
9257 (void) snprintf(new_arg, new_str_len, "%s %s",
9258 DIRECT_BOOT_KERNEL, old_args);
9259 free(kernelp->arg);
9260 kernelp->arg = new_arg;
9263 * We have changed the kernel line, so we may need
9264 * to update the archive line as well.
9266 set_archive_line(entryp, kernelp);
9267 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
9268 fcn, kernelp->arg));
9269 } else {
9271 * We're resetting the boot args to nothing, so
9272 * we only need to copy the kernel. We've already
9273 * checked that the kernel is not the default.
9275 new_arg = s_calloc(1, old_kernel_len + 1);
9276 (void) snprintf(new_arg, old_kernel_len + 1, "%s",
9277 kernelp->arg);
9278 free(kernelp->arg);
9279 kernelp->arg = new_arg;
9280 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
9281 fcn, kernelp->arg));
9283 rv = BAM_WRITE;
9284 goto done;
9288 * Expand the kernel file to a full path, if necessary
9290 if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9291 new_path = expand_path(path);
9292 if (new_path == NULL) {
9293 bam_error(UNKNOWN_KERNEL, path);
9294 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9295 return (BAM_ERROR);
9297 free_new_path = 1;
9298 } else {
9299 new_path = path;
9300 free_new_path = 0;
9304 * At this point, we know we're setting a new value. First, take care
9305 * of the case where there was no previous entry.
9307 if (entryp == NULL) {
9309 /* Similar to code in update_temp */
9310 fstype = get_fstype("/");
9311 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9312 if (fstype == NULL) {
9313 bam_error(BOOTENV_FSTYPE_FAILED);
9314 rv = BAM_ERROR;
9315 goto done;
9318 osdev = get_special("/");
9319 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9320 if (osdev == NULL) {
9321 free(fstype);
9322 bam_error(BOOTENV_SPECIAL_FAILED);
9323 rv = BAM_ERROR;
9324 goto done;
9327 sign = find_existing_sign("/", osdev, fstype);
9328 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9329 if (sign == NULL) {
9330 free(fstype);
9331 free(osdev);
9332 bam_error(BOOTENV_SIGN_FAILED);
9333 rv = BAM_ERROR;
9334 goto done;
9337 free(osdev);
9338 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9339 free(sign);
9340 assert(strchr(signbuf, '(') == NULL &&
9341 strchr(signbuf, ',') == NULL &&
9342 strchr(signbuf, ')') == NULL);
9344 if (optnum == KERNEL_CMD) {
9345 if (strcmp(fstype, "zfs") == 0) {
9346 new_str_len = strlen(new_path) +
9347 strlen(ZFS_BOOT) + 8;
9348 new_arg = s_calloc(1, new_str_len);
9349 (void) snprintf(new_arg, new_str_len, "%s %s",
9350 new_path, ZFS_BOOT);
9351 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9352 new_arg));
9353 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9354 signbuf, new_arg, NULL, NULL, NULL);
9355 free(new_arg);
9356 } else {
9357 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9358 new_path));
9359 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9360 signbuf, new_path, NULL, NULL, NULL);
9362 } else {
9363 new_str_len = strlen(path) + 8;
9364 if (strcmp(fstype, "zfs") == 0) {
9365 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9366 new_arg = s_calloc(1, new_str_len);
9367 (void) snprintf(new_arg, new_str_len, "%s %s",
9368 DIRECT_BOOT_KERNEL_ZFS, path);
9369 } else {
9370 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9371 new_arg = s_calloc(1, new_str_len);
9372 (void) snprintf(new_arg, new_str_len, "%s %s",
9373 DIRECT_BOOT_KERNEL, path);
9376 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
9377 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9378 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9379 free(new_arg);
9381 free(fstype);
9382 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9383 entryNum = BAM_ERROR);
9384 if (entryNum == BAM_ERROR) {
9385 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
9386 BOOTENV_RC_TITLE);
9387 rv = BAM_ERROR;
9388 goto done;
9390 save_default_entry(mp, BAM_OLD_RC_DEF);
9391 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9392 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9393 if (ret == BAM_ERROR) {
9394 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
9396 rv = BAM_WRITE;
9397 goto done;
9401 * There was already an bootenv entry which we need to edit.
9403 if (optnum == KERNEL_CMD) {
9404 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9405 new_arg = s_calloc(1, new_str_len);
9406 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9407 old_args);
9408 free(kernelp->arg);
9409 kernelp->arg = new_arg;
9412 * If we have changed the kernel line, we may need to update
9413 * the archive line as well.
9415 set_archive_line(entryp, kernelp);
9416 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
9417 kernelp->arg));
9418 } else {
9419 new_str_len = old_kernel_len + strlen(path) + 8;
9420 new_arg = s_calloc(1, new_str_len);
9421 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9422 (void) strlcat(new_arg, " ", new_str_len);
9423 (void) strlcat(new_arg, path, new_str_len);
9424 free(kernelp->arg);
9425 kernelp->arg = new_arg;
9426 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
9427 kernelp->arg));
9429 rv = BAM_WRITE;
9431 done:
9432 if ((rv == BAM_WRITE) && kernelp)
9433 update_line(kernelp);
9434 if (free_new_path)
9435 free(new_path);
9436 if (rv == BAM_WRITE) {
9437 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9438 } else {
9439 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9441 return (rv);
9444 static error_t
9445 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9447 const char *fcn = "get_kernel()";
9448 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
9449 return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9452 static error_t
9453 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9455 const char *fcn = "set_kernel()";
9456 assert(path != NULL);
9457 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
9458 return (get_set_kernel(mp, optnum, path, buf, bufsize));
9461 /*ARGSUSED*/
9462 static error_t
9463 set_option(menu_t *mp, char *dummy, char *opt)
9465 int optnum;
9466 int optval;
9467 char *val;
9468 char buf[BUFSIZ] = "";
9469 error_t rv;
9470 const char *fcn = "set_option()";
9472 assert(mp);
9473 assert(opt);
9474 assert(dummy == NULL);
9476 /* opt is set from bam_argv[0] and is always non-NULL */
9477 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
9479 val = strchr(opt, '=');
9480 if (val != NULL) {
9481 *val = '\0';
9484 if (strcmp(opt, "default") == 0) {
9485 optnum = DEFAULT_CMD;
9486 } else if (strcmp(opt, "timeout") == 0) {
9487 optnum = TIMEOUT_CMD;
9488 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9489 optnum = KERNEL_CMD;
9490 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9491 optnum = ARGS_CMD;
9492 } else {
9493 bam_error(INVALID_OPTION, opt);
9494 return (BAM_ERROR);
9498 * kernel and args are allowed without "=new_value" strings. All
9499 * others cause errors
9501 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9502 bam_error(NO_OPTION_ARG, opt);
9503 return (BAM_ERROR);
9504 } else if (val != NULL) {
9505 *val = '=';
9508 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9509 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
9510 val ? val + 1 : "NULL"));
9512 if (val)
9513 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9514 else
9515 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9516 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9517 (void) printf("%s\n", buf);
9518 } else {
9519 optval = s_strtol(val + 1);
9520 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
9521 rv = set_global(mp, menu_cmds[optnum], optval);
9524 if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9525 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9526 } else {
9527 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9530 return (rv);
9534 * The quiet argument suppresses messages. This is used
9535 * when invoked in the context of other commands (e.g. list_entry)
9537 static error_t
9538 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9540 line_t *lp;
9541 char *arg;
9542 int done, ret = BAM_SUCCESS;
9544 assert(mp);
9545 assert(menu_path);
9546 assert(globalcmd);
9548 if (mp->start == NULL) {
9549 if (!quiet)
9550 bam_error(NO_MENU, menu_path);
9551 return (BAM_ERROR);
9554 done = 0;
9555 for (lp = mp->start; lp; lp = lp->next) {
9556 if (lp->flags != BAM_GLOBAL)
9557 continue;
9559 if (lp->cmd == NULL) {
9560 if (!quiet)
9561 bam_error(NO_CMD, lp->lineNum);
9562 continue;
9565 if (strcmp(globalcmd, lp->cmd) != 0)
9566 continue;
9568 /* Found global. Check for duplicates */
9569 if (done && !quiet) {
9570 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
9571 ret = BAM_ERROR;
9574 arg = lp->arg ? lp->arg : "";
9575 bam_print(GLOBAL_CMD, globalcmd, arg);
9576 done = 1;
9579 if (!done && bam_verbose)
9580 bam_print(NO_ENTRY, globalcmd);
9582 return (ret);
9585 static error_t
9586 menu_write(char *root, menu_t *mp)
9588 const char *fcn = "menu_write()";
9590 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
9591 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9594 void
9595 line_free(line_t *lp)
9597 if (lp == NULL)
9598 return;
9600 if (lp->cmd != NULL)
9601 free(lp->cmd);
9602 if (lp->sep)
9603 free(lp->sep);
9604 if (lp->arg)
9605 free(lp->arg);
9606 if (lp->line)
9607 free(lp->line);
9608 free(lp);
9611 static void
9612 linelist_free(line_t *start)
9614 line_t *lp;
9616 while (start) {
9617 lp = start;
9618 start = start->next;
9619 line_free(lp);
9623 static void
9624 filelist_free(filelist_t *flistp)
9626 linelist_free(flistp->head);
9627 flistp->head = NULL;
9628 flistp->tail = NULL;
9631 static void
9632 menu_free(menu_t *mp)
9634 entry_t *ent, *tmp;
9635 assert(mp);
9637 if (mp->start)
9638 linelist_free(mp->start);
9639 ent = mp->entries;
9640 while (ent) {
9641 tmp = ent;
9642 ent = tmp->next;
9643 free(tmp);
9646 free(mp);
9650 * Utility routines
9655 * Returns 0 on success
9656 * Any other value indicates an error
9658 static int
9659 exec_cmd(char *cmdline, filelist_t *flistp)
9661 char buf[BUFSIZ];
9662 int ret;
9663 FILE *ptr;
9664 sigset_t set;
9665 void (*disp)(int);
9668 * For security
9669 * - only absolute paths are allowed
9670 * - set IFS to space and tab
9672 if (*cmdline != '/') {
9673 bam_error(ABS_PATH_REQ, cmdline);
9674 return (-1);
9676 (void) putenv("IFS= \t");
9679 * We may have been exec'ed with SIGCHLD blocked
9680 * unblock it here
9682 (void) sigemptyset(&set);
9683 (void) sigaddset(&set, SIGCHLD);
9684 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9685 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
9686 return (-1);
9690 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9692 disp = sigset(SIGCHLD, SIG_DFL);
9693 if (disp == SIG_ERR) {
9694 bam_error(FAILED_SIG, strerror(errno));
9695 return (-1);
9697 if (disp == SIG_HOLD) {
9698 bam_error(BLOCKED_SIG, cmdline);
9699 return (-1);
9702 ptr = popen(cmdline, "r");
9703 if (ptr == NULL) {
9704 bam_error(POPEN_FAIL, cmdline, strerror(errno));
9705 return (-1);
9709 * If we simply do a pclose() following a popen(), pclose()
9710 * will close the reader end of the pipe immediately even
9711 * if the child process has not started/exited. pclose()
9712 * does wait for cmd to terminate before returning though.
9713 * When the executed command writes its output to the pipe
9714 * there is no reader process and the command dies with
9715 * SIGPIPE. To avoid this we read repeatedly until read
9716 * terminates with EOF. This indicates that the command
9717 * (writer) has closed the pipe and we can safely do a
9718 * pclose().
9720 * Since pclose() does wait for the command to exit,
9721 * we can safely reap the exit status of the command
9722 * from the value returned by pclose()
9724 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9725 if (flistp == NULL) {
9726 /* s_fgets strips newlines, so insert them at the end */
9727 bam_print(PRINT, buf);
9728 } else {
9729 append_to_flist(flistp, buf);
9733 ret = pclose(ptr);
9734 if (ret == -1) {
9735 bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
9736 return (-1);
9739 if (WIFEXITED(ret)) {
9740 return (WEXITSTATUS(ret));
9741 } else {
9742 bam_error(EXEC_FAIL, cmdline, ret);
9743 return (-1);
9748 * Since this function returns -1 on error
9749 * it cannot be used to convert -1. However,
9750 * that is sufficient for what we need.
9752 static long
9753 s_strtol(char *str)
9755 long l;
9756 char *res = NULL;
9758 if (str == NULL) {
9759 return (-1);
9762 errno = 0;
9763 l = strtol(str, &res, 10);
9764 if (errno || *res != '\0') {
9765 return (-1);
9768 return (l);
9772 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9774 static int
9775 s_fputs(char *str, FILE *fp)
9777 char linebuf[BAM_MAXLINE];
9779 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9780 return (fputs(linebuf, fp));
9784 * Wrapper around fgets, that strips newlines returned by fgets
9786 char *
9787 s_fgets(char *buf, int buflen, FILE *fp)
9789 int n;
9791 buf = fgets(buf, buflen, fp);
9792 if (buf) {
9793 n = strlen(buf);
9794 if (n == buflen - 1 && buf[n-1] != '\n')
9795 bam_error(TOO_LONG, buflen - 1, buf);
9796 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9799 return (buf);
9802 void *
9803 s_calloc(size_t nelem, size_t sz)
9805 void *ptr;
9807 ptr = calloc(nelem, sz);
9808 if (ptr == NULL) {
9809 bam_error(NO_MEM, nelem*sz);
9810 bam_exit(1);
9812 return (ptr);
9815 void *
9816 s_realloc(void *ptr, size_t sz)
9818 ptr = realloc(ptr, sz);
9819 if (ptr == NULL) {
9820 bam_error(NO_MEM, sz);
9821 bam_exit(1);
9823 return (ptr);
9826 char *
9827 s_strdup(char *str)
9829 char *ptr;
9831 if (str == NULL)
9832 return (NULL);
9834 ptr = strdup(str);
9835 if (ptr == NULL) {
9836 bam_error(NO_MEM, strlen(str) + 1);
9837 bam_exit(1);
9839 return (ptr);
9843 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9844 * Returns 0 otherwise
9846 static int
9847 is_amd64(void)
9849 static int amd64 = -1;
9850 char isabuf[257]; /* from sysinfo(2) manpage */
9852 if (amd64 != -1)
9853 return (amd64);
9855 if (bam_alt_platform) {
9856 if (strcmp(bam_platform, "i86pc") == 0) {
9857 amd64 = 1; /* diskless server */
9859 } else {
9860 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
9861 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
9862 amd64 = 1;
9863 } else if (strstr(isabuf, "i386") == NULL) {
9864 amd64 = 1; /* diskless server */
9867 if (amd64 == -1)
9868 amd64 = 0;
9870 return (amd64);
9873 static char *
9874 get_machine(void)
9876 static int cached = -1;
9877 static char mbuf[257]; /* from sysinfo(2) manpage */
9879 if (cached == 0)
9880 return (mbuf);
9882 if (bam_alt_platform) {
9883 return (bam_platform);
9884 } else {
9885 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
9886 cached = 1;
9889 if (cached == -1) {
9890 mbuf[0] = '\0';
9891 cached = 0;
9894 return (mbuf);
9898 is_sparc(void)
9900 static int issparc = -1;
9901 char mbuf[257]; /* from sysinfo(2) manpage */
9903 if (issparc != -1)
9904 return (issparc);
9906 if (bam_alt_platform) {
9907 if (strncmp(bam_platform, "sun4", 4) == 0) {
9908 issparc = 1;
9910 } else {
9911 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
9912 strcmp(mbuf, "sparc") == 0) {
9913 issparc = 1;
9916 if (issparc == -1)
9917 issparc = 0;
9919 return (issparc);
9922 static void
9923 append_to_flist(filelist_t *flistp, char *s)
9925 line_t *lp;
9927 lp = s_calloc(1, sizeof (line_t));
9928 lp->line = s_strdup(s);
9929 if (flistp->head == NULL)
9930 flistp->head = lp;
9931 else
9932 flistp->tail->next = lp;
9933 flistp->tail = lp;
9936 #if !defined(_OBP)
9938 UCODE_VENDORS;
9940 /*ARGSUSED*/
9941 static void
9942 ucode_install(char *root)
9944 int i;
9946 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
9947 int cmd_len = PATH_MAX + 256;
9948 char cmd[PATH_MAX + 256];
9949 char file[PATH_MAX];
9950 char timestamp[PATH_MAX];
9951 struct stat fstatus, tstatus;
9952 struct utimbuf u_times;
9954 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
9955 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
9956 ucode_vendors[i].extstr);
9958 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
9959 continue;
9961 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
9963 if (stat(timestamp, &tstatus) == 0 &&
9964 fstatus.st_mtime <= tstatus.st_mtime)
9965 continue;
9967 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
9968 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
9969 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
9970 if (system(cmd) != 0)
9971 return;
9973 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
9974 return;
9976 u_times.actime = fstatus.st_atime;
9977 u_times.modtime = fstatus.st_mtime;
9978 (void) utime(timestamp, &u_times);
9981 #endif