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