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