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