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