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