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         (void) unlink(tmpfile);
3884 
3885         for (i = 0; i < files; i++) {
3886                 char    filepath[PATH_MAX];
3887                 char    *fname;
3888 
3889                 fname = filelist[i]->d_name;
3890 
3891                 /* skip anything that starts with a dot */
3892                 if (strncmp(fname, ".", 1) == 0) {
3893                         free(filelist[i]);
3894                         continue;
3895                 }
3896 
3897                 if (bam_verbose)
3898                         bam_print(_("/etc/system.d adding %s/%s\n"),
3899                             path, fname);
3900 
3901                 (void) snprintf(filepath, sizeof (filepath), "%s/%s",
3902                     path, fname);
3903 
3904                 if ((assemble_systemfile(filepath, tmpfile)) < 0) {
3905                         bam_error(_("failed to append file: %s: %s\n"),
3906                             filepath, strerror(errno));
3907                         ret = BAM_ERROR;
3908                         break;
3909                 }
3910                 sysfiles++;
3911         }
3912 
3913         if (sysfiles > 0) {
3914                 if (rename(tmpfile, self_assembly) < 0) {
3915                         bam_error(_("failed to rename file: %s: %s\n"), tmpfile,
3916                             strerror(errno));
3917                         return (BAM_ERROR);
3918                 }
3919 
3920                 /*
3921                  * Use previous attribute times to avoid
3922                  * boot archive recreation.
3923                  */
3924                 if (times[1].tv_nsec != 0 &&
3925                     utimensat(AT_FDCWD, self_assembly, times, 0) != 0) {
3926                         bam_error(_("failed to change times: %s\n"),
3927                             strerror(errno));
3928                         return (BAM_ERROR);
3929                 }
3930         } else {
3931                 (void) unlink(tmpfile);
3932                 (void) unlink(self_assembly);
3933         }
3934         return (ret);
3935 }
3936 
3937 static error_t
3938 create_ramdisk(char *root)
3939 {
3940         char *cmdline, path[PATH_MAX];
3941         size_t len;
3942         struct stat sb;
3943         int ret, status = BAM_SUCCESS;
3944 
3945         /* If mkisofs should be used, use it to create the required archives */
3946         if (use_mkisofs()) {
3947                 if (has_cachedir() && is_dir_flag_on(NEED_UPDATE)) {
3948                         ret = mkisofs_archive(root);
3949                         if (ret != 0)
3950                                 status = BAM_ERROR;
3951                 }
3952                 return (status);
3953         } else if (bam_format == BAM_FORMAT_HSFS) {
3954                 bam_error(_("cannot create hsfs archive\n"));
3955                 return (BAM_ERROR);
3956         }
3957 
3958         /*
3959          * Else setup command args for create_ramdisk.ksh for the archive
3960          * Note: we will not create hash here, CREATE_RAMDISK should create it.
3961          */
3962 
3963         (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3964         if (stat(path, &sb) != 0) {
3965                 bam_error(_("archive creation file not found: %s: %s\n"),
3966                     path, strerror(errno));
3967                 return (BAM_ERROR);
3968         }
3969 
3970         if (is_safe_exec(path) == BAM_ERROR)
3971                 return (BAM_ERROR);
3972 
3973         len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3974         if (bam_alt_platform)
3975                 len += strlen(bam_platform) + strlen(" -p ");
3976         if (bam_format != BAM_FORMAT_UNSET)
3977                 len += strlen(bam_formats[bam_format]) + strlen(" -f ");
3978         cmdline = s_calloc(1, len);
3979 
3980         if (bam_alt_platform) {
3981                 assert(strlen(root) > 1);
3982                 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3983                     path, bam_platform, root);
3984                 /* chop off / at the end */
3985                 cmdline[strlen(cmdline) - 1] = '\0';
3986         } else if (strlen(root) > 1) {
3987                 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3988                 /* chop off / at the end */
3989                 cmdline[strlen(cmdline) - 1] = '\0';
3990         } else
3991                 (void) snprintf(cmdline, len, "%s", path);
3992 
3993         if (bam_format != BAM_FORMAT_UNSET) {
3994                 if (strlcat(cmdline, " -f ", len) >= len ||
3995                     strlcat(cmdline, bam_formats[bam_format], len) >= len) {
3996                         bam_error(_("boot-archive command line too long\n"));
3997                         free(cmdline);
3998                         return (BAM_ERROR);
3999                 }
4000         }
4001 
4002         if (exec_cmd(cmdline, NULL) != 0) {
4003                 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
4004                     cmdline);
4005                 free(cmdline);
4006                 return (BAM_ERROR);
4007         }
4008         free(cmdline);
4009         /*
4010          * The existence of the expected archives used to be
4011          * verified here. This check is done in create_ramdisk as
4012          * it needs to be in sync with the altroot operated upon.
4013          */
4014         return (BAM_SUCCESS);
4015 }
4016 
4017 /*
4018  * Checks if target filesystem is on a ramdisk
4019  * 1 - is miniroot
4020  * 0 - is not
4021  * When in doubt assume it is not a ramdisk.
4022  */
4023 static int
4024 is_ramdisk(char *root)
4025 {
4026         struct extmnttab mnt;
4027         FILE *fp;
4028         int found;
4029         char mntpt[PATH_MAX];
4030         char *cp;
4031 
4032         /*
4033          * There are 3 situations where creating archive is
4034          * of dubious value:
4035          *      - create boot_archive on a lofi-mounted boot_archive
4036          *      - create it on a ramdisk which is the root filesystem
4037          *      - create it on a ramdisk mounted somewhere else
4038          * The first is not easy to detect and checking for it is not
4039          * worth it.
4040          * The other two conditions are handled here
4041          */
4042         fp = fopen(MNTTAB, "r");
4043         if (fp == NULL) {
4044                 bam_error(_("failed to open file: %s: %s\n"),
4045                     MNTTAB, strerror(errno));
4046                 return (0);
4047         }
4048 
4049         resetmnttab(fp);
4050 
4051         /*
4052          * Remove any trailing / from the mount point
4053          */
4054         (void) strlcpy(mntpt, root, sizeof (mntpt));
4055         if (strcmp(root, "/") != 0) {
4056                 cp = mntpt + strlen(mntpt) - 1;
4057                 if (*cp == '/')
4058                         *cp = '\0';
4059         }
4060         found = 0;
4061         while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4062                 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
4063                         found = 1;
4064                         break;
4065                 }
4066         }
4067 
4068         if (!found) {
4069                 if (bam_verbose)
4070                         bam_error(_("alternate root %s not in mnttab\n"),
4071                             mntpt);
4072                 (void) fclose(fp);
4073                 return (0);
4074         }
4075 
4076         if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
4077             strlen(RAMDISK_SPECIAL)) == 0) {
4078                 if (bam_verbose)
4079                         bam_error(_("%s is on a ramdisk device\n"), bam_root);
4080                 (void) fclose(fp);
4081                 return (1);
4082         }
4083 
4084         (void) fclose(fp);
4085 
4086         return (0);
4087 }
4088 
4089 static int
4090 is_boot_archive(char *root)
4091 {
4092         char            path[PATH_MAX];
4093         struct stat     sb;
4094         int             error;
4095         const char      *fcn = "is_boot_archive()";
4096 
4097         /*
4098          * We can't create an archive without the create_ramdisk script
4099          */
4100         (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
4101         error = stat(path, &sb);
4102         INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
4103         if (error == -1) {
4104                 if (bam_verbose)
4105                         bam_print(_("file not found: %s\n"), path);
4106                 BAM_DPRINTF(("%s: not a boot archive based Solaris "
4107                     "instance: %s\n", fcn, root));
4108                 return (0);
4109         }
4110 
4111         BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
4112             fcn, root));
4113         return (1);
4114 }
4115 
4116 /*
4117  * Need to call this for anything that operates on the GRUB menu
4118  * In the x86 live upgrade case the directory /boot/grub may be present
4119  * even on pre-newboot BEs. The authoritative way to check for a GRUB target
4120  * is to check for the presence of the stage2 binary which is present
4121  * only on GRUB targets (even on x86 boot partitions). Checking for the
4122  * presence of the multiboot binary is not correct as it is not present
4123  * on x86 boot partitions.
4124  */
4125 int
4126 is_grub(const char *root)
4127 {
4128         char path[PATH_MAX];
4129         struct stat sb;
4130         void *defp;
4131         boolean_t grub = B_FALSE;
4132         const char *res = NULL;
4133         const char *fcn = "is_grub()";
4134 
4135         /* grub is disabled by default */
4136         if ((defp = defopen_r(BE_DEFAULTS)) == NULL) {
4137                 return (0);
4138         } else {
4139                 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
4140                 if (res != NULL && res[0] != '\0') {
4141                         if (strcasecmp(res, "true") == 0)
4142                                 grub = B_TRUE;
4143                 }
4144                 defclose_r(defp);
4145         }
4146 
4147         if (grub == B_TRUE) {
4148                 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
4149                 if (stat(path, &sb) == -1) {
4150                         BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
4151                             fcn, path));
4152                         return (0);
4153                 } else
4154                         return (1);
4155         }
4156 
4157         return (0);
4158 }
4159 
4160 int
4161 is_zfs(char *root)
4162 {
4163         struct statvfs          vfs;
4164         int                     ret;
4165         const char              *fcn = "is_zfs()";
4166 
4167         ret = statvfs(root, &vfs);
4168         INJECT_ERROR1("STATVFS_ZFS", ret = 1);
4169         if (ret != 0) {
4170                 bam_error(_("statvfs failed for %s: %s\n"), root,
4171                     strerror(errno));
4172                 return (0);
4173         }
4174 
4175         if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
4176                 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
4177                 return (1);
4178         } else {
4179                 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
4180                 return (0);
4181         }
4182 }
4183 
4184 int
4185 is_pcfs(char *root)
4186 {
4187         struct statvfs          vfs;
4188         int                     ret;
4189         const char              *fcn = "is_pcfs()";
4190 
4191         ret = statvfs(root, &vfs);
4192         INJECT_ERROR1("STATVFS_PCFS", ret = 1);
4193         if (ret != 0) {
4194                 bam_error(_("statvfs failed for %s: %s\n"), root,
4195                     strerror(errno));
4196                 return (0);
4197         }
4198 
4199         if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
4200                 BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn, root));
4201                 return (1);
4202         } else {
4203                 BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4204                     fcn, root));
4205                 return (0);
4206         }
4207 }
4208 
4209 static int
4210 is_readonly(char *root)
4211 {
4212         int             fd;
4213         int             error;
4214         char            testfile[PATH_MAX];
4215         const char      *fcn = "is_readonly()";
4216 
4217         /*
4218          * Using statvfs() to check for a read-only filesystem is not
4219          * reliable. The only way to reliably test is to attempt to
4220          * create a file
4221          */
4222         (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
4223             root, BOOTADM_RDONLY_TEST, getpid());
4224 
4225         (void) unlink(testfile);
4226 
4227         errno = 0;
4228         fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
4229         error = errno;
4230         INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
4231         if (fd == -1 && error == EROFS) {
4232                 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
4233                 return (1);
4234         } else if (fd == -1) {
4235                 bam_error(_("error during read-only test on %s: %s\n"),
4236                     root, strerror(error));
4237         }
4238 
4239         (void) close(fd);
4240         (void) unlink(testfile);
4241 
4242         BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
4243         return (0);
4244 }
4245 
4246 static error_t
4247 update_archive(char *root, char *opt)
4248 {
4249         error_t ret;
4250 
4251         assert(root);
4252         assert(opt == NULL);
4253 
4254         init_walk_args();
4255         (void) umask(022);
4256 
4257         /*
4258          * Never update non-BE root in update_all
4259          */
4260         if (bam_update_all && !is_be(root))
4261                 return (BAM_SUCCESS);
4262         /*
4263          * root must belong to a boot archive based OS,
4264          */
4265         if (!is_boot_archive(root)) {
4266                 /*
4267                  * Emit message only if not in context of update_all.
4268                  * If in update_all, emit only if verbose flag is set.
4269                  */
4270                 if (!bam_update_all || bam_verbose)
4271                         bam_print(_("%s: not a boot archive based Solaris "
4272                             "instance\n"), root);
4273                 return (BAM_ERROR);
4274         }
4275 
4276         /*
4277          * If smf check is requested when / is writable (can happen
4278          * on first reboot following an upgrade because service
4279          * dependency is messed up), skip the check.
4280          */
4281         if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
4282                 return (BAM_SUCCESS);
4283 
4284         /*
4285          * Don't generate archive on ramdisk.
4286          */
4287         if (is_ramdisk(root))
4288                 return (BAM_SUCCESS);
4289 
4290         /*
4291          * root must be writable. This check applies to alternate
4292          * root (-R option); bam_root_readonly applies to '/' only.
4293          * The behaviour translates into being the one of a 'check'.
4294          */
4295         if (!bam_smf_check && !bam_check && is_readonly(root)) {
4296                 set_flag(RDONLY_FSCHK);
4297                 bam_check = 1;
4298         }
4299 
4300         /*
4301          * Process the /etc/system.d/.self-assembly file.
4302          */
4303         if (build_etc_system_dir(bam_root) == BAM_ERROR)
4304                 return (BAM_ERROR);
4305 
4306         /*
4307          * Now check if an update is really needed.
4308          */
4309         ret = update_required(root);
4310 
4311         /*
4312          * The check command (-n) is *not* a dry run.
4313          * It only checks if the archive is in sync.
4314          * A readonly filesystem has to be considered an error only if an update
4315          * is required.
4316          */
4317         if (bam_nowrite()) {
4318                 if (is_flag_on(RDONLY_FSCHK)) {
4319                         bam_check = bam_saved_check;
4320                         if (ret > 0)
4321                                 bam_error(_("%s filesystem is read-only, "
4322                                     "skipping archives update\n"), root);
4323                         if (bam_update_all)
4324                                 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4325                 }
4326 
4327                 bam_exit((ret != 0) ? 1 : 0);
4328         }
4329 
4330         if (ret == 1) {
4331                 /* create the ramdisk */
4332                 ret = create_ramdisk(root);
4333         }
4334 
4335         /*
4336          * if the archive is updated, save the new stat data and update the
4337          * timestamp file
4338          */
4339         if (ret == 0 && walk_arg.new_nvlp != NULL) {
4340                 savenew(root);
4341                 update_timestamp(root);
4342         }
4343 
4344         clear_walk_args();
4345 
4346         return (ret);
4347 }
4348 
4349 static char *
4350 find_root_pool()
4351 {
4352         char *special = get_special("/");
4353         char *p;
4354 
4355         if (special == NULL)
4356                 return (NULL);
4357 
4358         if (*special == '/') {
4359                 free(special);
4360                 return (NULL);
4361         }
4362 
4363         if ((p = strchr(special, '/')) != NULL)
4364                 *p = '\0';
4365 
4366         return (special);
4367 }
4368 
4369 static error_t
4370 synchronize_BE_menu(void)
4371 {
4372         struct stat     sb;
4373         char            cmdline[PATH_MAX];
4374         char            cksum_line[PATH_MAX];
4375         filelist_t      flist = {0};
4376         char            *old_cksum_str;
4377         char            *old_size_str;
4378         char            *old_file;
4379         char            *curr_cksum_str;
4380         char            *curr_size_str;
4381         char            *curr_file;
4382         char            *pool = NULL;
4383         char            *mntpt = NULL;
4384         zfs_mnted_t     mnted;
4385         FILE            *cfp;
4386         int             found;
4387         int             ret;
4388         const char      *fcn = "synchronize_BE_menu()";
4389 
4390         BAM_DPRINTF(("%s: entered. No args\n", fcn));
4391 
4392         /* Check if findroot enabled LU BE */
4393         if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4394                 BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn));
4395                 return (BAM_SUCCESS);
4396         }
4397 
4398         if (stat(LU_MENU_CKSUM, &sb) != 0) {
4399                 BAM_DPRINTF(("%s: checksum file absent: %s\n",
4400                     fcn, LU_MENU_CKSUM));
4401                 goto menu_sync;
4402         }
4403 
4404         cfp = fopen(LU_MENU_CKSUM, "r");
4405         INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4406         if (cfp == NULL) {
4407                 bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4408                     LU_MENU_CKSUM);
4409                 goto menu_sync;
4410         }
4411         BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn, LU_MENU_CKSUM));
4412 
4413         found = 0;
4414         while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4415                 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4416                 if (found) {
4417                         bam_error(_("multiple checksums for GRUB menu in "
4418                             "checksum file: %s\n"), LU_MENU_CKSUM);
4419                         (void) fclose(cfp);
4420                         goto menu_sync;
4421                 }
4422                 found = 1;
4423         }
4424         BAM_DPRINTF(("%s: read checksum file: %s\n", fcn, LU_MENU_CKSUM));
4425 
4426 
4427         old_cksum_str = strtok(cksum_line, " \t");
4428         old_size_str = strtok(NULL, " \t");
4429         old_file = strtok(NULL, " \t");
4430 
4431         INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4432         INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4433         INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4434         if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4435                 bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4436                     LU_MENU_CKSUM);
4437                 goto menu_sync;
4438         }
4439         BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn, LU_MENU_CKSUM));
4440 
4441         /* Get checksum of current menu */
4442         pool = find_root_pool();
4443         if (pool) {
4444                 mntpt = mount_top_dataset(pool, &mnted);
4445                 if (mntpt == NULL) {
4446                         bam_error(_("failed to mount top dataset for %s\n"),
4447                             pool);
4448                         free(pool);
4449                         return (BAM_ERROR);
4450                 }
4451                 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4452                     CKSUM, mntpt, GRUB_MENU);
4453         } else {
4454                 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4455                     CKSUM, GRUB_MENU);
4456         }
4457         ret = exec_cmd(cmdline, &flist);
4458         if (pool) {
4459                 (void) umount_top_dataset(pool, mnted, mntpt);
4460                 free(pool);
4461         }
4462         INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4463         if (ret != 0) {
4464                 bam_error(_("error generating checksum of GRUB menu\n"));
4465                 return (BAM_ERROR);
4466         }
4467         BAM_DPRINTF(("%s: successfully generated checksum\n", fcn));
4468 
4469         INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4470         if ((flist.head == NULL) || (flist.head != flist.tail)) {
4471                 bam_error(_("bad checksum generated for GRUB menu\n"));
4472                 filelist_free(&flist);
4473                 return (BAM_ERROR);
4474         }
4475         BAM_DPRINTF(("%s: generated checksum output valid\n", fcn));
4476 
4477         curr_cksum_str = strtok(flist.head->line, " \t");
4478         curr_size_str = strtok(NULL, " \t");
4479         curr_file = strtok(NULL, " \t");
4480 
4481         INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4482         INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4483         INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4484         if (curr_cksum_str == NULL || curr_size_str == NULL ||
4485             curr_file == NULL) {
4486                 bam_error(_("error parsing checksum generated "
4487                     "for GRUB menu\n"));
4488                 filelist_free(&flist);
4489                 return (BAM_ERROR);
4490         }
4491         BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn));
4492 
4493         if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4494             strcmp(old_size_str, curr_size_str) == 0 &&
4495             strcmp(old_file, curr_file) == 0) {
4496                 filelist_free(&flist);
4497                 BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn));
4498                 return (BAM_SUCCESS);
4499         }
4500 
4501         filelist_free(&flist);
4502 
4503         /* cksum doesn't match - the menu has changed */
4504         BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn));
4505 
4506 menu_sync:
4507         bam_print(_("propagating updated GRUB menu\n"));
4508 
4509         (void) snprintf(cmdline, sizeof (cmdline),
4510             "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4511             LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4512         ret = exec_cmd(cmdline, NULL);
4513         INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4514         if (ret != 0) {
4515                 bam_error(_("error propagating updated GRUB menu\n"));
4516                 return (BAM_ERROR);
4517         }
4518         BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn));
4519 
4520         (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4521             GRUB_MENU, GRUB_BACKUP_MENU);
4522         ret = exec_cmd(cmdline, NULL);
4523         INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4524         if (ret != 0) {
4525                 bam_error(_("failed to create backup for GRUB menu: %s\n"),
4526                     GRUB_BACKUP_MENU);
4527                 return (BAM_ERROR);
4528         }
4529         BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4530             fcn, GRUB_BACKUP_MENU));
4531 
4532         (void) snprintf(cmdline, sizeof (cmdline),
4533             "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4534             LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4535         ret = exec_cmd(cmdline, NULL);
4536         INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4537         if (ret != 0) {
4538                 bam_error(_("error propagating backup GRUB menu: %s\n"),
4539                     GRUB_BACKUP_MENU);
4540                 return (BAM_ERROR);
4541         }
4542         BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4543             fcn, GRUB_BACKUP_MENU));
4544 
4545         (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4546             CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4547         ret = exec_cmd(cmdline, NULL);
4548         INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4549         if (ret != 0) {
4550                 bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4551                     LU_MENU_CKSUM);
4552                 return (BAM_ERROR);
4553         }
4554         BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4555             fcn, LU_MENU_CKSUM));
4556 
4557         (void) snprintf(cmdline, sizeof (cmdline),
4558             "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4559             LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4560         ret = exec_cmd(cmdline, NULL);
4561         INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4562         if (ret != 0) {
4563                 bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4564                     LU_MENU_CKSUM);
4565                 return (BAM_ERROR);
4566         }
4567         BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4568             fcn, LU_MENU_CKSUM));
4569 
4570         return (BAM_SUCCESS);
4571 }
4572 
4573 static error_t
4574 update_all(char *root, char *opt)
4575 {
4576         struct extmnttab mnt;
4577         struct stat sb;
4578         FILE *fp;
4579         char multibt[PATH_MAX];
4580         char creatram[PATH_MAX];
4581         error_t ret = BAM_SUCCESS;
4582 
4583         assert(root);
4584         assert(opt == NULL);
4585 
4586         if (bam_rootlen != 1 || *root != '/') {
4587                 elide_trailing_slash(root, multibt, sizeof (multibt));
4588                 bam_error(_("an alternate root (%s) cannot be used with this "
4589                     "sub-command\n"), multibt);
4590                 return (BAM_ERROR);
4591         }
4592 
4593         /*
4594          * First update archive for current root
4595          */
4596         if (update_archive(root, opt) != BAM_SUCCESS)
4597                 ret = BAM_ERROR;
4598 
4599         if (ret == BAM_ERROR)
4600                 goto out;
4601 
4602         /*
4603          * Now walk the mount table, performing archive update
4604          * for all mounted Newboot root filesystems
4605          */
4606         fp = fopen(MNTTAB, "r");
4607         if (fp == NULL) {
4608                 bam_error(_("failed to open file: %s: %s\n"),
4609                     MNTTAB, strerror(errno));
4610                 ret = BAM_ERROR;
4611                 goto out;
4612         }
4613 
4614         resetmnttab(fp);
4615 
4616         while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4617                 if (mnt.mnt_special == NULL)
4618                         continue;
4619                 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4620                     (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4621                         continue;
4622                 if (strcmp(mnt.mnt_mountp, "/") == 0)
4623                         continue;
4624 
4625                 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4626                     mnt.mnt_mountp, CREATE_RAMDISK);
4627 
4628                 if (stat(creatram, &sb) == -1)
4629                         continue;
4630 
4631                 /*
4632                  * We put a trailing slash to be consistent with root = "/"
4633                  * case, such that we don't have to print // in some cases.
4634                  */
4635                 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4636                     mnt.mnt_mountp);
4637                 bam_rootlen = strlen(rootbuf);
4638 
4639                 /*
4640                  * It's possible that other mounts may be an alternate boot
4641                  * architecture, so check it again.
4642                  */
4643                 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4644                     (update_archive(rootbuf, opt) != BAM_SUCCESS))
4645                         ret = BAM_ERROR;
4646         }
4647 
4648         (void) fclose(fp);
4649 
4650 out:
4651         /*
4652          * We no longer use biosdev for Live Upgrade. Hence
4653          * there is no need to defer (to shutdown time) any fdisk
4654          * updates
4655          */
4656         if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4657                 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4658                     "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
4659         }
4660 
4661         /*
4662          * If user has updated menu in current BE, propagate the
4663          * updates to all BEs.
4664          */
4665         if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4666                 ret = BAM_ERROR;
4667 
4668         return (ret);
4669 }
4670 
4671 static void
4672 append_line(menu_t *mp, line_t *lp)
4673 {
4674         if (mp->start == NULL) {
4675                 mp->start = lp;
4676         } else {
4677                 mp->end->next = lp;
4678                 lp->prev = mp->end;
4679         }
4680         mp->end = lp;
4681 }
4682 
4683 void
4684 unlink_line(menu_t *mp, line_t *lp)
4685 {
4686         /* unlink from list */
4687         if (lp->prev)
4688                 lp->prev->next = lp->next;
4689         else
4690                 mp->start = lp->next;
4691         if (lp->next)
4692                 lp->next->prev = lp->prev;
4693         else
4694                 mp->end = lp->prev;
4695 }
4696 
4697 static entry_t *
4698 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4699 {
4700         entry_t *ent, *prev;
4701         const char *fcn = "boot_entry_new()";
4702 
4703         assert(mp);
4704         assert(start);
4705         assert(end);
4706 
4707         ent = s_calloc(1, sizeof (entry_t));
4708         BAM_DPRINTF(("%s: new boot entry alloced\n", fcn));
4709         ent->start = start;
4710         ent->end = end;
4711 
4712         if (mp->entries == NULL) {
4713                 mp->entries = ent;
4714                 BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn));
4715                 return (ent);
4716         }
4717 
4718         prev = mp->entries;
4719         while (prev->next)
4720                 prev = prev->next;
4721         prev->next = ent;
4722         ent->prev = prev;
4723         BAM_DPRINTF(("%s: new boot entry linked in\n", fcn));
4724         return (ent);
4725 }
4726 
4727 static void
4728 boot_entry_addline(entry_t *ent, line_t *lp)
4729 {
4730         if (ent)
4731                 ent->end = lp;
4732 }
4733 
4734 /*
4735  * Check whether cmd matches the one indexed by which, and whether arg matches
4736  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4737  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
4738  * strstr(), so it can be a partial match.
4739  */
4740 static int
4741 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4742 {
4743         int                     ret;
4744         const char              *fcn = "check_cmd()";
4745 
4746         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, arg, str));
4747 
4748         if (cmd != NULL) {
4749                 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4750                     (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4751                         BAM_DPRINTF(("%s: command %s does not match %s\n",
4752                             fcn, cmd, menu_cmds[which]));
4753                         return (0);
4754                 }
4755                 ret = (strstr(arg, str) != NULL);
4756         } else
4757                 ret = 0;
4758 
4759         if (ret) {
4760                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
4761         } else {
4762                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
4763         }
4764 
4765         return (ret);
4766 }
4767 
4768 static error_t
4769 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4770 {
4771         const char              *fcn  = "kernel_parser()";
4772 
4773         assert(entry);
4774         assert(cmd);
4775         assert(arg);
4776 
4777         if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4778             strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4779                 BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn, cmd));
4780                 return (BAM_ERROR);
4781         }
4782 
4783         if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4784                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4785                     fcn, arg));
4786                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4787         } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4788             sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4789                 BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn, arg));
4790                 entry->flags |= BAM_ENTRY_DBOOT;
4791         } else if (strncmp(arg, DIRECT_BOOT_64,
4792             sizeof (DIRECT_BOOT_64) - 1) == 0) {
4793                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4794                     fcn, arg));
4795                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4796         } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4797             sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4798                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4799                     fcn, arg));
4800                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4801         } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4802             sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4803                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4804                     "flag: %s\n", fcn, arg));
4805                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4806                     | BAM_ENTRY_32BIT;
4807         } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4808             sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4809                 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4810                     "flag: %s\n", fcn, arg));
4811                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4812                     | BAM_ENTRY_64BIT;
4813         } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4814                 BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn, arg));
4815                 entry->flags |= BAM_ENTRY_MULTIBOOT;
4816         } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4817             sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4818                 BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4819                     "flag: %s\n", fcn, arg));
4820                 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4821         } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4822                 BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn, arg));
4823                 entry->flags |= BAM_ENTRY_HV;
4824         } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4825                 BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn, arg));
4826                 return (BAM_ERROR);
4827         } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4828             strstr(arg, UNIX_SPACE)) {
4829                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4830         } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4831             strstr(arg, AMD_UNIX_SPACE)) {
4832                 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4833         } else {
4834                 BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn, arg));
4835                 bam_error(_("kernel command on line %d not recognized.\n"),
4836                     linenum);
4837                 return (BAM_ERROR);
4838         }
4839 
4840         return (BAM_SUCCESS);
4841 }
4842 
4843 static error_t
4844 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4845 {
4846         const char              *fcn = "module_parser()";
4847 
4848         assert(entry);
4849         assert(cmd);
4850         assert(arg);
4851 
4852         if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4853             strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4854                 BAM_DPRINTF(("%s: not module cmd: %s\n", fcn, cmd));
4855                 return (BAM_ERROR);
4856         }
4857 
4858         if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4859             strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4860             strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4861             strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4862             strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4863             strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4864             strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4865             strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4866             strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4867                 BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn, arg));
4868                 return (BAM_SUCCESS);
4869         } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4870             !(entry->flags & BAM_ENTRY_LU)) {
4871                 /* don't emit warning for hand entries */
4872                 BAM_DPRINTF(("%s: is HAND module: %s\n", fcn, arg));
4873                 return (BAM_ERROR);
4874         } else {
4875                 BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn, arg));
4876                 bam_error(_("module command on line %d not recognized.\n"),
4877                     linenum);
4878                 return (BAM_ERROR);
4879         }
4880 }
4881 
4882 /*
4883  * A line in menu.lst looks like
4884  * [ ]*<cmd>[ \t=]*<arg>*
4885  */
4886 static void
4887 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4888 {
4889         /*
4890          * save state across calls. This is so that
4891          * header gets the right entry# after title has
4892          * been processed
4893          */
4894         static line_t *prev = NULL;
4895         static entry_t *curr_ent = NULL;
4896         static int in_liveupgrade = 0;
4897         static int is_libbe_ent = 0;
4898 
4899         line_t  *lp;
4900         char *cmd, *sep, *arg;
4901         char save, *cp, *line;
4902         menu_flag_t flag = BAM_INVALID;
4903         const char *fcn = "line_parser()";
4904 
4905         cmd = NULL;
4906         if (str == NULL) {
4907                 return;
4908         }
4909 
4910         /*
4911          * First save a copy of the entire line.
4912          * We use this later to set the line field.
4913          */
4914         line = s_strdup(str);
4915 
4916         /* Eat up leading whitespace */
4917         while (*str == ' ' || *str == '\t')
4918                 str++;
4919 
4920         if (*str == '#') {              /* comment */
4921                 cmd = s_strdup("#");
4922                 sep = NULL;
4923                 arg = s_strdup(str + 1);
4924                 flag = BAM_COMMENT;
4925                 if (strstr(arg, BAM_LU_HDR) != NULL) {
4926                         in_liveupgrade = 1;
4927                 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4928                         in_liveupgrade = 0;
4929                 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4930                         is_libbe_ent = 1;
4931                 }
4932         } else if (*str == '\0') {      /* blank line */
4933                 cmd = sep = arg = NULL;
4934                 flag = BAM_EMPTY;
4935         } else {
4936                 /*
4937                  * '=' is not a documented separator in grub syntax.
4938                  * However various development bits use '=' as a
4939                  * separator. In addition, external users also
4940                  * use = as a separator. So we will allow that usage.
4941                  */
4942                 cp = str;
4943                 while (*str != ' ' && *str != '\t' && *str != '=') {
4944                         if (*str == '\0') {
4945                                 cmd = s_strdup(cp);
4946                                 sep = arg = NULL;
4947                                 break;
4948                         }
4949                         str++;
4950                 }
4951 
4952                 if (*str != '\0') {
4953                         save = *str;
4954                         *str = '\0';
4955                         cmd = s_strdup(cp);
4956                         *str = save;
4957 
4958                         str++;
4959                         save = *str;
4960                         *str = '\0';
4961                         sep = s_strdup(str - 1);
4962                         *str = save;
4963 
4964                         while (*str == ' ' || *str == '\t')
4965                                 str++;
4966                         if (*str == '\0')
4967                                 arg = NULL;
4968                         else
4969                                 arg = s_strdup(str);
4970                 }
4971         }
4972 
4973         lp = s_calloc(1, sizeof (line_t));
4974 
4975         lp->cmd = cmd;
4976         lp->sep = sep;
4977         lp->arg = arg;
4978         lp->line = line;
4979         lp->lineNum = ++(*lineNum);
4980         if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4981                 lp->entryNum = ++(*entryNum);
4982                 lp->flags = BAM_TITLE;
4983                 if (prev && prev->flags == BAM_COMMENT &&
4984                     prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4985                         prev->entryNum = lp->entryNum;
4986                         curr_ent = boot_entry_new(mp, prev, lp);
4987                         curr_ent->flags |= BAM_ENTRY_BOOTADM;
4988                         BAM_DPRINTF(("%s: is bootadm(1M) entry: %s\n",
4989                             fcn, arg));
4990                 } else {
4991                         curr_ent = boot_entry_new(mp, lp, lp);
4992                         if (in_liveupgrade) {
4993                                 curr_ent->flags |= BAM_ENTRY_LU;
4994                                 BAM_DPRINTF(("%s: is LU entry: %s\n",
4995                                     fcn, arg));
4996                         }
4997                 }
4998                 curr_ent->entryNum = *entryNum;
4999         } else if (flag != BAM_INVALID) {
5000                 /*
5001                  * For header comments, the entry# is "fixed up"
5002                  * by the subsequent title
5003                  */
5004                 lp->entryNum = *entryNum;
5005                 lp->flags = flag;
5006         } else {
5007                 lp->entryNum = *entryNum;
5008 
5009                 if (*entryNum == ENTRY_INIT) {
5010                         lp->flags = BAM_GLOBAL;
5011                 } else {
5012                         lp->flags = BAM_ENTRY;
5013 
5014                         if (cmd && arg) {
5015                                 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
5016                                         BAM_DPRINTF(("%s: setting ROOT: %s\n",
5017                                             fcn, arg));
5018                                         curr_ent->flags |= BAM_ENTRY_ROOT;
5019                                 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
5020                                     == 0) {
5021                                         BAM_DPRINTF(("%s: setting "
5022                                             "FINDROOT: %s\n", fcn, arg));
5023                                         curr_ent->flags |= BAM_ENTRY_FINDROOT;
5024                                 } else if (strcmp(cmd,
5025                                     menu_cmds[CHAINLOADER_CMD]) == 0) {
5026                                         BAM_DPRINTF(("%s: setting "
5027                                             "CHAINLOADER: %s\n", fcn, arg));
5028                                         curr_ent->flags |=
5029                                             BAM_ENTRY_CHAINLOADER;
5030                                 } else if (kernel_parser(curr_ent, cmd, arg,
5031                                     lp->lineNum) != BAM_SUCCESS) {
5032                                         (void) module_parser(curr_ent, cmd,
5033                                             arg, lp->lineNum);
5034                                 }
5035                         }
5036                 }
5037         }
5038 
5039         /* record default, old default, and entry line ranges */
5040         if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
5041             strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
5042                 mp->curdefault = lp;
5043         } else if (lp->flags == BAM_COMMENT &&
5044             strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
5045                 mp->olddefault = lp;
5046         } else if (lp->flags == BAM_COMMENT &&
5047             strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
5048                 mp->old_rc_default = lp;
5049         } else if (lp->flags == BAM_ENTRY ||
5050             (lp->flags == BAM_COMMENT &&
5051             ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
5052                 if (is_libbe_ent) {
5053                         curr_ent->flags |= BAM_ENTRY_LIBBE;
5054                         is_libbe_ent = 0;
5055                 }
5056 
5057                 boot_entry_addline(curr_ent, lp);
5058         }
5059         append_line(mp, lp);
5060 
5061         prev = lp;
5062 }
5063 
5064 void
5065 update_numbering(menu_t *mp)
5066 {
5067         int lineNum;
5068         int entryNum;
5069         int old_default_value;
5070         line_t *lp, *prev, *default_lp, *default_entry;
5071         char buf[PATH_MAX];
5072 
5073         if (mp->start == NULL) {
5074                 return;
5075         }
5076 
5077         lineNum = LINE_INIT;
5078         entryNum = ENTRY_INIT;
5079         old_default_value = ENTRY_INIT;
5080         lp = default_lp = default_entry = NULL;
5081 
5082         prev = NULL;
5083         for (lp = mp->start; lp; prev = lp, lp = lp->next) {
5084                 lp->lineNum = ++lineNum;
5085 
5086                 /*
5087                  * Get the value of the default command
5088                  */
5089                 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
5090                     strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
5091                     lp->arg) {
5092                         old_default_value = atoi(lp->arg);
5093                         default_lp = lp;
5094                 }
5095 
5096                 /*
5097                  * If not a booting entry, nothing else to fix for this
5098                  * entry
5099                  */
5100                 if (lp->entryNum == ENTRY_INIT)
5101                         continue;
5102 
5103                 /*
5104                  * Record the position of the default entry.
5105                  * The following works because global
5106                  * commands like default and timeout should precede
5107                  * actual boot entries, so old_default_value
5108                  * is already known (or default cmd is missing).
5109                  */
5110                 if (default_entry == NULL &&
5111                     old_default_value != ENTRY_INIT &&
5112                     lp->entryNum == old_default_value) {
5113                         default_entry = lp;
5114                 }
5115 
5116                 /*
5117                  * Now fixup the entry number
5118                  */
5119                 if (lp->cmd != NULL &&
5120                     strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
5121                         lp->entryNum = ++entryNum;
5122                         /* fixup the bootadm header */
5123                         if (prev && prev->flags == BAM_COMMENT &&
5124                             prev->arg &&
5125                             strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
5126                                 prev->entryNum = lp->entryNum;
5127                         }
5128                 } else {
5129                         lp->entryNum = entryNum;
5130                 }
5131         }
5132 
5133         /*
5134          * No default command in menu, simply return
5135          */
5136         if (default_lp == NULL) {
5137                 return;
5138         }
5139 
5140         free(default_lp->arg);
5141         free(default_lp->line);
5142 
5143         if (default_entry == NULL) {
5144                 default_lp->arg = s_strdup("0");
5145         } else {
5146                 (void) snprintf(buf, sizeof (buf), "%d",
5147                     default_entry->entryNum);
5148                 default_lp->arg = s_strdup(buf);
5149         }
5150 
5151         /*
5152          * The following is required since only the line field gets
5153          * written back to menu.lst
5154          */
5155         (void) snprintf(buf, sizeof (buf), "%s%s%s",
5156             menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
5157         default_lp->line = s_strdup(buf);
5158 }
5159 
5160 
5161 static menu_t *
5162 menu_read(char *menu_path)
5163 {
5164         FILE *fp;
5165         char buf[BAM_MAXLINE], *cp;
5166         menu_t *mp;
5167         int line, entry, len, n;
5168 
5169         mp = s_calloc(1, sizeof (menu_t));
5170 
5171         fp = fopen(menu_path, "r");
5172         if (fp == NULL) { /* Let the caller handle this error */
5173                 free(mp);
5174                 return (NULL);
5175         }
5176 
5177         /* Note: GRUB boot entry number starts with 0 */
5178         line = LINE_INIT;
5179         entry = ENTRY_INIT;
5180         cp = buf;
5181         len = sizeof (buf);
5182         while (s_fgets(cp, len, fp) != NULL) {
5183                 n = strlen(cp);
5184                 if (cp[n - 1] == '\\') {
5185                         len -= n - 1;
5186                         assert(len >= 2);
5187                         cp += n - 1;
5188                         continue;
5189                 }
5190                 line_parser(mp, buf, &line, &entry);
5191                 cp = buf;
5192                 len = sizeof (buf);
5193         }
5194 
5195         if (fclose(fp) == EOF) {
5196                 bam_error(_("failed to close file: %s: %s\n"), menu_path,
5197                     strerror(errno));
5198         }
5199 
5200         return (mp);
5201 }
5202 
5203 static error_t
5204 selector(menu_t *mp, char *opt, int *entry, char **title)
5205 {
5206         char *eq;
5207         char *opt_dup;
5208         int entryNum;
5209 
5210         assert(mp);
5211         assert(mp->start);
5212         assert(opt);
5213 
5214         opt_dup = s_strdup(opt);
5215 
5216         if (entry)
5217                 *entry = ENTRY_INIT;
5218         if (title)
5219                 *title = NULL;
5220 
5221         eq = strchr(opt_dup, '=');
5222         if (eq == NULL) {
5223                 bam_error(_("invalid option: %s\n"), opt);
5224                 free(opt_dup);
5225                 return (BAM_ERROR);
5226         }
5227 
5228         *eq = '\0';
5229         if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
5230                 assert(mp->end);
5231                 entryNum = s_strtol(eq + 1);
5232                 if (entryNum < 0 || entryNum > mp->end->entryNum) {
5233                         bam_error(_("invalid boot entry number: %s\n"), eq + 1);
5234                         free(opt_dup);
5235                         return (BAM_ERROR);
5236                 }
5237                 *entry = entryNum;
5238         } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
5239                 *title = opt + (eq - opt_dup) + 1;
5240         } else {
5241                 bam_error(_("invalid option: %s\n"), opt);
5242                 free(opt_dup);
5243                 return (BAM_ERROR);
5244         }
5245 
5246         free(opt_dup);
5247         return (BAM_SUCCESS);
5248 }
5249 
5250 /*
5251  * If invoked with no titles/entries (opt == NULL)
5252  * only title lines in file are printed.
5253  *
5254  * If invoked with a title or entry #, all
5255  * lines in *every* matching entry are listed
5256  */
5257 static error_t
5258 list_entry(menu_t *mp, char *menu_path, char *opt)
5259 {
5260         line_t *lp;
5261         int entry = ENTRY_INIT;
5262         int found;
5263         char *title = NULL;
5264 
5265         assert(mp);
5266         assert(menu_path);
5267 
5268         /* opt is optional */
5269         BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path,
5270             opt ? opt : "<NULL>"));
5271 
5272         if (mp->start == NULL) {
5273                 bam_error(_("menu file not found: %s\n"), menu_path);
5274                 return (BAM_ERROR);
5275         }
5276 
5277         if (opt != NULL) {
5278                 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
5279                         return (BAM_ERROR);
5280                 }
5281                 assert((entry != ENTRY_INIT) ^ (title != NULL));
5282         } else {
5283                 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
5284                 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
5285         }
5286 
5287         found = 0;
5288         for (lp = mp->start; lp; lp = lp->next) {
5289                 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
5290                         continue;
5291                 if (opt == NULL && lp->flags == BAM_TITLE) {
5292                         bam_print(_("%d %s\n"), lp->entryNum,
5293                             lp->arg);
5294                         found = 1;
5295                         continue;
5296                 }
5297                 if (entry != ENTRY_INIT && lp->entryNum == entry) {
5298                         bam_print(_("%s\n"), lp->line);
5299                         found = 1;
5300                         continue;
5301                 }
5302 
5303                 /*
5304                  * We set the entry value here so that all lines
5305                  * in entry get printed. If we subsequently match
5306                  * title in other entries, all lines in those
5307                  * entries get printed as well.
5308                  */
5309                 if (title && lp->flags == BAM_TITLE && lp->arg &&
5310                     strncmp(title, lp->arg, strlen(title)) == 0) {
5311                         bam_print(_("%s\n"), lp->line);
5312                         entry = lp->entryNum;
5313                         found = 1;
5314                         continue;
5315                 }
5316         }
5317 
5318         if (!found) {
5319                 bam_error(_("no matching entry found\n"));
5320                 return (BAM_ERROR);
5321         }
5322 
5323         return (BAM_SUCCESS);
5324 }
5325 
5326 int
5327 add_boot_entry(menu_t *mp,
5328     char *title,
5329     char *findroot,
5330     char *kernel,
5331     char *mod_kernel,
5332     char *module,
5333     char *bootfs)
5334 {
5335         int             lineNum;
5336         int             entryNum;
5337         char            linebuf[BAM_MAXLINE];
5338         menu_cmd_t      k_cmd;
5339         menu_cmd_t      m_cmd;
5340         const char      *fcn = "add_boot_entry()";
5341 
5342         assert(mp);
5343 
5344         INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
5345         if (findroot == NULL) {
5346                 bam_error(_("can't find argument for findroot command\n"));
5347                 return (BAM_ERROR);
5348         }
5349 
5350         if (title == NULL) {
5351                 title = "Solaris";      /* default to Solaris */
5352         }
5353         if (kernel == NULL) {
5354                 bam_error(_("missing suboption: %s\n"), menu_cmds[KERNEL_CMD]);
5355                 return (BAM_ERROR);
5356         }
5357         if (module == NULL) {
5358                 if (bam_direct != BAM_DIRECT_DBOOT) {
5359                         bam_error(_("missing suboption: %s\n"),
5360                             menu_cmds[MODULE_CMD]);
5361                         return (BAM_ERROR);
5362                 }
5363 
5364                 /* Figure the commands out from the kernel line */
5365                 if (strstr(kernel, "$ISADIR") != NULL) {
5366                         module = DIRECT_BOOT_ARCHIVE;
5367                 } else if (strstr(kernel, "amd64") != NULL) {
5368                         module = DIRECT_BOOT_ARCHIVE_64;
5369                 } else {
5370                         module = DIRECT_BOOT_ARCHIVE_32;
5371                 }
5372         }
5373 
5374         k_cmd = KERNEL_DOLLAR_CMD;
5375         m_cmd = MODULE_DOLLAR_CMD;
5376 
5377         if (mp->start) {
5378                 lineNum = mp->end->lineNum;
5379                 entryNum = mp->end->entryNum;
5380         } else {
5381                 lineNum = LINE_INIT;
5382                 entryNum = ENTRY_INIT;
5383         }
5384 
5385         /*
5386          * No separator for comment (HDR/FTR) commands
5387          * The syntax for comments is #<comment>
5388          */
5389         (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5390             menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5391         line_parser(mp, linebuf, &lineNum, &entryNum);
5392 
5393         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5394             menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5395         line_parser(mp, linebuf, &lineNum, &entryNum);
5396 
5397         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5398             menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5399         line_parser(mp, linebuf, &lineNum, &entryNum);
5400         BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5401             fcn, lineNum, entryNum));
5402 
5403         if (bootfs != NULL) {
5404                 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5405                     menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5406                 line_parser(mp, linebuf, &lineNum, &entryNum);
5407         }
5408 
5409         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5410             menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5411         line_parser(mp, linebuf, &lineNum, &entryNum);
5412 
5413         if (mod_kernel != NULL) {
5414                 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5415                     menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5416                 line_parser(mp, linebuf, &lineNum, &entryNum);
5417         }
5418 
5419         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5420             menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5421         line_parser(mp, linebuf, &lineNum, &entryNum);
5422 
5423         (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5424             menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5425         line_parser(mp, linebuf, &lineNum, &entryNum);
5426 
5427         return (entryNum);
5428 }
5429 
5430 error_t
5431 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5432 {
5433         line_t          *lp;
5434         line_t          *freed;
5435         entry_t         *ent;
5436         entry_t         *tmp;
5437         int             deleted = 0;
5438         const char      *fcn = "delete_boot_entry()";
5439 
5440         assert(entryNum != ENTRY_INIT);
5441 
5442         tmp = NULL;
5443 
5444         ent = mp->entries;
5445         while (ent) {
5446                 lp = ent->start;
5447 
5448                 /*
5449                  * Check entry number and make sure it's a modifiable entry.
5450                  *
5451                  * Guidelines:
5452                  *      + We can modify a bootadm-created entry
5453                  *      + We can modify a libbe-created entry
5454                  */
5455                 if ((lp->flags != BAM_COMMENT &&
5456                     (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5457                     strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5458                     (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5459                         ent = ent->next;
5460                         continue;
5461                 }
5462 
5463                 /* free the entry content */
5464                 do {
5465                         freed = lp;
5466                         lp = lp->next;       /* prev stays the same */
5467                         BAM_DPRINTF(("%s: freeing line: %d\n",
5468                             fcn, freed->lineNum));
5469                         unlink_line(mp, freed);
5470                         line_free(freed);
5471                 } while (freed != ent->end);
5472 
5473                 /* free the entry_t structure */
5474                 assert(tmp == NULL);
5475                 tmp = ent;
5476                 ent = ent->next;
5477                 if (tmp->prev)
5478                         tmp->prev->next = ent;
5479                 else
5480                         mp->entries = ent;
5481                 if (ent)
5482                         ent->prev = tmp->prev;
5483                 BAM_DPRINTF(("%s: freeing entry: %d\n", fcn, tmp->entryNum));
5484                 free(tmp);
5485                 tmp = NULL;
5486                 deleted = 1;
5487         }
5488 
5489         assert(tmp == NULL);
5490 
5491         if (!deleted && entryNum != ALL_ENTRIES) {
5492                 if (quiet == DBE_PRINTERR)
5493                         bam_error(_("no matching bootadm entry found\n"));
5494                 return (BAM_ERROR);
5495         }
5496 
5497         /*
5498          * Now that we have deleted an entry, update
5499          * the entry numbering and the default cmd.
5500          */
5501         update_numbering(mp);
5502 
5503         return (BAM_SUCCESS);
5504 }
5505 
5506 static error_t
5507 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5508 {
5509         assert(mp);
5510         assert(dummy == NULL);
5511         assert(opt == NULL);
5512 
5513         BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5514 
5515         if (mp->start == NULL) {
5516                 bam_print(_("the GRUB menu is empty\n"));
5517                 return (BAM_SUCCESS);
5518         }
5519 
5520         if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5521                 return (BAM_ERROR);
5522         }
5523 
5524         return (BAM_WRITE);
5525 }
5526 
5527 static FILE *
5528 create_diskmap(char *osroot)
5529 {
5530         FILE *fp;
5531         char cmd[PATH_MAX + 16];
5532         char path[PATH_MAX];
5533         const char *fcn = "create_diskmap()";
5534 
5535         /* make sure we have a map file */
5536         fp = fopen(GRUBDISK_MAP, "r");
5537         if (fp == NULL) {
5538                 int     ret;
5539 
5540                 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5541                     CREATE_DISKMAP);
5542                 if (ret >= sizeof (path)) {
5543                         bam_error(_("unable to create path on mountpoint %s, "
5544                             "path too long\n"), osroot);
5545                         return (NULL);
5546                 }
5547                 if (is_safe_exec(path) == BAM_ERROR)
5548                         return (NULL);
5549 
5550                 (void) snprintf(cmd, sizeof (cmd),
5551                     "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5552                 if (exec_cmd(cmd, NULL) != 0)
5553                         return (NULL);
5554                 fp = fopen(GRUBDISK_MAP, "r");
5555                 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5556                 if (fp) {
5557                         BAM_DPRINTF(("%s: created diskmap file: %s\n",
5558                             fcn, GRUBDISK_MAP));
5559                 } else {
5560                         BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5561                             fcn, GRUBDISK_MAP));
5562                 }
5563         }
5564         return (fp);
5565 }
5566 
5567 #define SECTOR_SIZE     512
5568 
5569 static int
5570 get_partition(char *device)
5571 {
5572         int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5573         struct mboot *mboot;
5574         char boot_sect[SECTOR_SIZE];
5575         char *wholedisk, *slice;
5576 #ifdef i386
5577         ext_part_t *epp;
5578         uint32_t secnum, numsec;
5579         int rval, pno, ext_partno = PARTNO_NOTFOUND;
5580 #endif
5581 
5582         /* form whole disk (p0) */
5583         slice = device + strlen(device) - 2;
5584         is_pcfs = (*slice != 's');
5585         if (!is_pcfs)
5586                 *slice = '\0';
5587         wholedisk = s_calloc(1, strlen(device) + 3);
5588         (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5589         if (!is_pcfs)
5590                 *slice = 's';
5591 
5592         /* read boot sector */
5593         fd = open(wholedisk, O_RDONLY);
5594         if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5595                 return (partno);
5596         }
5597         (void) close(fd);
5598 
5599 #ifdef i386
5600         /* Read/Initialize extended partition information */
5601         if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5602             != FDISK_SUCCESS) {
5603                 switch (rval) {
5604                         /*
5605                          * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5606                          * be considered as soft errors and hence
5607                          * we do not return
5608                          */
5609                         case FDISK_EBADLOGDRIVE:
5610                                 break;
5611                         case FDISK_ENOLOGDRIVE:
5612                                 break;
5613                         case FDISK_EBADMAGIC:
5614                                 /*FALLTHROUGH*/
5615                         default:
5616                                 free(wholedisk);
5617                                 libfdisk_fini(&epp);
5618                                 return (partno);
5619                 }
5620         }
5621 #endif
5622         free(wholedisk);
5623 
5624         /* parse fdisk table */
5625         mboot = (struct mboot *)((void *)boot_sect);
5626         for (i = 0; i < FD_NUMPART; i++) {
5627                 struct ipart *part =
5628                     (struct ipart *)(uintptr_t)mboot->parts + i;
5629                 if (is_pcfs) {  /* looking for solaris boot part */
5630                         if (part->systid == 0xbe) {
5631                                 partno = i;
5632                                 break;
5633                         }
5634                 } else {        /* look for solaris partition, old and new */
5635                         if (part->systid == EFI_PMBR) {
5636                                 partno = PARTNO_EFI;
5637                                 break;
5638                         }
5639 
5640 #ifdef i386
5641                         if ((part->systid == SUNIXOS &&
5642                             (fdisk_is_linux_swap(epp, part->relsect,
5643                             NULL) != 0)) || part->systid == SUNIXOS2) {
5644 #else
5645                         if (part->systid == SUNIXOS ||
5646                             part->systid == SUNIXOS2) {
5647 #endif
5648                                 partno = i;
5649                                 break;
5650                         }
5651 
5652 #ifdef i386
5653                         if (fdisk_is_dos_extended(part->systid))
5654                                 ext_partno = i;
5655 #endif
5656                 }
5657         }
5658 #ifdef i386
5659         /* If no primary solaris partition, check extended partition */
5660         if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5661                 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5662                 if (rval == FDISK_SUCCESS) {
5663                         partno = pno - 1;
5664                 }
5665         }
5666         libfdisk_fini(&epp);
5667 #endif
5668         return (partno);
5669 }
5670 
5671 char *
5672 get_grubroot(char *osroot, char *osdev, char *menu_root)
5673 {
5674         char            *grubroot;      /* (hd#,#,#) */
5675         char            *slice;
5676         char            *grubhd = NULL;
5677         int             fdiskpart;
5678         int             found = 0;
5679         char            *devname;
5680         char            *ctdname = strstr(osdev, "dsk/");
5681         char            linebuf[PATH_MAX];
5682         FILE            *fp;
5683 
5684         INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5685         if (ctdname == NULL) {
5686                 bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev);
5687                 return (NULL);
5688         }
5689 
5690         if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5691                 /* menu bears no resemblance to our reality */
5692                 bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5693                     "bootdisk: %s\n"), osdev);
5694                 return (NULL);
5695         }
5696 
5697         ctdname += strlen("dsk/");
5698         slice = strrchr(ctdname, 's');
5699         if (slice)
5700                 *slice = '\0';
5701 
5702         fp = create_diskmap(osroot);
5703         if (fp == NULL) {
5704                 bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5705                     osroot);
5706                 return (NULL);
5707         }
5708 
5709         rewind(fp);
5710         while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5711                 grubhd = strtok(linebuf, " \t\n");
5712                 if (grubhd)
5713                         devname = strtok(NULL, " \t\n");
5714                 else
5715                         devname = NULL;
5716                 if (devname && strcmp(devname, ctdname) == 0) {
5717                         found = 1;
5718                         break;
5719                 }
5720         }
5721 
5722         if (slice)
5723                 *slice = 's';
5724 
5725         (void) fclose(fp);
5726         fp = NULL;
5727 
5728         INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5729         if (found == 0) {
5730                 bam_error(_("not using biosdev command for disk: %s.\n"),
5731                     osdev);
5732                 return (NULL);
5733         }
5734 
5735         fdiskpart = get_partition(osdev);
5736         INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5737         if (fdiskpart == PARTNO_NOTFOUND) {
5738                 bam_error(_("failed to determine fdisk partition: %s\n"),
5739                     osdev);
5740                 return (NULL);
5741         }
5742 
5743         grubroot = s_calloc(1, 10);
5744         if (fdiskpart == PARTNO_EFI) {
5745                 fdiskpart = atoi(&slice[1]);
5746                 slice = NULL;
5747         }
5748 
5749         if (slice) {
5750                 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5751                     grubhd, fdiskpart, slice[1] + 'a' - '0');
5752         } else
5753                 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5754                     grubhd, fdiskpart);
5755 
5756         assert(fp == NULL);
5757         assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5758         return (grubroot);
5759 }
5760 
5761 static char *
5762 find_primary_common(char *mntpt, char *fstype)
5763 {
5764         char            signdir[PATH_MAX];
5765         char            tmpsign[MAXNAMELEN + 1];
5766         char            *lu;
5767         char            *ufs;
5768         char            *zfs;
5769         DIR             *dirp = NULL;
5770         struct dirent   *entp;
5771         struct stat     sb;
5772         const char      *fcn = "find_primary_common()";
5773 
5774         (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5775             mntpt, GRUBSIGN_DIR);
5776 
5777         if (stat(signdir, &sb) == -1) {
5778                 BAM_DPRINTF(("%s: no sign dir: %s\n", fcn, signdir));
5779                 return (NULL);
5780         }
5781 
5782         dirp = opendir(signdir);
5783         INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5784         if (dirp == NULL) {
5785                 bam_error(_("opendir of %s failed: %s\n"), signdir,
5786                     strerror(errno));
5787                 return (NULL);
5788         }
5789 
5790         ufs = zfs = lu = NULL;
5791 
5792         while ((entp = readdir(dirp)) != NULL) {
5793                 if (strcmp(entp->d_name, ".") == 0 ||
5794                     strcmp(entp->d_name, "..") == 0)
5795                         continue;
5796 
5797                 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5798 
5799                 if (lu == NULL &&
5800                     strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5801                     strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5802                         lu = s_strdup(tmpsign);
5803                 }
5804 
5805                 if (ufs == NULL &&
5806                     strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5807                     strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5808                         ufs = s_strdup(tmpsign);
5809                 }
5810 
5811                 if (zfs == NULL &&
5812                     strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5813                     strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5814                         zfs = s_strdup(tmpsign);
5815                 }
5816         }
5817 
5818         BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn,
5819             zfs ? zfs : "NULL",
5820             ufs ? ufs : "NULL",
5821             lu ? lu : "NULL"));
5822 
5823         if (dirp) {
5824                 (void) closedir(dirp);
5825                 dirp = NULL;
5826         }
5827 
5828         if (strcmp(fstype, "ufs") == 0 && zfs) {
5829                 bam_error(_("found mismatched boot signature %s for "
5830                     "filesystem type: %s.\n"), zfs, "ufs");
5831                 free(zfs);
5832                 zfs = NULL;
5833         } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5834                 bam_error(_("found mismatched boot signature %s for "
5835                     "filesystem type: %s.\n"), ufs, "zfs");
5836                 free(ufs);
5837                 ufs = NULL;
5838         }
5839 
5840         assert(dirp == NULL);
5841 
5842         /* For now, we let Live Upgrade take care of its signature itself */
5843         if (lu) {
5844                 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5845                 free(lu);
5846                 lu = NULL;
5847         }
5848 
5849         return (zfs ? zfs : ufs);
5850 }
5851 
5852 static char *
5853 find_backup_common(char *mntpt, char *fstype)
5854 {
5855         FILE            *bfp = NULL;
5856         char            tmpsign[MAXNAMELEN + 1];
5857         char            backup[PATH_MAX];
5858         char            *ufs;
5859         char            *zfs;
5860         char            *lu;
5861         int             error;
5862         const char      *fcn = "find_backup_common()";
5863 
5864         /*
5865          * We didn't find it in the primary directory.
5866          * Look at the backup
5867          */
5868         (void) snprintf(backup, sizeof (backup), "%s%s",
5869             mntpt, GRUBSIGN_BACKUP);
5870 
5871         bfp = fopen(backup, "r");
5872         if (bfp == NULL) {
5873                 error = errno;
5874                 if (bam_verbose) {
5875                         bam_error(_("failed to open file: %s: %s\n"),
5876                             backup, strerror(error));
5877                 }
5878                 BAM_DPRINTF(("%s: failed to open %s: %s\n",
5879                     fcn, backup, strerror(error)));
5880                 return (NULL);
5881         }
5882 
5883         ufs = zfs = lu = NULL;
5884 
5885         while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5886 
5887                 if (lu == NULL &&
5888                     strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5889                     strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5890                         lu = s_strdup(tmpsign);
5891                 }
5892 
5893                 if (ufs == NULL &&
5894                     strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5895                     strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5896                         ufs = s_strdup(tmpsign);
5897                 }
5898 
5899                 if (zfs == NULL &&
5900                     strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5901                     strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5902                         zfs = s_strdup(tmpsign);
5903                 }
5904         }
5905 
5906         BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn,
5907             zfs ? zfs : "NULL",
5908             ufs ? ufs : "NULL",
5909             lu ? lu : "NULL"));
5910 
5911         if (bfp) {
5912                 (void) fclose(bfp);
5913                 bfp = NULL;
5914         }
5915 
5916         if (strcmp(fstype, "ufs") == 0 && zfs) {
5917                 bam_error(_("found mismatched boot signature %s for "
5918                     "filesystem type: %s.\n"), zfs, "ufs");
5919                 free(zfs);
5920                 zfs = NULL;
5921         } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5922                 bam_error(_("found mismatched boot signature %s for "
5923                     "filesystem type: %s.\n"), ufs, "zfs");
5924                 free(ufs);
5925                 ufs = NULL;
5926         }
5927 
5928         assert(bfp == NULL);
5929 
5930         /* For now, we let Live Upgrade take care of its signature itself */
5931         if (lu) {
5932                 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5933                 free(lu);
5934                 lu = NULL;
5935         }
5936 
5937         return (zfs ? zfs : ufs);
5938 }
5939 
5940 static char *
5941 find_ufs_existing(char *osroot)
5942 {
5943         char            *sign;
5944         const char      *fcn = "find_ufs_existing()";
5945 
5946         sign = find_primary_common(osroot, "ufs");
5947         if (sign == NULL) {
5948                 sign = find_backup_common(osroot, "ufs");
5949                 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
5950                     sign ? sign : "NULL"));
5951         } else {
5952                 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
5953         }
5954 
5955         return (sign);
5956 }
5957 
5958 char *
5959 get_mountpoint(char *special, char *fstype)
5960 {
5961         FILE            *mntfp;
5962         struct mnttab   mp = {0};
5963         struct mnttab   mpref = {0};
5964         int             error;
5965         int             ret;
5966         const char      *fcn = "get_mountpoint()";
5967 
5968         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
5969 
5970         mntfp = fopen(MNTTAB, "r");
5971         error = errno;
5972         INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5973         if (mntfp == NULL) {
5974                 bam_error(_("failed to open file: %s: %s\n"),
5975                     MNTTAB, strerror(error));
5976                 return (NULL);
5977         }
5978 
5979         mpref.mnt_special = special;
5980         mpref.mnt_fstype = fstype;
5981 
5982         ret = getmntany(mntfp, &mp, &mpref);
5983         INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5984         if (ret != 0) {
5985                 (void) fclose(mntfp);
5986                 BAM_DPRINTF(("%s: no mount-point for special=%s and "
5987                     "fstype=%s\n", fcn, special, fstype));
5988                 return (NULL);
5989         }
5990         (void) fclose(mntfp);
5991 
5992         assert(mp.mnt_mountp);
5993 
5994         BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5995             fcn, special, mp.mnt_mountp));
5996 
5997         return (s_strdup(mp.mnt_mountp));
5998 }
5999 
6000 /*
6001  * Mounts a "legacy" top dataset (if needed)
6002  * Returns:     The mountpoint of the legacy top dataset or NULL on error
6003  *              mnted returns one of the above values defined for zfs_mnted_t
6004  */
6005 static char *
6006 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
6007 {
6008         char            cmd[PATH_MAX];
6009         char            tmpmnt[PATH_MAX];
6010         filelist_t      flist = {0};
6011         char            *is_mounted;
6012         struct stat     sb;
6013         int             ret;
6014         const char      *fcn = "mount_legacy_dataset()";
6015 
6016         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
6017 
6018         *mnted = ZFS_MNT_ERROR;
6019 
6020         (void) snprintf(cmd, sizeof (cmd),
6021             "/sbin/zfs get -Ho value mounted %s",
6022             pool);
6023 
6024         ret = exec_cmd(cmd, &flist);
6025         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
6026         if (ret != 0) {
6027                 bam_error(_("failed to determine mount status of ZFS "
6028                     "pool %s\n"), pool);
6029                 return (NULL);
6030         }
6031 
6032         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
6033         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6034                 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
6035                 filelist_free(&flist);
6036                 return (NULL);
6037         }
6038 
6039         is_mounted = strtok(flist.head->line, " \t\n");
6040         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
6041         INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
6042         if (strcmp(is_mounted, "no") != 0) {
6043                 filelist_free(&flist);
6044                 *mnted = LEGACY_ALREADY;
6045                 /* get_mountpoint returns a strdup'ed string */
6046                 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
6047                     fcn, pool));
6048                 return (get_mountpoint(pool, "zfs"));
6049         }
6050 
6051         filelist_free(&flist);
6052 
6053         /*
6054          * legacy top dataset is not mounted. Mount it now
6055          * First create a mountpoint.
6056          */
6057         (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
6058             ZFS_LEGACY_MNTPT, getpid());
6059 
6060         ret = stat(tmpmnt, &sb);
6061         if (ret == -1) {
6062                 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
6063                     fcn, pool, tmpmnt));
6064                 ret = mkdirp(tmpmnt, DIR_PERMS);
6065                 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
6066                 if (ret == -1) {
6067                         bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
6068                             strerror(errno));
6069                         return (NULL);
6070                 }
6071         } else {
6072                 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
6073                     "present\n", fcn, pool, tmpmnt));
6074         }
6075 
6076         (void) snprintf(cmd, sizeof (cmd),
6077             "/sbin/mount -F zfs %s %s",
6078             pool, tmpmnt);
6079 
6080         ret = exec_cmd(cmd, NULL);
6081         INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
6082         if (ret != 0) {
6083                 bam_error(_("mount of ZFS pool %s failed\n"), pool);
6084                 (void) rmdir(tmpmnt);
6085                 return (NULL);
6086         }
6087 
6088         *mnted = LEGACY_MOUNTED;
6089         BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
6090             fcn, pool, tmpmnt));
6091         return (s_strdup(tmpmnt));
6092 }
6093 
6094 /*
6095  * Mounts the top dataset (if needed)
6096  * Returns:     The mountpoint of the top dataset or NULL on error
6097  *              mnted returns one of the above values defined for zfs_mnted_t
6098  */
6099 char *
6100 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
6101 {
6102         char            cmd[PATH_MAX];
6103         filelist_t      flist = {0};
6104         char            *is_mounted;
6105         char            *mntpt;
6106         char            *zmntpt;
6107         int             ret;
6108         const char      *fcn = "mount_top_dataset()";
6109 
6110         *mnted = ZFS_MNT_ERROR;
6111 
6112         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
6113 
6114         /*
6115          * First check if the top dataset is a "legacy" dataset
6116          */
6117         (void) snprintf(cmd, sizeof (cmd),
6118             "/sbin/zfs get -Ho value mountpoint %s",
6119             pool);
6120         ret = exec_cmd(cmd, &flist);
6121         INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
6122         if (ret != 0) {
6123                 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6124                     pool);
6125                 return (NULL);
6126         }
6127 
6128         if (flist.head && (flist.head == flist.tail)) {
6129                 char *legacy = strtok(flist.head->line, " \t\n");
6130                 if (legacy && strcmp(legacy, "legacy") == 0) {
6131                         filelist_free(&flist);
6132                         BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
6133                         return (mount_legacy_dataset(pool, mnted));
6134                 }
6135         }
6136 
6137         filelist_free(&flist);
6138 
6139         BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
6140 
6141         (void) snprintf(cmd, sizeof (cmd),
6142             "/sbin/zfs get -Ho value mounted %s",
6143             pool);
6144 
6145         ret = exec_cmd(cmd, &flist);
6146         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
6147         if (ret != 0) {
6148                 bam_error(_("failed to determine mount status of ZFS "
6149                     "pool %s\n"), pool);
6150                 return (NULL);
6151         }
6152 
6153         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
6154         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6155                 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
6156                 filelist_free(&flist);
6157                 return (NULL);
6158         }
6159 
6160         is_mounted = strtok(flist.head->line, " \t\n");
6161         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
6162         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
6163         if (strcmp(is_mounted, "no") != 0) {
6164                 filelist_free(&flist);
6165                 *mnted = ZFS_ALREADY;
6166                 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
6167                     fcn, pool));
6168                 goto mounted;
6169         }
6170 
6171         filelist_free(&flist);
6172         BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
6173             fcn, pool));
6174 
6175         /* top dataset is not mounted. Mount it now */
6176         (void) snprintf(cmd, sizeof (cmd),
6177             "/sbin/zfs mount %s", pool);
6178         ret = exec_cmd(cmd, NULL);
6179         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
6180         if (ret != 0) {
6181                 bam_error(_("mount of ZFS pool %s failed\n"), pool);
6182                 return (NULL);
6183         }
6184         *mnted = ZFS_MOUNTED;
6185         BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
6186         /*FALLTHRU*/
6187 mounted:
6188         /*
6189          * Now get the mountpoint
6190          */
6191         (void) snprintf(cmd, sizeof (cmd),
6192             "/sbin/zfs get -Ho value mountpoint %s",
6193             pool);
6194 
6195         ret = exec_cmd(cmd, &flist);
6196         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
6197         if (ret != 0) {
6198                 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6199                     pool);
6200                 goto error;
6201         }
6202 
6203         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
6204         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6205                 bam_error(_("ZFS pool %s has no mount-point\n"), pool);
6206                 goto error;
6207         }
6208 
6209         mntpt = strtok(flist.head->line, " \t\n");
6210         INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
6211         if (*mntpt != '/') {
6212                 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6213                     pool, mntpt);
6214                 goto error;
6215         }
6216         zmntpt = s_strdup(mntpt);
6217 
6218         filelist_free(&flist);
6219 
6220         BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6221             fcn, pool, zmntpt));
6222 
6223         return (zmntpt);
6224 
6225 error:
6226         filelist_free(&flist);
6227         (void) umount_top_dataset(pool, *mnted, NULL);
6228         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6229         return (NULL);
6230 }
6231 
6232 int
6233 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
6234 {
6235         char            cmd[PATH_MAX];
6236         int             ret;
6237         const char      *fcn = "umount_top_dataset()";
6238 
6239         INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
6240         switch (mnted) {
6241         case LEGACY_ALREADY:
6242         case ZFS_ALREADY:
6243                 /* nothing to do */
6244                 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6245                     "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
6246                 free(mntpt);
6247                 return (BAM_SUCCESS);
6248         case LEGACY_MOUNTED:
6249                 (void) snprintf(cmd, sizeof (cmd),
6250                     "/sbin/umount %s", pool);
6251                 ret = exec_cmd(cmd, NULL);
6252                 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
6253                 if (ret != 0) {
6254                         bam_error(_("umount of %s failed\n"), pool);
6255                         free(mntpt);
6256                         return (BAM_ERROR);
6257                 }
6258                 if (mntpt)
6259                         (void) rmdir(mntpt);
6260                 free(mntpt);
6261                 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6262                     "successfully unmounted\n", fcn, pool));
6263                 return (BAM_SUCCESS);
6264         case ZFS_MOUNTED:
6265                 free(mntpt);
6266                 (void) snprintf(cmd, sizeof (cmd),
6267                     "/sbin/zfs unmount %s", pool);
6268                 ret = exec_cmd(cmd, NULL);
6269                 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
6270                 if (ret != 0) {
6271                         bam_error(_("umount of %s failed\n"), pool);
6272                         return (BAM_ERROR);
6273                 }
6274                 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6275                     "successfully unmounted\n", fcn, pool));
6276                 return (BAM_SUCCESS);
6277         default:
6278                 bam_error(_("Internal error: bad saved mount state for "
6279                     "pool %s\n"), pool);
6280                 return (BAM_ERROR);
6281         }
6282         /*NOTREACHED*/
6283 }
6284 
6285 /*
6286  * For ZFS, osdev can be one of two forms
6287  * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6288  * It can be a /dev/[r]dsk special file. We handle both instances
6289  */
6290 static char *
6291 get_pool(char *osdev)
6292 {
6293         char            cmd[PATH_MAX];
6294         char            buf[PATH_MAX];
6295         filelist_t      flist = {0};
6296         char            *pool;
6297         char            *cp;
6298         char            *slash;
6299         int             ret;
6300         const char      *fcn = "get_pool()";
6301 
6302         INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
6303         if (osdev == NULL) {
6304                 bam_error(_("NULL device: cannot determine pool name\n"));
6305                 return (NULL);
6306         }
6307 
6308         BAM_DPRINTF(("%s: osdev arg = %s\n", fcn, osdev));
6309 
6310         if (osdev[0] != '/') {
6311                 (void) strlcpy(buf, osdev, sizeof (buf));
6312                 slash = strchr(buf, '/');
6313                 if (slash)
6314                         *slash = '\0';
6315                 pool = s_strdup(buf);
6316                 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6317                 return (pool);
6318         } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6319             strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6320                 bam_error(_("invalid device %s: cannot determine pool name\n"),
6321                     osdev);
6322                 return (NULL);
6323         }
6324 
6325         /*
6326          * Call the zfs fstyp directly since this is a zpool. This avoids
6327          * potential pcfs conflicts if the first block wasn't cleared.
6328          */
6329         (void) snprintf(cmd, sizeof (cmd),
6330             "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6331             osdev);
6332 
6333         ret = exec_cmd(cmd, &flist);
6334         INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
6335         if (ret != 0) {
6336                 bam_error(_("fstyp -a on device %s failed\n"), osdev);
6337                 return (NULL);
6338         }
6339 
6340         INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
6341         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6342                 bam_error(_("NULL fstyp -a output for device %s\n"), osdev);
6343                 filelist_free(&flist);
6344                 return (NULL);
6345         }
6346 
6347         (void) strtok(flist.head->line, "'");
6348         cp = strtok(NULL, "'");
6349         INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6350         if (cp == NULL) {
6351                 bam_error(_("bad fstyp -a output for device %s\n"), osdev);
6352                 filelist_free(&flist);
6353                 return (NULL);
6354         }
6355 
6356         pool = s_strdup(cp);
6357 
6358         filelist_free(&flist);
6359 
6360         BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6361 
6362         return (pool);
6363 }
6364 
6365 static char *
6366 find_zfs_existing(char *osdev)
6367 {
6368         char            *pool;
6369         zfs_mnted_t     mnted;
6370         char            *mntpt;
6371         char            *sign;
6372         const char      *fcn = "find_zfs_existing()";
6373 
6374         pool = get_pool(osdev);
6375         INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6376         if (pool == NULL) {
6377                 bam_error(_("failed to get pool for device: %s\n"), osdev);
6378                 return (NULL);
6379         }
6380 
6381         mntpt = mount_top_dataset(pool, &mnted);
6382         INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
6383         if (mntpt == NULL) {
6384                 bam_error(_("failed to mount top dataset for pool: %s\n"),
6385                     pool);
6386                 free(pool);
6387                 return (NULL);
6388         }
6389 
6390         sign = find_primary_common(mntpt, "zfs");
6391         if (sign == NULL) {
6392                 sign = find_backup_common(mntpt, "zfs");
6393                 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
6394                     sign ? sign : "NULL"));
6395         } else {
6396                 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
6397         }
6398 
6399         (void) umount_top_dataset(pool, mnted, mntpt);
6400 
6401         free(pool);
6402 
6403         return (sign);
6404 }
6405 
6406 static char *
6407 find_existing_sign(char *osroot, char *osdev, char *fstype)
6408 {
6409         const char              *fcn = "find_existing_sign()";
6410 
6411         INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6412         if (strcmp(fstype, "ufs") == 0) {
6413                 BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn));
6414                 return (find_ufs_existing(osroot));
6415         } else if (strcmp(fstype, "zfs") == 0) {
6416                 BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn));
6417                 return (find_zfs_existing(osdev));
6418         } else {
6419                 bam_error(_("boot signature not supported for fstype: %s\n"),
6420                     fstype);
6421                 return (NULL);
6422         }
6423 }
6424 
6425 #define MH_HASH_SZ      16
6426 
6427 typedef enum {
6428         MH_ERROR = -1,
6429         MH_NOMATCH,
6430         MH_MATCH
6431 } mh_search_t;
6432 
6433 typedef struct mcache {
6434         char    *mc_special;
6435         char    *mc_mntpt;
6436         char    *mc_fstype;
6437         struct mcache *mc_next;
6438 } mcache_t;
6439 
6440 typedef struct mhash {
6441         mcache_t *mh_hash[MH_HASH_SZ];
6442 } mhash_t;
6443 
6444 static int
6445 mhash_fcn(char *key)
6446 {
6447         int             i;
6448         uint64_t        sum = 0;
6449 
6450         for (i = 0; key[i] != '\0'; i++) {
6451                 sum += (uchar_t)key[i];
6452         }
6453 
6454         sum %= MH_HASH_SZ;
6455 
6456         assert(sum < MH_HASH_SZ);
6457 
6458         return (sum);
6459 }
6460 
6461 static mhash_t *
6462 cache_mnttab(void)
6463 {
6464         FILE            *mfp;
6465         struct extmnttab mnt;
6466         mcache_t        *mcp;
6467         mhash_t         *mhp;
6468         char            *ctds;
6469         int             idx;
6470         int             error;
6471         char            *special_dup;
6472         const char      *fcn = "cache_mnttab()";
6473 
6474         mfp = fopen(MNTTAB, "r");
6475         error = errno;
6476         INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6477         if (mfp == NULL) {
6478                 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
6479                     strerror(error));
6480                 return (NULL);
6481         }
6482 
6483         mhp = s_calloc(1, sizeof (mhash_t));
6484 
6485         resetmnttab(mfp);
6486 
6487         while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6488                 /* only cache ufs */
6489                 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6490                         continue;
6491 
6492                 /* basename() modifies its arg, so dup it */
6493                 special_dup = s_strdup(mnt.mnt_special);
6494                 ctds = basename(special_dup);
6495 
6496                 mcp = s_calloc(1, sizeof (mcache_t));
6497                 mcp->mc_special = s_strdup(ctds);
6498                 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6499                 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6500                 BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6501                     "fstype=%s\n", fcn, ctds, mnt.mnt_mountp, mnt.mnt_fstype));
6502                 idx = mhash_fcn(ctds);
6503                 mcp->mc_next = mhp->mh_hash[idx];
6504                 mhp->mh_hash[idx] = mcp;
6505                 free(special_dup);
6506         }
6507 
6508         (void) fclose(mfp);
6509 
6510         return (mhp);
6511 }
6512 
6513 static void
6514 free_mnttab(mhash_t *mhp)
6515 {
6516         mcache_t        *mcp;
6517         int             i;
6518 
6519         for (i = 0; i < MH_HASH_SZ; i++) {
6520                 while ((mcp = mhp->mh_hash[i]) != NULL) {
6521                         mhp->mh_hash[i] = mcp->mc_next;
6522                         free(mcp->mc_special);
6523                         free(mcp->mc_mntpt);
6524                         free(mcp->mc_fstype);
6525                         free(mcp);
6526                 }
6527         }
6528 
6529         for (i = 0; i < MH_HASH_SZ; i++) {
6530                 assert(mhp->mh_hash[i] == NULL);
6531         }
6532         free(mhp);
6533 }
6534 
6535 static mh_search_t
6536 search_hash(mhash_t *mhp, char *special, char **mntpt)
6537 {
6538         int             idx;
6539         mcache_t        *mcp;
6540         const char      *fcn = "search_hash()";
6541 
6542         assert(mntpt);
6543 
6544         *mntpt = NULL;
6545 
6546         INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6547         if (strchr(special, '/')) {
6548                 bam_error(_("invalid key for mnttab hash: %s\n"), special);
6549                 return (MH_ERROR);
6550         }
6551 
6552         idx = mhash_fcn(special);
6553 
6554         for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6555                 if (strcmp(mcp->mc_special, special) == 0)
6556                         break;
6557         }
6558 
6559         if (mcp == NULL) {
6560                 BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn, special));
6561                 return (MH_NOMATCH);
6562         }
6563 
6564         assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6565         *mntpt = mcp->mc_mntpt;
6566         BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn, special));
6567         return (MH_MATCH);
6568 }
6569 
6570 static int
6571 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6572 {
6573         char            *sign;
6574         char            *signline;
6575         char            signbuf[MAXNAMELEN];
6576         int             len;
6577         int             error;
6578         const char      *fcn = "check_add_ufs_sign_to_list()";
6579 
6580         /* safe to specify NULL as "osdev" arg for UFS */
6581         sign = find_existing_sign(mntpt, NULL, "ufs");
6582         if (sign == NULL) {
6583                 /* No existing signature, nothing to add to list */
6584                 BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6585                     fcn, mntpt));
6586                 return (0);
6587         }
6588 
6589         (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6590         signline = signbuf;
6591 
6592         INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6593         if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6594             strlen(GRUBSIGN_UFS_PREFIX))) {
6595                 bam_error(_("invalid UFS boot signature %s\n"), sign);
6596                 free(sign);
6597                 /* ignore invalid signatures */
6598                 return (0);
6599         }
6600 
6601         len = fputs(signline, tfp);
6602         error = errno;
6603         INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6604         if (len != strlen(signline)) {
6605                 bam_error(_("failed to write signature %s to signature "
6606                     "list: %s\n"), sign, strerror(error));
6607                 free(sign);
6608                 return (-1);
6609         }
6610 
6611         free(sign);
6612 
6613         BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6614             fcn, mntpt));
6615         return (0);
6616 }
6617 
6618 /*
6619  * slice is a basename not a full pathname
6620  */
6621 static int
6622 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6623 {
6624         int             ret;
6625         char            cmd[PATH_MAX];
6626         char            path[PATH_MAX];
6627         struct stat     sbuf;
6628         char            *mntpt;
6629         filelist_t      flist = {0};
6630         char            *fstype;
6631         char            blkslice[PATH_MAX];
6632         const char      *fcn = "process_slice_common()";
6633 
6634 
6635         ret = search_hash(mhp, slice, &mntpt);
6636         switch (ret) {
6637                 case MH_MATCH:
6638                         if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6639                                 return (-1);
6640                         else
6641                                 return (0);
6642                 case MH_NOMATCH:
6643                         break;
6644                 case MH_ERROR:
6645                 default:
6646                         return (-1);
6647         }
6648 
6649         (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6650         if (stat(path, &sbuf) == -1) {
6651                 BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn, path));
6652                 return (0);
6653         }
6654 
6655         /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6656         (void) snprintf(cmd, sizeof (cmd),
6657             "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6658             slice);
6659 
6660         if (exec_cmd(cmd, &flist) != 0) {
6661                 if (bam_verbose)
6662                         bam_print(_("fstyp failed for slice: %s\n"), slice);
6663                 return (0);
6664         }
6665 
6666         if ((flist.head == NULL) || (flist.head != flist.tail)) {
6667                 if (bam_verbose)
6668                         bam_print(_("bad output from fstyp for slice: %s\n"),
6669                             slice);
6670                 filelist_free(&flist);
6671                 return (0);
6672         }
6673 
6674         fstype = strtok(flist.head->line, " \t\n");
6675         if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6676                 if (bam_verbose)
6677                         bam_print(_("%s is not a ufs slice: %s\n"),
6678                             slice, fstype);
6679                 filelist_free(&flist);
6680                 return (0);
6681         }
6682 
6683         filelist_free(&flist);
6684 
6685         /*
6686          * Since we are mounting the filesystem read-only, the
6687          * the last mount field of the superblock is unchanged
6688          * and does not need to be fixed up post-mount;
6689          */
6690 
6691         (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6692             slice);
6693 
6694         (void) snprintf(cmd, sizeof (cmd),
6695             "/usr/sbin/mount -F ufs -o ro %s %s "
6696             "> /dev/null 2>&1", blkslice, tmpmnt);
6697 
6698         if (exec_cmd(cmd, NULL) != 0) {
6699                 if (bam_verbose)
6700                         bam_print(_("mount of %s (fstype %s) failed\n"),
6701                             blkslice, "ufs");
6702                 return (0);
6703         }
6704 
6705         ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6706 
6707         (void) snprintf(cmd, sizeof (cmd),
6708             "/usr/sbin/umount -f %s > /dev/null 2>&1",
6709             tmpmnt);
6710 
6711         if (exec_cmd(cmd, NULL) != 0) {
6712                 bam_print(_("umount of %s failed\n"), slice);
6713                 return (0);
6714         }
6715 
6716         return (ret);
6717 }
6718 
6719 static int
6720 process_vtoc_slices(
6721         char *s0,
6722         struct vtoc *vtoc,
6723         FILE *tfp,
6724         mhash_t *mhp,
6725         char *tmpmnt)
6726 {
6727         int             idx;
6728         char            slice[PATH_MAX];
6729         size_t          len;
6730         char            *cp;
6731         const char      *fcn = "process_vtoc_slices()";
6732 
6733         len = strlen(s0);
6734 
6735         assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6736 
6737         s0[len - 1] = '\0';
6738 
6739         (void) strlcpy(slice, s0, sizeof (slice));
6740 
6741         s0[len - 1] = '0';
6742 
6743         cp = slice + len - 1;
6744 
6745         for (idx = 0; idx < vtoc->v_nparts; idx++) {
6746 
6747                 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6748 
6749                 if (vtoc->v_part[idx].p_size == 0) {
6750                         BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6751                             fcn, slice));
6752                         continue;
6753                 }
6754 
6755                 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6756                 switch (vtoc->v_part[idx].p_tag) {
6757                 case V_SWAP:
6758                 case V_USR:
6759                 case V_BACKUP:
6760                 case V_VAR:
6761                 case V_HOME:
6762                 case V_ALTSCTR:
6763                         BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6764                             "skipping: %s\n", fcn, slice));
6765                         continue;
6766                 default:
6767                         BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6768                             fcn, slice));
6769                         break;
6770                 }
6771 
6772                 /* skip unmountable and readonly slices */
6773                 switch (vtoc->v_part[idx].p_flag) {
6774                 case V_UNMNT:
6775                 case V_RONLY:
6776                         BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6777                             fcn, slice));
6778                         continue;
6779                 default:
6780                         BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6781                             fcn, slice));
6782                         break;
6783                 }
6784 
6785                 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6786                         return (-1);
6787                 }
6788         }
6789 
6790         return (0);
6791 }
6792 
6793 static int
6794 process_efi_slices(
6795         char *s0,
6796         struct dk_gpt *efi,
6797         FILE *tfp,
6798         mhash_t *mhp,
6799         char *tmpmnt)
6800 {
6801         int             idx;
6802         char            slice[PATH_MAX];
6803         size_t          len;
6804         char            *cp;
6805         const char      *fcn = "process_efi_slices()";
6806 
6807         len = strlen(s0);
6808 
6809         assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6810 
6811         s0[len - 1] = '\0';
6812 
6813         (void) strlcpy(slice, s0, sizeof (slice));
6814 
6815         s0[len - 1] = '0';
6816 
6817         cp = slice + len - 1;
6818 
6819         for (idx = 0; idx < efi->efi_nparts; idx++) {
6820 
6821                 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6822 
6823                 if (efi->efi_parts[idx].p_size == 0) {
6824                         BAM_DPRINTF(("%s: EFI: skipping 0-length slice: %s\n",
6825                             fcn, slice));
6826                         continue;
6827                 }
6828 
6829                 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6830                 switch (efi->efi_parts[idx].p_tag) {
6831                 case V_SWAP:
6832                 case V_USR:
6833                 case V_BACKUP:
6834                 case V_VAR:
6835                 case V_HOME:
6836                 case V_ALTSCTR:
6837                         BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6838                             fcn, slice));
6839                         continue;
6840                 default:
6841                         BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6842                             fcn, slice));
6843                         break;
6844                 }
6845 
6846                 /* skip unmountable and readonly slices */
6847                 switch (efi->efi_parts[idx].p_flag) {
6848                 case V_UNMNT:
6849                 case V_RONLY:
6850                         BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6851                             fcn, slice));
6852                         continue;
6853                 default:
6854                         BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6855                             fcn, slice));
6856                         break;
6857                 }
6858 
6859                 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6860                         return (-1);
6861                 }
6862         }
6863 
6864         return (0);
6865 }
6866 
6867 /*
6868  * s0 is a basename not a full path
6869  */
6870 static int
6871 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6872 {
6873         struct vtoc             vtoc;
6874         struct dk_gpt           *efi;
6875         char                    s0path[PATH_MAX];
6876         struct stat             sbuf;
6877         int                     e_flag;
6878         int                     v_flag;
6879         int                     retval;
6880         int                     err;
6881         int                     fd;
6882         const char              *fcn = "process_slice0()";
6883 
6884         (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6885 
6886         if (stat(s0path, &sbuf) == -1) {
6887                 BAM_DPRINTF(("%s: slice 0 does not exist: %s\n", fcn, s0path));
6888                 return (0);
6889         }
6890 
6891         fd = open(s0path, O_NONBLOCK|O_RDONLY);
6892         if (fd == -1) {
6893                 bam_error(_("failed to open file: %s: %s\n"), s0path,
6894                     strerror(errno));
6895                 return (0);
6896         }
6897 
6898         e_flag = v_flag = 0;
6899         retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6900         switch (retval) {
6901                 case VT_EIO:
6902                         BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6903                             fcn, s0path));
6904                         break;
6905                 case VT_EINVAL:
6906                         BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6907                             fcn, s0path));
6908                         break;
6909                 case VT_ERROR:
6910                         BAM_DPRINTF(("%s: VTOC: unknown error while "
6911                             "reading: %s\n", fcn, s0path));
6912                         break;
6913                 case VT_ENOTSUP:
6914                         e_flag = 1;
6915                         BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6916                             fcn, s0path));
6917                         break;
6918                 case 0:
6919                         v_flag = 1;
6920                         BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6921                             fcn, s0path));
6922                         break;
6923                 default:
6924                         BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6925                             "code: %s\n", fcn, s0path));
6926                         break;
6927         }
6928 
6929 
6930         if (e_flag) {
6931                 e_flag = 0;
6932                 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6933                 switch (retval) {
6934                 case VT_EIO:
6935                         BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6936                             fcn, s0path));
6937                         break;
6938                 case VT_EINVAL:
6939                         BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn, s0path));
6940                         break;
6941                 case VT_ERROR:
6942                         BAM_DPRINTF(("%s: EFI: unknown error while "
6943                             "reading: %s\n", fcn, s0path));
6944                         break;
6945                 case VT_ENOTSUP:
6946                         BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6947                             fcn, s0path));
6948                         break;
6949                 case 0:
6950                         e_flag = 1;
6951                         BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6952                             fcn, s0path));
6953                         break;
6954                 default:
6955                         BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6956                             fcn, s0path));
6957                         break;
6958                 }
6959         }
6960 
6961         (void) close(fd);
6962 
6963         if (v_flag) {
6964                 retval = process_vtoc_slices(s0,
6965                     &vtoc, tfp, mhp, tmpmnt);
6966         } else if (e_flag) {
6967                 retval = process_efi_slices(s0,
6968                     efi, tfp, mhp, tmpmnt);
6969         } else {
6970                 BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6971                     fcn, s0path));
6972                 return (0);
6973         }
6974 
6975         return (retval);
6976 }
6977 
6978 /*
6979  * Find and create a list of all existing UFS boot signatures
6980  */
6981 static int
6982 FindAllUfsSignatures(void)
6983 {
6984         mhash_t         *mnttab_hash;
6985         DIR             *dirp = NULL;
6986         struct dirent   *dp;
6987         char            tmpmnt[PATH_MAX];
6988         char            cmd[PATH_MAX];
6989         struct stat     sb;
6990         int             fd;
6991         FILE            *tfp;
6992         size_t          len;
6993         int             ret;
6994         int             error;
6995         const char      *fcn = "FindAllUfsSignatures()";
6996 
6997         if (stat(UFS_SIGNATURE_LIST, &sb) != -1)  {
6998                 bam_print(_("       - signature list %s exists\n"),
6999                     UFS_SIGNATURE_LIST);
7000                 return (0);
7001         }
7002 
7003         fd = open(UFS_SIGNATURE_LIST".tmp",
7004             O_RDWR|O_CREAT|O_TRUNC, 0644);
7005         error = errno;
7006         INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
7007         if (fd == -1) {
7008                 bam_error(_("failed to open file: %s: %s\n"),
7009                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7010                 return (-1);
7011         }
7012 
7013         ret = close(fd);
7014         error = errno;
7015         INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
7016         if (ret == -1) {
7017                 bam_error(_("failed to close file: %s: %s\n"),
7018                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7019                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7020                 return (-1);
7021         }
7022 
7023         tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7024         error = errno;
7025         INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
7026         if (tfp == NULL) {
7027                 bam_error(_("failed to open file: %s: %s\n"),
7028                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7029                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7030                 return (-1);
7031         }
7032 
7033         mnttab_hash = cache_mnttab();
7034         INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
7035         if (mnttab_hash == NULL) {
7036                 (void) fclose(tfp);
7037                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7038                 bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn);
7039                 return (-1);
7040         }
7041 
7042         (void) snprintf(tmpmnt, sizeof (tmpmnt),
7043             "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
7044         (void) unlink(tmpmnt);
7045 
7046         ret = mkdirp(tmpmnt, DIR_PERMS);
7047         error = errno;
7048         INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
7049         if (ret == -1) {
7050                 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
7051                     strerror(error));
7052                 free_mnttab(mnttab_hash);
7053                 (void) fclose(tfp);
7054                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7055                 return (-1);
7056         }
7057 
7058         dirp = opendir("/dev/rdsk");
7059         error = errno;
7060         INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
7061         if (dirp == NULL) {
7062                 bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
7063                     strerror(error));
7064                 goto fail;
7065         }
7066 
7067         while ((dp = readdir(dirp)) != NULL) {
7068                 if (strcmp(dp->d_name, ".") == 0 ||
7069                     strcmp(dp->d_name, "..") == 0)
7070                         continue;
7071 
7072                 /*
7073                  * we only look for the s0 slice. This is guranteed to
7074                  * have 's' at len - 2.
7075                  */
7076                 len = strlen(dp->d_name);
7077                 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
7078                         BAM_DPRINTF(("%s: skipping non-s0 slice: %s\n",
7079                             fcn, dp->d_name));
7080                         continue;
7081                 }
7082 
7083                 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
7084                 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
7085                 if (ret == -1)
7086                         goto fail;
7087         }
7088 
7089         (void) closedir(dirp);
7090         free_mnttab(mnttab_hash);
7091         (void) rmdir(tmpmnt);
7092 
7093         ret = fclose(tfp);
7094         error = errno;
7095         INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
7096         if (ret == EOF) {
7097                 bam_error(_("failed to close file: %s: %s\n"),
7098                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7099                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7100                 return (-1);
7101         }
7102 
7103         /* We have a list of existing GRUB signatures. Sort it first */
7104         (void) snprintf(cmd, sizeof (cmd),
7105             "/usr/bin/sort -u %s.tmp > %s.sorted",
7106             UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7107 
7108         ret = exec_cmd(cmd, NULL);
7109         INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
7110         if (ret != 0) {
7111                 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7112                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7113                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7114                 return (-1);
7115         }
7116 
7117         (void) unlink(UFS_SIGNATURE_LIST".tmp");
7118 
7119         ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7120         error = errno;
7121         INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
7122         if (ret == -1) {
7123                 bam_error(_("rename to file failed: %s: %s\n"),
7124                     UFS_SIGNATURE_LIST, strerror(error));
7125                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7126                 return (-1);
7127         }
7128 
7129         if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
7130                 BAM_DPRINTF(("%s: generated zero length signlist: %s.\n",
7131                     fcn, UFS_SIGNATURE_LIST));
7132         }
7133 
7134         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7135         return (0);
7136 
7137 fail:
7138         if (dirp)
7139                 (void) closedir(dirp);
7140         free_mnttab(mnttab_hash);
7141         (void) rmdir(tmpmnt);
7142         (void) fclose(tfp);
7143         (void) unlink(UFS_SIGNATURE_LIST".tmp");
7144         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7145         return (-1);
7146 }
7147 
7148 static char *
7149 create_ufs_sign(void)
7150 {
7151         struct stat     sb;
7152         int             signnum = -1;
7153         char            tmpsign[MAXNAMELEN + 1];
7154         char            *numstr;
7155         int             i;
7156         FILE            *tfp;
7157         int             ret;
7158         int             error;
7159         const char      *fcn = "create_ufs_sign()";
7160 
7161         bam_print(_("  - searching for UFS boot signatures\n"));
7162 
7163         ret = FindAllUfsSignatures();
7164         INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
7165         if (ret == -1) {
7166                 bam_error(_("search for UFS boot signatures failed\n"));
7167                 return (NULL);
7168         }
7169 
7170         /* Make sure the list exists and is owned by root */
7171         INJECT_ERROR1("SIGNLIST_NOT_CREATED",
7172             (void) unlink(UFS_SIGNATURE_LIST));
7173         if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
7174                 (void) unlink(UFS_SIGNATURE_LIST);
7175                 bam_error(_("missing UFS signature list file: %s\n"),
7176                     UFS_SIGNATURE_LIST);
7177                 return (NULL);
7178         }
7179 
7180         if (sb.st_size == 0) {
7181                 bam_print(_("   - no existing UFS boot signatures\n"));
7182                 i = 0;
7183                 goto found;
7184         }
7185 
7186         /* The signature list was sorted when it was created */
7187         tfp = fopen(UFS_SIGNATURE_LIST, "r");
7188         error = errno;
7189         INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
7190         if (tfp == NULL) {
7191                 bam_error(_("error opening UFS boot signature list "
7192                     "file %s: %s\n"), UFS_SIGNATURE_LIST, strerror(error));
7193                 (void) unlink(UFS_SIGNATURE_LIST);
7194                 return (NULL);
7195         }
7196 
7197         for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
7198 
7199                 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
7200                     strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7201                         (void) fclose(tfp);
7202                         (void) unlink(UFS_SIGNATURE_LIST);
7203                         bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7204                         return (NULL);
7205                 }
7206                 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
7207 
7208                 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
7209                         (void) fclose(tfp);
7210                         (void) unlink(UFS_SIGNATURE_LIST);
7211                         bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7212                         return (NULL);
7213                 }
7214 
7215                 signnum = atoi(numstr);
7216                 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
7217                 if (signnum < 0) {
7218                         (void) fclose(tfp);
7219                         (void) unlink(UFS_SIGNATURE_LIST);
7220                         bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7221                         return (NULL);
7222                 }
7223 
7224                 if (i != signnum) {
7225                         BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7226                             fcn, i));
7227                         break;
7228                 }
7229         }
7230 
7231         (void) fclose(tfp);
7232 
7233 found:
7234         (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
7235 
7236         /* add the ufs signature to the /var/run list of signatures */
7237         ret = ufs_add_to_sign_list(tmpsign);
7238         INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
7239         if (ret == -1) {
7240                 (void) unlink(UFS_SIGNATURE_LIST);
7241                 bam_error(_("failed to add sign %s to signlist.\n"), tmpsign);
7242                 return (NULL);
7243         }
7244 
7245         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7246 
7247         return (s_strdup(tmpsign));
7248 }
7249 
7250 static char *
7251 get_fstype(char *osroot)
7252 {
7253         FILE            *mntfp;
7254         struct mnttab   mp = {0};
7255         struct mnttab   mpref = {0};
7256         int             error;
7257         int             ret;
7258         const char      *fcn = "get_fstype()";
7259 
7260         INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
7261         if (osroot == NULL) {
7262                 bam_error(_("no OS mountpoint. Cannot determine fstype\n"));
7263                 return (NULL);
7264         }
7265 
7266         mntfp = fopen(MNTTAB, "r");
7267         error = errno;
7268         INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
7269         if (mntfp == NULL) {
7270                 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7271                     strerror(error));
7272                 return (NULL);
7273         }
7274 
7275         if (*osroot == '\0')
7276                 mpref.mnt_mountp = "/";
7277         else
7278                 mpref.mnt_mountp = osroot;
7279 
7280         ret = getmntany(mntfp, &mp, &mpref);
7281         INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
7282         if (ret != 0) {
7283                 bam_error(_("failed to find OS mountpoint %s in %s\n"),
7284                     osroot, MNTTAB);
7285                 (void) fclose(mntfp);
7286                 return (NULL);
7287         }
7288         (void) fclose(mntfp);
7289 
7290         INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
7291         if (mp.mnt_fstype == NULL) {
7292                 bam_error(_("NULL fstype found for OS root %s\n"), osroot);
7293                 return (NULL);
7294         }
7295 
7296         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7297 
7298         return (s_strdup(mp.mnt_fstype));
7299 }
7300 
7301 static char *
7302 create_zfs_sign(char *osdev)
7303 {
7304         char            tmpsign[PATH_MAX];
7305         char            *pool;
7306         const char      *fcn = "create_zfs_sign()";
7307 
7308         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, osdev));
7309 
7310         /*
7311          * First find the pool name
7312          */
7313         pool = get_pool(osdev);
7314         INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
7315         if (pool == NULL) {
7316                 bam_error(_("failed to get pool name from %s\n"), osdev);
7317                 return (NULL);
7318         }
7319 
7320         (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
7321 
7322         BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn, tmpsign));
7323 
7324         free(pool);
7325 
7326         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7327 
7328         return (s_strdup(tmpsign));
7329 }
7330 
7331 static char *
7332 create_new_sign(char *osdev, char *fstype)
7333 {
7334         char            *sign;
7335         const char      *fcn = "create_new_sign()";
7336 
7337         INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
7338 
7339         if (strcmp(fstype, "zfs") == 0) {
7340                 BAM_DPRINTF(("%s: created new ZFS sign\n", fcn));
7341                 sign = create_zfs_sign(osdev);
7342         } else if (strcmp(fstype, "ufs") == 0) {
7343                 BAM_DPRINTF(("%s: created new UFS sign\n", fcn));
7344                 sign = create_ufs_sign();
7345         } else {
7346                 bam_error(_("boot signature not supported for fstype: %s\n"),
7347                     fstype);
7348                 sign = NULL;
7349         }
7350 
7351         BAM_DPRINTF(("%s: created new sign: %s\n", fcn,
7352             sign ? sign : "<NULL>"));
7353         return (sign);
7354 }
7355 
7356 static int
7357 set_backup_common(char *mntpt, char *sign)
7358 {
7359         FILE            *bfp;
7360         char            backup[PATH_MAX];
7361         char            tmpsign[PATH_MAX];
7362         int             error;
7363         char            *bdir;
7364         char            *backup_dup;
7365         struct stat     sb;
7366         int             ret;
7367         const char      *fcn = "set_backup_common()";
7368 
7369         (void) snprintf(backup, sizeof (backup), "%s%s",
7370             mntpt, GRUBSIGN_BACKUP);
7371 
7372         /* First read the backup */
7373         bfp = fopen(backup, "r");
7374         if (bfp != NULL) {
7375                 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
7376                         if (strcmp(tmpsign, sign) == 0) {
7377                                 BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7378                                     fcn, sign));
7379                                 (void) fclose(bfp);
7380                                 return (0);
7381                         }
7382                 }
7383                 (void) fclose(bfp);
7384                 BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7385                     fcn, sign));
7386         } else {
7387                 BAM_DPRINTF(("%s: no backup file (%s) found.\n", fcn, backup));
7388         }
7389 
7390         /*
7391          * Didn't find the correct signature. First create
7392          * the directory if necessary.
7393          */
7394 
7395         /* dirname() modifies its argument so dup it */
7396         backup_dup = s_strdup(backup);
7397         bdir = dirname(backup_dup);
7398         assert(bdir);
7399 
7400         ret = stat(bdir, &sb);
7401         INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
7402         if (ret == -1) {
7403                 BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7404                     fcn, bdir));
7405                 ret = mkdirp(bdir, DIR_PERMS);
7406                 error = errno;
7407                 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
7408                 if (ret == -1) {
7409                         bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7410                             GRUBSIGN_BACKUP, strerror(error));
7411                         free(backup_dup);
7412                         return (-1);
7413                 }
7414         }
7415         free(backup_dup);
7416 
7417         /*
7418          * Open the backup in append mode to add the correct
7419          * signature;
7420          */
7421         bfp = fopen(backup, "a");
7422         error = errno;
7423         INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
7424         if (bfp == NULL) {
7425                 bam_error(_("error opening boot signature backup "
7426                     "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7427                 return (-1);
7428         }
7429 
7430         (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
7431 
7432         ret = fputs(tmpsign, bfp);
7433         error = errno;
7434         INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
7435         if (ret != strlen(tmpsign)) {
7436                 bam_error(_("error writing boot signature backup "
7437                     "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7438                 (void) fclose(bfp);
7439                 return (-1);
7440         }
7441 
7442         (void) fclose(bfp);
7443 
7444         if (bam_verbose)
7445                 bam_print(_("updated boot signature backup file %s\n"),
7446                     GRUBSIGN_BACKUP);
7447 
7448         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7449 
7450         return (0);
7451 }
7452 
7453 static int
7454 set_backup_ufs(char *osroot, char *sign)
7455 {
7456         const char      *fcn = "set_backup_ufs()";
7457 
7458         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7459         return (set_backup_common(osroot, sign));
7460 }
7461 
7462 static int
7463 set_backup_zfs(char *osdev, char *sign)
7464 {
7465         char            *pool;
7466         char            *mntpt;
7467         zfs_mnted_t     mnted;
7468         int             ret;
7469         const char      *fcn = "set_backup_zfs()";
7470 
7471         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7472 
7473         pool = get_pool(osdev);
7474         INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7475         if (pool == NULL) {
7476                 bam_error(_("failed to get pool name from %s\n"), osdev);
7477                 return (-1);
7478         }
7479 
7480         mntpt = mount_top_dataset(pool, &mnted);
7481         INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7482         if (mntpt == NULL) {
7483                 bam_error(_("failed to mount top dataset for %s\n"), pool);
7484                 free(pool);
7485                 return (-1);
7486         }
7487 
7488         ret = set_backup_common(mntpt, sign);
7489 
7490         (void) umount_top_dataset(pool, mnted, mntpt);
7491 
7492         free(pool);
7493 
7494         INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7495         if (ret == 0) {
7496                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7497         } else {
7498                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7499         }
7500 
7501         return (ret);
7502 }
7503 
7504 static int
7505 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7506 {
7507         const char      *fcn = "set_backup()";
7508         int             ret;
7509 
7510         INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7511 
7512         if (strcmp(fstype, "ufs") == 0) {
7513                 BAM_DPRINTF(("%s: setting UFS backup sign\n", fcn));
7514                 ret = set_backup_ufs(osroot, sign);
7515         } else if (strcmp(fstype, "zfs") == 0) {
7516                 BAM_DPRINTF(("%s: setting ZFS backup sign\n", fcn));
7517                 ret = set_backup_zfs(osdev, sign);
7518         } else {
7519                 bam_error(_("boot signature not supported for fstype: %s\n"),
7520                     fstype);
7521                 ret = -1;
7522         }
7523 
7524         if (ret == 0) {
7525                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7526         } else {
7527                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7528         }
7529 
7530         return (ret);
7531 }
7532 
7533 static int
7534 set_primary_common(char *mntpt, char *sign)
7535 {
7536         char            signfile[PATH_MAX];
7537         char            signdir[PATH_MAX];
7538         struct stat     sb;
7539         int             fd;
7540         int             error;
7541         int             ret;
7542         const char      *fcn = "set_primary_common()";
7543 
7544         (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7545             mntpt, GRUBSIGN_DIR, sign);
7546 
7547         if (stat(signfile, &sb) != -1) {
7548                 if (bam_verbose)
7549                         bam_print(_("primary sign %s exists\n"), sign);
7550                 return (0);
7551         } else {
7552                 BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7553                     fcn, signfile));
7554         }
7555 
7556         (void) snprintf(signdir, sizeof (signdir), "%s/%s",
7557             mntpt, GRUBSIGN_DIR);
7558 
7559         if (stat(signdir, &sb) == -1) {
7560                 BAM_DPRINTF(("%s: primary signdir (%s) does not exist\n",
7561                     fcn, signdir));
7562                 ret = mkdirp(signdir, DIR_PERMS);
7563                 error = errno;
7564                 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7565                 if (ret == -1) {
7566                         bam_error(_("error creating boot signature "
7567                             "directory %s: %s\n"), signdir, strerror(errno));
7568                         return (-1);
7569                 }
7570         }
7571 
7572         fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7573         error = errno;
7574         INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7575         if (fd == -1) {
7576                 bam_error(_("error creating primary boot signature %s: %s\n"),
7577                     signfile, strerror(error));
7578                 return (-1);
7579         }
7580 
7581         ret = fsync(fd);
7582         error = errno;
7583         INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7584         if (ret != 0) {
7585                 bam_error(_("error syncing primary boot signature %s: %s\n"),
7586                     signfile, strerror(error));
7587         }
7588 
7589         (void) close(fd);
7590 
7591         if (bam_verbose)
7592                 bam_print(_("created primary GRUB boot signature: %s\n"),
7593                     signfile);
7594 
7595         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7596 
7597         return (0);
7598 }
7599 
7600 static int
7601 set_primary_ufs(char *osroot, char *sign)
7602 {
7603         const char      *fcn = "set_primary_ufs()";
7604 
7605         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7606         return (set_primary_common(osroot, sign));
7607 }
7608 
7609 static int
7610 set_primary_zfs(char *osdev, char *sign)
7611 {
7612         char            *pool;
7613         char            *mntpt;
7614         zfs_mnted_t     mnted;
7615         int             ret;
7616         const char      *fcn = "set_primary_zfs()";
7617 
7618         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7619 
7620         pool = get_pool(osdev);
7621         INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7622         if (pool == NULL) {
7623                 bam_error(_("failed to get pool name from %s\n"), osdev);
7624                 return (-1);
7625         }
7626 
7627         /* Pool name must exist in the sign */
7628         ret = (strstr(sign, pool) != NULL);
7629         INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7630         if (ret == 0) {
7631                 bam_error(_("pool name %s not present in signature %s\n"),
7632                     pool, sign);
7633                 free(pool);
7634                 return (-1);
7635         }
7636 
7637         mntpt = mount_top_dataset(pool, &mnted);
7638         INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7639         if (mntpt == NULL) {
7640                 bam_error(_("failed to mount top dataset for %s\n"), pool);
7641                 free(pool);
7642                 return (-1);
7643         }
7644 
7645         ret = set_primary_common(mntpt, sign);
7646 
7647         (void) umount_top_dataset(pool, mnted, mntpt);
7648 
7649         free(pool);
7650 
7651         INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7652         if (ret == 0) {
7653                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7654         } else {
7655                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7656         }
7657 
7658         return (ret);
7659 }
7660 
7661 static int
7662 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7663 {
7664         const char      *fcn = "set_primary()";
7665         int             ret;
7666 
7667         INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7668         if (strcmp(fstype, "ufs") == 0) {
7669                 BAM_DPRINTF(("%s: setting UFS primary sign\n", fcn));
7670                 ret = set_primary_ufs(osroot, sign);
7671         } else if (strcmp(fstype, "zfs") == 0) {
7672                 BAM_DPRINTF(("%s: setting ZFS primary sign\n", fcn));
7673                 ret = set_primary_zfs(osdev, sign);
7674         } else {
7675                 bam_error(_("boot signature not supported for fstype: %s\n"),
7676                     fstype);
7677                 ret = -1;
7678         }
7679 
7680         if (ret == 0) {
7681                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7682         } else {
7683                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7684         }
7685 
7686         return (ret);
7687 }
7688 
7689 static int
7690 ufs_add_to_sign_list(char *sign)
7691 {
7692         FILE            *tfp;
7693         char            signline[MAXNAMELEN];
7694         char            cmd[PATH_MAX];
7695         int             ret;
7696         int             error;
7697         const char      *fcn = "ufs_add_to_sign_list()";
7698 
7699         INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7700         if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7701             strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7702                 bam_error(_("invalid UFS boot signature %s\n"), sign);
7703                 (void) unlink(UFS_SIGNATURE_LIST);
7704                 return (-1);
7705         }
7706 
7707         /*
7708          * most failures in this routine are not a fatal error
7709          * We simply unlink the /var/run file and continue
7710          */
7711 
7712         ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7713         error = errno;
7714         INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7715         if (ret == -1) {
7716                 bam_error(_("rename to file failed: %s: %s\n"),
7717                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7718                 (void) unlink(UFS_SIGNATURE_LIST);
7719                 return (0);
7720         }
7721 
7722         tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7723         error = errno;
7724         INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7725         if (tfp == NULL) {
7726                 bam_error(_("failed to open file: %s: %s\n"),
7727                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7728                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7729                 return (0);
7730         }
7731 
7732         (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7733 
7734         ret = fputs(signline, tfp);
7735         error = errno;
7736         INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7737         if (ret != strlen(signline)) {
7738                 bam_error(_("failed to write signature %s to signature "
7739                     "list: %s\n"), sign, strerror(error));
7740                 (void) fclose(tfp);
7741                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7742                 return (0);
7743         }
7744 
7745         ret = fclose(tfp);
7746         error = errno;
7747         INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7748         if (ret == EOF) {
7749                 bam_error(_("failed to close file: %s: %s\n"),
7750                     UFS_SIGNATURE_LIST".tmp", strerror(error));
7751                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7752                 return (0);
7753         }
7754 
7755         /* Sort the list again */
7756         (void) snprintf(cmd, sizeof (cmd),
7757             "/usr/bin/sort -u %s.tmp > %s.sorted",
7758             UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7759 
7760         ret = exec_cmd(cmd, NULL);
7761         INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7762         if (ret != 0) {
7763                 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7764                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7765                 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7766                 return (0);
7767         }
7768 
7769         (void) unlink(UFS_SIGNATURE_LIST".tmp");
7770 
7771         ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7772         error = errno;
7773         INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7774         if (ret == -1) {
7775                 bam_error(_("rename to file failed: %s: %s\n"),
7776                     UFS_SIGNATURE_LIST, strerror(error));
7777                 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7778                 return (0);
7779         }
7780 
7781         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7782 
7783         return (0);
7784 }
7785 
7786 static int
7787 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7788 {
7789         int             ret;
7790         const char      *fcn = "set_signature()";
7791 
7792         BAM_DPRINTF(("%s: entered. args: %s %s %s %s\n", fcn,
7793             osroot, osdev, sign, fstype));
7794 
7795         ret = set_backup(osroot, osdev, sign, fstype);
7796         INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7797         if (ret == -1) {
7798                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7799                 bam_error(_("failed to set backup sign (%s) for %s: %s\n"),
7800                     sign, osroot, osdev);
7801                 return (-1);
7802         }
7803 
7804         ret = set_primary(osroot, osdev, sign, fstype);
7805         INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7806 
7807         if (ret == 0) {
7808                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7809         } else {
7810                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7811                 bam_error(_("failed to set primary sign (%s) for %s: %s\n"),
7812                     sign, osroot, osdev);
7813 
7814         }
7815         return (ret);
7816 }
7817 
7818 char *
7819 get_grubsign(char *osroot, char *osdev)
7820 {
7821         char            *grubsign;      /* (<sign>,#,#) */
7822         char            *slice;
7823         int             fdiskpart;
7824         char            *sign;
7825         char            *fstype;
7826         int             ret;
7827         const char      *fcn = "get_grubsign()";
7828 
7829         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, osdev));
7830         fstype = get_fstype(osroot);
7831         INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7832         if (fstype == NULL) {
7833                 bam_error(_("failed to get fstype for %s\n"), osroot);
7834                 return (NULL);
7835         }
7836 
7837         sign = find_existing_sign(osroot, osdev, fstype);
7838         INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7839         if (sign == NULL) {
7840                 BAM_DPRINTF(("%s: no existing grubsign for %s: %s\n",
7841                     fcn, osroot, osdev));
7842                 sign = create_new_sign(osdev, fstype);
7843                 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7844                 if (sign == NULL) {
7845                         bam_error(_("failed to create GRUB boot signature for "
7846                             "device: %s\n"), osdev);
7847                         free(fstype);
7848                         return (NULL);
7849                 }
7850         }
7851 
7852         ret = set_signature(osroot, osdev, sign, fstype);
7853         INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7854         if (ret == -1) {
7855                 bam_error(_("failed to write GRUB boot signature for "
7856                     "device: %s\n"), osdev);
7857                 free(sign);
7858                 free(fstype);
7859                 (void) unlink(UFS_SIGNATURE_LIST);
7860                 return (NULL);
7861         }
7862 
7863         free(fstype);
7864 
7865         if (bam_verbose)
7866                 bam_print(_("found or created GRUB signature %s for %s\n"),
7867                     sign, osdev);
7868 
7869         fdiskpart = get_partition(osdev);
7870         INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7871         if (fdiskpart == PARTNO_NOTFOUND) {
7872                 bam_error(_("failed to determine fdisk partition: %s\n"),
7873                     osdev);
7874                 free(sign);
7875                 return (NULL);
7876         }
7877 
7878         slice = strrchr(osdev, 's');
7879 
7880         if (fdiskpart == PARTNO_EFI) {
7881                 fdiskpart = atoi(&slice[1]);
7882                 slice = NULL;
7883         }
7884 
7885         grubsign = s_calloc(1, MAXNAMELEN + 10);
7886         if (slice) {
7887                 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7888                     sign, fdiskpart, slice[1] + 'a' - '0');
7889         } else
7890                 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7891                     sign, fdiskpart);
7892 
7893         free(sign);
7894 
7895         BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn, grubsign));
7896 
7897         return (grubsign);
7898 }
7899 
7900 static char *
7901 get_title(char *rootdir)
7902 {
7903         static char     title[80];
7904         char            *cp = NULL;
7905         char            release[PATH_MAX];
7906         FILE            *fp;
7907         const char      *fcn = "get_title()";
7908 
7909         /* open the /etc/release file */
7910         (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7911 
7912         fp = fopen(release, "r");
7913         if (fp == NULL) {
7914                 bam_error(_("failed to open file: %s: %s\n"), release,
7915                     strerror(errno));
7916                 cp = NULL;
7917                 goto out;
7918         }
7919 
7920         /* grab first line of /etc/release */
7921         cp = s_fgets(title, sizeof (title), fp);
7922         if (cp) {
7923                 while (isspace(*cp))    /* remove leading spaces */
7924                         cp++;
7925         }
7926 
7927         (void) fclose(fp);
7928 
7929 out:
7930         cp = cp ? cp : "Oracle Solaris";
7931 
7932         BAM_DPRINTF(("%s: got title: %s\n", fcn, cp));
7933 
7934         return (cp);
7935 }
7936 
7937 char *
7938 get_special(char *mountp)
7939 {
7940         FILE            *mntfp;
7941         struct mnttab   mp = {0};
7942         struct mnttab   mpref = {0};
7943         int             error;
7944         int             ret;
7945         const char      *fcn = "get_special()";
7946 
7947         INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7948         if (mountp == NULL) {
7949                 bam_error(_("cannot get special file: NULL mount-point\n"));
7950                 return (NULL);
7951         }
7952 
7953         mntfp = fopen(MNTTAB, "r");
7954         error = errno;
7955         INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7956         if (mntfp == NULL) {
7957                 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7958                     strerror(error));
7959                 return (NULL);
7960         }
7961 
7962         if (*mountp == '\0')
7963                 mpref.mnt_mountp = "/";
7964         else
7965                 mpref.mnt_mountp = mountp;
7966 
7967         ret = getmntany(mntfp, &mp, &mpref);
7968         INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7969         if (ret != 0) {
7970                 (void) fclose(mntfp);
7971                 BAM_DPRINTF(("%s: Cannot get special file:  mount-point %s "
7972                     "not in mnttab\n", fcn, mountp));
7973                 return (NULL);
7974         }
7975         (void) fclose(mntfp);
7976 
7977         BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
7978 
7979         return (s_strdup(mp.mnt_special));
7980 }
7981 
7982 static void
7983 free_physarray(char **physarray, int n)
7984 {
7985         int                     i;
7986         const char              *fcn = "free_physarray()";
7987 
7988         assert(physarray);
7989         assert(n);
7990 
7991         BAM_DPRINTF(("%s: entering args: %d\n", fcn, n));
7992 
7993         for (i = 0; i < n; i++) {
7994                 free(physarray[i]);
7995         }
7996         free(physarray);
7997 
7998         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7999 }
8000 
8001 static int
8002 zfs_get_physical(char *special, char ***physarray, int *n)
8003 {
8004         char                    sdup[PATH_MAX];
8005         char                    cmd[PATH_MAX];
8006         char                    dsk[PATH_MAX];
8007         char                    *pool;
8008         filelist_t              flist = {0};
8009         line_t                  *lp;
8010         line_t                  *startlp;
8011         char                    *comp1;
8012         int                     i;
8013         int                     ret;
8014         const char              *fcn = "zfs_get_physical()";
8015 
8016         assert(special);
8017 
8018         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, special));
8019 
8020         INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
8021         if (special[0] == '/') {
8022                 bam_error(_("invalid device for ZFS filesystem: %s\n"),
8023                     special);
8024                 return (-1);
8025         }
8026 
8027         (void) strlcpy(sdup, special, sizeof (sdup));
8028 
8029         pool = strtok(sdup, "/");
8030         INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
8031         if (pool == NULL) {
8032                 bam_error(_("cannot derive ZFS pool from special: %s\n"),
8033                     special);
8034                 return (-1);
8035         }
8036 
8037         (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
8038 
8039         ret = exec_cmd(cmd, &flist);
8040         INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
8041         if (ret != 0) {
8042                 bam_error(_("cannot get zpool status for pool: %s\n"), pool);
8043                 return (-1);
8044         }
8045 
8046         INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
8047         if (flist.head == NULL) {
8048                 bam_error(_("bad zpool status for pool=%s\n"), pool);
8049                 filelist_free(&flist);
8050                 return (-1);
8051         }
8052 
8053         for (lp = flist.head; lp; lp = lp->next) {
8054                 BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
8055                     fcn, lp->line));
8056                 comp1 = strtok(lp->line, " \t");
8057                 if (comp1 == NULL) {
8058                         free(lp->line);
8059                         lp->line = NULL;
8060                 } else {
8061                         comp1 = s_strdup(comp1);
8062                         free(lp->line);
8063                         lp->line = comp1;
8064                 }
8065         }
8066 
8067         for (lp = flist.head; lp; lp = lp->next) {
8068                 if (lp->line == NULL)
8069                         continue;
8070                 if (strcmp(lp->line, pool) == 0) {
8071                         BAM_DPRINTF(("%s: found pool name: %s in zpool "
8072                             "status\n", fcn, pool));
8073                         break;
8074                 }
8075         }
8076 
8077         if (lp == NULL) {
8078                 bam_error(_("no pool name %s in zpool status\n"), pool);
8079                 filelist_free(&flist);
8080                 return (-1);
8081         }
8082 
8083         startlp = lp->next;
8084         for (i = 0, lp = startlp; lp; lp = lp->next) {
8085                 if (lp->line == NULL)
8086                         continue;
8087                 if (strcmp(lp->line, "mirror") == 0)
8088                         continue;
8089                 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
8090                         break;
8091                 i++;
8092                 BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
8093                     fcn, i));
8094         }
8095 
8096         if (i == 0) {
8097                 bam_error(_("no physical device in zpool status for pool=%s\n"),
8098                     pool);
8099                 filelist_free(&flist);
8100                 return (-1);
8101         }
8102 
8103         *n = i;
8104         *physarray = s_calloc(*n, sizeof (char *));
8105         for (i = 0, lp = startlp; lp; lp = lp->next) {
8106                 if (lp->line == NULL)
8107                         continue;
8108                 if (strcmp(lp->line, "mirror") == 0)
8109                         continue;
8110                 if (strcmp(lp->line, "errors:") == 0)
8111                         break;
8112                 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
8113                     strncmp(lp->line, "/dev/rdsk/",
8114                     strlen("/dev/rdsk/")) != 0)  {
8115                         (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
8116                             lp->line);
8117                 } else {
8118                         (void) strlcpy(dsk, lp->line, sizeof (dsk));
8119                 }
8120                 BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
8121                     fcn, dsk, pool));
8122                 (*physarray)[i++] = s_strdup(dsk);
8123         }
8124 
8125         assert(i == *n);
8126 
8127         filelist_free(&flist);
8128 
8129         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8130         return (0);
8131 }
8132 
8133 static int
8134 get_physical(char *menu_root, char ***physarray, int *n)
8135 {
8136         char                    *special;
8137         int                     ret;
8138         const char              *fcn = "get_physical()";
8139 
8140         assert(menu_root);
8141         assert(physarray);
8142         assert(n);
8143 
8144         *physarray = NULL;
8145         *n = 0;
8146 
8147         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_root));
8148 
8149         /* First get the device special file from /etc/mnttab */
8150         special = get_special(menu_root);
8151         INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
8152         if (special == NULL) {
8153                 bam_error(_("cannot get special file for mount-point: %s\n"),
8154                     menu_root);
8155                 return (-1);
8156         }
8157 
8158         /* If already a physical device nothing to do */
8159         if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
8160             strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
8161                 BAM_DPRINTF(("%s: got physical device already directly for "
8162                     "menu_root=%s special=%s\n", fcn, menu_root, special));
8163                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8164                 *physarray = s_calloc(1, sizeof (char *));
8165                 (*physarray)[0] = special;
8166                 *n = 1;
8167                 return (0);
8168         }
8169 
8170         if (is_zfs(menu_root)) {
8171                 ret = zfs_get_physical(special, physarray, n);
8172         } else {
8173                 bam_error(_("cannot derive physical device for %s (%s), "
8174                     "unsupported filesystem\n"), menu_root, special);
8175                 ret = -1;
8176         }
8177 
8178         free(special);
8179 
8180         INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
8181         if (ret == -1) {
8182                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8183         } else {
8184                 int     i;
8185                 assert (*n > 0);
8186                 for (i = 0; i < *n; i++) {
8187                         BAM_DPRINTF(("%s: returning physical=%s\n",
8188                             fcn, (*physarray)[i]));
8189                 }
8190         }
8191 
8192         return (ret);
8193 }
8194 
8195 static int
8196 is_bootdisk(char *osroot, char *physical)
8197 {
8198         int                     ret;
8199         char                    *grubroot;
8200         char                    *bootp;
8201         const char              *fcn = "is_bootdisk()";
8202 
8203         assert(osroot);
8204         assert(physical);
8205 
8206         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, physical));
8207 
8208         bootp = strstr(physical, "p0:boot");
8209         if (bootp)
8210                 *bootp = '\0';
8211         /*
8212          * We just want the BIOS mapping for menu disk.
8213          * Don't pass menu_root to get_grubroot() as the
8214          * check that it is used for is not relevant here.
8215          * The osroot is immaterial as well - it is only used to
8216          * to find create_diskmap script. Everything hinges on
8217          * "physical"
8218          */
8219         grubroot = get_grubroot(osroot, physical, NULL);
8220 
8221         INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
8222         if (grubroot == NULL) {
8223                 if (bam_verbose)
8224                         bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8225                             "disk: %s\n"), physical);
8226                 return (0);
8227         }
8228         ret = grubroot[3] == '0';
8229         free(grubroot);
8230 
8231         BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
8232 
8233         return (ret);
8234 }
8235 
8236 /*
8237  * Check if menu is on the boot device
8238  * Return 0 (false) on error
8239  */
8240 static int
8241 menu_on_bootdisk(char *osroot, char *menu_root)
8242 {
8243         char            **physarray;
8244         int             ret;
8245         int             n;
8246         int             i;
8247         int             on_bootdisk;
8248         const char      *fcn = "menu_on_bootdisk()";
8249 
8250         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8251 
8252         ret = get_physical(menu_root, &physarray, &n);
8253         INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
8254         if (ret != 0) {
8255                 bam_error(_("cannot get physical device special file for menu "
8256                     "root: %s\n"), menu_root);
8257                 return (0);
8258         }
8259 
8260         assert(physarray);
8261         assert(n > 0);
8262 
8263         on_bootdisk = 0;
8264         for (i = 0; i < n; i++) {
8265                 assert(strncmp(physarray[i], "/dev/dsk/",
8266                     strlen("/dev/dsk/")) == 0 ||
8267                     strncmp(physarray[i], "/dev/rdsk/",
8268                     strlen("/dev/rdsk/")) == 0);
8269 
8270                 BAM_DPRINTF(("%s: checking if phys-device=%s is on bootdisk\n",
8271                     fcn, physarray[i]));
8272                 if (is_bootdisk(osroot, physarray[i])) {
8273                         on_bootdisk = 1;
8274                         BAM_DPRINTF(("%s: phys-device=%s *IS* on bootdisk\n",
8275                             fcn, physarray[i]));
8276                 }
8277         }
8278 
8279         free_physarray(physarray, n);
8280 
8281         INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8282         INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8283         if (on_bootdisk) {
8284                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8285         } else {
8286                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8287         }
8288 
8289         return (on_bootdisk);
8290 }
8291 
8292 void
8293 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8294 {
8295         const char      *fcn = "bam_add_line()";
8296 
8297         assert(mp);
8298         assert(entry);
8299         assert(prev);
8300         assert(lp);
8301 
8302         lp->next = prev->next;
8303         if (prev->next) {
8304                 BAM_DPRINTF(("%s: previous next exists\n", fcn));
8305                 prev->next->prev = lp;
8306         } else {
8307                 BAM_DPRINTF(("%s: previous next does not exist\n", fcn));
8308         }
8309         prev->next = lp;
8310         lp->prev = prev;
8311 
8312         if (entry->end == prev) {
8313                 BAM_DPRINTF(("%s: last line in entry\n", fcn));
8314                 entry->end = lp;
8315         }
8316         if (mp->end == prev) {
8317                 assert(lp->next == NULL);
8318                 mp->end = lp;
8319                 BAM_DPRINTF(("%s: last line in menu\n", fcn));
8320         }
8321 }
8322 
8323 /*
8324  * look for matching bootadm entry with specified parameters
8325  * Here are the rules (based on existing usage):
8326  * - If title is specified, match on title only
8327  * - Else, match on root/findroot, kernel, and module.
8328  *   Note that, if root_opt is non-zero, the absence of
8329  *   root line is considered a match.
8330  */
8331 static entry_t *
8332 find_boot_entry(
8333         menu_t *mp,
8334         char *title,
8335         char *kernel,
8336         char *findroot,
8337         char *root,
8338         char *module,
8339         int root_opt,
8340         int *entry_num)
8341 {
8342         int             i;
8343         line_t          *lp;
8344         entry_t         *ent;
8345         const char      *fcn = "find_boot_entry()";
8346 
8347         if (entry_num)
8348                 *entry_num = BAM_ERROR;
8349 
8350         /* find matching entry */
8351         for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8352                 lp = ent->start;
8353 
8354                 /* first line of entry must be bootadm comment */
8355                 lp = ent->start;
8356                 if (lp->flags != BAM_COMMENT ||
8357                     strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8358                         continue;
8359                 }
8360 
8361                 /* advance to title line */
8362                 lp = lp->next;
8363                 if (title) {
8364                         if (lp->flags == BAM_TITLE && lp->arg &&
8365                             strcmp(lp->arg, title) == 0) {
8366                                 BAM_DPRINTF(("%s: matched title: %s\n",
8367                                     fcn, title));
8368                                 break;
8369                         }
8370                         BAM_DPRINTF(("%s: no match title: %s, %s\n",
8371                             fcn, title, lp->arg));
8372                         continue;       /* check title only */
8373                 }
8374 
8375                 lp = lp->next;       /* advance to root line */
8376                 if (lp == NULL) {
8377                         continue;
8378                 } else if (lp->cmd != NULL &&
8379                     strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8380                         INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8381                             findroot = NULL);
8382                         if (findroot == NULL) {
8383                                 BAM_DPRINTF(("%s: no match line has findroot, "
8384                                     "we don't: %s\n", fcn, lp->arg));
8385                                 continue;
8386                         }
8387                         /* findroot command found, try match  */
8388                         if (strcmp(lp->arg, findroot) != 0) {
8389                                 BAM_DPRINTF(("%s: no match findroot: %s, %s\n",
8390                                     fcn, findroot, lp->arg));
8391                                 continue;
8392                         }
8393                         BAM_DPRINTF(("%s: matched findroot: %s\n",
8394                             fcn, findroot));
8395                         lp = lp->next;       /* advance to kernel line */
8396                 } else if (lp->cmd != NULL &&
8397                     strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8398                         INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8399                         if (root == NULL) {
8400                                 BAM_DPRINTF(("%s: no match, line has root, we "
8401                                     "don't: %s\n", fcn, lp->arg));
8402                                 continue;
8403                         }
8404                         /* root cmd found, try match */
8405                         if (strcmp(lp->arg, root) != 0) {
8406                                 BAM_DPRINTF(("%s: no match root: %s, %s\n",
8407                                     fcn, root, lp->arg));
8408                                 continue;
8409                         }
8410                         BAM_DPRINTF(("%s: matched root: %s\n", fcn, root));
8411                         lp = lp->next;       /* advance to kernel line */
8412                 } else {
8413                         INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8414                             root_opt = 0);
8415                         INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8416                             root_opt = 1);
8417                         /* no root command, see if root is optional */
8418                         if (root_opt == 0) {
8419                                 BAM_DPRINTF(("%s: root NOT optional\n", fcn));
8420                                 continue;
8421                         }
8422                         BAM_DPRINTF(("%s: root IS optional\n", fcn));
8423                 }
8424 
8425                 if (lp == NULL || lp->next == NULL) {
8426                         continue;
8427                 }
8428 
8429                 if (kernel &&
8430                     (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8431                         if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8432                             !(ent->flags & BAM_ENTRY_DBOOT) ||
8433                             strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8434                                 continue;
8435 
8436                         ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8437 
8438                 }
8439                 BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn,
8440                     kernel, lp->arg));
8441 
8442                 /*
8443                  * Check for matching module entry (failsafe or normal).
8444                  * If it fails to match, we go around the loop again.
8445                  * For xpv entries, there are two module lines, so we
8446                  * do the check twice.
8447                  */
8448                 lp = lp->next;       /* advance to module line */
8449                 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8450                     (((lp = lp->next) != NULL) &&
8451                     check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8452                         /* match found */
8453                         BAM_DPRINTF(("%s: module match: %s, %s\n", fcn,
8454                             module, lp->arg));
8455                         break;
8456                 }
8457 
8458                 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8459                     (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8460                     strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8461                         ent->flags |= BAM_ENTRY_UPGFSMODULE;
8462                         break;
8463                 }
8464 
8465         }
8466 
8467         if (ent && entry_num) {
8468                 *entry_num = i;
8469         }
8470 
8471         if (ent) {
8472                 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8473         } else {
8474                 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_ERROR));
8475         }
8476         return (ent);
8477 }
8478 
8479 static int
8480 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8481     char *kernel, char *mod_kernel, char *module, int root_opt)
8482 {
8483         int             i;
8484         int             change_kernel = 0;
8485         entry_t         *ent;
8486         line_t          *lp;
8487         line_t          *tlp;
8488         char            linebuf[BAM_MAXLINE];
8489         const char      *fcn = "update_boot_entry()";
8490 
8491         /* note: don't match on title, it's updated on upgrade */
8492         ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8493             root_opt, &i);
8494         if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8495                 /*
8496                  * We may be upgrading a kernel from multiboot to
8497                  * directboot.  Look for a multiboot entry. A multiboot
8498                  * entry will not have a findroot line.
8499                  */
8500                 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8501                     MULTIBOOT_ARCHIVE, root_opt, &i);
8502                 if (ent != NULL) {
8503                         BAM_DPRINTF(("%s: upgrading entry from dboot to "
8504                             "multiboot: root = %s\n", fcn, root));
8505                         change_kernel = 1;
8506                 }
8507         } else if (ent) {
8508                 BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8509                     fcn, findroot));
8510         }
8511 
8512         if (ent == NULL) {
8513                 BAM_DPRINTF(("%s: boot entry not found in menu. Creating "
8514                     "new entry, findroot = %s\n", fcn, findroot));
8515                 return (add_boot_entry(mp, title, findroot,
8516                     kernel, mod_kernel, module, NULL));
8517         }
8518 
8519         /* replace title of existing entry and update findroot line */
8520         lp = ent->start;
8521         lp = lp->next;       /* title line */
8522         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8523             menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8524         free(lp->arg);
8525         free(lp->line);
8526         lp->arg = s_strdup(title);
8527         lp->line = s_strdup(linebuf);
8528         BAM_DPRINTF(("%s: changing title to: %s\n", fcn, title));
8529 
8530         tlp = lp;       /* title line */
8531         lp = lp->next;       /* root line */
8532 
8533         /* if no root or findroot command, create a new line_t */
8534         if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8535             strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8536                 lp = s_calloc(1, sizeof (line_t));
8537                 bam_add_line(mp, ent, tlp, lp);
8538         } else {
8539                 if (lp->cmd != NULL)
8540                         free(lp->cmd);
8541 
8542                 free(lp->sep);
8543                 free(lp->arg);
8544                 free(lp->line);
8545         }
8546 
8547         lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8548         lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8549         lp->arg = s_strdup(findroot);
8550         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8551             menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8552         lp->line = s_strdup(linebuf);
8553         BAM_DPRINTF(("%s: adding findroot line: %s\n", fcn, findroot));
8554 
8555         /* kernel line */
8556         lp = lp->next;
8557 
8558         if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8559                 char            *params = NULL;
8560 
8561                 params = strstr(lp->line, "-s");
8562                 if (params != NULL)
8563                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8564                             menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8565                             kernel, params+2);
8566                 else
8567                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8568                             menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8569                             kernel);
8570 
8571                 if (lp->cmd != NULL)
8572                         free(lp->cmd);
8573 
8574                 free(lp->arg);
8575                 free(lp->line);
8576                 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8577                 lp->arg = s_strdup(strstr(linebuf, "/"));
8578                 lp->line = s_strdup(linebuf);
8579                 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8580                 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8581                     fcn, lp->prev->cmd));
8582         }
8583 
8584         if (change_kernel) {
8585                 /*
8586                  * We're upgrading from multiboot to directboot.
8587                  */
8588                 if (lp->cmd != NULL &&
8589                     strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8590                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8591                             menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8592                             kernel);
8593                         free(lp->cmd);
8594                         free(lp->arg);
8595                         free(lp->line);
8596                         lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8597                         lp->arg = s_strdup(kernel);
8598                         lp->line = s_strdup(linebuf);
8599                         lp = lp->next;
8600                         BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8601                             fcn, kernel));
8602                 }
8603                 if (lp->cmd != NULL &&
8604                     strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8605                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8606                             menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8607                             module);
8608                         free(lp->cmd);
8609                         free(lp->arg);
8610                         free(lp->line);
8611                         lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8612                         lp->arg = s_strdup(module);
8613                         lp->line = s_strdup(linebuf);
8614                         lp = lp->next;
8615                         BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8616                             fcn, module));
8617                 }
8618         }
8619 
8620         /* module line */
8621         lp = lp->next;
8622 
8623         if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8624                 if (lp->cmd != NULL &&
8625                     strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8626                         (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8627                             menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8628                             module);
8629                         free(lp->cmd);
8630                         free(lp->arg);
8631                         free(lp->line);
8632                         lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8633                         lp->arg = s_strdup(module);
8634                         lp->line = s_strdup(linebuf);
8635                         lp = lp->next;
8636                         ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8637                         BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8638                             fcn, module));
8639                 }
8640         }
8641 
8642         BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8643         return (i);
8644 }
8645 
8646 int
8647 root_optional(char *osroot, char *menu_root)
8648 {
8649         char                    *ospecial;
8650         char                    *mspecial;
8651         char                    *slash;
8652         int                     root_opt;
8653         int                     ret1;
8654         int                     ret2;
8655         const char              *fcn = "root_optional()";
8656 
8657         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8658 
8659         /*
8660          * For all filesystems except ZFS, a straight compare of osroot
8661          * and menu_root will tell us if root is optional.
8662          * For ZFS, the situation is complicated by the fact that
8663          * menu_root and osroot are always different
8664          */
8665         ret1 = is_zfs(osroot);
8666         ret2 = is_zfs(menu_root);
8667         INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8668         if (!ret1 || !ret2) {
8669                 BAM_DPRINTF(("%s: one or more non-ZFS filesystems (%s, %s)\n",
8670                     fcn, osroot, menu_root));
8671                 root_opt = (strcmp(osroot, menu_root) == 0);
8672                 goto out;
8673         }
8674 
8675         ospecial = get_special(osroot);
8676         INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8677         if (ospecial == NULL) {
8678                 bam_error(_("failed to get special file for osroot: %s\n"),
8679                     osroot);
8680                 return (0);
8681         }
8682         BAM_DPRINTF(("%s: ospecial=%s for osroot=%s\n", fcn, ospecial, osroot));
8683 
8684         mspecial = get_special(menu_root);
8685         INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8686         if (mspecial == NULL) {
8687                 bam_error(_("failed to get special file for menu_root: %s\n"),
8688                     menu_root);
8689                 free(ospecial);
8690                 return (0);
8691         }
8692         BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8693             fcn, mspecial, menu_root));
8694 
8695         slash = strchr(ospecial, '/');
8696         if (slash)
8697                 *slash = '\0';
8698         BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8699             fcn, ospecial, osroot));
8700 
8701         root_opt = (strcmp(ospecial, mspecial) == 0);
8702 
8703         free(ospecial);
8704         free(mspecial);
8705 
8706 out:
8707         INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8708         INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8709         if (root_opt) {
8710                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8711         } else {
8712                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8713         }
8714 
8715         return (root_opt);
8716 }
8717 
8718 /*ARGSUSED*/
8719 static error_t
8720 update_entry(menu_t *mp, char *menu_root, char *osdev)
8721 {
8722         int             entry;
8723         char            *grubsign;
8724         char            *grubroot;
8725         char            *title;
8726         char            osroot[PATH_MAX];
8727         char            *failsafe_kernel = NULL;
8728         struct stat     sbuf;
8729         char            failsafe[256];
8730         char            failsafe_64[256];
8731         int             ret;
8732         const char      *fcn = "update_entry()";
8733 
8734         assert(mp);
8735         assert(menu_root);
8736         assert(osdev);
8737         assert(bam_root);
8738 
8739         BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, menu_root, osdev,
8740             bam_root));
8741 
8742         (void) strlcpy(osroot, bam_root, sizeof (osroot));
8743 
8744         title = get_title(osroot);
8745         assert(title);
8746 
8747         grubsign = get_grubsign(osroot, osdev);
8748         INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8749         if (grubsign == NULL) {
8750                 bam_error(_("failed to get grubsign for root: %s, device %s\n"),
8751                     osroot, osdev);
8752                 return (BAM_ERROR);
8753         }
8754 
8755         /*
8756          * It is not a fatal error if get_grubroot() fails
8757          * We no longer rely on biosdev to populate the
8758          * menu
8759          */
8760         grubroot = get_grubroot(osroot, osdev, menu_root);
8761         INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8762         if (grubroot) {
8763                 BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8764                     "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8765         } else {
8766                 BAM_DPRINTF(("%s: get_grubroot failed. osroot=%s, osdev=%s, "
8767                     "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8768         }
8769 
8770         /* add the entry for normal Solaris */
8771         INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8772             bam_direct = BAM_DIRECT_MULTIBOOT);
8773         if (bam_direct == BAM_DIRECT_DBOOT) {
8774                 entry = update_boot_entry(mp, title, grubsign, grubroot,
8775                     (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8776                     NULL, DIRECT_BOOT_ARCHIVE,
8777                     root_optional(osroot, menu_root));
8778                 BAM_DPRINTF(("%s: updated boot entry bam_zfs=%d, "
8779                     "grubsign = %s\n", fcn, bam_zfs, grubsign));
8780                 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8781                         (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8782                             grubroot, XEN_MENU, bam_zfs ?
8783                             XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8784                             DIRECT_BOOT_ARCHIVE,
8785                             root_optional(osroot, menu_root));
8786                         BAM_DPRINTF(("%s: updated HV entry bam_zfs=%d, "
8787                             "grubsign = %s\n", fcn, bam_zfs, grubsign));
8788                 }
8789         } else {
8790                 entry = update_boot_entry(mp, title, grubsign, grubroot,
8791                     MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8792                     root_optional(osroot, menu_root));
8793 
8794                 BAM_DPRINTF(("%s: updated MULTIBOOT entry grubsign = %s\n",
8795                     fcn, grubsign));
8796         }
8797 
8798         /*
8799          * Add the entry for failsafe archive.  On a bfu'd system, the
8800          * failsafe may be different than the installed kernel.
8801          */
8802         (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8803             osroot, FAILSAFE_ARCHIVE_32);
8804         (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8805             osroot, FAILSAFE_ARCHIVE_64);
8806 
8807         /*
8808          * Check if at least one of the two archives exists
8809          * Using $ISADIR as the default line, we have an entry which works
8810          * for both the cases.
8811          */
8812 
8813         if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8814 
8815                 /* Figure out where the kernel line should point */
8816                 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8817                     DIRECT_BOOT_FAILSAFE_32);
8818                 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8819                     osroot, DIRECT_BOOT_FAILSAFE_64);
8820                 if (stat(failsafe, &sbuf) == 0 ||
8821                     stat(failsafe_64, &sbuf) == 0) {
8822                         failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8823                 } else {
8824                         (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8825                             osroot, MULTI_BOOT_FAILSAFE);
8826                         if (stat(failsafe, &sbuf) == 0) {
8827                                 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8828                         }
8829                 }
8830                 if (failsafe_kernel != NULL) {
8831                         (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8832                             grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8833                             root_optional(osroot, menu_root));
8834                         BAM_DPRINTF(("%s: updated FAILSAFE entry "
8835                             "failsafe_kernel = %s\n", fcn, failsafe_kernel));
8836                 }
8837         }
8838         free(grubroot);
8839 
8840         INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8841         if (entry == BAM_ERROR) {
8842                 bam_error(_("failed to add boot entry with title=%s, grub "
8843                     "signature=%s\n"), title, grubsign);
8844                 free(grubsign);
8845                 return (BAM_ERROR);
8846         }
8847         free(grubsign);
8848 
8849         update_numbering(mp);
8850         ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8851         INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8852         if (ret == BAM_ERROR) {
8853                 bam_error(_("failed to set GRUB menu default to %d\n"), entry);
8854         }
8855         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8856         return (BAM_WRITE);
8857 }
8858 
8859 static void
8860 save_default_entry(menu_t *mp, const char *which)
8861 {
8862         int             lineNum;
8863         int             entryNum;
8864         int             entry = 0;      /* default is 0 */
8865         char            linebuf[BAM_MAXLINE];
8866         line_t          *lp = mp->curdefault;
8867         const char      *fcn = "save_default_entry()";
8868 
8869         if (mp->start) {
8870                 lineNum = mp->end->lineNum;
8871                 entryNum = mp->end->entryNum;
8872         } else {
8873                 lineNum = LINE_INIT;
8874                 entryNum = ENTRY_INIT;
8875         }
8876 
8877         if (lp)
8878                 entry = s_strtol(lp->arg);
8879 
8880         (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8881         BAM_DPRINTF(("%s: saving default to: %s\n", fcn, linebuf));
8882         line_parser(mp, linebuf, &lineNum, &entryNum);
8883         BAM_DPRINTF(("%s: saved default to lineNum=%d, entryNum=%d\n", fcn,
8884             lineNum, entryNum));
8885 }
8886 
8887 static void
8888 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8889 {
8890         int             entry;
8891         char            *str;
8892         const char      *fcn = "restore_default_entry()";
8893 
8894         if (lp == NULL) {
8895                 BAM_DPRINTF(("%s: NULL saved default\n", fcn));
8896                 return;         /* nothing to restore */
8897         }
8898 
8899         BAM_DPRINTF(("%s: saved default string: %s\n", fcn, which));
8900 
8901         str = lp->arg + strlen(which);
8902         entry = s_strtol(str);
8903         (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8904 
8905         BAM_DPRINTF(("%s: restored default to entryNum: %d\n", fcn, entry));
8906 
8907         /* delete saved old default line */
8908         unlink_line(mp, lp);
8909         line_free(lp);
8910 }
8911 
8912 /*
8913  * This function is for supporting reboot with args.
8914  * The opt value can be:
8915  * NULL         delete temp entry, if present
8916  * entry=<n>      switches default entry to <n>
8917  * else         treated as boot-args and setup a temperary menu entry
8918  *              and make it the default
8919  * Note that we are always rebooting the current OS instance
8920  * so osroot == / always.
8921  */
8922 #define REBOOT_TITLE    "Solaris_reboot_transient"
8923 
8924 /*ARGSUSED*/
8925 static error_t
8926 update_temp(menu_t *mp, char *dummy, char *opt)
8927 {
8928         int             entry;
8929         char            *osdev;
8930         char            *fstype;
8931         char            *sign;
8932         char            *opt_ptr;
8933         char            *path;
8934         char            kernbuf[BUFSIZ];
8935         char            args_buf[BUFSIZ];
8936         char            signbuf[PATH_MAX];
8937         int             ret;
8938         const char      *fcn = "update_temp()";
8939 
8940         assert(mp);
8941         assert(dummy == NULL);
8942 
8943         /* opt can be NULL */
8944         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt ? opt : "<NULL>"));
8945         BAM_DPRINTF(("%s: bam_alt_root: %d, bam_root: %s\n", fcn,
8946             bam_alt_root, bam_root));
8947 
8948         if (bam_alt_root || bam_rootlen != 1 ||
8949             strcmp(bam_root, "/") != 0 ||
8950             strcmp(rootbuf, "/") != 0) {
8951                 bam_error(_("an alternate root (%s) cannot be used with this "
8952                     "sub-command\n"), bam_root);
8953                 return (BAM_ERROR);
8954         }
8955 
8956         /* If no option, delete exiting reboot menu entry */
8957         if (opt == NULL) {
8958                 entry_t         *ent;
8959                 BAM_DPRINTF(("%s: opt is NULL\n", fcn));
8960                 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8961                     NULL, NULL, 0, &entry);
8962                 if (ent == NULL) {      /* not found is ok */
8963                         BAM_DPRINTF(("%s: transient entry not found\n", fcn));
8964                         return (BAM_SUCCESS);
8965                 }
8966                 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8967                 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8968                 mp->olddefault = NULL;
8969                 BAM_DPRINTF(("%s: restored old default\n", fcn));
8970                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8971                 return (BAM_WRITE);
8972         }
8973 
8974         /* if entry= is specified, set the default entry */
8975         if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8976                 int entryNum = s_strtol(opt + strlen("entry="));
8977                 BAM_DPRINTF(("%s: opt has entry=: %s\n", fcn, opt));
8978                 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8979                         /* this is entry=# option */
8980                         ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8981                         BAM_DPRINTF(("%s: default set to %d, "
8982                             "set_default ret=%d\n", fcn, entry, ret));
8983                         return (ret);
8984                 } else {
8985                         bam_error(_("failed to set GRUB menu default to %d\n"),
8986                             entryNum);
8987                         return (BAM_ERROR);
8988                 }
8989         }
8990 
8991         /*
8992          * add a new menu entry based on opt and make it the default
8993          */
8994 
8995         fstype = get_fstype("/");
8996         INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8997         if (fstype == NULL) {
8998                 bam_error(_("failed to determine filesystem type for \"/\". "
8999                     "Reboot with \narguments failed.\n"));
9000                 return (BAM_ERROR);
9001         }
9002 
9003         osdev = get_special("/");
9004         INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
9005         if (osdev == NULL) {
9006                 free(fstype);
9007                 bam_error(_("failed to find device special file for \"/\". "
9008                     "Reboot with \narguments failed.\n"));
9009                 return (BAM_ERROR);
9010         }
9011 
9012         sign = find_existing_sign("/", osdev, fstype);
9013         INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
9014         if (sign == NULL) {
9015                 free(fstype);
9016                 free(osdev);
9017                 bam_error(_("failed to find boot signature. Reboot with "
9018                     "arguments failed.\n"));
9019                 return (BAM_ERROR);
9020         }
9021 
9022         free(osdev);
9023         (void) strlcpy(signbuf, sign, sizeof (signbuf));
9024         free(sign);
9025 
9026         assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
9027             strchr(signbuf, ')') == NULL);
9028 
9029         /*
9030          * There is no alternate root while doing reboot with args
9031          * This version of bootadm is only delivered with a DBOOT
9032          * version of Solaris.
9033          */
9034         INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
9035         if (bam_direct != BAM_DIRECT_DBOOT) {
9036                 free(fstype);
9037                 bam_error(_("the root filesystem is not a dboot Solaris "
9038                     "instance. \nThis version of bootadm is not supported "
9039                     "on this version of Solaris.\n"));
9040                 return (BAM_ERROR);
9041         }
9042 
9043         /* add an entry for Solaris reboot */
9044         if (opt[0] == '-') {
9045                 /* It's an option - first see if boot-file is set */
9046                 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
9047                 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
9048                 if (ret != BAM_SUCCESS) {
9049                         free(fstype);
9050                         bam_error(_("reboot with arguments: error querying "
9051                             "current boot-file settings\n"));
9052                         return (BAM_ERROR);
9053                 }
9054                 if (kernbuf[0] == '\0')
9055                         (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
9056                             sizeof (kernbuf));
9057                 /*
9058                  * If this is a zfs file system and kernbuf does not
9059                  * have "-B $ZFS-BOOTFS" string yet, add it.
9060                  */
9061                 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
9062                         (void) strlcat(kernbuf, " ", sizeof (kernbuf));
9063                         (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
9064                 }
9065                 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
9066                 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
9067                 BAM_DPRINTF(("%s: reboot with args, option specified: "
9068                     "kern=%s\n", fcn, kernbuf));
9069         } else if (opt[0] == '/') {
9070                 /* It's a full path, so write it out. */
9071                 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
9072 
9073                 /*
9074                  * If someone runs:
9075                  *
9076                  *      # eeprom boot-args='-kd'
9077                  *      # reboot /platform/i86pc/kernel/unix
9078                  *
9079                  * we want to use the boot-args as part of the boot
9080                  * line.  On the other hand, if someone runs:
9081                  *
9082                  *      # reboot "/platform/i86pc/kernel/unix -kd"
9083                  *
9084                  * we don't need to mess with boot-args.  If there's
9085                  * no space in the options string, assume we're in the
9086                  * first case.
9087                  */
9088                 if (strchr(opt, ' ') == NULL) {
9089                         ret = get_kernel(mp, ARGS_CMD, args_buf,
9090                             sizeof (args_buf));
9091                         INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
9092                         if (ret != BAM_SUCCESS) {
9093                                 free(fstype);
9094                                 bam_error(_("reboot with arguments: error "
9095                                     "querying current boot-args settings\n"));
9096                                 return (BAM_ERROR);
9097                         }
9098 
9099                         if (args_buf[0] != '\0') {
9100                                 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
9101                                 (void) strlcat(kernbuf, args_buf,
9102                                     sizeof (kernbuf));
9103                         }
9104                 }
9105                 BAM_DPRINTF(("%s: reboot with args, abspath specified: "
9106                     "kern=%s\n", fcn, kernbuf));
9107         } else {
9108                 /*
9109                  * It may be a partial path, or it may be a partial
9110                  * path followed by options.  Assume that only options
9111                  * follow a space.  If someone sends us a kernel path
9112                  * that includes a space, they deserve to be broken.
9113                  */
9114                 opt_ptr = strchr(opt, ' ');
9115                 if (opt_ptr != NULL) {
9116                         *opt_ptr = '\0';
9117                 }
9118 
9119                 path = expand_path(opt);
9120                 if (path != NULL) {
9121                         (void) strlcpy(kernbuf, path, sizeof (kernbuf));
9122                         free(path);
9123 
9124                         /*
9125                          * If there were options given, use those.
9126                          * Otherwise, copy over the default options.
9127                          */
9128                         if (opt_ptr != NULL) {
9129                                 /* Restore the space in opt string */
9130                                 *opt_ptr = ' ';
9131                                 (void) strlcat(kernbuf, opt_ptr,
9132                                     sizeof (kernbuf));
9133                         } else {
9134                                 ret = get_kernel(mp, ARGS_CMD, args_buf,
9135                                     sizeof (args_buf));
9136                                 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
9137                                     ret = BAM_ERROR);
9138                                 if (ret != BAM_SUCCESS) {
9139                                         free(fstype);
9140                                         bam_error(_("reboot with arguments: "
9141                                             "error querying current boot-args "
9142                                             "settings\n"));
9143                                         return (BAM_ERROR);
9144                                 }
9145 
9146                                 if (args_buf[0] != '\0') {
9147                                         (void) strlcat(kernbuf, " ",
9148                                             sizeof (kernbuf));
9149                                         (void) strlcat(kernbuf,
9150                                             args_buf, sizeof (kernbuf));
9151                                 }
9152                         }
9153                         BAM_DPRINTF(("%s: resolved partial path: %s\n",
9154                             fcn, kernbuf));
9155                 } else {
9156                         free(fstype);
9157                         bam_error(_("unable to expand %s to a full file"
9158                             " path.\n"), opt);
9159                         bam_print_stderr(_("Rebooting with default kernel "
9160                             "and options.\n"));
9161                         return (BAM_ERROR);
9162                 }
9163         }
9164         free(fstype);
9165         entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
9166             NULL, NULL, NULL);
9167         INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
9168         if (entry == BAM_ERROR) {
9169                 bam_error(_("Cannot update menu. Cannot reboot with "
9170                     "requested arguments\n"));
9171                 return (BAM_ERROR);
9172         }
9173 
9174         save_default_entry(mp, BAM_OLDDEF);
9175         ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
9176         INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
9177         if (ret == BAM_ERROR) {
9178                 bam_error(_("reboot with arguments: setting GRUB menu default "
9179                     "to %d failed\n"), entry);
9180         }
9181         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9182         return (BAM_WRITE);
9183 }
9184 
9185 error_t
9186 set_global(menu_t *mp, char *globalcmd, int val)
9187 {
9188         line_t          *lp;
9189         line_t          *found;
9190         line_t          *last;
9191         char            *cp;
9192         char            *str;
9193         char            prefix[BAM_MAXLINE];
9194         size_t          len;
9195         const char      *fcn = "set_global()";
9196 
9197         assert(mp);
9198         assert(globalcmd);
9199 
9200         if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
9201                 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
9202                 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
9203                 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
9204                 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
9205                         (void) snprintf(prefix, sizeof (prefix), "%d", val);
9206                         bam_error(_("invalid boot entry number: %s\n"), prefix);
9207                         return (BAM_ERROR);
9208                 }
9209         }
9210 
9211         found = last = NULL;
9212         for (lp = mp->start; lp; lp = lp->next) {
9213                 if (lp->flags != BAM_GLOBAL)
9214                         continue;
9215 
9216                 last = lp; /* track the last global found */
9217 
9218                 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
9219                 if (lp->cmd == NULL) {
9220                         bam_error(_("no command at line %d\n"), lp->lineNum);
9221                         continue;
9222                 }
9223                 if (strcmp(globalcmd, lp->cmd) != 0)
9224                         continue;
9225 
9226                 BAM_DPRINTF(("%s: found matching global command: %s\n",
9227                     fcn, globalcmd));
9228 
9229                 if (found) {
9230                         bam_error(_("duplicate command %s at line %d of "
9231                             "%sboot/grub/menu.lst\n"), globalcmd,
9232                             lp->lineNum, bam_root);
9233                 }
9234                 found = lp;
9235         }
9236 
9237         if (found == NULL) {
9238                 lp = s_calloc(1, sizeof (line_t));
9239                 if (last == NULL) {
9240                         lp->next = mp->start;
9241                         mp->start = lp;
9242                         mp->end = (mp->end) ? mp->end : lp;
9243                 } else {
9244                         lp->next = last->next;
9245                         last->next = lp;
9246                         if (lp->next == NULL)
9247                                 mp->end = lp;
9248                 }
9249                 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
9250                 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
9251                 len += 10;      /* val < 10 digits */
9252                 lp->line = s_calloc(1, len);
9253                 (void) snprintf(lp->line, len, "%s%s%d",
9254                     globalcmd, menu_cmds[SEP_CMD], val);
9255                 BAM_DPRINTF(("%s: wrote new global line: %s\n", fcn, lp->line));
9256                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9257                 return (BAM_WRITE);
9258         }
9259 
9260         /*
9261          * We are changing an existing entry. Retain any prefix whitespace,
9262          * but overwrite everything else. This preserves tabs added for
9263          * readability.
9264          */
9265         str = found->line;
9266         cp = prefix;
9267         while (*str == ' ' || *str == '\t')
9268                 *(cp++) = *(str++);
9269         *cp = '\0'; /* Terminate prefix */
9270         len = strlen(prefix) + strlen(globalcmd);
9271         len += strlen(menu_cmds[SEP_CMD]) + 10;
9272 
9273         free(found->line);
9274         found->line = s_calloc(1, len);
9275         (void) snprintf(found->line, len,
9276             "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
9277 
9278         BAM_DPRINTF(("%s: replaced global line with: %s\n", fcn, found->line));
9279         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9280         return (BAM_WRITE); /* need a write to menu */
9281 }
9282 
9283 /*
9284  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
9285  * expand it to a full unix path.  The calling function is expected to
9286  * output a message if an error occurs and NULL is returned.
9287  */
9288 static char *
9289 expand_path(const char *partial_path)
9290 {
9291         int             new_path_len;
9292         char            *new_path;
9293         char            new_path2[PATH_MAX];
9294         struct stat     sb;
9295         const char      *fcn = "expand_path()";
9296 
9297         new_path_len = strlen(partial_path) + 64;
9298         new_path = s_calloc(1, new_path_len);
9299 
9300         /* First, try the simplest case - something like "kernel/unix" */
9301         (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
9302             partial_path);
9303         if (stat(new_path, &sb) == 0) {
9304                 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9305                 return (new_path);
9306         }
9307 
9308         if (strcmp(partial_path, "kmdb") == 0) {
9309                 (void) snprintf(new_path, new_path_len, "%s -k",
9310                     DIRECT_BOOT_KERNEL);
9311                 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9312                 return (new_path);
9313         }
9314 
9315         /*
9316          * We've quickly reached unsupported usage.  Try once more to
9317          * see if we were just given a glom name.
9318          */
9319         (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9320             partial_path);
9321         (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9322             partial_path);
9323         if (stat(new_path, &sb) == 0) {
9324                 if (stat(new_path2, &sb) == 0) {
9325                         /*
9326                          * We matched both, so we actually
9327                          * want to write the $ISADIR version.
9328                          */
9329                         (void) snprintf(new_path, new_path_len,
9330                             "/platform/i86pc/kernel/%s/$ISADIR/unix",
9331                             partial_path);
9332                 }
9333                 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9334                 return (new_path);
9335         }
9336 
9337         free(new_path);
9338         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9339         return (NULL);
9340 }
9341 
9342 /*
9343  * The kernel cmd and arg have been changed, so
9344  * check whether the archive line needs to change.
9345  */
9346 static void
9347 set_archive_line(entry_t *entryp, line_t *kernelp)
9348 {
9349         line_t          *lp = entryp->start;
9350         char            *new_archive;
9351         menu_cmd_t      m_cmd;
9352         const char      *fcn = "set_archive_line()";
9353 
9354         for (; lp != NULL; lp = lp->next) {
9355                 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9356                     sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9357                         break;
9358                 }
9359 
9360                 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9361                 if (lp == entryp->end) {
9362                         BAM_DPRINTF(("%s: no module/archive line for entry: "
9363                             "%d\n", fcn, entryp->entryNum));
9364                         return;
9365                 }
9366         }
9367         INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9368         if (lp == NULL) {
9369                 BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9370                     fcn, entryp->entryNum));
9371                 return;
9372         }
9373 
9374         if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9375                 new_archive = DIRECT_BOOT_ARCHIVE;
9376                 m_cmd = MODULE_DOLLAR_CMD;
9377         } else if (strstr(kernelp->arg, "amd64") != NULL) {
9378                 new_archive = DIRECT_BOOT_ARCHIVE_64;
9379                 m_cmd = MODULE_CMD;
9380         } else {
9381                 new_archive = DIRECT_BOOT_ARCHIVE_32;
9382                 m_cmd = MODULE_CMD;
9383         }
9384 
9385         if (strcmp(lp->arg, new_archive) == 0) {
9386                 BAM_DPRINTF(("%s: no change for line: %s\n", fcn, lp->arg));
9387                 return;
9388         }
9389 
9390         if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9391                 free(lp->cmd);
9392                 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9393         }
9394 
9395         free(lp->arg);
9396         lp->arg = s_strdup(new_archive);
9397         update_line(lp);
9398         BAM_DPRINTF(("%s: replaced for line: %s\n", fcn, lp->line));
9399 }
9400 
9401 /*
9402  * Title for an entry to set properties that once went in bootenv.rc.
9403  */
9404 #define BOOTENV_RC_TITLE        "Solaris bootenv rc"
9405 
9406 /*
9407  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9408  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
9409  * string, reset the value to the default.  If path is a non-zero-length
9410  * string, set the kernel or arguments.
9411  */
9412 static error_t
9413 get_set_kernel(
9414         menu_t *mp,
9415         menu_cmd_t optnum,
9416         char *path,
9417         char *buf,
9418         size_t bufsize)
9419 {
9420         int             entryNum;
9421         int             rv = BAM_SUCCESS;
9422         int             free_new_path = 0;
9423         entry_t         *entryp;
9424         line_t          *ptr;
9425         line_t          *kernelp;
9426         char            *new_arg;
9427         char            *old_args;
9428         char            *space;
9429         char            *new_path;
9430         char            old_space;
9431         size_t          old_kernel_len = 0;
9432         size_t          new_str_len;
9433         char            *fstype;
9434         char            *osdev;
9435         char            *sign;
9436         char            signbuf[PATH_MAX];
9437         int             ret;
9438         const char      *fcn = "get_set_kernel()";
9439 
9440         assert(bufsize > 0);
9441 
9442         ptr = kernelp = NULL;
9443         new_arg = old_args = space = NULL;
9444         new_path = NULL;
9445         buf[0] = '\0';
9446 
9447         INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9448             bam_direct = BAM_DIRECT_MULTIBOOT);
9449         if (bam_direct != BAM_DIRECT_DBOOT) {
9450                 bam_error(_("bootadm set-menu %s may only be run on "
9451                     "directboot kernels.\n"),
9452                     optnum == KERNEL_CMD ? "kernel" : "args");
9453                 return (BAM_ERROR);
9454         }
9455 
9456         /*
9457          * If a user changed the default entry to a non-bootadm controlled
9458          * one, we don't want to mess with it.  Just print an error and
9459          * return.
9460          */
9461         if (mp->curdefault) {
9462                 entryNum = s_strtol(mp->curdefault->arg);
9463                 for (entryp = mp->entries; entryp; entryp = entryp->next) {
9464                         if (entryp->entryNum == entryNum)
9465                                 break;
9466                 }
9467                 if ((entryp != NULL) &&
9468                     ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9469                         bam_error(_("Default /boot/grub/menu.lst entry is not "
9470                             "controlled by bootadm.  Exiting\n"));
9471                         return (BAM_ERROR);
9472                 }
9473         }
9474 
9475         entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9476             0, &entryNum);
9477 
9478         if (entryp != NULL) {
9479                 for (ptr = entryp->start; ptr && ptr != entryp->end;
9480                     ptr = ptr->next) {
9481                         if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9482                             sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9483                                 kernelp = ptr;
9484                                 break;
9485                         }
9486                 }
9487                 if (kernelp == NULL) {
9488                         bam_error(_("no kernel line found in entry %d\n"),
9489                             entryNum);
9490                         return (BAM_ERROR);
9491                 }
9492 
9493                 old_kernel_len = strcspn(kernelp->arg, " \t");
9494                 space = old_args = kernelp->arg + old_kernel_len;
9495                 while ((*old_args == ' ') || (*old_args == '\t'))
9496                         old_args++;
9497         }
9498 
9499         if (path == NULL) {
9500                 if (entryp == NULL) {
9501                         BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9502                             fcn));
9503                         BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9504                         return (BAM_SUCCESS);
9505                 }
9506                 assert(kernelp);
9507                 if (optnum == ARGS_CMD) {
9508                         if (old_args[0] != '\0') {
9509                                 (void) strlcpy(buf, old_args, bufsize);
9510                                 BAM_DPRINTF(("%s: read menu boot-args: %s\n",
9511                                     fcn, buf));
9512                         }
9513                 } else {
9514                         /*
9515                          * We need to print the kernel, so we just turn the
9516                          * first space into a '\0' and print the beginning.
9517                          * We don't print anything if it's the default kernel.
9518                          */
9519                         old_space = *space;
9520                         *space = '\0';
9521                         if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9522                                 (void) strlcpy(buf, kernelp->arg, bufsize);
9523                                 BAM_DPRINTF(("%s: read menu boot-file: %s\n",
9524                                     fcn, buf));
9525                         }
9526                         *space = old_space;
9527                 }
9528                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9529                 return (BAM_SUCCESS);
9530         }
9531 
9532         /*
9533          * First, check if we're resetting an entry to the default.
9534          */
9535         if ((path[0] == '\0') ||
9536             ((optnum == KERNEL_CMD) &&
9537             (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9538                 if ((entryp == NULL) || (kernelp == NULL)) {
9539                         /* No previous entry, it's already the default */
9540                         BAM_DPRINTF(("%s: no reset, already has default\n",
9541                             fcn));
9542                         return (BAM_SUCCESS);
9543                 }
9544 
9545                 /*
9546                  * Check if we can delete the entry.  If we're resetting the
9547                  * kernel command, and the args is already empty, or if we're
9548                  * resetting the args command, and the kernel is already the
9549                  * default, we can restore the old default and delete the entry.
9550                  */
9551                 if (((optnum == KERNEL_CMD) &&
9552                     ((old_args == NULL) || (old_args[0] == '\0'))) ||
9553                     ((optnum == ARGS_CMD) &&
9554                     (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9555                     sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9556                         kernelp = NULL;
9557                         (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9558                         restore_default_entry(mp, BAM_OLD_RC_DEF,
9559                             mp->old_rc_default);
9560                         mp->old_rc_default = NULL;
9561                         rv = BAM_WRITE;
9562                         BAM_DPRINTF(("%s: resetting to default\n", fcn));
9563                         goto done;
9564                 }
9565 
9566                 if (optnum == KERNEL_CMD) {
9567                         /*
9568                          * At this point, we've already checked that old_args
9569                          * and entryp are valid pointers.  The "+ 2" is for
9570                          * a space a the string termination character.
9571                          */
9572                         new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9573                             strlen(old_args) + 2;
9574                         new_arg = s_calloc(1, new_str_len);
9575                         (void) snprintf(new_arg, new_str_len, "%s %s",
9576                             DIRECT_BOOT_KERNEL, old_args);
9577                         free(kernelp->arg);
9578                         kernelp->arg = new_arg;
9579 
9580                         /*
9581                          * We have changed the kernel line, so we may need
9582                          * to update the archive line as well.
9583                          */
9584                         set_archive_line(entryp, kernelp);
9585                         BAM_DPRINTF(("%s: reset kernel to default, but "
9586                             "retained old args: %s\n", fcn, kernelp->arg));
9587                 } else {
9588                         /*
9589                          * We're resetting the boot args to nothing, so
9590                          * we only need to copy the kernel.  We've already
9591                          * checked that the kernel is not the default.
9592                          */
9593                         new_arg = s_calloc(1, old_kernel_len + 1);
9594                         (void) snprintf(new_arg, old_kernel_len + 1, "%s",
9595                             kernelp->arg);
9596                         free(kernelp->arg);
9597                         kernelp->arg = new_arg;
9598                         BAM_DPRINTF(("%s: reset args to default, but retained "
9599                             "old kernel: %s\n", fcn, kernelp->arg));
9600                 }
9601                 rv = BAM_WRITE;
9602                 goto done;
9603         }
9604 
9605         /*
9606          * Expand the kernel file to a full path, if necessary
9607          */
9608         if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9609                 new_path = expand_path(path);
9610                 if (new_path == NULL) {
9611                         bam_error(_("unable to expand %s to a full file "
9612                             "path.\n"), path);
9613                         BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9614                         return (BAM_ERROR);
9615                 }
9616                 free_new_path = 1;
9617         } else {
9618                 new_path = path;
9619                 free_new_path = 0;
9620         }
9621 
9622         /*
9623          * At this point, we know we're setting a new value.  First, take care
9624          * of the case where there was no previous entry.
9625          */
9626         if (entryp == NULL) {
9627 
9628                 /* Similar to code in update_temp */
9629                 fstype = get_fstype("/");
9630                 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9631                 if (fstype == NULL) {
9632                         bam_error(_("cannot determine filesystem type for "
9633                             "\"/\".\nCannot generate GRUB menu entry with "
9634                             "EEPROM arguments.\n"));
9635                         rv = BAM_ERROR;
9636                         goto done;
9637                 }
9638 
9639                 osdev = get_special("/");
9640                 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9641                 if (osdev == NULL) {
9642                         free(fstype);
9643                         bam_error(_("cannot determine device special file for "
9644                             "\"/\".\nCannot generate GRUB menu entry with "
9645                             "EEPROM arguments.\n"));
9646                         rv = BAM_ERROR;
9647                         goto done;
9648                 }
9649 
9650                 sign = find_existing_sign("/", osdev, fstype);
9651                 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9652                 if (sign == NULL) {
9653                         free(fstype);
9654                         free(osdev);
9655                         bam_error(_("cannot determine boot signature for "
9656                             "\"/\".\nCannot generate GRUB menu entry with "
9657                             "EEPROM arguments.\n"));
9658                         rv = BAM_ERROR;
9659                         goto done;
9660                 }
9661 
9662                 free(osdev);
9663                 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9664                 free(sign);
9665                 assert(strchr(signbuf, '(') == NULL &&
9666                     strchr(signbuf, ',') == NULL &&
9667                     strchr(signbuf, ')') == NULL);
9668 
9669                 if (optnum == KERNEL_CMD) {
9670                         if (strcmp(fstype, "zfs") == 0) {
9671                                 new_str_len = strlen(new_path) +
9672                                     strlen(ZFS_BOOT) + 8;
9673                                 new_arg = s_calloc(1, new_str_len);
9674                                 (void) snprintf(new_arg, new_str_len, "%s %s",
9675                                     new_path, ZFS_BOOT);
9676                                 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9677                                     new_arg));
9678                                 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9679                                     signbuf, new_arg, NULL, NULL, NULL);
9680                                 free(new_arg);
9681                         } else {
9682                                 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9683                                     new_path));
9684                                 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9685                                     signbuf, new_path, NULL, NULL, NULL);
9686                         }
9687                 } else {
9688                         new_str_len = strlen(path) + 8;
9689                         if (strcmp(fstype, "zfs") == 0) {
9690                                 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9691                                 new_arg = s_calloc(1, new_str_len);
9692                                 (void) snprintf(new_arg, new_str_len, "%s %s",
9693                                     DIRECT_BOOT_KERNEL_ZFS, path);
9694                         } else {
9695                                 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9696                                 new_arg = s_calloc(1, new_str_len);
9697                                 (void) snprintf(new_arg, new_str_len, "%s %s",
9698                                     DIRECT_BOOT_KERNEL, path);
9699                         }
9700 
9701                         BAM_DPRINTF(("%s: new args=%s\n", fcn, new_arg));
9702                         entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9703                             signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9704                         free(new_arg);
9705                 }
9706                 free(fstype);
9707                 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9708                     entryNum = BAM_ERROR);
9709                 if (entryNum == BAM_ERROR) {
9710                         bam_error(_("failed to add boot entry: %s\n"),
9711                             BOOTENV_RC_TITLE);
9712                         rv = BAM_ERROR;
9713                         goto done;
9714                 }
9715                 save_default_entry(mp, BAM_OLD_RC_DEF);
9716                 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9717                 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9718                 if (ret == BAM_ERROR) {
9719                         bam_error(_("failed to set default to: %d\n"),
9720                             entryNum);
9721                 }
9722                 rv = BAM_WRITE;
9723                 goto done;
9724         }
9725 
9726         /*
9727          * There was already an bootenv entry which we need to edit.
9728          */
9729         if (optnum == KERNEL_CMD) {
9730                 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9731                 new_arg = s_calloc(1, new_str_len);
9732                 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9733                     old_args);
9734                 free(kernelp->arg);
9735                 kernelp->arg = new_arg;
9736 
9737                 /*
9738                  * If we have changed the kernel line, we may need to update
9739                  * the archive line as well.
9740                  */
9741                 set_archive_line(entryp, kernelp);
9742                 BAM_DPRINTF(("%s: rc line exists, replaced kernel, same "
9743                     "args: %s\n", fcn, kernelp->arg));
9744         } else {
9745                 new_str_len = old_kernel_len + strlen(path) + 8;
9746                 new_arg = s_calloc(1, new_str_len);
9747                 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9748                 (void) strlcat(new_arg, " ", new_str_len);
9749                 (void) strlcat(new_arg, path, new_str_len);
9750                 free(kernelp->arg);
9751                 kernelp->arg = new_arg;
9752                 BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9753                     "args: %s\n", fcn, kernelp->arg));
9754         }
9755         rv = BAM_WRITE;
9756 
9757 done:
9758         if ((rv == BAM_WRITE) && kernelp)
9759                 update_line(kernelp);
9760         if (free_new_path)
9761                 free(new_path);
9762         if (rv == BAM_WRITE) {
9763                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9764         } else {
9765                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9766         }
9767         return (rv);
9768 }
9769 
9770 static error_t
9771 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9772 {
9773         const char      *fcn = "get_kernel()";
9774         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_cmds[optnum]));
9775         return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9776 }
9777 
9778 static error_t
9779 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9780 {
9781         const char      *fcn = "set_kernel()";
9782         assert(path != NULL);
9783         BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn,
9784             menu_cmds[optnum], path));
9785         return (get_set_kernel(mp, optnum, path, buf, bufsize));
9786 }
9787 
9788 /*ARGSUSED*/
9789 static error_t
9790 set_option(menu_t *mp, char *dummy, char *opt)
9791 {
9792         int             optnum;
9793         int             optval;
9794         char            *val;
9795         char            buf[BUFSIZ] = "";
9796         error_t         rv;
9797         const char      *fcn = "set_option()";
9798 
9799         assert(mp);
9800         assert(opt);
9801         assert(dummy == NULL);
9802 
9803         /* opt is set from bam_argv[0] and is always non-NULL */
9804         BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt));
9805 
9806         val = strchr(opt, '=');
9807         if (val != NULL) {
9808                 *val = '\0';
9809         }
9810 
9811         if (strcmp(opt, "default") == 0) {
9812                 optnum = DEFAULT_CMD;
9813         } else if (strcmp(opt, "timeout") == 0) {
9814                 optnum = TIMEOUT_CMD;
9815         } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9816                 optnum = KERNEL_CMD;
9817         } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9818                 optnum = ARGS_CMD;
9819         } else {
9820                 bam_error(_("invalid option: %s\n"), opt);
9821                 return (BAM_ERROR);
9822         }
9823 
9824         /*
9825          * kernel and args are allowed without "=new_value" strings.  All
9826          * others cause errors
9827          */
9828         if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9829                 bam_error(_("option has no argument: %s\n"), opt);
9830                 return (BAM_ERROR);
9831         } else if (val != NULL) {
9832                 *val = '=';
9833         }
9834 
9835         if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9836                 BAM_DPRINTF(("%s: setting %s option to %s\n",
9837                     fcn, menu_cmds[optnum], val ? val + 1 : "NULL"));
9838 
9839                 if (val)
9840                         rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9841                 else
9842                         rv = get_kernel(mp, optnum, buf, sizeof (buf));
9843                 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9844                         (void) printf("%s\n", buf);
9845         } else {
9846                 optval = s_strtol(val + 1);
9847                 BAM_DPRINTF(("%s: setting %s option to %s\n", fcn,
9848                     menu_cmds[optnum], val + 1));
9849                 rv = set_global(mp, menu_cmds[optnum], optval);
9850         }
9851 
9852         if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9853                 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9854         } else {
9855                 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9856         }
9857 
9858         return (rv);
9859 }
9860 
9861 /*
9862  * The quiet argument suppresses messages. This is used
9863  * when invoked in the context of other commands (e.g. list_entry)
9864  */
9865 static error_t
9866 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9867 {
9868         line_t *lp;
9869         char *arg;
9870         int done, ret = BAM_SUCCESS;
9871 
9872         assert(mp);
9873         assert(menu_path);
9874         assert(globalcmd);
9875 
9876         if (mp->start == NULL) {
9877                 if (!quiet)
9878                         bam_error(_("menu file not found: %s\n"), menu_path);
9879                 return (BAM_ERROR);
9880         }
9881 
9882         done = 0;
9883         for (lp = mp->start; lp; lp = lp->next) {
9884                 if (lp->flags != BAM_GLOBAL)
9885                         continue;
9886 
9887                 if (lp->cmd == NULL) {
9888                         if (!quiet)
9889                                 bam_error(_("no command at line %d\n"),
9890                                     lp->lineNum);
9891                         continue;
9892                 }
9893 
9894                 if (strcmp(globalcmd, lp->cmd) != 0)
9895                         continue;
9896 
9897                 /* Found global. Check for duplicates */
9898                 if (done && !quiet) {
9899                         bam_error(_("duplicate command %s at line %d of "
9900                             "%sboot/grub/menu.lst\n"), globalcmd,
9901                             lp->lineNum, bam_root);
9902                         ret = BAM_ERROR;
9903                 }
9904 
9905                 arg = lp->arg ? lp->arg : "";
9906                 bam_print(_("%s %s\n"), globalcmd, arg);
9907                 done = 1;
9908         }
9909 
9910         if (!done && bam_verbose)
9911                 bam_print(_("no %s entry found\n"), globalcmd);
9912 
9913         return (ret);
9914 }
9915 
9916 static error_t
9917 menu_write(char *root, menu_t *mp)
9918 {
9919         const char *fcn = "menu_write()";
9920 
9921         BAM_DPRINTF(("%s: entered menu_write() for root: <%s>\n", fcn, root));
9922         return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9923 }
9924 
9925 void
9926 line_free(line_t *lp)
9927 {
9928         if (lp == NULL)
9929                 return;
9930 
9931         if (lp->cmd != NULL)
9932                 free(lp->cmd);
9933         if (lp->sep)
9934                 free(lp->sep);
9935         if (lp->arg)
9936                 free(lp->arg);
9937         if (lp->line)
9938                 free(lp->line);
9939         free(lp);
9940 }
9941 
9942 static void
9943 linelist_free(line_t *start)
9944 {
9945         line_t *lp;
9946 
9947         while (start) {
9948                 lp = start;
9949                 start = start->next;
9950                 line_free(lp);
9951         }
9952 }
9953 
9954 static void
9955 filelist_free(filelist_t *flistp)
9956 {
9957         linelist_free(flistp->head);
9958         flistp->head = NULL;
9959         flistp->tail = NULL;
9960 }
9961 
9962 static void
9963 menu_free(menu_t *mp)
9964 {
9965         entry_t *ent, *tmp;
9966         assert(mp);
9967 
9968         if (mp->start)
9969                 linelist_free(mp->start);
9970         ent = mp->entries;
9971         while (ent) {
9972                 tmp = ent;
9973                 ent = tmp->next;
9974                 free(tmp);
9975         }
9976 
9977         free(mp);
9978 }
9979 
9980 /*
9981  * Utility routines
9982  */
9983 
9984 
9985 /*
9986  * Returns 0 on success
9987  * Any other value indicates an error
9988  */
9989 static int
9990 exec_cmd(char *cmdline, filelist_t *flistp)
9991 {
9992         char buf[BUFSIZ];
9993         int ret;
9994         FILE *ptr;
9995         sigset_t set;
9996         void (*disp)(int);
9997 
9998         /*
9999          * For security
10000          * - only absolute paths are allowed
10001          * - set IFS to space and tab
10002          */
10003         if (*cmdline != '/') {
10004                 bam_error(_("path is not absolute: %s\n"), cmdline);
10005                 return (-1);
10006         }
10007         (void) putenv("IFS= \t");
10008 
10009         /*
10010          * We may have been exec'ed with SIGCHLD blocked
10011          * unblock it here
10012          */
10013         (void) sigemptyset(&set);
10014         (void) sigaddset(&set, SIGCHLD);
10015         if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
10016                 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno));
10017                 return (-1);
10018         }
10019 
10020         /*
10021          * Set SIGCHLD disposition to SIG_DFL for popen/pclose
10022          */
10023         disp = sigset(SIGCHLD, SIG_DFL);
10024         if (disp == SIG_ERR) {
10025                 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
10026                     strerror(errno));
10027                 return (-1);
10028         }
10029         if (disp == SIG_HOLD) {
10030                 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
10031                     cmdline);
10032                 return (-1);
10033         }
10034 
10035         ptr = popen(cmdline, "r");
10036         if (ptr == NULL) {
10037                 bam_error(_("popen failed: %s: %s\n"), cmdline,
10038                     strerror(errno));
10039                 return (-1);
10040         }
10041 
10042         /*
10043          * If we simply do a pclose() following a popen(), pclose()
10044          * will close the reader end of the pipe immediately even
10045          * if the child process has not started/exited. pclose()
10046          * does wait for cmd to terminate before returning though.
10047          * When the executed command writes its output to the pipe
10048          * there is no reader process and the command dies with
10049          * SIGPIPE. To avoid this we read repeatedly until read
10050          * terminates with EOF. This indicates that the command
10051          * (writer) has closed the pipe and we can safely do a
10052          * pclose().
10053          *
10054          * Since pclose() does wait for the command to exit,
10055          * we can safely reap the exit status of the command
10056          * from the value returned by pclose()
10057          */
10058         while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
10059                 if (flistp == NULL) {
10060                         /* s_fgets strips newlines, so insert them at the end */
10061                         bam_print(_("%s\n"), buf);
10062                 } else {
10063                         append_to_flist(flistp, buf);
10064                 }
10065         }
10066 
10067         ret = pclose(ptr);
10068         if (ret == -1) {
10069                 bam_error(_("pclose failed: %s: %s\n"), cmdline,
10070                     strerror(errno));
10071                 return (-1);
10072         }
10073 
10074         if (WIFEXITED(ret)) {
10075                 return (WEXITSTATUS(ret));
10076         } else {
10077                 bam_error(_("command terminated abnormally: %s: %d\n"),
10078                     cmdline, ret);
10079                 return (-1);
10080         }
10081 }
10082 
10083 /*
10084  * Since this function returns -1 on error
10085  * it cannot be used to convert -1. However,
10086  * that is sufficient for what we need.
10087  */
10088 static long
10089 s_strtol(char *str)
10090 {
10091         long l;
10092         char *res = NULL;
10093 
10094         if (str == NULL) {
10095                 return (-1);
10096         }
10097 
10098         errno = 0;
10099         l = strtol(str, &res, 10);
10100         if (errno || *res != '\0') {
10101                 return (-1);
10102         }
10103 
10104         return (l);
10105 }
10106 
10107 /*
10108  * Wrapper around fputs, that adds a newline (since fputs doesn't)
10109  */
10110 static int
10111 s_fputs(char *str, FILE *fp)
10112 {
10113         char linebuf[BAM_MAXLINE];
10114 
10115         (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
10116         return (fputs(linebuf, fp));
10117 }
10118 
10119 /*
10120  * Wrapper around fgets, that strips newlines returned by fgets
10121  */
10122 char *
10123 s_fgets(char *buf, int buflen, FILE *fp)
10124 {
10125         int n;
10126 
10127         buf = fgets(buf, buflen, fp);
10128         if (buf) {
10129                 n = strlen(buf);
10130                 if (n == buflen - 1 && buf[n-1] != '\n')
10131                         bam_error(_("the following line is too long "
10132                             "(> %d chars)\n\t%s\n"), buflen - 1, buf);
10133                 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
10134         }
10135 
10136         return (buf);
10137 }
10138 
10139 void *
10140 s_calloc(size_t nelem, size_t sz)
10141 {
10142         void *ptr;
10143 
10144         ptr = calloc(nelem, sz);
10145         if (ptr == NULL) {
10146                 bam_error(_("could not allocate memory: size = %u\n"),
10147                     nelem*sz);
10148                 bam_exit(1);
10149         }
10150         return (ptr);
10151 }
10152 
10153 void *
10154 s_realloc(void *ptr, size_t sz)
10155 {
10156         ptr = realloc(ptr, sz);
10157         if (ptr == NULL) {
10158                 bam_error(_("could not allocate memory: size = %u\n"), sz);
10159                 bam_exit(1);
10160         }
10161         return (ptr);
10162 }
10163 
10164 char *
10165 s_strdup(char *str)
10166 {
10167         char *ptr;
10168 
10169         if (str == NULL)
10170                 return (NULL);
10171 
10172         ptr = strdup(str);
10173         if (ptr == NULL) {
10174                 bam_error(_("could not allocate memory: size = %u\n"),
10175                     strlen(str) + 1);
10176                 bam_exit(1);
10177         }
10178         return (ptr);
10179 }
10180 
10181 /*
10182  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
10183  * Returns 0 otherwise
10184  */
10185 static int
10186 is_amd64(void)
10187 {
10188         static int amd64 = -1;
10189         char isabuf[257];       /* from sysinfo(2) manpage */
10190 
10191         if (amd64 != -1)
10192                 return (amd64);
10193 
10194         if (bam_alt_platform) {
10195                 if (strcmp(bam_platform, "i86pc") == 0) {
10196                         amd64 = 1;              /* diskless server */
10197                 }
10198         } else {
10199                 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
10200                     strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
10201                         amd64 = 1;
10202                 } else if (strstr(isabuf, "i386") == NULL) {
10203                         amd64 = 1;              /* diskless server */
10204                 }
10205         }
10206         if (amd64 == -1)
10207                 amd64 = 0;
10208 
10209         return (amd64);
10210 }
10211 
10212 static char *
10213 get_machine(void)
10214 {
10215         static int cached = -1;
10216         static char mbuf[257];  /* from sysinfo(2) manpage */
10217 
10218         if (cached == 0)
10219                 return (mbuf);
10220 
10221         if (bam_alt_platform) {
10222                 return (bam_platform);
10223         } else {
10224                 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
10225                         cached = 1;
10226                 }
10227         }
10228         if (cached == -1) {
10229                 mbuf[0] = '\0';
10230                 cached = 0;
10231         }
10232 
10233         return (mbuf);
10234 }
10235 
10236 int
10237 is_sparc(void)
10238 {
10239         static int issparc = -1;
10240         char mbuf[257]; /* from sysinfo(2) manpage */
10241 
10242         if (issparc != -1)
10243                 return (issparc);
10244 
10245         if (bam_alt_platform) {
10246                 if (strncmp(bam_platform, "sun4", 4) == 0) {
10247                         issparc = 1;
10248                 }
10249         } else {
10250                 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
10251                     strcmp(mbuf, "sparc") == 0) {
10252                         issparc = 1;
10253                 }
10254         }
10255         if (issparc == -1)
10256                 issparc = 0;
10257 
10258         return (issparc);
10259 }
10260 
10261 static void
10262 append_to_flist(filelist_t *flistp, char *s)
10263 {
10264         line_t *lp;
10265 
10266         lp = s_calloc(1, sizeof (line_t));
10267         lp->line = s_strdup(s);
10268         if (flistp->head == NULL)
10269                 flistp->head = lp;
10270         else
10271                 flistp->tail->next = lp;
10272         flistp->tail = lp;
10273 }
10274 
10275 #if !defined(_OBP)
10276 
10277 UCODE_VENDORS;
10278 
10279 /*ARGSUSED*/
10280 static void
10281 ucode_install(char *root)
10282 {
10283         int i;
10284 
10285         for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
10286                 int cmd_len = PATH_MAX + 256;
10287                 char cmd[PATH_MAX + 256];
10288                 char file[PATH_MAX];
10289                 char timestamp[PATH_MAX];
10290                 struct stat fstatus, tstatus;
10291                 struct utimbuf u_times;
10292 
10293                 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
10294                     bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
10295                     ucode_vendors[i].extstr);
10296 
10297                 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
10298                         continue;
10299 
10300                 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
10301 
10302                 if (stat(timestamp, &tstatus) == 0 &&
10303                     fstatus.st_mtime <= tstatus.st_mtime)
10304                         continue;
10305 
10306                 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
10307                     "%s/%s/%s %s > /dev/null 2>&1", bam_root,
10308                     UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
10309                 if (system(cmd) != 0)
10310                         return;
10311 
10312                 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
10313                         return;
10314 
10315                 u_times.actime = fstatus.st_atime;
10316                 u_times.modtime = fstatus.st_mtime;
10317                 (void) utime(timestamp, &u_times);
10318         }
10319 }
10320 #endif