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