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