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