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