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