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