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