1 /*
   2  * CDDL HEADER START
   3  *
   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.
   7  *
   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.
  12  *
  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]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2012 Milan Jurik. All rights reserved.
  25  * Copyright (c) 2015 by Delphix. All rights reserved.
  26  * Copyright 2016 Toomas Soome <tsoome@me.com>
  27  * Copyright 2016 Nexenta Systems, Inc.
  28  */
  29 
  30 /*
  31  * bootadm(1M) is a new utility for managing bootability of
  32  * Solaris *Newboot* environments. It has two primary tasks:
  33  *      - Allow end users to manage bootability of Newboot Solaris instances
  34  *      - Provide services to other subsystems in Solaris (primarily Install)
  35  */
  36 
  37 /* Headers */
  38 #include <stdio.h>
  39 #include <errno.h>
  40 #include <stdlib.h>
  41 #include <string.h>
  42 #include <unistd.h>
  43 #include <sys/types.h>
  44 #include <sys/stat.h>
  45 #include <alloca.h>
  46 #include <stdarg.h>
  47 #include <limits.h>
  48 #include <signal.h>
  49 #include <sys/wait.h>
  50 #include <sys/mnttab.h>
  51 #include <sys/mntent.h>
  52 #include <sys/statvfs.h>
  53 #include <libnvpair.h>
  54 #include <ftw.h>
  55 #include <fcntl.h>
  56 #include <strings.h>
  57 #include <utime.h>
  58 #include <sys/systeminfo.h>
  59 #include <sys/dktp/fdisk.h>
  60 #include <sys/param.h>
  61 #include <dirent.h>
  62 #include <ctype.h>
  63 #include <libgen.h>
  64 #include <sys/sysmacros.h>
  65 #include <sys/elf.h>
  66 #include <libscf.h>
  67 #include <zlib.h>
  68 #include <sys/lockfs.h>
  69 #include <sys/filio.h>
  70 #include <libbe.h>
  71 #include <deflt.h>
  72 #ifdef i386
  73 #include <libfdisk.h>
  74 #endif
  75 
  76 #if !defined(_OBP)
  77 #include <sys/ucode.h>
  78 #endif
  79 
  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>
  88 
  89 #include "bootadm.h"
  90 
  91 #ifndef TEXT_DOMAIN
  92 #define TEXT_DOMAIN     "SUNW_OST_OSCMD"
  93 #endif  /* TEXT_DOMAIN */
  94 
  95 /* Type definitions */
  96 
  97 /* Primary subcmds */
  98 typedef enum {
  99         BAM_MENU = 3,
 100         BAM_ARCHIVE,
 101         BAM_INSTALL
 102 } subcmd_t;
 103 
 104 #define LINE_INIT       0       /* lineNum initial value */
 105 #define ENTRY_INIT      -1      /* entryNum initial value */
 106 #define ALL_ENTRIES     -2      /* selects all boot entries */
 107 
 108 #define PARTNO_NOTFOUND -1      /* Solaris partition not found */
 109 #define PARTNO_EFI      -2      /* EFI partition table found */
 110 
 111 #define GRUB_DIR                "/boot/grub"
 112 #define GRUB_STAGE2             GRUB_DIR "/stage2"
 113 #define GRUB_MENU               "/boot/grub/menu.lst"
 114 #define MENU_TMP                "/boot/grub/menu.lst.tmp"
 115 #define GRUB_BACKUP_MENU        "/etc/lu/GRUB_backup_menu"
 116 #define RAMDISK_SPECIAL         "/devices/ramdisk"
 117 #define STUBBOOT                "/stubboot"
 118 #define MULTIBOOT               "/platform/i86pc/multiboot"
 119 #define GRUBSIGN_DIR            "/boot/grub/bootsign"
 120 #define GRUBSIGN_BACKUP         "/etc/bootsign"
 121 #define GRUBSIGN_UFS_PREFIX     "rootfs"
 122 #define GRUBSIGN_ZFS_PREFIX     "pool_"
 123 #define GRUBSIGN_LU_PREFIX      "BE_"
 124 #define UFS_SIGNATURE_LIST      "/var/run/grub_ufs_signatures"
 125 #define ZFS_LEGACY_MNTPT        "/tmp/bootadm_mnt_zfs_legacy"
 126 
 127 /* BE defaults */
 128 #define BE_DEFAULTS             "/etc/default/be"
 129 #define BE_DFLT_BE_HAS_GRUB     "BE_HAS_GRUB="
 130 
 131 #define BOOTADM_RDONLY_TEST     "BOOTADM_RDONLY_TEST"
 132 
 133 /* lock related */
 134 #define BAM_LOCK_FILE           "/var/run/bootadm.lock"
 135 #define LOCK_FILE_PERMS         (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
 136 
 137 #define CREATE_RAMDISK          "boot/solaris/bin/create_ramdisk"
 138 #define CREATE_DISKMAP          "boot/solaris/bin/create_diskmap"
 139 #define EXTRACT_BOOT_FILELIST   "boot/solaris/bin/extract_boot_filelist"
 140 #define GRUBDISK_MAP            "/var/run/solaris_grubdisk.map"
 141 
 142 #define GRUB_slice              "/etc/lu/GRUB_slice"
 143 #define GRUB_root               "/etc/lu/GRUB_root"
 144 #define GRUB_fdisk              "/etc/lu/GRUB_fdisk"
 145 #define GRUB_fdisk_target       "/etc/lu/GRUB_fdisk_target"
 146 #define FINDROOT_INSTALLGRUB    "/etc/lu/installgrub.findroot"
 147 #define LULIB                   "/usr/lib/lu/lulib"
 148 #define LULIB_PROPAGATE_FILE    "lulib_propagate_file"
 149 #define CKSUM                   "/usr/bin/cksum"
 150 #define LU_MENU_CKSUM           "/etc/lu/menu.cksum"
 151 #define BOOTADM                 "/sbin/bootadm"
 152 
 153 #define INSTALLGRUB             "/sbin/installgrub"
 154 #define STAGE1                  "/boot/grub/stage1"
 155 #define STAGE2                  "/boot/grub/stage2"
 156 
 157 /*
 158  * Default file attributes
 159  */
 160 #define DEFAULT_DEV_MODE        0644    /* default permissions */
 161 #define DEFAULT_DEV_UID         0       /* user root */
 162 #define DEFAULT_DEV_GID         3       /* group sys */
 163 
 164 /*
 165  * Menu related
 166  * menu_cmd_t and menu_cmds must be kept in sync
 167  */
 168 char *menu_cmds[] = {
 169         "default",      /* DEFAULT_CMD */
 170         "timeout",      /* TIMEOUT_CMD */
 171         "title",        /* TITLE_CMD */
 172         "root",         /* ROOT_CMD */
 173         "kernel",       /* KERNEL_CMD */
 174         "kernel$",      /* KERNEL_DOLLAR_CMD */
 175         "module",       /* MODULE_CMD */
 176         "module$",      /* MODULE_DOLLAR_CMD */
 177         " ",            /* SEP_CMD */
 178         "#",            /* COMMENT_CMD */
 179         "chainloader",  /* CHAINLOADER_CMD */
 180         "args",         /* ARGS_CMD */
 181         "findroot",     /* FINDROOT_CMD */
 182         "bootfs",       /* BOOTFS_CMD */
 183         NULL
 184 };
 185 
 186 #define OPT_ENTRY_NUM   "entry"
 187 
 188 /*
 189  * exec_cmd related
 190  */
 191 typedef struct {
 192         line_t *head;
 193         line_t *tail;
 194 } filelist_t;
 195 
 196 #define BOOT_FILE_LIST  "boot/solaris/filelist.ramdisk"
 197 #define ETC_FILE_LIST   "etc/boot/solaris/filelist.ramdisk"
 198 
 199 #define FILE_STAT       "boot/solaris/filestat.ramdisk"
 200 #define FILE_STAT_TMP   "boot/solaris/filestat.ramdisk.tmp"
 201 #define DIR_PERMS       (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
 202 #define FILE_STAT_MODE  (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
 203 
 204 #define FILE_STAT_TIMESTAMP     "boot/solaris/timestamp.cache"
 205 
 206 /* Globals */
 207 int bam_verbose;
 208 int bam_force;
 209 int bam_debug;
 210 static char *prog;
 211 static subcmd_t bam_cmd;
 212 char *bam_root;
 213 int bam_rootlen;
 214 static int bam_root_readonly;
 215 int bam_alt_root;
 216 static int bam_extend = 0;
 217 static int bam_purge = 0;
 218 static char *bam_subcmd;
 219 static char *bam_opt;
 220 static char **bam_argv;
 221 static char *bam_pool;
 222 static int bam_argc;
 223 static int bam_check;
 224 static int bam_saved_check;
 225 static int bam_smf_check;
 226 static int bam_lock_fd = -1;
 227 static int bam_zfs;
 228 static int bam_mbr;
 229 char rootbuf[PATH_MAX] = "/";
 230 static int bam_update_all;
 231 static int bam_alt_platform;
 232 static char *bam_platform;
 233 static char *bam_home_env = NULL;
 234 
 235 /* function prototypes */
 236 static void parse_args_internal(int, char *[]);
 237 static void parse_args(int, char *argv[]);
 238 static error_t bam_menu(char *, char *, int, char *[]);
 239 static error_t bam_install(char *, char *);
 240 static error_t bam_archive(char *, char *);
 241 
 242 static void bam_lock(void);
 243 static void bam_unlock(void);
 244 
 245 static int exec_cmd(char *, filelist_t *);
 246 static error_t read_globals(menu_t *, char *, char *, int);
 247 static int menu_on_bootdisk(char *os_root, char *menu_root);
 248 static menu_t *menu_read(char *);
 249 static error_t menu_write(char *, menu_t *);
 250 static void linelist_free(line_t *);
 251 static void menu_free(menu_t *);
 252 static void filelist_free(filelist_t *);
 253 static error_t list2file(char *, char *, char *, line_t *);
 254 static error_t list_entry(menu_t *, char *, char *);
 255 static error_t list_setting(menu_t *, char *, char *);
 256 static error_t delete_all_entries(menu_t *, char *, char *);
 257 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
 258 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
 259 
 260 static error_t install_bootloader(void);
 261 static error_t update_archive(char *, char *);
 262 static error_t list_archive(char *, char *);
 263 static error_t update_all(char *, char *);
 264 static error_t read_list(char *, filelist_t *);
 265 static error_t set_option(menu_t *, char *, char *);
 266 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
 267 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
 268 static char *expand_path(const char *);
 269 
 270 static long s_strtol(char *);
 271 static int s_fputs(char *, FILE *);
 272 
 273 static int is_amd64(void);
 274 static char *get_machine(void);
 275 static void append_to_flist(filelist_t *, char *);
 276 static int ufs_add_to_sign_list(char *sign);
 277 static error_t synchronize_BE_menu(void);
 278 
 279 #if !defined(_OBP)
 280 static void ucode_install();
 281 #endif
 282 
 283 /* Menu related sub commands */
 284 static subcmd_defn_t menu_subcmds[] = {
 285         "set_option",           OPT_ABSENT,     set_option, 0,  /* PUB */
 286         "list_entry",           OPT_OPTIONAL,   list_entry, 1,  /* PUB */
 287         "delete_all_entries",   OPT_ABSENT,     delete_all_entries, 0, /* PVT */
 288         "update_entry",         OPT_REQ,        update_entry, 0, /* menu */
 289         "update_temp",          OPT_OPTIONAL,   update_temp, 0, /* reboot */
 290         "upgrade",              OPT_ABSENT,     upgrade_menu, 0, /* menu */
 291         "list_setting",         OPT_OPTIONAL,   list_setting, 1, /* menu */
 292         "disable_hypervisor",   OPT_ABSENT,     cvt_to_metal, 0, /* menu */
 293         "enable_hypervisor",    OPT_ABSENT,     cvt_to_hyper, 0, /* menu */
 294         NULL,                   0,              NULL, 0 /* must be last */
 295 };
 296 
 297 /* Archive related sub commands */
 298 static subcmd_defn_t arch_subcmds[] = {
 299         "update",               OPT_ABSENT,     update_archive, 0, /* PUB */
 300         "update_all",           OPT_ABSENT,     update_all, 0,  /* PVT */
 301         "list",                 OPT_OPTIONAL,   list_archive, 1, /* PUB */
 302         NULL,                   0,              NULL, 0 /* must be last */
 303 };
 304 
 305 /* Install related sub commands */
 306 static subcmd_defn_t inst_subcmds[] = {
 307         "install_bootloader",   OPT_ABSENT,     install_bootloader, 0, /* PUB */
 308         NULL,                   0,              NULL, 0 /* must be last */
 309 };
 310 
 311 enum dircache_copy_opt {
 312         FILE32 = 0,
 313         FILE64,
 314         CACHEDIR_NUM
 315 };
 316 
 317 /*
 318  * Directory specific flags:
 319  * NEED_UPDATE : the specified archive needs to be updated
 320  * NO_MULTI : don't extend the specified archive, but recreate it
 321  */
 322 #define NEED_UPDATE             0x00000001
 323 #define NO_MULTI                0x00000002
 324 
 325 #define set_dir_flag(id, f)     (walk_arg.dirinfo[id].flags |= f)
 326 #define unset_dir_flag(id, f)   (walk_arg.dirinfo[id].flags &= ~f)
 327 #define is_dir_flag_on(id, f)   (walk_arg.dirinfo[id].flags & f ? 1 : 0)
 328 
 329 #define get_cachedir(id)        (walk_arg.dirinfo[id].cdir_path)
 330 #define get_updatedir(id)       (walk_arg.dirinfo[id].update_path)
 331 #define get_count(id)           (walk_arg.dirinfo[id].count)
 332 #define has_cachedir(id)        (walk_arg.dirinfo[id].has_dir)
 333 #define set_dir_present(id)     (walk_arg.dirinfo[id].has_dir = 1)
 334 
 335 /*
 336  * dirinfo_t (specific cache directory information):
 337  * cdir_path:   path to the archive cache directory
 338  * update_path: path to the update directory (contains the files that will be
 339  *              used to extend the archive)
 340  * has_dir:     the specified cache directory is active
 341  * count:       the number of files to update
 342  * flags:       directory specific flags
 343  */
 344 typedef struct _dirinfo {
 345         char    cdir_path[PATH_MAX];
 346         char    update_path[PATH_MAX];
 347         int     has_dir;
 348         int     count;
 349         int     flags;
 350 } dirinfo_t;
 351 
 352 /*
 353  * Update flags:
 354  * NEED_CACHE_DIR : cache directory is missing and needs to be created
 355  * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
 356  * UPDATE_ERROR : an error occourred while traversing the list of files
 357  * RDONLY_FSCHK : the target filesystem is read-only
 358  * RAMDSK_FSCHK : the target filesystem is on a ramdisk
 359  */
 360 #define NEED_CACHE_DIR          0x00000001
 361 #define IS_SPARC_TARGET         0x00000002
 362 #define UPDATE_ERROR            0x00000004
 363 #define RDONLY_FSCHK            0x00000008
 364 #define INVALIDATE_CACHE        0x00000010
 365 
 366 #define is_flag_on(flag)        (walk_arg.update_flags & flag ? 1 : 0)
 367 #define set_flag(flag)          (walk_arg.update_flags |= flag)
 368 #define unset_flag(flag)        (walk_arg.update_flags &= ~flag)
 369 
 370 /*
 371  * struct walk_arg :
 372  * update_flags: flags related to the current updating process
 373  * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
 374  * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
 375  */
 376 static struct {
 377         int             update_flags;
 378         nvlist_t        *new_nvlp;
 379         nvlist_t        *old_nvlp;
 380         FILE            *sparcfile;
 381         dirinfo_t       dirinfo[CACHEDIR_NUM];
 382 } walk_arg;
 383 
 384 struct safefile {
 385         char *name;
 386         struct safefile *next;
 387 };
 388 
 389 static struct safefile *safefiles = NULL;
 390 
 391 /*
 392  * svc:/system/filesystem/usr:default service checks for this file and
 393  * does a boot archive update and then reboot the system.
 394  */
 395 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
 396 
 397 /*
 398  * svc:/system/boot-archive-update:default checks for this file and
 399  * updates the boot archive.
 400  */
 401 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
 402 
 403 /* Thanks growisofs */
 404 #define CD_BLOCK        ((off64_t)2048)
 405 #define VOLDESC_OFF     16
 406 #define DVD_BLOCK       (32*1024)
 407 #define MAX_IVDs        16
 408 
 409 struct iso_pdesc {
 410     unsigned char type  [1];
 411     unsigned char id    [5];
 412     unsigned char void1 [80-5-1];
 413     unsigned char volume_space_size [8];
 414     unsigned char void2 [2048-80-8];
 415 };
 416 
 417 /*
 418  * COUNT_MAX:   maximum number of changed files to justify a multisession update
 419  * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
 420  *              update
 421  */
 422 #define COUNT_MAX               50
 423 #define BA_SIZE_MAX             (50 * 1024 * 1024)
 424 
 425 #define bam_nowrite()           (bam_check || bam_smf_check)
 426 
 427 static int sync_menu = 1;       /* whether we need to sync the BE menus */
 428 
 429 static void
 430 usage(void)
 431 {
 432         (void) fprintf(stderr, "USAGE:\n");
 433 
 434         /* archive usage */
 435         (void) fprintf(stderr,
 436             "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
 437         (void) fprintf(stderr,
 438             "\t%s list-archive [-R altroot [-p platform]]\n", prog);
 439 #if defined(_OBP)
 440         (void) fprintf(stderr,
 441             "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
 442 #else
 443         (void) fprintf(stderr,
 444             "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
 445 #endif
 446 #if !defined(_OBP)
 447         /* x86 only */
 448         (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
 449         (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
 450 #endif
 451 }
 452 
 453 /*
 454  * Best effort attempt to restore the $HOME value.
 455  */
 456 static void
 457 restore_env()
 458 {
 459         char    home_env[PATH_MAX];
 460 
 461         if (bam_home_env) {
 462                 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
 463                     bam_home_env);
 464                 (void) putenv(home_env);
 465         }
 466 }
 467 
 468 
 469 #define         SLEEP_TIME      5
 470 #define         MAX_TRIES       4
 471 
 472 /*
 473  * Sanitize the environment in which bootadm will execute its sub-processes
 474  * (ex. mkisofs). This is done to prevent those processes from attempting
 475  * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
 476  * or, potentially, insecure.
 477  */
 478 static void
 479 sanitize_env()
 480 {
 481         int     stry = 0;
 482 
 483         /* don't depend on caller umask */
 484         (void) umask(0022);
 485 
 486         /* move away from a potential unsafe current working directory */
 487         while (chdir("/") == -1) {
 488                 if (errno != EINTR) {
 489                         bam_print("WARNING: unable to chdir to /");
 490                         break;
 491                 }
 492         }
 493 
 494         bam_home_env = getenv("HOME");
 495         while (bam_home_env != NULL && putenv("HOME=/") == -1) {
 496                 if (errno == ENOMEM) {
 497                         /* retry no more than MAX_TRIES times */
 498                         if (++stry > MAX_TRIES) {
 499                                 bam_print("WARNING: unable to recover from "
 500                                     "system memory pressure... aborting \n");
 501                                 bam_exit(EXIT_FAILURE);
 502                         }
 503                         /* memory is tight, try to sleep */
 504                         bam_print("Attempting to recover from memory pressure: "
 505                             "sleeping for %d seconds\n", SLEEP_TIME * stry);
 506                         (void) sleep(SLEEP_TIME * stry);
 507                 } else {
 508                         bam_print("WARNING: unable to sanitize HOME\n");
 509                 }
 510         }
 511 }
 512 
 513 int
 514 main(int argc, char *argv[])
 515 {
 516         error_t ret = BAM_SUCCESS;
 517 
 518         (void) setlocale(LC_ALL, "");
 519         (void) textdomain(TEXT_DOMAIN);
 520 
 521         if ((prog = strrchr(argv[0], '/')) == NULL) {
 522                 prog = argv[0];
 523         } else {
 524                 prog++;
 525         }
 526 
 527         INJECT_ERROR1("ASSERT_ON", assert(0))
 528 
 529         sanitize_env();
 530 
 531         parse_args(argc, argv);
 532 
 533         switch (bam_cmd) {
 534                 case BAM_MENU:
 535                         if (is_grub(bam_alt_root ? bam_root : "/")) {
 536                                 ret = bam_menu(bam_subcmd, bam_opt,
 537                                     bam_argc, bam_argv);
 538                         } else {
 539                                 ret = bam_loader_menu(bam_subcmd, bam_opt,
 540                                     bam_argc, bam_argv);
 541                         }
 542                         break;
 543                 case BAM_ARCHIVE:
 544                         ret = bam_archive(bam_subcmd, bam_opt);
 545                         break;
 546                 case BAM_INSTALL:
 547                         ret = bam_install(bam_subcmd, bam_opt);
 548                         break;
 549                 default:
 550                         usage();
 551                         bam_exit(1);
 552         }
 553 
 554         if (ret != BAM_SUCCESS)
 555                 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
 556 
 557         bam_unlock();
 558         return (0);
 559 }
 560 
 561 /*
 562  * Equivalence of public and internal commands:
 563  *      update-archive  -- -a update
 564  *      list-archive    -- -a list
 565  *      set-menu        -- -m set_option
 566  *      list-menu       -- -m list_entry
 567  *      update-menu     -- -m update_entry
 568  *      install-bootloader      -- -i install_bootloader
 569  */
 570 static struct cmd_map {
 571         char *bam_cmdname;
 572         int bam_cmd;
 573         char *bam_subcmd;
 574 } cmd_map[] = {
 575         { "update-archive",     BAM_ARCHIVE,    "update"},
 576         { "list-archive",       BAM_ARCHIVE,    "list"},
 577         { "set-menu",           BAM_MENU,       "set_option"},
 578         { "list-menu",          BAM_MENU,       "list_entry"},
 579         { "update-menu",        BAM_MENU,       "update_entry"},
 580         { "install-bootloader", BAM_INSTALL,    "install_bootloader"},
 581         { NULL,                 0,              NULL}
 582 };
 583 
 584 /*
 585  * Commands syntax published in bootadm(1M) are parsed here
 586  */
 587 static void
 588 parse_args(int argc, char *argv[])
 589 {
 590         struct cmd_map *cmp = cmd_map;
 591 
 592         /* command conforming to the final spec */
 593         if (argc > 1 && argv[1][0] != '-') {
 594                 /*
 595                  * Map commands to internal table.
 596                  */
 597                 while (cmp->bam_cmdname) {
 598                         if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
 599                                 bam_cmd = cmp->bam_cmd;
 600                                 bam_subcmd = cmp->bam_subcmd;
 601                                 break;
 602                         }
 603                         cmp++;
 604                 }
 605                 if (cmp->bam_cmdname == NULL) {
 606                         usage();
 607                         bam_exit(1);
 608                 }
 609                 argc--;
 610                 argv++;
 611         }
 612 
 613         parse_args_internal(argc, argv);
 614 }
 615 
 616 /*
 617  * A combination of public and private commands are parsed here.
 618  * The internal syntax and the corresponding functionality are:
 619  *      -a update                       -- update-archive
 620  *      -a list                         -- list-archive
 621  *      -a update-all                   -- (reboot to sync all mnted OS archive)
 622  *      -i install_bootloader           -- install-bootloader
 623  *      -m update_entry                 -- update-menu
 624  *      -m list_entry                   -- list-menu
 625  *      -m update_temp                  -- (reboot -- [boot-args])
 626  *      -m delete_all_entries           -- (called from install)
 627  *      -m enable_hypervisor [args]     -- cvt_to_hyper
 628  *      -m disable_hypervisor           -- cvt_to_metal
 629  *      -m list_setting [entry] [value] -- list_setting
 630  *
 631  * A set of private flags is there too:
 632  *      -F              -- purge the cache directories and rebuild them
 633  *      -e              -- use the (faster) archive update approach (used by
 634  *                         reboot)
 635  */
 636 static void
 637 parse_args_internal(int argc, char *argv[])
 638 {
 639         int c, error;
 640         extern char *optarg;
 641         extern int optind, opterr;
 642 #if defined(_OBP)
 643         const char *optstring = "a:d:fi:m:no:veFCR:p:P:XZ";
 644 #else
 645         const char *optstring = "a:d:fi:m:no:veFCMR:p:P:XZ";
 646 #endif
 647 
 648         /* Suppress error message from getopt */
 649         opterr = 0;
 650 
 651         error = 0;
 652         while ((c = getopt(argc, argv, optstring)) != -1) {
 653                 switch (c) {
 654                 case 'a':
 655                         if (bam_cmd) {
 656                                 error = 1;
 657                                 bam_error(
 658                                     _("multiple commands specified: -%c\n"), c);
 659                         }
 660                         bam_cmd = BAM_ARCHIVE;
 661                         bam_subcmd = optarg;
 662                         break;
 663                 case 'd':
 664                         if (bam_debug) {
 665                                 error = 1;
 666                                 bam_error(
 667                                     _("duplicate options specified: -%c\n"), c);
 668                         }
 669                         bam_debug = s_strtol(optarg);
 670                         break;
 671                 case 'f':
 672                         bam_force = 1;
 673                         break;
 674                 case 'F':
 675                         bam_purge = 1;
 676                         break;
 677                 case 'i':
 678                         if (bam_cmd) {
 679                                 error = 1;
 680                                 bam_error(
 681                                     _("multiple commands specified: -%c\n"), c);
 682                         }
 683                         bam_cmd = BAM_INSTALL;
 684                         bam_subcmd = optarg;
 685                         break;
 686                 case 'm':
 687                         if (bam_cmd) {
 688                                 error = 1;
 689                                 bam_error(
 690                                     _("multiple commands specified: -%c\n"), c);
 691                         }
 692                         bam_cmd = BAM_MENU;
 693                         bam_subcmd = optarg;
 694                         break;
 695 #if !defined(_OBP)
 696                 case 'M':
 697                         bam_mbr = 1;
 698                         break;
 699 #endif
 700                 case 'n':
 701                         bam_check = 1;
 702                         /*
 703                          * We save the original value of bam_check. The new
 704                          * approach in case of a read-only filesystem is to
 705                          * behave as a check, so we need a way to restore the
 706                          * original value after the evaluation of the read-only
 707                          * filesystem has been done.
 708                          * Even if we don't allow at the moment a check with
 709                          * update_all, this approach is more robust than
 710                          * simply resetting bam_check to zero.
 711                          */
 712                         bam_saved_check = 1;
 713                         break;
 714                 case 'o':
 715                         if (bam_opt) {
 716                                 error = 1;
 717                                 bam_error(
 718                                     _("duplicate options specified: -%c\n"), c);
 719                         }
 720                         bam_opt = optarg;
 721                         break;
 722                 case 'v':
 723                         bam_verbose = 1;
 724                         break;
 725                 case 'C':
 726                         bam_smf_check = 1;
 727                         break;
 728                 case 'P':
 729                         if (bam_pool != NULL) {
 730                                 error = 1;
 731                                 bam_error(
 732                                     _("duplicate options specified: -%c\n"), c);
 733                         }
 734                         bam_pool = optarg;
 735                         break;
 736                 case 'R':
 737                         if (bam_root) {
 738                                 error = 1;
 739                                 bam_error(
 740                                     _("duplicate options specified: -%c\n"), c);
 741                                 break;
 742                         } else if (realpath(optarg, rootbuf) == NULL) {
 743                                 error = 1;
 744                                 bam_error(_("cannot resolve path %s: %s\n"),
 745                                     optarg, strerror(errno));
 746                                 break;
 747                         }
 748                         bam_alt_root = 1;
 749                         bam_root = rootbuf;
 750                         bam_rootlen = strlen(rootbuf);
 751                         break;
 752                 case 'p':
 753                         bam_alt_platform = 1;
 754                         bam_platform = optarg;
 755                         if ((strcmp(bam_platform, "i86pc") != 0) &&
 756                             (strcmp(bam_platform, "sun4u") != 0) &&
 757                             (strcmp(bam_platform, "sun4v") != 0)) {
 758                                 error = 1;
 759                                 bam_error(_("invalid platform %s - must be "
 760                                     "one of sun4u, sun4v or i86pc\n"),
 761                                     bam_platform);
 762                         }
 763                         break;
 764                 case 'X':
 765                         bam_is_hv = BAM_HV_PRESENT;
 766                         break;
 767                 case 'Z':
 768                         bam_zfs = 1;
 769                         break;
 770                 case 'e':
 771                         bam_extend = 1;
 772                         break;
 773                 case '?':
 774                         error = 1;
 775                         bam_error(_("invalid option or missing option "
 776                             "argument: -%c\n"), optopt);
 777                         break;
 778                 default :
 779                         error = 1;
 780                         bam_error(_("invalid option or missing option "
 781                             "argument: -%c\n"), c);
 782                         break;
 783                 }
 784         }
 785 
 786         /*
 787          * An alternate platform requires an alternate root
 788          */
 789         if (bam_alt_platform && bam_alt_root == 0) {
 790                 usage();
 791                 bam_exit(0);
 792         }
 793 
 794         /*
 795          * A command option must be specfied
 796          */
 797         if (!bam_cmd) {
 798                 if (bam_opt && strcmp(bam_opt, "all") == 0) {
 799                         usage();
 800                         bam_exit(0);
 801                 }
 802                 bam_error(_("a command option must be specified\n"));
 803                 error = 1;
 804         }
 805 
 806         if (error) {
 807                 usage();
 808                 bam_exit(1);
 809         }
 810 
 811         if (optind > argc) {
 812                 bam_error(_("Internal error: %s\n"), "parse_args");
 813                 bam_exit(1);
 814         } else if (optind < argc) {
 815                 bam_argv = &argv[optind];
 816                 bam_argc = argc - optind;
 817         }
 818 
 819         /*
 820          * mbr and pool are options for install_bootloader
 821          */
 822         if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
 823                 usage();
 824                 bam_exit(0);
 825         }
 826 
 827         /*
 828          * -n implies verbose mode
 829          */
 830         if (bam_check)
 831                 bam_verbose = 1;
 832 }
 833 
 834 error_t
 835 check_subcmd_and_options(
 836         char *subcmd,
 837         char *opt,
 838         subcmd_defn_t *table,
 839         error_t (**fp)())
 840 {
 841         int i;
 842 
 843         if (subcmd == NULL) {
 844                 bam_error(_("this command requires a sub-command\n"));
 845                 return (BAM_ERROR);
 846         }
 847 
 848         if (strcmp(subcmd, "set_option") == 0) {
 849                 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
 850                         bam_error(_("missing argument for sub-command\n"));
 851                         usage();
 852                         return (BAM_ERROR);
 853                 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
 854                         bam_error(_("invalid trailing arguments\n"));
 855                         usage();
 856                         return (BAM_ERROR);
 857                 }
 858         } else if (strcmp(subcmd, "update_all") == 0) {
 859                 /*
 860                  * The only option we accept for the "update_all"
 861                  * subcmd is "fastboot".
 862                  */
 863                 if (bam_argc > 1 || (bam_argc == 1 &&
 864                     strcmp(bam_argv[0], "fastboot") != 0)) {
 865                         bam_error(_("invalid trailing arguments\n"));
 866                         usage();
 867                         return (BAM_ERROR);
 868                 }
 869                 if (bam_argc == 1)
 870                         sync_menu = 0;
 871         } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
 872             (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
 873                 /*
 874                  * Of the remaining subcommands, only "enable_hypervisor" and
 875                  * "list_setting" take trailing arguments.
 876                  */
 877                 bam_error(_("invalid trailing arguments\n"));
 878                 usage();
 879                 return (BAM_ERROR);
 880         }
 881 
 882         if (bam_root == NULL) {
 883                 bam_root = rootbuf;
 884                 bam_rootlen = 1;
 885         }
 886 
 887         /* verify that subcmd is valid */
 888         for (i = 0; table[i].subcmd != NULL; i++) {
 889                 if (strcmp(table[i].subcmd, subcmd) == 0)
 890                         break;
 891         }
 892 
 893         if (table[i].subcmd == NULL) {
 894                 bam_error(_("invalid sub-command specified: %s\n"), subcmd);
 895                 return (BAM_ERROR);
 896         }
 897 
 898         if (table[i].unpriv == 0 && geteuid() != 0) {
 899                 bam_error(_("you must be root to run this command\n"));
 900                 return (BAM_ERROR);
 901         }
 902 
 903         /*
 904          * Currently only privileged commands need a lock
 905          */
 906         if (table[i].unpriv == 0)
 907                 bam_lock();
 908 
 909         /* subcmd verifies that opt is appropriate */
 910         if (table[i].option != OPT_OPTIONAL) {
 911                 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
 912                         if (opt)
 913                                 bam_error(_("this sub-command (%s) does not "
 914                                     "take options\n"), subcmd);
 915                         else
 916                                 bam_error(_("an option is required for this "
 917                                     "sub-command: %s\n"), subcmd);
 918                         return (BAM_ERROR);
 919                 }
 920         }
 921 
 922         *fp = table[i].handler;
 923 
 924         return (BAM_SUCCESS);
 925 }
 926 
 927 /*
 928  * NOTE: A single "/" is also considered a trailing slash and will
 929  * be deleted.
 930  */
 931 void
 932 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
 933 {
 934         size_t dstlen;
 935 
 936         assert(src);
 937         assert(dst);
 938 
 939         (void) strlcpy(dst, src, dstsize);
 940 
 941         dstlen = strlen(dst);
 942         if (dst[dstlen - 1] == '/') {
 943                 dst[dstlen - 1] = '\0';
 944         }
 945 }
 946 
 947 static int
 948 is_safe_exec(char *path)
 949 {
 950         struct stat     sb;
 951 
 952         if (lstat(path, &sb) != 0) {
 953                 bam_error(_("stat of file failed: %s: %s\n"), path,
 954                     strerror(errno));
 955                 return (BAM_ERROR);
 956         }
 957 
 958         if (!S_ISREG(sb.st_mode)) {
 959                 bam_error(_("%s is not a regular file, skipping\n"), path);
 960                 return (BAM_ERROR);
 961         }
 962 
 963         if (sb.st_uid != getuid()) {
 964                 bam_error(_("%s is not owned by %d, skipping\n"),
 965                     path, getuid());
 966                 return (BAM_ERROR);
 967         }
 968 
 969         if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
 970                 bam_error(_("%s is others or group writable, skipping\n"),
 971                     path);
 972                 return (BAM_ERROR);
 973         }
 974 
 975         return (BAM_SUCCESS);
 976 }
 977 
 978 static error_t
 979 list_setting(menu_t *mp, char *which, char *setting)
 980 {
 981         line_t  *lp;
 982         entry_t *ent;
 983 
 984         char    *p = which;
 985         int     entry;
 986 
 987         int     found;
 988 
 989         assert(which);
 990         assert(setting);
 991 
 992         if (*which != NULL) {
 993                 /*
 994                  * If "which" is not a number, assume it's a setting we want
 995                  * to look for and so set up the routine to look for "which"
 996                  * in the default entry.
 997                  */
 998                 while (*p != NULL)
 999                         if (!(isdigit((int)*p++))) {
1000                                 setting = which;
1001                                 which = mp->curdefault->arg;
1002                                 break;
1003                         }
1004         } else {
1005                 which = mp->curdefault->arg;
1006         }
1007 
1008         entry = atoi(which);
1009 
1010         for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1011             ent = ent->next)
1012                 ;
1013 
1014         if (!ent) {
1015                 bam_error(_("no matching entry found\n"));
1016                 return (BAM_ERROR);
1017         }
1018 
1019         found = (*setting == NULL);
1020 
1021         for (lp = ent->start; lp != NULL; lp = lp->next) {
1022                 if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
1023                         bam_print("%s\n", lp->line);
1024                 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1025                         bam_print("%s\n", lp->arg);
1026                         found = 1;
1027                 }
1028 
1029                 if (lp == ent->end)
1030                         break;
1031         }
1032 
1033         if (!found) {
1034                 bam_error(_("no matching entry found\n"));
1035                 return (BAM_ERROR);
1036         }
1037 
1038         return (BAM_SUCCESS);
1039 }
1040 
1041 static error_t
1042 install_bootloader(void)
1043 {
1044         nvlist_t        *nvl;
1045         uint16_t        flags = 0;
1046         int             found = 0;
1047         struct extmnttab mnt;
1048         struct stat     statbuf = {0};
1049         be_node_list_t  *be_nodes, *node;
1050         FILE            *fp;
1051         char            *root_ds = NULL;
1052         int             ret = BAM_ERROR;
1053 
1054         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1055                 bam_error(_("out of memory\n"));
1056                 return (ret);
1057         }
1058 
1059         /*
1060          * if bam_alt_root is set, the stage files are used from alt root.
1061          * if pool is set, the target devices are pool devices, stage files
1062          * are read from pool bootfs unless alt root is set.
1063          *
1064          * use arguments as targets, stage files are from alt or current root
1065          * if no arguments and no pool, install on current boot pool.
1066          */
1067 
1068         if (bam_alt_root) {
1069                 if (stat(bam_root, &statbuf) != 0) {
1070                         bam_error(_("stat of file failed: %s: %s\n"), bam_root,
1071                             strerror(errno));
1072                         goto done;
1073                 }
1074                 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1075                         bam_error(_("failed to open file: %s: %s\n"),
1076                             MNTTAB, strerror(errno));
1077                         goto done;
1078                 }
1079                 resetmnttab(fp);
1080                 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1081                         if (mnt.mnt_major == major(statbuf.st_dev) &&
1082                             mnt.mnt_minor == minor(statbuf.st_dev)) {
1083                                 found = 1;
1084                                 root_ds = strdup(mnt.mnt_special);
1085                                 break;
1086                         }
1087                 }
1088                 (void) fclose(fp);
1089 
1090                 if (found == 0) {
1091                         bam_error(_("alternate root %s not in mnttab\n"),
1092                             bam_root);
1093                         goto done;
1094                 }
1095                 if (root_ds == NULL) {
1096                         bam_error(_("out of memory\n"));
1097                         goto done;
1098                 }
1099 
1100                 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1101                         bam_error(_("No BE's found\n"));
1102                         goto done;
1103                 }
1104                 for (node = be_nodes; node != NULL; node = node->be_next_node)
1105                         if (strcmp(root_ds, node->be_root_ds) == 0)
1106                                 break;
1107 
1108                 if (node == NULL)
1109                         bam_error(_("BE (%s) does not exist\n"), root_ds);
1110 
1111                 free(root_ds);
1112                 root_ds = NULL;
1113                 if (node == NULL) {
1114                         be_free_list(be_nodes);
1115                         goto done;
1116                 }
1117                 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1118                     node->be_node_name);
1119                 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1120                     node->be_root_ds);
1121                 be_free_list(be_nodes);
1122                 if (ret != 0) {
1123                         ret = BAM_ERROR;
1124                         goto done;
1125                 }
1126         }
1127 
1128         if (bam_force)
1129                 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1130         if (bam_mbr)
1131                 flags |= BE_INSTALLBOOT_FLAG_MBR;
1132         if (bam_verbose)
1133                 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1134 
1135         if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1136                 bam_error(_("out of memory\n"));
1137                 ret = BAM_ERROR;
1138                 goto done;
1139         }
1140 
1141         /*
1142          * if altroot was set, we got be name and be root, only need
1143          * to set pool name as target.
1144          * if no altroot, need to find be name and root from pool.
1145          */
1146         if (bam_pool != NULL) {
1147                 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1148                 if (ret != 0) {
1149                         ret = BAM_ERROR;
1150                         goto done;
1151                 }
1152                 if (found) {
1153                         ret = be_installboot(nvl);
1154                         if (ret != 0)
1155                                 ret = BAM_ERROR;
1156                         goto done;
1157                 }
1158         }
1159 
1160         if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1161                 bam_error(_("No BE's found\n"));
1162                 ret = BAM_ERROR;
1163                 goto done;
1164         }
1165 
1166         if (bam_pool != NULL) {
1167                 /*
1168                  * find active be_node in bam_pool
1169                  */
1170                 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1171                         if (strcmp(bam_pool, node->be_rpool) != 0)
1172                                 continue;
1173                         if (node->be_active_on_boot)
1174                                 break;
1175                 }
1176                 if (node == NULL) {
1177                         bam_error(_("No active BE in %s\n"), bam_pool);
1178                         be_free_list(be_nodes);
1179                         ret = BAM_ERROR;
1180                         goto done;
1181                 }
1182                 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1183                     node->be_node_name);
1184                 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1185                     node->be_root_ds);
1186                 be_free_list(be_nodes);
1187                 if (ret != 0) {
1188                         ret = BAM_ERROR;
1189                         goto done;
1190                 }
1191                 ret = be_installboot(nvl);
1192                 if (ret != 0)
1193                         ret = BAM_ERROR;
1194                 goto done;
1195         }
1196 
1197         /*
1198          * get dataset for "/" and fill up the args.
1199          */
1200         if ((fp = fopen(MNTTAB, "r")) == NULL) {
1201                 bam_error(_("failed to open file: %s: %s\n"),
1202                     MNTTAB, strerror(errno));
1203                 ret = BAM_ERROR;
1204                 be_free_list(be_nodes);
1205                 goto done;
1206         }
1207         resetmnttab(fp);
1208         found = 0;
1209         while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1210                 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1211                         found = 1;
1212                         root_ds = strdup(mnt.mnt_special);
1213                         break;
1214                 }
1215         }
1216         (void) fclose(fp);
1217 
1218         if (found == 0) {
1219                 bam_error(_("alternate root %s not in mnttab\n"), "/");
1220                 ret = BAM_ERROR;
1221                 be_free_list(be_nodes);
1222                 goto done;
1223         }
1224         if (root_ds == NULL) {
1225                 bam_error(_("out of memory\n"));
1226                 ret = BAM_ERROR;
1227                 be_free_list(be_nodes);
1228                 goto done;
1229         }
1230 
1231         for (node = be_nodes; node != NULL; node = node->be_next_node) {
1232                 if (strcmp(root_ds, node->be_root_ds) == 0)
1233                         break;
1234         }
1235 
1236         if (node == NULL) {
1237                 bam_error(_("No such BE: %s\n"), root_ds);
1238                 free(root_ds);
1239                 be_free_list(be_nodes);
1240                 ret = BAM_ERROR;
1241                 goto done;
1242         }
1243         free(root_ds);
1244 
1245         ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1246         ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1247         ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1248         be_free_list(be_nodes);
1249 
1250         if (ret != 0)
1251                 ret = BAM_ERROR;
1252         else
1253                 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1254 done:
1255         nvlist_free(nvl);
1256 
1257         return (ret);
1258 }
1259 
1260 static error_t
1261 bam_install(char *subcmd, char *opt)
1262 {
1263         error_t (*f)(void);
1264 
1265         /*
1266          * Check arguments
1267          */
1268         if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1269             BAM_ERROR)
1270                 return (BAM_ERROR);
1271 
1272         return (f());
1273 }
1274 
1275 static error_t
1276 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1277 {
1278         error_t                 ret;
1279         char                    menu_path[PATH_MAX];
1280         char                    clean_menu_root[PATH_MAX];
1281         char                    path[PATH_MAX];
1282         menu_t                  *menu;
1283         char                    menu_root[PATH_MAX];
1284         struct stat             sb;
1285         error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1286         char                    *special = NULL;
1287         char                    *pool = NULL;
1288         zfs_mnted_t             zmnted;
1289         char                    *zmntpt = NULL;
1290         char                    *osdev;
1291         char                    *osroot;
1292         const char              *fcn = "bam_menu()";
1293 
1294         /*
1295          * Menu sub-command only applies to GRUB (i.e. x86)
1296          */
1297         if (!is_grub(bam_alt_root ? bam_root : "/")) {
1298                 bam_error(_("not a GRUB 0.97 based Illumos instance. "
1299                     "Operation not supported\n"));
1300                 return (BAM_ERROR);
1301         }
1302 
1303         /*
1304          * Check arguments
1305          */
1306         ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1307         if (ret == BAM_ERROR) {
1308                 return (BAM_ERROR);
1309         }
1310 
1311         assert(bam_root);
1312 
1313         (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1314         osdev = osroot = NULL;
1315 
1316         if (strcmp(subcmd, "update_entry") == 0) {
1317                 assert(opt);
1318 
1319                 osdev = strtok(opt, ",");
1320                 assert(osdev);
1321                 osroot = strtok(NULL, ",");
1322                 if (osroot) {
1323                         /* fixup bam_root so that it points at osroot */
1324                         if (realpath(osroot, rootbuf) == NULL) {
1325                                 bam_error(_("cannot resolve path %s: %s\n"),
1326                                     osroot, strerror(errno));
1327                                 return (BAM_ERROR);
1328                         }
1329                         bam_alt_root = 1;
1330                         bam_root  = rootbuf;
1331                         bam_rootlen = strlen(rootbuf);
1332                 }
1333         }
1334 
1335         /*
1336          * We support menu on PCFS (under certain conditions), but
1337          * not the OS root
1338          */
1339         if (is_pcfs(bam_root)) {
1340                 bam_error(_("root <%s> on PCFS is not supported\n"), bam_root);
1341                 return (BAM_ERROR);
1342         }
1343 
1344         if (stat(menu_root, &sb) == -1) {
1345                 bam_error(_("cannot find GRUB menu\n"));
1346                 return (BAM_ERROR);
1347         }
1348 
1349         BAM_DPRINTF(("%s: menu root is %s\n", fcn, menu_root));
1350 
1351         /*
1352          * We no longer use the GRUB slice file. If it exists, then
1353          * the user is doing something that is unsupported (such as
1354          * standard upgrading an old Live Upgrade BE). If that
1355          * happens, mimic existing behavior i.e. pretend that it is
1356          * not a BE. Emit a warning though.
1357          */
1358         if (bam_alt_root) {
1359                 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1360                     GRUB_slice);
1361         } else {
1362                 (void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1363         }
1364 
1365         if (bam_verbose && stat(path, &sb) == 0)
1366                 bam_error(_("unsupported GRUB slice file (%s) exists - "
1367                     "ignoring.\n"), path);
1368 
1369         if (is_zfs(menu_root)) {
1370                 assert(strcmp(menu_root, bam_root) == 0);
1371                 special = get_special(menu_root);
1372                 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1373                 if (special == NULL) {
1374                         bam_error(_("cant find special file for "
1375                             "mount-point %s\n"), menu_root);
1376                         return (BAM_ERROR);
1377                 }
1378                 pool = strtok(special, "/");
1379                 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1380                 if (pool == NULL) {
1381                         free(special);
1382                         bam_error(_("cant find pool for mount-point %s\n"),
1383                             menu_root);
1384                         return (BAM_ERROR);
1385                 }
1386                 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
1387 
1388                 zmntpt = mount_top_dataset(pool, &zmnted);
1389                 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1390                 if (zmntpt == NULL) {
1391                         bam_error(_("cannot mount pool dataset for pool: %s\n"),
1392                             pool);
1393                         free(special);
1394                         return (BAM_ERROR);
1395                 }
1396                 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
1397 
1398                 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1399                 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
1400         }
1401 
1402         elide_trailing_slash(menu_root, clean_menu_root,
1403             sizeof (clean_menu_root));
1404 
1405         BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
1406 
1407         (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1408         (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1409 
1410         BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
1411 
1412         /*
1413          * If listing the menu, display the menu location
1414          */
1415         if (strcmp(subcmd, "list_entry") == 0)
1416                 bam_print(_("the location for the active GRUB menu is: %s\n"),
1417                     menu_path);
1418 
1419         if ((menu = menu_read(menu_path)) == NULL) {
1420                 bam_error(_("cannot find GRUB menu file: %s\n"), menu_path);
1421                 free(special);
1422 
1423                 return (BAM_ERROR);
1424         }
1425 
1426         /*
1427          * We already checked the following case in
1428          * check_subcmd_and_suboptions() above. Complete the
1429          * final step now.
1430          */
1431         if (strcmp(subcmd, "set_option") == 0) {
1432                 assert(largc == 1 && largv[0] && largv[1] == NULL);
1433                 opt = largv[0];
1434         } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1435             (strcmp(subcmd, "list_setting") != 0)) {
1436                 assert(largc == 0 && largv == NULL);
1437         }
1438 
1439         ret = get_boot_cap(bam_root);
1440         if (ret != BAM_SUCCESS) {
1441                 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1442                 goto out;
1443         }
1444 
1445         /*
1446          * Once the sub-cmd handler has run
1447          * only the line field is guaranteed to have valid values
1448          */
1449         if (strcmp(subcmd, "update_entry") == 0) {
1450                 ret = f(menu, menu_root, osdev);
1451         } else if (strcmp(subcmd, "upgrade") == 0) {
1452                 ret = f(menu, bam_root, menu_root);
1453         } else if (strcmp(subcmd, "list_entry") == 0) {
1454                 ret = f(menu, menu_path, opt);
1455         } else if (strcmp(subcmd, "list_setting") == 0) {
1456                 ret = f(menu, ((largc > 0) ? largv[0] : ""),
1457                     ((largc > 1) ? largv[1] : ""));
1458         } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1459                 if (is_sparc()) {
1460                         bam_error(_("%s operation unsupported on SPARC "
1461                             "machines\n"), subcmd);
1462                         ret = BAM_ERROR;
1463                 } else {
1464                         ret = f(menu, bam_root, NULL);
1465                 }
1466         } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1467                 if (is_sparc()) {
1468                         bam_error(_("%s operation unsupported on SPARC "
1469                             "machines\n"), subcmd);
1470                         ret = BAM_ERROR;
1471                 } else {
1472                         char *extra_args = NULL;
1473 
1474                         /*
1475                          * Compress all arguments passed in the largv[] array
1476                          * into one string that can then be appended to the
1477                          * end of the kernel$ string the routine to enable the
1478                          * hypervisor will build.
1479                          *
1480                          * This allows the caller to supply arbitrary unparsed
1481                          * arguments, such as dom0 memory settings or APIC
1482                          * options.
1483                          *
1484                          * This concatenation will be done without ANY syntax
1485                          * checking whatsoever, so it's the responsibility of
1486                          * the caller to make sure the arguments are valid and
1487                          * do not duplicate arguments the conversion routines
1488                          * may create.
1489                          */
1490                         if (largc > 0) {
1491                                 int extra_len, i;
1492 
1493                                 for (extra_len = 0, i = 0; i < largc; i++)
1494                                         extra_len += strlen(largv[i]);
1495 
1496                                 /*
1497                                  * Allocate space for argument strings,
1498                                  * intervening spaces and terminating NULL.
1499                                  */
1500                                 extra_args = alloca(extra_len + largc);
1501 
1502                                 (void) strcpy(extra_args, largv[0]);
1503 
1504                                 for (i = 1; i < largc; i++) {
1505                                         (void) strcat(extra_args, " ");
1506                                         (void) strcat(extra_args, largv[i]);
1507                                 }
1508                         }
1509 
1510                         ret = f(menu, bam_root, extra_args);
1511                 }
1512         } else
1513                 ret = f(menu, NULL, opt);
1514 
1515         if (ret == BAM_WRITE) {
1516                 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
1517                     fcn, clean_menu_root));
1518                 ret = menu_write(clean_menu_root, menu);
1519         }
1520 
1521 out:
1522         INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1523         assert((is_zfs(menu_root)) ^ (pool == NULL));
1524         if (pool) {
1525                 (void) umount_top_dataset(pool, zmnted, zmntpt);
1526                 free(special);
1527         }
1528         menu_free(menu);
1529         return (ret);
1530 }
1531 
1532 
1533 static error_t
1534 bam_archive(
1535         char *subcmd,
1536         char *opt)
1537 {
1538         error_t                 ret;
1539         error_t                 (*f)(char *root, char *opt);
1540         const char              *fcn = "bam_archive()";
1541 
1542         /*
1543          * Add trailing / for archive subcommands
1544          */
1545         if (rootbuf[strlen(rootbuf) - 1] != '/')
1546                 (void) strcat(rootbuf, "/");
1547         bam_rootlen = strlen(rootbuf);
1548 
1549         /*
1550          * Check arguments
1551          */
1552         ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1553         if (ret != BAM_SUCCESS) {
1554                 return (BAM_ERROR);
1555         }
1556 
1557         ret = get_boot_cap(rootbuf);
1558         if (ret != BAM_SUCCESS) {
1559                 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1560                 return (ret);
1561         }
1562 
1563         /*
1564          * Check archive not supported with update_all
1565          * since it is awkward to display out-of-sync
1566          * information for each BE.
1567          */
1568         if (bam_check && strcmp(subcmd, "update_all") == 0) {
1569                 bam_error(_("the check option is not supported with "
1570                     "subcmd: %s\n"), subcmd);
1571                 return (BAM_ERROR);
1572         }
1573 
1574         if (strcmp(subcmd, "update_all") == 0)
1575                 bam_update_all = 1;
1576 
1577 #if !defined(_OBP)
1578         ucode_install(bam_root);
1579 #endif
1580 
1581         ret = f(bam_root, opt);
1582 
1583         bam_update_all = 0;
1584 
1585         return (ret);
1586 }
1587 
1588 /*PRINTFLIKE1*/
1589 void
1590 bam_error(char *format, ...)
1591 {
1592         va_list ap;
1593 
1594         va_start(ap, format);
1595         (void) fprintf(stderr, "%s: ", prog);
1596         (void) vfprintf(stderr, format, ap);
1597         va_end(ap);
1598 }
1599 
1600 /*PRINTFLIKE1*/
1601 void
1602 bam_derror(char *format, ...)
1603 {
1604         va_list ap;
1605 
1606         assert(bam_debug);
1607 
1608         va_start(ap, format);
1609         (void) fprintf(stderr, "DEBUG: ");
1610         (void) vfprintf(stderr, format, ap);
1611         va_end(ap);
1612 }
1613 
1614 /*PRINTFLIKE1*/
1615 void
1616 bam_print(char *format, ...)
1617 {
1618         va_list ap;
1619 
1620         va_start(ap, format);
1621         (void) vfprintf(stdout, format, ap);
1622         va_end(ap);
1623 }
1624 
1625 /*PRINTFLIKE1*/
1626 void
1627 bam_print_stderr(char *format, ...)
1628 {
1629         va_list ap;
1630 
1631         va_start(ap, format);
1632         (void) vfprintf(stderr, format, ap);
1633         va_end(ap);
1634 }
1635 
1636 void
1637 bam_exit(int excode)
1638 {
1639         restore_env();
1640         bam_unlock();
1641         exit(excode);
1642 }
1643 
1644 static void
1645 bam_lock(void)
1646 {
1647         struct flock lock;
1648         pid_t pid;
1649 
1650         bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1651         if (bam_lock_fd < 0) {
1652                 /*
1653                  * We may be invoked early in boot for archive verification.
1654                  * In this case, root is readonly and /var/run may not exist.
1655                  * Proceed without the lock
1656                  */
1657                 if (errno == EROFS || errno == ENOENT) {
1658                         bam_root_readonly = 1;
1659                         return;
1660                 }
1661 
1662                 bam_error(_("failed to open file: %s: %s\n"),
1663                     BAM_LOCK_FILE, strerror(errno));
1664                 bam_exit(1);
1665         }
1666 
1667         lock.l_type = F_WRLCK;
1668         lock.l_whence = SEEK_SET;
1669         lock.l_start = 0;
1670         lock.l_len = 0;
1671 
1672         if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1673                 if (errno != EACCES && errno != EAGAIN) {
1674                         bam_error(_("failed to lock file: %s: %s\n"),
1675                             BAM_LOCK_FILE, strerror(errno));
1676                         (void) close(bam_lock_fd);
1677                         bam_lock_fd = -1;
1678                         bam_exit(1);
1679                 }
1680                 pid = 0;
1681                 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1682                 bam_print(
1683                     _("another instance of bootadm (pid %lu) is running\n"),
1684                     pid);
1685 
1686                 lock.l_type = F_WRLCK;
1687                 lock.l_whence = SEEK_SET;
1688                 lock.l_start = 0;
1689                 lock.l_len = 0;
1690                 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1691                         bam_error(_("failed to lock file: %s: %s\n"),
1692                             BAM_LOCK_FILE, strerror(errno));
1693                         (void) close(bam_lock_fd);
1694                         bam_lock_fd = -1;
1695                         bam_exit(1);
1696                 }
1697         }
1698 
1699         /* We own the lock now */
1700         pid = getpid();
1701         (void) write(bam_lock_fd, &pid, sizeof (pid));
1702 }
1703 
1704 static void
1705 bam_unlock(void)
1706 {
1707         struct flock unlock;
1708 
1709         /*
1710          * NOP if we don't hold the lock
1711          */
1712         if (bam_lock_fd < 0) {
1713                 return;
1714         }
1715 
1716         unlock.l_type = F_UNLCK;
1717         unlock.l_whence = SEEK_SET;
1718         unlock.l_start = 0;
1719         unlock.l_len = 0;
1720 
1721         if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1722                 bam_error(_("failed to unlock file: %s: %s\n"),
1723                     BAM_LOCK_FILE, strerror(errno));
1724         }
1725 
1726         if (close(bam_lock_fd) == -1) {
1727                 bam_error(_("failed to close file: %s: %s\n"),
1728                     BAM_LOCK_FILE, strerror(errno));
1729         }
1730         bam_lock_fd = -1;
1731 }
1732 
1733 static error_t
1734 list_archive(char *root, char *opt)
1735 {
1736         filelist_t flist;
1737         filelist_t *flistp = &flist;
1738         line_t *lp;
1739 
1740         assert(root);
1741         assert(opt == NULL);
1742 
1743         flistp->head = flistp->tail = NULL;
1744         if (read_list(root, flistp) != BAM_SUCCESS) {
1745                 return (BAM_ERROR);
1746         }
1747         assert(flistp->head && flistp->tail);
1748 
1749         for (lp = flistp->head; lp; lp = lp->next) {
1750                 bam_print(_("%s\n"), lp->line);
1751         }
1752 
1753         filelist_free(flistp);
1754 
1755         return (BAM_SUCCESS);
1756 }
1757 
1758 /*
1759  * This routine writes a list of lines to a file.
1760  * The list is *not* freed
1761  */
1762 static error_t
1763 list2file(char *root, char *tmp, char *final, line_t *start)
1764 {
1765         char            tmpfile[PATH_MAX];
1766         char            path[PATH_MAX];
1767         FILE            *fp;
1768         int             ret;
1769         struct stat     sb;
1770         mode_t          mode;
1771         uid_t           root_uid;
1772         gid_t           sys_gid;
1773         struct passwd   *pw;
1774         struct group    *gp;
1775         const char      *fcn = "list2file()";
1776 
1777         (void) snprintf(path, sizeof (path), "%s%s", root, final);
1778 
1779         if (start == NULL) {
1780                 /* Empty GRUB menu */
1781                 if (stat(path, &sb) != -1) {
1782                         bam_print(_("file is empty, deleting file: %s\n"),
1783                             path);
1784                         if (unlink(path) != 0) {
1785                                 bam_error(_("failed to unlink file: %s: %s\n"),
1786                                     path, strerror(errno));
1787                                 return (BAM_ERROR);
1788                         } else {
1789                                 return (BAM_SUCCESS);
1790                         }
1791                 }
1792                 return (BAM_SUCCESS);
1793         }
1794 
1795         /*
1796          * Preserve attributes of existing file if possible,
1797          * otherwise ask the system for uid/gid of root/sys.
1798          * If all fails, fall back on hard-coded defaults.
1799          */
1800         if (stat(path, &sb) != -1) {
1801                 mode = sb.st_mode;
1802                 root_uid = sb.st_uid;
1803                 sys_gid = sb.st_gid;
1804         } else {
1805                 mode = DEFAULT_DEV_MODE;
1806                 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1807                         root_uid = pw->pw_uid;
1808                 } else {
1809                         bam_error(_("getpwnam: uid for %s failed, "
1810                             "defaulting to %d\n"),
1811                             DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1812                         root_uid = (uid_t)DEFAULT_DEV_UID;
1813                 }
1814                 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1815                         sys_gid = gp->gr_gid;
1816                 } else {
1817                         bam_error(_("getgrnam: gid for %s failed, "
1818                             "defaulting to %d\n"),
1819                             DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1820                         sys_gid = (gid_t)DEFAULT_DEV_GID;
1821                 }
1822         }
1823 
1824         (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1825 
1826         /* Truncate tmpfile first */
1827         fp = fopen(tmpfile, "w");
1828         if (fp == NULL) {
1829                 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1830                     strerror(errno));
1831                 return (BAM_ERROR);
1832         }
1833         ret = fclose(fp);
1834         INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1835         if (ret == EOF) {
1836                 bam_error(_("failed to close file: %s: %s\n"),
1837                     tmpfile, strerror(errno));
1838                 return (BAM_ERROR);
1839         }
1840 
1841         /* Now open it in append mode */
1842         fp = fopen(tmpfile, "a");
1843         if (fp == NULL) {
1844                 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1845                     strerror(errno));
1846                 return (BAM_ERROR);
1847         }
1848 
1849         for (; start; start = start->next) {
1850                 ret = s_fputs(start->line, fp);
1851                 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1852                 if (ret == EOF) {
1853                         bam_error(_("write to file failed: %s: %s\n"),
1854                             tmpfile, strerror(errno));
1855                         (void) fclose(fp);
1856                         return (BAM_ERROR);
1857                 }
1858         }
1859 
1860         ret = fclose(fp);
1861         INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1862         if (ret == EOF) {
1863                 bam_error(_("failed to close file: %s: %s\n"),
1864                     tmpfile, strerror(errno));
1865                 return (BAM_ERROR);
1866         }
1867 
1868         /*
1869          * Set up desired attributes.  Ignore failures on filesystems
1870          * not supporting these operations - pcfs reports unsupported
1871          * operations as EINVAL.
1872          */
1873         ret = chmod(tmpfile, mode);
1874         if (ret == -1 &&
1875             errno != EINVAL && errno != ENOTSUP) {
1876                 bam_error(_("chmod operation on %s failed - %s\n"),
1877                     tmpfile, strerror(errno));
1878                 return (BAM_ERROR);
1879         }
1880 
1881         ret = chown(tmpfile, root_uid, sys_gid);
1882         if (ret == -1 &&
1883             errno != EINVAL && errno != ENOTSUP) {
1884                 bam_error(_("chgrp operation on %s failed - %s\n"),
1885                     tmpfile, strerror(errno));
1886                 return (BAM_ERROR);
1887         }
1888 
1889         /*
1890          * Do an atomic rename
1891          */
1892         ret = rename(tmpfile, path);
1893         INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1894         if (ret != 0) {
1895                 bam_error(_("rename to file failed: %s: %s\n"), path,
1896                     strerror(errno));
1897                 return (BAM_ERROR);
1898         }
1899 
1900         BAM_DPRINTF(("%s: wrote file successfully: %s\n", fcn, path));
1901         return (BAM_SUCCESS);
1902 }
1903 
1904 /*
1905  * Checks if the path specified (without the file name at the end) exists
1906  * and creates it if not. If the path exists and is not a directory, an attempt
1907  * to unlink is made.
1908  */
1909 static int
1910 setup_path(char *path)
1911 {
1912         char            *p;
1913         int             ret;
1914         struct stat     sb;
1915 
1916         p = strrchr(path, '/');
1917         if (p != NULL) {
1918                 *p = '\0';
1919                 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1920                         /* best effort attempt, mkdirp will catch the error */
1921                         (void) unlink(path);
1922                         if (bam_verbose)
1923                                 bam_print(_("need to create directory "
1924                                     "path for %s\n"), path);
1925                         ret = mkdirp(path, DIR_PERMS);
1926                         if (ret == -1) {
1927                                 bam_error(_("mkdir of %s failed: %s\n"),
1928                                     path, strerror(errno));
1929                                 *p = '/';
1930                                 return (BAM_ERROR);
1931                         }
1932                 }
1933                 *p = '/';
1934                 return (BAM_SUCCESS);
1935         }
1936         return (BAM_SUCCESS);
1937 }
1938 
1939 typedef union {
1940         gzFile  gzfile;
1941         int     fdfile;
1942 } outfile;
1943 
1944 typedef struct {
1945         char            path[PATH_MAX];
1946         outfile         out;
1947 } cachefile;
1948 
1949 static int
1950 setup_file(char *base, const char *path, cachefile *cf)
1951 {
1952         int     ret;
1953         char    *strip;
1954 
1955         /* init gzfile or fdfile in case we fail before opening */
1956         if (bam_direct == BAM_DIRECT_DBOOT)
1957                 cf->out.gzfile = NULL;
1958         else
1959                 cf->out.fdfile = -1;
1960 
1961         /* strip the trailing altroot path */
1962         strip = (char *)path + strlen(rootbuf);
1963 
1964         ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1965         if (ret >= sizeof (cf->path)) {
1966                 bam_error(_("unable to create path on mountpoint %s, "
1967                     "path too long\n"), rootbuf);
1968                 return (BAM_ERROR);
1969         }
1970 
1971         /* Check if path is present in the archive cache directory */
1972         if (setup_path(cf->path) == BAM_ERROR)
1973                 return (BAM_ERROR);
1974 
1975         if (bam_direct == BAM_DIRECT_DBOOT) {
1976                 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1977                         bam_error(_("failed to open file: %s: %s\n"),
1978                             cf->path, strerror(errno));
1979                         return (BAM_ERROR);
1980                 }
1981                 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1982                     Z_DEFAULT_STRATEGY);
1983         } else {
1984                 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1985                     == -1) {
1986                         bam_error(_("failed to open file: %s: %s\n"),
1987                             cf->path, strerror(errno));
1988                         return (BAM_ERROR);
1989                 }
1990         }
1991 
1992         return (BAM_SUCCESS);
1993 }
1994 
1995 static int
1996 cache_write(cachefile cf, char *buf, int size)
1997 {
1998         int     err;
1999 
2000         if (bam_direct == BAM_DIRECT_DBOOT) {
2001                 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
2002                         bam_error(_("failed to write to %s\n"),
2003                             gzerror(cf.out.gzfile, &err));
2004                         if (err == Z_ERRNO && bam_verbose) {
2005                                 bam_error(_("write to file failed: %s: %s\n"),
2006                                     cf.path, strerror(errno));
2007                         }
2008                         return (BAM_ERROR);
2009                 }
2010         } else {
2011                 if (write(cf.out.fdfile, buf, size) < 1) {
2012                         bam_error(_("write to file failed: %s: %s\n"),
2013                             cf.path, strerror(errno));
2014                         return (BAM_ERROR);
2015                 }
2016         }
2017         return (BAM_SUCCESS);
2018 }
2019 
2020 static int
2021 cache_close(cachefile cf)
2022 {
2023         int     ret;
2024 
2025         if (bam_direct == BAM_DIRECT_DBOOT) {
2026                 if (cf.out.gzfile) {
2027                         ret = gzclose(cf.out.gzfile);
2028                         if (ret != Z_OK) {
2029                                 bam_error(_("failed to close file: %s: %s\n"),
2030                                     cf.path, strerror(errno));
2031                                 return (BAM_ERROR);
2032                         }
2033                 }
2034         } else {
2035                 if (cf.out.fdfile != -1) {
2036                         ret = close(cf.out.fdfile);
2037                         if (ret != 0) {
2038                                 bam_error(_("failed to close file: %s: %s\n"),
2039                                     cf.path, strerror(errno));
2040                                 return (BAM_ERROR);
2041                         }
2042                 }
2043         }
2044 
2045         return (BAM_SUCCESS);
2046 }
2047 
2048 static int
2049 dircache_updatefile(const char *path, int what)
2050 {
2051         int             ret, exitcode;
2052         char            buf[4096 * 4];
2053         FILE            *infile;
2054         cachefile       outfile, outupdt;
2055 
2056         if (bam_nowrite()) {
2057                 set_dir_flag(what, NEED_UPDATE);
2058                 return (BAM_SUCCESS);
2059         }
2060 
2061         if (!has_cachedir(what))
2062                 return (BAM_SUCCESS);
2063 
2064         if ((infile = fopen(path, "rb")) == NULL) {
2065                 bam_error(_("failed to open file: %s: %s\n"), path,
2066                     strerror(errno));
2067                 return (BAM_ERROR);
2068         }
2069 
2070         ret = setup_file(get_cachedir(what), path, &outfile);
2071         if (ret == BAM_ERROR) {
2072                 exitcode = BAM_ERROR;
2073                 goto out;
2074         }
2075         if (!is_dir_flag_on(what, NO_MULTI)) {
2076                 ret = setup_file(get_updatedir(what), path, &outupdt);
2077                 if (ret == BAM_ERROR)
2078                         set_dir_flag(what, NO_MULTI);
2079         }
2080 
2081         while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2082                 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2083                         exitcode = BAM_ERROR;
2084                         goto out;
2085                 }
2086                 if (!is_dir_flag_on(what, NO_MULTI))
2087                         if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2088                                 set_dir_flag(what, NO_MULTI);
2089         }
2090 
2091         set_dir_flag(what, NEED_UPDATE);
2092         get_count(what)++;
2093         if (get_count(what) > COUNT_MAX)
2094                 set_dir_flag(what, NO_MULTI);
2095         exitcode = BAM_SUCCESS;
2096 out:
2097         (void) fclose(infile);
2098         if (cache_close(outfile) == BAM_ERROR)
2099                 exitcode = BAM_ERROR;
2100         if (!is_dir_flag_on(what, NO_MULTI) &&
2101             cache_close(outupdt) == BAM_ERROR)
2102                 exitcode = BAM_ERROR;
2103         if (exitcode == BAM_ERROR)
2104                 set_flag(UPDATE_ERROR);
2105         return (exitcode);
2106 }
2107 
2108 static int
2109 dircache_updatedir(const char *path, int what, int updt)
2110 {
2111         int             ret;
2112         char            dpath[PATH_MAX];
2113         char            *strip;
2114         struct stat     sb;
2115 
2116         strip = (char *)path + strlen(rootbuf);
2117 
2118         ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2119             get_updatedir(what) : get_cachedir(what), strip);
2120 
2121         if (ret >= sizeof (dpath)) {
2122                 bam_error(_("unable to create path on mountpoint %s, "
2123                     "path too long\n"), rootbuf);
2124                 set_flag(UPDATE_ERROR);
2125                 return (BAM_ERROR);
2126         }
2127 
2128         if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2129                 return (BAM_SUCCESS);
2130 
2131         if (updt) {
2132                 if (!is_dir_flag_on(what, NO_MULTI))
2133                         if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2134                                 set_dir_flag(what, NO_MULTI);
2135         } else {
2136                 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2137                         set_flag(UPDATE_ERROR);
2138                         return (BAM_ERROR);
2139                 }
2140         }
2141 
2142         set_dir_flag(what, NEED_UPDATE);
2143         return (BAM_SUCCESS);
2144 }
2145 
2146 #define DO_CACHE_DIR    0
2147 #define DO_UPDATE_DIR   1
2148 
2149 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2150 typedef         Elf64_Ehdr      _elfhdr;
2151 #else
2152 typedef         Elf32_Ehdr      _elfhdr;
2153 #endif
2154 
2155 /*
2156  * This routine updates the contents of the cache directory
2157  */
2158 static int
2159 update_dircache(const char *path, int flags)
2160 {
2161         int rc = BAM_SUCCESS;
2162 
2163         switch (flags) {
2164         case FTW_F:
2165                 {
2166                 int     fd;
2167                 _elfhdr elf;
2168 
2169                 if ((fd = open(path, O_RDONLY)) < 0) {
2170                         bam_error(_("failed to open file: %s: %s\n"),
2171                             path, strerror(errno));
2172                         set_flag(UPDATE_ERROR);
2173                         rc = BAM_ERROR;
2174                         break;
2175                 }
2176 
2177                 /*
2178                  * libelf and gelf would be a cleaner and easier way to handle
2179                  * this, but libelf fails compilation if _ILP32 is defined &&
2180                  * _FILE_OFFSET_BITS is != 32 ...
2181                  */
2182                 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2183                         bam_error(_("read failed for file: %s: %s\n"),
2184                             path, strerror(errno));
2185                         set_flag(UPDATE_ERROR);
2186                         (void) close(fd);
2187                         rc = BAM_ERROR;
2188                         break;
2189                 }
2190                 (void) close(fd);
2191 
2192                 /*
2193                  * If the file is not an executable and is not inside an amd64
2194                  * directory, we copy it in both the cache directories,
2195                  * otherwise, we only copy it inside the 64-bit one.
2196                  */
2197                 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2198                         if (strstr(path, "/amd64")) {
2199                                 rc = dircache_updatefile(path, FILE64);
2200                         } else {
2201                                 rc = dircache_updatefile(path, FILE32);
2202                                 if (rc == BAM_SUCCESS)
2203                                         rc = dircache_updatefile(path, FILE64);
2204                         }
2205                 } else {
2206                         /*
2207                          * Based on the ELF class we copy the file in the 32-bit
2208                          * or the 64-bit cache directory.
2209                          */
2210                         if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
2211                                 rc = dircache_updatefile(path, FILE32);
2212                         } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
2213                                 rc = dircache_updatefile(path, FILE64);
2214                         } else {
2215                                 bam_print(_("WARNING: file %s is neither a "
2216                                     "32-bit nor a 64-bit ELF\n"), path);
2217                                 /* paranoid */
2218                                 rc  = dircache_updatefile(path, FILE32);
2219                                 if (rc == BAM_SUCCESS)
2220                                         rc = dircache_updatefile(path, FILE64);
2221                         }
2222                 }
2223                 break;
2224                 }
2225         case FTW_D:
2226                 if (strstr(path, "/amd64") == NULL) {
2227                         rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
2228                         if (rc == BAM_SUCCESS)
2229                                 rc = dircache_updatedir(path, FILE32,
2230                                     DO_CACHE_DIR);
2231                 } else {
2232                         if (has_cachedir(FILE64)) {
2233                                 rc = dircache_updatedir(path, FILE64,
2234                                     DO_UPDATE_DIR);
2235                                 if (rc == BAM_SUCCESS)
2236                                         rc = dircache_updatedir(path, FILE64,
2237                                             DO_CACHE_DIR);
2238                         }
2239                 }
2240                 break;
2241         default:
2242                 rc = BAM_ERROR;
2243                 break;
2244         }
2245 
2246         return (rc);
2247 }
2248 
2249 /*ARGSUSED*/
2250 static int
2251 cmpstat(
2252         const char *file,
2253         const struct stat *st,
2254         int flags,
2255         struct FTW *ftw)
2256 {
2257         uint_t          sz;
2258         uint64_t        *value;
2259         uint64_t        filestat[2];
2260         int             error, ret, status;
2261 
2262         struct safefile *safefilep;
2263         FILE            *fp;
2264         struct stat     sb;
2265         regex_t re;
2266 
2267         /*
2268          * On SPARC we create/update links too.
2269          */
2270         if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2271             !is_flag_on(IS_SPARC_TARGET)))
2272                 return (0);
2273 
2274         /*
2275          * Ignore broken links
2276          */
2277         if (flags == FTW_SL && stat(file, &sb) < 0)
2278                 return (0);
2279 
2280         /*
2281          * new_nvlp may be NULL if there were errors earlier
2282          * but this is not fatal to update determination.
2283          */
2284         if (walk_arg.new_nvlp) {
2285                 filestat[0] = st->st_size;
2286                 filestat[1] = st->st_mtime;
2287                 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2288                     file + bam_rootlen, filestat, 2);
2289                 if (error)
2290                         bam_error(_("failed to update stat data for: %s: %s\n"),
2291                             file, strerror(error));
2292         }
2293 
2294         /*
2295          * If we are invoked as part of system/filesystem/boot-archive, then
2296          * there are a number of things we should not worry about
2297          */
2298         if (bam_smf_check) {
2299                 /* ignore amd64 modules unless we are booted amd64. */
2300                 if (!is_amd64() && strstr(file, "/amd64/") != 0)
2301                         return (0);
2302 
2303                 /* read in list of safe files */
2304                 if (safefiles == NULL) {
2305                         fp = fopen("/boot/solaris/filelist.safe", "r");
2306                         if (fp != NULL) {
2307                                 safefiles = s_calloc(1,
2308                                     sizeof (struct safefile));
2309                                 safefilep = safefiles;
2310                                 safefilep->name = s_calloc(1, MAXPATHLEN +
2311                                     MAXNAMELEN);
2312                                 safefilep->next = NULL;
2313                                 while (s_fgets(safefilep->name, MAXPATHLEN +
2314                                     MAXNAMELEN, fp) != NULL) {
2315                                         safefilep->next = s_calloc(1,
2316                                             sizeof (struct safefile));
2317                                         safefilep = safefilep->next;
2318                                         safefilep->name = s_calloc(1,
2319                                             MAXPATHLEN + MAXNAMELEN);
2320                                         safefilep->next = NULL;
2321                                 }
2322                                 (void) fclose(fp);
2323                         }
2324                 }
2325         }
2326 
2327         /*
2328          * On SPARC we create a -path-list file for mkisofs
2329          */
2330         if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2331                 if (flags != FTW_D) {
2332                         char    *strip;
2333 
2334                         strip = (char *)file + strlen(rootbuf);
2335                         (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2336                             file);
2337                 }
2338         }
2339 
2340         /*
2341          * We are transitioning from the old model to the dircache or the cache
2342          * directory was removed: create the entry without further checkings.
2343          */
2344         if (is_flag_on(NEED_CACHE_DIR)) {
2345                 if (bam_verbose)
2346                         bam_print(_("    new     %s\n"), file);
2347 
2348                 if (is_flag_on(IS_SPARC_TARGET)) {
2349                         set_dir_flag(FILE64, NEED_UPDATE);
2350                         return (0);
2351                 }
2352 
2353                 ret = update_dircache(file, flags);
2354                 if (ret == BAM_ERROR) {
2355                         bam_error(_("directory cache update failed for %s\n"),
2356                             file);
2357                         return (-1);
2358                 }
2359 
2360                 return (0);
2361         }
2362 
2363         /*
2364          * We need an update if file doesn't exist in old archive
2365          */
2366         if (walk_arg.old_nvlp == NULL ||
2367             nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2368             file + bam_rootlen, &value, &sz) != 0) {
2369                 if (bam_smf_check)      /* ignore new during smf check */
2370                         return (0);
2371 
2372                 if (is_flag_on(IS_SPARC_TARGET)) {
2373                         set_dir_flag(FILE64, NEED_UPDATE);
2374                 } else {
2375                         ret = update_dircache(file, flags);
2376                         if (ret == BAM_ERROR) {
2377                                 bam_error(_("directory cache update "
2378                                     "failed for %s\n"), file);
2379                                 return (-1);
2380                         }
2381                 }
2382 
2383                 if (bam_verbose)
2384                         bam_print(_("    new     %s\n"), file);
2385                 return (0);
2386         }
2387 
2388         /*
2389          * If we got there, the file is already listed as to be included in the
2390          * iso image. We just need to know if we are going to rebuild it or not
2391          */
2392         if (is_flag_on(IS_SPARC_TARGET) &&
2393             is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
2394                 return (0);
2395         /*
2396          * File exists in old archive. Check if file has changed
2397          */
2398         assert(sz == 2);
2399         bcopy(value, filestat, sizeof (filestat));
2400 
2401         if (flags != FTW_D && (filestat[0] != st->st_size ||
2402             filestat[1] != st->st_mtime)) {
2403                 if (bam_smf_check) {
2404                         safefilep = safefiles;
2405                         while (safefilep != NULL &&
2406                             safefilep->name[0] != '\0') {
2407                                 if (regcomp(&re, safefilep->name,
2408                                     REG_EXTENDED|REG_NOSUB) == 0) {
2409                                         status = regexec(&re,
2410                                             file + bam_rootlen, 0, NULL, 0);
2411                                         regfree(&re);
2412                                         if (status == 0) {
2413                                                 (void) creat(
2414                                                     NEED_UPDATE_SAFE_FILE,
2415                                                     0644);
2416                                                 return (0);
2417                                         }
2418                                 }
2419                                 safefilep = safefilep->next;
2420                         }
2421                 }
2422 
2423                 if (is_flag_on(IS_SPARC_TARGET)) {
2424                         set_dir_flag(FILE64, NEED_UPDATE);
2425                 } else {
2426                         ret = update_dircache(file, flags);
2427                         if (ret == BAM_ERROR) {
2428                                 bam_error(_("directory cache update failed "
2429                                     "for %s\n"), file);
2430                                 return (-1);
2431                         }
2432                 }
2433 
2434                 if (bam_verbose) {
2435                         if (bam_smf_check)
2436                                 bam_print("    %s\n", file);
2437                         else
2438                                 bam_print(_("    changed %s\n"), file);
2439                 }
2440         }
2441 
2442         return (0);
2443 }
2444 
2445 /*
2446  * Remove a directory path recursively
2447  */
2448 static int
2449 rmdir_r(char *path)
2450 {
2451         struct dirent   *d = NULL;
2452         DIR             *dir = NULL;
2453         char            tpath[PATH_MAX];
2454         struct stat     sb;
2455 
2456         if ((dir = opendir(path)) == NULL)
2457                 return (-1);
2458 
2459         while ((d = readdir(dir)) != NULL) {
2460                 if ((strcmp(d->d_name, ".") != 0) &&
2461                     (strcmp(d->d_name, "..") != 0)) {
2462                         (void) snprintf(tpath, sizeof (tpath), "%s/%s",
2463                             path, d->d_name);
2464                         if (stat(tpath, &sb) == 0) {
2465                                 if (sb.st_mode & S_IFDIR)
2466                                         (void) rmdir_r(tpath);
2467                                 else
2468                                         (void) remove(tpath);
2469                         }
2470                 }
2471         }
2472         return (remove(path));
2473 }
2474 
2475 /*
2476  * Check if cache directory exists and, if not, create it and update flags
2477  * accordingly. If the path exists, but it's not a directory, a best effort
2478  * attempt to remove and recreate it is made.
2479  * If the user requested a 'purge', always recreate the directory from scratch.
2480  */
2481 static int
2482 set_cache_dir(char *root, int what)
2483 {
2484         struct stat     sb;
2485         int             ret = 0;
2486 
2487         ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
2488             "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
2489             "/amd64" : "", CACHEDIR_SUFFIX);
2490 
2491         if (ret >= sizeof (get_cachedir(what))) {
2492                 bam_error(_("unable to create path on mountpoint %s, "
2493                     "path too long\n"), rootbuf);
2494                 return (BAM_ERROR);
2495         }
2496 
2497         if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2498                 (void) rmdir_r(get_cachedir(what));
2499 
2500         if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2501                 /* best effort unlink attempt, mkdir will catch errors */
2502                 (void) unlink(get_cachedir(what));
2503 
2504                 if (bam_verbose)
2505                         bam_print(_("archive cache directory not found: %s\n"),
2506                             get_cachedir(what));
2507                 ret = mkdir(get_cachedir(what), DIR_PERMS);
2508                 if (ret < 0) {
2509                         bam_error(_("mkdir of %s failed: %s\n"),
2510                             get_cachedir(what), strerror(errno));
2511                         get_cachedir(what)[0] = '\0';
2512                         return (ret);
2513                 }
2514                 set_flag(NEED_CACHE_DIR);
2515                 set_dir_flag(what, NO_MULTI);
2516         }
2517 
2518         return (BAM_SUCCESS);
2519 }
2520 
2521 static int
2522 set_update_dir(char *root, int what)
2523 {
2524         struct stat     sb;
2525         int             ret;
2526 
2527         if (is_dir_flag_on(what, NO_MULTI))
2528                 return (BAM_SUCCESS);
2529 
2530         if (!bam_extend) {
2531                 set_dir_flag(what, NO_MULTI);
2532                 return (BAM_SUCCESS);
2533         }
2534 
2535         if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2536                 ret = snprintf(get_updatedir(what),
2537                     sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2538                     ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2539         else
2540                 ret = snprintf(get_updatedir(what),
2541                     sizeof (get_updatedir(what)), "%s%s%s%s", root,
2542                     ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2543 
2544         if (ret >= sizeof (get_updatedir(what))) {
2545                 bam_error(_("unable to create path on mountpoint %s, "
2546                     "path too long\n"), rootbuf);
2547                 return (BAM_ERROR);
2548         }
2549 
2550         if (stat(get_updatedir(what), &sb) == 0) {
2551                 if (S_ISDIR(sb.st_mode))
2552                         ret = rmdir_r(get_updatedir(what));
2553                 else
2554                         ret = unlink(get_updatedir(what));
2555 
2556                 if (ret != 0)
2557                         set_dir_flag(what, NO_MULTI);
2558         }
2559 
2560         if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2561                 set_dir_flag(what, NO_MULTI);
2562 
2563         return (BAM_SUCCESS);
2564 }
2565 
2566 static int
2567 is_valid_archive(char *root, int what)
2568 {
2569         char            archive_path[PATH_MAX];
2570         char            timestamp_path[PATH_MAX];
2571         struct stat     sb, timestamp;
2572         int             ret;
2573 
2574         if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2575                 ret = snprintf(archive_path, sizeof (archive_path),
2576                     "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2577                     ARCHIVE_SUFFIX);
2578         else
2579                 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2580                     root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2581 
2582         if (ret >= sizeof (archive_path)) {
2583                 bam_error(_("unable to create path on mountpoint %s, "
2584                     "path too long\n"), rootbuf);
2585                 return (BAM_ERROR);
2586         }
2587 
2588         if (stat(archive_path, &sb) != 0) {
2589                 if (bam_verbose && !bam_check)
2590                         bam_print(_("archive not found: %s\n"), archive_path);
2591                 set_dir_flag(what, NEED_UPDATE);
2592                 set_dir_flag(what, NO_MULTI);
2593                 return (BAM_SUCCESS);
2594         }
2595 
2596         /*
2597          * The timestamp file is used to prevent stale files in the archive
2598          * cache.
2599          * Stale files can happen if the system is booted back and forth across
2600          * the transition from bootadm-before-the-cache to
2601          * bootadm-after-the-cache, since older versions of bootadm don't know
2602          * about the existence of the archive cache.
2603          *
2604          * Since only bootadm-after-the-cache versions know about about this
2605          * file, we require that the boot archive be older than this file.
2606          */
2607         ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2608             FILE_STAT_TIMESTAMP);
2609 
2610         if (ret >= sizeof (timestamp_path)) {
2611                 bam_error(_("unable to create path on mountpoint %s, "
2612                     "path too long\n"), rootbuf);
2613                 return (BAM_ERROR);
2614         }
2615 
2616         if (stat(timestamp_path, &timestamp) != 0 ||
2617             sb.st_mtime > timestamp.st_mtime) {
2618                 if (bam_verbose && !bam_check)
2619                         bam_print(
2620                             _("archive cache is out of sync. Rebuilding.\n"));
2621                 /*
2622                  * Don't generate a false positive for the boot-archive service
2623                  * but trigger an update of the archive cache in
2624                  * boot-archive-update.
2625                  */
2626                 if (bam_smf_check) {
2627                         (void) creat(NEED_UPDATE_FILE, 0644);
2628                         return (BAM_SUCCESS);
2629                 }
2630 
2631                 set_flag(INVALIDATE_CACHE);
2632                 set_dir_flag(what, NEED_UPDATE);
2633                 set_dir_flag(what, NO_MULTI);
2634                 return (BAM_SUCCESS);
2635         }
2636 
2637         if (is_flag_on(IS_SPARC_TARGET))
2638                 return (BAM_SUCCESS);
2639 
2640         if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2641                 if (bam_verbose && !bam_check)
2642                         bam_print(_("archive %s is bigger than %d bytes and "
2643                             "will be rebuilt\n"), archive_path, BA_SIZE_MAX);
2644                 set_dir_flag(what, NO_MULTI);
2645         }
2646 
2647         return (BAM_SUCCESS);
2648 }
2649 
2650 /*
2651  * Check flags and presence of required files and directories.
2652  * The force flag and/or absence of files should
2653  * trigger an update.
2654  * Suppress stdout output if check (-n) option is set
2655  * (as -n should only produce parseable output.)
2656  */
2657 static int
2658 check_flags_and_files(char *root)
2659 {
2660 
2661         struct stat     sb;
2662         int             ret;
2663 
2664         /*
2665          * If archive is missing, create archive
2666          */
2667         if (is_flag_on(IS_SPARC_TARGET)) {
2668                 ret = is_valid_archive(root, FILE64);
2669                 if (ret == BAM_ERROR)
2670                         return (BAM_ERROR);
2671         } else {
2672                 int     what = FILE32;
2673                 do {
2674                         ret = is_valid_archive(root, what);
2675                         if (ret == BAM_ERROR)
2676                                 return (BAM_ERROR);
2677                         what++;
2678                 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2679         }
2680 
2681         if (bam_nowrite())
2682                 return (BAM_SUCCESS);
2683 
2684 
2685         /*
2686          * check if cache directories exist on x86.
2687          * check (and always open) the cache file on SPARC.
2688          */
2689         if (is_sparc()) {
2690                 ret = snprintf(get_cachedir(FILE64),
2691                     sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2692                     ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2693 
2694                 if (ret >= sizeof (get_cachedir(FILE64))) {
2695                         bam_error(_("unable to create path on mountpoint %s, "
2696                             "path too long\n"), rootbuf);
2697                         return (BAM_ERROR);
2698                 }
2699 
2700                 if (stat(get_cachedir(FILE64), &sb) != 0) {
2701                         set_flag(NEED_CACHE_DIR);
2702                         set_dir_flag(FILE64, NEED_UPDATE);
2703                 }
2704 
2705                 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2706                 if (walk_arg.sparcfile == NULL) {
2707                         bam_error(_("failed to open file: %s: %s\n"),
2708                             get_cachedir(FILE64), strerror(errno));
2709                         return (BAM_ERROR);
2710                 }
2711 
2712                 set_dir_present(FILE64);
2713         } else {
2714                 int     what = FILE32;
2715 
2716                 do {
2717                         if (set_cache_dir(root, what) != 0)
2718                                 return (BAM_ERROR);
2719 
2720                         set_dir_present(what);
2721 
2722                         if (set_update_dir(root, what) != 0)
2723                                 return (BAM_ERROR);
2724                         what++;
2725                 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2726         }
2727 
2728         /*
2729          * if force, create archive unconditionally
2730          */
2731         if (bam_force) {
2732                 if (!is_sparc())
2733                         set_dir_flag(FILE32, NEED_UPDATE);
2734                 set_dir_flag(FILE64, NEED_UPDATE);
2735                 if (bam_verbose)
2736                         bam_print(_("forced update of archive requested\n"));
2737                 return (BAM_SUCCESS);
2738         }
2739 
2740         return (BAM_SUCCESS);
2741 }
2742 
2743 static error_t
2744 read_one_list(char *root, filelist_t  *flistp, char *filelist)
2745 {
2746         char            path[PATH_MAX];
2747         FILE            *fp;
2748         char            buf[BAM_MAXLINE];
2749         const char      *fcn = "read_one_list()";
2750 
2751         (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2752 
2753         fp = fopen(path, "r");
2754         if (fp == NULL) {
2755                 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2756                     fcn, path, strerror(errno)));
2757                 return (BAM_ERROR);
2758         }
2759         while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2760                 /* skip blank lines */
2761                 if (strspn(buf, " \t") == strlen(buf))
2762                         continue;
2763                 append_to_flist(flistp, buf);
2764         }
2765         if (fclose(fp) != 0) {
2766                 bam_error(_("failed to close file: %s: %s\n"),
2767                     path, strerror(errno));
2768                 return (BAM_ERROR);
2769         }
2770         return (BAM_SUCCESS);
2771 }
2772 
2773 static error_t
2774 read_list(char *root, filelist_t  *flistp)
2775 {
2776         char            path[PATH_MAX];
2777         char            cmd[PATH_MAX];
2778         struct stat     sb;
2779         int             n, rval;
2780         const char      *fcn = "read_list()";
2781 
2782         flistp->head = flistp->tail = NULL;
2783 
2784         /*
2785          * build and check path to extract_boot_filelist.ksh
2786          */
2787         n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2788         if (n >= sizeof (path)) {
2789                 bam_error(_("archive filelist is empty\n"));
2790                 return (BAM_ERROR);
2791         }
2792 
2793         if (is_safe_exec(path) == BAM_ERROR)
2794                 return (BAM_ERROR);
2795 
2796         /*
2797          * If extract_boot_filelist is present, exec it, otherwise read
2798          * the filelists directly, for compatibility with older images.
2799          */
2800         if (stat(path, &sb) == 0) {
2801                 /*
2802                  * build arguments to exec extract_boot_filelist.ksh
2803                  */
2804                 char *rootarg, *platarg;
2805                 int platarglen = 1, rootarglen = 1;
2806                 if (strlen(root) > 1)
2807                         rootarglen += strlen(root) + strlen("-R ");
2808                 if (bam_alt_platform)
2809                         platarglen += strlen(bam_platform) + strlen("-p ");
2810                 platarg = s_calloc(1, platarglen);
2811                 rootarg = s_calloc(1, rootarglen);
2812                 *platarg = 0;
2813                 *rootarg = 0;
2814 
2815                 if (strlen(root) > 1) {
2816                         (void) snprintf(rootarg, rootarglen,
2817                             "-R %s", root);
2818                 }
2819                 if (bam_alt_platform) {
2820                         (void) snprintf(platarg, platarglen,
2821                             "-p %s", bam_platform);
2822                 }
2823                 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2824                     path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2825                 free(platarg);
2826                 free(rootarg);
2827                 if (n >= sizeof (cmd)) {
2828                         bam_error(_("archive filelist is empty\n"));
2829                         return (BAM_ERROR);
2830                 }
2831                 if (exec_cmd(cmd, flistp) != 0) {
2832                         BAM_DPRINTF(("%s: failed to open archive "
2833                             "filelist: %s: %s\n", fcn, path, strerror(errno)));
2834                         return (BAM_ERROR);
2835                 }
2836         } else {
2837                 /*
2838                  * Read current lists of files - only the first is mandatory
2839                  */
2840                 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2841                 if (rval != BAM_SUCCESS)
2842                         return (rval);
2843                 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2844         }
2845 
2846         if (flistp->head == NULL) {
2847                 bam_error(_("archive filelist is empty\n"));
2848                 return (BAM_ERROR);
2849         }
2850 
2851         return (BAM_SUCCESS);
2852 }
2853 
2854 static void
2855 getoldstat(char *root)
2856 {
2857         char            path[PATH_MAX];
2858         int             fd, error;
2859         struct stat     sb;
2860         char            *ostat;
2861 
2862         (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2863         fd = open(path, O_RDONLY);
2864         if (fd == -1) {
2865                 if (bam_verbose)
2866                         bam_print(_("failed to open file: %s: %s\n"),
2867                             path, strerror(errno));
2868                 goto out_err;
2869         }
2870 
2871         if (fstat(fd, &sb) != 0) {
2872                 bam_error(_("stat of file failed: %s: %s\n"), path,
2873                     strerror(errno));
2874                 goto out_err;
2875         }
2876 
2877         ostat = s_calloc(1, sb.st_size);
2878 
2879         if (read(fd, ostat, sb.st_size) != sb.st_size) {
2880                 bam_error(_("read failed for file: %s: %s\n"), path,
2881                     strerror(errno));
2882                 free(ostat);
2883                 goto out_err;
2884         }
2885 
2886         (void) close(fd);
2887         fd = -1;
2888 
2889         walk_arg.old_nvlp = NULL;
2890         error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2891 
2892         free(ostat);
2893 
2894         if (error) {
2895                 bam_error(_("failed to unpack stat data: %s: %s\n"),
2896                     path, strerror(error));
2897                 walk_arg.old_nvlp = NULL;
2898                 goto out_err;
2899         } else {
2900                 return;
2901         }
2902 
2903 out_err:
2904         if (fd != -1)
2905                 (void) close(fd);
2906         if (!is_flag_on(IS_SPARC_TARGET))
2907                 set_dir_flag(FILE32, NEED_UPDATE);
2908         set_dir_flag(FILE64, NEED_UPDATE);
2909 }
2910 
2911 /* Best effort stale entry removal */
2912 static void
2913 delete_stale(char *file, int what)
2914 {
2915         char            path[PATH_MAX];
2916         struct stat     sb;
2917 
2918         (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2919         if (!bam_check && stat(path, &sb) == 0) {
2920                 if (sb.st_mode & S_IFDIR)
2921                         (void) rmdir_r(path);
2922                 else
2923                         (void) unlink(path);
2924 
2925                 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2926         }
2927 }
2928 
2929 /*
2930  * Checks if a file in the current (old) archive has
2931  * been deleted from the root filesystem. This is needed for
2932  * software like Trusted Extensions (TX) that switch early
2933  * in boot based on presence/absence of a kernel module.
2934  */
2935 static void
2936 check4stale(char *root)
2937 {
2938         nvpair_t        *nvp;
2939         nvlist_t        *nvlp;
2940         char            *file;
2941         char            path[PATH_MAX];
2942 
2943         /*
2944          * Skip stale file check during smf check
2945          */
2946         if (bam_smf_check)
2947                 return;
2948 
2949         /*
2950          * If we need to (re)create the cache, there's no need to check for
2951          * stale files
2952          */
2953         if (is_flag_on(NEED_CACHE_DIR))
2954                 return;
2955 
2956         /* Nothing to do if no old stats */
2957         if ((nvlp = walk_arg.old_nvlp) == NULL)
2958                 return;
2959 
2960         for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2961             nvp = nvlist_next_nvpair(nvlp, nvp)) {
2962                 file = nvpair_name(nvp);
2963                 if (file == NULL)
2964                         continue;
2965                 (void) snprintf(path, sizeof (path), "%s/%s",
2966                     root, file);
2967                 if (access(path, F_OK) < 0) {
2968                         int     what;
2969 
2970                         if (bam_verbose)
2971                                 bam_print(_("    stale %s\n"), path);
2972 
2973                         if (is_flag_on(IS_SPARC_TARGET)) {
2974                                 set_dir_flag(FILE64, NEED_UPDATE);
2975                         } else {
2976                                 for (what = FILE32; what < CACHEDIR_NUM; what++)
2977                                         if (has_cachedir(what))
2978                                                 delete_stale(file, what);
2979                         }
2980                 }
2981         }
2982 }
2983 
2984 static void
2985 create_newstat(void)
2986 {
2987         int error;
2988 
2989         error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2990         if (error) {
2991                 /*
2992                  * Not fatal - we can still create archive
2993                  */
2994                 walk_arg.new_nvlp = NULL;
2995                 bam_error(_("failed to create stat data: %s\n"),
2996                     strerror(error));
2997         }
2998 }
2999 
3000 static int
3001 walk_list(char *root, filelist_t *flistp)
3002 {
3003         char path[PATH_MAX];
3004         line_t *lp;
3005 
3006         for (lp = flistp->head; lp; lp = lp->next) {
3007                 /*
3008                  * Don't follow symlinks.  A symlink must refer to
3009                  * a file that would appear in the archive through
3010                  * a direct reference.  This matches the archive
3011                  * construction behavior.
3012                  */
3013                 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
3014                 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
3015                         if (is_flag_on(UPDATE_ERROR))
3016                                 return (BAM_ERROR);
3017                         /*
3018                          * Some files may not exist.
3019                          * For example: etc/rtc_config on a x86 diskless system
3020                          * Emit verbose message only
3021                          */
3022                         if (bam_verbose)
3023                                 bam_print(_("cannot find: %s: %s\n"),
3024                                     path, strerror(errno));
3025                 }
3026         }
3027 
3028         return (BAM_SUCCESS);
3029 }
3030 
3031 /*
3032  * Update the timestamp file.
3033  */
3034 static void
3035 update_timestamp(char *root)
3036 {
3037         char    timestamp_path[PATH_MAX];
3038 
3039         /* this path length has already been checked in check_flags_and_files */
3040         (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
3041             FILE_STAT_TIMESTAMP);
3042 
3043         /*
3044          * recreate the timestamp file. Since an outdated or absent timestamp
3045          * file translates in a complete rebuild of the archive cache, notify
3046          * the user of the performance issue.
3047          */
3048         if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
3049                 bam_error(_("failed to open file: %s: %s\n"), timestamp_path,
3050                     strerror(errno));
3051                 bam_error(_("failed to update the timestamp file, next"
3052                     " archive update may experience reduced performance\n"));
3053         }
3054 }
3055 
3056 
3057 static void
3058 savenew(char *root)
3059 {
3060         char    path[PATH_MAX];
3061         char    path2[PATH_MAX];
3062         size_t  sz;
3063         char    *nstat;
3064         int     fd, wrote, error;
3065 
3066         nstat = NULL;
3067         sz = 0;
3068         error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3069             NV_ENCODE_XDR, 0);
3070         if (error) {
3071                 bam_error(_("failed to pack stat data: %s\n"),
3072                     strerror(error));
3073                 return;
3074         }
3075 
3076         (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3077         fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3078         if (fd == -1) {
3079                 bam_error(_("failed to open file: %s: %s\n"), path,
3080                     strerror(errno));
3081                 free(nstat);
3082                 return;
3083         }
3084         wrote = write(fd, nstat, sz);
3085         if (wrote != sz) {
3086                 bam_error(_("write to file failed: %s: %s\n"), path,
3087                     strerror(errno));
3088                 (void) close(fd);
3089                 free(nstat);
3090                 return;
3091         }
3092         (void) close(fd);
3093         free(nstat);
3094 
3095         (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3096         if (rename(path, path2) != 0) {
3097                 bam_error(_("rename to file failed: %s: %s\n"), path2,
3098                     strerror(errno));
3099         }
3100 }
3101 
3102 #define init_walk_args()        bzero(&walk_arg, sizeof (walk_arg))
3103 
3104 static void
3105 clear_walk_args(void)
3106 {
3107         nvlist_free(walk_arg.old_nvlp);
3108         nvlist_free(walk_arg.new_nvlp);
3109         if (walk_arg.sparcfile)
3110                 (void) fclose(walk_arg.sparcfile);
3111         walk_arg.old_nvlp = NULL;
3112         walk_arg.new_nvlp = NULL;
3113         walk_arg.sparcfile = NULL;
3114 }
3115 
3116 /*
3117  * Returns:
3118  *      0 - no update necessary
3119  *      1 - update required.
3120  *      BAM_ERROR (-1) - An error occurred
3121  *
3122  * Special handling for check (-n):
3123  * ================================
3124  * The check (-n) option produces parseable output.
3125  * To do this, we suppress all stdout messages unrelated
3126  * to out of sync files.
3127  * All stderr messages are still printed though.
3128  *
3129  */
3130 static int
3131 update_required(char *root)
3132 {
3133         struct stat     sb;
3134         char            path[PATH_MAX];
3135         filelist_t      flist;
3136         filelist_t      *flistp = &flist;
3137         int             ret;
3138 
3139         flistp->head = flistp->tail = NULL;
3140 
3141         if (is_sparc())
3142                 set_flag(IS_SPARC_TARGET);
3143 
3144         /*
3145          * Check if cache directories and archives are present
3146          */
3147 
3148         ret = check_flags_and_files(root);
3149         if (ret < 0)
3150                 return (BAM_ERROR);
3151 
3152         /*
3153          * In certain deployment scenarios, filestat may not
3154          * exist. Do not stop the boot process, but trigger an update
3155          * of the archives (which will recreate filestat.ramdisk).
3156          */
3157         if (bam_smf_check) {
3158                 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3159                 if (stat(path, &sb) != 0) {
3160                         (void) creat(NEED_UPDATE_FILE, 0644);
3161                         return (0);
3162                 }
3163         }
3164 
3165         getoldstat(root);
3166 
3167         /*
3168          * Check if the archive contains files that are no longer
3169          * present on the root filesystem.
3170          */
3171         check4stale(root);
3172 
3173         /*
3174          * read list of files
3175          */
3176         if (read_list(root, flistp) != BAM_SUCCESS) {
3177                 clear_walk_args();
3178                 return (BAM_ERROR);
3179         }
3180 
3181         assert(flistp->head && flistp->tail);
3182 
3183         /*
3184          * At this point either the update is required
3185          * or the decision is pending. In either case
3186          * we need to create new stat nvlist
3187          */
3188         create_newstat();
3189         /*
3190          * This walk does 2 things:
3191          *      - gets new stat data for every file
3192          *      - (optional) compare old and new stat data
3193          */
3194         ret = walk_list(root, &flist);
3195 
3196         /* done with the file list */
3197         filelist_free(flistp);
3198 
3199         /* something went wrong */
3200 
3201         if (ret == BAM_ERROR) {
3202                 bam_error(_("Failed to gather cache files, archives "
3203                     "generation aborted\n"));
3204                 return (BAM_ERROR);
3205         }
3206 
3207         if (walk_arg.new_nvlp == NULL) {
3208                 if (walk_arg.sparcfile != NULL)
3209                         (void) fclose(walk_arg.sparcfile);
3210                 bam_error(_("cannot create new stat data\n"));
3211         }
3212 
3213         /* If nothing was updated, discard newstat. */
3214 
3215         if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
3216             !is_dir_flag_on(FILE64, NEED_UPDATE)) {
3217                 clear_walk_args();
3218                 return (0);
3219         }
3220 
3221         if (walk_arg.sparcfile != NULL)
3222                 (void) fclose(walk_arg.sparcfile);
3223 
3224         return (1);
3225 }
3226 
3227 static int
3228 flushfs(char *root)
3229 {
3230         char    cmd[PATH_MAX + 30];
3231 
3232         (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3233             LOCKFS_PATH, root);
3234 
3235         return (exec_cmd(cmd, NULL));
3236 }
3237 
3238 static int
3239 do_archive_copy(char *source, char *dest)
3240 {
3241 
3242         sync();
3243 
3244         /* the equivalent of mv archive-new-$pid boot_archive */
3245         if (rename(source, dest) != 0) {
3246                 (void) unlink(source);
3247                 return (BAM_ERROR);
3248         }
3249 
3250         if (flushfs(bam_root) != 0)
3251                 sync();
3252 
3253         return (BAM_SUCCESS);
3254 }
3255 
3256 static int
3257 check_cmdline(filelist_t flist)
3258 {
3259         line_t  *lp;
3260 
3261         for (lp = flist.head; lp; lp = lp->next) {
3262                 if (strstr(lp->line, "Error:") != NULL ||
3263                     strstr(lp->line, "Inode number overflow") != NULL) {
3264                         (void) fprintf(stderr, "%s\n", lp->line);
3265                         return (BAM_ERROR);
3266                 }
3267         }
3268 
3269         return (BAM_SUCCESS);
3270 }
3271 
3272 static void
3273 dump_errormsg(filelist_t flist)
3274 {
3275         line_t  *lp;
3276 
3277         for (lp = flist.head; lp; lp = lp->next)
3278                 (void) fprintf(stderr, "%s\n", lp->line);
3279 }
3280 
3281 static int
3282 check_archive(char *dest)
3283 {
3284         struct stat     sb;
3285 
3286         if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3287             sb.st_size < 10000) {
3288                 bam_error(_("archive file %s not generated correctly\n"), dest);
3289                 (void) unlink(dest);
3290                 return (BAM_ERROR);
3291         }
3292 
3293         return (BAM_SUCCESS);
3294 }
3295 
3296 static boolean_t
3297 is_be(char *root)
3298 {
3299         zfs_handle_t    *zhp;
3300         libzfs_handle_t *hdl;
3301         be_node_list_t  *be_nodes = NULL;
3302         be_node_list_t  *cur_be;
3303         boolean_t       be_exist = B_FALSE;
3304         char            ds_path[ZFS_MAX_DATASET_NAME_LEN];
3305 
3306         if (!is_zfs(root))
3307                 return (B_FALSE);
3308         /*
3309          * Get dataset for mountpoint
3310          */
3311         if ((hdl = libzfs_init()) == NULL)
3312                 return (B_FALSE);
3313 
3314         if ((zhp = zfs_path_to_zhandle(hdl, root,
3315             ZFS_TYPE_FILESYSTEM)) == NULL) {
3316                 libzfs_fini(hdl);
3317                 return (B_FALSE);
3318         }
3319 
3320         (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3321 
3322         /*
3323          * Check if the current dataset is BE
3324          */
3325         if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
3326                 for (cur_be = be_nodes; cur_be != NULL;
3327                     cur_be = cur_be->be_next_node) {
3328 
3329                         /*
3330                          * Because we guarantee that cur_be->be_root_ds
3331                          * is null-terminated by internal data structure,
3332                          * we can safely use strcmp()
3333                          */
3334                         if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3335                                 be_exist = B_TRUE;
3336                                 break;
3337                         }
3338                 }
3339                 be_free_list(be_nodes);
3340         }
3341         zfs_close(zhp);
3342         libzfs_fini(hdl);
3343 
3344         return (be_exist);
3345 }
3346 
3347 /*
3348  * Returns 1 if mkiso is in the expected PATH, 0 otherwise
3349  */
3350 static int
3351 is_mkisofs()
3352 {
3353         if (access(MKISOFS_PATH, X_OK) == 0)
3354                 return (1);
3355         return (0);
3356 }
3357 
3358 #define MKISO_PARAMS    " -quiet -graft-points -dlrDJN -relaxed-filenames "
3359 
3360 static int
3361 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3362 {
3363         int             ret;
3364         char            cmdline[3 * PATH_MAX + 64];
3365         filelist_t      flist = {0};
3366         const char      *func = "create_sparc_archive()";
3367 
3368         if (access(bootblk, R_OK) == 1) {
3369                 bam_error(_("unable to access bootblk file : %s\n"), bootblk);
3370                 return (BAM_ERROR);
3371         }
3372 
3373         /*
3374          * Prepare mkisofs command line and execute it
3375          */
3376         (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3377             "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3378             tempname, list);
3379 
3380         BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3381 
3382         ret = exec_cmd(cmdline, &flist);
3383         if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3384                 dump_errormsg(flist);
3385                 goto out_err;
3386         }
3387 
3388         filelist_free(&flist);
3389 
3390         /*
3391          * Prepare dd command line to copy the bootblk on the new archive and
3392          * execute it
3393          */
3394         (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3395             " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3396             bootblk, tempname);
3397 
3398         BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3399 
3400         ret = exec_cmd(cmdline, &flist);
3401         if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3402                 goto out_err;
3403 
3404         filelist_free(&flist);
3405 
3406         /* Did we get a valid archive ? */
3407         if (check_archive(tempname) == BAM_ERROR)
3408                 return (BAM_ERROR);
3409 
3410         return (do_archive_copy(tempname, archive));
3411 
3412 out_err:
3413         filelist_free(&flist);
3414         bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline);
3415         (void) unlink(tempname);
3416         return (BAM_ERROR);
3417 }
3418 
3419 static unsigned int
3420 from_733(unsigned char *s)
3421 {
3422         int             i;
3423         unsigned int    ret = 0;
3424 
3425         for (i = 0; i < 4; i++)
3426                 ret |= s[i] << (8 * i);
3427 
3428         return (ret);
3429 }
3430 
3431 static void
3432 to_733(unsigned char *s, unsigned int val)
3433 {
3434         int     i;
3435 
3436         for (i = 0; i < 4; i++)
3437                 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3438 }
3439 
3440 /*
3441  * creates sha1 hash of archive
3442  */
3443 static int
3444 digest_archive(const char *archive)
3445 {
3446         char *archive_hash;
3447         char *hash;
3448         int ret;
3449         FILE *fp;
3450 
3451         (void) asprintf(&archive_hash, "%s.hash", archive);
3452         if (archive_hash == NULL)
3453                 return (BAM_ERROR);
3454 
3455         if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
3456                 free(archive_hash);
3457                 return (ret);
3458         }
3459 
3460         fp = fopen(archive_hash, "w");
3461         if (fp == NULL) {
3462                 free(archive_hash);
3463                 free(hash);
3464                 return (BAM_ERROR);
3465         }
3466 
3467         (void) fprintf(fp, "%s\n", hash);
3468         (void) fclose(fp);
3469         free(hash);
3470         free(archive_hash);
3471         return (BAM_SUCCESS);
3472 }
3473 
3474 /*
3475  * Extends the current boot archive without recreating it from scratch
3476  */
3477 static int
3478 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3479 {
3480         int                     fd = -1, newfd = -1, ret, i;
3481         int                     next_session = 0, new_size = 0;
3482         char                    cmdline[3 * PATH_MAX + 64];
3483         const char              *func = "extend_iso_archive()";
3484         filelist_t              flist = {0};
3485         struct iso_pdesc        saved_desc[MAX_IVDs];
3486 
3487         fd = open(archive, O_RDWR);
3488         if (fd == -1) {
3489                 if (bam_verbose)
3490                         bam_error(_("failed to open file: %s: %s\n"),
3491                             archive, strerror(errno));
3492                 goto out_err;
3493         }
3494 
3495         /*
3496          * A partial read is likely due to a corrupted file
3497          */
3498         ret = pread64(fd, saved_desc, sizeof (saved_desc),
3499             VOLDESC_OFF * CD_BLOCK);
3500         if (ret != sizeof (saved_desc)) {
3501                 if (bam_verbose)
3502                         bam_error(_("read failed for file: %s: %s\n"),
3503                             archive, strerror(errno));
3504                 goto out_err;
3505         }
3506 
3507         if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3508                 if (bam_verbose)
3509                         bam_error(_("iso descriptor signature for %s is "
3510                             "invalid\n"), archive);
3511                 goto out_err;
3512         }
3513 
3514         /*
3515          * Read primary descriptor and locate next_session offset (it should
3516          * point to the end of the archive)
3517          */
3518         next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3519 
3520         (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3521             "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3522             MKISO_PARAMS, tempname, update_dir);
3523 
3524         BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3525 
3526         ret = exec_cmd(cmdline, &flist);
3527         if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3528                 if (bam_verbose) {
3529                         bam_error(_("Command '%s' failed while generating "
3530                             "multisession archive\n"), cmdline);
3531                         dump_errormsg(flist);
3532                 }
3533                 goto out_flist_err;
3534         }
3535         filelist_free(&flist);
3536 
3537         newfd = open(tempname, O_RDONLY);
3538         if (newfd == -1) {
3539                 if (bam_verbose)
3540                         bam_error(_("failed to open file: %s: %s\n"),
3541                             archive, strerror(errno));
3542                 goto out_err;
3543         }
3544 
3545         ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3546             VOLDESC_OFF * CD_BLOCK);
3547         if (ret != sizeof (saved_desc)) {
3548                 if (bam_verbose)
3549                         bam_error(_("read failed for file: %s: %s\n"),
3550                             archive, strerror(errno));
3551                 goto out_err;
3552         }
3553 
3554         if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3555                 if (bam_verbose)
3556                         bam_error(_("iso descriptor signature for %s is "
3557                             "invalid\n"), archive);
3558                 goto out_err;
3559         }
3560 
3561         new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3562         to_733(saved_desc[0].volume_space_size, new_size);
3563 
3564         for (i = 1; i < MAX_IVDs; i++) {
3565                 if (saved_desc[i].type[0] == (unsigned char)255)
3566                         break;
3567                 if (memcmp(saved_desc[i].id, "CD001", 5))
3568                         break;
3569 
3570                 if (bam_verbose)
3571                         bam_print("%s: Updating descriptor entry [%d]\n", func,
3572                             i);
3573 
3574                 to_733(saved_desc[i].volume_space_size, new_size);
3575         }
3576 
3577         ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3578         if (ret != DVD_BLOCK) {
3579                 if (bam_verbose)
3580                         bam_error(_("write to file failed: %s: %s\n"),
3581                             archive, strerror(errno));
3582                 goto out_err;
3583         }
3584         (void) close(newfd);
3585         newfd = -1;
3586 
3587         ret = fsync(fd);
3588         if (ret != 0)
3589                 sync();
3590 
3591         ret = close(fd);
3592         if (ret != 0) {
3593                 if (bam_verbose)
3594                         bam_error(_("failed to close file: %s: %s\n"),
3595                             archive, strerror(errno));
3596                 return (BAM_ERROR);
3597         }
3598         fd = -1;
3599 
3600         (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3601             "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3602             (next_session/16));
3603 
3604         BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3605 
3606         ret = exec_cmd(cmdline, &flist);
3607         if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3608                 if (bam_verbose)
3609                         bam_error(_("Command '%s' failed while generating "
3610                             "multisession archive\n"), cmdline);
3611                 goto out_flist_err;
3612         }
3613         filelist_free(&flist);
3614 
3615         (void) unlink(tempname);
3616 
3617         if (digest_archive(archive) == BAM_ERROR && bam_verbose)
3618                 bam_print("boot archive hashing failed\n");
3619 
3620         if (flushfs(bam_root) != 0)
3621                 sync();
3622 
3623         if (bam_verbose)
3624                 bam_print("boot archive updated successfully\n");
3625 
3626         return (BAM_SUCCESS);
3627 
3628 out_flist_err:
3629         filelist_free(&flist);
3630 out_err:
3631         if (fd != -1)
3632                 (void) close(fd);
3633         if (newfd != -1)
3634                 (void) close(newfd);
3635         return (BAM_ERROR);
3636 }
3637 
3638 static int
3639 create_x86_archive(char *archive, char *tempname, char *update_dir)
3640 {
3641         int             ret;
3642         char            cmdline[3 * PATH_MAX + 64];
3643         filelist_t      flist = {0};
3644         const char      *func = "create_x86_archive()";
3645 
3646         (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3647             "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3648 
3649         BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3650 
3651         ret = exec_cmd(cmdline, &flist);
3652         if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3653                 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3654                     cmdline);
3655                 dump_errormsg(flist);
3656                 filelist_free(&flist);
3657                 (void) unlink(tempname);
3658                 return (BAM_ERROR);
3659         }
3660 
3661         filelist_free(&flist);
3662 
3663         if (check_archive(tempname) == BAM_ERROR)
3664                 return (BAM_ERROR);
3665 
3666         return (do_archive_copy(tempname, archive));
3667 }
3668 
3669 static int
3670 mkisofs_archive(char *root, int what)
3671 {
3672         int             ret;
3673         char            temp[PATH_MAX];
3674         char            bootblk[PATH_MAX];
3675         char            boot_archive[PATH_MAX];
3676 
3677         if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3678                 ret = snprintf(temp, sizeof (temp),
3679                     "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3680                     get_machine(), getpid());
3681         else
3682                 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3683                     root, ARCHIVE_PREFIX, get_machine(), getpid());
3684 
3685         if (ret >= sizeof (temp))
3686                 goto out_path_err;
3687 
3688         if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3689                 ret = snprintf(boot_archive, sizeof (boot_archive),
3690                     "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3691                     ARCHIVE_SUFFIX);
3692         else
3693                 ret = snprintf(boot_archive, sizeof (boot_archive),
3694                     "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3695                     ARCHIVE_SUFFIX);
3696 
3697         if (ret >= sizeof (boot_archive))
3698                 goto out_path_err;
3699 
3700         bam_print("updating %s\n", boot_archive);
3701 
3702         if (is_flag_on(IS_SPARC_TARGET)) {
3703                 ret = snprintf(bootblk, sizeof (bootblk),
3704                     "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3705                 if (ret >= sizeof (bootblk))
3706                         goto out_path_err;
3707 
3708                 ret = create_sparc_archive(boot_archive, temp, bootblk,
3709                     get_cachedir(what));
3710         } else {
3711                 if (!is_dir_flag_on(what, NO_MULTI)) {
3712                         if (bam_verbose)
3713                                 bam_print("Attempting to extend x86 archive: "
3714                                     "%s\n", boot_archive);
3715 
3716                         ret = extend_iso_archive(boot_archive, temp,
3717                             get_updatedir(what));
3718                         if (ret == BAM_SUCCESS) {
3719                                 if (bam_verbose)
3720                                         bam_print("Successfully extended %s\n",
3721                                             boot_archive);
3722 
3723                                 (void) rmdir_r(get_updatedir(what));
3724                                 return (BAM_SUCCESS);
3725                         }
3726                 }
3727                 /*
3728                  * The boot archive will be recreated from scratch. We get here
3729                  * if at least one of these conditions is true:
3730                  * - bootadm was called without the -e switch
3731                  * - the archive (or the archive cache) doesn't exist
3732                  * - archive size is bigger than BA_SIZE_MAX
3733                  * - more than COUNT_MAX files need to be updated
3734                  * - an error occourred either populating the /updates directory
3735                  *   or extend_iso_archive() failed
3736                  */
3737                 if (bam_verbose)
3738                         bam_print("Unable to extend %s... rebuilding archive\n",
3739                             boot_archive);
3740 
3741                 if (get_updatedir(what)[0] != '\0')
3742                         (void) rmdir_r(get_updatedir(what));
3743 
3744 
3745                 ret = create_x86_archive(boot_archive, temp,
3746                     get_cachedir(what));
3747         }
3748 
3749         if (digest_archive(boot_archive) == BAM_ERROR && bam_verbose)
3750                 bam_print("boot archive hashing failed\n");
3751 
3752         if (ret == BAM_SUCCESS && bam_verbose)
3753                 bam_print("Successfully created %s\n", boot_archive);
3754 
3755         return (ret);
3756 
3757 out_path_err:
3758         bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3759             root);
3760         return (BAM_ERROR);
3761 }
3762 
3763 static error_t
3764 create_ramdisk(char *root)
3765 {
3766         char *cmdline, path[PATH_MAX];
3767         size_t len;
3768         struct stat sb;
3769         int ret, what, status = BAM_SUCCESS;
3770 
3771         /* If there is mkisofs, use it to create the required archives */
3772         if (is_mkisofs()) {
3773                 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3774                         if (has_cachedir(what) && is_dir_flag_on(what,
3775                             NEED_UPDATE)) {
3776                                 ret = mkisofs_archive(root, what);
3777                                 if (ret != 0)
3778                                         status = BAM_ERROR;
3779                         }
3780                 }
3781                 return (status);
3782         }
3783 
3784         /*
3785          * Else setup command args for create_ramdisk.ksh for the UFS archives
3786          * Note: we will not create hash here, CREATE_RAMDISK should create it.
3787          */
3788         if (bam_verbose)
3789                 bam_print("mkisofs not found, creating UFS archive\n");
3790 
3791         (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3792         if (stat(path, &sb) != 0) {
3793                 bam_error(_("archive creation file not found: %s: %s\n"),
3794                     path, strerror(errno));
3795                 return (BAM_ERROR);
3796         }
3797 
3798         if (is_safe_exec(path) == BAM_ERROR)
3799                 return (BAM_ERROR);
3800 
3801         len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3802         if (bam_alt_platform)
3803                 len += strlen(bam_platform) + strlen("-p ");
3804         cmdline = s_calloc(1, len);
3805 
3806         if (bam_alt_platform) {
3807                 assert(strlen(root) > 1);
3808                 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3809                     path, bam_platform, root);
3810                 /* chop off / at the end */
3811                 cmdline[strlen(cmdline) - 1] = '\0';
3812         } else if (strlen(root) > 1) {
3813                 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3814                 /* chop off / at the end */
3815                 cmdline[strlen(cmdline) - 1] = '\0';
3816         } else
3817                 (void) snprintf(cmdline, len, "%s", path);
3818 
3819         if (exec_cmd(cmdline, NULL) != 0) {
3820                 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3821                     cmdline);
3822                 free(cmdline);
3823                 return (BAM_ERROR);
3824         }
3825         free(cmdline);
3826         /*
3827          * The existence of the expected archives used to be
3828          * verified here. This check is done in create_ramdisk as
3829          * it needs to be in sync with the altroot operated upon.
3830          */
3831         return (BAM_SUCCESS);
3832 }
3833 
3834 /*
3835  * Checks if target filesystem is on a ramdisk
3836  * 1 - is miniroot
3837  * 0 - is not
3838  * When in doubt assume it is not a ramdisk.
3839  */
3840 static int
3841 is_ramdisk(char *root)
3842 {
3843         struct extmnttab mnt;
3844         FILE *fp;
3845         int found;
3846         char mntpt[PATH_MAX];
3847         char *cp;
3848 
3849         /*
3850          * There are 3 situations where creating archive is
3851          * of dubious value:
3852          *      - create boot_archive on a lofi-mounted boot_archive
3853          *      - create it on a ramdisk which is the root filesystem
3854          *      - create it on a ramdisk mounted somewhere else
3855          * The first is not easy to detect and checking for it is not
3856          * worth it.
3857          * The other two conditions are handled here
3858          */
3859         fp = fopen(MNTTAB, "r");
3860         if (fp == NULL) {
3861                 bam_error(_("failed to open file: %s: %s\n"),
3862                     MNTTAB, strerror(errno));
3863                 return (0);
3864         }
3865 
3866         resetmnttab(fp);
3867 
3868         /*
3869          * Remove any trailing / from the mount point
3870          */
3871         (void) strlcpy(mntpt, root, sizeof (mntpt));
3872         if (strcmp(root, "/") != 0) {
3873                 cp = mntpt + strlen(mntpt) - 1;
3874                 if (*cp == '/')
3875                         *cp = '\0';
3876         }
3877         found = 0;
3878         while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3879                 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3880                         found = 1;
3881                         break;
3882                 }
3883         }
3884 
3885         if (!found) {
3886                 if (bam_verbose)
3887                         bam_error(_("alternate root %s not in mnttab\n"),
3888                             mntpt);
3889                 (void) fclose(fp);
3890                 return (0);
3891         }
3892 
3893         if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3894             strlen(RAMDISK_SPECIAL)) == 0) {
3895                 if (bam_verbose)
3896                         bam_error(_("%s is on a ramdisk device\n"), bam_root);
3897                 (void) fclose(fp);
3898                 return (1);
3899         }
3900 
3901         (void) fclose(fp);
3902 
3903         return (0);
3904 }
3905 
3906 static int
3907 is_boot_archive(char *root)
3908 {
3909         char            path[PATH_MAX];
3910         struct stat     sb;
3911         int             error;
3912         const char      *fcn = "is_boot_archive()";
3913 
3914         /*
3915          * We can't create an archive without the create_ramdisk script
3916          */
3917         (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3918         error = stat(path, &sb);
3919         INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3920         if (error == -1) {
3921                 if (bam_verbose)
3922                         bam_print(_("file not found: %s\n"), path);
3923                 BAM_DPRINTF(("%s: not a boot archive based Solaris "
3924                     "instance: %s\n", fcn, root));
3925                 return (0);
3926         }
3927 
3928         BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
3929             fcn, root));
3930         return (1);
3931 }
3932 
3933 /*
3934  * Need to call this for anything that operates on the GRUB menu
3935  * In the x86 live upgrade case the directory /boot/grub may be present
3936  * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3937  * is to check for the presence of the stage2 binary which is present
3938  * only on GRUB targets (even on x86 boot partitions). Checking for the
3939  * presence of the multiboot binary is not correct as it is not present
3940  * on x86 boot partitions.
3941  */
3942 int
3943 is_grub(const char *root)
3944 {
3945         char path[PATH_MAX];
3946         struct stat sb;
3947         void *defp;
3948         boolean_t grub = B_FALSE;
3949         const char *res = NULL;
3950         const char *fcn = "is_grub()";
3951 
3952         /* grub is disabled by default */
3953         if ((defp = defopen_r(BE_DEFAULTS)) == NULL) {
3954                 return (0);
3955         } else {
3956                 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
3957                 if (res != NULL && res[0] != '\0') {
3958                         if (strcasecmp(res, "true") == 0)
3959                                 grub = B_TRUE;
3960                 }
3961                 defclose_r(defp);
3962         }
3963 
3964         if (grub == B_TRUE) {
3965                 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3966                 if (stat(path, &sb) == -1) {
3967                         BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
3968                             fcn, path));
3969                         return (0);
3970                 } else
3971                         return (1);
3972         }
3973 
3974         return (0);
3975 }
3976 
3977 int
3978 is_zfs(char *root)
3979 {
3980         struct statvfs          vfs;
3981         int                     ret;
3982         const char              *fcn = "is_zfs()";
3983 
3984         ret = statvfs(root, &vfs);
3985         INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3986         if (ret != 0) {
3987                 bam_error(_("statvfs failed for %s: %s\n"), root,
3988                     strerror(errno));
3989                 return (0);
3990         }
3991 
3992         if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3993                 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
3994                 return (1);
3995         } else {
3996                 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
3997                 return (0);
3998         }
3999 }
4000 
4001 int
4002 is_pcfs(char *root)
4003 {
4004         struct statvfs          vfs;
4005         int                     ret;
4006         const char              *fcn = "is_pcfs()";
4007 
4008         ret = statvfs(root, &vfs);
4009         INJECT_ERROR1("STATVFS_PCFS", ret = 1);
4010         if (ret != 0) {
4011                 bam_error(_("statvfs failed for %s: %s\n"), root,
4012                     strerror(errno));
4013                 return (0);
4014         }
4015 
4016         if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
4017                 BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn, root));
4018                 return (1);
4019         } else {
4020                 BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4021                     fcn, root));
4022                 return (0);
4023         }
4024 }
4025 
4026 static int
4027 is_readonly(char *root)
4028 {
4029         int             fd;
4030         int             error;
4031         char            testfile[PATH_MAX];
4032         const char      *fcn = "is_readonly()";
4033 
4034         /*
4035          * Using statvfs() to check for a read-only filesystem is not
4036          * reliable. The only way to reliably test is to attempt to
4037          * create a file
4038          */
4039         (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
4040             root, BOOTADM_RDONLY_TEST, getpid());
4041 
4042         (void) unlink(testfile);
4043 
4044         errno = 0;
4045         fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
4046         error = errno;
4047         INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
4048         if (fd == -1 && error == EROFS) {
4049                 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
4050                 return (1);
4051         } else if (fd == -1) {
4052                 bam_error(_("error during read-only test on %s: %s\n"),
4053                     root, strerror(error));
4054         }
4055 
4056         (void) close(fd);
4057         (void) unlink(testfile);
4058 
4059         BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
4060         return (0);
4061 }
4062 
4063 static error_t
4064 update_archive(char *root, char *opt)
4065 {
4066         error_t ret;
4067 
4068         assert(root);
4069         assert(opt == NULL);
4070 
4071         init_walk_args();
4072         (void) umask(022);
4073 
4074         /*
4075          * Never update non-BE root in update_all
4076          */
4077         if (!is_be(root) && bam_update_all)
4078                 return (BAM_SUCCESS);
4079         /*
4080          * root must belong to a boot archive based OS,
4081          */
4082         if (!is_boot_archive(root)) {
4083                 /*
4084                  * Emit message only if not in context of update_all.
4085                  * If in update_all, emit only if verbose flag is set.
4086                  */
4087                 if (!bam_update_all || bam_verbose)
4088                         bam_print(_("%s: not a boot archive based Solaris "
4089                             "instance\n"), root);
4090                 return (BAM_ERROR);
4091         }
4092 
4093         /*
4094          * If smf check is requested when / is writable (can happen
4095          * on first reboot following an upgrade because service
4096          * dependency is messed up), skip the check.
4097          */
4098         if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
4099                 return (BAM_SUCCESS);
4100 
4101         /*
4102          * Don't generate archive on ramdisk.
4103          */
4104         if (is_ramdisk(root))
4105                 return (BAM_SUCCESS);
4106 
4107         /*
4108          * root must be writable. This check applies to alternate
4109          * root (-R option); bam_root_readonly applies to '/' only.
4110          * The behaviour translates into being the one of a 'check'.
4111          */
4112         if (!bam_smf_check && !bam_check && is_readonly(root)) {
4113                 set_flag(RDONLY_FSCHK);
4114                 bam_check = 1;
4115         }
4116 
4117         /*
4118          * Now check if an update is really needed.
4119          */
4120         ret = update_required(root);
4121 
4122         /*
4123          * The check command (-n) is *not* a dry run.
4124          * It only checks if the archive is in sync.
4125          * A readonly filesystem has to be considered an error only if an update
4126          * is required.
4127          */
4128         if (bam_nowrite()) {
4129                 if (is_flag_on(RDONLY_FSCHK)) {
4130                         bam_check = bam_saved_check;
4131                         if (ret > 0)
4132                                 bam_error(_("%s filesystem is read-only, "
4133                                     "skipping archives update\n"), root);
4134                         if (bam_update_all)
4135                                 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4136                 }
4137 
4138                 bam_exit((ret != 0) ? 1 : 0);
4139         }
4140 
4141         if (ret == 1) {
4142                 /* create the ramdisk */
4143                 ret = create_ramdisk(root);
4144         }
4145 
4146         /*
4147          * if the archive is updated, save the new stat data and update the
4148          * timestamp file
4149          */
4150         if (ret == 0 && walk_arg.new_nvlp != NULL) {
4151                 savenew(root);
4152                 update_timestamp(root);
4153         }
4154 
4155         clear_walk_args();
4156 
4157         return (ret);
4158 }
4159 
4160 static char *
4161 find_root_pool()
4162 {
4163         char *special = get_special("/");
4164         char *p;
4165 
4166         if (special == NULL)
4167                 return (NULL);
4168 
4169         if (*special == '/') {
4170                 free(special);
4171                 return (NULL);
4172         }
4173 
4174         if ((p = strchr(special, '/')) != NULL)
4175                 *p = '\0';
4176 
4177         return (special);
4178 }
4179 
4180 static error_t
4181 synchronize_BE_menu(void)
4182 {
4183         struct stat     sb;
4184         char            cmdline[PATH_MAX];
4185         char            cksum_line[PATH_MAX];
4186         filelist_t      flist = {0};
4187         char            *old_cksum_str;
4188         char            *old_size_str;
4189         char            *old_file;
4190         char            *curr_cksum_str;
4191         char            *curr_size_str;
4192         char            *curr_file;
4193         char            *pool = NULL;
4194         char            *mntpt = NULL;
4195         zfs_mnted_t     mnted;
4196         FILE            *cfp;
4197         int             found;
4198         int             ret;
4199         const char      *fcn = "synchronize_BE_menu()";
4200 
4201         BAM_DPRINTF(("%s: entered. No args\n", fcn));
4202 
4203         /* Check if findroot enabled LU BE */
4204         if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4205                 BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn));
4206                 return (BAM_SUCCESS);
4207         }
4208 
4209         if (stat(LU_MENU_CKSUM, &sb) != 0) {
4210                 BAM_DPRINTF(("%s: checksum file absent: %s\n",
4211                     fcn, LU_MENU_CKSUM));
4212                 goto menu_sync;
4213         }
4214 
4215         cfp = fopen(LU_MENU_CKSUM, "r");
4216         INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4217         if (cfp == NULL) {
4218                 bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4219                     LU_MENU_CKSUM);
4220                 goto menu_sync;
4221         }
4222         BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn, LU_MENU_CKSUM));
4223 
4224         found = 0;
4225         while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4226                 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4227                 if (found) {
4228                         bam_error(_("multiple checksums for GRUB menu in "
4229                             "checksum file: %s\n"), LU_MENU_CKSUM);
4230                         (void) fclose(cfp);
4231                         goto menu_sync;
4232                 }
4233                 found = 1;
4234         }
4235         BAM_DPRINTF(("%s: read checksum file: %s\n", fcn, LU_MENU_CKSUM));
4236 
4237 
4238         old_cksum_str = strtok(cksum_line, " \t");
4239         old_size_str = strtok(NULL, " \t");
4240         old_file = strtok(NULL, " \t");
4241 
4242         INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4243         INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4244         INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4245         if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4246                 bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4247                     LU_MENU_CKSUM);
4248                 goto menu_sync;
4249         }
4250         BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn, LU_MENU_CKSUM));
4251 
4252         /* Get checksum of current menu */
4253         pool = find_root_pool();
4254         if (pool) {
4255                 mntpt = mount_top_dataset(pool, &mnted);
4256                 if (mntpt == NULL) {
4257                         bam_error(_("failed to mount top dataset for %s\n"),
4258                             pool);
4259                         free(pool);
4260                         return (BAM_ERROR);
4261                 }
4262                 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4263                     CKSUM, mntpt, GRUB_MENU);
4264         } else {
4265                 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4266                     CKSUM, GRUB_MENU);
4267         }
4268         ret = exec_cmd(cmdline, &flist);
4269         if (pool) {
4270                 (void) umount_top_dataset(pool, mnted, mntpt);
4271                 free(pool);
4272         }
4273         INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4274         if (ret != 0) {
4275                 bam_error(_("error generating checksum of GRUB menu\n"));
4276                 return (BAM_ERROR);
4277         }
4278         BAM_DPRINTF(("%s: successfully generated checksum\n", fcn));
4279 
4280         INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4281         if ((flist.head == NULL) || (flist.head != flist.tail)) {
4282                 bam_error(_("bad checksum generated for GRUB menu\n"));
4283                 filelist_free(&flist);
4284                 return (BAM_ERROR);
4285         }
4286         BAM_DPRINTF(("%s: generated checksum output valid\n", fcn));
4287 
4288         curr_cksum_str = strtok(flist.head->line, " \t");
4289         curr_size_str = strtok(NULL, " \t");
4290         curr_file = strtok(NULL, " \t");
4291 
4292         INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4293         INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4294         INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4295         if (curr_cksum_str == NULL || curr_size_str == NULL ||
4296             curr_file == NULL) {
4297                 bam_error(_("error parsing checksum generated "
4298                     "for GRUB menu\n"));
4299                 filelist_free(&flist);
4300                 return (BAM_ERROR);
4301         }
4302         BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn));
4303 
4304         if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4305             strcmp(old_size_str, curr_size_str) == 0 &&
4306             strcmp(old_file, curr_file) == 0) {
4307                 filelist_free(&flist);
4308                 BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn));
4309                 return (BAM_SUCCESS);
4310         }
4311 
4312         filelist_free(&flist);
4313 
4314         /* cksum doesn't match - the menu has changed */
4315         BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn));
4316 
4317 menu_sync:
4318         bam_print(_("propagating updated GRUB menu\n"));
4319 
4320         (void) snprintf(cmdline, sizeof (cmdline),
4321             "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4322             LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4323         ret = exec_cmd(cmdline, NULL);
4324         INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4325         if (ret != 0) {
4326                 bam_error(_("error propagating updated GRUB menu\n"));
4327                 return (BAM_ERROR);
4328         }
4329         BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn));
4330 
4331         (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4332             GRUB_MENU, GRUB_BACKUP_MENU);
4333         ret = exec_cmd(cmdline, NULL);
4334         INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4335         if (ret != 0) {
4336                 bam_error(_("failed to create backup for GRUB menu: %s\n"),
4337                     GRUB_BACKUP_MENU);
4338                 return (BAM_ERROR);
4339         }
4340         BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4341             fcn, GRUB_BACKUP_MENU));
4342 
4343         (void) snprintf(cmdline, sizeof (cmdline),
4344             "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4345             LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4346         ret = exec_cmd(cmdline, NULL);
4347         INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4348         if (ret != 0) {
4349                 bam_error(_("error propagating backup GRUB menu: %s\n"),
4350                     GRUB_BACKUP_MENU);
4351                 return (BAM_ERROR);
4352         }
4353         BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4354             fcn, GRUB_BACKUP_MENU));
4355 
4356         (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4357             CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4358         ret = exec_cmd(cmdline, NULL);
4359         INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4360         if (ret != 0) {
4361                 bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4362                     LU_MENU_CKSUM);
4363                 return (BAM_ERROR);
4364         }
4365         BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4366             fcn, LU_MENU_CKSUM));
4367 
4368         (void) snprintf(cmdline, sizeof (cmdline),
4369             "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4370             LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4371         ret = exec_cmd(cmdline, NULL);
4372         INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4373         if (ret != 0) {
4374                 bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4375                     LU_MENU_CKSUM);
4376                 return (BAM_ERROR);
4377         }
4378         BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4379             fcn, LU_MENU_CKSUM));
4380 
4381         return (BAM_SUCCESS);
4382 }
4383 
4384 static error_t
4385 update_all(char *root, char *opt)
4386 {
4387         struct extmnttab mnt;
4388         struct stat sb;
4389         FILE *fp;
4390         char multibt[PATH_MAX];
4391         char creatram[PATH_MAX];
4392         error_t ret = BAM_SUCCESS;
4393 
4394         assert(root);
4395         assert(opt == NULL);
4396 
4397         if (bam_rootlen != 1 || *root != '/') {
4398                 elide_trailing_slash(root, multibt, sizeof (multibt));
4399                 bam_error(_("an alternate root (%s) cannot be used with this "
4400                     "sub-command\n"), multibt);
4401                 return (BAM_ERROR);
4402         }
4403 
4404         /*
4405          * First update archive for current root
4406          */
4407         if (update_archive(root, opt) != BAM_SUCCESS)
4408                 ret = BAM_ERROR;
4409 
4410         if (ret == BAM_ERROR)
4411                 goto out;
4412 
4413         /*
4414          * Now walk the mount table, performing archive update
4415          * for all mounted Newboot root filesystems
4416          */
4417         fp = fopen(MNTTAB, "r");
4418         if (fp == NULL) {
4419                 bam_error(_("failed to open file: %s: %s\n"),
4420                     MNTTAB, strerror(errno));
4421                 ret = BAM_ERROR;
4422                 goto out;
4423         }
4424 
4425         resetmnttab(fp);
4426 
4427         while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4428                 if (mnt.mnt_special == NULL)
4429                         continue;
4430                 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4431                     (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4432                         continue;
4433                 if (strcmp(mnt.mnt_mountp, "/") == 0)
4434                         continue;
4435 
4436                 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4437                     mnt.mnt_mountp, CREATE_RAMDISK);
4438 
4439                 if (stat(creatram, &sb) == -1)
4440                         continue;
4441 
4442                 /*
4443                  * We put a trailing slash to be consistent with root = "/"
4444                  * case, such that we don't have to print // in some cases.
4445                  */
4446                 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4447                     mnt.mnt_mountp);
4448                 bam_rootlen = strlen(rootbuf);
4449 
4450                 /*
4451                  * It's possible that other mounts may be an alternate boot
4452                  * architecture, so check it again.
4453                  */
4454                 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4455                     (update_archive(rootbuf, opt) != BAM_SUCCESS))
4456                         ret = BAM_ERROR;
4457         }
4458 
4459         (void) fclose(fp);
4460 
4461 out:
4462         /*
4463          * We no longer use biosdev for Live Upgrade. Hence
4464          * there is no need to defer (to shutdown time) any fdisk
4465          * updates
4466          */
4467         if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4468                 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4469                     "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
4470         }
4471 
4472         /*
4473          * If user has updated menu in current BE, propagate the
4474          * updates to all BEs.
4475          */
4476         if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4477                 ret = BAM_ERROR;
4478 
4479         return (ret);
4480 }
4481 
4482 static void
4483 append_line(menu_t *mp, line_t *lp)
4484 {
4485         if (mp->start == NULL) {
4486                 mp->start = lp;
4487         } else {
4488                 mp->end->next = lp;
4489                 lp->prev = mp->end;
4490         }
4491         mp->end = lp;
4492 }
4493 
4494 void
4495 unlink_line(menu_t *mp, line_t *lp)
4496 {
4497         /* unlink from list */
4498         if (lp->prev)
4499                 lp->prev->next = lp->next;
4500         else
4501                 mp->start = lp->next;
4502         if (lp->next)
4503                 lp->next->prev = lp->prev;
4504         else
4505                 mp->end = lp->prev;
4506 }
4507 
4508 static entry_t *
4509 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4510 {
4511         entry_t *ent, *prev;
4512         const char *fcn = "boot_entry_new()";
4513 
4514         assert(mp);
4515         assert(start);
4516         assert(end);
4517 
4518         ent = s_calloc(1, sizeof (entry_t));
4519         BAM_DPRINTF(("%s: new boot entry alloced\n", fcn));
4520         ent->start = start;
4521         ent->end = end;
4522 
4523         if (mp->entries == NULL) {
4524                 mp->entries = ent;
4525                 BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn));
4526                 return (ent);
4527         }
4528 
4529         prev = mp->entries;
4530         while (prev->next)
4531                 prev = prev->next;
4532         prev->next = ent;
4533         ent->prev = prev;
4534         BAM_DPRINTF(("%s: new boot entry linked in\n", fcn));
4535         return (ent);
4536 }
4537 
4538 static void
4539 boot_entry_addline(entry_t *ent, line_t *lp)
4540 {
4541         if (ent)
4542                 ent->end = lp;
4543 }
4544 
4545 /*
4546  * Check whether cmd matches the one indexed by which, and whether arg matches
4547  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4548  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
4549  * strstr(), so it can be a partial match.
4550  */
4551 static int
4552 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4553 {
4554         int                     ret;
4555         const char              *fcn = "check_cmd()";
4556 
4557         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, arg, str));
4558 
4559         if (cmd != NULL) {
4560                 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4561                     (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4562                         BAM_DPRINTF(("%s: command %s does not match %s\n",
4563                             fcn, cmd, menu_cmds[which]));
4564                         return (0);
4565                 }
4566                 ret = (strstr(arg, str) != NULL);
4567         } else
4568                 ret = 0;
4569 
4570         if (ret) {
4571                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
4572         } else {
4573                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
4574         }
4575 
4576         return (ret);
4577 }
4578 
4579 static error_t
4580 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4581 {
4582         const char              *fcn  = "kernel_parser()";
4583 
4584         assert(entry);
4585         assert(cmd);
4586         assert(arg);
4587 
4588         if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4589             strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4590                 BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn, cmd));
4591                 return (BAM_ERROR);
4592         }
4593 
4594         if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4595                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4596                     fcn, arg));
4597                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4598         } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4599             sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4600                 BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn, arg));
4601                 entry->flags |= BAM_ENTRY_DBOOT;
4602         } else if (strncmp(arg, DIRECT_BOOT_64,
4603             sizeof (DIRECT_BOOT_64) - 1) == 0) {
4604                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4605                     fcn, arg));
4606                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4607         } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4608             sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4609                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4610                     fcn, arg));
4611                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4612         } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4613             sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4614                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4615                     "flag: %s\n", fcn, arg));
4616                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4617                     | BAM_ENTRY_32BIT;
4618         } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4619             sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4620                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4621                     "flag: %s\n", fcn, arg));
4622                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4623                     | BAM_ENTRY_64BIT;
4624         } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4625                 BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn, arg));
4626                 entry->flags |= BAM_ENTRY_MULTIBOOT;
4627         } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4628             sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4629                 BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4630                     "flag: %s\n", fcn, arg));
4631                 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4632         } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4633                 BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn, arg));
4634                 entry->flags |= BAM_ENTRY_HV;
4635         } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4636                 BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn, arg));
4637                 return (BAM_ERROR);
4638         } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4639             strstr(arg, UNIX_SPACE)) {
4640                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4641         } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4642             strstr(arg, AMD_UNIX_SPACE)) {
4643                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4644         } else {
4645                 BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn, arg));
4646                 bam_error(_("kernel command on line %d not recognized.\n"),
4647                     linenum);
4648                 return (BAM_ERROR);
4649         }
4650 
4651         return (BAM_SUCCESS);
4652 }
4653 
4654 static error_t
4655 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4656 {
4657         const char              *fcn = "module_parser()";
4658 
4659         assert(entry);
4660         assert(cmd);
4661         assert(arg);
4662 
4663         if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4664             strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4665                 BAM_DPRINTF(("%s: not module cmd: %s\n", fcn, cmd));
4666                 return (BAM_ERROR);
4667         }
4668 
4669         if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4670             strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4671             strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4672             strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4673             strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4674             strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4675             strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4676             strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4677             strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4678                 BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn, arg));
4679                 return (BAM_SUCCESS);
4680         } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4681             !(entry->flags & BAM_ENTRY_LU)) {
4682                 /* don't emit warning for hand entries */
4683                 BAM_DPRINTF(("%s: is HAND module: %s\n", fcn, arg));
4684                 return (BAM_ERROR);
4685         } else {
4686                 BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn, arg));
4687                 bam_error(_("module command on line %d not recognized.\n"),
4688                     linenum);
4689                 return (BAM_ERROR);
4690         }
4691 }
4692 
4693 /*
4694  * A line in menu.lst looks like
4695  * [ ]*<cmd>[ \t=]*<arg>*
4696  */
4697 static void
4698 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4699 {
4700         /*
4701          * save state across calls. This is so that
4702          * header gets the right entry# after title has
4703          * been processed
4704          */
4705         static line_t *prev = NULL;
4706         static entry_t *curr_ent = NULL;
4707         static int in_liveupgrade = 0;
4708         static int is_libbe_ent = 0;
4709 
4710         line_t  *lp;
4711         char *cmd, *sep, *arg;
4712         char save, *cp, *line;
4713         menu_flag_t flag = BAM_INVALID;
4714         const char *fcn = "line_parser()";
4715 
4716         cmd = NULL;
4717         if (str == NULL) {
4718                 return;
4719         }
4720 
4721         /*
4722          * First save a copy of the entire line.
4723          * We use this later to set the line field.
4724          */
4725         line = s_strdup(str);
4726 
4727         /* Eat up leading whitespace */
4728         while (*str == ' ' || *str == '\t')
4729                 str++;
4730 
4731         if (*str == '#') {              /* comment */
4732                 cmd = s_strdup("#");
4733                 sep = NULL;
4734                 arg = s_strdup(str + 1);
4735                 flag = BAM_COMMENT;
4736                 if (strstr(arg, BAM_LU_HDR) != NULL) {
4737                         in_liveupgrade = 1;
4738                 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4739                         in_liveupgrade = 0;
4740                 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4741                         is_libbe_ent = 1;
4742                 }
4743         } else if (*str == '\0') {      /* blank line */
4744                 cmd = sep = arg = NULL;
4745                 flag = BAM_EMPTY;
4746         } else {
4747                 /*
4748                  * '=' is not a documented separator in grub syntax.
4749                  * However various development bits use '=' as a
4750                  * separator. In addition, external users also
4751                  * use = as a separator. So we will allow that usage.
4752                  */
4753                 cp = str;
4754                 while (*str != ' ' && *str != '\t' && *str != '=') {
4755                         if (*str == '\0') {
4756                                 cmd = s_strdup(cp);
4757                                 sep = arg = NULL;
4758                                 break;
4759                         }
4760                         str++;
4761                 }
4762 
4763                 if (*str != '\0') {
4764                         save = *str;
4765                         *str = '\0';
4766                         cmd = s_strdup(cp);
4767                         *str = save;
4768 
4769                         str++;
4770                         save = *str;
4771                         *str = '\0';
4772                         sep = s_strdup(str - 1);
4773                         *str = save;
4774 
4775                         while (*str == ' ' || *str == '\t')
4776                                 str++;
4777                         if (*str == '\0')
4778                                 arg = NULL;
4779                         else
4780                                 arg = s_strdup(str);
4781                 }
4782         }
4783 
4784         lp = s_calloc(1, sizeof (line_t));
4785 
4786         lp->cmd = cmd;
4787         lp->sep = sep;
4788         lp->arg = arg;
4789         lp->line = line;
4790         lp->lineNum = ++(*lineNum);
4791         if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4792                 lp->entryNum = ++(*entryNum);
4793                 lp->flags = BAM_TITLE;
4794                 if (prev && prev->flags == BAM_COMMENT &&
4795                     prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4796                         prev->entryNum = lp->entryNum;
4797                         curr_ent = boot_entry_new(mp, prev, lp);
4798                         curr_ent->flags |= BAM_ENTRY_BOOTADM;
4799                         BAM_DPRINTF(("%s: is bootadm(1M) entry: %s\n",
4800                             fcn, arg));
4801                 } else {
4802                         curr_ent = boot_entry_new(mp, lp, lp);
4803                         if (in_liveupgrade) {
4804                                 curr_ent->flags |= BAM_ENTRY_LU;
4805                                 BAM_DPRINTF(("%s: is LU entry: %s\n",
4806                                     fcn, arg));
4807                         }
4808                 }
4809                 curr_ent->entryNum = *entryNum;
4810         } else if (flag != BAM_INVALID) {
4811                 /*
4812                  * For header comments, the entry# is "fixed up"
4813                  * by the subsequent title
4814                  */
4815                 lp->entryNum = *entryNum;
4816                 lp->flags = flag;
4817         } else {
4818                 lp->entryNum = *entryNum;
4819 
4820                 if (*entryNum == ENTRY_INIT) {
4821                         lp->flags = BAM_GLOBAL;
4822                 } else {
4823                         lp->flags = BAM_ENTRY;
4824 
4825                         if (cmd && arg) {
4826                                 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4827                                         BAM_DPRINTF(("%s: setting ROOT: %s\n",
4828                                             fcn, arg));
4829                                         curr_ent->flags |= BAM_ENTRY_ROOT;
4830                                 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4831                                     == 0) {
4832                                         BAM_DPRINTF(("%s: setting "
4833                                             "FINDROOT: %s\n", fcn, arg));
4834                                         curr_ent->flags |= BAM_ENTRY_FINDROOT;
4835                                 } else if (strcmp(cmd,
4836                                     menu_cmds[CHAINLOADER_CMD]) == 0) {
4837                                         BAM_DPRINTF(("%s: setting "
4838                                             "CHAINLOADER: %s\n", fcn, arg));
4839                                         curr_ent->flags |=
4840                                             BAM_ENTRY_CHAINLOADER;
4841                                 } else if (kernel_parser(curr_ent, cmd, arg,
4842                                     lp->lineNum) != BAM_SUCCESS) {
4843                                         (void) module_parser(curr_ent, cmd,
4844                                             arg, lp->lineNum);
4845                                 }
4846                         }
4847                 }
4848         }
4849 
4850         /* record default, old default, and entry line ranges */
4851         if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4852             strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4853                 mp->curdefault = lp;
4854         } else if (lp->flags == BAM_COMMENT &&
4855             strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4856                 mp->olddefault = lp;
4857         } else if (lp->flags == BAM_COMMENT &&
4858             strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4859                 mp->old_rc_default = lp;
4860         } else if (lp->flags == BAM_ENTRY ||
4861             (lp->flags == BAM_COMMENT &&
4862             ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4863                 if (is_libbe_ent) {
4864                         curr_ent->flags |= BAM_ENTRY_LIBBE;
4865                         is_libbe_ent = 0;
4866                 }
4867 
4868                 boot_entry_addline(curr_ent, lp);
4869         }
4870         append_line(mp, lp);
4871 
4872         prev = lp;
4873 }
4874 
4875 void
4876 update_numbering(menu_t *mp)
4877 {
4878         int lineNum;
4879         int entryNum;
4880         int old_default_value;
4881         line_t *lp, *prev, *default_lp, *default_entry;
4882         char buf[PATH_MAX];
4883 
4884         if (mp->start == NULL) {
4885                 return;
4886         }
4887 
4888         lineNum = LINE_INIT;
4889         entryNum = ENTRY_INIT;
4890         old_default_value = ENTRY_INIT;
4891         lp = default_lp = default_entry = NULL;
4892 
4893         prev = NULL;
4894         for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4895                 lp->lineNum = ++lineNum;
4896 
4897                 /*
4898                  * Get the value of the default command
4899                  */
4900                 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4901                     strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4902                     lp->arg) {
4903                         old_default_value = atoi(lp->arg);
4904                         default_lp = lp;
4905                 }
4906 
4907                 /*
4908                  * If not a booting entry, nothing else to fix for this
4909                  * entry
4910                  */
4911                 if (lp->entryNum == ENTRY_INIT)
4912                         continue;
4913 
4914                 /*
4915                  * Record the position of the default entry.
4916                  * The following works because global
4917                  * commands like default and timeout should precede
4918                  * actual boot entries, so old_default_value
4919                  * is already known (or default cmd is missing).
4920                  */
4921                 if (default_entry == NULL &&
4922                     old_default_value != ENTRY_INIT &&
4923                     lp->entryNum == old_default_value) {
4924                         default_entry = lp;
4925                 }
4926 
4927                 /*
4928                  * Now fixup the entry number
4929                  */
4930                 if (lp->cmd != NULL &&
4931                     strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4932                         lp->entryNum = ++entryNum;
4933                         /* fixup the bootadm header */
4934                         if (prev && prev->flags == BAM_COMMENT &&
4935                             prev->arg &&
4936                             strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4937                                 prev->entryNum = lp->entryNum;
4938                         }
4939                 } else {
4940                         lp->entryNum = entryNum;
4941                 }
4942         }
4943 
4944         /*
4945          * No default command in menu, simply return
4946          */
4947         if (default_lp == NULL) {
4948                 return;
4949         }
4950 
4951         free(default_lp->arg);
4952         free(default_lp->line);
4953 
4954         if (default_entry == NULL) {
4955                 default_lp->arg = s_strdup("0");
4956         } else {
4957                 (void) snprintf(buf, sizeof (buf), "%d",
4958                     default_entry->entryNum);
4959                 default_lp->arg = s_strdup(buf);
4960         }
4961 
4962         /*
4963          * The following is required since only the line field gets
4964          * written back to menu.lst
4965          */
4966         (void) snprintf(buf, sizeof (buf), "%s%s%s",
4967             menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4968         default_lp->line = s_strdup(buf);
4969 }
4970 
4971 
4972 static menu_t *
4973 menu_read(char *menu_path)
4974 {
4975         FILE *fp;
4976         char buf[BAM_MAXLINE], *cp;
4977         menu_t *mp;
4978         int line, entry, len, n;
4979 
4980         mp = s_calloc(1, sizeof (menu_t));
4981 
4982         fp = fopen(menu_path, "r");
4983         if (fp == NULL) { /* Let the caller handle this error */
4984                 free(mp);
4985                 return (NULL);
4986         }
4987 
4988         /* Note: GRUB boot entry number starts with 0 */
4989         line = LINE_INIT;
4990         entry = ENTRY_INIT;
4991         cp = buf;
4992         len = sizeof (buf);
4993         while (s_fgets(cp, len, fp) != NULL) {
4994                 n = strlen(cp);
4995                 if (cp[n - 1] == '\\') {
4996                         len -= n - 1;
4997                         assert(len >= 2);
4998                         cp += n - 1;
4999                         continue;
5000                 }
5001                 line_parser(mp, buf, &line, &entry);
5002                 cp = buf;
5003                 len = sizeof (buf);
5004         }
5005 
5006         if (fclose(fp) == EOF) {
5007                 bam_error(_("failed to close file: %s: %s\n"), menu_path,
5008                     strerror(errno));
5009         }
5010 
5011         return (mp);
5012 }
5013 
5014 static error_t
5015 selector(menu_t *mp, char *opt, int *entry, char **title)
5016 {
5017         char *eq;
5018         char *opt_dup;
5019         int entryNum;
5020 
5021         assert(mp);
5022         assert(mp->start);
5023         assert(opt);
5024 
5025         opt_dup = s_strdup(opt);
5026 
5027         if (entry)
5028                 *entry = ENTRY_INIT;
5029         if (title)
5030                 *title = NULL;
5031 
5032         eq = strchr(opt_dup, '=');
5033         if (eq == NULL) {
5034                 bam_error(_("invalid option: %s\n"), opt);
5035                 free(opt_dup);
5036                 return (BAM_ERROR);
5037         }
5038 
5039         *eq = '\0';
5040         if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
5041                 assert(mp->end);
5042                 entryNum = s_strtol(eq + 1);
5043                 if (entryNum < 0 || entryNum > mp->end->entryNum) {
5044                         bam_error(_("invalid boot entry number: %s\n"), eq + 1);
5045                         free(opt_dup);
5046                         return (BAM_ERROR);
5047                 }
5048                 *entry = entryNum;
5049         } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
5050                 *title = opt + (eq - opt_dup) + 1;
5051         } else {
5052                 bam_error(_("invalid option: %s\n"), opt);
5053                 free(opt_dup);
5054                 return (BAM_ERROR);
5055         }
5056 
5057         free(opt_dup);
5058         return (BAM_SUCCESS);
5059 }
5060 
5061 /*
5062  * If invoked with no titles/entries (opt == NULL)
5063  * only title lines in file are printed.
5064  *
5065  * If invoked with a title or entry #, all
5066  * lines in *every* matching entry are listed
5067  */
5068 static error_t
5069 list_entry(menu_t *mp, char *menu_path, char *opt)
5070 {
5071         line_t *lp;
5072         int entry = ENTRY_INIT;
5073         int found;
5074         char *title = NULL;
5075 
5076         assert(mp);
5077         assert(menu_path);
5078 
5079         /* opt is optional */
5080         BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path,
5081             opt ? opt : "<NULL>"));
5082 
5083         if (mp->start == NULL) {
5084                 bam_error(_("menu file not found: %s\n"), menu_path);
5085                 return (BAM_ERROR);
5086         }
5087 
5088         if (opt != NULL) {
5089                 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
5090                         return (BAM_ERROR);
5091                 }
5092                 assert((entry != ENTRY_INIT) ^ (title != NULL));
5093         } else {
5094                 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
5095                 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
5096         }
5097 
5098         found = 0;
5099         for (lp = mp->start; lp; lp = lp->next) {
5100                 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
5101                         continue;
5102                 if (opt == NULL && lp->flags == BAM_TITLE) {
5103                         bam_print(_("%d %s\n"), lp->entryNum,
5104                             lp->arg);
5105                         found = 1;
5106                         continue;
5107                 }
5108                 if (entry != ENTRY_INIT && lp->entryNum == entry) {
5109                         bam_print(_("%s\n"), lp->line);
5110                         found = 1;
5111                         continue;
5112                 }
5113 
5114                 /*
5115                  * We set the entry value here so that all lines
5116                  * in entry get printed. If we subsequently match
5117                  * title in other entries, all lines in those
5118                  * entries get printed as well.
5119                  */
5120                 if (title && lp->flags == BAM_TITLE && lp->arg &&
5121                     strncmp(title, lp->arg, strlen(title)) == 0) {
5122                         bam_print(_("%s\n"), lp->line);
5123                         entry = lp->entryNum;
5124                         found = 1;
5125                         continue;
5126                 }
5127         }
5128 
5129         if (!found) {
5130                 bam_error(_("no matching entry found\n"));
5131                 return (BAM_ERROR);
5132         }
5133 
5134         return (BAM_SUCCESS);
5135 }
5136 
5137 int
5138 add_boot_entry(menu_t *mp,
5139     char *title,
5140     char *findroot,
5141     char *kernel,
5142     char *mod_kernel,
5143     char *module,
5144     char *bootfs)
5145 {
5146         int             lineNum;
5147         int             entryNum;
5148         char            linebuf[BAM_MAXLINE];
5149         menu_cmd_t      k_cmd;
5150         menu_cmd_t      m_cmd;
5151         const char      *fcn = "add_boot_entry()";
5152 
5153         assert(mp);
5154 
5155         INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
5156         if (findroot == NULL) {
5157                 bam_error(_("can't find argument for findroot command\n"));
5158                 return (BAM_ERROR);
5159         }
5160 
5161         if (title == NULL) {
5162                 title = "Solaris";      /* default to Solaris */
5163         }
5164         if (kernel == NULL) {
5165                 bam_error(_("missing suboption: %s\n"), menu_cmds[KERNEL_CMD]);
5166                 return (BAM_ERROR);
5167         }
5168         if (module == NULL) {
5169                 if (bam_direct != BAM_DIRECT_DBOOT) {
5170                         bam_error(_("missing suboption: %s\n"),
5171                             menu_cmds[MODULE_CMD]);
5172                         return (BAM_ERROR);
5173                 }
5174 
5175                 /* Figure the commands out from the kernel line */
5176                 if (strstr(kernel, "$ISADIR") != NULL) {
5177                         module = DIRECT_BOOT_ARCHIVE;
5178                 } else if (strstr(kernel, "amd64") != NULL) {
5179                         module = DIRECT_BOOT_ARCHIVE_64;
5180                 } else {
5181                         module = DIRECT_BOOT_ARCHIVE_32;
5182                 }
5183         }
5184 
5185         k_cmd = KERNEL_DOLLAR_CMD;
5186         m_cmd = MODULE_DOLLAR_CMD;
5187 
5188         if (mp->start) {
5189                 lineNum = mp->end->lineNum;
5190                 entryNum = mp->end->entryNum;
5191         } else {
5192                 lineNum = LINE_INIT;
5193                 entryNum = ENTRY_INIT;
5194         }
5195 
5196         /*
5197          * No separator for comment (HDR/FTR) commands
5198          * The syntax for comments is #<comment>
5199          */
5200         (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5201             menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5202         line_parser(mp, linebuf, &lineNum, &entryNum);
5203 
5204         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5205             menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5206         line_parser(mp, linebuf, &lineNum, &entryNum);
5207 
5208         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5209             menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5210         line_parser(mp, linebuf, &lineNum, &entryNum);
5211         BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5212             fcn, lineNum, entryNum));
5213 
5214         if (bootfs != NULL) {
5215                 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5216                     menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5217                 line_parser(mp, linebuf, &lineNum, &entryNum);
5218         }
5219 
5220         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5221             menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5222         line_parser(mp, linebuf, &lineNum, &entryNum);
5223 
5224         if (mod_kernel != NULL) {
5225                 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5226                     menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5227                 line_parser(mp, linebuf, &lineNum, &entryNum);
5228         }
5229 
5230         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5231             menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5232         line_parser(mp, linebuf, &lineNum, &entryNum);
5233 
5234         (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5235             menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5236         line_parser(mp, linebuf, &lineNum, &entryNum);
5237 
5238         return (entryNum);
5239 }
5240 
5241 error_t
5242 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5243 {
5244         line_t          *lp;
5245         line_t          *freed;
5246         entry_t         *ent;
5247         entry_t         *tmp;
5248         int             deleted = 0;
5249         const char      *fcn = "delete_boot_entry()";
5250 
5251         assert(entryNum != ENTRY_INIT);
5252 
5253         tmp = NULL;
5254 
5255         ent = mp->entries;
5256         while (ent) {
5257                 lp = ent->start;
5258 
5259                 /*
5260                  * Check entry number and make sure it's a modifiable entry.
5261                  *
5262                  * Guidelines:
5263                  *      + We can modify a bootadm-created entry
5264                  *      + We can modify a libbe-created entry
5265                  */
5266                 if ((lp->flags != BAM_COMMENT &&
5267                     (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5268                     strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5269                     (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5270                         ent = ent->next;
5271                         continue;
5272                 }
5273 
5274                 /* free the entry content */
5275                 do {
5276                         freed = lp;
5277                         lp = lp->next;       /* prev stays the same */
5278                         BAM_DPRINTF(("%s: freeing line: %d\n",
5279                             fcn, freed->lineNum));
5280                         unlink_line(mp, freed);
5281                         line_free(freed);
5282                 } while (freed != ent->end);
5283 
5284                 /* free the entry_t structure */
5285                 assert(tmp == NULL);
5286                 tmp = ent;
5287                 ent = ent->next;
5288                 if (tmp->prev)
5289                         tmp->prev->next = ent;
5290                 else
5291                         mp->entries = ent;
5292                 if (ent)
5293                         ent->prev = tmp->prev;
5294                 BAM_DPRINTF(("%s: freeing entry: %d\n", fcn, tmp->entryNum));
5295                 free(tmp);
5296                 tmp = NULL;
5297                 deleted = 1;
5298         }
5299 
5300         assert(tmp == NULL);
5301 
5302         if (!deleted && entryNum != ALL_ENTRIES) {
5303                 if (quiet == DBE_PRINTERR)
5304                         bam_error(_("no matching bootadm entry found\n"));
5305                 return (BAM_ERROR);
5306         }
5307 
5308         /*
5309          * Now that we have deleted an entry, update
5310          * the entry numbering and the default cmd.
5311          */
5312         update_numbering(mp);
5313 
5314         return (BAM_SUCCESS);
5315 }
5316 
5317 static error_t
5318 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5319 {
5320         assert(mp);
5321         assert(dummy == NULL);
5322         assert(opt == NULL);
5323 
5324         BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5325 
5326         if (mp->start == NULL) {
5327                 bam_print(_("the GRUB menu is empty\n"));
5328                 return (BAM_SUCCESS);
5329         }
5330 
5331         if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5332                 return (BAM_ERROR);
5333         }
5334 
5335         return (BAM_WRITE);
5336 }
5337 
5338 static FILE *
5339 create_diskmap(char *osroot)
5340 {
5341         FILE *fp;
5342         char cmd[PATH_MAX + 16];
5343         char path[PATH_MAX];
5344         const char *fcn = "create_diskmap()";
5345 
5346         /* make sure we have a map file */
5347         fp = fopen(GRUBDISK_MAP, "r");
5348         if (fp == NULL) {
5349                 int     ret;
5350 
5351                 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5352                     CREATE_DISKMAP);
5353                 if (ret >= sizeof (path)) {
5354                         bam_error(_("unable to create path on mountpoint %s, "
5355                             "path too long\n"), osroot);
5356                         return (NULL);
5357                 }
5358                 if (is_safe_exec(path) == BAM_ERROR)
5359                         return (NULL);
5360 
5361                 (void) snprintf(cmd, sizeof (cmd),
5362                     "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5363                 if (exec_cmd(cmd, NULL) != 0)
5364                         return (NULL);
5365                 fp = fopen(GRUBDISK_MAP, "r");
5366                 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5367                 if (fp) {
5368                         BAM_DPRINTF(("%s: created diskmap file: %s\n",
5369                             fcn, GRUBDISK_MAP));
5370                 } else {
5371                         BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5372                             fcn, GRUBDISK_MAP));
5373                 }
5374         }
5375         return (fp);
5376 }
5377 
5378 #define SECTOR_SIZE     512
5379 
5380 static int
5381 get_partition(char *device)
5382 {
5383         int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5384         struct mboot *mboot;
5385         char boot_sect[SECTOR_SIZE];
5386         char *wholedisk, *slice;
5387 #ifdef i386
5388         ext_part_t *epp;
5389         uint32_t secnum, numsec;
5390         int rval, pno, ext_partno = PARTNO_NOTFOUND;
5391 #endif
5392 
5393         /* form whole disk (p0) */
5394         slice = device + strlen(device) - 2;
5395         is_pcfs = (*slice != 's');
5396         if (!is_pcfs)
5397                 *slice = '\0';
5398         wholedisk = s_calloc(1, strlen(device) + 3);
5399         (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5400         if (!is_pcfs)
5401                 *slice = 's';
5402 
5403         /* read boot sector */
5404         fd = open(wholedisk, O_RDONLY);
5405         if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5406                 return (partno);
5407         }
5408         (void) close(fd);
5409 
5410 #ifdef i386
5411         /* Read/Initialize extended partition information */
5412         if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5413             != FDISK_SUCCESS) {
5414                 switch (rval) {
5415                         /*
5416                          * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5417                          * be considered as soft errors and hence
5418                          * we do not return
5419                          */
5420                         case FDISK_EBADLOGDRIVE:
5421                                 break;
5422                         case FDISK_ENOLOGDRIVE:
5423                                 break;
5424                         case FDISK_EBADMAGIC:
5425                                 /*FALLTHROUGH*/
5426                         default:
5427                                 free(wholedisk);
5428                                 libfdisk_fini(&epp);
5429                                 return (partno);
5430                 }
5431         }
5432 #endif
5433         free(wholedisk);
5434 
5435         /* parse fdisk table */
5436         mboot = (struct mboot *)((void *)boot_sect);
5437         for (i = 0; i < FD_NUMPART; i++) {
5438                 struct ipart *part =
5439                     (struct ipart *)(uintptr_t)mboot->parts + i;
5440                 if (is_pcfs) {  /* looking for solaris boot part */
5441                         if (part->systid == 0xbe) {
5442                                 partno = i;
5443                                 break;
5444                         }
5445                 } else {        /* look for solaris partition, old and new */
5446                         if (part->systid == EFI_PMBR) {
5447                                 partno = PARTNO_EFI;
5448                                 break;
5449                         }
5450 
5451 #ifdef i386
5452                         if ((part->systid == SUNIXOS &&
5453                             (fdisk_is_linux_swap(epp, part->relsect,
5454                             NULL) != 0)) || part->systid == SUNIXOS2) {
5455 #else
5456                         if (part->systid == SUNIXOS ||
5457                             part->systid == SUNIXOS2) {
5458 #endif
5459                                 partno = i;
5460                                 break;
5461                         }
5462 
5463 #ifdef i386
5464                         if (fdisk_is_dos_extended(part->systid))
5465                                 ext_partno = i;
5466 #endif
5467                 }
5468         }
5469 #ifdef i386
5470         /* If no primary solaris partition, check extended partition */
5471         if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5472                 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5473                 if (rval == FDISK_SUCCESS) {
5474                         partno = pno - 1;
5475                 }
5476         }
5477         libfdisk_fini(&epp);
5478 #endif
5479         return (partno);
5480 }
5481 
5482 char *
5483 get_grubroot(char *osroot, char *osdev, char *menu_root)
5484 {
5485         char            *grubroot;      /* (hd#,#,#) */
5486         char            *slice;
5487         char            *grubhd = NULL;
5488         int             fdiskpart;
5489         int             found = 0;
5490         char            *devname;
5491         char            *ctdname = strstr(osdev, "dsk/");
5492         char            linebuf[PATH_MAX];
5493         FILE            *fp;
5494 
5495         INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5496         if (ctdname == NULL) {
5497                 bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev);
5498                 return (NULL);
5499         }
5500 
5501         if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5502                 /* menu bears no resemblance to our reality */
5503                 bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5504                     "bootdisk: %s\n"), osdev);
5505                 return (NULL);
5506         }
5507 
5508         ctdname += strlen("dsk/");
5509         slice = strrchr(ctdname, 's');
5510         if (slice)
5511                 *slice = '\0';
5512 
5513         fp = create_diskmap(osroot);
5514         if (fp == NULL) {
5515                 bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5516                     osroot);
5517                 return (NULL);
5518         }
5519 
5520         rewind(fp);
5521         while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5522                 grubhd = strtok(linebuf, " \t\n");
5523                 if (grubhd)
5524                         devname = strtok(NULL, " \t\n");
5525                 else
5526                         devname = NULL;
5527                 if (devname && strcmp(devname, ctdname) == 0) {
5528                         found = 1;
5529                         break;
5530                 }
5531         }
5532 
5533         if (slice)
5534                 *slice = 's';
5535 
5536         (void) fclose(fp);
5537         fp = NULL;
5538 
5539         INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5540         if (found == 0) {
5541                 bam_error(_("not using biosdev command for disk: %s.\n"),
5542                     osdev);
5543                 return (NULL);
5544         }
5545 
5546         fdiskpart = get_partition(osdev);
5547         INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5548         if (fdiskpart == PARTNO_NOTFOUND) {
5549                 bam_error(_("failed to determine fdisk partition: %s\n"),
5550                     osdev);
5551                 return (NULL);
5552         }
5553 
5554         grubroot = s_calloc(1, 10);
5555         if (fdiskpart == PARTNO_EFI) {
5556                 fdiskpart = atoi(&slice[1]);
5557                 slice = NULL;
5558         }
5559 
5560         if (slice) {
5561                 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5562                     grubhd, fdiskpart, slice[1] + 'a' - '0');
5563         } else
5564                 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5565                     grubhd, fdiskpart);
5566 
5567         assert(fp == NULL);
5568         assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5569         return (grubroot);
5570 }
5571 
5572 static char *
5573 find_primary_common(char *mntpt, char *fstype)
5574 {
5575         char            signdir[PATH_MAX];
5576         char            tmpsign[MAXNAMELEN + 1];
5577         char            *lu;
5578         char            *ufs;
5579         char            *zfs;
5580         DIR             *dirp = NULL;
5581         struct dirent   *entp;
5582         struct stat     sb;
5583         const char      *fcn = "find_primary_common()";
5584 
5585         (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5586             mntpt, GRUBSIGN_DIR);
5587 
5588         if (stat(signdir, &sb) == -1) {
5589                 BAM_DPRINTF(("%s: no sign dir: %s\n", fcn, signdir));
5590                 return (NULL);
5591         }
5592 
5593         dirp = opendir(signdir);
5594         INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5595         if (dirp == NULL) {
5596                 bam_error(_("opendir of %s failed: %s\n"), signdir,
5597                     strerror(errno));
5598                 return (NULL);
5599         }
5600 
5601         ufs = zfs = lu = NULL;
5602 
5603         while ((entp = readdir(dirp)) != NULL) {
5604                 if (strcmp(entp->d_name, ".") == 0 ||
5605                     strcmp(entp->d_name, "..") == 0)
5606                         continue;
5607 
5608                 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5609 
5610                 if (lu == NULL &&
5611                     strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5612                     strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5613                         lu = s_strdup(tmpsign);
5614                 }
5615 
5616                 if (ufs == NULL &&
5617                     strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5618                     strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5619                         ufs = s_strdup(tmpsign);
5620                 }
5621 
5622                 if (zfs == NULL &&
5623                     strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5624                     strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5625                         zfs = s_strdup(tmpsign);
5626                 }
5627         }
5628 
5629         BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn,
5630             zfs ? zfs : "NULL",
5631             ufs ? ufs : "NULL",
5632             lu ? lu : "NULL"));
5633 
5634         if (dirp) {
5635                 (void) closedir(dirp);
5636                 dirp = NULL;
5637         }
5638 
5639         if (strcmp(fstype, "ufs") == 0 && zfs) {
5640                 bam_error(_("found mismatched boot signature %s for "
5641                     "filesystem type: %s.\n"), zfs, "ufs");
5642                 free(zfs);
5643                 zfs = NULL;
5644         } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5645                 bam_error(_("found mismatched boot signature %s for "
5646                     "filesystem type: %s.\n"), ufs, "zfs");
5647                 free(ufs);
5648                 ufs = NULL;
5649         }
5650 
5651         assert(dirp == NULL);
5652 
5653         /* For now, we let Live Upgrade take care of its signature itself */
5654         if (lu) {
5655                 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5656                 free(lu);
5657                 lu = NULL;
5658         }
5659 
5660         return (zfs ? zfs : ufs);
5661 }
5662 
5663 static char *
5664 find_backup_common(char *mntpt, char *fstype)
5665 {
5666         FILE            *bfp = NULL;
5667         char            tmpsign[MAXNAMELEN + 1];
5668         char            backup[PATH_MAX];
5669         char            *ufs;
5670         char            *zfs;
5671         char            *lu;
5672         int             error;
5673         const char      *fcn = "find_backup_common()";
5674 
5675         /*
5676          * We didn't find it in the primary directory.
5677          * Look at the backup
5678          */
5679         (void) snprintf(backup, sizeof (backup), "%s%s",
5680             mntpt, GRUBSIGN_BACKUP);
5681 
5682         bfp = fopen(backup, "r");
5683         if (bfp == NULL) {
5684                 error = errno;
5685                 if (bam_verbose) {
5686                         bam_error(_("failed to open file: %s: %s\n"),
5687                             backup, strerror(error));
5688                 }
5689                 BAM_DPRINTF(("%s: failed to open %s: %s\n",
5690                     fcn, backup, strerror(error)));
5691                 return (NULL);
5692         }
5693 
5694         ufs = zfs = lu = NULL;
5695 
5696         while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5697 
5698                 if (lu == NULL &&
5699                     strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5700                     strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5701                         lu = s_strdup(tmpsign);
5702                 }
5703 
5704                 if (ufs == NULL &&
5705                     strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5706                     strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5707                         ufs = s_strdup(tmpsign);
5708                 }
5709 
5710                 if (zfs == NULL &&
5711                     strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5712                     strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5713                         zfs = s_strdup(tmpsign);
5714                 }
5715         }
5716 
5717         BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn,
5718             zfs ? zfs : "NULL",
5719             ufs ? ufs : "NULL",
5720             lu ? lu : "NULL"));
5721 
5722         if (bfp) {
5723                 (void) fclose(bfp);
5724                 bfp = NULL;
5725         }
5726 
5727         if (strcmp(fstype, "ufs") == 0 && zfs) {
5728                 bam_error(_("found mismatched boot signature %s for "
5729                     "filesystem type: %s.\n"), zfs, "ufs");
5730                 free(zfs);
5731                 zfs = NULL;
5732         } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5733                 bam_error(_("found mismatched boot signature %s for "
5734                     "filesystem type: %s.\n"), ufs, "zfs");
5735                 free(ufs);
5736                 ufs = NULL;
5737         }
5738 
5739         assert(bfp == NULL);
5740 
5741         /* For now, we let Live Upgrade take care of its signature itself */
5742         if (lu) {
5743                 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5744                 free(lu);
5745                 lu = NULL;
5746         }
5747 
5748         return (zfs ? zfs : ufs);
5749 }
5750 
5751 static char *
5752 find_ufs_existing(char *osroot)
5753 {
5754         char            *sign;
5755         const char      *fcn = "find_ufs_existing()";
5756 
5757         sign = find_primary_common(osroot, "ufs");
5758         if (sign == NULL) {
5759                 sign = find_backup_common(osroot, "ufs");
5760                 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
5761                     sign ? sign : "NULL"));
5762         } else {
5763                 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
5764         }
5765 
5766         return (sign);
5767 }
5768 
5769 char *
5770 get_mountpoint(char *special, char *fstype)
5771 {
5772         FILE            *mntfp;
5773         struct mnttab   mp = {0};
5774         struct mnttab   mpref = {0};
5775         int             error;
5776         int             ret;
5777         const char      *fcn = "get_mountpoint()";
5778 
5779         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
5780 
5781         mntfp = fopen(MNTTAB, "r");
5782         error = errno;
5783         INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5784         if (mntfp == NULL) {
5785                 bam_error(_("failed to open file: %s: %s\n"),
5786                     MNTTAB, strerror(error));
5787                 return (NULL);
5788         }
5789 
5790         mpref.mnt_special = special;
5791         mpref.mnt_fstype = fstype;
5792 
5793         ret = getmntany(mntfp, &mp, &mpref);
5794         INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5795         if (ret != 0) {
5796                 (void) fclose(mntfp);
5797                 BAM_DPRINTF(("%s: no mount-point for special=%s and "
5798                     "fstype=%s\n", fcn, special, fstype));
5799                 return (NULL);
5800         }
5801         (void) fclose(mntfp);
5802 
5803         assert(mp.mnt_mountp);
5804 
5805         BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5806             fcn, special, mp.mnt_mountp));
5807 
5808         return (s_strdup(mp.mnt_mountp));
5809 }
5810 
5811 /*
5812  * Mounts a "legacy" top dataset (if needed)
5813  * Returns:     The mountpoint of the legacy top dataset or NULL on error
5814  *              mnted returns one of the above values defined for zfs_mnted_t
5815  */
5816 static char *
5817 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5818 {
5819         char            cmd[PATH_MAX];
5820         char            tmpmnt[PATH_MAX];
5821         filelist_t      flist = {0};
5822         char            *is_mounted;
5823         struct stat     sb;
5824         int             ret;
5825         const char      *fcn = "mount_legacy_dataset()";
5826 
5827         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5828 
5829         *mnted = ZFS_MNT_ERROR;
5830 
5831         (void) snprintf(cmd, sizeof (cmd),
5832             "/sbin/zfs get -Ho value mounted %s",
5833             pool);
5834 
5835         ret = exec_cmd(cmd, &flist);
5836         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5837         if (ret != 0) {
5838                 bam_error(_("failed to determine mount status of ZFS "
5839                     "pool %s\n"), pool);
5840                 return (NULL);
5841         }
5842 
5843         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5844         if ((flist.head == NULL) || (flist.head != flist.tail)) {
5845                 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
5846                 filelist_free(&flist);
5847                 return (NULL);
5848         }
5849 
5850         is_mounted = strtok(flist.head->line, " \t\n");
5851         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5852         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5853         if (strcmp(is_mounted, "no") != 0) {
5854                 filelist_free(&flist);
5855                 *mnted = LEGACY_ALREADY;
5856                 /* get_mountpoint returns a strdup'ed string */
5857                 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
5858                     fcn, pool));
5859                 return (get_mountpoint(pool, "zfs"));
5860         }
5861 
5862         filelist_free(&flist);
5863 
5864         /*
5865          * legacy top dataset is not mounted. Mount it now
5866          * First create a mountpoint.
5867          */
5868         (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5869             ZFS_LEGACY_MNTPT, getpid());
5870 
5871         ret = stat(tmpmnt, &sb);
5872         if (ret == -1) {
5873                 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
5874                     fcn, pool, tmpmnt));
5875                 ret = mkdirp(tmpmnt, DIR_PERMS);
5876                 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5877                 if (ret == -1) {
5878                         bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
5879                             strerror(errno));
5880                         return (NULL);
5881                 }
5882         } else {
5883                 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
5884                     "present\n", fcn, pool, tmpmnt));
5885         }
5886 
5887         (void) snprintf(cmd, sizeof (cmd),
5888             "/sbin/mount -F zfs %s %s",
5889             pool, tmpmnt);
5890 
5891         ret = exec_cmd(cmd, NULL);
5892         INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5893         if (ret != 0) {
5894                 bam_error(_("mount of ZFS pool %s failed\n"), pool);
5895                 (void) rmdir(tmpmnt);
5896                 return (NULL);
5897         }
5898 
5899         *mnted = LEGACY_MOUNTED;
5900         BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
5901             fcn, pool, tmpmnt));
5902         return (s_strdup(tmpmnt));
5903 }
5904 
5905 /*
5906  * Mounts the top dataset (if needed)
5907  * Returns:     The mountpoint of the top dataset or NULL on error
5908  *              mnted returns one of the above values defined for zfs_mnted_t
5909  */
5910 char *
5911 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5912 {
5913         char            cmd[PATH_MAX];
5914         filelist_t      flist = {0};
5915         char            *is_mounted;
5916         char            *mntpt;
5917         char            *zmntpt;
5918         int             ret;
5919         const char      *fcn = "mount_top_dataset()";
5920 
5921         *mnted = ZFS_MNT_ERROR;
5922 
5923         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5924 
5925         /*
5926          * First check if the top dataset is a "legacy" dataset
5927          */
5928         (void) snprintf(cmd, sizeof (cmd),
5929             "/sbin/zfs get -Ho value mountpoint %s",
5930             pool);
5931         ret = exec_cmd(cmd, &flist);
5932         INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5933         if (ret != 0) {
5934                 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
5935                     pool);
5936                 return (NULL);
5937         }
5938 
5939         if (flist.head && (flist.head == flist.tail)) {
5940                 char *legacy = strtok(flist.head->line, " \t\n");
5941                 if (legacy && strcmp(legacy, "legacy") == 0) {
5942                         filelist_free(&flist);
5943                         BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
5944                         return (mount_legacy_dataset(pool, mnted));
5945                 }
5946         }
5947 
5948         filelist_free(&flist);
5949 
5950         BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
5951 
5952         (void) snprintf(cmd, sizeof (cmd),
5953             "/sbin/zfs get -Ho value mounted %s",
5954             pool);
5955 
5956         ret = exec_cmd(cmd, &flist);
5957         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5958         if (ret != 0) {
5959                 bam_error(_("failed to determine mount status of ZFS "
5960                     "pool %s\n"), pool);
5961                 return (NULL);
5962         }
5963 
5964         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5965         if ((flist.head == NULL) || (flist.head != flist.tail)) {
5966                 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
5967                 filelist_free(&flist);
5968                 return (NULL);
5969         }
5970 
5971         is_mounted = strtok(flist.head->line, " \t\n");
5972         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5973         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5974         if (strcmp(is_mounted, "no") != 0) {
5975                 filelist_free(&flist);
5976                 *mnted = ZFS_ALREADY;
5977                 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
5978                     fcn, pool));
5979                 goto mounted;
5980         }
5981 
5982         filelist_free(&flist);
5983         BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
5984             fcn, pool));
5985 
5986         /* top dataset is not mounted. Mount it now */
5987         (void) snprintf(cmd, sizeof (cmd),
5988             "/sbin/zfs mount %s", pool);
5989         ret = exec_cmd(cmd, NULL);
5990         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5991         if (ret != 0) {
5992                 bam_error(_("mount of ZFS pool %s failed\n"), pool);
5993                 return (NULL);
5994         }
5995         *mnted = ZFS_MOUNTED;
5996         BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
5997         /*FALLTHRU*/
5998 mounted:
5999         /*
6000          * Now get the mountpoint
6001          */
6002         (void) snprintf(cmd, sizeof (cmd),
6003             "/sbin/zfs get -Ho value mountpoint %s",
6004             pool);
6005 
6006         ret = exec_cmd(cmd, &flist);
6007         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
6008         if (ret != 0) {
6009                 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6010                     pool);
6011                 goto error;
6012         }
6013 
6014         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
6015         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6016                 bam_error(_("ZFS pool %s has no mount-point\n"), pool);
6017                 goto error;
6018         }
6019 
6020         mntpt = strtok(flist.head->line, " \t\n");
6021         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
6022         if (*mntpt != '/') {
6023                 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6024                     pool, mntpt);
6025                 goto error;
6026         }
6027         zmntpt = s_strdup(mntpt);
6028 
6029         filelist_free(&flist);
6030 
6031         BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6032             fcn, pool, zmntpt));
6033 
6034         return (zmntpt);
6035 
6036 error:
6037         filelist_free(&flist);
6038         (void) umount_top_dataset(pool, *mnted, NULL);
6039         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6040         return (NULL);
6041 }
6042 
6043 int
6044 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
6045 {
6046         char            cmd[PATH_MAX];
6047         int             ret;
6048         const char      *fcn = "umount_top_dataset()";
6049 
6050         INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
6051         switch (mnted) {
6052         case LEGACY_ALREADY:
6053         case ZFS_ALREADY:
6054                 /* nothing to do */
6055                 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6056                     "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
6057                 free(mntpt);
6058                 return (BAM_SUCCESS);
6059         case LEGACY_MOUNTED:
6060                 (void) snprintf(cmd, sizeof (cmd),
6061                     "/sbin/umount %s", pool);
6062                 ret = exec_cmd(cmd, NULL);
6063                 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
6064                 if (ret != 0) {
6065                         bam_error(_("umount of %s failed\n"), pool);
6066                         free(mntpt);
6067                         return (BAM_ERROR);
6068                 }
6069                 if (mntpt)
6070                         (void) rmdir(mntpt);
6071                 free(mntpt);
6072                 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6073                     "successfully unmounted\n", fcn, pool));
6074                 return (BAM_SUCCESS);
6075         case ZFS_MOUNTED:
6076                 free(mntpt);
6077                 (void) snprintf(cmd, sizeof (cmd),
6078                     "/sbin/zfs unmount %s", pool);
6079                 ret = exec_cmd(cmd, NULL);
6080                 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
6081                 if (ret != 0) {
6082                         bam_error(_("umount of %s failed\n"), pool);
6083                         return (BAM_ERROR);
6084                 }
6085                 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6086                     "successfully unmounted\n", fcn, pool));
6087                 return (BAM_SUCCESS);
6088         default:
6089                 bam_error(_("Internal error: bad saved mount state for "
6090                     "pool %s\n"), pool);
6091                 return (BAM_ERROR);
6092         }
6093         /*NOTREACHED*/
6094 }
6095 
6096 /*
6097  * For ZFS, osdev can be one of two forms
6098  * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6099  * It can be a /dev/[r]dsk special file. We handle both instances
6100  */
6101 static char *
6102 get_pool(char *osdev)
6103 {
6104         char            cmd[PATH_MAX];
6105         char            buf[PATH_MAX];
6106         filelist_t      flist = {0};
6107         char            *pool;
6108         char            *cp;
6109         char            *slash;
6110         int             ret;
6111         const char      *fcn = "get_pool()";
6112 
6113         INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
6114         if (osdev == NULL) {
6115                 bam_error(_("NULL device: cannot determine pool name\n"));
6116                 return (NULL);
6117         }
6118 
6119         BAM_DPRINTF(("%s: osdev arg = %s\n", fcn, osdev));
6120 
6121         if (osdev[0] != '/') {
6122                 (void) strlcpy(buf, osdev, sizeof (buf));
6123                 slash = strchr(buf, '/');
6124                 if (slash)
6125                         *slash = '\0';
6126                 pool = s_strdup(buf);
6127                 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6128                 return (pool);
6129         } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6130             strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6131                 bam_error(_("invalid device %s: cannot determine pool name\n"),
6132                     osdev);
6133                 return (NULL);
6134         }
6135 
6136         /*
6137          * Call the zfs fstyp directly since this is a zpool. This avoids
6138          * potential pcfs conflicts if the first block wasn't cleared.
6139          */
6140         (void) snprintf(cmd, sizeof (cmd),
6141             "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6142             osdev);
6143 
6144         ret = exec_cmd(cmd, &flist);
6145         INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
6146         if (ret != 0) {
6147                 bam_error(_("fstyp -a on device %s failed\n"), osdev);
6148                 return (NULL);
6149         }
6150 
6151         INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
6152         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6153                 bam_error(_("NULL fstyp -a output for device %s\n"), osdev);
6154                 filelist_free(&flist);
6155                 return (NULL);
6156         }
6157 
6158         (void) strtok(flist.head->line, "'");
6159         cp = strtok(NULL, "'");
6160         INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6161         if (cp == NULL) {
6162                 bam_error(_("bad fstyp -a output for device %s\n"), osdev);
6163                 filelist_free(&flist);
6164                 return (NULL);
6165         }
6166 
6167         pool = s_strdup(cp);
6168 
6169         filelist_free(&flist);
6170 
6171         BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6172 
6173         return (pool);
6174 }
6175 
6176 static char *
6177 find_zfs_existing(char *osdev)
6178 {
6179         char            *pool;
6180         zfs_mnted_t     mnted;
6181         char            *mntpt;
6182         char            *sign;
6183         const char      *fcn = "find_zfs_existing()";
6184 
6185         pool = get_pool(osdev);
6186         INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6187         if (pool == NULL) {
6188                 bam_error(_("failed to get pool for device: %s\n"), osdev);
6189                 return (NULL);
6190         }
6191 
6192         mntpt = mount_top_dataset(pool, &mnted);
6193         INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
6194         if (mntpt == NULL) {
6195                 bam_error(_("failed to mount top dataset for pool: %s\n"),
6196                     pool);
6197                 free(pool);
6198                 return (NULL);
6199         }
6200 
6201         sign = find_primary_common(mntpt, "zfs");
6202         if (sign == NULL) {
6203                 sign = find_backup_common(mntpt, "zfs");
6204                 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
6205                     sign ? sign : "NULL"));
6206         } else {
6207                 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
6208         }
6209 
6210         (void) umount_top_dataset(pool, mnted, mntpt);
6211 
6212         free(pool);
6213 
6214         return (sign);
6215 }
6216 
6217 static char *
6218 find_existing_sign(char *osroot, char *osdev, char *fstype)
6219 {
6220         const char              *fcn = "find_existing_sign()";
6221 
6222         INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6223         if (strcmp(fstype, "ufs") == 0) {
6224                 BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn));
6225                 return (find_ufs_existing(osroot));
6226         } else if (strcmp(fstype, "zfs") == 0) {
6227                 BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn));
6228                 return (find_zfs_existing(osdev));
6229         } else {
6230                 bam_error(_("boot signature not supported for fstype: %s\n"),
6231                     fstype);
6232                 return (NULL);
6233         }
6234 }
6235 
6236 #define MH_HASH_SZ      16
6237 
6238 typedef enum {
6239         MH_ERROR = -1,
6240         MH_NOMATCH,
6241         MH_MATCH
6242 } mh_search_t;
6243 
6244 typedef struct mcache {
6245         char    *mc_special;
6246         char    *mc_mntpt;
6247         char    *mc_fstype;
6248         struct mcache *mc_next;
6249 } mcache_t;
6250 
6251 typedef struct mhash {
6252         mcache_t *mh_hash[MH_HASH_SZ];
6253 } mhash_t;
6254 
6255 static int
6256 mhash_fcn(char *key)
6257 {
6258         int             i;
6259         uint64_t        sum = 0;
6260 
6261         for (i = 0; key[i] != '\0'; i++) {
6262                 sum += (uchar_t)key[i];
6263         }
6264 
6265         sum %= MH_HASH_SZ;
6266 
6267         assert(sum < MH_HASH_SZ);
6268 
6269         return (sum);
6270 }
6271 
6272 static mhash_t *
6273 cache_mnttab(void)
6274 {
6275         FILE            *mfp;
6276         struct extmnttab mnt;
6277         mcache_t        *mcp;
6278         mhash_t         *mhp;
6279         char            *ctds;
6280         int             idx;
6281         int             error;
6282         char            *special_dup;
6283         const char      *fcn = "cache_mnttab()";
6284 
6285         mfp = fopen(MNTTAB, "r");
6286         error = errno;
6287         INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6288         if (mfp == NULL) {
6289                 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
6290                     strerror(error));
6291                 return (NULL);
6292         }
6293 
6294         mhp = s_calloc(1, sizeof (mhash_t));
6295 
6296         resetmnttab(mfp);
6297 
6298         while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6299                 /* only cache ufs */
6300                 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6301                         continue;
6302 
6303                 /* basename() modifies its arg, so dup it */
6304                 special_dup = s_strdup(mnt.mnt_special);
6305                 ctds = basename(special_dup);
6306 
6307                 mcp = s_calloc(1, sizeof (mcache_t));
6308                 mcp->mc_special = s_strdup(ctds);
6309                 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6310                 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6311                 BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6312                     "fstype=%s\n", fcn, ctds, mnt.mnt_mountp, mnt.mnt_fstype));
6313                 idx = mhash_fcn(ctds);
6314                 mcp->mc_next = mhp->mh_hash[idx];
6315                 mhp->mh_hash[idx] = mcp;
6316                 free(special_dup);
6317         }
6318 
6319         (void) fclose(mfp);
6320 
6321         return (mhp);
6322 }
6323 
6324 static void
6325 free_mnttab(mhash_t *mhp)
6326 {
6327         mcache_t        *mcp;
6328         int             i;
6329 
6330         for (i = 0; i < MH_HASH_SZ; i++) {
6331                 while ((mcp = mhp->mh_hash[i]) != NULL) {
6332                         mhp->mh_hash[i] = mcp->mc_next;
6333                         free(mcp->mc_special);
6334                         free(mcp->mc_mntpt);
6335                         free(mcp->mc_fstype);
6336                         free(mcp);
6337                 }
6338         }
6339 
6340         for (i = 0; i < MH_HASH_SZ; i++) {
6341                 assert(mhp->mh_hash[i] == NULL);
6342         }
6343         free(mhp);
6344 }
6345 
6346 static mh_search_t
6347 search_hash(mhash_t *mhp, char *special, char **mntpt)
6348 {
6349         int             idx;
6350         mcache_t        *mcp;
6351         const char      *fcn = "search_hash()";
6352 
6353         assert(mntpt);
6354 
6355         *mntpt = NULL;
6356 
6357         INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6358         if (strchr(special, '/')) {
6359                 bam_error(_("invalid key for mnttab hash: %s\n"), special);
6360                 return (MH_ERROR);
6361         }
6362 
6363         idx = mhash_fcn(special);
6364 
6365         for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6366                 if (strcmp(mcp->mc_special, special) == 0)
6367                         break;
6368         }
6369 
6370         if (mcp == NULL) {
6371                 BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn, special));
6372                 return (MH_NOMATCH);
6373         }
6374 
6375         assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6376         *mntpt = mcp->mc_mntpt;
6377         BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn, special));
6378         return (MH_MATCH);
6379 }
6380 
6381 static int
6382 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6383 {
6384         char            *sign;
6385         char            *signline;
6386         char            signbuf[MAXNAMELEN];
6387         int             len;
6388         int             error;
6389         const char      *fcn = "check_add_ufs_sign_to_list()";
6390 
6391         /* safe to specify NULL as "osdev" arg for UFS */
6392         sign = find_existing_sign(mntpt, NULL, "ufs");
6393         if (sign == NULL) {
6394                 /* No existing signature, nothing to add to list */
6395                 BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6396                     fcn, mntpt));
6397                 return (0);
6398         }
6399 
6400         (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6401         signline = signbuf;
6402 
6403         INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6404         if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6405             strlen(GRUBSIGN_UFS_PREFIX))) {
6406                 bam_error(_("invalid UFS boot signature %s\n"), sign);
6407                 free(sign);
6408                 /* ignore invalid signatures */
6409                 return (0);
6410         }
6411 
6412         len = fputs(signline, tfp);
6413         error = errno;
6414         INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6415         if (len != strlen(signline)) {
6416                 bam_error(_("failed to write signature %s to signature "
6417                     "list: %s\n"), sign, strerror(error));
6418                 free(sign);
6419                 return (-1);
6420         }
6421 
6422         free(sign);
6423 
6424         BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6425             fcn, mntpt));
6426         return (0);
6427 }
6428 
6429 /*
6430  * slice is a basename not a full pathname
6431  */
6432 static int
6433 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6434 {
6435         int             ret;
6436         char            cmd[PATH_MAX];
6437         char            path[PATH_MAX];
6438         struct stat     sbuf;
6439         char            *mntpt;
6440         filelist_t      flist = {0};
6441         char            *fstype;
6442         char            blkslice[PATH_MAX];
6443         const char      *fcn = "process_slice_common()";
6444 
6445 
6446         ret = search_hash(mhp, slice, &mntpt);
6447         switch (ret) {
6448                 case MH_MATCH:
6449                         if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6450                                 return (-1);
6451                         else
6452                                 return (0);
6453                 case MH_NOMATCH:
6454                         break;
6455                 case MH_ERROR:
6456                 default:
6457                         return (-1);
6458         }
6459 
6460         (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6461         if (stat(path, &sbuf) == -1) {
6462                 BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn, path));
6463                 return (0);
6464         }
6465 
6466         /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6467         (void) snprintf(cmd, sizeof (cmd),
6468             "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6469             slice);
6470 
6471         if (exec_cmd(cmd, &flist) != 0) {
6472                 if (bam_verbose)
6473                         bam_print(_("fstyp failed for slice: %s\n"), slice);
6474                 return (0);
6475         }
6476 
6477         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6478                 if (bam_verbose)
6479                         bam_print(_("bad output from fstyp for slice: %s\n"),
6480                             slice);
6481                 filelist_free(&flist);
6482                 return (0);
6483         }
6484 
6485         fstype = strtok(flist.head->line, " \t\n");
6486         if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6487                 if (bam_verbose)
6488                         bam_print(_("%s is not a ufs slice: %s\n"),
6489                             slice, fstype);
6490                 filelist_free(&flist);
6491                 return (0);
6492         }
6493 
6494         filelist_free(&flist);
6495 
6496         /*
6497          * Since we are mounting the filesystem read-only, the
6498          * the last mount field of the superblock is unchanged
6499          * and does not need to be fixed up post-mount;
6500          */
6501 
6502         (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6503             slice);
6504 
6505         (void) snprintf(cmd, sizeof (cmd),
6506             "/usr/sbin/mount -F ufs -o ro %s %s "
6507             "> /dev/null 2>&1", blkslice, tmpmnt);
6508 
6509         if (exec_cmd(cmd, NULL) != 0) {
6510                 if (bam_verbose)
6511                         bam_print(_("mount of %s (fstype %s) failed\n"),
6512                             blkslice, "ufs");
6513                 return (0);
6514         }
6515 
6516         ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6517 
6518         (void) snprintf(cmd, sizeof (cmd),
6519             "/usr/sbin/umount -f %s > /dev/null 2>&1",
6520             tmpmnt);
6521 
6522         if (exec_cmd(cmd, NULL) != 0) {
6523                 bam_print(_("umount of %s failed\n"), slice);
6524                 return (0);
6525         }
6526 
6527         return (ret);
6528 }
6529 
6530 static int
6531 process_vtoc_slices(
6532         char *s0,
6533         struct vtoc *vtoc,
6534         FILE *tfp,
6535         mhash_t *mhp,
6536         char *tmpmnt)
6537 {
6538         int             idx;
6539         char            slice[PATH_MAX];
6540         size_t          len;
6541         char            *cp;
6542         const char      *fcn = "process_vtoc_slices()";
6543 
6544         len = strlen(s0);
6545 
6546         assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6547 
6548         s0[len - 1] = '\0';
6549 
6550         (void) strlcpy(slice, s0, sizeof (slice));
6551 
6552         s0[len - 1] = '0';
6553 
6554         cp = slice + len - 1;
6555 
6556         for (idx = 0; idx < vtoc->v_nparts; idx++) {
6557 
6558                 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6559 
6560                 if (vtoc->v_part[idx].p_size == 0) {
6561                         BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6562                             fcn, slice));
6563                         continue;
6564                 }
6565 
6566                 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6567                 switch (vtoc->v_part[idx].p_tag) {
6568                 case V_SWAP:
6569                 case V_USR:
6570                 case V_BACKUP:
6571                 case V_VAR:
6572                 case V_HOME:
6573                 case V_ALTSCTR:
6574                         BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6575                             "skipping: %s\n", fcn, slice));
6576                         continue;
6577                 default:
6578                         BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6579                             fcn, slice));
6580                         break;
6581                 }
6582 
6583                 /* skip unmountable and readonly slices */
6584                 switch (vtoc->v_part[idx].p_flag) {
6585                 case V_UNMNT:
6586                 case V_RONLY:
6587                         BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6588                             fcn, slice));
6589                         continue;
6590                 default:
6591                         BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6592                             fcn, slice));
6593                         break;
6594                 }
6595 
6596                 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6597                         return (-1);
6598                 }
6599         }
6600 
6601         return (0);
6602 }
6603 
6604 static int
6605 process_efi_slices(
6606         char *s0,
6607         struct dk_gpt *efi,
6608         FILE *tfp,
6609         mhash_t *mhp,
6610         char *tmpmnt)
6611 {
6612         int             idx;
6613         char            slice[PATH_MAX];
6614         size_t          len;
6615         char            *cp;
6616         const char      *fcn = "process_efi_slices()";
6617 
6618         len = strlen(s0);
6619 
6620         assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6621 
6622         s0[len - 1] = '\0';
6623 
6624         (void) strlcpy(slice, s0, sizeof (slice));
6625 
6626         s0[len - 1] = '0';
6627 
6628         cp = slice + len - 1;
6629 
6630         for (idx = 0; idx < efi->efi_nparts; idx++) {
6631 
6632                 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6633 
6634                 if (efi->efi_parts[idx].p_size == 0) {
6635                         BAM_DPRINTF(("%s: EFI: skipping 0-length slice: %s\n",
6636                             fcn, slice));
6637                         continue;
6638                 }
6639 
6640                 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6641                 switch (efi->efi_parts[idx].p_tag) {
6642                 case V_SWAP:
6643                 case V_USR:
6644                 case V_BACKUP:
6645                 case V_VAR:
6646                 case V_HOME:
6647                 case V_ALTSCTR:
6648                         BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6649                             fcn, slice));
6650                         continue;
6651                 default:
6652                         BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6653                             fcn, slice));
6654                         break;
6655                 }
6656 
6657                 /* skip unmountable and readonly slices */
6658                 switch (efi->efi_parts[idx].p_flag) {
6659                 case V_UNMNT:
6660                 case V_RONLY:
6661                         BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6662                             fcn, slice));
6663                         continue;
6664                 default:
6665                         BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6666                             fcn, slice));
6667                         break;
6668                 }
6669 
6670                 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6671                         return (-1);
6672                 }
6673         }
6674 
6675         return (0);
6676 }
6677 
6678 /*
6679  * s0 is a basename not a full path
6680  */
6681 static int
6682 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6683 {
6684         struct vtoc             vtoc;
6685         struct dk_gpt           *efi;
6686         char                    s0path[PATH_MAX];
6687         struct stat             sbuf;
6688         int                     e_flag;
6689         int                     v_flag;
6690         int                     retval;
6691         int                     err;
6692         int                     fd;
6693         const char              *fcn = "process_slice0()";
6694 
6695         (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6696 
6697         if (stat(s0path, &sbuf) == -1) {
6698                 BAM_DPRINTF(("%s: slice 0 does not exist: %s\n", fcn, s0path));
6699                 return (0);
6700         }
6701 
6702         fd = open(s0path, O_NONBLOCK|O_RDONLY);
6703         if (fd == -1) {
6704                 bam_error(_("failed to open file: %s: %s\n"), s0path,
6705                     strerror(errno));
6706                 return (0);
6707         }
6708 
6709         e_flag = v_flag = 0;
6710         retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6711         switch (retval) {
6712                 case VT_EIO:
6713                         BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6714                             fcn, s0path));
6715                         break;
6716                 case VT_EINVAL:
6717                         BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6718                             fcn, s0path));
6719                         break;
6720                 case VT_ERROR:
6721                         BAM_DPRINTF(("%s: VTOC: unknown error while "
6722                             "reading: %s\n", fcn, s0path));
6723                         break;
6724                 case VT_ENOTSUP:
6725                         e_flag = 1;
6726                         BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6727                             fcn, s0path));
6728                         break;
6729                 case 0:
6730                         v_flag = 1;
6731                         BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6732                             fcn, s0path));
6733                         break;
6734                 default:
6735                         BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6736                             "code: %s\n", fcn, s0path));
6737                         break;
6738         }
6739 
6740 
6741         if (e_flag) {
6742                 e_flag = 0;
6743                 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6744                 switch (retval) {
6745                 case VT_EIO:
6746                         BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6747                             fcn, s0path));
6748                         break;
6749                 case VT_EINVAL:
6750                         BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn, s0path));
6751                         break;
6752                 case VT_ERROR:
6753                         BAM_DPRINTF(("%s: EFI: unknown error while "
6754                             "reading: %s\n", fcn, s0path));
6755                         break;
6756                 case VT_ENOTSUP:
6757                         BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6758                             fcn, s0path));
6759                         break;
6760                 case 0:
6761                         e_flag = 1;
6762                         BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6763                             fcn, s0path));
6764                         break;
6765                 default:
6766                         BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6767                             fcn, s0path));
6768                         break;
6769                 }
6770         }
6771 
6772         (void) close(fd);
6773 
6774         if (v_flag) {
6775                 retval = process_vtoc_slices(s0,
6776                     &vtoc, tfp, mhp, tmpmnt);
6777         } else if (e_flag) {
6778                 retval = process_efi_slices(s0,
6779                     efi, tfp, mhp, tmpmnt);
6780         } else {
6781                 BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6782                     fcn, s0path));
6783                 return (0);
6784         }
6785 
6786         return (retval);
6787 }
6788 
6789 /*
6790  * Find and create a list of all existing UFS boot signatures
6791  */
6792 static int
6793 FindAllUfsSignatures(void)
6794 {
6795         mhash_t         *mnttab_hash;
6796         DIR             *dirp = NULL;
6797         struct dirent   *dp;
6798         char            tmpmnt[PATH_MAX];
6799         char            cmd[PATH_MAX];
6800         struct stat     sb;
6801         int             fd;
6802         FILE            *tfp;
6803         size_t          len;
6804         int             ret;
6805         int             error;
6806         const char      *fcn = "FindAllUfsSignatures()";
6807 
6808         if (stat(UFS_SIGNATURE_LIST, &sb) != -1)  {
6809                 bam_print(_("       - signature list %s exists\n"),
6810                     UFS_SIGNATURE_LIST);
6811                 return (0);
6812         }
6813 
6814         fd = open(UFS_SIGNATURE_LIST".tmp",
6815             O_RDWR|O_CREAT|O_TRUNC, 0644);
6816         error = errno;
6817         INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6818         if (fd == -1) {
6819                 bam_error(_("failed to open file: %s: %s\n"),
6820                     UFS_SIGNATURE_LIST".tmp", strerror(error));
6821                 return (-1);
6822         }
6823 
6824         ret = close(fd);
6825         error = errno;
6826         INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6827         if (ret == -1) {
6828                 bam_error(_("failed to close file: %s: %s\n"),
6829                     UFS_SIGNATURE_LIST".tmp", strerror(error));
6830                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6831                 return (-1);
6832         }
6833 
6834         tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6835         error = errno;
6836         INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6837         if (tfp == NULL) {
6838                 bam_error(_("failed to open file: %s: %s\n"),
6839                     UFS_SIGNATURE_LIST".tmp", strerror(error));
6840                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6841                 return (-1);
6842         }
6843 
6844         mnttab_hash = cache_mnttab();
6845         INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6846         if (mnttab_hash == NULL) {
6847                 (void) fclose(tfp);
6848                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6849                 bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn);
6850                 return (-1);
6851         }
6852 
6853         (void) snprintf(tmpmnt, sizeof (tmpmnt),
6854             "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6855         (void) unlink(tmpmnt);
6856 
6857         ret = mkdirp(tmpmnt, DIR_PERMS);
6858         error = errno;
6859         INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6860         if (ret == -1) {
6861                 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
6862                     strerror(error));
6863                 free_mnttab(mnttab_hash);
6864                 (void) fclose(tfp);
6865                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6866                 return (-1);
6867         }
6868 
6869         dirp = opendir("/dev/rdsk");
6870         error = errno;
6871         INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6872         if (dirp == NULL) {
6873                 bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
6874                     strerror(error));
6875                 goto fail;
6876         }
6877 
6878         while ((dp = readdir(dirp)) != NULL) {
6879                 if (strcmp(dp->d_name, ".") == 0 ||
6880                     strcmp(dp->d_name, "..") == 0)
6881                         continue;
6882 
6883                 /*
6884                  * we only look for the s0 slice. This is guranteed to
6885                  * have 's' at len - 2.
6886                  */
6887                 len = strlen(dp->d_name);
6888                 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6889                         BAM_DPRINTF(("%s: skipping non-s0 slice: %s\n",
6890                             fcn, dp->d_name));
6891                         continue;
6892                 }
6893 
6894                 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6895                 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6896                 if (ret == -1)
6897                         goto fail;
6898         }
6899 
6900         (void) closedir(dirp);
6901         free_mnttab(mnttab_hash);
6902         (void) rmdir(tmpmnt);
6903 
6904         ret = fclose(tfp);
6905         error = errno;
6906         INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6907         if (ret == EOF) {
6908                 bam_error(_("failed to close file: %s: %s\n"),
6909                     UFS_SIGNATURE_LIST".tmp", strerror(error));
6910                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6911                 return (-1);
6912         }
6913 
6914         /* We have a list of existing GRUB signatures. Sort it first */
6915         (void) snprintf(cmd, sizeof (cmd),
6916             "/usr/bin/sort -u %s.tmp > %s.sorted",
6917             UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6918 
6919         ret = exec_cmd(cmd, NULL);
6920         INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6921         if (ret != 0) {
6922                 bam_error(_("error sorting GRUB UFS boot signatures\n"));
6923                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6924                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6925                 return (-1);
6926         }
6927 
6928         (void) unlink(UFS_SIGNATURE_LIST".tmp");
6929 
6930         ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6931         error = errno;
6932         INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6933         if (ret == -1) {
6934                 bam_error(_("rename to file failed: %s: %s\n"),
6935                     UFS_SIGNATURE_LIST, strerror(error));
6936                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6937                 return (-1);
6938         }
6939 
6940         if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6941                 BAM_DPRINTF(("%s: generated zero length signlist: %s.\n",
6942                     fcn, UFS_SIGNATURE_LIST));
6943         }
6944 
6945         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
6946         return (0);
6947 
6948 fail:
6949         if (dirp)
6950                 (void) closedir(dirp);
6951         free_mnttab(mnttab_hash);
6952         (void) rmdir(tmpmnt);
6953         (void) fclose(tfp);
6954         (void) unlink(UFS_SIGNATURE_LIST".tmp");
6955         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6956         return (-1);
6957 }
6958 
6959 static char *
6960 create_ufs_sign(void)
6961 {
6962         struct stat     sb;
6963         int             signnum = -1;
6964         char            tmpsign[MAXNAMELEN + 1];
6965         char            *numstr;
6966         int             i;
6967         FILE            *tfp;
6968         int             ret;
6969         int             error;
6970         const char      *fcn = "create_ufs_sign()";
6971 
6972         bam_print(_("  - searching for UFS boot signatures\n"));
6973 
6974         ret = FindAllUfsSignatures();
6975         INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6976         if (ret == -1) {
6977                 bam_error(_("search for UFS boot signatures failed\n"));
6978                 return (NULL);
6979         }
6980 
6981         /* Make sure the list exists and is owned by root */
6982         INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6983             (void) unlink(UFS_SIGNATURE_LIST));
6984         if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6985                 (void) unlink(UFS_SIGNATURE_LIST);
6986                 bam_error(_("missing UFS signature list file: %s\n"),
6987                     UFS_SIGNATURE_LIST);
6988                 return (NULL);
6989         }
6990 
6991         if (sb.st_size == 0) {
6992                 bam_print(_("   - no existing UFS boot signatures\n"));
6993                 i = 0;
6994                 goto found;
6995         }
6996 
6997         /* The signature list was sorted when it was created */
6998         tfp = fopen(UFS_SIGNATURE_LIST, "r");
6999         error = errno;
7000         INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
7001         if (tfp == NULL) {
7002                 bam_error(_("error opening UFS boot signature list "
7003                     "file %s: %s\n"), UFS_SIGNATURE_LIST, strerror(error));
7004                 (void) unlink(UFS_SIGNATURE_LIST);
7005                 return (NULL);
7006         }
7007 
7008         for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
7009 
7010                 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
7011                     strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7012                         (void) fclose(tfp);
7013                         (void) unlink(UFS_SIGNATURE_LIST);
7014                         bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7015                         return (NULL);
7016                 }
7017                 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
7018 
7019                 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
7020                         (void) fclose(tfp);
7021                         (void) unlink(UFS_SIGNATURE_LIST);
7022                         bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7023                         return (NULL);
7024                 }
7025 
7026                 signnum = atoi(numstr);
7027                 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
7028                 if (signnum < 0) {
7029                         (void) fclose(tfp);
7030                         (void) unlink(UFS_SIGNATURE_LIST);
7031                         bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7032                         return (NULL);
7033                 }
7034 
7035                 if (i != signnum) {
7036                         BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7037                             fcn, i));
7038                         break;
7039                 }
7040         }
7041 
7042         (void) fclose(tfp);
7043 
7044 found:
7045         (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
7046 
7047         /* add the ufs signature to the /var/run list of signatures */
7048         ret = ufs_add_to_sign_list(tmpsign);
7049         INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
7050         if (ret == -1) {
7051                 (void) unlink(UFS_SIGNATURE_LIST);
7052                 bam_error(_("failed to add sign %s to signlist.\n"), tmpsign);
7053                 return (NULL);
7054         }
7055 
7056         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7057 
7058         return (s_strdup(tmpsign));
7059 }
7060 
7061 static char *
7062 get_fstype(char *osroot)
7063 {
7064         FILE            *mntfp;
7065         struct mnttab   mp = {0};
7066         struct mnttab   mpref = {0};
7067         int             error;
7068         int             ret;
7069         const char      *fcn = "get_fstype()";
7070 
7071         INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
7072         if (osroot == NULL) {
7073                 bam_error(_("no OS mountpoint. Cannot determine fstype\n"));
7074                 return (NULL);
7075         }
7076 
7077         mntfp = fopen(MNTTAB, "r");
7078         error = errno;
7079         INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
7080         if (mntfp == NULL) {
7081                 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7082                     strerror(error));
7083                 return (NULL);
7084         }
7085 
7086         if (*osroot == '\0')
7087                 mpref.mnt_mountp = "/";
7088         else
7089                 mpref.mnt_mountp = osroot;
7090 
7091         ret = getmntany(mntfp, &mp, &mpref);
7092         INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
7093         if (ret != 0) {
7094                 bam_error(_("failed to find OS mountpoint %s in %s\n"),
7095                     osroot, MNTTAB);
7096                 (void) fclose(mntfp);
7097                 return (NULL);
7098         }
7099         (void) fclose(mntfp);
7100 
7101         INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
7102         if (mp.mnt_fstype == NULL) {
7103                 bam_error(_("NULL fstype found for OS root %s\n"), osroot);
7104                 return (NULL);
7105         }
7106 
7107         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7108 
7109         return (s_strdup(mp.mnt_fstype));
7110 }
7111 
7112 static char *
7113 create_zfs_sign(char *osdev)
7114 {
7115         char            tmpsign[PATH_MAX];
7116         char            *pool;
7117         const char      *fcn = "create_zfs_sign()";
7118 
7119         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, osdev));
7120 
7121         /*
7122          * First find the pool name
7123          */
7124         pool = get_pool(osdev);
7125         INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
7126         if (pool == NULL) {
7127                 bam_error(_("failed to get pool name from %s\n"), osdev);
7128                 return (NULL);
7129         }
7130 
7131         (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
7132 
7133         BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn, tmpsign));
7134 
7135         free(pool);
7136 
7137         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7138 
7139         return (s_strdup(tmpsign));
7140 }
7141 
7142 static char *
7143 create_new_sign(char *osdev, char *fstype)
7144 {
7145         char            *sign;
7146         const char      *fcn = "create_new_sign()";
7147 
7148         INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
7149 
7150         if (strcmp(fstype, "zfs") == 0) {
7151                 BAM_DPRINTF(("%s: created new ZFS sign\n", fcn));
7152                 sign = create_zfs_sign(osdev);
7153         } else if (strcmp(fstype, "ufs") == 0) {
7154                 BAM_DPRINTF(("%s: created new UFS sign\n", fcn));
7155                 sign = create_ufs_sign();
7156         } else {
7157                 bam_error(_("boot signature not supported for fstype: %s\n"),
7158                     fstype);
7159                 sign = NULL;
7160         }
7161 
7162         BAM_DPRINTF(("%s: created new sign: %s\n", fcn,
7163             sign ? sign : "<NULL>"));
7164         return (sign);
7165 }
7166 
7167 static int
7168 set_backup_common(char *mntpt, char *sign)
7169 {
7170         FILE            *bfp;
7171         char            backup[PATH_MAX];
7172         char            tmpsign[PATH_MAX];
7173         int             error;
7174         char            *bdir;
7175         char            *backup_dup;
7176         struct stat     sb;
7177         int             ret;
7178         const char      *fcn = "set_backup_common()";
7179 
7180         (void) snprintf(backup, sizeof (backup), "%s%s",
7181             mntpt, GRUBSIGN_BACKUP);
7182 
7183         /* First read the backup */
7184         bfp = fopen(backup, "r");
7185         if (bfp != NULL) {
7186                 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
7187                         if (strcmp(tmpsign, sign) == 0) {
7188                                 BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7189                                     fcn, sign));
7190                                 (void) fclose(bfp);
7191                                 return (0);
7192                         }
7193                 }
7194                 (void) fclose(bfp);
7195                 BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7196                     fcn, sign));
7197         } else {
7198                 BAM_DPRINTF(("%s: no backup file (%s) found.\n", fcn, backup));
7199         }
7200 
7201         /*
7202          * Didn't find the correct signature. First create
7203          * the directory if necessary.
7204          */
7205 
7206         /* dirname() modifies its argument so dup it */
7207         backup_dup = s_strdup(backup);
7208         bdir = dirname(backup_dup);
7209         assert(bdir);
7210 
7211         ret = stat(bdir, &sb);
7212         INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
7213         if (ret == -1) {
7214                 BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7215                     fcn, bdir));
7216                 ret = mkdirp(bdir, DIR_PERMS);
7217                 error = errno;
7218                 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
7219                 if (ret == -1) {
7220                         bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7221                             GRUBSIGN_BACKUP, strerror(error));
7222                         free(backup_dup);
7223                         return (-1);
7224                 }
7225         }
7226         free(backup_dup);
7227 
7228         /*
7229          * Open the backup in append mode to add the correct
7230          * signature;
7231          */
7232         bfp = fopen(backup, "a");
7233         error = errno;
7234         INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
7235         if (bfp == NULL) {
7236                 bam_error(_("error opening boot signature backup "
7237                     "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7238                 return (-1);
7239         }
7240 
7241         (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
7242 
7243         ret = fputs(tmpsign, bfp);
7244         error = errno;
7245         INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
7246         if (ret != strlen(tmpsign)) {
7247                 bam_error(_("error writing boot signature backup "
7248                     "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7249                 (void) fclose(bfp);
7250                 return (-1);
7251         }
7252 
7253         (void) fclose(bfp);
7254 
7255         if (bam_verbose)
7256                 bam_print(_("updated boot signature backup file %s\n"),
7257                     GRUBSIGN_BACKUP);
7258 
7259         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7260 
7261         return (0);
7262 }
7263 
7264 static int
7265 set_backup_ufs(char *osroot, char *sign)
7266 {
7267         const char      *fcn = "set_backup_ufs()";
7268 
7269         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7270         return (set_backup_common(osroot, sign));
7271 }
7272 
7273 static int
7274 set_backup_zfs(char *osdev, char *sign)
7275 {
7276         char            *pool;
7277         char            *mntpt;
7278         zfs_mnted_t     mnted;
7279         int             ret;
7280         const char      *fcn = "set_backup_zfs()";
7281 
7282         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7283 
7284         pool = get_pool(osdev);
7285         INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7286         if (pool == NULL) {
7287                 bam_error(_("failed to get pool name from %s\n"), osdev);
7288                 return (-1);
7289         }
7290 
7291         mntpt = mount_top_dataset(pool, &mnted);
7292         INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7293         if (mntpt == NULL) {
7294                 bam_error(_("failed to mount top dataset for %s\n"), pool);
7295                 free(pool);
7296                 return (-1);
7297         }
7298 
7299         ret = set_backup_common(mntpt, sign);
7300 
7301         (void) umount_top_dataset(pool, mnted, mntpt);
7302 
7303         free(pool);
7304 
7305         INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7306         if (ret == 0) {
7307                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7308         } else {
7309                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7310         }
7311 
7312         return (ret);
7313 }
7314 
7315 static int
7316 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7317 {
7318         const char      *fcn = "set_backup()";
7319         int             ret;
7320 
7321         INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7322 
7323         if (strcmp(fstype, "ufs") == 0) {
7324                 BAM_DPRINTF(("%s: setting UFS backup sign\n", fcn));
7325                 ret = set_backup_ufs(osroot, sign);
7326         } else if (strcmp(fstype, "zfs") == 0) {
7327                 BAM_DPRINTF(("%s: setting ZFS backup sign\n", fcn));
7328                 ret = set_backup_zfs(osdev, sign);
7329         } else {
7330                 bam_error(_("boot signature not supported for fstype: %s\n"),
7331                     fstype);
7332                 ret = -1;
7333         }
7334 
7335         if (ret == 0) {
7336                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7337         } else {
7338                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7339         }
7340 
7341         return (ret);
7342 }
7343 
7344 static int
7345 set_primary_common(char *mntpt, char *sign)
7346 {
7347         char            signfile[PATH_MAX];
7348         char            signdir[PATH_MAX];
7349         struct stat     sb;
7350         int             fd;
7351         int             error;
7352         int             ret;
7353         const char      *fcn = "set_primary_common()";
7354 
7355         (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7356             mntpt, GRUBSIGN_DIR, sign);
7357 
7358         if (stat(signfile, &sb) != -1) {
7359                 if (bam_verbose)
7360                         bam_print(_("primary sign %s exists\n"), sign);
7361                 return (0);
7362         } else {
7363                 BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7364                     fcn, signfile));
7365         }
7366 
7367         (void) snprintf(signdir, sizeof (signdir), "%s/%s",
7368             mntpt, GRUBSIGN_DIR);
7369 
7370         if (stat(signdir, &sb) == -1) {
7371                 BAM_DPRINTF(("%s: primary signdir (%s) does not exist\n",
7372                     fcn, signdir));
7373                 ret = mkdirp(signdir, DIR_PERMS);
7374                 error = errno;
7375                 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7376                 if (ret == -1) {
7377                         bam_error(_("error creating boot signature "
7378                             "directory %s: %s\n"), signdir, strerror(errno));
7379                         return (-1);
7380                 }
7381         }
7382 
7383         fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7384         error = errno;
7385         INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7386         if (fd == -1) {
7387                 bam_error(_("error creating primary boot signature %s: %s\n"),
7388                     signfile, strerror(error));
7389                 return (-1);
7390         }
7391 
7392         ret = fsync(fd);
7393         error = errno;
7394         INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7395         if (ret != 0) {
7396                 bam_error(_("error syncing primary boot signature %s: %s\n"),
7397                     signfile, strerror(error));
7398         }
7399 
7400         (void) close(fd);
7401 
7402         if (bam_verbose)
7403                 bam_print(_("created primary GRUB boot signature: %s\n"),
7404                     signfile);
7405 
7406         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7407 
7408         return (0);
7409 }
7410 
7411 static int
7412 set_primary_ufs(char *osroot, char *sign)
7413 {
7414         const char      *fcn = "set_primary_ufs()";
7415 
7416         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7417         return (set_primary_common(osroot, sign));
7418 }
7419 
7420 static int
7421 set_primary_zfs(char *osdev, char *sign)
7422 {
7423         char            *pool;
7424         char            *mntpt;
7425         zfs_mnted_t     mnted;
7426         int             ret;
7427         const char      *fcn = "set_primary_zfs()";
7428 
7429         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7430 
7431         pool = get_pool(osdev);
7432         INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7433         if (pool == NULL) {
7434                 bam_error(_("failed to get pool name from %s\n"), osdev);
7435                 return (-1);
7436         }
7437 
7438         /* Pool name must exist in the sign */
7439         ret = (strstr(sign, pool) != NULL);
7440         INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7441         if (ret == 0) {
7442                 bam_error(_("pool name %s not present in signature %s\n"),
7443                     pool, sign);
7444                 free(pool);
7445                 return (-1);
7446         }
7447 
7448         mntpt = mount_top_dataset(pool, &mnted);
7449         INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7450         if (mntpt == NULL) {
7451                 bam_error(_("failed to mount top dataset for %s\n"), pool);
7452                 free(pool);
7453                 return (-1);
7454         }
7455 
7456         ret = set_primary_common(mntpt, sign);
7457 
7458         (void) umount_top_dataset(pool, mnted, mntpt);
7459 
7460         free(pool);
7461 
7462         INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7463         if (ret == 0) {
7464                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7465         } else {
7466                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7467         }
7468 
7469         return (ret);
7470 }
7471 
7472 static int
7473 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7474 {
7475         const char      *fcn = "set_primary()";
7476         int             ret;
7477 
7478         INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7479         if (strcmp(fstype, "ufs") == 0) {
7480                 BAM_DPRINTF(("%s: setting UFS primary sign\n", fcn));
7481                 ret = set_primary_ufs(osroot, sign);
7482         } else if (strcmp(fstype, "zfs") == 0) {
7483                 BAM_DPRINTF(("%s: setting ZFS primary sign\n", fcn));
7484                 ret = set_primary_zfs(osdev, sign);
7485         } else {
7486                 bam_error(_("boot signature not supported for fstype: %s\n"),
7487                     fstype);
7488                 ret = -1;
7489         }
7490 
7491         if (ret == 0) {
7492                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7493         } else {
7494                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7495         }
7496 
7497         return (ret);
7498 }
7499 
7500 static int
7501 ufs_add_to_sign_list(char *sign)
7502 {
7503         FILE            *tfp;
7504         char            signline[MAXNAMELEN];
7505         char            cmd[PATH_MAX];
7506         int             ret;
7507         int             error;
7508         const char      *fcn = "ufs_add_to_sign_list()";
7509 
7510         INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7511         if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7512             strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7513                 bam_error(_("invalid UFS boot signature %s\n"), sign);
7514                 (void) unlink(UFS_SIGNATURE_LIST);
7515                 return (-1);
7516         }
7517 
7518         /*
7519          * most failures in this routine are not a fatal error
7520          * We simply unlink the /var/run file and continue
7521          */
7522 
7523         ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7524         error = errno;
7525         INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7526         if (ret == -1) {
7527                 bam_error(_("rename to file failed: %s: %s\n"),
7528                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7529                 (void) unlink(UFS_SIGNATURE_LIST);
7530                 return (0);
7531         }
7532 
7533         tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7534         error = errno;
7535         INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7536         if (tfp == NULL) {
7537                 bam_error(_("failed to open file: %s: %s\n"),
7538                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7539                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7540                 return (0);
7541         }
7542 
7543         (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7544 
7545         ret = fputs(signline, tfp);
7546         error = errno;
7547         INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7548         if (ret != strlen(signline)) {
7549                 bam_error(_("failed to write signature %s to signature "
7550                     "list: %s\n"), sign, strerror(error));
7551                 (void) fclose(tfp);
7552                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7553                 return (0);
7554         }
7555 
7556         ret = fclose(tfp);
7557         error = errno;
7558         INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7559         if (ret == EOF) {
7560                 bam_error(_("failed to close file: %s: %s\n"),
7561                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7562                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7563                 return (0);
7564         }
7565 
7566         /* Sort the list again */
7567         (void) snprintf(cmd, sizeof (cmd),
7568             "/usr/bin/sort -u %s.tmp > %s.sorted",
7569             UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7570 
7571         ret = exec_cmd(cmd, NULL);
7572         INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7573         if (ret != 0) {
7574                 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7575                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7576                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7577                 return (0);
7578         }
7579 
7580         (void) unlink(UFS_SIGNATURE_LIST".tmp");
7581 
7582         ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7583         error = errno;
7584         INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7585         if (ret == -1) {
7586                 bam_error(_("rename to file failed: %s: %s\n"),
7587                     UFS_SIGNATURE_LIST, strerror(error));
7588                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7589                 return (0);
7590         }
7591 
7592         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7593 
7594         return (0);
7595 }
7596 
7597 static int
7598 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7599 {
7600         int             ret;
7601         const char      *fcn = "set_signature()";
7602 
7603         BAM_DPRINTF(("%s: entered. args: %s %s %s %s\n", fcn,
7604             osroot, osdev, sign, fstype));
7605 
7606         ret = set_backup(osroot, osdev, sign, fstype);
7607         INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7608         if (ret == -1) {
7609                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7610                 bam_error(_("failed to set backup sign (%s) for %s: %s\n"),
7611                     sign, osroot, osdev);
7612                 return (-1);
7613         }
7614 
7615         ret = set_primary(osroot, osdev, sign, fstype);
7616         INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7617 
7618         if (ret == 0) {
7619                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7620         } else {
7621                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7622                 bam_error(_("failed to set primary sign (%s) for %s: %s\n"),
7623                     sign, osroot, osdev);
7624 
7625         }
7626         return (ret);
7627 }
7628 
7629 char *
7630 get_grubsign(char *osroot, char *osdev)
7631 {
7632         char            *grubsign;      /* (<sign>,#,#) */
7633         char            *slice;
7634         int             fdiskpart;
7635         char            *sign;
7636         char            *fstype;
7637         int             ret;
7638         const char      *fcn = "get_grubsign()";
7639 
7640         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, osdev));
7641         fstype = get_fstype(osroot);
7642         INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7643         if (fstype == NULL) {
7644                 bam_error(_("failed to get fstype for %s\n"), osroot);
7645                 return (NULL);
7646         }
7647 
7648         sign = find_existing_sign(osroot, osdev, fstype);
7649         INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7650         if (sign == NULL) {
7651                 BAM_DPRINTF(("%s: no existing grubsign for %s: %s\n",
7652                     fcn, osroot, osdev));
7653                 sign = create_new_sign(osdev, fstype);
7654                 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7655                 if (sign == NULL) {
7656                         bam_error(_("failed to create GRUB boot signature for "
7657                             "device: %s\n"), osdev);
7658                         free(fstype);
7659                         return (NULL);
7660                 }
7661         }
7662 
7663         ret = set_signature(osroot, osdev, sign, fstype);
7664         INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7665         if (ret == -1) {
7666                 bam_error(_("failed to write GRUB boot signature for "
7667                     "device: %s\n"), osdev);
7668                 free(sign);
7669                 free(fstype);
7670                 (void) unlink(UFS_SIGNATURE_LIST);
7671                 return (NULL);
7672         }
7673 
7674         free(fstype);
7675 
7676         if (bam_verbose)
7677                 bam_print(_("found or created GRUB signature %s for %s\n"),
7678                     sign, osdev);
7679 
7680         fdiskpart = get_partition(osdev);
7681         INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7682         if (fdiskpart == PARTNO_NOTFOUND) {
7683                 bam_error(_("failed to determine fdisk partition: %s\n"),
7684                     osdev);
7685                 free(sign);
7686                 return (NULL);
7687         }
7688 
7689         slice = strrchr(osdev, 's');
7690 
7691         if (fdiskpart == PARTNO_EFI) {
7692                 fdiskpart = atoi(&slice[1]);
7693                 slice = NULL;
7694         }
7695 
7696         grubsign = s_calloc(1, MAXNAMELEN + 10);
7697         if (slice) {
7698                 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7699                     sign, fdiskpart, slice[1] + 'a' - '0');
7700         } else
7701                 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7702                     sign, fdiskpart);
7703 
7704         free(sign);
7705 
7706         BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn, grubsign));
7707 
7708         return (grubsign);
7709 }
7710 
7711 static char *
7712 get_title(char *rootdir)
7713 {
7714         static char     title[80];
7715         char            *cp = NULL;
7716         char            release[PATH_MAX];
7717         FILE            *fp;
7718         const char      *fcn = "get_title()";
7719 
7720         /* open the /etc/release file */
7721         (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7722 
7723         fp = fopen(release, "r");
7724         if (fp == NULL) {
7725                 bam_error(_("failed to open file: %s: %s\n"), release,
7726                     strerror(errno));
7727                 cp = NULL;
7728                 goto out;
7729         }
7730 
7731         /* grab first line of /etc/release */
7732         cp = s_fgets(title, sizeof (title), fp);
7733         if (cp) {
7734                 while (isspace(*cp))    /* remove leading spaces */
7735                         cp++;
7736         }
7737 
7738         (void) fclose(fp);
7739 
7740 out:
7741         cp = cp ? cp : "Oracle Solaris";
7742 
7743         BAM_DPRINTF(("%s: got title: %s\n", fcn, cp));
7744 
7745         return (cp);
7746 }
7747 
7748 char *
7749 get_special(char *mountp)
7750 {
7751         FILE            *mntfp;
7752         struct mnttab   mp = {0};
7753         struct mnttab   mpref = {0};
7754         int             error;
7755         int             ret;
7756         const char      *fcn = "get_special()";
7757 
7758         INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7759         if (mountp == NULL) {
7760                 bam_error(_("cannot get special file: NULL mount-point\n"));
7761                 return (NULL);
7762         }
7763 
7764         mntfp = fopen(MNTTAB, "r");
7765         error = errno;
7766         INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7767         if (mntfp == NULL) {
7768                 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7769                     strerror(error));
7770                 return (NULL);
7771         }
7772 
7773         if (*mountp == '\0')
7774                 mpref.mnt_mountp = "/";
7775         else
7776                 mpref.mnt_mountp = mountp;
7777 
7778         ret = getmntany(mntfp, &mp, &mpref);
7779         INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7780         if (ret != 0) {
7781                 (void) fclose(mntfp);
7782                 BAM_DPRINTF(("%s: Cannot get special file:  mount-point %s "
7783                     "not in mnttab\n", fcn, mountp));
7784                 return (NULL);
7785         }
7786         (void) fclose(mntfp);
7787 
7788         BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
7789 
7790         return (s_strdup(mp.mnt_special));
7791 }
7792 
7793 static void
7794 free_physarray(char **physarray, int n)
7795 {
7796         int                     i;
7797         const char              *fcn = "free_physarray()";
7798 
7799         assert(physarray);
7800         assert(n);
7801 
7802         BAM_DPRINTF(("%s: entering args: %d\n", fcn, n));
7803 
7804         for (i = 0; i < n; i++) {
7805                 free(physarray[i]);
7806         }
7807         free(physarray);
7808 
7809         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7810 }
7811 
7812 static int
7813 zfs_get_physical(char *special, char ***physarray, int *n)
7814 {
7815         char                    sdup[PATH_MAX];
7816         char                    cmd[PATH_MAX];
7817         char                    dsk[PATH_MAX];
7818         char                    *pool;
7819         filelist_t              flist = {0};
7820         line_t                  *lp;
7821         line_t                  *startlp;
7822         char                    *comp1;
7823         int                     i;
7824         int                     ret;
7825         const char              *fcn = "zfs_get_physical()";
7826 
7827         assert(special);
7828 
7829         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, special));
7830 
7831         INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7832         if (special[0] == '/') {
7833                 bam_error(_("invalid device for ZFS filesystem: %s\n"),
7834                     special);
7835                 return (-1);
7836         }
7837 
7838         (void) strlcpy(sdup, special, sizeof (sdup));
7839 
7840         pool = strtok(sdup, "/");
7841         INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7842         if (pool == NULL) {
7843                 bam_error(_("cannot derive ZFS pool from special: %s\n"),
7844                     special);
7845                 return (-1);
7846         }
7847 
7848         (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7849 
7850         ret = exec_cmd(cmd, &flist);
7851         INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7852         if (ret != 0) {
7853                 bam_error(_("cannot get zpool status for pool: %s\n"), pool);
7854                 return (-1);
7855         }
7856 
7857         INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7858         if (flist.head == NULL) {
7859                 bam_error(_("bad zpool status for pool=%s\n"), pool);
7860                 filelist_free(&flist);
7861                 return (-1);
7862         }
7863 
7864         for (lp = flist.head; lp; lp = lp->next) {
7865                 BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
7866                     fcn, lp->line));
7867                 comp1 = strtok(lp->line, " \t");
7868                 if (comp1 == NULL) {
7869                         free(lp->line);
7870                         lp->line = NULL;
7871                 } else {
7872                         comp1 = s_strdup(comp1);
7873                         free(lp->line);
7874                         lp->line = comp1;
7875                 }
7876         }
7877 
7878         for (lp = flist.head; lp; lp = lp->next) {
7879                 if (lp->line == NULL)
7880                         continue;
7881                 if (strcmp(lp->line, pool) == 0) {
7882                         BAM_DPRINTF(("%s: found pool name: %s in zpool "
7883                             "status\n", fcn, pool));
7884                         break;
7885                 }
7886         }
7887 
7888         if (lp == NULL) {
7889                 bam_error(_("no pool name %s in zpool status\n"), pool);
7890                 filelist_free(&flist);
7891                 return (-1);
7892         }
7893 
7894         startlp = lp->next;
7895         for (i = 0, lp = startlp; lp; lp = lp->next) {
7896                 if (lp->line == NULL)
7897                         continue;
7898                 if (strcmp(lp->line, "mirror") == 0)
7899                         continue;
7900                 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7901                         break;
7902                 i++;
7903                 BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
7904                     fcn, i));
7905         }
7906 
7907         if (i == 0) {
7908                 bam_error(_("no physical device in zpool status for pool=%s\n"),
7909                     pool);
7910                 filelist_free(&flist);
7911                 return (-1);
7912         }
7913 
7914         *n = i;
7915         *physarray = s_calloc(*n, sizeof (char *));
7916         for (i = 0, lp = startlp; lp; lp = lp->next) {
7917                 if (lp->line == NULL)
7918                         continue;
7919                 if (strcmp(lp->line, "mirror") == 0)
7920                         continue;
7921                 if (strcmp(lp->line, "errors:") == 0)
7922                         break;
7923                 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7924                     strncmp(lp->line, "/dev/rdsk/",
7925                     strlen("/dev/rdsk/")) != 0)  {
7926                         (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7927                             lp->line);
7928                 } else {
7929                         (void) strlcpy(dsk, lp->line, sizeof (dsk));
7930                 }
7931                 BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
7932                     fcn, dsk, pool));
7933                 (*physarray)[i++] = s_strdup(dsk);
7934         }
7935 
7936         assert(i == *n);
7937 
7938         filelist_free(&flist);
7939 
7940         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7941         return (0);
7942 }
7943 
7944 static int
7945 get_physical(char *menu_root, char ***physarray, int *n)
7946 {
7947         char                    *special;
7948         int                     ret;
7949         const char              *fcn = "get_physical()";
7950 
7951         assert(menu_root);
7952         assert(physarray);
7953         assert(n);
7954 
7955         *physarray = NULL;
7956         *n = 0;
7957 
7958         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_root));
7959 
7960         /* First get the device special file from /etc/mnttab */
7961         special = get_special(menu_root);
7962         INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7963         if (special == NULL) {
7964                 bam_error(_("cannot get special file for mount-point: %s\n"),
7965                     menu_root);
7966                 return (-1);
7967         }
7968 
7969         /* If already a physical device nothing to do */
7970         if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7971             strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7972                 BAM_DPRINTF(("%s: got physical device already directly for "
7973                     "menu_root=%s special=%s\n", fcn, menu_root, special));
7974                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7975                 *physarray = s_calloc(1, sizeof (char *));
7976                 (*physarray)[0] = special;
7977                 *n = 1;
7978                 return (0);
7979         }
7980 
7981         if (is_zfs(menu_root)) {
7982                 ret = zfs_get_physical(special, physarray, n);
7983         } else {
7984                 bam_error(_("cannot derive physical device for %s (%s), "
7985                     "unsupported filesystem\n"), menu_root, special);
7986                 ret = -1;
7987         }
7988 
7989         free(special);
7990 
7991         INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7992         if (ret == -1) {
7993                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7994         } else {
7995                 int     i;
7996                 assert (*n > 0);
7997                 for (i = 0; i < *n; i++) {
7998                         BAM_DPRINTF(("%s: returning physical=%s\n",
7999                             fcn, (*physarray)[i]));
8000                 }
8001         }
8002 
8003         return (ret);
8004 }
8005 
8006 static int
8007 is_bootdisk(char *osroot, char *physical)
8008 {
8009         int                     ret;
8010         char                    *grubroot;
8011         char                    *bootp;
8012         const char              *fcn = "is_bootdisk()";
8013 
8014         assert(osroot);
8015         assert(physical);
8016 
8017         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, physical));
8018 
8019         bootp = strstr(physical, "p0:boot");
8020         if (bootp)
8021                 *bootp = '\0';
8022         /*
8023          * We just want the BIOS mapping for menu disk.
8024          * Don't pass menu_root to get_grubroot() as the
8025          * check that it is used for is not relevant here.
8026          * The osroot is immaterial as well - it is only used to
8027          * to find create_diskmap script. Everything hinges on
8028          * "physical"
8029          */
8030         grubroot = get_grubroot(osroot, physical, NULL);
8031 
8032         INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
8033         if (grubroot == NULL) {
8034                 if (bam_verbose)
8035                         bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8036                             "disk: %s\n"), physical);
8037                 return (0);
8038         }
8039         ret = grubroot[3] == '0';
8040         free(grubroot);
8041 
8042         BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
8043 
8044         return (ret);
8045 }
8046 
8047 /*
8048  * Check if menu is on the boot device
8049  * Return 0 (false) on error
8050  */
8051 static int
8052 menu_on_bootdisk(char *osroot, char *menu_root)
8053 {
8054         char            **physarray;
8055         int             ret;
8056         int             n;
8057         int             i;
8058         int             on_bootdisk;
8059         const char      *fcn = "menu_on_bootdisk()";
8060 
8061         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8062 
8063         ret = get_physical(menu_root, &physarray, &n);
8064         INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
8065         if (ret != 0) {
8066                 bam_error(_("cannot get physical device special file for menu "
8067                     "root: %s\n"), menu_root);
8068                 return (0);
8069         }
8070 
8071         assert(physarray);
8072         assert(n > 0);
8073 
8074         on_bootdisk = 0;
8075         for (i = 0; i < n; i++) {
8076                 assert(strncmp(physarray[i], "/dev/dsk/",
8077                     strlen("/dev/dsk/")) == 0 ||
8078                     strncmp(physarray[i], "/dev/rdsk/",
8079                     strlen("/dev/rdsk/")) == 0);
8080 
8081                 BAM_DPRINTF(("%s: checking if phys-device=%s is on bootdisk\n",
8082                     fcn, physarray[i]));
8083                 if (is_bootdisk(osroot, physarray[i])) {
8084                         on_bootdisk = 1;
8085                         BAM_DPRINTF(("%s: phys-device=%s *IS* on bootdisk\n",
8086                             fcn, physarray[i]));
8087                 }
8088         }
8089 
8090         free_physarray(physarray, n);
8091 
8092         INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8093         INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8094         if (on_bootdisk) {
8095                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8096         } else {
8097                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8098         }
8099 
8100         return (on_bootdisk);
8101 }
8102 
8103 void
8104 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8105 {
8106         const char      *fcn = "bam_add_line()";
8107 
8108         assert(mp);
8109         assert(entry);
8110         assert(prev);
8111         assert(lp);
8112 
8113         lp->next = prev->next;
8114         if (prev->next) {
8115                 BAM_DPRINTF(("%s: previous next exists\n", fcn));
8116                 prev->next->prev = lp;
8117         } else {
8118                 BAM_DPRINTF(("%s: previous next does not exist\n", fcn));
8119         }
8120         prev->next = lp;
8121         lp->prev = prev;
8122 
8123         if (entry->end == prev) {
8124                 BAM_DPRINTF(("%s: last line in entry\n", fcn));
8125                 entry->end = lp;
8126         }
8127         if (mp->end == prev) {
8128                 assert(lp->next == NULL);
8129                 mp->end = lp;
8130                 BAM_DPRINTF(("%s: last line in menu\n", fcn));
8131         }
8132 }
8133 
8134 /*
8135  * look for matching bootadm entry with specified parameters
8136  * Here are the rules (based on existing usage):
8137  * - If title is specified, match on title only
8138  * - Else, match on root/findroot, kernel, and module.
8139  *   Note that, if root_opt is non-zero, the absence of
8140  *   root line is considered a match.
8141  */
8142 static entry_t *
8143 find_boot_entry(
8144         menu_t *mp,
8145         char *title,
8146         char *kernel,
8147         char *findroot,
8148         char *root,
8149         char *module,
8150         int root_opt,
8151         int *entry_num)
8152 {
8153         int             i;
8154         line_t          *lp;
8155         entry_t         *ent;
8156         const char      *fcn = "find_boot_entry()";
8157 
8158         if (entry_num)
8159                 *entry_num = BAM_ERROR;
8160 
8161         /* find matching entry */
8162         for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8163                 lp = ent->start;
8164 
8165                 /* first line of entry must be bootadm comment */
8166                 lp = ent->start;
8167                 if (lp->flags != BAM_COMMENT ||
8168                     strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8169                         continue;
8170                 }
8171 
8172                 /* advance to title line */
8173                 lp = lp->next;
8174                 if (title) {
8175                         if (lp->flags == BAM_TITLE && lp->arg &&
8176                             strcmp(lp->arg, title) == 0) {
8177                                 BAM_DPRINTF(("%s: matched title: %s\n",
8178                                     fcn, title));
8179                                 break;
8180                         }
8181                         BAM_DPRINTF(("%s: no match title: %s, %s\n",
8182                             fcn, title, lp->arg));
8183                         continue;       /* check title only */
8184                 }
8185 
8186                 lp = lp->next;       /* advance to root line */
8187                 if (lp == NULL) {
8188                         continue;
8189                 } else if (lp->cmd != NULL &&
8190                     strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8191                         INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8192                             findroot = NULL);
8193                         if (findroot == NULL) {
8194                                 BAM_DPRINTF(("%s: no match line has findroot, "
8195                                     "we don't: %s\n", fcn, lp->arg));
8196                                 continue;
8197                         }
8198                         /* findroot command found, try match  */
8199                         if (strcmp(lp->arg, findroot) != 0) {
8200                                 BAM_DPRINTF(("%s: no match findroot: %s, %s\n",
8201                                     fcn, findroot, lp->arg));
8202                                 continue;
8203                         }
8204                         BAM_DPRINTF(("%s: matched findroot: %s\n",
8205                             fcn, findroot));
8206                         lp = lp->next;       /* advance to kernel line */
8207                 } else if (lp->cmd != NULL &&
8208                     strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8209                         INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8210                         if (root == NULL) {
8211                                 BAM_DPRINTF(("%s: no match, line has root, we "
8212                                     "don't: %s\n", fcn, lp->arg));
8213                                 continue;
8214                         }
8215                         /* root cmd found, try match */
8216                         if (strcmp(lp->arg, root) != 0) {
8217                                 BAM_DPRINTF(("%s: no match root: %s, %s\n",
8218                                     fcn, root, lp->arg));
8219                                 continue;
8220                         }
8221                         BAM_DPRINTF(("%s: matched root: %s\n", fcn, root));
8222                         lp = lp->next;       /* advance to kernel line */
8223                 } else {
8224                         INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8225                             root_opt = 0);
8226                         INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8227                             root_opt = 1);
8228                         /* no root command, see if root is optional */
8229                         if (root_opt == 0) {
8230                                 BAM_DPRINTF(("%s: root NOT optional\n", fcn));
8231                                 continue;
8232                         }
8233                         BAM_DPRINTF(("%s: root IS optional\n", fcn));
8234                 }
8235 
8236                 if (lp == NULL || lp->next == NULL) {
8237                         continue;
8238                 }
8239 
8240                 if (kernel &&
8241                     (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8242                         if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8243                             !(ent->flags & BAM_ENTRY_DBOOT) ||
8244                             strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8245                                 continue;
8246 
8247                         ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8248 
8249                 }
8250                 BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn,
8251                     kernel, lp->arg));
8252 
8253                 /*
8254                  * Check for matching module entry (failsafe or normal).
8255                  * If it fails to match, we go around the loop again.
8256                  * For xpv entries, there are two module lines, so we
8257                  * do the check twice.
8258                  */
8259                 lp = lp->next;       /* advance to module line */
8260                 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8261                     (((lp = lp->next) != NULL) &&
8262                     check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8263                         /* match found */
8264                         BAM_DPRINTF(("%s: module match: %s, %s\n", fcn,
8265                             module, lp->arg));
8266                         break;
8267                 }
8268 
8269                 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8270                     (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8271                     strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8272                         ent->flags |= BAM_ENTRY_UPGFSMODULE;
8273                         break;
8274                 }
8275 
8276         }
8277 
8278         if (ent && entry_num) {
8279                 *entry_num = i;
8280         }
8281 
8282         if (ent) {
8283                 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8284         } else {
8285                 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_ERROR));
8286         }
8287         return (ent);
8288 }
8289 
8290 static int
8291 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8292     char *kernel, char *mod_kernel, char *module, int root_opt)
8293 {
8294         int             i;
8295         int             change_kernel = 0;
8296         entry_t         *ent;
8297         line_t          *lp;
8298         line_t          *tlp;
8299         char            linebuf[BAM_MAXLINE];
8300         const char      *fcn = "update_boot_entry()";
8301 
8302         /* note: don't match on title, it's updated on upgrade */
8303         ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8304             root_opt, &i);
8305         if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8306                 /*
8307                  * We may be upgrading a kernel from multiboot to
8308                  * directboot.  Look for a multiboot entry. A multiboot
8309                  * entry will not have a findroot line.
8310                  */
8311                 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8312                     MULTIBOOT_ARCHIVE, root_opt, &i);
8313                 if (ent != NULL) {
8314                         BAM_DPRINTF(("%s: upgrading entry from dboot to "
8315                             "multiboot: root = %s\n", fcn, root));
8316                         change_kernel = 1;
8317                 }
8318         } else if (ent) {
8319                 BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8320                     fcn, findroot));
8321         }
8322 
8323         if (ent == NULL) {
8324                 BAM_DPRINTF(("%s: boot entry not found in menu. Creating "
8325                     "new entry, findroot = %s\n", fcn, findroot));
8326                 return (add_boot_entry(mp, title, findroot,
8327                     kernel, mod_kernel, module, NULL));
8328         }
8329 
8330         /* replace title of existing entry and update findroot line */
8331         lp = ent->start;
8332         lp = lp->next;       /* title line */
8333         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8334             menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8335         free(lp->arg);
8336         free(lp->line);
8337         lp->arg = s_strdup(title);
8338         lp->line = s_strdup(linebuf);
8339         BAM_DPRINTF(("%s: changing title to: %s\n", fcn, title));
8340 
8341         tlp = lp;       /* title line */
8342         lp = lp->next;       /* root line */
8343 
8344         /* if no root or findroot command, create a new line_t */
8345         if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8346             strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8347                 lp = s_calloc(1, sizeof (line_t));
8348                 bam_add_line(mp, ent, tlp, lp);
8349         } else {
8350                 if (lp->cmd != NULL)
8351                         free(lp->cmd);
8352 
8353                 free(lp->sep);
8354                 free(lp->arg);
8355                 free(lp->line);
8356         }
8357 
8358         lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8359         lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8360         lp->arg = s_strdup(findroot);
8361         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8362             menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8363         lp->line = s_strdup(linebuf);
8364         BAM_DPRINTF(("%s: adding findroot line: %s\n", fcn, findroot));
8365 
8366         /* kernel line */
8367         lp = lp->next;
8368 
8369         if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8370                 char            *params = NULL;
8371 
8372                 params = strstr(lp->line, "-s");
8373                 if (params != NULL)
8374                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8375                             menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8376                             kernel, params+2);
8377                 else
8378                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8379                             menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8380                             kernel);
8381 
8382                 if (lp->cmd != NULL)
8383                         free(lp->cmd);
8384 
8385                 free(lp->arg);
8386                 free(lp->line);
8387                 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8388                 lp->arg = s_strdup(strstr(linebuf, "/"));
8389                 lp->line = s_strdup(linebuf);
8390                 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8391                 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8392                     fcn, lp->prev->cmd));
8393         }
8394 
8395         if (change_kernel) {
8396                 /*
8397                  * We're upgrading from multiboot to directboot.
8398                  */
8399                 if (lp->cmd != NULL &&
8400                     strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8401                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8402                             menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8403                             kernel);
8404                         free(lp->cmd);
8405                         free(lp->arg);
8406                         free(lp->line);
8407                         lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8408                         lp->arg = s_strdup(kernel);
8409                         lp->line = s_strdup(linebuf);
8410                         lp = lp->next;
8411                         BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8412                             fcn, kernel));
8413                 }
8414                 if (lp->cmd != NULL &&
8415                     strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8416                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8417                             menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8418                             module);
8419                         free(lp->cmd);
8420                         free(lp->arg);
8421                         free(lp->line);
8422                         lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8423                         lp->arg = s_strdup(module);
8424                         lp->line = s_strdup(linebuf);
8425                         lp = lp->next;
8426                         BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8427                             fcn, module));
8428                 }
8429         }
8430 
8431         /* module line */
8432         lp = lp->next;
8433 
8434         if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8435                 if (lp->cmd != NULL &&
8436                     strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8437                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8438                             menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8439                             module);
8440                         free(lp->cmd);
8441                         free(lp->arg);
8442                         free(lp->line);
8443                         lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8444                         lp->arg = s_strdup(module);
8445                         lp->line = s_strdup(linebuf);
8446                         lp = lp->next;
8447                         ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8448                         BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8449                             fcn, module));
8450                 }
8451         }
8452 
8453         BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8454         return (i);
8455 }
8456 
8457 int
8458 root_optional(char *osroot, char *menu_root)
8459 {
8460         char                    *ospecial;
8461         char                    *mspecial;
8462         char                    *slash;
8463         int                     root_opt;
8464         int                     ret1;
8465         int                     ret2;
8466         const char              *fcn = "root_optional()";
8467 
8468         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8469 
8470         /*
8471          * For all filesystems except ZFS, a straight compare of osroot
8472          * and menu_root will tell us if root is optional.
8473          * For ZFS, the situation is complicated by the fact that
8474          * menu_root and osroot are always different
8475          */
8476         ret1 = is_zfs(osroot);
8477         ret2 = is_zfs(menu_root);
8478         INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8479         if (!ret1 || !ret2) {
8480                 BAM_DPRINTF(("%s: one or more non-ZFS filesystems (%s, %s)\n",
8481                     fcn, osroot, menu_root));
8482                 root_opt = (strcmp(osroot, menu_root) == 0);
8483                 goto out;
8484         }
8485 
8486         ospecial = get_special(osroot);
8487         INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8488         if (ospecial == NULL) {
8489                 bam_error(_("failed to get special file for osroot: %s\n"),
8490                     osroot);
8491                 return (0);
8492         }
8493         BAM_DPRINTF(("%s: ospecial=%s for osroot=%s\n", fcn, ospecial, osroot));
8494 
8495         mspecial = get_special(menu_root);
8496         INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8497         if (mspecial == NULL) {
8498                 bam_error(_("failed to get special file for menu_root: %s\n"),
8499                     menu_root);
8500                 free(ospecial);
8501                 return (0);
8502         }
8503         BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8504             fcn, mspecial, menu_root));
8505 
8506         slash = strchr(ospecial, '/');
8507         if (slash)
8508                 *slash = '\0';
8509         BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8510             fcn, ospecial, osroot));
8511 
8512         root_opt = (strcmp(ospecial, mspecial) == 0);
8513 
8514         free(ospecial);
8515         free(mspecial);
8516 
8517 out:
8518         INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8519         INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8520         if (root_opt) {
8521                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8522         } else {
8523                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8524         }
8525 
8526         return (root_opt);
8527 }
8528 
8529 /*ARGSUSED*/
8530 static error_t
8531 update_entry(menu_t *mp, char *menu_root, char *osdev)
8532 {
8533         int             entry;
8534         char            *grubsign;
8535         char            *grubroot;
8536         char            *title;
8537         char            osroot[PATH_MAX];
8538         char            *failsafe_kernel = NULL;
8539         struct stat     sbuf;
8540         char            failsafe[256];
8541         char            failsafe_64[256];
8542         int             ret;
8543         const char      *fcn = "update_entry()";
8544 
8545         assert(mp);
8546         assert(menu_root);
8547         assert(osdev);
8548         assert(bam_root);
8549 
8550         BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, menu_root, osdev,
8551             bam_root));
8552 
8553         (void) strlcpy(osroot, bam_root, sizeof (osroot));
8554 
8555         title = get_title(osroot);
8556         assert(title);
8557 
8558         grubsign = get_grubsign(osroot, osdev);
8559         INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8560         if (grubsign == NULL) {
8561                 bam_error(_("failed to get grubsign for root: %s, device %s\n"),
8562                     osroot, osdev);
8563                 return (BAM_ERROR);
8564         }
8565 
8566         /*
8567          * It is not a fatal error if get_grubroot() fails
8568          * We no longer rely on biosdev to populate the
8569          * menu
8570          */
8571         grubroot = get_grubroot(osroot, osdev, menu_root);
8572         INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8573         if (grubroot) {
8574                 BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8575                     "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8576         } else {
8577                 BAM_DPRINTF(("%s: get_grubroot failed. osroot=%s, osdev=%s, "
8578                     "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8579         }
8580 
8581         /* add the entry for normal Solaris */
8582         INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8583             bam_direct = BAM_DIRECT_MULTIBOOT);
8584         if (bam_direct == BAM_DIRECT_DBOOT) {
8585                 entry = update_boot_entry(mp, title, grubsign, grubroot,
8586                     (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8587                     NULL, DIRECT_BOOT_ARCHIVE,
8588                     root_optional(osroot, menu_root));
8589                 BAM_DPRINTF(("%s: updated boot entry bam_zfs=%d, "
8590                     "grubsign = %s\n", fcn, bam_zfs, grubsign));
8591                 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8592                         (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8593                             grubroot, XEN_MENU, bam_zfs ?
8594                             XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8595                             DIRECT_BOOT_ARCHIVE,
8596                             root_optional(osroot, menu_root));
8597                         BAM_DPRINTF(("%s: updated HV entry bam_zfs=%d, "
8598                             "grubsign = %s\n", fcn, bam_zfs, grubsign));
8599                 }
8600         } else {
8601                 entry = update_boot_entry(mp, title, grubsign, grubroot,
8602                     MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8603                     root_optional(osroot, menu_root));
8604 
8605                 BAM_DPRINTF(("%s: updated MULTIBOOT entry grubsign = %s\n",
8606                     fcn, grubsign));
8607         }
8608 
8609         /*
8610          * Add the entry for failsafe archive.  On a bfu'd system, the
8611          * failsafe may be different than the installed kernel.
8612          */
8613         (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8614             osroot, FAILSAFE_ARCHIVE_32);
8615         (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8616             osroot, FAILSAFE_ARCHIVE_64);
8617 
8618         /*
8619          * Check if at least one of the two archives exists
8620          * Using $ISADIR as the default line, we have an entry which works
8621          * for both the cases.
8622          */
8623 
8624         if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8625 
8626                 /* Figure out where the kernel line should point */
8627                 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8628                     DIRECT_BOOT_FAILSAFE_32);
8629                 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8630                     osroot, DIRECT_BOOT_FAILSAFE_64);
8631                 if (stat(failsafe, &sbuf) == 0 ||
8632                     stat(failsafe_64, &sbuf) == 0) {
8633                         failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8634                 } else {
8635                         (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8636                             osroot, MULTI_BOOT_FAILSAFE);
8637                         if (stat(failsafe, &sbuf) == 0) {
8638                                 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8639                         }
8640                 }
8641                 if (failsafe_kernel != NULL) {
8642                         (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8643                             grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8644                             root_optional(osroot, menu_root));
8645                         BAM_DPRINTF(("%s: updated FAILSAFE entry "
8646                             "failsafe_kernel = %s\n", fcn, failsafe_kernel));
8647                 }
8648         }
8649         free(grubroot);
8650 
8651         INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8652         if (entry == BAM_ERROR) {
8653                 bam_error(_("failed to add boot entry with title=%s, grub "
8654                     "signature=%s\n"), title, grubsign);
8655                 free(grubsign);
8656                 return (BAM_ERROR);
8657         }
8658         free(grubsign);
8659 
8660         update_numbering(mp);
8661         ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8662         INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8663         if (ret == BAM_ERROR) {
8664                 bam_error(_("failed to set GRUB menu default to %d\n"), entry);
8665         }
8666         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8667         return (BAM_WRITE);
8668 }
8669 
8670 static void
8671 save_default_entry(menu_t *mp, const char *which)
8672 {
8673         int             lineNum;
8674         int             entryNum;
8675         int             entry = 0;      /* default is 0 */
8676         char            linebuf[BAM_MAXLINE];
8677         line_t          *lp = mp->curdefault;
8678         const char      *fcn = "save_default_entry()";
8679 
8680         if (mp->start) {
8681                 lineNum = mp->end->lineNum;
8682                 entryNum = mp->end->entryNum;
8683         } else {
8684                 lineNum = LINE_INIT;
8685                 entryNum = ENTRY_INIT;
8686         }
8687 
8688         if (lp)
8689                 entry = s_strtol(lp->arg);
8690 
8691         (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8692         BAM_DPRINTF(("%s: saving default to: %s\n", fcn, linebuf));
8693         line_parser(mp, linebuf, &lineNum, &entryNum);
8694         BAM_DPRINTF(("%s: saved default to lineNum=%d, entryNum=%d\n", fcn,
8695             lineNum, entryNum));
8696 }
8697 
8698 static void
8699 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8700 {
8701         int             entry;
8702         char            *str;
8703         const char      *fcn = "restore_default_entry()";
8704 
8705         if (lp == NULL) {
8706                 BAM_DPRINTF(("%s: NULL saved default\n", fcn));
8707                 return;         /* nothing to restore */
8708         }
8709 
8710         BAM_DPRINTF(("%s: saved default string: %s\n", fcn, which));
8711 
8712         str = lp->arg + strlen(which);
8713         entry = s_strtol(str);
8714         (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8715 
8716         BAM_DPRINTF(("%s: restored default to entryNum: %d\n", fcn, entry));
8717 
8718         /* delete saved old default line */
8719         unlink_line(mp, lp);
8720         line_free(lp);
8721 }
8722 
8723 /*
8724  * This function is for supporting reboot with args.
8725  * The opt value can be:
8726  * NULL         delete temp entry, if present
8727  * entry=<n>      switches default entry to <n>
8728  * else         treated as boot-args and setup a temperary menu entry
8729  *              and make it the default
8730  * Note that we are always rebooting the current OS instance
8731  * so osroot == / always.
8732  */
8733 #define REBOOT_TITLE    "Solaris_reboot_transient"
8734 
8735 /*ARGSUSED*/
8736 static error_t
8737 update_temp(menu_t *mp, char *dummy, char *opt)
8738 {
8739         int             entry;
8740         char            *osdev;
8741         char            *fstype;
8742         char            *sign;
8743         char            *opt_ptr;
8744         char            *path;
8745         char            kernbuf[BUFSIZ];
8746         char            args_buf[BUFSIZ];
8747         char            signbuf[PATH_MAX];
8748         int             ret;
8749         const char      *fcn = "update_temp()";
8750 
8751         assert(mp);
8752         assert(dummy == NULL);
8753 
8754         /* opt can be NULL */
8755         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt ? opt : "<NULL>"));
8756         BAM_DPRINTF(("%s: bam_alt_root: %d, bam_root: %s\n", fcn,
8757             bam_alt_root, bam_root));
8758 
8759         if (bam_alt_root || bam_rootlen != 1 ||
8760             strcmp(bam_root, "/") != 0 ||
8761             strcmp(rootbuf, "/") != 0) {
8762                 bam_error(_("an alternate root (%s) cannot be used with this "
8763                     "sub-command\n"), bam_root);
8764                 return (BAM_ERROR);
8765         }
8766 
8767         /* If no option, delete exiting reboot menu entry */
8768         if (opt == NULL) {
8769                 entry_t         *ent;
8770                 BAM_DPRINTF(("%s: opt is NULL\n", fcn));
8771                 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8772                     NULL, NULL, 0, &entry);
8773                 if (ent == NULL) {      /* not found is ok */
8774                         BAM_DPRINTF(("%s: transient entry not found\n", fcn));
8775                         return (BAM_SUCCESS);
8776                 }
8777                 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8778                 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8779                 mp->olddefault = NULL;
8780                 BAM_DPRINTF(("%s: restored old default\n", fcn));
8781                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8782                 return (BAM_WRITE);
8783         }
8784 
8785         /* if entry= is specified, set the default entry */
8786         if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8787                 int entryNum = s_strtol(opt + strlen("entry="));
8788                 BAM_DPRINTF(("%s: opt has entry=: %s\n", fcn, opt));
8789                 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8790                         /* this is entry=# option */
8791                         ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8792                         BAM_DPRINTF(("%s: default set to %d, "
8793                             "set_default ret=%d\n", fcn, entry, ret));
8794                         return (ret);
8795                 } else {
8796                         bam_error(_("failed to set GRUB menu default to %d\n"),
8797                             entryNum);
8798                         return (BAM_ERROR);
8799                 }
8800         }
8801 
8802         /*
8803          * add a new menu entry based on opt and make it the default
8804          */
8805 
8806         fstype = get_fstype("/");
8807         INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8808         if (fstype == NULL) {
8809                 bam_error(_("failed to determine filesystem type for \"/\". "
8810                     "Reboot with \narguments failed.\n"));
8811                 return (BAM_ERROR);
8812         }
8813 
8814         osdev = get_special("/");
8815         INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8816         if (osdev == NULL) {
8817                 free(fstype);
8818                 bam_error(_("failed to find device special file for \"/\". "
8819                     "Reboot with \narguments failed.\n"));
8820                 return (BAM_ERROR);
8821         }
8822 
8823         sign = find_existing_sign("/", osdev, fstype);
8824         INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8825         if (sign == NULL) {
8826                 free(fstype);
8827                 free(osdev);
8828                 bam_error(_("failed to find boot signature. Reboot with "
8829                     "arguments failed.\n"));
8830                 return (BAM_ERROR);
8831         }
8832 
8833         free(osdev);
8834         (void) strlcpy(signbuf, sign, sizeof (signbuf));
8835         free(sign);
8836 
8837         assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8838             strchr(signbuf, ')') == NULL);
8839 
8840         /*
8841          * There is no alternate root while doing reboot with args
8842          * This version of bootadm is only delivered with a DBOOT
8843          * version of Solaris.
8844          */
8845         INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8846         if (bam_direct != BAM_DIRECT_DBOOT) {
8847                 free(fstype);
8848                 bam_error(_("the root filesystem is not a dboot Solaris "
8849                     "instance. \nThis version of bootadm is not supported "
8850                     "on this version of Solaris.\n"));
8851                 return (BAM_ERROR);
8852         }
8853 
8854         /* add an entry for Solaris reboot */
8855         if (opt[0] == '-') {
8856                 /* It's an option - first see if boot-file is set */
8857                 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8858                 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8859                 if (ret != BAM_SUCCESS) {
8860                         free(fstype);
8861                         bam_error(_("reboot with arguments: error querying "
8862                             "current boot-file settings\n"));
8863                         return (BAM_ERROR);
8864                 }
8865                 if (kernbuf[0] == '\0')
8866                         (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8867                             sizeof (kernbuf));
8868                 /*
8869                  * If this is a zfs file system and kernbuf does not
8870                  * have "-B $ZFS-BOOTFS" string yet, add it.
8871                  */
8872                 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8873                         (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8874                         (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8875                 }
8876                 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8877                 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8878                 BAM_DPRINTF(("%s: reboot with args, option specified: "
8879                     "kern=%s\n", fcn, kernbuf));
8880         } else if (opt[0] == '/') {
8881                 /* It's a full path, so write it out. */
8882                 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8883 
8884                 /*
8885                  * If someone runs:
8886                  *
8887                  *      # eeprom boot-args='-kd'
8888                  *      # reboot /platform/i86pc/kernel/unix
8889                  *
8890                  * we want to use the boot-args as part of the boot
8891                  * line.  On the other hand, if someone runs:
8892                  *
8893                  *      # reboot "/platform/i86pc/kernel/unix -kd"
8894                  *
8895                  * we don't need to mess with boot-args.  If there's
8896                  * no space in the options string, assume we're in the
8897                  * first case.
8898                  */
8899                 if (strchr(opt, ' ') == NULL) {
8900                         ret = get_kernel(mp, ARGS_CMD, args_buf,
8901                             sizeof (args_buf));
8902                         INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8903                         if (ret != BAM_SUCCESS) {
8904                                 free(fstype);
8905                                 bam_error(_("reboot with arguments: error "
8906                                     "querying current boot-args settings\n"));
8907                                 return (BAM_ERROR);
8908                         }
8909 
8910                         if (args_buf[0] != '\0') {
8911                                 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8912                                 (void) strlcat(kernbuf, args_buf,
8913                                     sizeof (kernbuf));
8914                         }
8915                 }
8916                 BAM_DPRINTF(("%s: reboot with args, abspath specified: "
8917                     "kern=%s\n", fcn, kernbuf));
8918         } else {
8919                 /*
8920                  * It may be a partial path, or it may be a partial
8921                  * path followed by options.  Assume that only options
8922                  * follow a space.  If someone sends us a kernel path
8923                  * that includes a space, they deserve to be broken.
8924                  */
8925                 opt_ptr = strchr(opt, ' ');
8926                 if (opt_ptr != NULL) {
8927                         *opt_ptr = '\0';
8928                 }
8929 
8930                 path = expand_path(opt);
8931                 if (path != NULL) {
8932                         (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8933                         free(path);
8934 
8935                         /*
8936                          * If there were options given, use those.
8937                          * Otherwise, copy over the default options.
8938                          */
8939                         if (opt_ptr != NULL) {
8940                                 /* Restore the space in opt string */
8941                                 *opt_ptr = ' ';
8942                                 (void) strlcat(kernbuf, opt_ptr,
8943                                     sizeof (kernbuf));
8944                         } else {
8945                                 ret = get_kernel(mp, ARGS_CMD, args_buf,
8946                                     sizeof (args_buf));
8947                                 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8948                                     ret = BAM_ERROR);
8949                                 if (ret != BAM_SUCCESS) {
8950                                         free(fstype);
8951                                         bam_error(_("reboot with arguments: "
8952                                             "error querying current boot-args "
8953                                             "settings\n"));
8954                                         return (BAM_ERROR);
8955                                 }
8956 
8957                                 if (args_buf[0] != '\0') {
8958                                         (void) strlcat(kernbuf, " ",
8959                                             sizeof (kernbuf));
8960                                         (void) strlcat(kernbuf,
8961                                             args_buf, sizeof (kernbuf));
8962                                 }
8963                         }
8964                         BAM_DPRINTF(("%s: resolved partial path: %s\n",
8965                             fcn, kernbuf));
8966                 } else {
8967                         free(fstype);
8968                         bam_error(_("unable to expand %s to a full file"
8969                             " path.\n"), opt);
8970                         bam_print_stderr(_("Rebooting with default kernel "
8971                             "and options.\n"));
8972                         return (BAM_ERROR);
8973                 }
8974         }
8975         free(fstype);
8976         entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8977             NULL, NULL, NULL);
8978         INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8979         if (entry == BAM_ERROR) {
8980                 bam_error(_("Cannot update menu. Cannot reboot with "
8981                     "requested arguments\n"));
8982                 return (BAM_ERROR);
8983         }
8984 
8985         save_default_entry(mp, BAM_OLDDEF);
8986         ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8987         INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8988         if (ret == BAM_ERROR) {
8989                 bam_error(_("reboot with arguments: setting GRUB menu default "
8990                     "to %d failed\n"), entry);
8991         }
8992         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8993         return (BAM_WRITE);
8994 }
8995 
8996 error_t
8997 set_global(menu_t *mp, char *globalcmd, int val)
8998 {
8999         line_t          *lp;
9000         line_t          *found;
9001         line_t          *last;
9002         char            *cp;
9003         char            *str;
9004         char            prefix[BAM_MAXLINE];
9005         size_t          len;
9006         const char      *fcn = "set_global()";
9007 
9008         assert(mp);
9009         assert(globalcmd);
9010 
9011         if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
9012                 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
9013                 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
9014                 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
9015                 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
9016                         (void) snprintf(prefix, sizeof (prefix), "%d", val);
9017                         bam_error(_("invalid boot entry number: %s\n"), prefix);
9018                         return (BAM_ERROR);
9019                 }
9020         }
9021 
9022         found = last = NULL;
9023         for (lp = mp->start; lp; lp = lp->next) {
9024                 if (lp->flags != BAM_GLOBAL)
9025                         continue;
9026 
9027                 last = lp; /* track the last global found */
9028 
9029                 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
9030                 if (lp->cmd == NULL) {
9031                         bam_error(_("no command at line %d\n"), lp->lineNum);
9032                         continue;
9033                 }
9034                 if (strcmp(globalcmd, lp->cmd) != 0)
9035                         continue;
9036 
9037                 BAM_DPRINTF(("%s: found matching global command: %s\n",
9038                     fcn, globalcmd));
9039 
9040                 if (found) {
9041                         bam_error(_("duplicate command %s at line %d of "
9042                             "%sboot/grub/menu.lst\n"), globalcmd,
9043                             lp->lineNum, bam_root);
9044                 }
9045                 found = lp;
9046         }
9047 
9048         if (found == NULL) {
9049                 lp = s_calloc(1, sizeof (line_t));
9050                 if (last == NULL) {
9051                         lp->next = mp->start;
9052                         mp->start = lp;
9053                         mp->end = (mp->end) ? mp->end : lp;
9054                 } else {
9055                         lp->next = last->next;
9056                         last->next = lp;
9057                         if (lp->next == NULL)
9058                                 mp->end = lp;
9059                 }
9060                 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
9061                 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
9062                 len += 10;      /* val < 10 digits */
9063                 lp->line = s_calloc(1, len);
9064                 (void) snprintf(lp->line, len, "%s%s%d",
9065                     globalcmd, menu_cmds[SEP_CMD], val);
9066                 BAM_DPRINTF(("%s: wrote new global line: %s\n", fcn, lp->line));
9067                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9068                 return (BAM_WRITE);
9069         }
9070 
9071         /*
9072          * We are changing an existing entry. Retain any prefix whitespace,
9073          * but overwrite everything else. This preserves tabs added for
9074          * readability.
9075          */
9076         str = found->line;
9077         cp = prefix;
9078         while (*str == ' ' || *str == '\t')
9079                 *(cp++) = *(str++);
9080         *cp = '\0'; /* Terminate prefix */
9081         len = strlen(prefix) + strlen(globalcmd);
9082         len += strlen(menu_cmds[SEP_CMD]) + 10;
9083 
9084         free(found->line);
9085         found->line = s_calloc(1, len);
9086         (void) snprintf(found->line, len,
9087             "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
9088 
9089         BAM_DPRINTF(("%s: replaced global line with: %s\n", fcn, found->line));
9090         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9091         return (BAM_WRITE); /* need a write to menu */
9092 }
9093 
9094 /*
9095  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
9096  * expand it to a full unix path.  The calling function is expected to
9097  * output a message if an error occurs and NULL is returned.
9098  */
9099 static char *
9100 expand_path(const char *partial_path)
9101 {
9102         int             new_path_len;
9103         char            *new_path;
9104         char            new_path2[PATH_MAX];
9105         struct stat     sb;
9106         const char      *fcn = "expand_path()";
9107 
9108         new_path_len = strlen(partial_path) + 64;
9109         new_path = s_calloc(1, new_path_len);
9110 
9111         /* First, try the simplest case - something like "kernel/unix" */
9112         (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
9113             partial_path);
9114         if (stat(new_path, &sb) == 0) {
9115                 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9116                 return (new_path);
9117         }
9118 
9119         if (strcmp(partial_path, "kmdb") == 0) {
9120                 (void) snprintf(new_path, new_path_len, "%s -k",
9121                     DIRECT_BOOT_KERNEL);
9122                 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9123                 return (new_path);
9124         }
9125 
9126         /*
9127          * We've quickly reached unsupported usage.  Try once more to
9128          * see if we were just given a glom name.
9129          */
9130         (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9131             partial_path);
9132         (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9133             partial_path);
9134         if (stat(new_path, &sb) == 0) {
9135                 if (stat(new_path2, &sb) == 0) {
9136                         /*
9137                          * We matched both, so we actually
9138                          * want to write the $ISADIR version.
9139                          */
9140                         (void) snprintf(new_path, new_path_len,
9141                             "/platform/i86pc/kernel/%s/$ISADIR/unix",
9142                             partial_path);
9143                 }
9144                 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9145                 return (new_path);
9146         }
9147 
9148         free(new_path);
9149         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9150         return (NULL);
9151 }
9152 
9153 /*
9154  * The kernel cmd and arg have been changed, so
9155  * check whether the archive line needs to change.
9156  */
9157 static void
9158 set_archive_line(entry_t *entryp, line_t *kernelp)
9159 {
9160         line_t          *lp = entryp->start;
9161         char            *new_archive;
9162         menu_cmd_t      m_cmd;
9163         const char      *fcn = "set_archive_line()";
9164 
9165         for (; lp != NULL; lp = lp->next) {
9166                 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9167                     sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9168                         break;
9169                 }
9170 
9171                 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9172                 if (lp == entryp->end) {
9173                         BAM_DPRINTF(("%s: no module/archive line for entry: "
9174                             "%d\n", fcn, entryp->entryNum));
9175                         return;
9176                 }
9177         }
9178         INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9179         if (lp == NULL) {
9180                 BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9181                     fcn, entryp->entryNum));
9182                 return;
9183         }
9184 
9185         if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9186                 new_archive = DIRECT_BOOT_ARCHIVE;
9187                 m_cmd = MODULE_DOLLAR_CMD;
9188         } else if (strstr(kernelp->arg, "amd64") != NULL) {
9189                 new_archive = DIRECT_BOOT_ARCHIVE_64;
9190                 m_cmd = MODULE_CMD;
9191         } else {
9192                 new_archive = DIRECT_BOOT_ARCHIVE_32;
9193                 m_cmd = MODULE_CMD;
9194         }
9195 
9196         if (strcmp(lp->arg, new_archive) == 0) {
9197                 BAM_DPRINTF(("%s: no change for line: %s\n", fcn, lp->arg));
9198                 return;
9199         }
9200 
9201         if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9202                 free(lp->cmd);
9203                 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9204         }
9205 
9206         free(lp->arg);
9207         lp->arg = s_strdup(new_archive);
9208         update_line(lp);
9209         BAM_DPRINTF(("%s: replaced for line: %s\n", fcn, lp->line));
9210 }
9211 
9212 /*
9213  * Title for an entry to set properties that once went in bootenv.rc.
9214  */
9215 #define BOOTENV_RC_TITLE        "Solaris bootenv rc"
9216 
9217 /*
9218  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9219  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
9220  * string, reset the value to the default.  If path is a non-zero-length
9221  * string, set the kernel or arguments.
9222  */
9223 static error_t
9224 get_set_kernel(
9225         menu_t *mp,
9226         menu_cmd_t optnum,
9227         char *path,
9228         char *buf,
9229         size_t bufsize)
9230 {
9231         int             entryNum;
9232         int             rv = BAM_SUCCESS;
9233         int             free_new_path = 0;
9234         entry_t         *entryp;
9235         line_t          *ptr;
9236         line_t          *kernelp;
9237         char            *new_arg;
9238         char            *old_args;
9239         char            *space;
9240         char            *new_path;
9241         char            old_space;
9242         size_t          old_kernel_len = 0;
9243         size_t          new_str_len;
9244         char            *fstype;
9245         char            *osdev;
9246         char            *sign;
9247         char            signbuf[PATH_MAX];
9248         int             ret;
9249         const char      *fcn = "get_set_kernel()";
9250 
9251         assert(bufsize > 0);
9252 
9253         ptr = kernelp = NULL;
9254         new_arg = old_args = space = NULL;
9255         new_path = NULL;
9256         buf[0] = '\0';
9257 
9258         INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9259             bam_direct = BAM_DIRECT_MULTIBOOT);
9260         if (bam_direct != BAM_DIRECT_DBOOT) {
9261                 bam_error(_("bootadm set-menu %s may only be run on "
9262                     "directboot kernels.\n"),
9263                     optnum == KERNEL_CMD ? "kernel" : "args");
9264                 return (BAM_ERROR);
9265         }
9266 
9267         /*
9268          * If a user changed the default entry to a non-bootadm controlled
9269          * one, we don't want to mess with it.  Just print an error and
9270          * return.
9271          */
9272         if (mp->curdefault) {
9273                 entryNum = s_strtol(mp->curdefault->arg);
9274                 for (entryp = mp->entries; entryp; entryp = entryp->next) {
9275                         if (entryp->entryNum == entryNum)
9276                                 break;
9277                 }
9278                 if ((entryp != NULL) &&
9279                     ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9280                         bam_error(_("Default /boot/grub/menu.lst entry is not "
9281                             "controlled by bootadm.  Exiting\n"));
9282                         return (BAM_ERROR);
9283                 }
9284         }
9285 
9286         entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9287             0, &entryNum);
9288 
9289         if (entryp != NULL) {
9290                 for (ptr = entryp->start; ptr && ptr != entryp->end;
9291                     ptr = ptr->next) {
9292                         if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9293                             sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9294                                 kernelp = ptr;
9295                                 break;
9296                         }
9297                 }
9298                 if (kernelp == NULL) {
9299                         bam_error(_("no kernel line found in entry %d\n"),
9300                             entryNum);
9301                         return (BAM_ERROR);
9302                 }
9303 
9304                 old_kernel_len = strcspn(kernelp->arg, " \t");
9305                 space = old_args = kernelp->arg + old_kernel_len;
9306                 while ((*old_args == ' ') || (*old_args == '\t'))
9307                         old_args++;
9308         }
9309 
9310         if (path == NULL) {
9311                 if (entryp == NULL) {
9312                         BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9313                             fcn));
9314                         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9315                         return (BAM_SUCCESS);
9316                 }
9317                 assert(kernelp);
9318                 if (optnum == ARGS_CMD) {
9319                         if (old_args[0] != '\0') {
9320                                 (void) strlcpy(buf, old_args, bufsize);
9321                                 BAM_DPRINTF(("%s: read menu boot-args: %s\n",
9322                                     fcn, buf));
9323                         }
9324                 } else {
9325                         /*
9326                          * We need to print the kernel, so we just turn the
9327                          * first space into a '\0' and print the beginning.
9328                          * We don't print anything if it's the default kernel.
9329                          */
9330                         old_space = *space;
9331                         *space = '\0';
9332                         if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9333                                 (void) strlcpy(buf, kernelp->arg, bufsize);
9334                                 BAM_DPRINTF(("%s: read menu boot-file: %s\n",
9335                                     fcn, buf));
9336                         }
9337                         *space = old_space;
9338                 }
9339                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9340                 return (BAM_SUCCESS);
9341         }
9342 
9343         /*
9344          * First, check if we're resetting an entry to the default.
9345          */
9346         if ((path[0] == '\0') ||
9347             ((optnum == KERNEL_CMD) &&
9348             (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9349                 if ((entryp == NULL) || (kernelp == NULL)) {
9350                         /* No previous entry, it's already the default */
9351                         BAM_DPRINTF(("%s: no reset, already has default\n",
9352                             fcn));
9353                         return (BAM_SUCCESS);
9354                 }
9355 
9356                 /*
9357                  * Check if we can delete the entry.  If we're resetting the
9358                  * kernel command, and the args is already empty, or if we're
9359                  * resetting the args command, and the kernel is already the
9360                  * default, we can restore the old default and delete the entry.
9361                  */
9362                 if (((optnum == KERNEL_CMD) &&
9363                     ((old_args == NULL) || (old_args[0] == '\0'))) ||
9364                     ((optnum == ARGS_CMD) &&
9365                     (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9366                     sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9367                         kernelp = NULL;
9368                         (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9369                         restore_default_entry(mp, BAM_OLD_RC_DEF,
9370                             mp->old_rc_default);
9371                         mp->old_rc_default = NULL;
9372                         rv = BAM_WRITE;
9373                         BAM_DPRINTF(("%s: resetting to default\n", fcn));
9374                         goto done;
9375                 }
9376 
9377                 if (optnum == KERNEL_CMD) {
9378                         /*
9379                          * At this point, we've already checked that old_args
9380                          * and entryp are valid pointers.  The "+ 2" is for
9381                          * a space a the string termination character.
9382                          */
9383                         new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9384                             strlen(old_args) + 2;
9385                         new_arg = s_calloc(1, new_str_len);
9386                         (void) snprintf(new_arg, new_str_len, "%s %s",
9387                             DIRECT_BOOT_KERNEL, old_args);
9388                         free(kernelp->arg);
9389                         kernelp->arg = new_arg;
9390 
9391                         /*
9392                          * We have changed the kernel line, so we may need
9393                          * to update the archive line as well.
9394                          */
9395                         set_archive_line(entryp, kernelp);
9396                         BAM_DPRINTF(("%s: reset kernel to default, but "
9397                             "retained old args: %s\n", fcn, kernelp->arg));
9398                 } else {
9399                         /*
9400                          * We're resetting the boot args to nothing, so
9401                          * we only need to copy the kernel.  We've already
9402                          * checked that the kernel is not the default.
9403                          */
9404                         new_arg = s_calloc(1, old_kernel_len + 1);
9405                         (void) snprintf(new_arg, old_kernel_len + 1, "%s",
9406                             kernelp->arg);
9407                         free(kernelp->arg);
9408                         kernelp->arg = new_arg;
9409                         BAM_DPRINTF(("%s: reset args to default, but retained "
9410                             "old kernel: %s\n", fcn, kernelp->arg));
9411                 }
9412                 rv = BAM_WRITE;
9413                 goto done;
9414         }
9415 
9416         /*
9417          * Expand the kernel file to a full path, if necessary
9418          */
9419         if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9420                 new_path = expand_path(path);
9421                 if (new_path == NULL) {
9422                         bam_error(_("unable to expand %s to a full file "
9423                             "path.\n"), path);
9424                         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9425                         return (BAM_ERROR);
9426                 }
9427                 free_new_path = 1;
9428         } else {
9429                 new_path = path;
9430                 free_new_path = 0;
9431         }
9432 
9433         /*
9434          * At this point, we know we're setting a new value.  First, take care
9435          * of the case where there was no previous entry.
9436          */
9437         if (entryp == NULL) {
9438 
9439                 /* Similar to code in update_temp */
9440                 fstype = get_fstype("/");
9441                 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9442                 if (fstype == NULL) {
9443                         bam_error(_("cannot determine filesystem type for "
9444                             "\"/\".\nCannot generate GRUB menu entry with "
9445                             "EEPROM arguments.\n"));
9446                         rv = BAM_ERROR;
9447                         goto done;
9448                 }
9449 
9450                 osdev = get_special("/");
9451                 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9452                 if (osdev == NULL) {
9453                         free(fstype);
9454                         bam_error(_("cannot determine device special file for "
9455                             "\"/\".\nCannot generate GRUB menu entry with "
9456                             "EEPROM arguments.\n"));
9457                         rv = BAM_ERROR;
9458                         goto done;
9459                 }
9460 
9461                 sign = find_existing_sign("/", osdev, fstype);
9462                 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9463                 if (sign == NULL) {
9464                         free(fstype);
9465                         free(osdev);
9466                         bam_error(_("cannot determine boot signature for "
9467                             "\"/\".\nCannot generate GRUB menu entry with "
9468                             "EEPROM arguments.\n"));
9469                         rv = BAM_ERROR;
9470                         goto done;
9471                 }
9472 
9473                 free(osdev);
9474                 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9475                 free(sign);
9476                 assert(strchr(signbuf, '(') == NULL &&
9477                     strchr(signbuf, ',') == NULL &&
9478                     strchr(signbuf, ')') == NULL);
9479 
9480                 if (optnum == KERNEL_CMD) {
9481                         if (strcmp(fstype, "zfs") == 0) {
9482                                 new_str_len = strlen(new_path) +
9483                                     strlen(ZFS_BOOT) + 8;
9484                                 new_arg = s_calloc(1, new_str_len);
9485                                 (void) snprintf(new_arg, new_str_len, "%s %s",
9486                                     new_path, ZFS_BOOT);
9487                                 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9488                                     new_arg));
9489                                 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9490                                     signbuf, new_arg, NULL, NULL, NULL);
9491                                 free(new_arg);
9492                         } else {
9493                                 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9494                                     new_path));
9495                                 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9496                                     signbuf, new_path, NULL, NULL, NULL);
9497                         }
9498                 } else {
9499                         new_str_len = strlen(path) + 8;
9500                         if (strcmp(fstype, "zfs") == 0) {
9501                                 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9502                                 new_arg = s_calloc(1, new_str_len);
9503                                 (void) snprintf(new_arg, new_str_len, "%s %s",
9504                                     DIRECT_BOOT_KERNEL_ZFS, path);
9505                         } else {
9506                                 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9507                                 new_arg = s_calloc(1, new_str_len);
9508                                 (void) snprintf(new_arg, new_str_len, "%s %s",
9509                                     DIRECT_BOOT_KERNEL, path);
9510                         }
9511 
9512                         BAM_DPRINTF(("%s: new args=%s\n", fcn, new_arg));
9513                         entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9514                             signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9515                         free(new_arg);
9516                 }
9517                 free(fstype);
9518                 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9519                     entryNum = BAM_ERROR);
9520                 if (entryNum == BAM_ERROR) {
9521                         bam_error(_("failed to add boot entry: %s\n"),
9522                             BOOTENV_RC_TITLE);
9523                         rv = BAM_ERROR;
9524                         goto done;
9525                 }
9526                 save_default_entry(mp, BAM_OLD_RC_DEF);
9527                 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9528                 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9529                 if (ret == BAM_ERROR) {
9530                         bam_error(_("failed to set default to: %d\n"),
9531                             entryNum);
9532                 }
9533                 rv = BAM_WRITE;
9534                 goto done;
9535         }
9536 
9537         /*
9538          * There was already an bootenv entry which we need to edit.
9539          */
9540         if (optnum == KERNEL_CMD) {
9541                 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9542                 new_arg = s_calloc(1, new_str_len);
9543                 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9544                     old_args);
9545                 free(kernelp->arg);
9546                 kernelp->arg = new_arg;
9547 
9548                 /*
9549                  * If we have changed the kernel line, we may need to update
9550                  * the archive line as well.
9551                  */
9552                 set_archive_line(entryp, kernelp);
9553                 BAM_DPRINTF(("%s: rc line exists, replaced kernel, same "
9554                     "args: %s\n", fcn, kernelp->arg));
9555         } else {
9556                 new_str_len = old_kernel_len + strlen(path) + 8;
9557                 new_arg = s_calloc(1, new_str_len);
9558                 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9559                 (void) strlcat(new_arg, " ", new_str_len);
9560                 (void) strlcat(new_arg, path, new_str_len);
9561                 free(kernelp->arg);
9562                 kernelp->arg = new_arg;
9563                 BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9564                     "args: %s\n", fcn, kernelp->arg));
9565         }
9566         rv = BAM_WRITE;
9567 
9568 done:
9569         if ((rv == BAM_WRITE) && kernelp)
9570                 update_line(kernelp);
9571         if (free_new_path)
9572                 free(new_path);
9573         if (rv == BAM_WRITE) {
9574                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9575         } else {
9576                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9577         }
9578         return (rv);
9579 }
9580 
9581 static error_t
9582 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9583 {
9584         const char      *fcn = "get_kernel()";
9585         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_cmds[optnum]));
9586         return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9587 }
9588 
9589 static error_t
9590 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9591 {
9592         const char      *fcn = "set_kernel()";
9593         assert(path != NULL);
9594         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn,
9595             menu_cmds[optnum], path));
9596         return (get_set_kernel(mp, optnum, path, buf, bufsize));
9597 }
9598 
9599 /*ARGSUSED*/
9600 static error_t
9601 set_option(menu_t *mp, char *dummy, char *opt)
9602 {
9603         int             optnum;
9604         int             optval;
9605         char            *val;
9606         char            buf[BUFSIZ] = "";
9607         error_t         rv;
9608         const char      *fcn = "set_option()";
9609 
9610         assert(mp);
9611         assert(opt);
9612         assert(dummy == NULL);
9613 
9614         /* opt is set from bam_argv[0] and is always non-NULL */
9615         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt));
9616 
9617         val = strchr(opt, '=');
9618         if (val != NULL) {
9619                 *val = '\0';
9620         }
9621 
9622         if (strcmp(opt, "default") == 0) {
9623                 optnum = DEFAULT_CMD;
9624         } else if (strcmp(opt, "timeout") == 0) {
9625                 optnum = TIMEOUT_CMD;
9626         } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9627                 optnum = KERNEL_CMD;
9628         } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9629                 optnum = ARGS_CMD;
9630         } else {
9631                 bam_error(_("invalid option: %s\n"), opt);
9632                 return (BAM_ERROR);
9633         }
9634 
9635         /*
9636          * kernel and args are allowed without "=new_value" strings.  All
9637          * others cause errors
9638          */
9639         if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9640                 bam_error(_("option has no argument: %s\n"), opt);
9641                 return (BAM_ERROR);
9642         } else if (val != NULL) {
9643                 *val = '=';
9644         }
9645 
9646         if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9647                 BAM_DPRINTF(("%s: setting %s option to %s\n",
9648                     fcn, menu_cmds[optnum], val ? val + 1 : "NULL"));
9649 
9650                 if (val)
9651                         rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9652                 else
9653                         rv = get_kernel(mp, optnum, buf, sizeof (buf));
9654                 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9655                         (void) printf("%s\n", buf);
9656         } else {
9657                 optval = s_strtol(val + 1);
9658                 BAM_DPRINTF(("%s: setting %s option to %s\n", fcn,
9659                     menu_cmds[optnum], val + 1));
9660                 rv = set_global(mp, menu_cmds[optnum], optval);
9661         }
9662 
9663         if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9664                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9665         } else {
9666                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9667         }
9668 
9669         return (rv);
9670 }
9671 
9672 /*
9673  * The quiet argument suppresses messages. This is used
9674  * when invoked in the context of other commands (e.g. list_entry)
9675  */
9676 static error_t
9677 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9678 {
9679         line_t *lp;
9680         char *arg;
9681         int done, ret = BAM_SUCCESS;
9682 
9683         assert(mp);
9684         assert(menu_path);
9685         assert(globalcmd);
9686 
9687         if (mp->start == NULL) {
9688                 if (!quiet)
9689                         bam_error(_("menu file not found: %s\n"), menu_path);
9690                 return (BAM_ERROR);
9691         }
9692 
9693         done = 0;
9694         for (lp = mp->start; lp; lp = lp->next) {
9695                 if (lp->flags != BAM_GLOBAL)
9696                         continue;
9697 
9698                 if (lp->cmd == NULL) {
9699                         if (!quiet)
9700                                 bam_error(_("no command at line %d\n"),
9701                                     lp->lineNum);
9702                         continue;
9703                 }
9704 
9705                 if (strcmp(globalcmd, lp->cmd) != 0)
9706                         continue;
9707 
9708                 /* Found global. Check for duplicates */
9709                 if (done && !quiet) {
9710                         bam_error(_("duplicate command %s at line %d of "
9711                             "%sboot/grub/menu.lst\n"), globalcmd,
9712                             lp->lineNum, bam_root);
9713                         ret = BAM_ERROR;
9714                 }
9715 
9716                 arg = lp->arg ? lp->arg : "";
9717                 bam_print(_("%s %s\n"), globalcmd, arg);
9718                 done = 1;
9719         }
9720 
9721         if (!done && bam_verbose)
9722                 bam_print(_("no %s entry found\n"), globalcmd);
9723 
9724         return (ret);
9725 }
9726 
9727 static error_t
9728 menu_write(char *root, menu_t *mp)
9729 {
9730         const char *fcn = "menu_write()";
9731 
9732         BAM_DPRINTF(("%s: entered menu_write() for root: <%s>\n", fcn, root));
9733         return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9734 }
9735 
9736 void
9737 line_free(line_t *lp)
9738 {
9739         if (lp == NULL)
9740                 return;
9741 
9742         if (lp->cmd != NULL)
9743                 free(lp->cmd);
9744         if (lp->sep)
9745                 free(lp->sep);
9746         if (lp->arg)
9747                 free(lp->arg);
9748         if (lp->line)
9749                 free(lp->line);
9750         free(lp);
9751 }
9752 
9753 static void
9754 linelist_free(line_t *start)
9755 {
9756         line_t *lp;
9757 
9758         while (start) {
9759                 lp = start;
9760                 start = start->next;
9761                 line_free(lp);
9762         }
9763 }
9764 
9765 static void
9766 filelist_free(filelist_t *flistp)
9767 {
9768         linelist_free(flistp->head);
9769         flistp->head = NULL;
9770         flistp->tail = NULL;
9771 }
9772 
9773 static void
9774 menu_free(menu_t *mp)
9775 {
9776         entry_t *ent, *tmp;
9777         assert(mp);
9778 
9779         if (mp->start)
9780                 linelist_free(mp->start);
9781         ent = mp->entries;
9782         while (ent) {
9783                 tmp = ent;
9784                 ent = tmp->next;
9785                 free(tmp);
9786         }
9787 
9788         free(mp);
9789 }
9790 
9791 /*
9792  * Utility routines
9793  */
9794 
9795 
9796 /*
9797  * Returns 0 on success
9798  * Any other value indicates an error
9799  */
9800 static int
9801 exec_cmd(char *cmdline, filelist_t *flistp)
9802 {
9803         char buf[BUFSIZ];
9804         int ret;
9805         FILE *ptr;
9806         sigset_t set;
9807         void (*disp)(int);
9808 
9809         /*
9810          * For security
9811          * - only absolute paths are allowed
9812          * - set IFS to space and tab
9813          */
9814         if (*cmdline != '/') {
9815                 bam_error(_("path is not absolute: %s\n"), cmdline);
9816                 return (-1);
9817         }
9818         (void) putenv("IFS= \t");
9819 
9820         /*
9821          * We may have been exec'ed with SIGCHLD blocked
9822          * unblock it here
9823          */
9824         (void) sigemptyset(&set);
9825         (void) sigaddset(&set, SIGCHLD);
9826         if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9827                 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno));
9828                 return (-1);
9829         }
9830 
9831         /*
9832          * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9833          */
9834         disp = sigset(SIGCHLD, SIG_DFL);
9835         if (disp == SIG_ERR) {
9836                 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
9837                     strerror(errno));
9838                 return (-1);
9839         }
9840         if (disp == SIG_HOLD) {
9841                 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
9842                     cmdline);
9843                 return (-1);
9844         }
9845 
9846         ptr = popen(cmdline, "r");
9847         if (ptr == NULL) {
9848                 bam_error(_("popen failed: %s: %s\n"), cmdline,
9849                     strerror(errno));
9850                 return (-1);
9851         }
9852 
9853         /*
9854          * If we simply do a pclose() following a popen(), pclose()
9855          * will close the reader end of the pipe immediately even
9856          * if the child process has not started/exited. pclose()
9857          * does wait for cmd to terminate before returning though.
9858          * When the executed command writes its output to the pipe
9859          * there is no reader process and the command dies with
9860          * SIGPIPE. To avoid this we read repeatedly until read
9861          * terminates with EOF. This indicates that the command
9862          * (writer) has closed the pipe and we can safely do a
9863          * pclose().
9864          *
9865          * Since pclose() does wait for the command to exit,
9866          * we can safely reap the exit status of the command
9867          * from the value returned by pclose()
9868          */
9869         while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9870                 if (flistp == NULL) {
9871                         /* s_fgets strips newlines, so insert them at the end */
9872                         bam_print(_("%s\n"), buf);
9873                 } else {
9874                         append_to_flist(flistp, buf);
9875                 }
9876         }
9877 
9878         ret = pclose(ptr);
9879         if (ret == -1) {
9880                 bam_error(_("pclose failed: %s: %s\n"), cmdline,
9881                     strerror(errno));
9882                 return (-1);
9883         }
9884 
9885         if (WIFEXITED(ret)) {
9886                 return (WEXITSTATUS(ret));
9887         } else {
9888                 bam_error(_("command terminated abnormally: %s: %d\n"),
9889                     cmdline, ret);
9890                 return (-1);
9891         }
9892 }
9893 
9894 /*
9895  * Since this function returns -1 on error
9896  * it cannot be used to convert -1. However,
9897  * that is sufficient for what we need.
9898  */
9899 static long
9900 s_strtol(char *str)
9901 {
9902         long l;
9903         char *res = NULL;
9904 
9905         if (str == NULL) {
9906                 return (-1);
9907         }
9908 
9909         errno = 0;
9910         l = strtol(str, &res, 10);
9911         if (errno || *res != '\0') {
9912                 return (-1);
9913         }
9914 
9915         return (l);
9916 }
9917 
9918 /*
9919  * Wrapper around fputs, that adds a newline (since fputs doesn't)
9920  */
9921 static int
9922 s_fputs(char *str, FILE *fp)
9923 {
9924         char linebuf[BAM_MAXLINE];
9925 
9926         (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9927         return (fputs(linebuf, fp));
9928 }
9929 
9930 /*
9931  * Wrapper around fgets, that strips newlines returned by fgets
9932  */
9933 char *
9934 s_fgets(char *buf, int buflen, FILE *fp)
9935 {
9936         int n;
9937 
9938         buf = fgets(buf, buflen, fp);
9939         if (buf) {
9940                 n = strlen(buf);
9941                 if (n == buflen - 1 && buf[n-1] != '\n')
9942                         bam_error(_("the following line is too long "
9943                             "(> %d chars)\n\t%s\n"), buflen - 1, buf);
9944                 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9945         }
9946 
9947         return (buf);
9948 }
9949 
9950 void *
9951 s_calloc(size_t nelem, size_t sz)
9952 {
9953         void *ptr;
9954 
9955         ptr = calloc(nelem, sz);
9956         if (ptr == NULL) {
9957                 bam_error(_("could not allocate memory: size = %u\n"),
9958                     nelem*sz);
9959                 bam_exit(1);
9960         }
9961         return (ptr);
9962 }
9963 
9964 void *
9965 s_realloc(void *ptr, size_t sz)
9966 {
9967         ptr = realloc(ptr, sz);
9968         if (ptr == NULL) {
9969                 bam_error(_("could not allocate memory: size = %u\n"), sz);
9970                 bam_exit(1);
9971         }
9972         return (ptr);
9973 }
9974 
9975 char *
9976 s_strdup(char *str)
9977 {
9978         char *ptr;
9979 
9980         if (str == NULL)
9981                 return (NULL);
9982 
9983         ptr = strdup(str);
9984         if (ptr == NULL) {
9985                 bam_error(_("could not allocate memory: size = %u\n"),
9986                     strlen(str) + 1);
9987                 bam_exit(1);
9988         }
9989         return (ptr);
9990 }
9991 
9992 /*
9993  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9994  * Returns 0 otherwise
9995  */
9996 static int
9997 is_amd64(void)
9998 {
9999         static int amd64 = -1;
10000         char isabuf[257];       /* from sysinfo(2) manpage */
10001 
10002         if (amd64 != -1)
10003                 return (amd64);
10004 
10005         if (bam_alt_platform) {
10006                 if (strcmp(bam_platform, "i86pc") == 0) {
10007                         amd64 = 1;              /* diskless server */
10008                 }
10009         } else {
10010                 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
10011                     strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
10012                         amd64 = 1;
10013                 } else if (strstr(isabuf, "i386") == NULL) {
10014                         amd64 = 1;              /* diskless server */
10015                 }
10016         }
10017         if (amd64 == -1)
10018                 amd64 = 0;
10019 
10020         return (amd64);
10021 }
10022 
10023 static char *
10024 get_machine(void)
10025 {
10026         static int cached = -1;
10027         static char mbuf[257];  /* from sysinfo(2) manpage */
10028 
10029         if (cached == 0)
10030                 return (mbuf);
10031 
10032         if (bam_alt_platform) {
10033                 return (bam_platform);
10034         } else {
10035                 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
10036                         cached = 1;
10037                 }
10038         }
10039         if (cached == -1) {
10040                 mbuf[0] = '\0';
10041                 cached = 0;
10042         }
10043 
10044         return (mbuf);
10045 }
10046 
10047 int
10048 is_sparc(void)
10049 {
10050         static int issparc = -1;
10051         char mbuf[257]; /* from sysinfo(2) manpage */
10052 
10053         if (issparc != -1)
10054                 return (issparc);
10055 
10056         if (bam_alt_platform) {
10057                 if (strncmp(bam_platform, "sun4", 4) == 0) {
10058                         issparc = 1;
10059                 }
10060         } else {
10061                 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
10062                     strcmp(mbuf, "sparc") == 0) {
10063                         issparc = 1;
10064                 }
10065         }
10066         if (issparc == -1)
10067                 issparc = 0;
10068 
10069         return (issparc);
10070 }
10071 
10072 static void
10073 append_to_flist(filelist_t *flistp, char *s)
10074 {
10075         line_t *lp;
10076 
10077         lp = s_calloc(1, sizeof (line_t));
10078         lp->line = s_strdup(s);
10079         if (flistp->head == NULL)
10080                 flistp->head = lp;
10081         else
10082                 flistp->tail->next = lp;
10083         flistp->tail = lp;
10084 }
10085 
10086 #if !defined(_OBP)
10087 
10088 UCODE_VENDORS;
10089 
10090 /*ARGSUSED*/
10091 static void
10092 ucode_install(char *root)
10093 {
10094         int i;
10095 
10096         for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
10097                 int cmd_len = PATH_MAX + 256;
10098                 char cmd[PATH_MAX + 256];
10099                 char file[PATH_MAX];
10100                 char timestamp[PATH_MAX];
10101                 struct stat fstatus, tstatus;
10102                 struct utimbuf u_times;
10103 
10104                 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
10105                     bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
10106                     ucode_vendors[i].extstr);
10107 
10108                 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
10109                         continue;
10110 
10111                 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
10112 
10113                 if (stat(timestamp, &tstatus) == 0 &&
10114                     fstatus.st_mtime <= tstatus.st_mtime)
10115                         continue;
10116 
10117                 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
10118                     "%s/%s/%s %s > /dev/null 2>&1", bam_root,
10119                     UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
10120                 if (system(cmd) != 0)
10121                         return;
10122 
10123                 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
10124                         return;
10125 
10126                 u_times.actime = fstatus.st_atime;
10127                 u_times.modtime = fstatus.st_mtime;
10128                 (void) utime(timestamp, &u_times);
10129         }
10130 }
10131 #endif