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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * bootadm(1M) is a new utility for managing bootability of 30 * Solaris *Newboot* environments. It has two primary tasks: 31 * - Allow end users to manage bootability of Newboot Solaris instances 32 * - Provide services to other subsystems in Solaris (primarily Install) 33 */ 34 35 /* Headers */ 36 #include <stdio.h> 37 #include <errno.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <stdarg.h> 44 #include <limits.h> 45 #include <signal.h> 46 #include <sys/wait.h> 47 #include <sys/mnttab.h> 48 #include <sys/statvfs.h> 49 #include <libnvpair.h> 50 #include <ftw.h> 51 #include <fcntl.h> 52 #include <strings.h> 53 #include <utime.h> 54 #include <sys/systeminfo.h> 55 #include <sys/dktp/fdisk.h> 56 #include <sys/param.h> 57 #include <dirent.h> 58 #include <ctype.h> 59 #include <libgen.h> 60 #include <sys/sysmacros.h> 61 62 #if !defined(_OPB) 63 #include <sys/ucode.h> 64 #endif 65 66 #include <pwd.h> 67 #include <grp.h> 68 #include <device_info.h> 69 #include <sys/vtoc.h> 70 #include <sys/efi_partition.h> 71 72 #include <locale.h> 73 74 #include "message.h" 75 #include "bootadm.h" 76 77 #ifndef TEXT_DOMAIN 78 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 79 #endif /* TEXT_DOMAIN */ 80 81 /* Type definitions */ 82 83 /* Primary subcmds */ 84 typedef enum { 85 BAM_MENU = 3, 86 BAM_ARCHIVE 87 } subcmd_t; 88 89 typedef enum { 90 OPT_ABSENT = 0, /* No option */ 91 OPT_REQ, /* option required */ 92 OPT_OPTIONAL /* option may or may not be present */ 93 } option_t; 94 95 typedef struct { 96 char *subcmd; 97 option_t option; 98 error_t (*handler)(); 99 int unpriv; /* is this an unprivileged command */ 100 } subcmd_defn_t; 101 102 #define LINE_INIT 0 /* lineNum initial value */ 103 #define ENTRY_INIT -1 /* entryNum initial value */ 104 #define ALL_ENTRIES -2 /* selects all boot entries */ 105 106 #define GRUB_DIR "/boot/grub" 107 #define GRUB_MENU "/boot/grub/menu.lst" 108 #define MENU_TMP "/boot/grub/menu.lst.tmp" 109 #define RAMDISK_SPECIAL "/ramdisk" 110 #define STUBBOOT "/stubboot" 111 #define MULTIBOOT "/platform/i86pc/multiboot" 112 #define GRUBSIGN_DIR "/boot/grub/bootsign" 113 #define GRUBSIGN_BACKUP "/etc/bootsign" 114 #define GRUBSIGN_UFS_PREFIX "rootfs" 115 #define GRUBSIGN_ZFS_PREFIX "pool_" 116 #define GRUBSIGN_LU_PREFIX "BE_" 117 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures" 118 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy" 119 120 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST" 121 122 /* lock related */ 123 #define BAM_LOCK_FILE "/var/run/bootadm.lock" 124 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 125 126 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk" 127 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap" 128 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist" 129 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map" 130 131 #define GRUB_slice "/etc/lu/GRUB_slice" 132 #define GRUB_root "/etc/lu/GRUB_root" 133 #define GRUB_fdisk "/etc/lu/GRUB_fdisk" 134 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target" 135 136 #define INSTALLGRUB "/sbin/installgrub" 137 #define STAGE1 "/boot/grub/stage1" 138 #define STAGE2 "/boot/grub/stage2" 139 140 typedef enum zfs_mnted { 141 ZFS_MNT_ERROR = -1, 142 LEGACY_MOUNTED = 1, 143 LEGACY_ALREADY, 144 ZFS_MOUNTED, 145 ZFS_ALREADY 146 } zfs_mnted_t; 147 148 149 150 151 /* 152 * The following two defines are used to detect and create the correct 153 * boot archive when safemode patching is underway. LOFS_PATCH_FILE is a 154 * contracted private interface between bootadm and the install 155 * consolidation. It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE 156 * is applied. 157 */ 158 159 #define LOFS_PATCH_FILE "/var/run/.patch_loopback_mode" 160 #define LOFS_PATCH_MNT "/var/run/.patch_root_loopbackmnt" 161 162 /* 163 * Default file attributes 164 */ 165 #define DEFAULT_DEV_MODE 0644 /* default permissions */ 166 #define DEFAULT_DEV_UID 0 /* user root */ 167 #define DEFAULT_DEV_GID 3 /* group sys */ 168 169 /* 170 * Menu related 171 * menu_cmd_t and menu_cmds must be kept in sync 172 */ 173 char *menu_cmds[] = { 174 "default", /* DEFAULT_CMD */ 175 "timeout", /* TIMEOUT_CMD */ 176 "title", /* TITLE_CMD */ 177 "root", /* ROOT_CMD */ 178 "kernel", /* KERNEL_CMD */ 179 "kernel$", /* KERNEL_DOLLAR_CMD */ 180 "module", /* MODULE_CMD */ 181 "module$", /* MODULE_DOLLAR_CMD */ 182 " ", /* SEP_CMD */ 183 "#", /* COMMENT_CMD */ 184 "chainloader", /* CHAINLOADER_CMD */ 185 "args", /* ARGS_CMD */ 186 "findroot", /* FINDROOT_CMD */ 187 NULL 188 }; 189 190 #define OPT_ENTRY_NUM "entry" 191 192 /* 193 * exec_cmd related 194 */ 195 typedef struct { 196 line_t *head; 197 line_t *tail; 198 } filelist_t; 199 200 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk" 201 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk" 202 203 #define FILE_STAT "boot/solaris/filestat.ramdisk" 204 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp" 205 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 206 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 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 static char *bam_root; 215 static int bam_rootlen; 216 static int bam_root_readonly; 217 static int bam_alt_root; 218 static char *bam_subcmd; 219 static char *bam_opt; 220 static char **bam_argv; 221 static int bam_argc; 222 static int bam_check; 223 static int bam_smf_check; 224 static int bam_lock_fd = -1; 225 static int bam_zfs; 226 static char rootbuf[PATH_MAX] = "/"; 227 static int bam_update_all; 228 static int bam_alt_platform; 229 static char *bam_platform; 230 231 /* function prototypes */ 232 static void parse_args_internal(int, char *[]); 233 static void parse_args(int, char *argv[]); 234 static error_t bam_menu(char *, char *, int, char *[]); 235 static error_t bam_archive(char *, char *); 236 237 static void bam_exit(int); 238 static void bam_lock(void); 239 static void bam_unlock(void); 240 241 static int exec_cmd(char *, filelist_t *); 242 static error_t read_globals(menu_t *, char *, char *, int); 243 static int menu_on_bootdisk(char *os_root, char *menu_root); 244 static menu_t *menu_read(char *); 245 static error_t menu_write(char *, menu_t *); 246 static void linelist_free(line_t *); 247 static void menu_free(menu_t *); 248 static void filelist_free(filelist_t *); 249 static error_t list2file(char *, char *, char *, line_t *); 250 static error_t list_entry(menu_t *, char *, char *); 251 static error_t delete_all_entries(menu_t *, char *, char *); 252 static error_t update_entry(menu_t *mp, char *menu_root, char *opt); 253 static error_t update_temp(menu_t *mp, char *dummy, char *opt); 254 255 static error_t update_archive(char *, char *); 256 static error_t list_archive(char *, char *); 257 static error_t update_all(char *, char *); 258 static error_t read_list(char *, filelist_t *); 259 static error_t set_global(menu_t *, char *, int); 260 static error_t set_option(menu_t *, char *, char *); 261 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t); 262 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t); 263 static char *expand_path(const char *); 264 265 static long s_strtol(char *); 266 static int s_fputs(char *, FILE *); 267 268 static int is_zfs(char *root); 269 static int is_ufs(char *root); 270 static int is_pcfs(char *root); 271 static int is_amd64(void); 272 static int is_sun4u(void); 273 static int is_sun4v(void); 274 static void append_to_flist(filelist_t *, char *); 275 static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted); 276 static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt); 277 static int ufs_add_to_sign_list(char *sign); 278 279 #if !defined(_OPB) 280 static void ucode_install(); 281 #endif 282 283 /* Menu related sub commands */ 284 static subcmd_defn_t menu_subcmds[] = { 285 "set_option", OPT_ABSENT, set_option, 0, /* PUB */ 286 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */ 287 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */ 288 "update_entry", OPT_REQ, update_entry, 0, /* menu */ 289 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */ 290 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */ 291 NULL, 0, NULL, 0 /* must be last */ 292 }; 293 294 /* Archive related sub commands */ 295 static subcmd_defn_t arch_subcmds[] = { 296 "update", OPT_ABSENT, update_archive, 0, /* PUB */ 297 "update_all", OPT_ABSENT, update_all, 0, /* PVT */ 298 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */ 299 NULL, 0, NULL, 0 /* must be last */ 300 }; 301 302 static struct { 303 nvlist_t *new_nvlp; 304 nvlist_t *old_nvlp; 305 int need_update; 306 } walk_arg; 307 308 309 struct safefile { 310 char *name; 311 struct safefile *next; 312 }; 313 314 static struct safefile *safefiles = NULL; 315 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update" 316 317 static void 318 usage(void) 319 { 320 (void) fprintf(stderr, "USAGE:\n"); 321 322 323 /* archive usage */ 324 (void) fprintf(stderr, 325 "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog); 326 (void) fprintf(stderr, 327 "\t%s list-archive [-R altroot [-p platform>]]\n", prog); 328 #if !defined(_OPB) 329 /* x86 only */ 330 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog); 331 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog); 332 #endif 333 } 334 335 int 336 main(int argc, char *argv[]) 337 { 338 error_t ret; 339 340 (void) setlocale(LC_ALL, ""); 341 (void) textdomain(TEXT_DOMAIN); 342 343 if ((prog = strrchr(argv[0], '/')) == NULL) { 344 prog = argv[0]; 345 } else { 346 prog++; 347 } 348 349 INJECT_ERROR1("ASSERT_ON", assert(0)) 350 351 /* 352 * Don't depend on caller's umask 353 */ 354 (void) umask(0022); 355 356 parse_args(argc, argv); 357 358 switch (bam_cmd) { 359 case BAM_MENU: 360 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv); 361 break; 362 case BAM_ARCHIVE: 363 ret = bam_archive(bam_subcmd, bam_opt); 364 break; 365 default: 366 usage(); 367 bam_exit(1); 368 } 369 370 if (ret != BAM_SUCCESS) 371 bam_exit(1); 372 373 bam_unlock(); 374 return (0); 375 } 376 377 /* 378 * Equivalence of public and internal commands: 379 * update-archive -- -a update 380 * list-archive -- -a list 381 * set-menu -- -m set_option 382 * list-menu -- -m list_entry 383 * update-menu -- -m update_entry 384 */ 385 static struct cmd_map { 386 char *bam_cmdname; 387 int bam_cmd; 388 char *bam_subcmd; 389 } cmd_map[] = { 390 { "update-archive", BAM_ARCHIVE, "update"}, 391 { "list-archive", BAM_ARCHIVE, "list"}, 392 { "set-menu", BAM_MENU, "set_option"}, 393 { "list-menu", BAM_MENU, "list_entry"}, 394 { "update-menu", BAM_MENU, "update_entry"}, 395 { NULL, 0, NULL} 396 }; 397 398 /* 399 * Commands syntax published in bootadm(1M) are parsed here 400 */ 401 static void 402 parse_args(int argc, char *argv[]) 403 { 404 struct cmd_map *cmp = cmd_map; 405 406 /* command conforming to the final spec */ 407 if (argc > 1 && argv[1][0] != '-') { 408 /* 409 * Map commands to internal table. 410 */ 411 while (cmp->bam_cmdname) { 412 if (strcmp(argv[1], cmp->bam_cmdname) == 0) { 413 bam_cmd = cmp->bam_cmd; 414 bam_subcmd = cmp->bam_subcmd; 415 break; 416 } 417 cmp++; 418 } 419 if (cmp->bam_cmdname == NULL) { 420 usage(); 421 bam_exit(1); 422 } 423 argc--; 424 argv++; 425 } 426 427 parse_args_internal(argc, argv); 428 } 429 430 /* 431 * A combination of public and private commands are parsed here. 432 * The internal syntax and the corresponding functionality are: 433 * -a update -- update-archive 434 * -a list -- list-archive 435 * -a update-all -- (reboot to sync all mounted OS archive) 436 * -m update_entry -- update-menu 437 * -m list_entry -- list-menu 438 * -m update_temp -- (reboot -- [boot-args]) 439 * -m delete_all_entries -- (called from install) 440 */ 441 static void 442 parse_args_internal(int argc, char *argv[]) 443 { 444 int c, error; 445 extern char *optarg; 446 extern int optind, opterr; 447 448 /* Suppress error message from getopt */ 449 opterr = 0; 450 451 error = 0; 452 while ((c = getopt(argc, argv, "a:d:fm:no:vCR:p:Z")) != -1) { 453 switch (c) { 454 case 'a': 455 if (bam_cmd) { 456 error = 1; 457 bam_error(MULT_CMDS, c); 458 } 459 bam_cmd = BAM_ARCHIVE; 460 bam_subcmd = optarg; 461 break; 462 case 'd': 463 if (bam_debug) { 464 error = 1; 465 bam_error(DUP_OPT, c); 466 } 467 bam_debug = s_strtol(optarg); 468 break; 469 case 'f': 470 if (bam_force) { 471 error = 1; 472 bam_error(DUP_OPT, c); 473 } 474 bam_force = 1; 475 break; 476 case 'm': 477 if (bam_cmd) { 478 error = 1; 479 bam_error(MULT_CMDS, c); 480 } 481 bam_cmd = BAM_MENU; 482 bam_subcmd = optarg; 483 break; 484 case 'n': 485 if (bam_check) { 486 error = 1; 487 bam_error(DUP_OPT, c); 488 } 489 bam_check = 1; 490 break; 491 case 'o': 492 if (bam_opt) { 493 error = 1; 494 bam_error(DUP_OPT, c); 495 } 496 bam_opt = optarg; 497 break; 498 case 'v': 499 if (bam_verbose) { 500 error = 1; 501 bam_error(DUP_OPT, c); 502 } 503 bam_verbose = 1; 504 break; 505 case 'C': 506 bam_smf_check = 1; 507 break; 508 case 'R': 509 if (bam_root) { 510 error = 1; 511 bam_error(DUP_OPT, c); 512 break; 513 } else if (realpath(optarg, rootbuf) == NULL) { 514 error = 1; 515 bam_error(CANT_RESOLVE, optarg, 516 strerror(errno)); 517 break; 518 } 519 bam_alt_root = 1; 520 bam_root = rootbuf; 521 bam_rootlen = strlen(rootbuf); 522 break; 523 case 'p': 524 bam_alt_platform = 1; 525 bam_platform = optarg; 526 if ((strcmp(bam_platform, "i86pc") != 0) && 527 (strcmp(bam_platform, "sun4u") != 0) && 528 (strcmp(bam_platform, "sun4v") != 0)) { 529 error = 1; 530 bam_error(INVALID_PLAT, bam_platform); 531 } 532 break; 533 case 'Z': 534 bam_zfs = 1; 535 break; 536 case '?': 537 error = 1; 538 bam_error(BAD_OPT, optopt); 539 break; 540 default : 541 error = 1; 542 bam_error(BAD_OPT, c); 543 break; 544 } 545 } 546 547 /* 548 * An alternate platform requires an alternate root 549 */ 550 if (bam_alt_platform && bam_alt_root == 0) { 551 usage(); 552 bam_exit(0); 553 } 554 555 /* 556 * A command option must be specfied 557 */ 558 if (!bam_cmd) { 559 if (bam_opt && strcmp(bam_opt, "all") == 0) { 560 usage(); 561 bam_exit(0); 562 } 563 bam_error(NEED_CMD); 564 error = 1; 565 } 566 567 if (error) { 568 usage(); 569 bam_exit(1); 570 } 571 572 if (optind > argc) { 573 bam_error(INT_ERROR, "parse_args"); 574 bam_exit(1); 575 } else if (optind < argc) { 576 bam_argv = &argv[optind]; 577 bam_argc = argc - optind; 578 } 579 580 /* 581 * -n implies verbose mode 582 */ 583 if (bam_check) 584 bam_verbose = 1; 585 } 586 587 static error_t 588 check_subcmd_and_options( 589 char *subcmd, 590 char *opt, 591 subcmd_defn_t *table, 592 error_t (**fp)()) 593 { 594 int i; 595 596 if (subcmd == NULL) { 597 bam_error(NEED_SUBCMD); 598 return (BAM_ERROR); 599 } 600 601 if (strcmp(subcmd, "set_option") == 0) { 602 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) { 603 bam_error(MISSING_ARG); 604 usage(); 605 return (BAM_ERROR); 606 } else if (bam_argc > 1 || bam_argv[1] != NULL) { 607 bam_error(TRAILING_ARGS); 608 usage(); 609 return (BAM_ERROR); 610 } 611 } else if (bam_argc || bam_argv) { 612 bam_error(TRAILING_ARGS); 613 usage(); 614 return (BAM_ERROR); 615 } 616 617 if (bam_root == NULL) { 618 bam_root = rootbuf; 619 bam_rootlen = 1; 620 } 621 622 /* verify that subcmd is valid */ 623 for (i = 0; table[i].subcmd != NULL; i++) { 624 if (strcmp(table[i].subcmd, subcmd) == 0) 625 break; 626 } 627 628 if (table[i].subcmd == NULL) { 629 bam_error(INVALID_SUBCMD, subcmd); 630 return (BAM_ERROR); 631 } 632 633 if (table[i].unpriv == 0 && geteuid() != 0) { 634 bam_error(MUST_BE_ROOT); 635 return (BAM_ERROR); 636 } 637 638 /* 639 * Currently only privileged commands need a lock 640 */ 641 if (table[i].unpriv == 0) 642 bam_lock(); 643 644 /* subcmd verifies that opt is appropriate */ 645 if (table[i].option != OPT_OPTIONAL) { 646 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) { 647 if (opt) 648 bam_error(NO_OPT_REQ, subcmd); 649 else 650 bam_error(MISS_OPT, subcmd); 651 return (BAM_ERROR); 652 } 653 } 654 655 *fp = table[i].handler; 656 657 return (BAM_SUCCESS); 658 } 659 660 /* 661 * NOTE: A single "/" is also considered a trailing slash and will 662 * be deleted. 663 */ 664 static void 665 elide_trailing_slash(const char *src, char *dst, size_t dstsize) 666 { 667 size_t dstlen; 668 669 assert(src); 670 assert(dst); 671 672 (void) strlcpy(dst, src, dstsize); 673 674 dstlen = strlen(dst); 675 if (dst[dstlen - 1] == '/') { 676 dst[dstlen - 1] = '\0'; 677 } 678 } 679 680 static error_t 681 bam_menu(char *subcmd, char *opt, int largc, char *largv[]) 682 { 683 error_t ret; 684 char menu_path[PATH_MAX]; 685 char clean_menu_root[PATH_MAX]; 686 char path[PATH_MAX]; 687 menu_t *menu; 688 char menu_root[PATH_MAX]; 689 struct stat sb; 690 error_t (*f)(menu_t *mp, char *menu_path, char *opt); 691 char *special; 692 char *pool = NULL; 693 zfs_mnted_t zmnted; 694 char *zmntpt; 695 char *osdev; 696 char *osroot; 697 const char *fcn = "bam_menu()"; 698 699 /* 700 * Menu sub-command only applies to GRUB (i.e. x86) 701 */ 702 if (!is_grub(bam_alt_root ? bam_root : "/")) { 703 bam_error(NOT_GRUB_BOOT); 704 return (BAM_ERROR); 705 } 706 707 /* 708 * Check arguments 709 */ 710 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f); 711 if (ret == BAM_ERROR) { 712 return (BAM_ERROR); 713 } 714 715 assert(bam_root); 716 717 (void) strlcpy(menu_root, bam_root, sizeof (menu_root)); 718 osdev = osroot = NULL; 719 720 if (strcmp(subcmd, "update_entry") == 0) { 721 assert(opt); 722 723 osdev = strtok(opt, ","); 724 assert(osdev); 725 osroot = strtok(NULL, ","); 726 if (osroot) { 727 /* fixup bam_root so that it points at osroot */ 728 if (realpath(osroot, rootbuf) == NULL) { 729 bam_error(CANT_RESOLVE, osroot, 730 strerror(errno)); 731 return (BAM_ERROR); 732 } 733 bam_alt_root = 1; 734 bam_root = rootbuf; 735 bam_rootlen = strlen(rootbuf); 736 } 737 } 738 739 /* 740 * We support menu on PCFS (under certain conditions), but 741 * not the OS root 742 */ 743 if (is_pcfs(bam_root)) { 744 bam_error(PCFS_ROOT_NOTSUP, bam_root); 745 return (BAM_ERROR); 746 } 747 748 if (stat(menu_root, &sb) == -1) { 749 bam_error(CANNOT_LOCATE_GRUB_MENU); 750 return (BAM_ERROR); 751 } 752 753 BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root)); 754 755 /* 756 * We no longer use the GRUB slice file. If it exists, then 757 * the user is doing something that is unsupported (such as 758 * standard upgrading an old Live Upgrade BE). If that 759 * happens, mimic existing behavior i.e. pretend that it is 760 * not a BE. Emit a warning though. 761 */ 762 if (bam_alt_root) { 763 (void) snprintf(path, sizeof (path), "%s%s", bam_root, 764 GRUB_slice); 765 } else { 766 (void) snprintf(path, sizeof (path), "%s", GRUB_slice); 767 } 768 769 if (stat(path, &sb) == 0) 770 bam_error(GRUB_SLICE_FILE_EXISTS, path); 771 772 if (is_zfs(menu_root)) { 773 assert(strcmp(menu_root, bam_root) == 0); 774 special = get_special(menu_root); 775 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL); 776 if (special == NULL) { 777 bam_error(CANT_FIND_SPECIAL, menu_root); 778 return (BAM_ERROR); 779 } 780 pool = strtok(special, "/"); 781 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL); 782 if (pool == NULL) { 783 free(special); 784 bam_error(CANT_FIND_POOL, menu_root); 785 return (BAM_ERROR); 786 } 787 BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool)); 788 789 zmntpt = mount_top_dataset(pool, &zmnted); 790 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL); 791 if (zmntpt == NULL) { 792 bam_error(CANT_MOUNT_POOL_DATASET, pool); 793 free(special); 794 return (BAM_ERROR); 795 } 796 BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt)); 797 798 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root)); 799 BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root)); 800 } 801 802 elide_trailing_slash(menu_root, clean_menu_root, 803 sizeof (clean_menu_root)); 804 805 BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root)); 806 807 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path)); 808 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path)); 809 810 BAM_DPRINTF((D_MENU_PATH, fcn, menu_path)); 811 812 /* 813 * If listing the menu, display the menu location 814 */ 815 if (strcmp(subcmd, "list_entry") == 0) { 816 bam_print(GRUB_MENU_PATH, menu_path); 817 } 818 819 820 menu = menu_read(menu_path); 821 assert(menu); 822 823 /* 824 * We already checked the following case in 825 * check_subcmd_and_suboptions() above. Complete the 826 * final step now. 827 */ 828 if (strcmp(subcmd, "set_option") == 0) { 829 assert(largc == 1 && largv[0] && largv[1] == NULL); 830 opt = largv[0]; 831 } else { 832 assert(largc == 0 && largv == NULL); 833 } 834 835 ret = get_boot_cap(bam_root); 836 if (ret != BAM_SUCCESS) { 837 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn)); 838 goto out; 839 } 840 841 /* 842 * Once the sub-cmd handler has run 843 * only the line field is guaranteed to have valid values 844 */ 845 if (strcmp(subcmd, "update_entry") == 0) 846 ret = f(menu, menu_root, osdev); 847 else if (strcmp(subcmd, "upgrade") == 0) 848 ret = f(menu, bam_root, menu_root); 849 else if (strcmp(subcmd, "list_entry") == 0) 850 ret = f(menu, menu_path, opt); 851 else 852 ret = f(menu, NULL, opt); 853 854 if (ret == BAM_WRITE) { 855 BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root)); 856 ret = menu_write(clean_menu_root, menu); 857 } 858 859 out: 860 INJECT_ERROR1("POOL_SET", pool = "/pooldata"); 861 assert((is_zfs(menu_root)) ^ (pool == NULL)); 862 if (pool) { 863 (void) umount_top_dataset(pool, zmnted, zmntpt); 864 free(special); 865 } 866 menu_free(menu); 867 return (ret); 868 } 869 870 871 static error_t 872 bam_archive( 873 char *subcmd, 874 char *opt) 875 { 876 error_t ret; 877 error_t (*f)(char *root, char *opt); 878 const char *fcn = "bam_archive()"; 879 880 /* 881 * Add trailing / for archive subcommands 882 */ 883 if (rootbuf[strlen(rootbuf) - 1] != '/') 884 (void) strcat(rootbuf, "/"); 885 bam_rootlen = strlen(rootbuf); 886 887 /* 888 * Check arguments 889 */ 890 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f); 891 if (ret != BAM_SUCCESS) { 892 return (BAM_ERROR); 893 } 894 895 ret = get_boot_cap(rootbuf); 896 if (ret != BAM_SUCCESS) { 897 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn)); 898 return (ret); 899 } 900 901 /* 902 * Check archive not supported with update_all 903 * since it is awkward to display out-of-sync 904 * information for each BE. 905 */ 906 if (bam_check && strcmp(subcmd, "update_all") == 0) { 907 bam_error(CHECK_NOT_SUPPORTED, subcmd); 908 return (BAM_ERROR); 909 } 910 911 if (strcmp(subcmd, "update_all") == 0) 912 bam_update_all = 1; 913 914 #if !defined(_OPB) 915 ucode_install(bam_root); 916 #endif 917 918 ret = f(bam_root, opt); 919 920 bam_update_all = 0; 921 922 return (ret); 923 } 924 925 /*PRINTFLIKE1*/ 926 void 927 bam_error(char *format, ...) 928 { 929 va_list ap; 930 931 va_start(ap, format); 932 (void) fprintf(stderr, "%s: ", prog); 933 (void) vfprintf(stderr, format, ap); 934 va_end(ap); 935 } 936 937 /*PRINTFLIKE1*/ 938 void 939 bam_derror(char *format, ...) 940 { 941 va_list ap; 942 943 assert(bam_debug); 944 945 va_start(ap, format); 946 (void) fprintf(stderr, "DEBUG: "); 947 (void) vfprintf(stderr, format, ap); 948 va_end(ap); 949 } 950 951 /*PRINTFLIKE1*/ 952 void 953 bam_print(char *format, ...) 954 { 955 va_list ap; 956 957 va_start(ap, format); 958 (void) vfprintf(stdout, format, ap); 959 va_end(ap); 960 } 961 962 /*PRINTFLIKE1*/ 963 void 964 bam_print_stderr(char *format, ...) 965 { 966 va_list ap; 967 968 va_start(ap, format); 969 (void) vfprintf(stderr, format, ap); 970 va_end(ap); 971 } 972 973 static void 974 bam_exit(int excode) 975 { 976 bam_unlock(); 977 exit(excode); 978 } 979 980 static void 981 bam_lock(void) 982 { 983 struct flock lock; 984 pid_t pid; 985 986 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS); 987 if (bam_lock_fd < 0) { 988 /* 989 * We may be invoked early in boot for archive verification. 990 * In this case, root is readonly and /var/run may not exist. 991 * Proceed without the lock 992 */ 993 if (errno == EROFS || errno == ENOENT) { 994 bam_root_readonly = 1; 995 return; 996 } 997 998 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno)); 999 bam_exit(1); 1000 } 1001 1002 lock.l_type = F_WRLCK; 1003 lock.l_whence = SEEK_SET; 1004 lock.l_start = 0; 1005 lock.l_len = 0; 1006 1007 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) { 1008 if (errno != EACCES && errno != EAGAIN) { 1009 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1010 (void) close(bam_lock_fd); 1011 bam_lock_fd = -1; 1012 bam_exit(1); 1013 } 1014 pid = 0; 1015 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0); 1016 bam_print(FILE_LOCKED, pid); 1017 1018 lock.l_type = F_WRLCK; 1019 lock.l_whence = SEEK_SET; 1020 lock.l_start = 0; 1021 lock.l_len = 0; 1022 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) { 1023 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1024 (void) close(bam_lock_fd); 1025 bam_lock_fd = -1; 1026 bam_exit(1); 1027 } 1028 } 1029 1030 /* We own the lock now */ 1031 pid = getpid(); 1032 (void) write(bam_lock_fd, &pid, sizeof (pid)); 1033 } 1034 1035 static void 1036 bam_unlock(void) 1037 { 1038 struct flock unlock; 1039 1040 /* 1041 * NOP if we don't hold the lock 1042 */ 1043 if (bam_lock_fd < 0) { 1044 return; 1045 } 1046 1047 unlock.l_type = F_UNLCK; 1048 unlock.l_whence = SEEK_SET; 1049 unlock.l_start = 0; 1050 unlock.l_len = 0; 1051 1052 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) { 1053 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1054 } 1055 1056 if (close(bam_lock_fd) == -1) { 1057 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno)); 1058 } 1059 bam_lock_fd = -1; 1060 } 1061 1062 static error_t 1063 list_archive(char *root, char *opt) 1064 { 1065 filelist_t flist; 1066 filelist_t *flistp = &flist; 1067 line_t *lp; 1068 1069 assert(root); 1070 assert(opt == NULL); 1071 1072 flistp->head = flistp->tail = NULL; 1073 if (read_list(root, flistp) != BAM_SUCCESS) { 1074 return (BAM_ERROR); 1075 } 1076 assert(flistp->head && flistp->tail); 1077 1078 for (lp = flistp->head; lp; lp = lp->next) { 1079 bam_print(PRINT, lp->line); 1080 } 1081 1082 filelist_free(flistp); 1083 1084 return (BAM_SUCCESS); 1085 } 1086 1087 /* 1088 * This routine writes a list of lines to a file. 1089 * The list is *not* freed 1090 */ 1091 static error_t 1092 list2file(char *root, char *tmp, char *final, line_t *start) 1093 { 1094 char tmpfile[PATH_MAX]; 1095 char path[PATH_MAX]; 1096 FILE *fp; 1097 int ret; 1098 struct stat sb; 1099 mode_t mode; 1100 uid_t root_uid; 1101 gid_t sys_gid; 1102 struct passwd *pw; 1103 struct group *gp; 1104 const char *fcn = "list2file()"; 1105 1106 (void) snprintf(path, sizeof (path), "%s%s", root, final); 1107 1108 if (start == NULL) { 1109 /* Empty GRUB menu */ 1110 if (stat(path, &sb) != -1) { 1111 bam_print(UNLINK_EMPTY, path); 1112 if (unlink(path) != 0) { 1113 bam_error(UNLINK_FAIL, path, strerror(errno)); 1114 return (BAM_ERROR); 1115 } else { 1116 return (BAM_SUCCESS); 1117 } 1118 } 1119 return (BAM_SUCCESS); 1120 } 1121 1122 /* 1123 * Preserve attributes of existing file if possible, 1124 * otherwise ask the system for uid/gid of root/sys. 1125 * If all fails, fall back on hard-coded defaults. 1126 */ 1127 if (stat(path, &sb) != -1) { 1128 mode = sb.st_mode; 1129 root_uid = sb.st_uid; 1130 sys_gid = sb.st_gid; 1131 } else { 1132 mode = DEFAULT_DEV_MODE; 1133 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) { 1134 root_uid = pw->pw_uid; 1135 } else { 1136 bam_error(CANT_FIND_USER, 1137 DEFAULT_DEV_USER, DEFAULT_DEV_UID); 1138 root_uid = (uid_t)DEFAULT_DEV_UID; 1139 } 1140 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) { 1141 sys_gid = gp->gr_gid; 1142 } else { 1143 bam_error(CANT_FIND_GROUP, 1144 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID); 1145 sys_gid = (gid_t)DEFAULT_DEV_GID; 1146 } 1147 } 1148 1149 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp); 1150 1151 /* Truncate tmpfile first */ 1152 fp = fopen(tmpfile, "w"); 1153 if (fp == NULL) { 1154 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1155 return (BAM_ERROR); 1156 } 1157 if (fclose(fp) == EOF) { 1158 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1159 return (BAM_ERROR); 1160 } 1161 1162 /* Now open it in append mode */ 1163 fp = fopen(tmpfile, "a"); 1164 if (fp == NULL) { 1165 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1166 return (BAM_ERROR); 1167 } 1168 1169 for (; start; start = start->next) { 1170 if (s_fputs(start->line, fp) == EOF) { 1171 bam_error(WRITE_FAIL, tmpfile, strerror(errno)); 1172 (void) fclose(fp); 1173 return (BAM_ERROR); 1174 } 1175 } 1176 1177 if (fclose(fp) == EOF) { 1178 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1179 return (BAM_ERROR); 1180 } 1181 1182 /* 1183 * Set up desired attributes. Ignore failures on filesystems 1184 * not supporting these operations - pcfs reports unsupported 1185 * operations as EINVAL. 1186 */ 1187 ret = chmod(tmpfile, mode); 1188 if (ret == -1 && 1189 errno != EINVAL && errno != ENOTSUP) { 1190 bam_error(CHMOD_FAIL, tmpfile, strerror(errno)); 1191 return (BAM_ERROR); 1192 } 1193 1194 ret = chown(tmpfile, root_uid, sys_gid); 1195 if (ret == -1 && 1196 errno != EINVAL && errno != ENOTSUP) { 1197 bam_error(CHOWN_FAIL, tmpfile, strerror(errno)); 1198 return (BAM_ERROR); 1199 } 1200 1201 1202 /* 1203 * Do an atomic rename 1204 */ 1205 ret = rename(tmpfile, path); 1206 if (ret != 0) { 1207 bam_error(RENAME_FAIL, path, strerror(errno)); 1208 return (BAM_ERROR); 1209 } 1210 1211 BAM_DPRINTF((D_WROTE_FILE, fcn, path)); 1212 return (BAM_SUCCESS); 1213 } 1214 1215 /* 1216 * This function should always return 0 - since we want 1217 * to create stat data for *all* files in the list. 1218 */ 1219 /*ARGSUSED*/ 1220 static int 1221 cmpstat( 1222 const char *file, 1223 const struct stat *stat, 1224 int flags, 1225 struct FTW *ftw) 1226 { 1227 uint_t sz; 1228 uint64_t *value; 1229 uint64_t filestat[2]; 1230 int error; 1231 1232 struct safefile *safefilep; 1233 FILE *fp; 1234 1235 /* 1236 * We only want regular files 1237 */ 1238 if (!S_ISREG(stat->st_mode)) 1239 return (0); 1240 1241 /* 1242 * new_nvlp may be NULL if there were errors earlier 1243 * but this is not fatal to update determination. 1244 */ 1245 if (walk_arg.new_nvlp) { 1246 filestat[0] = stat->st_size; 1247 filestat[1] = stat->st_mtime; 1248 error = nvlist_add_uint64_array(walk_arg.new_nvlp, 1249 file + bam_rootlen, filestat, 2); 1250 if (error) 1251 bam_error(NVADD_FAIL, file, strerror(error)); 1252 } 1253 1254 /* 1255 * The remaining steps are only required if we haven't made a 1256 * decision about update or if we are checking (-n) 1257 */ 1258 if (walk_arg.need_update && !bam_check) 1259 return (0); 1260 1261 /* 1262 * If we are invoked as part of system/filesystem/boot-archive, then 1263 * there are a number of things we should not worry about 1264 */ 1265 if (bam_smf_check) { 1266 /* ignore amd64 modules unless we are booted amd64. */ 1267 if (!is_amd64() && strstr(file, "/amd64/") != 0) 1268 return (0); 1269 1270 /* read in list of safe files */ 1271 if (safefiles == NULL) 1272 if (fp = fopen("/boot/solaris/filelist.safe", "r")) { 1273 safefiles = s_calloc(1, 1274 sizeof (struct safefile)); 1275 safefilep = safefiles; 1276 safefilep->name = s_calloc(1, MAXPATHLEN + 1277 MAXNAMELEN); 1278 safefilep->next = NULL; 1279 while (s_fgets(safefilep->name, MAXPATHLEN + 1280 MAXNAMELEN, fp) != NULL) { 1281 safefilep->next = s_calloc(1, 1282 sizeof (struct safefile)); 1283 safefilep = safefilep->next; 1284 safefilep->name = s_calloc(1, 1285 MAXPATHLEN + MAXNAMELEN); 1286 safefilep->next = NULL; 1287 } 1288 (void) fclose(fp); 1289 } 1290 } 1291 1292 /* 1293 * We need an update if file doesn't exist in old archive 1294 */ 1295 if (walk_arg.old_nvlp == NULL || 1296 nvlist_lookup_uint64_array(walk_arg.old_nvlp, 1297 file + bam_rootlen, &value, &sz) != 0) { 1298 if (bam_smf_check) /* ignore new during smf check */ 1299 return (0); 1300 walk_arg.need_update = 1; 1301 if (bam_verbose) 1302 bam_print(PARSEABLE_NEW_FILE, file); 1303 return (0); 1304 } 1305 1306 /* 1307 * File exists in old archive. Check if file has changed 1308 */ 1309 assert(sz == 2); 1310 bcopy(value, filestat, sizeof (filestat)); 1311 1312 if (filestat[0] != stat->st_size || 1313 filestat[1] != stat->st_mtime) { 1314 if (bam_smf_check) { 1315 safefilep = safefiles; 1316 while (safefilep != NULL) { 1317 if (strcmp(file + bam_rootlen, 1318 safefilep->name) == 0) { 1319 (void) creat(NEED_UPDATE_FILE, 0644); 1320 return (0); 1321 } 1322 safefilep = safefilep->next; 1323 } 1324 } 1325 walk_arg.need_update = 1; 1326 if (bam_verbose) 1327 if (bam_smf_check) 1328 bam_print(" %s\n", file); 1329 else 1330 bam_print(PARSEABLE_OUT_DATE, file); 1331 } 1332 1333 return (0); 1334 } 1335 1336 /* 1337 * Check flags and presence of required files. 1338 * The force flag and/or absence of files should 1339 * trigger an update. 1340 * Suppress stdout output if check (-n) option is set 1341 * (as -n should only produce parseable output.) 1342 */ 1343 static void 1344 check_flags_and_files(char *root) 1345 { 1346 char path[PATH_MAX]; 1347 struct stat sb; 1348 1349 /* 1350 * if force, create archive unconditionally 1351 */ 1352 if (bam_force) { 1353 walk_arg.need_update = 1; 1354 if (bam_verbose && !bam_check) 1355 bam_print(UPDATE_FORCE); 1356 return; 1357 } 1358 1359 /* 1360 * If archive is missing, create archive 1361 */ 1362 if (is_sun4u()) { 1363 (void) snprintf(path, sizeof (path), "%s%s", root, 1364 SUN4U_ARCHIVE); 1365 } else if (is_sun4v()) { 1366 (void) snprintf(path, sizeof (path), "%s%s", root, 1367 SUN4V_ARCHIVE); 1368 } else { 1369 if (bam_direct == BAM_DIRECT_DBOOT) { 1370 (void) snprintf(path, sizeof (path), "%s%s", root, 1371 DIRECT_BOOT_ARCHIVE_64); 1372 if (stat(path, &sb) != 0) { 1373 if (bam_verbose && !bam_check) 1374 bam_print(UPDATE_ARCH_MISS, path); 1375 walk_arg.need_update = 1; 1376 return; 1377 } 1378 } 1379 (void) snprintf(path, sizeof (path), "%s%s", root, 1380 DIRECT_BOOT_ARCHIVE_32); 1381 } 1382 1383 if (stat(path, &sb) != 0) { 1384 if (bam_verbose && !bam_check) 1385 bam_print(UPDATE_ARCH_MISS, path); 1386 walk_arg.need_update = 1; 1387 return; 1388 } 1389 } 1390 1391 static error_t 1392 read_one_list(char *root, filelist_t *flistp, char *filelist) 1393 { 1394 char path[PATH_MAX]; 1395 FILE *fp; 1396 char buf[BAM_MAXLINE]; 1397 const char *fcn = "read_one_list()"; 1398 1399 (void) snprintf(path, sizeof (path), "%s%s", root, filelist); 1400 1401 fp = fopen(path, "r"); 1402 if (fp == NULL) { 1403 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno))); 1404 return (BAM_ERROR); 1405 } 1406 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 1407 /* skip blank lines */ 1408 if (strspn(buf, " \t") == strlen(buf)) 1409 continue; 1410 append_to_flist(flistp, buf); 1411 } 1412 if (fclose(fp) != 0) { 1413 bam_error(CLOSE_FAIL, path, strerror(errno)); 1414 return (BAM_ERROR); 1415 } 1416 return (BAM_SUCCESS); 1417 } 1418 1419 static error_t 1420 read_list(char *root, filelist_t *flistp) 1421 { 1422 char path[PATH_MAX]; 1423 char cmd[PATH_MAX]; 1424 struct stat sb; 1425 int n, rval; 1426 const char *fcn = "read_list()"; 1427 1428 flistp->head = flistp->tail = NULL; 1429 1430 /* 1431 * build and check path to extract_boot_filelist.ksh 1432 */ 1433 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST); 1434 if (n >= sizeof (path)) { 1435 bam_error(NO_FLIST); 1436 return (BAM_ERROR); 1437 } 1438 1439 /* 1440 * If extract_boot_filelist is present, exec it, otherwise read 1441 * the filelists directly, for compatibility with older images. 1442 */ 1443 if (stat(path, &sb) == 0) { 1444 /* 1445 * build arguments to exec extract_boot_filelist.ksh 1446 */ 1447 char *rootarg, *platarg; 1448 int platarglen = 1, rootarglen = 1; 1449 if (strlen(root) > 1) 1450 rootarglen += strlen(root) + strlen("-R "); 1451 if (bam_alt_platform) 1452 platarglen += strlen(bam_platform) + strlen("-p "); 1453 platarg = s_calloc(1, platarglen); 1454 rootarg = s_calloc(1, rootarglen); 1455 *platarg = 0; 1456 *rootarg = 0; 1457 1458 if (strlen(root) > 1) { 1459 (void) snprintf(rootarg, rootarglen, 1460 "-R %s", root); 1461 } 1462 if (bam_alt_platform) { 1463 (void) snprintf(platarg, platarglen, 1464 "-p %s", bam_platform); 1465 } 1466 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s", 1467 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST); 1468 free(platarg); 1469 free(rootarg); 1470 if (n >= sizeof (cmd)) { 1471 bam_error(NO_FLIST); 1472 return (BAM_ERROR); 1473 } 1474 if (exec_cmd(cmd, flistp) != 0) { 1475 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno))); 1476 return (BAM_ERROR); 1477 } 1478 } else { 1479 /* 1480 * Read current lists of files - only the first is mandatory 1481 */ 1482 rval = read_one_list(root, flistp, BOOT_FILE_LIST); 1483 if (rval != BAM_SUCCESS) 1484 return (rval); 1485 (void) read_one_list(root, flistp, ETC_FILE_LIST); 1486 } 1487 1488 if (flistp->head == NULL) { 1489 bam_error(NO_FLIST); 1490 return (BAM_ERROR); 1491 } 1492 1493 return (BAM_SUCCESS); 1494 } 1495 1496 static void 1497 getoldstat(char *root) 1498 { 1499 char path[PATH_MAX]; 1500 int fd, error; 1501 struct stat sb; 1502 char *ostat; 1503 1504 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 1505 fd = open(path, O_RDONLY); 1506 if (fd == -1) { 1507 if (bam_verbose) 1508 bam_print(OPEN_FAIL, path, strerror(errno)); 1509 walk_arg.need_update = 1; 1510 return; 1511 } 1512 1513 if (fstat(fd, &sb) != 0) { 1514 bam_error(STAT_FAIL, path, strerror(errno)); 1515 (void) close(fd); 1516 walk_arg.need_update = 1; 1517 return; 1518 } 1519 1520 ostat = s_calloc(1, sb.st_size); 1521 1522 if (read(fd, ostat, sb.st_size) != sb.st_size) { 1523 bam_error(READ_FAIL, path, strerror(errno)); 1524 (void) close(fd); 1525 free(ostat); 1526 walk_arg.need_update = 1; 1527 return; 1528 } 1529 1530 (void) close(fd); 1531 1532 walk_arg.old_nvlp = NULL; 1533 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0); 1534 1535 free(ostat); 1536 1537 if (error) { 1538 bam_error(UNPACK_FAIL, path, strerror(error)); 1539 walk_arg.old_nvlp = NULL; 1540 walk_arg.need_update = 1; 1541 return; 1542 } 1543 } 1544 1545 /* 1546 * Checks if a file in the current (old) archive has 1547 * been deleted from the root filesystem. This is needed for 1548 * software like Trusted Extensions (TX) that switch early 1549 * in boot based on presence/absence of a kernel module. 1550 */ 1551 static void 1552 check4stale(char *root) 1553 { 1554 nvpair_t *nvp; 1555 nvlist_t *nvlp; 1556 char *file; 1557 char path[PATH_MAX]; 1558 struct stat sb; 1559 1560 /* 1561 * Skip stale file check during smf check 1562 */ 1563 if (bam_smf_check) 1564 return; 1565 1566 /* Nothing to do if no old stats */ 1567 if ((nvlp = walk_arg.old_nvlp) == NULL) 1568 return; 1569 1570 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp; 1571 nvp = nvlist_next_nvpair(nvlp, nvp)) { 1572 file = nvpair_name(nvp); 1573 if (file == NULL) 1574 continue; 1575 (void) snprintf(path, sizeof (path), "%s/%s", 1576 root, file); 1577 if (stat(path, &sb) == -1) { 1578 walk_arg.need_update = 1; 1579 if (bam_verbose) 1580 bam_print(PARSEABLE_STALE_FILE, path); 1581 } 1582 } 1583 } 1584 1585 static void 1586 create_newstat(void) 1587 { 1588 int error; 1589 1590 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0); 1591 if (error) { 1592 /* 1593 * Not fatal - we can still create archive 1594 */ 1595 walk_arg.new_nvlp = NULL; 1596 bam_error(NVALLOC_FAIL, strerror(error)); 1597 } 1598 } 1599 1600 static void 1601 walk_list(char *root, filelist_t *flistp) 1602 { 1603 char path[PATH_MAX]; 1604 line_t *lp; 1605 1606 for (lp = flistp->head; lp; lp = lp->next) { 1607 /* 1608 * Don't follow symlinks. A symlink must refer to 1609 * a file that would appear in the archive through 1610 * a direct reference. This matches the archive 1611 * construction behavior. 1612 */ 1613 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line); 1614 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) { 1615 /* 1616 * Some files may not exist. 1617 * For example: etc/rtc_config on a x86 diskless system 1618 * Emit verbose message only 1619 */ 1620 if (bam_verbose) 1621 bam_print(NFTW_FAIL, path, strerror(errno)); 1622 } 1623 } 1624 } 1625 1626 static void 1627 savenew(char *root) 1628 { 1629 char path[PATH_MAX]; 1630 char path2[PATH_MAX]; 1631 size_t sz; 1632 char *nstat; 1633 int fd, wrote, error; 1634 1635 nstat = NULL; 1636 sz = 0; 1637 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz, 1638 NV_ENCODE_XDR, 0); 1639 if (error) { 1640 bam_error(PACK_FAIL, strerror(error)); 1641 return; 1642 } 1643 1644 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP); 1645 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE); 1646 if (fd == -1) { 1647 bam_error(OPEN_FAIL, path, strerror(errno)); 1648 free(nstat); 1649 return; 1650 } 1651 wrote = write(fd, nstat, sz); 1652 if (wrote != sz) { 1653 bam_error(WRITE_FAIL, path, strerror(errno)); 1654 (void) close(fd); 1655 free(nstat); 1656 return; 1657 } 1658 (void) close(fd); 1659 free(nstat); 1660 1661 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT); 1662 if (rename(path, path2) != 0) { 1663 bam_error(RENAME_FAIL, path2, strerror(errno)); 1664 } 1665 } 1666 1667 static void 1668 clear_walk_args(void) 1669 { 1670 if (walk_arg.old_nvlp) 1671 nvlist_free(walk_arg.old_nvlp); 1672 if (walk_arg.new_nvlp) 1673 nvlist_free(walk_arg.new_nvlp); 1674 walk_arg.need_update = 0; 1675 walk_arg.old_nvlp = NULL; 1676 walk_arg.new_nvlp = NULL; 1677 } 1678 1679 /* 1680 * Returns: 1681 * 0 - no update necessary 1682 * 1 - update required. 1683 * BAM_ERROR (-1) - An error occurred 1684 * 1685 * Special handling for check (-n): 1686 * ================================ 1687 * The check (-n) option produces parseable output. 1688 * To do this, we suppress all stdout messages unrelated 1689 * to out of sync files. 1690 * All stderr messages are still printed though. 1691 * 1692 */ 1693 static int 1694 update_required(char *root) 1695 { 1696 struct stat sb; 1697 char path[PATH_MAX]; 1698 filelist_t flist; 1699 filelist_t *flistp = &flist; 1700 int need_update; 1701 1702 flistp->head = flistp->tail = NULL; 1703 1704 walk_arg.need_update = 0; 1705 1706 /* 1707 * Without consulting stat data, check if we need update 1708 */ 1709 check_flags_and_files(root); 1710 1711 /* 1712 * In certain deployment scenarios, filestat may not 1713 * exist. Ignore it during boot-archive SMF check. 1714 */ 1715 if (bam_smf_check) { 1716 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 1717 if (stat(path, &sb) != 0) 1718 return (0); 1719 } 1720 1721 /* 1722 * consult stat data only if we haven't made a decision 1723 * about update. If checking (-n) however, we always 1724 * need stat data (since we want to compare old and new) 1725 */ 1726 if (!walk_arg.need_update || bam_check) 1727 getoldstat(root); 1728 1729 /* 1730 * Check if the archive contains files that are no longer 1731 * present on the root filesystem. 1732 */ 1733 if (!walk_arg.need_update || bam_check) 1734 check4stale(root); 1735 1736 /* 1737 * read list of files 1738 */ 1739 if (read_list(root, flistp) != BAM_SUCCESS) { 1740 clear_walk_args(); 1741 return (BAM_ERROR); 1742 } 1743 1744 assert(flistp->head && flistp->tail); 1745 1746 /* 1747 * At this point either the update is required 1748 * or the decision is pending. In either case 1749 * we need to create new stat nvlist 1750 */ 1751 create_newstat(); 1752 1753 /* 1754 * This walk does 2 things: 1755 * - gets new stat data for every file 1756 * - (optional) compare old and new stat data 1757 */ 1758 walk_list(root, &flist); 1759 1760 /* done with the file list */ 1761 filelist_free(flistp); 1762 1763 /* 1764 * if we didn't succeed in creating new stat data above 1765 * just return result of update check so that archive is built. 1766 */ 1767 if (walk_arg.new_nvlp == NULL) { 1768 bam_error(NO_NEW_STAT); 1769 need_update = walk_arg.need_update; 1770 clear_walk_args(); 1771 return (need_update ? 1 : 0); 1772 } 1773 1774 1775 /* 1776 * If no update required, discard newstat 1777 */ 1778 if (!walk_arg.need_update) { 1779 clear_walk_args(); 1780 return (0); 1781 } 1782 1783 return (1); 1784 } 1785 1786 static error_t 1787 create_ramdisk(char *root) 1788 { 1789 char *cmdline, path[PATH_MAX]; 1790 size_t len; 1791 struct stat sb; 1792 1793 /* 1794 * Setup command args for create_ramdisk.ksh 1795 */ 1796 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 1797 if (stat(path, &sb) != 0) { 1798 bam_error(ARCH_EXEC_MISS, path, strerror(errno)); 1799 return (BAM_ERROR); 1800 } 1801 1802 len = strlen(path) + strlen(root) + 10; /* room for space + -R */ 1803 if (bam_alt_platform) 1804 len += strlen(bam_platform) + strlen("-p "); 1805 cmdline = s_calloc(1, len); 1806 1807 if (bam_alt_platform) { 1808 assert(strlen(root) > 1); 1809 (void) snprintf(cmdline, len, "%s -p %s -R %s", 1810 path, bam_platform, root); 1811 /* chop off / at the end */ 1812 cmdline[strlen(cmdline) - 1] = '\0'; 1813 } else if (strlen(root) > 1) { 1814 (void) snprintf(cmdline, len, "%s -R %s", path, root); 1815 /* chop off / at the end */ 1816 cmdline[strlen(cmdline) - 1] = '\0'; 1817 } else 1818 (void) snprintf(cmdline, len, "%s", path); 1819 1820 if (exec_cmd(cmdline, NULL) != 0) { 1821 bam_error(ARCHIVE_FAIL, cmdline); 1822 free(cmdline); 1823 return (BAM_ERROR); 1824 } 1825 free(cmdline); 1826 1827 /* 1828 * The existence of the expected archives used to be 1829 * verified here. This check is done in create_ramdisk as 1830 * it needs to be in sync with the altroot operated upon. 1831 */ 1832 1833 return (BAM_SUCCESS); 1834 } 1835 1836 /* 1837 * Checks if target filesystem is on a ramdisk 1838 * 1 - is miniroot 1839 * 0 - is not 1840 * When in doubt assume it is not a ramdisk. 1841 */ 1842 static int 1843 is_ramdisk(char *root) 1844 { 1845 struct extmnttab mnt; 1846 FILE *fp; 1847 int found; 1848 char mntpt[PATH_MAX]; 1849 char *cp; 1850 1851 /* 1852 * There are 3 situations where creating archive is 1853 * of dubious value: 1854 * - create boot_archive on a lofi-mounted boot_archive 1855 * - create it on a ramdisk which is the root filesystem 1856 * - create it on a ramdisk mounted somewhere else 1857 * The first is not easy to detect and checking for it is not 1858 * worth it. 1859 * The other two conditions are handled here 1860 */ 1861 1862 fp = fopen(MNTTAB, "r"); 1863 if (fp == NULL) { 1864 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 1865 return (0); 1866 } 1867 1868 resetmnttab(fp); 1869 1870 /* 1871 * Remove any trailing / from the mount point 1872 */ 1873 (void) strlcpy(mntpt, root, sizeof (mntpt)); 1874 if (strcmp(root, "/") != 0) { 1875 cp = mntpt + strlen(mntpt) - 1; 1876 if (*cp == '/') 1877 *cp = '\0'; 1878 } 1879 found = 0; 1880 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 1881 if (strcmp(mnt.mnt_mountp, mntpt) == 0) { 1882 found = 1; 1883 break; 1884 } 1885 } 1886 1887 if (!found) { 1888 if (bam_verbose) 1889 bam_error(NOT_IN_MNTTAB, mntpt); 1890 (void) fclose(fp); 1891 return (0); 1892 } 1893 1894 if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) { 1895 if (bam_verbose) 1896 bam_error(IS_RAMDISK, bam_root); 1897 (void) fclose(fp); 1898 return (1); 1899 } 1900 1901 (void) fclose(fp); 1902 1903 return (0); 1904 } 1905 1906 static int 1907 is_boot_archive(char *root) 1908 { 1909 char path[PATH_MAX]; 1910 struct stat sb; 1911 int error; 1912 const char *fcn = "is_boot_archive()"; 1913 1914 /* 1915 * We can't create an archive without the create_ramdisk script 1916 */ 1917 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 1918 error = stat(path, &sb); 1919 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1); 1920 if (error == -1) { 1921 if (bam_verbose) 1922 bam_print(FILE_MISS, path); 1923 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root)); 1924 return (0); 1925 } 1926 1927 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root)); 1928 return (1); 1929 } 1930 1931 /* 1932 * Need to call this for anything that operates on the GRUB menu 1933 */ 1934 int 1935 is_grub(const char *root) 1936 { 1937 char path[PATH_MAX]; 1938 struct stat sb; 1939 const char *fcn = "is_grub()"; 1940 1941 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR); 1942 if (stat(path, &sb) == -1) { 1943 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path)); 1944 return (0); 1945 } 1946 1947 return (1); 1948 } 1949 1950 static int 1951 is_zfs(char *root) 1952 { 1953 struct statvfs vfs; 1954 int ret; 1955 const char *fcn = "is_zfs()"; 1956 1957 ret = statvfs(root, &vfs); 1958 INJECT_ERROR1("STATVFS_ZFS", ret = 1); 1959 if (ret != 0) { 1960 bam_error(STATVFS_FAIL, root, strerror(errno)); 1961 return (0); 1962 } 1963 1964 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) { 1965 BAM_DPRINTF((D_IS_ZFS, fcn, root)); 1966 return (1); 1967 } else { 1968 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root)); 1969 return (0); 1970 } 1971 } 1972 1973 static int 1974 is_ufs(char *root) 1975 { 1976 struct statvfs vfs; 1977 int ret; 1978 const char *fcn = "is_ufs()"; 1979 1980 ret = statvfs(root, &vfs); 1981 INJECT_ERROR1("STATVFS_UFS", ret = 1); 1982 if (ret != 0) { 1983 bam_error(STATVFS_FAIL, root, strerror(errno)); 1984 return (0); 1985 } 1986 1987 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) { 1988 BAM_DPRINTF((D_IS_UFS, fcn, root)); 1989 return (1); 1990 } else { 1991 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root)); 1992 return (0); 1993 } 1994 } 1995 1996 static int 1997 is_pcfs(char *root) 1998 { 1999 struct statvfs vfs; 2000 int ret; 2001 const char *fcn = "is_pcfs()"; 2002 2003 ret = statvfs(root, &vfs); 2004 INJECT_ERROR1("STATVFS_PCFS", ret = 1); 2005 if (ret != 0) { 2006 bam_error(STATVFS_FAIL, root, strerror(errno)); 2007 return (0); 2008 } 2009 2010 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) { 2011 BAM_DPRINTF((D_IS_PCFS, fcn, root)); 2012 return (1); 2013 } else { 2014 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root)); 2015 return (0); 2016 } 2017 } 2018 2019 static int 2020 is_readonly(char *root) 2021 { 2022 int fd; 2023 int error; 2024 char testfile[PATH_MAX]; 2025 const char *fcn = "is_readonly()"; 2026 2027 /* 2028 * Using statvfs() to check for a read-only filesystem is not 2029 * reliable. The only way to reliably test is to attempt to 2030 * create a file 2031 */ 2032 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d", 2033 root, BOOTADM_RDONLY_TEST, getpid()); 2034 2035 (void) unlink(testfile); 2036 2037 errno = 0; 2038 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644); 2039 error = errno; 2040 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES); 2041 if (fd == -1 && error == EROFS) { 2042 BAM_DPRINTF((D_RDONLY_FS, fcn, root)); 2043 return (1); 2044 } else if (fd == -1) { 2045 bam_error(RDONLY_TEST_ERROR, root, strerror(error)); 2046 } 2047 2048 (void) close(fd); 2049 (void) unlink(testfile); 2050 2051 BAM_DPRINTF((D_RDWR_FS, fcn, root)); 2052 return (0); 2053 } 2054 2055 static error_t 2056 update_archive(char *root, char *opt) 2057 { 2058 error_t ret; 2059 2060 assert(root); 2061 assert(opt == NULL); 2062 2063 /* 2064 * root must belong to a boot archive based OS, 2065 */ 2066 if (!is_boot_archive(root)) { 2067 /* 2068 * Emit message only if not in context of update_all. 2069 * If in update_all, emit only if verbose flag is set. 2070 */ 2071 if (!bam_update_all || bam_verbose) 2072 bam_print(NOT_ARCHIVE_BOOT, root); 2073 return (BAM_SUCCESS); 2074 } 2075 2076 /* 2077 * If smf check is requested when / is writable (can happen 2078 * on first reboot following an upgrade because service 2079 * dependency is messed up), skip the check. 2080 */ 2081 if (bam_smf_check && !bam_root_readonly) 2082 return (BAM_SUCCESS); 2083 2084 /* 2085 * root must be writable. This check applies to alternate 2086 * root (-R option); bam_root_readonly applies to '/' only. 2087 */ 2088 if (!bam_smf_check && !bam_check && is_readonly(root)) { 2089 if (bam_verbose) 2090 bam_print(RDONLY_FS, root); 2091 return (BAM_SUCCESS); 2092 } 2093 2094 /* 2095 * Don't generate archive on ramdisk 2096 */ 2097 if (is_ramdisk(root)) { 2098 if (bam_verbose) 2099 bam_print(SKIP_RAMDISK); 2100 return (BAM_SUCCESS); 2101 } 2102 2103 /* 2104 * Now check if updated is really needed 2105 */ 2106 ret = update_required(root); 2107 2108 /* 2109 * The check command (-n) is *not* a dry run 2110 * It only checks if the archive is in sync. 2111 */ 2112 if (bam_check) { 2113 bam_exit((ret != 0) ? 1 : 0); 2114 } 2115 2116 if (ret == 1) { 2117 /* create the ramdisk */ 2118 ret = create_ramdisk(root); 2119 } 2120 2121 /* if the archive is updated, save the new stat data */ 2122 if (ret == 0 && walk_arg.new_nvlp != NULL) { 2123 savenew(root); 2124 } 2125 2126 clear_walk_args(); 2127 2128 return (ret); 2129 } 2130 2131 static void 2132 update_fdisk(void) 2133 { 2134 struct stat sb; 2135 char cmd[PATH_MAX]; 2136 int ret1, ret2; 2137 2138 assert(stat(GRUB_fdisk, &sb) == 0); 2139 assert(stat(GRUB_fdisk_target, &sb) == 0); 2140 2141 (void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`", 2142 GRUB_fdisk, GRUB_fdisk_target); 2143 2144 bam_print(UPDATING_FDISK); 2145 if (exec_cmd(cmd, NULL) != 0) { 2146 bam_error(FDISK_UPDATE_FAILED); 2147 } 2148 2149 /* 2150 * We are done, remove the files. 2151 */ 2152 ret1 = unlink(GRUB_fdisk); 2153 ret2 = unlink(GRUB_fdisk_target); 2154 if (ret1 != 0 || ret2 != 0) { 2155 bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target); 2156 } 2157 } 2158 2159 static error_t 2160 update_all(char *root, char *opt) 2161 { 2162 struct extmnttab mnt; 2163 struct stat sb; 2164 FILE *fp; 2165 char multibt[PATH_MAX]; 2166 char creatram[PATH_MAX]; 2167 error_t ret = BAM_SUCCESS; 2168 int ret1, ret2; 2169 2170 assert(root); 2171 assert(opt == NULL); 2172 2173 if (bam_rootlen != 1 || *root != '/') { 2174 elide_trailing_slash(root, multibt, sizeof (multibt)); 2175 bam_error(ALT_ROOT_INVALID, multibt); 2176 return (BAM_ERROR); 2177 } 2178 2179 /* 2180 * Check to see if we are in the midst of safemode patching 2181 * If so skip building the archive for /. Instead build it 2182 * against the latest bits obtained by creating a fresh lofs 2183 * mount of root. 2184 */ 2185 if (stat(LOFS_PATCH_FILE, &sb) == 0) { 2186 if (mkdir(LOFS_PATCH_MNT, 0755) == -1 && 2187 errno != EEXIST) { 2188 bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT, 2189 strerror(errno)); 2190 ret = BAM_ERROR; 2191 goto out; 2192 } 2193 (void) snprintf(multibt, sizeof (multibt), 2194 "/sbin/mount -F lofs -o nosub / %s", LOFS_PATCH_MNT); 2195 if (exec_cmd(multibt, NULL) != 0) { 2196 bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs"); 2197 ret = BAM_ERROR; 2198 } 2199 if (ret != BAM_ERROR) { 2200 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 2201 LOFS_PATCH_MNT); 2202 bam_rootlen = strlen(rootbuf); 2203 if (update_archive(rootbuf, opt) != BAM_SUCCESS) 2204 ret = BAM_ERROR; 2205 /* 2206 * unmount the lofs mount since there could be 2207 * multiple invocations of bootadm -a update_all 2208 */ 2209 (void) snprintf(multibt, sizeof (multibt), 2210 "/sbin/umount %s", LOFS_PATCH_MNT); 2211 if (exec_cmd(multibt, NULL) != 0) { 2212 bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT); 2213 ret = BAM_ERROR; 2214 } 2215 } 2216 } else { 2217 /* 2218 * First update archive for current root 2219 */ 2220 if (update_archive(root, opt) != BAM_SUCCESS) 2221 ret = BAM_ERROR; 2222 } 2223 2224 if (ret == BAM_ERROR) 2225 goto out; 2226 2227 /* 2228 * Now walk the mount table, performing archive update 2229 * for all mounted Newboot root filesystems 2230 */ 2231 fp = fopen(MNTTAB, "r"); 2232 if (fp == NULL) { 2233 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 2234 ret = BAM_ERROR; 2235 goto out; 2236 } 2237 2238 resetmnttab(fp); 2239 2240 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 2241 if (mnt.mnt_special == NULL) 2242 continue; 2243 if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0) 2244 continue; 2245 if (strcmp(mnt.mnt_mountp, "/") == 0) 2246 continue; 2247 2248 (void) snprintf(creatram, sizeof (creatram), "%s/%s", 2249 mnt.mnt_mountp, CREATE_RAMDISK); 2250 2251 if (stat(creatram, &sb) == -1) 2252 continue; 2253 2254 /* 2255 * We put a trailing slash to be consistent with root = "/" 2256 * case, such that we don't have to print // in some cases. 2257 */ 2258 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 2259 mnt.mnt_mountp); 2260 bam_rootlen = strlen(rootbuf); 2261 2262 /* 2263 * It's possible that other mounts may be an alternate boot 2264 * architecture, so check it again. 2265 */ 2266 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) || 2267 (update_archive(rootbuf, opt) != BAM_SUCCESS)) 2268 ret = BAM_ERROR; 2269 } 2270 2271 (void) fclose(fp); 2272 2273 out: 2274 /* 2275 * Update fdisk table as we go down. Updating it when 2276 * the system is running will confuse biosdev. 2277 */ 2278 ret1 = stat(GRUB_fdisk, &sb); 2279 ret2 = stat(GRUB_fdisk_target, &sb); 2280 if ((ret1 == 0) && (ret2 == 0)) { 2281 update_fdisk(); 2282 } else if ((ret1 == 0) ^ (ret2 == 0)) { 2283 /* 2284 * It is an error for one file to be 2285 * present and the other absent. 2286 * It is normal for both files to be 2287 * absent - it indicates that no fdisk 2288 * update is required. 2289 */ 2290 bam_error(MISSING_FDISK_FILE, 2291 ret1 ? GRUB_fdisk : GRUB_fdisk_target); 2292 ret = BAM_ERROR; 2293 } 2294 2295 return (ret); 2296 } 2297 2298 static void 2299 append_line(menu_t *mp, line_t *lp) 2300 { 2301 if (mp->start == NULL) { 2302 mp->start = lp; 2303 } else { 2304 mp->end->next = lp; 2305 lp->prev = mp->end; 2306 } 2307 mp->end = lp; 2308 } 2309 2310 void 2311 unlink_line(menu_t *mp, line_t *lp) 2312 { 2313 /* unlink from list */ 2314 if (lp->prev) 2315 lp->prev->next = lp->next; 2316 else 2317 mp->start = lp->next; 2318 if (lp->next) 2319 lp->next->prev = lp->prev; 2320 else 2321 mp->end = lp->prev; 2322 } 2323 2324 static entry_t * 2325 boot_entry_new(menu_t *mp, line_t *start, line_t *end) 2326 { 2327 entry_t *ent, *prev; 2328 const char *fcn = "boot_entry_new()"; 2329 2330 assert(mp); 2331 assert(start); 2332 assert(end); 2333 2334 ent = s_calloc(1, sizeof (entry_t)); 2335 BAM_DPRINTF((D_ENTRY_NEW, fcn)); 2336 ent->start = start; 2337 ent->end = end; 2338 2339 if (mp->entries == NULL) { 2340 mp->entries = ent; 2341 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn)); 2342 return (ent); 2343 } 2344 2345 prev = mp->entries; 2346 while (prev->next) 2347 prev = prev->next; 2348 prev->next = ent; 2349 ent->prev = prev; 2350 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn)); 2351 return (ent); 2352 } 2353 2354 static void 2355 boot_entry_addline(entry_t *ent, line_t *lp) 2356 { 2357 if (ent) 2358 ent->end = lp; 2359 } 2360 2361 /* 2362 * Check whether cmd matches the one indexed by which, and whether arg matches 2363 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the 2364 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using 2365 * strstr(), so it can be a partial match. 2366 */ 2367 static int 2368 check_cmd(const char *cmd, const int which, const char *arg, const char *str) 2369 { 2370 int ret; 2371 const char *fcn = "check_cmd()"; 2372 2373 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str)); 2374 2375 if ((strcmp(cmd, menu_cmds[which]) != 0) && 2376 (strcmp(cmd, menu_cmds[which + 1]) != 0)) { 2377 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH, 2378 fcn, cmd, menu_cmds[which])); 2379 return (0); 2380 } 2381 ret = (strstr(arg, str) != NULL); 2382 2383 if (ret) { 2384 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 2385 } else { 2386 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 2387 } 2388 2389 return (ret); 2390 } 2391 2392 static error_t 2393 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum) 2394 { 2395 const char *fcn = "kernel_parser()"; 2396 2397 assert(entry); 2398 assert(cmd); 2399 assert(arg); 2400 2401 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 && 2402 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) { 2403 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd)); 2404 return (BAM_ERROR); 2405 } 2406 2407 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) { 2408 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg)); 2409 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT; 2410 } else if (strncmp(arg, DIRECT_BOOT_KERNEL, 2411 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) { 2412 BAM_DPRINTF((D_SET_DBOOT, fcn, arg)); 2413 entry->flags |= BAM_ENTRY_DBOOT; 2414 } else if (strncmp(arg, DIRECT_BOOT_64, 2415 sizeof (DIRECT_BOOT_64) - 1) == 0) { 2416 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg)); 2417 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT; 2418 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL, 2419 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) { 2420 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg)); 2421 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE; 2422 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) { 2423 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg)); 2424 entry->flags |= BAM_ENTRY_MULTIBOOT; 2425 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE, 2426 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) { 2427 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg)); 2428 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE; 2429 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) { 2430 BAM_DPRINTF((D_SET_HV, fcn, arg)); 2431 entry->flags |= BAM_ENTRY_HV; 2432 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) { 2433 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg)); 2434 return (BAM_ERROR); 2435 } else { 2436 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg)); 2437 bam_error(UNKNOWN_KERNEL_LINE, linenum); 2438 return (BAM_ERROR); 2439 } 2440 2441 return (BAM_SUCCESS); 2442 } 2443 2444 static error_t 2445 module_parser(entry_t *entry, char *cmd, char *arg, int linenum) 2446 { 2447 const char *fcn = "module_parser()"; 2448 2449 assert(entry); 2450 assert(cmd); 2451 assert(arg); 2452 2453 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 && 2454 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) { 2455 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd)); 2456 return (BAM_ERROR); 2457 } 2458 2459 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 || 2460 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 || 2461 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 || 2462 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 || 2463 strcmp(arg, FAILSAFE_ARCHIVE) == 0 || 2464 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 || 2465 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) { 2466 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg)); 2467 return (BAM_SUCCESS); 2468 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) && 2469 !(entry->flags & BAM_ENTRY_LU)) { 2470 /* don't emit warning for hand entries */ 2471 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg)); 2472 return (BAM_ERROR); 2473 } else { 2474 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg)); 2475 bam_error(UNKNOWN_MODULE_LINE, linenum); 2476 return (BAM_ERROR); 2477 } 2478 } 2479 2480 /* 2481 * A line in menu.lst looks like 2482 * [ ]*<cmd>[ \t=]*<arg>* 2483 */ 2484 static void 2485 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum) 2486 { 2487 /* 2488 * save state across calls. This is so that 2489 * header gets the right entry# after title has 2490 * been processed 2491 */ 2492 static line_t *prev = NULL; 2493 static entry_t *curr_ent = NULL; 2494 static int in_liveupgrade = 0; 2495 2496 line_t *lp; 2497 char *cmd, *sep, *arg; 2498 char save, *cp, *line; 2499 menu_flag_t flag = BAM_INVALID; 2500 const char *fcn = "line_parser()"; 2501 2502 if (str == NULL) { 2503 return; 2504 } 2505 2506 /* 2507 * First save a copy of the entire line. 2508 * We use this later to set the line field. 2509 */ 2510 line = s_strdup(str); 2511 2512 /* Eat up leading whitespace */ 2513 while (*str == ' ' || *str == '\t') 2514 str++; 2515 2516 if (*str == '#') { /* comment */ 2517 cmd = s_strdup("#"); 2518 sep = NULL; 2519 arg = s_strdup(str + 1); 2520 flag = BAM_COMMENT; 2521 if (strstr(arg, BAM_LU_HDR) != NULL) { 2522 in_liveupgrade = 1; 2523 } else if (strstr(arg, BAM_LU_FTR) != NULL) { 2524 in_liveupgrade = 0; 2525 } 2526 } else if (*str == '\0') { /* blank line */ 2527 cmd = sep = arg = NULL; 2528 flag = BAM_EMPTY; 2529 } else { 2530 /* 2531 * '=' is not a documented separator in grub syntax. 2532 * However various development bits use '=' as a 2533 * separator. In addition, external users also 2534 * use = as a separator. So we will allow that usage. 2535 */ 2536 cp = str; 2537 while (*str != ' ' && *str != '\t' && *str != '=') { 2538 if (*str == '\0') { 2539 cmd = s_strdup(cp); 2540 sep = arg = NULL; 2541 break; 2542 } 2543 str++; 2544 } 2545 2546 if (*str != '\0') { 2547 save = *str; 2548 *str = '\0'; 2549 cmd = s_strdup(cp); 2550 *str = save; 2551 2552 str++; 2553 save = *str; 2554 *str = '\0'; 2555 sep = s_strdup(str - 1); 2556 *str = save; 2557 2558 while (*str == ' ' || *str == '\t') 2559 str++; 2560 if (*str == '\0') 2561 arg = NULL; 2562 else 2563 arg = s_strdup(str); 2564 } 2565 } 2566 2567 lp = s_calloc(1, sizeof (line_t)); 2568 2569 lp->cmd = cmd; 2570 lp->sep = sep; 2571 lp->arg = arg; 2572 lp->line = line; 2573 lp->lineNum = ++(*lineNum); 2574 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) { 2575 lp->entryNum = ++(*entryNum); 2576 lp->flags = BAM_TITLE; 2577 if (prev && prev->flags == BAM_COMMENT && 2578 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2579 prev->entryNum = lp->entryNum; 2580 curr_ent = boot_entry_new(mp, prev, lp); 2581 curr_ent->flags |= BAM_ENTRY_BOOTADM; 2582 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg)); 2583 } else { 2584 curr_ent = boot_entry_new(mp, lp, lp); 2585 if (in_liveupgrade) { 2586 curr_ent->flags |= BAM_ENTRY_LU; 2587 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg)); 2588 } 2589 } 2590 curr_ent->entryNum = *entryNum; 2591 } else if (flag != BAM_INVALID) { 2592 /* 2593 * For header comments, the entry# is "fixed up" 2594 * by the subsequent title 2595 */ 2596 lp->entryNum = *entryNum; 2597 lp->flags = flag; 2598 } else { 2599 lp->entryNum = *entryNum; 2600 2601 if (*entryNum == ENTRY_INIT) { 2602 lp->flags = BAM_GLOBAL; 2603 } else { 2604 lp->flags = BAM_ENTRY; 2605 2606 if (cmd && arg) { 2607 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) { 2608 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg)); 2609 curr_ent->flags |= BAM_ENTRY_ROOT; 2610 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD]) 2611 == 0) { 2612 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn, 2613 arg)); 2614 curr_ent->flags |= BAM_ENTRY_FINDROOT; 2615 } else if (strcmp(cmd, 2616 menu_cmds[CHAINLOADER_CMD]) == 0) { 2617 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn, 2618 arg)); 2619 curr_ent->flags |= 2620 BAM_ENTRY_CHAINLOADER; 2621 } else if (kernel_parser(curr_ent, cmd, arg, 2622 lp->lineNum) != BAM_SUCCESS) { 2623 (void) module_parser(curr_ent, cmd, 2624 arg, lp->lineNum); 2625 } 2626 } 2627 } 2628 } 2629 2630 /* record default, old default, and entry line ranges */ 2631 if (lp->flags == BAM_GLOBAL && 2632 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) { 2633 mp->curdefault = lp; 2634 } else if (lp->flags == BAM_COMMENT && 2635 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) { 2636 mp->olddefault = lp; 2637 } else if (lp->flags == BAM_COMMENT && 2638 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) { 2639 mp->old_rc_default = lp; 2640 } else if (lp->flags == BAM_ENTRY || 2641 (lp->flags == BAM_COMMENT && 2642 strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) { 2643 boot_entry_addline(curr_ent, lp); 2644 } 2645 append_line(mp, lp); 2646 2647 prev = lp; 2648 } 2649 2650 void 2651 update_numbering(menu_t *mp) 2652 { 2653 int lineNum; 2654 int entryNum; 2655 int old_default_value; 2656 line_t *lp, *prev, *default_lp, *default_entry; 2657 char buf[PATH_MAX]; 2658 2659 if (mp->start == NULL) { 2660 return; 2661 } 2662 2663 lineNum = LINE_INIT; 2664 entryNum = ENTRY_INIT; 2665 old_default_value = ENTRY_INIT; 2666 lp = default_lp = default_entry = NULL; 2667 2668 prev = NULL; 2669 for (lp = mp->start; lp; prev = lp, lp = lp->next) { 2670 lp->lineNum = ++lineNum; 2671 2672 /* 2673 * Get the value of the default command 2674 */ 2675 if (lp->entryNum == ENTRY_INIT && lp->cmd && 2676 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 && 2677 lp->arg) { 2678 old_default_value = atoi(lp->arg); 2679 default_lp = lp; 2680 } 2681 2682 /* 2683 * If not a booting entry, nothing else to fix for this 2684 * entry 2685 */ 2686 if (lp->entryNum == ENTRY_INIT) 2687 continue; 2688 2689 /* 2690 * Record the position of the default entry. 2691 * The following works because global 2692 * commands like default and timeout should precede 2693 * actual boot entries, so old_default_value 2694 * is already known (or default cmd is missing). 2695 */ 2696 if (default_entry == NULL && 2697 old_default_value != ENTRY_INIT && 2698 lp->entryNum == old_default_value) { 2699 default_entry = lp; 2700 } 2701 2702 /* 2703 * Now fixup the entry number 2704 */ 2705 if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) { 2706 lp->entryNum = ++entryNum; 2707 /* fixup the bootadm header */ 2708 if (prev && prev->flags == BAM_COMMENT && 2709 prev->arg && 2710 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2711 prev->entryNum = lp->entryNum; 2712 } 2713 } else { 2714 lp->entryNum = entryNum; 2715 } 2716 } 2717 2718 /* 2719 * No default command in menu, simply return 2720 */ 2721 if (default_lp == NULL) { 2722 return; 2723 } 2724 2725 free(default_lp->arg); 2726 free(default_lp->line); 2727 2728 if (default_entry == NULL) { 2729 default_lp->arg = s_strdup("0"); 2730 } else { 2731 (void) snprintf(buf, sizeof (buf), "%d", 2732 default_entry->entryNum); 2733 default_lp->arg = s_strdup(buf); 2734 } 2735 2736 /* 2737 * The following is required since only the line field gets 2738 * written back to menu.lst 2739 */ 2740 (void) snprintf(buf, sizeof (buf), "%s%s%s", 2741 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg); 2742 default_lp->line = s_strdup(buf); 2743 } 2744 2745 2746 static menu_t * 2747 menu_read(char *menu_path) 2748 { 2749 FILE *fp; 2750 char buf[BAM_MAXLINE], *cp; 2751 menu_t *mp; 2752 int line, entry, len, n; 2753 2754 mp = s_calloc(1, sizeof (menu_t)); 2755 2756 fp = fopen(menu_path, "r"); 2757 if (fp == NULL) { /* Let the caller handle this error */ 2758 return (mp); 2759 } 2760 2761 2762 /* Note: GRUB boot entry number starts with 0 */ 2763 line = LINE_INIT; 2764 entry = ENTRY_INIT; 2765 cp = buf; 2766 len = sizeof (buf); 2767 while (s_fgets(cp, len, fp) != NULL) { 2768 n = strlen(cp); 2769 if (cp[n - 1] == '\\') { 2770 len -= n - 1; 2771 assert(len >= 2); 2772 cp += n - 1; 2773 continue; 2774 } 2775 line_parser(mp, buf, &line, &entry); 2776 cp = buf; 2777 len = sizeof (buf); 2778 } 2779 2780 if (fclose(fp) == EOF) { 2781 bam_error(CLOSE_FAIL, menu_path, strerror(errno)); 2782 } 2783 2784 return (mp); 2785 } 2786 2787 static error_t 2788 selector(menu_t *mp, char *opt, int *entry, char **title) 2789 { 2790 char *eq; 2791 char *opt_dup; 2792 int entryNum; 2793 2794 assert(mp); 2795 assert(mp->start); 2796 assert(opt); 2797 2798 opt_dup = s_strdup(opt); 2799 2800 if (entry) 2801 *entry = ENTRY_INIT; 2802 if (title) 2803 *title = NULL; 2804 2805 eq = strchr(opt_dup, '='); 2806 if (eq == NULL) { 2807 bam_error(INVALID_OPT, opt); 2808 free(opt_dup); 2809 return (BAM_ERROR); 2810 } 2811 2812 *eq = '\0'; 2813 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) { 2814 assert(mp->end); 2815 entryNum = s_strtol(eq + 1); 2816 if (entryNum < 0 || entryNum > mp->end->entryNum) { 2817 bam_error(INVALID_ENTRY, eq + 1); 2818 free(opt_dup); 2819 return (BAM_ERROR); 2820 } 2821 *entry = entryNum; 2822 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) { 2823 *title = opt + (eq - opt_dup) + 1; 2824 } else { 2825 bam_error(INVALID_OPT, opt); 2826 free(opt_dup); 2827 return (BAM_ERROR); 2828 } 2829 2830 free(opt_dup); 2831 return (BAM_SUCCESS); 2832 } 2833 2834 /* 2835 * If invoked with no titles/entries (opt == NULL) 2836 * only title lines in file are printed. 2837 * 2838 * If invoked with a title or entry #, all 2839 * lines in *every* matching entry are listed 2840 */ 2841 static error_t 2842 list_entry(menu_t *mp, char *menu_path, char *opt) 2843 { 2844 line_t *lp; 2845 int entry = ENTRY_INIT; 2846 int found; 2847 char *title = NULL; 2848 2849 assert(mp); 2850 assert(menu_path); 2851 2852 /* opt is optional */ 2853 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path, 2854 opt ? opt : "<NULL>")); 2855 2856 if (mp->start == NULL) { 2857 bam_error(NO_MENU, menu_path); 2858 return (BAM_ERROR); 2859 } 2860 2861 if (opt != NULL) { 2862 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) { 2863 return (BAM_ERROR); 2864 } 2865 assert((entry != ENTRY_INIT) ^ (title != NULL)); 2866 } else { 2867 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0); 2868 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0); 2869 } 2870 2871 found = 0; 2872 for (lp = mp->start; lp; lp = lp->next) { 2873 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY) 2874 continue; 2875 if (opt == NULL && lp->flags == BAM_TITLE) { 2876 bam_print(PRINT_TITLE, lp->entryNum, 2877 lp->arg); 2878 found = 1; 2879 continue; 2880 } 2881 if (entry != ENTRY_INIT && lp->entryNum == entry) { 2882 bam_print(PRINT, lp->line); 2883 found = 1; 2884 continue; 2885 } 2886 2887 /* 2888 * We set the entry value here so that all lines 2889 * in entry get printed. If we subsequently match 2890 * title in other entries, all lines in those 2891 * entries get printed as well. 2892 */ 2893 if (title && lp->flags == BAM_TITLE && lp->arg && 2894 strncmp(title, lp->arg, strlen(title)) == 0) { 2895 bam_print(PRINT, lp->line); 2896 entry = lp->entryNum; 2897 found = 1; 2898 continue; 2899 } 2900 } 2901 2902 if (!found) { 2903 bam_error(NO_MATCH_ENTRY); 2904 return (BAM_ERROR); 2905 } 2906 2907 return (BAM_SUCCESS); 2908 } 2909 2910 int 2911 add_boot_entry(menu_t *mp, 2912 char *title, 2913 char *findroot, 2914 char *kernel, 2915 char *mod_kernel, 2916 char *module) 2917 { 2918 int lineNum; 2919 int entryNum; 2920 char linebuf[BAM_MAXLINE]; 2921 menu_cmd_t k_cmd; 2922 menu_cmd_t m_cmd; 2923 const char *fcn = "add_boot_entry()"; 2924 2925 assert(mp); 2926 2927 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL); 2928 if (findroot == NULL) { 2929 bam_error(NULL_FINDROOT); 2930 return (BAM_ERROR); 2931 } 2932 2933 if (title == NULL) { 2934 title = "Solaris"; /* default to Solaris */ 2935 } 2936 if (kernel == NULL) { 2937 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]); 2938 return (BAM_ERROR); 2939 } 2940 if (module == NULL) { 2941 if (bam_direct != BAM_DIRECT_DBOOT) { 2942 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]); 2943 return (BAM_ERROR); 2944 } 2945 2946 /* Figure the commands out from the kernel line */ 2947 if (strstr(kernel, "$ISADIR") != NULL) { 2948 module = DIRECT_BOOT_ARCHIVE; 2949 k_cmd = KERNEL_DOLLAR_CMD; 2950 m_cmd = MODULE_DOLLAR_CMD; 2951 } else if (strstr(kernel, "amd64") != NULL) { 2952 module = DIRECT_BOOT_ARCHIVE_64; 2953 k_cmd = KERNEL_CMD; 2954 m_cmd = MODULE_CMD; 2955 } else { 2956 module = DIRECT_BOOT_ARCHIVE_32; 2957 k_cmd = KERNEL_CMD; 2958 m_cmd = MODULE_CMD; 2959 } 2960 } else if ((bam_direct == BAM_DIRECT_DBOOT) && 2961 (strstr(kernel, "$ISADIR") != NULL)) { 2962 /* 2963 * If it's a non-failsafe dboot kernel, use the "kernel$" 2964 * command. Otherwise, use "kernel". 2965 */ 2966 k_cmd = KERNEL_DOLLAR_CMD; 2967 m_cmd = MODULE_DOLLAR_CMD; 2968 } else { 2969 k_cmd = KERNEL_CMD; 2970 m_cmd = MODULE_CMD; 2971 } 2972 2973 if (mp->start) { 2974 lineNum = mp->end->lineNum; 2975 entryNum = mp->end->entryNum; 2976 } else { 2977 lineNum = LINE_INIT; 2978 entryNum = ENTRY_INIT; 2979 } 2980 2981 /* 2982 * No separator for comment (HDR/FTR) commands 2983 * The syntax for comments is #<comment> 2984 */ 2985 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2986 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR); 2987 line_parser(mp, linebuf, &lineNum, &entryNum); 2988 2989 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2990 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 2991 line_parser(mp, linebuf, &lineNum, &entryNum); 2992 2993 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2994 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot); 2995 line_parser(mp, linebuf, &lineNum, &entryNum); 2996 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum)); 2997 2998 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2999 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel); 3000 line_parser(mp, linebuf, &lineNum, &entryNum); 3001 3002 if (mod_kernel != NULL) { 3003 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3004 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel); 3005 line_parser(mp, linebuf, &lineNum, &entryNum); 3006 } 3007 3008 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3009 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module); 3010 line_parser(mp, linebuf, &lineNum, &entryNum); 3011 3012 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 3013 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR); 3014 line_parser(mp, linebuf, &lineNum, &entryNum); 3015 3016 return (entryNum); 3017 } 3018 3019 static error_t 3020 do_delete(menu_t *mp, int entryNum) 3021 { 3022 line_t *lp; 3023 line_t *freed; 3024 entry_t *ent; 3025 entry_t *tmp; 3026 int deleted; 3027 const char *fcn = "do_delete()"; 3028 3029 assert(entryNum != ENTRY_INIT); 3030 3031 tmp = NULL; 3032 3033 ent = mp->entries; 3034 while (ent) { 3035 lp = ent->start; 3036 /* check entry number and make sure it's a bootadm entry */ 3037 if (lp->flags != BAM_COMMENT || 3038 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 || 3039 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) { 3040 ent = ent->next; 3041 continue; 3042 } 3043 3044 /* free the entry content */ 3045 do { 3046 freed = lp; 3047 lp = lp->next; /* prev stays the same */ 3048 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum)); 3049 unlink_line(mp, freed); 3050 line_free(freed); 3051 } while (freed != ent->end); 3052 3053 /* free the entry_t structure */ 3054 assert(tmp == NULL); 3055 tmp = ent; 3056 ent = ent->next; 3057 if (tmp->prev) 3058 tmp->prev->next = ent; 3059 else 3060 mp->entries = ent; 3061 if (ent) 3062 ent->prev = tmp->prev; 3063 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum)); 3064 free(tmp); 3065 tmp = NULL; 3066 deleted = 1; 3067 } 3068 3069 assert(tmp == NULL); 3070 3071 if (!deleted && entryNum != ALL_ENTRIES) { 3072 bam_error(NO_BOOTADM_MATCH); 3073 return (BAM_ERROR); 3074 } 3075 3076 /* 3077 * Now that we have deleted an entry, update 3078 * the entry numbering and the default cmd. 3079 */ 3080 update_numbering(mp); 3081 3082 return (BAM_SUCCESS); 3083 } 3084 3085 static error_t 3086 delete_all_entries(menu_t *mp, char *dummy, char *opt) 3087 { 3088 assert(mp); 3089 assert(dummy == NULL); 3090 assert(opt == NULL); 3091 3092 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries")); 3093 3094 if (mp->start == NULL) { 3095 bam_print(EMPTY_MENU); 3096 return (BAM_SUCCESS); 3097 } 3098 3099 if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) { 3100 return (BAM_ERROR); 3101 } 3102 3103 return (BAM_WRITE); 3104 } 3105 3106 static FILE * 3107 create_diskmap(char *osroot) 3108 { 3109 FILE *fp; 3110 char cmd[PATH_MAX]; 3111 const char *fcn = "create_diskmap()"; 3112 3113 /* make sure we have a map file */ 3114 fp = fopen(GRUBDISK_MAP, "r"); 3115 if (fp == NULL) { 3116 (void) snprintf(cmd, sizeof (cmd), 3117 "%s/%s > /dev/null", osroot, CREATE_DISKMAP); 3118 if (exec_cmd(cmd, NULL) != 0) 3119 return (NULL); 3120 fp = fopen(GRUBDISK_MAP, "r"); 3121 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL); 3122 if (fp) { 3123 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP)); 3124 } else { 3125 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP)); 3126 } 3127 } 3128 return (fp); 3129 } 3130 3131 #define SECTOR_SIZE 512 3132 3133 static int 3134 get_partition(char *device) 3135 { 3136 int i, fd, is_pcfs, partno = -1; 3137 struct mboot *mboot; 3138 char boot_sect[SECTOR_SIZE]; 3139 char *wholedisk, *slice; 3140 3141 /* form whole disk (p0) */ 3142 slice = device + strlen(device) - 2; 3143 is_pcfs = (*slice != 's'); 3144 if (!is_pcfs) 3145 *slice = '\0'; 3146 wholedisk = s_calloc(1, strlen(device) + 3); 3147 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device); 3148 if (!is_pcfs) 3149 *slice = 's'; 3150 3151 /* read boot sector */ 3152 fd = open(wholedisk, O_RDONLY); 3153 free(wholedisk); 3154 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 3155 return (partno); 3156 } 3157 (void) close(fd); 3158 3159 /* parse fdisk table */ 3160 mboot = (struct mboot *)((void *)boot_sect); 3161 for (i = 0; i < FD_NUMPART; i++) { 3162 struct ipart *part = 3163 (struct ipart *)(uintptr_t)mboot->parts + i; 3164 if (is_pcfs) { /* looking for solaris boot part */ 3165 if (part->systid == 0xbe) { 3166 partno = i; 3167 break; 3168 } 3169 } else { /* look for solaris partition, old and new */ 3170 if (part->systid == SUNIXOS || 3171 part->systid == SUNIXOS2) { 3172 partno = i; 3173 break; 3174 } 3175 } 3176 } 3177 return (partno); 3178 } 3179 3180 char * 3181 get_grubroot(char *osroot, char *osdev, char *menu_root) 3182 { 3183 char *grubroot; /* (hd#,#,#) */ 3184 char *slice; 3185 char *grubhd; 3186 int fdiskpart; 3187 int found = 0; 3188 char *devname; 3189 char *ctdname = strstr(osdev, "dsk/"); 3190 char linebuf[PATH_MAX]; 3191 FILE *fp; 3192 const char *fcn = "get_grubroot()"; 3193 3194 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL); 3195 if (ctdname == NULL) { 3196 bam_error(INVALID_DEV_DSK, osdev); 3197 return (NULL); 3198 } 3199 3200 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) { 3201 /* menu bears no resemblance to our reality */ 3202 bam_error(CANNOT_GRUBROOT_BOOTDISK, fcn, osdev); 3203 return (NULL); 3204 } 3205 3206 ctdname += strlen("dsk/"); 3207 slice = strrchr(ctdname, 's'); 3208 if (slice) 3209 *slice = '\0'; 3210 3211 fp = create_diskmap(osroot); 3212 if (fp == NULL) { 3213 bam_error(DISKMAP_FAIL, osroot); 3214 return (NULL); 3215 } 3216 3217 rewind(fp); 3218 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) { 3219 grubhd = strtok(linebuf, " \t\n"); 3220 if (grubhd) 3221 devname = strtok(NULL, " \t\n"); 3222 else 3223 devname = NULL; 3224 if (devname && strcmp(devname, ctdname) == 0) { 3225 found = 1; 3226 break; 3227 } 3228 } 3229 3230 if (slice) 3231 *slice = 's'; 3232 3233 (void) fclose(fp); 3234 fp = NULL; 3235 3236 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0); 3237 if (found == 0) { 3238 bam_error(BIOSDEV_FAIL, osdev); 3239 return (NULL); 3240 } 3241 3242 fdiskpart = get_partition(osdev); 3243 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1); 3244 if (fdiskpart == -1) { 3245 bam_error(FDISKPART_FAIL, osdev); 3246 return (NULL); 3247 } 3248 3249 grubroot = s_calloc(1, 10); 3250 if (slice) { 3251 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)", 3252 grubhd, fdiskpart, slice[1] + 'a' - '0'); 3253 } else 3254 (void) snprintf(grubroot, 10, "(hd%s,%d)", 3255 grubhd, fdiskpart); 3256 3257 assert(fp == NULL); 3258 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0); 3259 return (grubroot); 3260 } 3261 3262 static char * 3263 find_primary_common(char *mntpt, char *fstype) 3264 { 3265 char signdir[PATH_MAX]; 3266 char tmpsign[MAXNAMELEN + 1]; 3267 char *lu; 3268 char *ufs; 3269 char *zfs; 3270 DIR *dirp = NULL; 3271 struct dirent *entp; 3272 struct stat sb; 3273 const char *fcn = "find_primary_common()"; 3274 3275 (void) snprintf(signdir, sizeof (signdir), "%s/%s", 3276 mntpt, GRUBSIGN_DIR); 3277 3278 if (stat(signdir, &sb) == -1) { 3279 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir)); 3280 return (NULL); 3281 } 3282 3283 dirp = opendir(signdir); 3284 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL); 3285 if (dirp == NULL) { 3286 bam_error(OPENDIR_FAILED, signdir, strerror(errno)); 3287 return (NULL); 3288 } 3289 3290 ufs = zfs = lu = NULL; 3291 3292 while (entp = readdir(dirp)) { 3293 if (strcmp(entp->d_name, ".") == 0 || 3294 strcmp(entp->d_name, "..") == 0) 3295 continue; 3296 3297 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name); 3298 3299 if (lu == NULL && 3300 strncmp(tmpsign, GRUBSIGN_LU_PREFIX, 3301 strlen(GRUBSIGN_LU_PREFIX)) == 0) { 3302 lu = s_strdup(tmpsign); 3303 } 3304 3305 if (ufs == NULL && 3306 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 3307 strlen(GRUBSIGN_UFS_PREFIX)) == 0) { 3308 ufs = s_strdup(tmpsign); 3309 } 3310 3311 if (zfs == NULL && 3312 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX, 3313 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) { 3314 zfs = s_strdup(tmpsign); 3315 } 3316 } 3317 3318 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn, 3319 zfs ? zfs : "NULL", 3320 ufs ? ufs : "NULL", 3321 lu ? lu : "NULL")); 3322 3323 if (dirp) { 3324 (void) closedir(dirp); 3325 dirp = NULL; 3326 } 3327 3328 if (strcmp(fstype, "ufs") == 0 && zfs) { 3329 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs"); 3330 free(zfs); 3331 zfs = NULL; 3332 } else if (strcmp(fstype, "zfs") == 0 && ufs) { 3333 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs"); 3334 free(ufs); 3335 ufs = NULL; 3336 } 3337 3338 assert(dirp == NULL); 3339 3340 /* For now, we let Live Upgrade take care of its signature itself */ 3341 if (lu) { 3342 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu)); 3343 free(lu); 3344 lu = NULL; 3345 } 3346 3347 return (zfs ? zfs : ufs); 3348 } 3349 3350 static char * 3351 find_backup_common(char *mntpt, char *fstype) 3352 { 3353 FILE *bfp = NULL; 3354 char tmpsign[MAXNAMELEN + 1]; 3355 char backup[PATH_MAX]; 3356 char *ufs; 3357 char *zfs; 3358 char *lu; 3359 int error; 3360 const char *fcn = "find_backup_common()"; 3361 3362 /* 3363 * We didn't find it in the primary directory. 3364 * Look at the backup 3365 */ 3366 (void) snprintf(backup, sizeof (backup), "%s%s", 3367 mntpt, GRUBSIGN_BACKUP); 3368 3369 bfp = fopen(backup, "r"); 3370 if (bfp == NULL) { 3371 error = errno; 3372 if (bam_verbose) { 3373 bam_error(OPEN_FAIL, backup, strerror(error)); 3374 } 3375 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error))); 3376 return (NULL); 3377 } 3378 3379 ufs = zfs = lu = NULL; 3380 3381 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) { 3382 3383 if (lu == NULL && 3384 strncmp(tmpsign, GRUBSIGN_LU_PREFIX, 3385 strlen(GRUBSIGN_LU_PREFIX)) == 0) { 3386 lu = s_strdup(tmpsign); 3387 } 3388 3389 if (ufs == NULL && 3390 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 3391 strlen(GRUBSIGN_UFS_PREFIX)) == 0) { 3392 ufs = s_strdup(tmpsign); 3393 } 3394 3395 if (zfs == NULL && 3396 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX, 3397 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) { 3398 zfs = s_strdup(tmpsign); 3399 } 3400 } 3401 3402 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn, 3403 zfs ? zfs : "NULL", 3404 ufs ? ufs : "NULL", 3405 lu ? lu : "NULL")); 3406 3407 if (bfp) { 3408 (void) fclose(bfp); 3409 bfp = NULL; 3410 } 3411 3412 if (strcmp(fstype, "ufs") == 0 && zfs) { 3413 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs"); 3414 free(zfs); 3415 zfs = NULL; 3416 } else if (strcmp(fstype, "zfs") == 0 && ufs) { 3417 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs"); 3418 free(ufs); 3419 ufs = NULL; 3420 } 3421 3422 assert(bfp == NULL); 3423 3424 /* For now, we let Live Upgrade take care of its signature itself */ 3425 if (lu) { 3426 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu)); 3427 free(lu); 3428 lu = NULL; 3429 } 3430 3431 return (zfs ? zfs : ufs); 3432 } 3433 3434 static char * 3435 find_ufs_existing(char *osroot) 3436 { 3437 char *sign; 3438 const char *fcn = "find_ufs_existing()"; 3439 3440 sign = find_primary_common(osroot, "ufs"); 3441 if (sign == NULL) { 3442 sign = find_backup_common(osroot, "ufs"); 3443 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL")); 3444 } else { 3445 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign)); 3446 } 3447 3448 return (sign); 3449 } 3450 3451 char * 3452 get_mountpoint(char *special, char *fstype) 3453 { 3454 FILE *mntfp; 3455 struct mnttab mp = {0}; 3456 struct mnttab mpref = {0}; 3457 int error; 3458 int ret; 3459 const char *fcn = "get_mountpoint()"; 3460 3461 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype)); 3462 3463 mntfp = fopen(MNTTAB, "r"); 3464 error = errno; 3465 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL); 3466 if (mntfp == NULL) { 3467 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 3468 return (NULL); 3469 } 3470 3471 mpref.mnt_special = special; 3472 mpref.mnt_fstype = fstype; 3473 3474 ret = getmntany(mntfp, &mp, &mpref); 3475 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1); 3476 if (ret != 0) { 3477 (void) fclose(mntfp); 3478 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype)); 3479 return (NULL); 3480 } 3481 (void) fclose(mntfp); 3482 3483 assert(mp.mnt_mountp); 3484 3485 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp)); 3486 3487 return (s_strdup(mp.mnt_mountp)); 3488 } 3489 3490 /* 3491 * Mounts a "legacy" top dataset (if needed) 3492 * Returns: The mountpoint of the legacy top dataset or NULL on error 3493 * mnted returns one of the above values defined for zfs_mnted_t 3494 */ 3495 static char * 3496 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted) 3497 { 3498 char cmd[PATH_MAX]; 3499 char tmpmnt[PATH_MAX]; 3500 filelist_t flist = {0}; 3501 char *is_mounted; 3502 struct stat sb; 3503 int ret; 3504 const char *fcn = "mount_legacy_dataset()"; 3505 3506 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool)); 3507 3508 *mnted = ZFS_MNT_ERROR; 3509 3510 (void) snprintf(cmd, sizeof (cmd), 3511 "/sbin/zfs get -Ho value mounted %s", 3512 pool); 3513 3514 ret = exec_cmd(cmd, &flist); 3515 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1); 3516 if (ret != 0) { 3517 bam_error(ZFS_MNTED_FAILED, pool); 3518 return (NULL); 3519 } 3520 3521 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL); 3522 if ((flist.head == NULL) || (flist.head != flist.tail)) { 3523 bam_error(BAD_ZFS_MNTED, pool); 3524 filelist_free(&flist); 3525 return (NULL); 3526 } 3527 3528 is_mounted = strtok(flist.head->line, " \t\n"); 3529 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes"); 3530 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no"); 3531 if (strcmp(is_mounted, "no") != 0) { 3532 filelist_free(&flist); 3533 *mnted = LEGACY_ALREADY; 3534 /* get_mountpoint returns a strdup'ed string */ 3535 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool)); 3536 return (get_mountpoint(pool, "zfs")); 3537 } 3538 3539 filelist_free(&flist); 3540 3541 /* 3542 * legacy top dataset is not mounted. Mount it now 3543 * First create a mountpoint. 3544 */ 3545 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d", 3546 ZFS_LEGACY_MNTPT, getpid()); 3547 3548 ret = stat(tmpmnt, &sb); 3549 if (ret == -1) { 3550 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt)); 3551 ret = mkdirp(tmpmnt, 0755); 3552 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1); 3553 if (ret == -1) { 3554 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno)); 3555 return (NULL); 3556 } 3557 } else { 3558 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt)); 3559 } 3560 3561 (void) snprintf(cmd, sizeof (cmd), 3562 "/sbin/mount -F zfs %s %s", 3563 pool, tmpmnt); 3564 3565 ret = exec_cmd(cmd, NULL); 3566 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1); 3567 if (ret != 0) { 3568 bam_error(ZFS_MOUNT_FAILED, pool); 3569 (void) rmdir(tmpmnt); 3570 return (NULL); 3571 } 3572 3573 *mnted = LEGACY_MOUNTED; 3574 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt)); 3575 return (s_strdup(tmpmnt)); 3576 } 3577 3578 /* 3579 * Mounts the top dataset (if needed) 3580 * Returns: The mountpoint of the top dataset or NULL on error 3581 * mnted returns one of the above values defined for zfs_mnted_t 3582 */ 3583 static char * 3584 mount_top_dataset(char *pool, zfs_mnted_t *mnted) 3585 { 3586 char cmd[PATH_MAX]; 3587 filelist_t flist = {0}; 3588 char *is_mounted; 3589 char *mntpt; 3590 char *zmntpt; 3591 int ret; 3592 const char *fcn = "mount_top_dataset()"; 3593 3594 *mnted = ZFS_MNT_ERROR; 3595 3596 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool)); 3597 3598 /* 3599 * First check if the top dataset is a "legacy" dataset 3600 */ 3601 (void) snprintf(cmd, sizeof (cmd), 3602 "/sbin/zfs get -Ho value mountpoint %s", 3603 pool); 3604 ret = exec_cmd(cmd, &flist); 3605 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1); 3606 if (ret != 0) { 3607 bam_error(ZFS_MNTPT_FAILED, pool); 3608 return (NULL); 3609 } 3610 3611 if (flist.head && (flist.head == flist.tail)) { 3612 char *legacy = strtok(flist.head->line, " \t\n"); 3613 if (legacy && strcmp(legacy, "legacy") == 0) { 3614 filelist_free(&flist); 3615 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool)); 3616 return (mount_legacy_dataset(pool, mnted)); 3617 } 3618 } 3619 3620 filelist_free(&flist); 3621 3622 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool)); 3623 3624 (void) snprintf(cmd, sizeof (cmd), 3625 "/sbin/zfs get -Ho value mounted %s", 3626 pool); 3627 3628 ret = exec_cmd(cmd, &flist); 3629 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1); 3630 if (ret != 0) { 3631 bam_error(ZFS_MNTED_FAILED, pool); 3632 return (NULL); 3633 } 3634 3635 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL); 3636 if ((flist.head == NULL) || (flist.head != flist.tail)) { 3637 bam_error(BAD_ZFS_MNTED, pool); 3638 filelist_free(&flist); 3639 return (NULL); 3640 } 3641 3642 is_mounted = strtok(flist.head->line, " \t\n"); 3643 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes"); 3644 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no"); 3645 if (strcmp(is_mounted, "no") != 0) { 3646 filelist_free(&flist); 3647 *mnted = ZFS_ALREADY; 3648 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool)); 3649 goto mounted; 3650 } 3651 3652 filelist_free(&flist); 3653 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool)); 3654 3655 /* top dataset is not mounted. Mount it now */ 3656 (void) snprintf(cmd, sizeof (cmd), 3657 "/sbin/zfs mount %s", pool); 3658 ret = exec_cmd(cmd, NULL); 3659 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1); 3660 if (ret != 0) { 3661 bam_error(ZFS_MOUNT_FAILED, pool); 3662 return (NULL); 3663 } 3664 *mnted = ZFS_MOUNTED; 3665 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool)); 3666 /*FALLTHRU*/ 3667 mounted: 3668 /* 3669 * Now get the mountpoint 3670 */ 3671 (void) snprintf(cmd, sizeof (cmd), 3672 "/sbin/zfs get -Ho value mountpoint %s", 3673 pool); 3674 3675 ret = exec_cmd(cmd, &flist); 3676 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1); 3677 if (ret != 0) { 3678 bam_error(ZFS_MNTPT_FAILED, pool); 3679 goto error; 3680 } 3681 3682 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL); 3683 if ((flist.head == NULL) || (flist.head != flist.tail)) { 3684 bam_error(NULL_ZFS_MNTPT, pool); 3685 goto error; 3686 } 3687 3688 mntpt = strtok(flist.head->line, " \t\n"); 3689 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo"); 3690 if (*mntpt != '/') { 3691 bam_error(BAD_ZFS_MNTPT, pool, mntpt); 3692 goto error; 3693 } 3694 zmntpt = s_strdup(mntpt); 3695 3696 filelist_free(&flist); 3697 3698 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt)); 3699 3700 return (zmntpt); 3701 3702 error: 3703 filelist_free(&flist); 3704 (void) umount_top_dataset(pool, *mnted, NULL); 3705 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 3706 return (NULL); 3707 } 3708 3709 static int 3710 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt) 3711 { 3712 char cmd[PATH_MAX]; 3713 int ret; 3714 const char *fcn = "umount_top_dataset()"; 3715 3716 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR); 3717 switch (mnted) { 3718 case LEGACY_ALREADY: 3719 case ZFS_ALREADY: 3720 /* nothing to do */ 3721 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool, 3722 mntpt ? mntpt : "NULL")); 3723 free(mntpt); 3724 return (BAM_SUCCESS); 3725 case LEGACY_MOUNTED: 3726 (void) snprintf(cmd, sizeof (cmd), 3727 "/sbin/umount %s", pool); 3728 ret = exec_cmd(cmd, NULL); 3729 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1); 3730 if (ret != 0) { 3731 bam_error(UMOUNT_FAILED, pool); 3732 free(mntpt); 3733 return (BAM_ERROR); 3734 } 3735 if (mntpt) 3736 (void) rmdir(mntpt); 3737 free(mntpt); 3738 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool)); 3739 return (BAM_SUCCESS); 3740 case ZFS_MOUNTED: 3741 free(mntpt); 3742 (void) snprintf(cmd, sizeof (cmd), 3743 "/sbin/zfs unmount %s", pool); 3744 ret = exec_cmd(cmd, NULL); 3745 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1); 3746 if (ret != 0) { 3747 bam_error(UMOUNT_FAILED, pool); 3748 return (BAM_ERROR); 3749 } 3750 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool)); 3751 return (BAM_SUCCESS); 3752 default: 3753 bam_error(INT_BAD_MNTSTATE, pool); 3754 return (BAM_ERROR); 3755 } 3756 /*NOTREACHED*/ 3757 } 3758 3759 /* 3760 * For ZFS, osdev can be one of two forms 3761 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402 3762 * It can be a /dev/[r]dsk special file. We handle both instances 3763 */ 3764 static char * 3765 get_pool(char *osdev) 3766 { 3767 char cmd[PATH_MAX]; 3768 char buf[PATH_MAX]; 3769 filelist_t flist = {0}; 3770 char *pool; 3771 char *cp; 3772 char *slash; 3773 int ret; 3774 const char *fcn = "get_pool()"; 3775 3776 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL); 3777 if (osdev == NULL) { 3778 bam_error(GET_POOL_OSDEV_NULL); 3779 return (NULL); 3780 } 3781 3782 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev)); 3783 3784 if (osdev[0] != '/') { 3785 (void) strlcpy(buf, osdev, sizeof (buf)); 3786 slash = strchr(buf, '/'); 3787 if (slash) 3788 *slash = '\0'; 3789 pool = s_strdup(buf); 3790 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool)); 3791 return (pool); 3792 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 3793 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) { 3794 bam_error(GET_POOL_BAD_OSDEV, osdev); 3795 return (NULL); 3796 } 3797 3798 (void) snprintf(cmd, sizeof (cmd), 3799 "/usr/sbin/fstyp -a %s 2>/dev/null | /bin/grep '^name:'", 3800 osdev); 3801 3802 ret = exec_cmd(cmd, &flist); 3803 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1); 3804 if (ret != 0) { 3805 bam_error(FSTYP_A_FAILED, osdev); 3806 return (NULL); 3807 } 3808 3809 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL); 3810 if ((flist.head == NULL) || (flist.head != flist.tail)) { 3811 bam_error(NULL_FSTYP_A, osdev); 3812 filelist_free(&flist); 3813 return (NULL); 3814 } 3815 3816 (void) strtok(flist.head->line, "'"); 3817 cp = strtok(NULL, "'"); 3818 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL); 3819 if (cp == NULL) { 3820 bam_error(BAD_FSTYP_A, osdev); 3821 filelist_free(&flist); 3822 return (NULL); 3823 } 3824 3825 pool = s_strdup(cp); 3826 3827 filelist_free(&flist); 3828 3829 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool)); 3830 3831 return (pool); 3832 } 3833 3834 static char * 3835 find_zfs_existing(char *osdev) 3836 { 3837 char *pool; 3838 zfs_mnted_t mnted; 3839 char *mntpt; 3840 char *sign; 3841 const char *fcn = "find_zfs_existing()"; 3842 3843 pool = get_pool(osdev); 3844 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL); 3845 if (pool == NULL) { 3846 bam_error(ZFS_GET_POOL_FAILED, osdev); 3847 return (NULL); 3848 } 3849 3850 mntpt = mount_top_dataset(pool, &mnted); 3851 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL); 3852 if (mntpt == NULL) { 3853 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool); 3854 free(pool); 3855 return (NULL); 3856 } 3857 3858 sign = find_primary_common(mntpt, "zfs"); 3859 if (sign == NULL) { 3860 sign = find_backup_common(mntpt, "zfs"); 3861 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL")); 3862 } else { 3863 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign)); 3864 } 3865 3866 (void) umount_top_dataset(pool, mnted, mntpt); 3867 3868 free(pool); 3869 3870 return (sign); 3871 } 3872 3873 static char * 3874 find_existing_sign(char *osroot, char *osdev, char *fstype) 3875 { 3876 const char *fcn = "find_existing_sign()"; 3877 3878 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs"); 3879 if (strcmp(fstype, "ufs") == 0) { 3880 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn)); 3881 return (find_ufs_existing(osroot)); 3882 } else if (strcmp(fstype, "zfs") == 0) { 3883 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn)); 3884 return (find_zfs_existing(osdev)); 3885 } else { 3886 bam_error(GRUBSIGN_NOTSUP, fstype); 3887 return (NULL); 3888 } 3889 } 3890 3891 #define MH_HASH_SZ 16 3892 3893 typedef enum { 3894 MH_ERROR = -1, 3895 MH_NOMATCH, 3896 MH_MATCH 3897 } mh_search_t; 3898 3899 typedef struct mcache { 3900 char *mc_special; 3901 char *mc_mntpt; 3902 char *mc_fstype; 3903 struct mcache *mc_next; 3904 } mcache_t; 3905 3906 typedef struct mhash { 3907 mcache_t *mh_hash[MH_HASH_SZ]; 3908 } mhash_t; 3909 3910 static int 3911 mhash_fcn(char *key) 3912 { 3913 int i; 3914 uint64_t sum = 0; 3915 3916 for (i = 0; key[i] != '\0'; i++) { 3917 sum += (uchar_t)key[i]; 3918 } 3919 3920 sum %= MH_HASH_SZ; 3921 3922 assert(sum < MH_HASH_SZ); 3923 3924 return (sum); 3925 } 3926 3927 static mhash_t * 3928 cache_mnttab(void) 3929 { 3930 FILE *mfp; 3931 struct extmnttab mnt; 3932 mcache_t *mcp; 3933 mhash_t *mhp; 3934 char *ctds; 3935 int idx; 3936 int error; 3937 char *special_dup; 3938 const char *fcn = "cache_mnttab()"; 3939 3940 mfp = fopen(MNTTAB, "r"); 3941 error = errno; 3942 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL); 3943 if (mfp == NULL) { 3944 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 3945 return (NULL); 3946 } 3947 3948 mhp = s_calloc(1, sizeof (mhash_t)); 3949 3950 resetmnttab(mfp); 3951 3952 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) { 3953 /* only cache ufs */ 3954 if (strcmp(mnt.mnt_fstype, "ufs") != 0) 3955 continue; 3956 3957 /* basename() modifies its arg, so dup it */ 3958 special_dup = s_strdup(mnt.mnt_special); 3959 ctds = basename(special_dup); 3960 3961 mcp = s_calloc(1, sizeof (mcache_t)); 3962 mcp->mc_special = s_strdup(ctds); 3963 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp); 3964 mcp->mc_fstype = s_strdup(mnt.mnt_fstype); 3965 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds, 3966 mnt.mnt_mountp, mnt.mnt_fstype)); 3967 idx = mhash_fcn(ctds); 3968 mcp->mc_next = mhp->mh_hash[idx]; 3969 mhp->mh_hash[idx] = mcp; 3970 free(special_dup); 3971 } 3972 3973 (void) fclose(mfp); 3974 3975 return (mhp); 3976 } 3977 3978 static void 3979 free_mnttab(mhash_t *mhp) 3980 { 3981 mcache_t *mcp; 3982 int i; 3983 3984 for (i = 0; i < MH_HASH_SZ; i++) { 3985 /*LINTED*/ 3986 while (mcp = mhp->mh_hash[i]) { 3987 mhp->mh_hash[i] = mcp->mc_next; 3988 free(mcp->mc_special); 3989 free(mcp->mc_mntpt); 3990 free(mcp->mc_fstype); 3991 free(mcp); 3992 } 3993 } 3994 3995 for (i = 0; i < MH_HASH_SZ; i++) { 3996 assert(mhp->mh_hash[i] == NULL); 3997 } 3998 free(mhp); 3999 } 4000 4001 static mh_search_t 4002 search_hash(mhash_t *mhp, char *special, char **mntpt) 4003 { 4004 int idx; 4005 mcache_t *mcp; 4006 const char *fcn = "search_hash()"; 4007 4008 assert(mntpt); 4009 4010 *mntpt = NULL; 4011 4012 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo"); 4013 if (strchr(special, '/')) { 4014 bam_error(INVALID_MHASH_KEY, special); 4015 return (MH_ERROR); 4016 } 4017 4018 idx = mhash_fcn(special); 4019 4020 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) { 4021 if (strcmp(mcp->mc_special, special) == 0) 4022 break; 4023 } 4024 4025 if (mcp == NULL) { 4026 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special)); 4027 return (MH_NOMATCH); 4028 } 4029 4030 assert(strcmp(mcp->mc_fstype, "ufs") == 0); 4031 *mntpt = mcp->mc_mntpt; 4032 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special)); 4033 return (MH_MATCH); 4034 } 4035 4036 static int 4037 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt) 4038 { 4039 char *sign; 4040 char *signline; 4041 char signbuf[MAXNAMELEN]; 4042 int len; 4043 int error; 4044 const char *fcn = "check_add_ufs_sign_to_list()"; 4045 4046 /* safe to specify NULL as "osdev" arg for UFS */ 4047 sign = find_existing_sign(mntpt, NULL, "ufs"); 4048 if (sign == NULL) { 4049 /* No existing signature, nothing to add to list */ 4050 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt)); 4051 return (0); 4052 } 4053 4054 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign); 4055 signline = signbuf; 4056 4057 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n"); 4058 if (strncmp(signline, GRUBSIGN_UFS_PREFIX, 4059 strlen(GRUBSIGN_UFS_PREFIX))) { 4060 bam_error(INVALID_UFS_SIGNATURE, sign); 4061 free(sign); 4062 /* ignore invalid signatures */ 4063 return (0); 4064 } 4065 4066 len = fputs(signline, tfp); 4067 error = errno; 4068 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0); 4069 if (len != strlen(signline)) { 4070 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error)); 4071 free(sign); 4072 return (-1); 4073 } 4074 4075 free(sign); 4076 4077 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt)); 4078 return (0); 4079 } 4080 4081 /* 4082 * slice is a basename not a full pathname 4083 */ 4084 static int 4085 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt) 4086 { 4087 int ret; 4088 char cmd[PATH_MAX]; 4089 char path[PATH_MAX]; 4090 struct stat sbuf; 4091 char *mntpt; 4092 filelist_t flist = {0}; 4093 char *fstype; 4094 char blkslice[PATH_MAX]; 4095 const char *fcn = "process_slice_common()"; 4096 4097 4098 ret = search_hash(mhp, slice, &mntpt); 4099 switch (ret) { 4100 case MH_MATCH: 4101 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1) 4102 return (-1); 4103 else 4104 return (0); 4105 case MH_NOMATCH: 4106 break; 4107 case MH_ERROR: 4108 default: 4109 return (-1); 4110 } 4111 4112 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice); 4113 if (stat(path, &sbuf) == -1) { 4114 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path)); 4115 return (0); 4116 } 4117 4118 /* Check if ufs */ 4119 (void) snprintf(cmd, sizeof (cmd), 4120 "/usr/sbin/fstyp /dev/rdsk/%s 2>/dev/null", 4121 slice); 4122 4123 if (exec_cmd(cmd, &flist) != 0) { 4124 if (bam_verbose) 4125 bam_print(FSTYP_FAILED, slice); 4126 return (0); 4127 } 4128 4129 if ((flist.head == NULL) || (flist.head != flist.tail)) { 4130 if (bam_verbose) 4131 bam_print(FSTYP_BAD, slice); 4132 filelist_free(&flist); 4133 return (0); 4134 } 4135 4136 fstype = strtok(flist.head->line, " \t\n"); 4137 if (fstype == NULL || strcmp(fstype, "ufs") != 0) { 4138 if (bam_verbose) 4139 bam_print(NOT_UFS_SLICE, slice, fstype); 4140 filelist_free(&flist); 4141 return (0); 4142 } 4143 4144 filelist_free(&flist); 4145 4146 /* 4147 * Since we are mounting the filesystem read-only, the 4148 * the last mount field of the superblock is unchanged 4149 * and does not need to be fixed up post-mount; 4150 */ 4151 4152 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s", 4153 slice); 4154 4155 (void) snprintf(cmd, sizeof (cmd), 4156 "/usr/sbin/mount -F ufs -o ro %s %s " 4157 "> /dev/null 2>&1", blkslice, tmpmnt); 4158 4159 if (exec_cmd(cmd, NULL) != 0) { 4160 if (bam_verbose) 4161 bam_print(MOUNT_FAILED, blkslice, "ufs"); 4162 return (0); 4163 } 4164 4165 ret = check_add_ufs_sign_to_list(tfp, tmpmnt); 4166 4167 (void) snprintf(cmd, sizeof (cmd), 4168 "/usr/sbin/umount -f %s > /dev/null 2>&1", 4169 tmpmnt); 4170 4171 if (exec_cmd(cmd, NULL) != 0) { 4172 bam_print(UMOUNT_FAILED, slice); 4173 return (0); 4174 } 4175 4176 return (ret); 4177 } 4178 4179 static int 4180 process_vtoc_slices( 4181 char *s0, 4182 struct vtoc *vtoc, 4183 FILE *tfp, 4184 mhash_t *mhp, 4185 char *tmpmnt) 4186 { 4187 int idx; 4188 char slice[PATH_MAX]; 4189 size_t len; 4190 char *cp; 4191 const char *fcn = "process_vtoc_slices()"; 4192 4193 len = strlen(s0); 4194 4195 assert(s0[len - 2] == 's' && s0[len - 1] == '0'); 4196 4197 s0[len - 1] = '\0'; 4198 4199 (void) strlcpy(slice, s0, sizeof (slice)); 4200 4201 s0[len - 1] = '0'; 4202 4203 cp = slice + len - 1; 4204 4205 for (idx = 0; idx < vtoc->v_nparts; idx++) { 4206 4207 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx); 4208 4209 if (vtoc->v_part[idx].p_size == 0) { 4210 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice)); 4211 continue; 4212 } 4213 4214 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */ 4215 switch (vtoc->v_part[idx].p_tag) { 4216 case V_SWAP: 4217 case V_USR: 4218 case V_BACKUP: 4219 case V_VAR: 4220 case V_HOME: 4221 case V_ALTSCTR: 4222 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice)); 4223 continue; 4224 default: 4225 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice)); 4226 break; 4227 } 4228 4229 /* skip unmountable and readonly slices */ 4230 switch (vtoc->v_part[idx].p_flag) { 4231 case V_UNMNT: 4232 case V_RONLY: 4233 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice)); 4234 continue; 4235 default: 4236 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice)); 4237 break; 4238 } 4239 4240 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) { 4241 return (-1); 4242 } 4243 } 4244 4245 return (0); 4246 } 4247 4248 static int 4249 process_efi_slices( 4250 char *s0, 4251 struct dk_gpt *efi, 4252 FILE *tfp, 4253 mhash_t *mhp, 4254 char *tmpmnt) 4255 { 4256 int idx; 4257 char slice[PATH_MAX]; 4258 size_t len; 4259 char *cp; 4260 const char *fcn = "process_efi_slices()"; 4261 4262 len = strlen(s0); 4263 4264 assert(s0[len - 2] == 's' && s0[len - 1] == '0'); 4265 4266 s0[len - 1] = '\0'; 4267 4268 (void) strlcpy(slice, s0, sizeof (slice)); 4269 4270 s0[len - 1] = '0'; 4271 4272 cp = slice + len - 1; 4273 4274 for (idx = 0; idx < efi->efi_nparts; idx++) { 4275 4276 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx); 4277 4278 if (efi->efi_parts[idx].p_size == 0) { 4279 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice)); 4280 continue; 4281 } 4282 4283 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */ 4284 switch (efi->efi_parts[idx].p_tag) { 4285 case V_SWAP: 4286 case V_USR: 4287 case V_BACKUP: 4288 case V_VAR: 4289 case V_HOME: 4290 case V_ALTSCTR: 4291 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice)); 4292 continue; 4293 default: 4294 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice)); 4295 break; 4296 } 4297 4298 /* skip unmountable and readonly slices */ 4299 switch (efi->efi_parts[idx].p_flag) { 4300 case V_UNMNT: 4301 case V_RONLY: 4302 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice)); 4303 continue; 4304 default: 4305 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice)); 4306 break; 4307 } 4308 4309 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) { 4310 return (-1); 4311 } 4312 } 4313 4314 return (0); 4315 } 4316 4317 /* 4318 * s0 is a basename not a full path 4319 */ 4320 static int 4321 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt) 4322 { 4323 struct vtoc vtoc; 4324 struct dk_gpt *efi; 4325 char s0path[PATH_MAX]; 4326 struct stat sbuf; 4327 int e_flag; 4328 int v_flag; 4329 int retval; 4330 int err; 4331 int fd; 4332 const char *fcn = "process_slice0()"; 4333 4334 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0); 4335 4336 if (stat(s0path, &sbuf) == -1) { 4337 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path)); 4338 return (0); 4339 } 4340 4341 fd = open(s0path, O_NONBLOCK|O_RDONLY); 4342 if (fd == -1) { 4343 bam_error(OPEN_FAIL, s0path, strerror(errno)); 4344 return (0); 4345 } 4346 4347 e_flag = v_flag = 0; 4348 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err; 4349 switch (retval) { 4350 case VT_EIO: 4351 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path)); 4352 break; 4353 case VT_EINVAL: 4354 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path)); 4355 break; 4356 case VT_ERROR: 4357 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path)); 4358 break; 4359 case VT_ENOTSUP: 4360 e_flag = 1; 4361 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path)); 4362 break; 4363 case 0: 4364 v_flag = 1; 4365 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path)); 4366 break; 4367 default: 4368 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path)); 4369 break; 4370 } 4371 4372 4373 if (e_flag) { 4374 e_flag = 0; 4375 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err; 4376 switch (retval) { 4377 case VT_EIO: 4378 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path)); 4379 break; 4380 case VT_EINVAL: 4381 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path)); 4382 break; 4383 case VT_ERROR: 4384 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path)); 4385 break; 4386 case VT_ENOTSUP: 4387 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path)); 4388 break; 4389 case 0: 4390 e_flag = 1; 4391 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path)); 4392 break; 4393 default: 4394 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path)); 4395 break; 4396 } 4397 } 4398 4399 (void) close(fd); 4400 4401 if (v_flag) { 4402 retval = process_vtoc_slices(s0, 4403 &vtoc, tfp, mhp, tmpmnt); 4404 } else if (e_flag) { 4405 retval = process_efi_slices(s0, 4406 efi, tfp, mhp, tmpmnt); 4407 } else { 4408 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path)); 4409 return (0); 4410 } 4411 4412 return (retval); 4413 } 4414 4415 /* 4416 * Find and create a list of all existing UFS boot signatures 4417 */ 4418 static int 4419 FindAllUfsSignatures(void) 4420 { 4421 mhash_t *mnttab_hash; 4422 DIR *dirp = NULL; 4423 struct dirent *dp; 4424 char tmpmnt[PATH_MAX]; 4425 char cmd[PATH_MAX]; 4426 struct stat sb; 4427 int fd; 4428 FILE *tfp; 4429 size_t len; 4430 int ret; 4431 int error; 4432 const char *fcn = "FindAllUfsSignatures()"; 4433 4434 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) { 4435 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST); 4436 return (0); 4437 } 4438 4439 fd = open(UFS_SIGNATURE_LIST".tmp", 4440 O_RDWR|O_CREAT|O_TRUNC, 0644); 4441 error = errno; 4442 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1); 4443 if (fd == -1) { 4444 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 4445 return (-1); 4446 } 4447 4448 ret = close(fd); 4449 error = errno; 4450 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1); 4451 if (ret == -1) { 4452 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 4453 strerror(error)); 4454 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4455 return (-1); 4456 } 4457 4458 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a"); 4459 error = errno; 4460 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL); 4461 if (tfp == NULL) { 4462 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 4463 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4464 return (-1); 4465 } 4466 4467 mnttab_hash = cache_mnttab(); 4468 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL); 4469 if (mnttab_hash == NULL) { 4470 (void) fclose(tfp); 4471 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4472 bam_error(CACHE_MNTTAB_FAIL, fcn); 4473 return (-1); 4474 } 4475 4476 (void) snprintf(tmpmnt, sizeof (tmpmnt), 4477 "/tmp/bootadm_ufs_sign_mnt.%d", getpid()); 4478 (void) unlink(tmpmnt); 4479 4480 ret = mkdirp(tmpmnt, 0755); 4481 error = errno; 4482 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1); 4483 if (ret == -1) { 4484 bam_error(MKDIR_FAILED, tmpmnt, strerror(error)); 4485 free_mnttab(mnttab_hash); 4486 (void) fclose(tfp); 4487 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4488 return (-1); 4489 } 4490 4491 dirp = opendir("/dev/rdsk"); 4492 error = errno; 4493 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL); 4494 if (dirp == NULL) { 4495 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error)); 4496 goto fail; 4497 } 4498 4499 while (dp = readdir(dirp)) { 4500 if (strcmp(dp->d_name, ".") == 0 || 4501 strcmp(dp->d_name, "..") == 0) 4502 continue; 4503 4504 /* 4505 * we only look for the s0 slice. This is guranteed to 4506 * have 's' at len - 2. 4507 */ 4508 len = strlen(dp->d_name); 4509 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') { 4510 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name)); 4511 continue; 4512 } 4513 4514 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt); 4515 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1); 4516 if (ret == -1) 4517 goto fail; 4518 } 4519 4520 (void) closedir(dirp); 4521 free_mnttab(mnttab_hash); 4522 (void) rmdir(tmpmnt); 4523 4524 ret = fclose(tfp); 4525 error = errno; 4526 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF); 4527 if (ret == EOF) { 4528 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 4529 strerror(error)); 4530 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4531 return (-1); 4532 } 4533 4534 /* We have a list of existing GRUB signatures. Sort it first */ 4535 (void) snprintf(cmd, sizeof (cmd), 4536 "/usr/bin/sort -u %s.tmp > %s.sorted", 4537 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST); 4538 4539 ret = exec_cmd(cmd, NULL); 4540 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1); 4541 if (ret != 0) { 4542 bam_error(GRUBSIGN_SORT_FAILED); 4543 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 4544 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4545 return (-1); 4546 } 4547 4548 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4549 4550 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST); 4551 error = errno; 4552 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1); 4553 if (ret == -1) { 4554 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error)); 4555 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 4556 return (-1); 4557 } 4558 4559 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) { 4560 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST)); 4561 } 4562 4563 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4564 return (0); 4565 4566 fail: 4567 if (dirp) 4568 (void) closedir(dirp); 4569 free_mnttab(mnttab_hash); 4570 (void) rmdir(tmpmnt); 4571 (void) fclose(tfp); 4572 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 4573 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 4574 return (-1); 4575 } 4576 4577 static char * 4578 create_ufs_sign(void) 4579 { 4580 struct stat sb; 4581 int signnum = -1; 4582 char tmpsign[MAXNAMELEN + 1]; 4583 char *numstr; 4584 int i; 4585 FILE *tfp; 4586 int ret; 4587 int error; 4588 const char *fcn = "create_ufs_sign()"; 4589 4590 bam_print(SEARCHING_UFS_SIGN); 4591 4592 ret = FindAllUfsSignatures(); 4593 INJECT_ERROR1("FIND_ALL_UFS", ret = -1); 4594 if (ret == -1) { 4595 bam_error(ERR_FIND_UFS_SIGN); 4596 return (NULL); 4597 } 4598 4599 /* Make sure the list exists and is owned by root */ 4600 INJECT_ERROR1("SIGNLIST_NOT_CREATED", 4601 (void) unlink(UFS_SIGNATURE_LIST)); 4602 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) { 4603 (void) unlink(UFS_SIGNATURE_LIST); 4604 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST); 4605 return (NULL); 4606 } 4607 4608 if (sb.st_size == 0) { 4609 bam_print(GRUBSIGN_UFS_NONE); 4610 i = 0; 4611 goto found; 4612 } 4613 4614 /* The signature list was sorted when it was created */ 4615 tfp = fopen(UFS_SIGNATURE_LIST, "r"); 4616 error = errno; 4617 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL); 4618 if (tfp == NULL) { 4619 bam_error(UFS_SIGNATURE_LIST_OPENERR, 4620 UFS_SIGNATURE_LIST, strerror(error)); 4621 (void) unlink(UFS_SIGNATURE_LIST); 4622 return (NULL); 4623 } 4624 4625 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) { 4626 4627 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 4628 strlen(GRUBSIGN_UFS_PREFIX)) != 0) { 4629 (void) fclose(tfp); 4630 (void) unlink(UFS_SIGNATURE_LIST); 4631 bam_error(UFS_BADSIGN, tmpsign); 4632 return (NULL); 4633 } 4634 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX); 4635 4636 if (numstr[0] == '\0' || !isdigit(numstr[0])) { 4637 (void) fclose(tfp); 4638 (void) unlink(UFS_SIGNATURE_LIST); 4639 bam_error(UFS_BADSIGN, tmpsign); 4640 return (NULL); 4641 } 4642 4643 signnum = atoi(numstr); 4644 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1); 4645 if (signnum < 0) { 4646 (void) fclose(tfp); 4647 (void) unlink(UFS_SIGNATURE_LIST); 4648 bam_error(UFS_BADSIGN, tmpsign); 4649 return (NULL); 4650 } 4651 4652 if (i != signnum) { 4653 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i)); 4654 break; 4655 } 4656 } 4657 4658 (void) fclose(tfp); 4659 4660 found: 4661 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i); 4662 4663 /* add the ufs signature to the /var/run list of signatures */ 4664 ret = ufs_add_to_sign_list(tmpsign); 4665 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1); 4666 if (ret == -1) { 4667 (void) unlink(UFS_SIGNATURE_LIST); 4668 bam_error(FAILED_ADD_SIGNLIST, tmpsign); 4669 return (NULL); 4670 } 4671 4672 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4673 4674 return (s_strdup(tmpsign)); 4675 } 4676 4677 static char * 4678 get_fstype(char *osroot) 4679 { 4680 FILE *mntfp; 4681 struct mnttab mp = {0}; 4682 struct mnttab mpref = {0}; 4683 int error; 4684 int ret; 4685 const char *fcn = "get_fstype()"; 4686 4687 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL); 4688 if (osroot == NULL) { 4689 bam_error(GET_FSTYPE_ARGS); 4690 return (NULL); 4691 } 4692 4693 mntfp = fopen(MNTTAB, "r"); 4694 error = errno; 4695 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL); 4696 if (mntfp == NULL) { 4697 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 4698 return (NULL); 4699 } 4700 4701 if (*osroot == '\0') 4702 mpref.mnt_mountp = "/"; 4703 else 4704 mpref.mnt_mountp = osroot; 4705 4706 ret = getmntany(mntfp, &mp, &mpref); 4707 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1); 4708 if (ret != 0) { 4709 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB); 4710 (void) fclose(mntfp); 4711 return (NULL); 4712 } 4713 (void) fclose(mntfp); 4714 4715 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL); 4716 if (mp.mnt_fstype == NULL) { 4717 bam_error(MNTTAB_FSTYPE_NULL, osroot); 4718 return (NULL); 4719 } 4720 4721 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4722 4723 return (s_strdup(mp.mnt_fstype)); 4724 } 4725 4726 static char * 4727 create_zfs_sign(char *osdev) 4728 { 4729 char tmpsign[PATH_MAX]; 4730 char *pool; 4731 const char *fcn = "create_zfs_sign()"; 4732 4733 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev)); 4734 4735 /* 4736 * First find the pool name 4737 */ 4738 pool = get_pool(osdev); 4739 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL); 4740 if (pool == NULL) { 4741 bam_error(GET_POOL_FAILED, osdev); 4742 return (NULL); 4743 } 4744 4745 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool); 4746 4747 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign)); 4748 4749 free(pool); 4750 4751 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4752 4753 return (s_strdup(tmpsign)); 4754 } 4755 4756 static char * 4757 create_new_sign(char *osdev, char *fstype) 4758 { 4759 char *sign; 4760 const char *fcn = "create_new_sign()"; 4761 4762 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs"); 4763 4764 if (strcmp(fstype, "zfs") == 0) { 4765 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn)); 4766 sign = create_zfs_sign(osdev); 4767 } else if (strcmp(fstype, "ufs") == 0) { 4768 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn)); 4769 sign = create_ufs_sign(); 4770 } else { 4771 bam_error(GRUBSIGN_NOTSUP, fstype); 4772 sign = NULL; 4773 } 4774 4775 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>")); 4776 return (sign); 4777 } 4778 4779 static int 4780 set_backup_common(char *mntpt, char *sign) 4781 { 4782 FILE *bfp; 4783 char backup[PATH_MAX]; 4784 char tmpsign[PATH_MAX]; 4785 int error; 4786 char *bdir; 4787 char *backup_dup; 4788 struct stat sb; 4789 int ret; 4790 const char *fcn = "set_backup_common()"; 4791 4792 (void) snprintf(backup, sizeof (backup), "%s%s", 4793 mntpt, GRUBSIGN_BACKUP); 4794 4795 /* First read the backup */ 4796 bfp = fopen(backup, "r"); 4797 if (bfp != NULL) { 4798 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) { 4799 if (strcmp(tmpsign, sign) == 0) { 4800 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign)); 4801 (void) fclose(bfp); 4802 return (0); 4803 } 4804 } 4805 (void) fclose(bfp); 4806 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign)); 4807 } else { 4808 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup)); 4809 } 4810 4811 /* 4812 * Didn't find the correct signature. First create 4813 * the directory if necessary. 4814 */ 4815 4816 /* dirname() modifies its argument so dup it */ 4817 backup_dup = s_strdup(backup); 4818 bdir = dirname(backup_dup); 4819 assert(bdir); 4820 4821 ret = stat(bdir, &sb); 4822 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1); 4823 if (ret == -1) { 4824 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir)); 4825 ret = mkdirp(bdir, 0755); 4826 error = errno; 4827 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1); 4828 if (ret == -1) { 4829 bam_error(GRUBSIGN_BACKUP_MKDIRERR, 4830 GRUBSIGN_BACKUP, strerror(error)); 4831 free(backup_dup); 4832 return (-1); 4833 } 4834 } 4835 free(backup_dup); 4836 4837 /* 4838 * Open the backup in append mode to add the correct 4839 * signature; 4840 */ 4841 bfp = fopen(backup, "a"); 4842 error = errno; 4843 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL); 4844 if (bfp == NULL) { 4845 bam_error(GRUBSIGN_BACKUP_OPENERR, 4846 GRUBSIGN_BACKUP, strerror(error)); 4847 return (-1); 4848 } 4849 4850 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign); 4851 4852 ret = fputs(tmpsign, bfp); 4853 error = errno; 4854 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0); 4855 if (ret != strlen(tmpsign)) { 4856 bam_error(GRUBSIGN_BACKUP_WRITEERR, 4857 GRUBSIGN_BACKUP, strerror(error)); 4858 (void) fclose(bfp); 4859 return (-1); 4860 } 4861 4862 (void) fclose(bfp); 4863 4864 if (bam_verbose) 4865 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP); 4866 4867 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4868 4869 return (0); 4870 } 4871 4872 static int 4873 set_backup_ufs(char *osroot, char *sign) 4874 { 4875 const char *fcn = "set_backup_ufs()"; 4876 4877 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign)); 4878 return (set_backup_common(osroot, sign)); 4879 } 4880 4881 static int 4882 set_backup_zfs(char *osdev, char *sign) 4883 { 4884 char *pool; 4885 char *mntpt; 4886 zfs_mnted_t mnted; 4887 int ret; 4888 const char *fcn = "set_backup_zfs()"; 4889 4890 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign)); 4891 4892 pool = get_pool(osdev); 4893 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL); 4894 if (pool == NULL) { 4895 bam_error(GET_POOL_FAILED, osdev); 4896 return (-1); 4897 } 4898 4899 mntpt = mount_top_dataset(pool, &mnted); 4900 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL); 4901 if (mntpt == NULL) { 4902 bam_error(FAIL_MNT_TOP_DATASET, pool); 4903 free(pool); 4904 return (-1); 4905 } 4906 4907 ret = set_backup_common(mntpt, sign); 4908 4909 (void) umount_top_dataset(pool, mnted, mntpt); 4910 4911 free(pool); 4912 4913 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1); 4914 if (ret == 0) { 4915 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4916 } else { 4917 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 4918 } 4919 4920 return (ret); 4921 } 4922 4923 static int 4924 set_backup(char *osroot, char *osdev, char *sign, char *fstype) 4925 { 4926 const char *fcn = "set_backup()"; 4927 int ret; 4928 4929 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs"); 4930 4931 if (strcmp(fstype, "ufs") == 0) { 4932 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn)); 4933 ret = set_backup_ufs(osroot, sign); 4934 } else if (strcmp(fstype, "zfs") == 0) { 4935 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn)); 4936 ret = set_backup_zfs(osdev, sign); 4937 } else { 4938 bam_error(GRUBSIGN_NOTSUP, fstype); 4939 ret = -1; 4940 } 4941 4942 if (ret == 0) { 4943 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4944 } else { 4945 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 4946 } 4947 4948 return (ret); 4949 } 4950 4951 static int 4952 set_primary_common(char *mntpt, char *sign) 4953 { 4954 char signfile[PATH_MAX]; 4955 char signdir[PATH_MAX]; 4956 struct stat sb; 4957 int fd; 4958 int error; 4959 int ret; 4960 const char *fcn = "set_primary_common()"; 4961 4962 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s", 4963 mntpt, GRUBSIGN_DIR, sign); 4964 4965 if (stat(signfile, &sb) != -1) { 4966 if (bam_verbose) 4967 bam_print(PRIMARY_SIGN_EXISTS, sign); 4968 return (0); 4969 } else { 4970 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile)); 4971 } 4972 4973 (void) snprintf(signdir, sizeof (signdir), "%s/%s", 4974 mntpt, GRUBSIGN_DIR); 4975 4976 if (stat(signdir, &sb) == -1) { 4977 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir)); 4978 ret = mkdirp(signdir, 0755); 4979 error = errno; 4980 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1); 4981 if (ret == -1) { 4982 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno)); 4983 return (-1); 4984 } 4985 } 4986 4987 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444); 4988 error = errno; 4989 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1); 4990 if (fd == -1) { 4991 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error)); 4992 return (-1); 4993 } 4994 4995 ret = fsync(fd); 4996 error = errno; 4997 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1); 4998 if (ret != 0) { 4999 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error)); 5000 } 5001 5002 (void) close(fd); 5003 5004 if (bam_verbose) 5005 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile); 5006 5007 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5008 5009 return (0); 5010 } 5011 5012 static int 5013 set_primary_ufs(char *osroot, char *sign) 5014 { 5015 const char *fcn = "set_primary_ufs()"; 5016 5017 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign)); 5018 return (set_primary_common(osroot, sign)); 5019 } 5020 5021 static int 5022 set_primary_zfs(char *osdev, char *sign) 5023 { 5024 char *pool; 5025 char *mntpt; 5026 zfs_mnted_t mnted; 5027 int ret; 5028 const char *fcn = "set_primary_zfs()"; 5029 5030 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign)); 5031 5032 pool = get_pool(osdev); 5033 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL); 5034 if (pool == NULL) { 5035 bam_error(GET_POOL_FAILED, osdev); 5036 return (-1); 5037 } 5038 5039 /* Pool name must exist in the sign */ 5040 ret = (strstr(sign, pool) != NULL); 5041 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0); 5042 if (ret == 0) { 5043 bam_error(POOL_SIGN_INCOMPAT, pool, sign); 5044 free(pool); 5045 return (-1); 5046 } 5047 5048 mntpt = mount_top_dataset(pool, &mnted); 5049 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL); 5050 if (mntpt == NULL) { 5051 bam_error(FAIL_MNT_TOP_DATASET, pool); 5052 free(pool); 5053 return (-1); 5054 } 5055 5056 ret = set_primary_common(mntpt, sign); 5057 5058 (void) umount_top_dataset(pool, mnted, mntpt); 5059 5060 free(pool); 5061 5062 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1); 5063 if (ret == 0) { 5064 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5065 } else { 5066 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5067 } 5068 5069 return (ret); 5070 } 5071 5072 static int 5073 set_primary(char *osroot, char *osdev, char *sign, char *fstype) 5074 { 5075 const char *fcn = "set_primary()"; 5076 int ret; 5077 5078 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs"); 5079 if (strcmp(fstype, "ufs") == 0) { 5080 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn)); 5081 ret = set_primary_ufs(osroot, sign); 5082 } else if (strcmp(fstype, "zfs") == 0) { 5083 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn)); 5084 ret = set_primary_zfs(osdev, sign); 5085 } else { 5086 bam_error(GRUBSIGN_NOTSUP, fstype); 5087 ret = -1; 5088 } 5089 5090 if (ret == 0) { 5091 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5092 } else { 5093 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5094 } 5095 5096 return (ret); 5097 } 5098 5099 static int 5100 ufs_add_to_sign_list(char *sign) 5101 { 5102 FILE *tfp; 5103 char signline[MAXNAMELEN]; 5104 char cmd[PATH_MAX]; 5105 int ret; 5106 int error; 5107 const char *fcn = "ufs_add_to_sign_list()"; 5108 5109 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5"); 5110 if (strncmp(sign, GRUBSIGN_UFS_PREFIX, 5111 strlen(GRUBSIGN_UFS_PREFIX)) != 0) { 5112 bam_error(INVALID_UFS_SIGN, sign); 5113 (void) unlink(UFS_SIGNATURE_LIST); 5114 return (-1); 5115 } 5116 5117 /* 5118 * most failures in this routine are not a fatal error 5119 * We simply unlink the /var/run file and continue 5120 */ 5121 5122 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp"); 5123 error = errno; 5124 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1); 5125 if (ret == -1) { 5126 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp", 5127 strerror(error)); 5128 (void) unlink(UFS_SIGNATURE_LIST); 5129 return (0); 5130 } 5131 5132 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a"); 5133 error = errno; 5134 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL); 5135 if (tfp == NULL) { 5136 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 5137 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 5138 return (0); 5139 } 5140 5141 (void) snprintf(signline, sizeof (signline), "%s\n", sign); 5142 5143 ret = fputs(signline, tfp); 5144 error = errno; 5145 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0); 5146 if (ret != strlen(signline)) { 5147 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error)); 5148 (void) fclose(tfp); 5149 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 5150 return (0); 5151 } 5152 5153 ret = fclose(tfp); 5154 error = errno; 5155 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF); 5156 if (ret == EOF) { 5157 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 5158 strerror(error)); 5159 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 5160 return (0); 5161 } 5162 5163 /* Sort the list again */ 5164 (void) snprintf(cmd, sizeof (cmd), 5165 "/usr/bin/sort -u %s.tmp > %s.sorted", 5166 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST); 5167 5168 ret = exec_cmd(cmd, NULL); 5169 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1); 5170 if (ret != 0) { 5171 bam_error(GRUBSIGN_SORT_FAILED); 5172 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 5173 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 5174 return (0); 5175 } 5176 5177 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 5178 5179 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST); 5180 error = errno; 5181 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1); 5182 if (ret == -1) { 5183 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error)); 5184 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 5185 return (0); 5186 } 5187 5188 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5189 5190 return (0); 5191 } 5192 5193 static int 5194 set_signature(char *osroot, char *osdev, char *sign, char *fstype) 5195 { 5196 int ret; 5197 const char *fcn = "set_signature()"; 5198 5199 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype)); 5200 5201 ret = set_backup(osroot, osdev, sign, fstype); 5202 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1); 5203 if (ret == -1) { 5204 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5205 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev); 5206 return (-1); 5207 } 5208 5209 ret = set_primary(osroot, osdev, sign, fstype); 5210 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1); 5211 5212 if (ret == 0) { 5213 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5214 } else { 5215 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5216 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev); 5217 5218 } 5219 return (ret); 5220 } 5221 5222 char * 5223 get_grubsign(char *osroot, char *osdev) 5224 { 5225 char *grubsign; /* (<sign>,#,#) */ 5226 char *slice; 5227 int fdiskpart; 5228 char *sign; 5229 char *fstype; 5230 int ret; 5231 const char *fcn = "get_grubsign()"; 5232 5233 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev)); 5234 5235 fstype = get_fstype(osroot); 5236 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL); 5237 if (fstype == NULL) { 5238 bam_error(GET_FSTYPE_FAILED, osroot); 5239 return (NULL); 5240 } 5241 5242 sign = find_existing_sign(osroot, osdev, fstype); 5243 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL); 5244 if (sign == NULL) { 5245 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev)); 5246 sign = create_new_sign(osdev, fstype); 5247 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL); 5248 if (sign == NULL) { 5249 bam_error(GRUBSIGN_CREATE_FAIL, osdev); 5250 free(fstype); 5251 return (NULL); 5252 } 5253 } 5254 5255 ret = set_signature(osroot, osdev, sign, fstype); 5256 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1); 5257 if (ret == -1) { 5258 bam_error(GRUBSIGN_WRITE_FAIL, osdev); 5259 free(sign); 5260 free(fstype); 5261 (void) unlink(UFS_SIGNATURE_LIST); 5262 return (NULL); 5263 } 5264 5265 free(fstype); 5266 5267 if (bam_verbose) 5268 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev); 5269 5270 fdiskpart = get_partition(osdev); 5271 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1); 5272 if (fdiskpart == -1) { 5273 bam_error(FDISKPART_FAIL, osdev); 5274 free(sign); 5275 return (NULL); 5276 } 5277 5278 slice = strrchr(osdev, 's'); 5279 5280 grubsign = s_calloc(1, MAXNAMELEN + 10); 5281 if (slice) { 5282 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)", 5283 sign, fdiskpart, slice[1] + 'a' - '0'); 5284 } else 5285 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)", 5286 sign, fdiskpart); 5287 5288 free(sign); 5289 5290 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign)); 5291 5292 return (grubsign); 5293 } 5294 5295 static char * 5296 get_title(char *rootdir) 5297 { 5298 static char title[80]; 5299 char *cp = NULL; 5300 char release[PATH_MAX]; 5301 FILE *fp; 5302 const char *fcn = "get_title()"; 5303 5304 /* open the /etc/release file */ 5305 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir); 5306 5307 fp = fopen(release, "r"); 5308 if (fp == NULL) { 5309 bam_error(OPEN_FAIL, release, strerror(errno)); 5310 cp = NULL; 5311 goto out; 5312 } 5313 5314 while (s_fgets(title, sizeof (title), fp) != NULL) { 5315 cp = strstr(title, "Solaris"); 5316 if (cp) 5317 break; 5318 } 5319 (void) fclose(fp); 5320 5321 out: 5322 cp = cp ? cp : "Solaris"; 5323 5324 BAM_DPRINTF((D_GET_TITLE, fcn, cp)); 5325 5326 return (cp); 5327 } 5328 5329 char * 5330 get_special(char *mountp) 5331 { 5332 FILE *mntfp; 5333 struct mnttab mp = {0}; 5334 struct mnttab mpref = {0}; 5335 int error; 5336 int ret; 5337 const char *fcn = "get_special()"; 5338 5339 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL); 5340 if (mountp == NULL) { 5341 bam_error(GET_SPECIAL_NULL_MNTPT); 5342 return (NULL); 5343 } 5344 5345 mntfp = fopen(MNTTAB, "r"); 5346 error = errno; 5347 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL); 5348 if (mntfp == NULL) { 5349 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 5350 return (NULL); 5351 } 5352 5353 if (*mountp == '\0') 5354 mpref.mnt_mountp = "/"; 5355 else 5356 mpref.mnt_mountp = mountp; 5357 5358 ret = getmntany(mntfp, &mp, &mpref); 5359 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1); 5360 if (ret != 0) { 5361 (void) fclose(mntfp); 5362 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp)); 5363 return (NULL); 5364 } 5365 (void) fclose(mntfp); 5366 5367 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special)); 5368 5369 return (s_strdup(mp.mnt_special)); 5370 } 5371 5372 static void 5373 free_physarray(char **physarray, int n) 5374 { 5375 int i; 5376 const char *fcn = "free_physarray()"; 5377 5378 assert(physarray); 5379 assert(n); 5380 5381 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n)); 5382 5383 for (i = 0; i < n; i++) { 5384 free(physarray[i]); 5385 } 5386 free(physarray); 5387 5388 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5389 } 5390 5391 static int 5392 zfs_get_physical(char *special, char ***physarray, int *n) 5393 { 5394 char sdup[PATH_MAX]; 5395 char cmd[PATH_MAX]; 5396 char dsk[PATH_MAX]; 5397 char *pool; 5398 filelist_t flist = {0}; 5399 line_t *lp; 5400 line_t *startlp; 5401 char *comp1; 5402 int i; 5403 int ret; 5404 const char *fcn = "zfs_get_physical()"; 5405 5406 assert(special); 5407 5408 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special)); 5409 5410 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo"); 5411 if (special[0] == '/') { 5412 bam_error(INVALID_ZFS_SPECIAL, special); 5413 return (-1); 5414 } 5415 5416 (void) strlcpy(sdup, special, sizeof (sdup)); 5417 5418 pool = strtok(sdup, "/"); 5419 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL); 5420 if (pool == NULL) { 5421 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special); 5422 return (-1); 5423 } 5424 5425 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool); 5426 5427 ret = exec_cmd(cmd, &flist); 5428 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1); 5429 if (ret != 0) { 5430 bam_error(ZFS_GET_POOL_STATUS, pool); 5431 return (-1); 5432 } 5433 5434 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL); 5435 if (flist.head == NULL) { 5436 bam_error(BAD_ZPOOL_STATUS, pool); 5437 filelist_free(&flist); 5438 return (-1); 5439 } 5440 5441 for (lp = flist.head; lp; lp = lp->next) { 5442 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line)); 5443 comp1 = strtok(lp->line, " \t"); 5444 if (comp1 == NULL) { 5445 free(lp->line); 5446 lp->line = NULL; 5447 } else { 5448 comp1 = s_strdup(comp1); 5449 free(lp->line); 5450 lp->line = comp1; 5451 } 5452 } 5453 5454 for (lp = flist.head; lp; lp = lp->next) { 5455 if (lp->line == NULL) 5456 continue; 5457 if (strcmp(lp->line, pool) == 0) { 5458 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool)); 5459 break; 5460 } 5461 } 5462 5463 if (lp == NULL) { 5464 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool); 5465 filelist_free(&flist); 5466 return (-1); 5467 } 5468 5469 startlp = lp->next; 5470 for (i = 0, lp = startlp; lp; lp = lp->next) { 5471 if (lp->line == NULL) 5472 continue; 5473 if (strcmp(lp->line, "mirror") == 0) 5474 continue; 5475 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0) 5476 break; 5477 i++; 5478 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i)); 5479 } 5480 5481 if (i == 0) { 5482 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool); 5483 filelist_free(&flist); 5484 return (-1); 5485 } 5486 5487 *n = i; 5488 *physarray = s_calloc(*n, sizeof (char *)); 5489 for (i = 0, lp = startlp; lp; lp = lp->next) { 5490 if (lp->line == NULL) 5491 continue; 5492 if (strcmp(lp->line, "mirror") == 0) 5493 continue; 5494 if (strcmp(lp->line, "errors:") == 0) 5495 break; 5496 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 5497 strncmp(lp->line, "/dev/rdsk/", 5498 strlen("/dev/rdsk/")) != 0) { 5499 (void) snprintf(dsk, sizeof (dsk), "/dev/dsk/%s", 5500 lp->line); 5501 } else { 5502 (void) strlcpy(dsk, lp->line, sizeof (dsk)); 5503 } 5504 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool)); 5505 (*physarray)[i++] = s_strdup(dsk); 5506 } 5507 5508 assert(i == *n); 5509 5510 filelist_free(&flist); 5511 5512 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5513 return (0); 5514 } 5515 5516 static int 5517 ufs_get_physical(char *special, char ***physarray, int *n) 5518 { 5519 char cmd[PATH_MAX]; 5520 char *shortname; 5521 filelist_t flist = {0}; 5522 char *meta; 5523 char *type; 5524 char *comp1; 5525 char *comp2; 5526 char *comp3; 5527 char *comp4; 5528 int i; 5529 line_t *lp; 5530 int ret; 5531 const char *fcn = "ufs_get_physical()"; 5532 5533 assert(special); 5534 5535 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special)); 5536 5537 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) { 5538 bam_error(UFS_GET_PHYS_NOT_SVM, special); 5539 return (-1); 5540 } 5541 5542 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) { 5543 shortname = special + strlen("/dev/md/dsk/"); 5544 } else if (strncmp(special, "/dev/md/rdsk/", 5545 strlen("/dev/md/rdsk/")) == 0) { 5546 shortname = special + strlen("/dev/md/rdsk"); 5547 } else { 5548 bam_error(UFS_GET_PHYS_INVALID_SVM, special); 5549 return (-1); 5550 } 5551 5552 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname)); 5553 5554 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname); 5555 5556 ret = exec_cmd(cmd, &flist); 5557 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1); 5558 if (ret != 0) { 5559 bam_error(UFS_SVM_METASTAT_ERR, shortname); 5560 return (-1); 5561 } 5562 5563 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL); 5564 if (flist.head == NULL) { 5565 bam_error(BAD_UFS_SVM_METASTAT, shortname); 5566 filelist_free(&flist); 5567 return (-1); 5568 } 5569 5570 /* 5571 * Check if not a mirror. We only parse a single metadevice 5572 * if not a mirror 5573 */ 5574 meta = strtok(flist.head->line, " \t"); 5575 type = strtok(NULL, " \t"); 5576 if (meta == NULL || type == NULL) { 5577 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname); 5578 filelist_free(&flist); 5579 return (-1); 5580 } 5581 if (strcmp(type, "-m") != 0) { 5582 comp1 = strtok(NULL, " \t"); 5583 comp2 = strtok(NULL, " \t"); 5584 if (comp1 == NULL || comp2 != NULL) { 5585 bam_error(INVALID_UFS_SVM_METASTAT, shortname); 5586 filelist_free(&flist); 5587 return (-1); 5588 } 5589 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname)); 5590 *physarray = s_calloc(1, sizeof (char *)); 5591 (*physarray)[0] = s_strdup(comp1); 5592 *n = 1; 5593 filelist_free(&flist); 5594 return (0); 5595 } 5596 5597 /* 5598 * Okay we have a mirror. Everything after the first line 5599 * is a submirror 5600 */ 5601 for (i = 0, lp = flist.head->next; lp; lp = lp->next) { 5602 if (strstr(lp->line, "/dev/dsk/") == NULL && 5603 strstr(lp->line, "/dev/rdsk/") == NULL) { 5604 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname); 5605 filelist_free(&flist); 5606 return (-1); 5607 } 5608 i++; 5609 } 5610 5611 *physarray = s_calloc(i, sizeof (char *)); 5612 *n = i; 5613 5614 for (i = 0, lp = flist.head->next; lp; lp = lp->next) { 5615 comp1 = strtok(lp->line, " \t"); 5616 comp2 = strtok(NULL, " \t"); 5617 comp3 = strtok(NULL, " \t"); 5618 comp4 = strtok(NULL, " \t"); 5619 5620 if (comp3 == NULL || comp4 == NULL || 5621 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 5622 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) { 5623 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname); 5624 filelist_free(&flist); 5625 free_physarray(*physarray, *n); 5626 return (-1); 5627 } 5628 5629 (*physarray)[i++] = s_strdup(comp4); 5630 } 5631 5632 assert(i == *n); 5633 5634 filelist_free(&flist); 5635 5636 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5637 return (0); 5638 } 5639 5640 static int 5641 get_physical(char *menu_root, char ***physarray, int *n) 5642 { 5643 char *special; 5644 int ret; 5645 const char *fcn = "get_physical()"; 5646 5647 assert(menu_root); 5648 assert(physarray); 5649 assert(n); 5650 5651 *physarray = NULL; 5652 *n = 0; 5653 5654 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root)); 5655 5656 /* First get the device special file from /etc/mnttab */ 5657 special = get_special(menu_root); 5658 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL); 5659 if (special == NULL) { 5660 bam_error(GET_SPECIAL_NULL, menu_root); 5661 return (-1); 5662 } 5663 5664 /* If already a physical device nothing to do */ 5665 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 || 5666 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) { 5667 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special)); 5668 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5669 *physarray = s_calloc(1, sizeof (char *)); 5670 (*physarray)[0] = special; 5671 *n = 1; 5672 return (0); 5673 } 5674 5675 if (is_zfs(menu_root)) { 5676 ret = zfs_get_physical(special, physarray, n); 5677 } else if (is_ufs(menu_root)) { 5678 ret = ufs_get_physical(special, physarray, n); 5679 } else { 5680 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special); 5681 ret = -1; 5682 } 5683 5684 free(special); 5685 5686 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1); 5687 if (ret == -1) { 5688 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5689 } else { 5690 int i; 5691 assert (*n > 0); 5692 for (i = 0; i < *n; i++) { 5693 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i])); 5694 } 5695 } 5696 5697 return (ret); 5698 } 5699 5700 static int 5701 is_bootdisk(char *osroot, char *physical) 5702 { 5703 int ret; 5704 char *grubroot; 5705 char *bootp; 5706 const char *fcn = "is_bootdisk()"; 5707 5708 assert(osroot); 5709 assert(physical); 5710 5711 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical)); 5712 5713 bootp = strstr(physical, "p0:boot"); 5714 if (bootp) 5715 *bootp = '\0'; 5716 /* 5717 * We just want the BIOS mapping for menu disk. 5718 * Don't pass menu_root to get_grubroot() as the 5719 * check that it is used for is not relevant here. 5720 * The osroot is immaterial as well - it is only used to 5721 * to find create_diskmap script. Everything hinges on 5722 * "physical" 5723 */ 5724 grubroot = get_grubroot(osroot, physical, NULL); 5725 5726 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL); 5727 if (grubroot == NULL) { 5728 bam_error(NO_GRUBROOT_FOR_DISK, fcn, physical); 5729 return (0); 5730 } 5731 ret = grubroot[3] == '0'; 5732 free(grubroot); 5733 5734 BAM_DPRINTF((D_RETURN_RET, fcn, ret)); 5735 5736 return (ret); 5737 } 5738 5739 /* 5740 * Check if menu is on the boot device 5741 * Return 0 (false) on error 5742 */ 5743 static int 5744 menu_on_bootdisk(char *osroot, char *menu_root) 5745 { 5746 char **physarray; 5747 int ret; 5748 int n; 5749 int i; 5750 int on_bootdisk; 5751 const char *fcn = "menu_on_bootdisk()"; 5752 5753 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 5754 5755 ret = get_physical(menu_root, &physarray, &n); 5756 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1); 5757 if (ret != 0) { 5758 bam_error(GET_PHYSICAL_MENU_NULL, menu_root); 5759 return (0); 5760 } 5761 5762 assert(physarray); 5763 assert(n > 0); 5764 5765 on_bootdisk = 0; 5766 for (i = 0; i < n; i++) { 5767 assert(strncmp(physarray[i], "/dev/dsk/", 5768 strlen("/dev/dsk/")) == 0 || 5769 strncmp(physarray[i], "/dev/rdsk/", 5770 strlen("/dev/rdsk/")) == 0); 5771 5772 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i])); 5773 if (is_bootdisk(osroot, physarray[i])) { 5774 on_bootdisk = 1; 5775 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i])); 5776 } 5777 } 5778 5779 free_physarray(physarray, n); 5780 5781 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1); 5782 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0); 5783 if (on_bootdisk) { 5784 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 5785 } else { 5786 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5787 } 5788 5789 return (on_bootdisk); 5790 } 5791 5792 void 5793 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp) 5794 { 5795 const char *fcn = "bam_add_line()"; 5796 5797 assert(mp); 5798 assert(entry); 5799 assert(prev); 5800 assert(lp); 5801 5802 lp->next = prev->next; 5803 if (prev->next) { 5804 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn)); 5805 prev->next->prev = lp; 5806 } else { 5807 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn)); 5808 } 5809 prev->next = lp; 5810 lp->prev = prev; 5811 5812 if (entry->end == prev) { 5813 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn)); 5814 entry->end = lp; 5815 } 5816 if (mp->end == prev) { 5817 assert(lp->next == NULL); 5818 mp->end = lp; 5819 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn)); 5820 } 5821 } 5822 5823 /* 5824 * look for matching bootadm entry with specified parameters 5825 * Here are the rules (based on existing usage): 5826 * - If title is specified, match on title only 5827 * - Else, match on root/findroot, kernel, and module. 5828 * Note that, if root_opt is non-zero, the absence of 5829 * root line is considered a match. 5830 */ 5831 static entry_t * 5832 find_boot_entry( 5833 menu_t *mp, 5834 char *title, 5835 char *kernel, 5836 char *findroot, 5837 char *root, 5838 char *module, 5839 int root_opt, 5840 int *entry_num) 5841 { 5842 int i; 5843 line_t *lp; 5844 entry_t *ent; 5845 const char *fcn = "find_boot_entry()"; 5846 5847 if (entry_num) 5848 *entry_num = BAM_ERROR; 5849 5850 /* find matching entry */ 5851 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) { 5852 lp = ent->start; 5853 5854 /* first line of entry must be bootadm comment */ 5855 lp = ent->start; 5856 if (lp->flags != BAM_COMMENT || 5857 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) { 5858 continue; 5859 } 5860 5861 /* advance to title line */ 5862 lp = lp->next; 5863 if (title) { 5864 if (lp->flags == BAM_TITLE && lp->arg && 5865 strcmp(lp->arg, title) == 0) { 5866 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title)); 5867 break; 5868 } 5869 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg)); 5870 continue; /* check title only */ 5871 } 5872 5873 lp = lp->next; /* advance to root line */ 5874 if (lp == NULL) { 5875 continue; 5876 } else if (strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) { 5877 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT", 5878 findroot = NULL); 5879 if (findroot == NULL) { 5880 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL, 5881 fcn, lp->arg)); 5882 continue; 5883 } 5884 /* findroot command found, try match */ 5885 if (strcmp(lp->arg, findroot) != 0) { 5886 BAM_DPRINTF((D_NOMATCH_FINDROOT, 5887 fcn, findroot, lp->arg)); 5888 continue; 5889 } 5890 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot)); 5891 lp = lp->next; /* advance to kernel line */ 5892 } else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 5893 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL); 5894 if (root == NULL) { 5895 BAM_DPRINTF((D_NOMATCH_ROOT_NULL, 5896 fcn, lp->arg)); 5897 continue; 5898 } 5899 /* root cmd found, try match */ 5900 if (strcmp(lp->arg, root) != 0) { 5901 BAM_DPRINTF((D_NOMATCH_ROOT, 5902 fcn, root, lp->arg)); 5903 continue; 5904 } 5905 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root)); 5906 lp = lp->next; /* advance to kernel line */ 5907 } else { 5908 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO", 5909 root_opt = 0); 5910 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES", 5911 root_opt = 1); 5912 /* no root command, see if root is optional */ 5913 if (root_opt == 0) { 5914 BAM_DPRINTF((D_NO_ROOT_OPT, fcn)); 5915 continue; 5916 } 5917 BAM_DPRINTF((D_ROOT_OPT, fcn)); 5918 } 5919 5920 if (lp == NULL || lp->next == NULL) { 5921 continue; 5922 } 5923 5924 if (kernel && 5925 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) { 5926 continue; 5927 } 5928 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg)); 5929 5930 /* 5931 * Check for matching module entry (failsafe or normal). 5932 * If it fails to match, we go around the loop again. 5933 * For xpv entries, there are two module lines, so we 5934 * do the check twice. 5935 */ 5936 lp = lp->next; /* advance to module line */ 5937 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) || 5938 (((lp = lp->next) != NULL) && 5939 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) { 5940 /* match found */ 5941 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg)); 5942 break; 5943 } 5944 } 5945 5946 if (ent && entry_num) { 5947 *entry_num = i; 5948 } 5949 5950 if (ent) { 5951 BAM_DPRINTF((D_RETURN_RET, fcn, i)); 5952 } else { 5953 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR)); 5954 } 5955 return (ent); 5956 } 5957 5958 static int 5959 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root, 5960 char *kernel, char *mod_kernel, char *module, int root_opt) 5961 { 5962 int i; 5963 int change_kernel = 0; 5964 entry_t *ent; 5965 line_t *lp; 5966 line_t *tlp; 5967 char linebuf[BAM_MAXLINE]; 5968 const char *fcn = "update_boot_entry()"; 5969 5970 /* note: don't match on title, it's updated on upgrade */ 5971 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module, 5972 root_opt, &i); 5973 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) { 5974 /* 5975 * We may be upgrading a kernel from multiboot to 5976 * directboot. Look for a multiboot entry. A multiboot 5977 * entry will not have a findroot line. 5978 */ 5979 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root, 5980 MULTIBOOT_ARCHIVE, root_opt, &i); 5981 if (ent != NULL) { 5982 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root)); 5983 change_kernel = 1; 5984 } 5985 } else if (ent) { 5986 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot)); 5987 } 5988 5989 if (ent == NULL) { 5990 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot)); 5991 return (add_boot_entry(mp, title, findroot, 5992 kernel, mod_kernel, module)); 5993 } 5994 5995 /* replace title of existing entry and update findroot line */ 5996 lp = ent->start; 5997 lp = lp->next; /* title line */ 5998 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 5999 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 6000 free(lp->arg); 6001 free(lp->line); 6002 lp->arg = s_strdup(title); 6003 lp->line = s_strdup(linebuf); 6004 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title)); 6005 6006 tlp = lp; /* title line */ 6007 lp = lp->next; /* root line */ 6008 6009 /* if no root or findroot command, create a new line_t */ 6010 if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 && 6011 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0) { 6012 lp = s_calloc(1, sizeof (line_t)); 6013 bam_add_line(mp, ent, tlp, lp); 6014 } else { 6015 free(lp->cmd); 6016 free(lp->sep); 6017 free(lp->arg); 6018 free(lp->line); 6019 } 6020 6021 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]); 6022 lp->sep = s_strdup(menu_cmds[SEP_CMD]); 6023 lp->arg = s_strdup(findroot); 6024 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 6025 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot); 6026 lp->line = s_strdup(linebuf); 6027 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot)); 6028 6029 /* kernel line */ 6030 lp = lp->next; 6031 6032 if (change_kernel) { 6033 /* 6034 * We're upgrading from multiboot to directboot. 6035 */ 6036 if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) { 6037 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 6038 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 6039 kernel); 6040 free(lp->cmd); 6041 free(lp->arg); 6042 free(lp->line); 6043 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); 6044 lp->arg = s_strdup(kernel); 6045 lp->line = s_strdup(linebuf); 6046 lp = lp->next; 6047 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel)); 6048 } 6049 if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 6050 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 6051 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 6052 module); 6053 free(lp->cmd); 6054 free(lp->arg); 6055 free(lp->line); 6056 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 6057 lp->arg = s_strdup(module); 6058 lp->line = s_strdup(linebuf); 6059 lp = lp->next; 6060 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module)); 6061 } 6062 } 6063 BAM_DPRINTF((D_RETURN_RET, fcn, i)); 6064 return (i); 6065 } 6066 6067 int 6068 root_optional(char *osroot, char *menu_root) 6069 { 6070 char *ospecial; 6071 char *mspecial; 6072 char *slash; 6073 int root_opt; 6074 int ret1; 6075 int ret2; 6076 const char *fcn = "root_optional()"; 6077 6078 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 6079 6080 /* 6081 * For all filesystems except ZFS, a straight compare of osroot 6082 * and menu_root will tell us if root is optional. 6083 * For ZFS, the situation is complicated by the fact that 6084 * menu_root and osroot are always different 6085 */ 6086 ret1 = is_zfs(osroot); 6087 ret2 = is_zfs(menu_root); 6088 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0); 6089 if (!ret1 || !ret2) { 6090 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root)); 6091 root_opt = (strcmp(osroot, menu_root) == 0); 6092 goto out; 6093 } 6094 6095 ospecial = get_special(osroot); 6096 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL); 6097 if (ospecial == NULL) { 6098 bam_error(GET_OSROOT_SPECIAL_ERR, osroot); 6099 return (0); 6100 } 6101 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot)); 6102 6103 mspecial = get_special(menu_root); 6104 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL); 6105 if (mspecial == NULL) { 6106 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root); 6107 free(ospecial); 6108 return (0); 6109 } 6110 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root)); 6111 6112 slash = strchr(ospecial, '/'); 6113 if (slash) 6114 *slash = '\0'; 6115 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot)); 6116 6117 root_opt = (strcmp(ospecial, mspecial) == 0); 6118 6119 free(ospecial); 6120 free(mspecial); 6121 6122 out: 6123 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0); 6124 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1); 6125 if (root_opt) { 6126 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6127 } else { 6128 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6129 } 6130 6131 return (root_opt); 6132 } 6133 6134 /*ARGSUSED*/ 6135 static error_t 6136 update_entry(menu_t *mp, char *menu_root, char *osdev) 6137 { 6138 int entry; 6139 char *grubsign; 6140 char *grubroot; 6141 char *title; 6142 char osroot[PATH_MAX]; 6143 char *failsafe_kernel = NULL; 6144 struct stat sbuf; 6145 char failsafe[256]; 6146 int ret; 6147 const char *fcn = "update_entry()"; 6148 6149 assert(mp); 6150 assert(menu_root); 6151 assert(osdev); 6152 assert(bam_root); 6153 6154 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root)); 6155 6156 (void) strlcpy(osroot, bam_root, sizeof (osroot)); 6157 6158 title = get_title(osroot); 6159 assert(title); 6160 6161 grubsign = get_grubsign(osroot, osdev); 6162 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL); 6163 if (grubsign == NULL) { 6164 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev); 6165 return (BAM_ERROR); 6166 } 6167 6168 /* 6169 * It is not a fatal error if get_grubroot() fails 6170 * We no longer rely on biosdev to populate the 6171 * menu 6172 */ 6173 grubroot = get_grubroot(osroot, osdev, menu_root); 6174 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL); 6175 if (grubroot) { 6176 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS, 6177 fcn, osroot, osdev, menu_root)); 6178 } else { 6179 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE, 6180 fcn, osroot, osdev, menu_root)); 6181 } 6182 6183 /* add the entry for normal Solaris */ 6184 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT", 6185 bam_direct = BAM_DIRECT_MULTIBOOT); 6186 if (bam_direct == BAM_DIRECT_DBOOT) { 6187 entry = update_boot_entry(mp, title, grubsign, grubroot, 6188 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL), 6189 NULL, DIRECT_BOOT_ARCHIVE, 6190 root_optional(osroot, menu_root)); 6191 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign)); 6192 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) { 6193 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign, 6194 grubroot, XEN_MENU, bam_zfs ? 6195 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE, 6196 DIRECT_BOOT_ARCHIVE, 6197 root_optional(osroot, menu_root)); 6198 BAM_DPRINTF((D_UPDATED_HV_ENTRY, 6199 fcn, bam_zfs, grubsign)); 6200 } 6201 } else { 6202 entry = update_boot_entry(mp, title, grubsign, grubroot, 6203 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE, 6204 root_optional(osroot, menu_root)); 6205 6206 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign)); 6207 } 6208 6209 /* 6210 * Add the entry for failsafe archive. On a bfu'd system, the 6211 * failsafe may be different than the installed kernel. 6212 */ 6213 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 6214 osroot, FAILSAFE_ARCHIVE); 6215 if (stat(failsafe, &sbuf) == 0) { 6216 6217 /* Figure out where the kernel line should point */ 6218 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, 6219 DIRECT_BOOT_FAILSAFE_KERNEL); 6220 if (stat(failsafe, &sbuf) == 0) { 6221 failsafe_kernel = 6222 (bam_zfs ? DIRECT_BOOT_FAILSAFE_LINE_ZFS : 6223 DIRECT_BOOT_FAILSAFE_LINE); 6224 } else { 6225 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 6226 osroot, MULTI_BOOT_FAILSAFE); 6227 if (stat(failsafe, &sbuf) == 0) { 6228 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE; 6229 } 6230 } 6231 if (failsafe_kernel != NULL) { 6232 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign, 6233 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE, 6234 root_optional(osroot, menu_root)); 6235 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn, 6236 failsafe_kernel)); 6237 } 6238 } 6239 free(grubroot); 6240 6241 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR); 6242 if (entry == BAM_ERROR) { 6243 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign); 6244 free(grubsign); 6245 return (BAM_ERROR); 6246 } 6247 free(grubsign); 6248 6249 update_numbering(mp); 6250 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 6251 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR); 6252 if (ret == BAM_ERROR) { 6253 bam_error(SET_DEFAULT_FAILED, entry); 6254 } 6255 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6256 return (BAM_WRITE); 6257 } 6258 6259 static void 6260 save_default_entry(menu_t *mp, const char *which) 6261 { 6262 int lineNum; 6263 int entryNum; 6264 int entry = 0; /* default is 0 */ 6265 char linebuf[BAM_MAXLINE]; 6266 line_t *lp = mp->curdefault; 6267 const char *fcn = "save_default_entry()"; 6268 6269 if (mp->start) { 6270 lineNum = mp->end->lineNum; 6271 entryNum = mp->end->entryNum; 6272 } else { 6273 lineNum = LINE_INIT; 6274 entryNum = ENTRY_INIT; 6275 } 6276 6277 if (lp) 6278 entry = s_strtol(lp->arg); 6279 6280 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry); 6281 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf)); 6282 line_parser(mp, linebuf, &lineNum, &entryNum); 6283 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum)); 6284 } 6285 6286 static void 6287 restore_default_entry(menu_t *mp, const char *which, line_t *lp) 6288 { 6289 int entry; 6290 char *str; 6291 const char *fcn = "restore_default_entry()"; 6292 6293 if (lp == NULL) { 6294 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn)); 6295 return; /* nothing to restore */ 6296 } 6297 6298 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which)); 6299 6300 str = lp->arg + strlen(which); 6301 entry = s_strtol(str); 6302 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 6303 6304 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry)); 6305 6306 /* delete saved old default line */ 6307 unlink_line(mp, lp); 6308 line_free(lp); 6309 } 6310 6311 /* 6312 * This function is for supporting reboot with args. 6313 * The opt value can be: 6314 * NULL delete temp entry, if present 6315 * entry=<n> switches default entry to <n> 6316 * else treated as boot-args and setup a temperary menu entry 6317 * and make it the default 6318 * Note that we are always rebooting the current OS instance 6319 * so osroot == / always. 6320 */ 6321 #define REBOOT_TITLE "Solaris_reboot_transient" 6322 6323 /*ARGSUSED*/ 6324 static error_t 6325 update_temp(menu_t *mp, char *dummy, char *opt) 6326 { 6327 int entry; 6328 char *osdev; 6329 char *fstype; 6330 char *sign; 6331 char *opt_ptr; 6332 char *path; 6333 char kernbuf[BUFSIZ]; 6334 char args_buf[BUFSIZ]; 6335 char signbuf[PATH_MAX]; 6336 int ret; 6337 const char *fcn = "update_temp()"; 6338 6339 assert(mp); 6340 assert(dummy == NULL); 6341 6342 /* opt can be NULL */ 6343 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>")); 6344 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root)); 6345 6346 if (bam_alt_root || bam_rootlen != 1 || 6347 strcmp(bam_root, "/") != 0 || 6348 strcmp(rootbuf, "/") != 0) { 6349 bam_error(ALT_ROOT_INVALID, bam_root); 6350 return (BAM_ERROR); 6351 } 6352 6353 /* If no option, delete exiting reboot menu entry */ 6354 if (opt == NULL) { 6355 entry_t *ent; 6356 BAM_DPRINTF((D_OPT_NULL, fcn)); 6357 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL, 6358 NULL, NULL, 0, &entry); 6359 if (ent == NULL) { /* not found is ok */ 6360 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn)); 6361 return (BAM_SUCCESS); 6362 } 6363 (void) do_delete(mp, entry); 6364 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault); 6365 mp->olddefault = NULL; 6366 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn)); 6367 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6368 return (BAM_WRITE); 6369 } 6370 6371 /* if entry= is specified, set the default entry */ 6372 if (strncmp(opt, "entry=", strlen("entry=")) == 0) { 6373 int entryNum = s_strtol(opt + strlen("entry=")); 6374 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt)); 6375 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) { 6376 /* this is entry=# option */ 6377 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 6378 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret)); 6379 return (ret); 6380 } else { 6381 bam_error(SET_DEFAULT_FAILED, entryNum); 6382 return (BAM_ERROR); 6383 } 6384 } 6385 6386 /* 6387 * add a new menu entry based on opt and make it the default 6388 */ 6389 6390 fstype = get_fstype("/"); 6391 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL); 6392 if (fstype == NULL) { 6393 bam_error(REBOOT_FSTYPE_FAILED); 6394 return (BAM_ERROR); 6395 } 6396 6397 osdev = get_special("/"); 6398 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL); 6399 if (osdev == NULL) { 6400 free(fstype); 6401 bam_error(REBOOT_SPECIAL_FAILED); 6402 return (BAM_ERROR); 6403 } 6404 6405 sign = find_existing_sign("/", osdev, fstype); 6406 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL); 6407 if (sign == NULL) { 6408 free(fstype); 6409 free(osdev); 6410 bam_error(REBOOT_SIGN_FAILED); 6411 return (BAM_ERROR); 6412 } 6413 6414 free(fstype); 6415 free(osdev); 6416 (void) strlcpy(signbuf, sign, sizeof (signbuf)); 6417 free(sign); 6418 6419 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL && 6420 strchr(signbuf, ')') == NULL); 6421 6422 /* 6423 * There is no alternate root while doing reboot with args 6424 * This version of bootadm is only delivered with a DBOOT 6425 * version of Solaris. 6426 */ 6427 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT); 6428 if (bam_direct != BAM_DIRECT_DBOOT) { 6429 bam_error(REBOOT_DIRECT_FAILED); 6430 return (BAM_ERROR); 6431 } 6432 6433 /* add an entry for Solaris reboot */ 6434 if (opt[0] == '-') { 6435 /* It's an option - first see if boot-file is set */ 6436 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf)); 6437 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR); 6438 if (ret != BAM_SUCCESS) { 6439 bam_error(REBOOT_GET_KERNEL_FAILED); 6440 return (BAM_ERROR); 6441 } 6442 if (kernbuf[0] == '\0') 6443 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL, 6444 sizeof (kernbuf)); 6445 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 6446 (void) strlcat(kernbuf, opt, sizeof (kernbuf)); 6447 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf)); 6448 } else if (opt[0] == '/') { 6449 /* It's a full path, so write it out. */ 6450 (void) strlcpy(kernbuf, opt, sizeof (kernbuf)); 6451 6452 /* 6453 * If someone runs: 6454 * 6455 * # eeprom boot-args='-kd' 6456 * # reboot /platform/i86pc/kernel/unix 6457 * 6458 * we want to use the boot-args as part of the boot 6459 * line. On the other hand, if someone runs: 6460 * 6461 * # reboot "/platform/i86pc/kernel/unix -kd" 6462 * 6463 * we don't need to mess with boot-args. If there's 6464 * no space in the options string, assume we're in the 6465 * first case. 6466 */ 6467 if (strchr(opt, ' ') == NULL) { 6468 ret = get_kernel(mp, ARGS_CMD, args_buf, 6469 sizeof (args_buf)); 6470 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR); 6471 if (ret != BAM_SUCCESS) { 6472 bam_error(REBOOT_GET_ARGS_FAILED); 6473 return (BAM_ERROR); 6474 } 6475 6476 if (args_buf[0] != '\0') { 6477 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 6478 (void) strlcat(kernbuf, args_buf, 6479 sizeof (kernbuf)); 6480 } 6481 } 6482 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf)); 6483 } else { 6484 /* 6485 * It may be a partial path, or it may be a partial 6486 * path followed by options. Assume that only options 6487 * follow a space. If someone sends us a kernel path 6488 * that includes a space, they deserve to be broken. 6489 */ 6490 opt_ptr = strchr(opt, ' '); 6491 if (opt_ptr != NULL) { 6492 *opt_ptr = '\0'; 6493 } 6494 6495 path = expand_path(opt); 6496 if (path != NULL) { 6497 (void) strlcpy(kernbuf, path, sizeof (kernbuf)); 6498 free(path); 6499 6500 /* 6501 * If there were options given, use those. 6502 * Otherwise, copy over the default options. 6503 */ 6504 if (opt_ptr != NULL) { 6505 /* Restore the space in opt string */ 6506 *opt_ptr = ' '; 6507 (void) strlcat(kernbuf, opt_ptr, 6508 sizeof (kernbuf)); 6509 } else { 6510 ret = get_kernel(mp, ARGS_CMD, args_buf, 6511 sizeof (args_buf)); 6512 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS", 6513 ret = BAM_ERROR); 6514 if (ret != BAM_SUCCESS) { 6515 bam_error(REBOOT_GET_ARGS_FAILED); 6516 return (BAM_ERROR); 6517 } 6518 6519 if (args_buf[0] != '\0') { 6520 (void) strlcat(kernbuf, " ", 6521 sizeof (kernbuf)); 6522 (void) strlcat(kernbuf, 6523 args_buf, sizeof (kernbuf)); 6524 } 6525 } 6526 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf)); 6527 } else { 6528 bam_error(UNKNOWN_KERNEL, opt); 6529 bam_print_stderr(UNKNOWN_KERNEL_REBOOT); 6530 return (BAM_ERROR); 6531 } 6532 } 6533 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf, 6534 NULL, NULL); 6535 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR); 6536 if (entry == BAM_ERROR) { 6537 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED); 6538 return (BAM_ERROR); 6539 } 6540 6541 save_default_entry(mp, BAM_OLDDEF); 6542 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 6543 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR); 6544 if (ret == BAM_ERROR) { 6545 bam_error(REBOOT_SET_DEFAULT_FAILED, entry); 6546 } 6547 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6548 return (BAM_WRITE); 6549 } 6550 6551 static error_t 6552 set_global(menu_t *mp, char *globalcmd, int val) 6553 { 6554 line_t *lp; 6555 line_t *found; 6556 line_t *last; 6557 char *cp; 6558 char *str; 6559 char prefix[BAM_MAXLINE]; 6560 size_t len; 6561 const char *fcn = "set_global()"; 6562 6563 assert(mp); 6564 assert(globalcmd); 6565 6566 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) { 6567 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1); 6568 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL); 6569 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100); 6570 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) { 6571 (void) snprintf(prefix, sizeof (prefix), "%d", val); 6572 bam_error(INVALID_ENTRY, prefix); 6573 return (BAM_ERROR); 6574 } 6575 } 6576 6577 found = last = NULL; 6578 for (lp = mp->start; lp; lp = lp->next) { 6579 if (lp->flags != BAM_GLOBAL) 6580 continue; 6581 6582 last = lp; /* track the last global found */ 6583 6584 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL); 6585 if (lp->cmd == NULL) { 6586 bam_error(NO_CMD, lp->lineNum); 6587 continue; 6588 } 6589 if (strcmp(globalcmd, lp->cmd) != 0) 6590 continue; 6591 6592 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd)); 6593 6594 if (found) { 6595 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 6596 } 6597 found = lp; 6598 } 6599 6600 if (found == NULL) { 6601 lp = s_calloc(1, sizeof (line_t)); 6602 if (last == NULL) { 6603 lp->next = mp->start; 6604 mp->start = lp; 6605 mp->end = (mp->end) ? mp->end : lp; 6606 } else { 6607 lp->next = last->next; 6608 last->next = lp; 6609 if (lp->next == NULL) 6610 mp->end = lp; 6611 } 6612 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */ 6613 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]); 6614 len += 10; /* val < 10 digits */ 6615 lp->line = s_calloc(1, len); 6616 (void) snprintf(lp->line, len, "%s%s%d", 6617 globalcmd, menu_cmds[SEP_CMD], val); 6618 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line)); 6619 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6620 return (BAM_WRITE); 6621 } 6622 6623 /* 6624 * We are changing an existing entry. Retain any prefix whitespace, 6625 * but overwrite everything else. This preserves tabs added for 6626 * readability. 6627 */ 6628 str = found->line; 6629 cp = prefix; 6630 while (*str == ' ' || *str == '\t') 6631 *(cp++) = *(str++); 6632 *cp = '\0'; /* Terminate prefix */ 6633 len = strlen(prefix) + strlen(globalcmd); 6634 len += strlen(menu_cmds[SEP_CMD]) + 10; 6635 6636 free(found->line); 6637 found->line = s_calloc(1, len); 6638 (void) snprintf(found->line, len, 6639 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val); 6640 6641 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line)); 6642 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6643 return (BAM_WRITE); /* need a write to menu */ 6644 } 6645 6646 /* 6647 * partial_path may be anything like "kernel/unix" or "kmdb". Try to 6648 * expand it to a full unix path. The calling function is expected to 6649 * output a message if an error occurs and NULL is returned. 6650 */ 6651 static char * 6652 expand_path(const char *partial_path) 6653 { 6654 int new_path_len; 6655 char *new_path; 6656 char new_path2[PATH_MAX]; 6657 struct stat sb; 6658 const char *fcn = "expand_path()"; 6659 6660 new_path_len = strlen(partial_path) + 64; 6661 new_path = s_calloc(1, new_path_len); 6662 6663 /* First, try the simplest case - something like "kernel/unix" */ 6664 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s", 6665 partial_path); 6666 if (stat(new_path, &sb) == 0) { 6667 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 6668 return (new_path); 6669 } 6670 6671 if (strcmp(partial_path, "kmdb") == 0) { 6672 (void) snprintf(new_path, new_path_len, "%s -k", 6673 DIRECT_BOOT_KERNEL); 6674 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 6675 return (new_path); 6676 } 6677 6678 /* 6679 * We've quickly reached unsupported usage. Try once more to 6680 * see if we were just given a glom name. 6681 */ 6682 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix", 6683 partial_path); 6684 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix", 6685 partial_path); 6686 if (stat(new_path, &sb) == 0) { 6687 if (stat(new_path2, &sb) == 0) { 6688 /* 6689 * We matched both, so we actually 6690 * want to write the $ISADIR version. 6691 */ 6692 (void) snprintf(new_path, new_path_len, 6693 "/platform/i86pc/kernel/%s/$ISADIR/unix", 6694 partial_path); 6695 } 6696 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 6697 return (new_path); 6698 } 6699 6700 free(new_path); 6701 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6702 return (NULL); 6703 } 6704 6705 /* 6706 * The kernel cmd and arg have been changed, so 6707 * check whether the archive line needs to change. 6708 */ 6709 static void 6710 set_archive_line(entry_t *entryp, line_t *kernelp) 6711 { 6712 line_t *lp = entryp->start; 6713 char *new_archive; 6714 menu_cmd_t m_cmd; 6715 const char *fcn = "set_archive_line()"; 6716 6717 for (; lp != NULL; lp = lp->next) { 6718 if (strncmp(lp->cmd, menu_cmds[MODULE_CMD], 6719 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) { 6720 break; 6721 } 6722 6723 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end); 6724 if (lp == entryp->end) { 6725 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, 6726 entryp->entryNum)); 6727 return; 6728 } 6729 } 6730 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL); 6731 if (lp == NULL) { 6732 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum)); 6733 return; 6734 } 6735 6736 if (strstr(kernelp->arg, "$ISADIR") != NULL) { 6737 new_archive = DIRECT_BOOT_ARCHIVE; 6738 m_cmd = MODULE_DOLLAR_CMD; 6739 } else if (strstr(kernelp->arg, "amd64") != NULL) { 6740 new_archive = DIRECT_BOOT_ARCHIVE_64; 6741 m_cmd = MODULE_CMD; 6742 } else { 6743 new_archive = DIRECT_BOOT_ARCHIVE_32; 6744 m_cmd = MODULE_CMD; 6745 } 6746 6747 if (strcmp(lp->arg, new_archive) == 0) { 6748 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg)); 6749 return; 6750 } 6751 6752 if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) { 6753 free(lp->cmd); 6754 lp->cmd = s_strdup(menu_cmds[m_cmd]); 6755 } 6756 6757 free(lp->arg); 6758 lp->arg = s_strdup(new_archive); 6759 update_line(lp); 6760 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line)); 6761 } 6762 6763 /* 6764 * Title for an entry to set properties that once went in bootenv.rc. 6765 */ 6766 #define BOOTENV_RC_TITLE "Solaris bootenv rc" 6767 6768 /* 6769 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments 6770 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length 6771 * string, reset the value to the default. If path is a non-zero-length 6772 * string, set the kernel or arguments. 6773 */ 6774 static error_t 6775 get_set_kernel( 6776 menu_t *mp, 6777 menu_cmd_t optnum, 6778 char *path, 6779 char *buf, 6780 size_t bufsize) 6781 { 6782 int entryNum; 6783 int rv = BAM_SUCCESS; 6784 int free_new_path = 0; 6785 entry_t *entryp; 6786 line_t *ptr; 6787 line_t *kernelp; 6788 char *new_arg; 6789 char *old_args; 6790 char *space; 6791 char *new_path; 6792 char old_space; 6793 size_t old_kernel_len; 6794 size_t new_str_len; 6795 char *fstype; 6796 char *osdev; 6797 char *sign; 6798 char signbuf[PATH_MAX]; 6799 int ret; 6800 const char *fcn = "get_set_kernel()"; 6801 6802 assert(bufsize > 0); 6803 6804 ptr = kernelp = NULL; 6805 new_arg = old_args = space = NULL; 6806 new_path = NULL; 6807 buf[0] = '\0'; 6808 6809 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT", 6810 bam_direct = BAM_DIRECT_MULTIBOOT); 6811 if (bam_direct != BAM_DIRECT_DBOOT) { 6812 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args"); 6813 return (BAM_ERROR); 6814 } 6815 6816 /* 6817 * If a user changed the default entry to a non-bootadm controlled 6818 * one, we don't want to mess with it. Just print an error and 6819 * return. 6820 */ 6821 if (mp->curdefault) { 6822 entryNum = s_strtol(mp->curdefault->arg); 6823 for (entryp = mp->entries; entryp; entryp = entryp->next) { 6824 if (entryp->entryNum == entryNum) 6825 break; 6826 } 6827 if ((entryp != NULL) && 6828 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) { 6829 bam_error(DEFAULT_NOT_BAM); 6830 return (BAM_ERROR); 6831 } 6832 } 6833 6834 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL, 6835 0, &entryNum); 6836 6837 if (entryp != NULL) { 6838 for (ptr = entryp->start; ptr && ptr != entryp->end; 6839 ptr = ptr->next) { 6840 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD], 6841 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) { 6842 kernelp = ptr; 6843 break; 6844 } 6845 } 6846 if (kernelp == NULL) { 6847 bam_error(NO_KERNEL, entryNum); 6848 return (BAM_ERROR); 6849 } 6850 6851 old_kernel_len = strcspn(kernelp->arg, " \t"); 6852 space = old_args = kernelp->arg + old_kernel_len; 6853 while ((*old_args == ' ') || (*old_args == '\t')) 6854 old_args++; 6855 } 6856 6857 if (path == NULL) { 6858 if (entryp == NULL) { 6859 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn)); 6860 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6861 return (BAM_SUCCESS); 6862 } 6863 assert(kernelp); 6864 if (optnum == ARGS_CMD) { 6865 if (old_args[0] != '\0') { 6866 (void) strlcpy(buf, old_args, bufsize); 6867 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf)); 6868 } 6869 } else { 6870 /* 6871 * We need to print the kernel, so we just turn the 6872 * first space into a '\0' and print the beginning. 6873 * We don't print anything if it's the default kernel. 6874 */ 6875 old_space = *space; 6876 *space = '\0'; 6877 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) { 6878 (void) strlcpy(buf, kernelp->arg, bufsize); 6879 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf)); 6880 } 6881 *space = old_space; 6882 } 6883 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6884 return (BAM_SUCCESS); 6885 } 6886 6887 /* 6888 * First, check if we're resetting an entry to the default. 6889 */ 6890 if ((path[0] == '\0') || 6891 ((optnum == KERNEL_CMD) && 6892 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) { 6893 if ((entryp == NULL) || (kernelp == NULL)) { 6894 /* No previous entry, it's already the default */ 6895 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn)); 6896 return (BAM_SUCCESS); 6897 } 6898 6899 /* 6900 * Check if we can delete the entry. If we're resetting the 6901 * kernel command, and the args is already empty, or if we're 6902 * resetting the args command, and the kernel is already the 6903 * default, we can restore the old default and delete the entry. 6904 */ 6905 if (((optnum == KERNEL_CMD) && 6906 ((old_args == NULL) || (old_args[0] == '\0'))) || 6907 ((optnum == ARGS_CMD) && 6908 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL, 6909 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) { 6910 kernelp = NULL; 6911 (void) do_delete(mp, entryNum); 6912 restore_default_entry(mp, BAM_OLD_RC_DEF, 6913 mp->old_rc_default); 6914 mp->old_rc_default = NULL; 6915 rv = BAM_WRITE; 6916 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn)); 6917 goto done; 6918 } 6919 6920 if (optnum == KERNEL_CMD) { 6921 /* 6922 * At this point, we've already checked that old_args 6923 * and entryp are valid pointers. The "+ 2" is for 6924 * a space a the string termination character. 6925 */ 6926 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) + 6927 strlen(old_args) + 2; 6928 new_arg = s_calloc(1, new_str_len); 6929 (void) snprintf(new_arg, new_str_len, "%s %s", 6930 DIRECT_BOOT_KERNEL, old_args); 6931 free(kernelp->arg); 6932 kernelp->arg = new_arg; 6933 6934 /* 6935 * We have changed the kernel line, so we may need 6936 * to update the archive line as well. 6937 */ 6938 set_archive_line(entryp, kernelp); 6939 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG, 6940 fcn, kernelp->arg)); 6941 } else { 6942 /* 6943 * We're resetting the boot args to nothing, so 6944 * we only need to copy the kernel. We've already 6945 * checked that the kernel is not the default. 6946 */ 6947 new_arg = s_calloc(1, old_kernel_len + 1); 6948 (void) snprintf(new_arg, old_kernel_len + 1, "%s", 6949 kernelp->arg); 6950 free(kernelp->arg); 6951 kernelp->arg = new_arg; 6952 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL, 6953 fcn, kernelp->arg)); 6954 } 6955 rv = BAM_WRITE; 6956 goto done; 6957 } 6958 6959 /* 6960 * Expand the kernel file to a full path, if necessary 6961 */ 6962 if ((optnum == KERNEL_CMD) && (path[0] != '/')) { 6963 new_path = expand_path(path); 6964 if (new_path == NULL) { 6965 bam_error(UNKNOWN_KERNEL, path); 6966 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6967 return (BAM_ERROR); 6968 } 6969 free_new_path = 1; 6970 } else { 6971 new_path = path; 6972 free_new_path = 0; 6973 } 6974 6975 /* 6976 * At this point, we know we're setting a new value. First, take care 6977 * of the case where there was no previous entry. 6978 */ 6979 if (entryp == NULL) { 6980 6981 /* Similar to code in update_temp */ 6982 fstype = get_fstype("/"); 6983 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL); 6984 if (fstype == NULL) { 6985 bam_error(BOOTENV_FSTYPE_FAILED); 6986 rv = BAM_ERROR; 6987 goto done; 6988 } 6989 6990 osdev = get_special("/"); 6991 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL); 6992 if (osdev == NULL) { 6993 free(fstype); 6994 bam_error(BOOTENV_SPECIAL_FAILED); 6995 rv = BAM_ERROR; 6996 goto done; 6997 } 6998 6999 sign = find_existing_sign("/", osdev, fstype); 7000 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL); 7001 if (sign == NULL) { 7002 free(fstype); 7003 free(osdev); 7004 bam_error(BOOTENV_SIGN_FAILED); 7005 rv = BAM_ERROR; 7006 goto done; 7007 } 7008 7009 free(fstype); 7010 free(osdev); 7011 (void) strlcpy(signbuf, sign, sizeof (signbuf)); 7012 free(sign); 7013 assert(strchr(signbuf, '(') == NULL && 7014 strchr(signbuf, ',') == NULL && 7015 strchr(signbuf, ')') == NULL); 7016 7017 if (optnum == KERNEL_CMD) { 7018 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, new_path)); 7019 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 7020 signbuf, new_path, NULL, NULL); 7021 } else { 7022 new_str_len = strlen(DIRECT_BOOT_KERNEL) + 7023 strlen(path) + 8; 7024 new_arg = s_calloc(1, new_str_len); 7025 7026 (void) snprintf(new_arg, new_str_len, "%s %s", 7027 DIRECT_BOOT_KERNEL, path); 7028 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg)); 7029 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 7030 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE); 7031 free(new_arg); 7032 } 7033 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY", 7034 entryNum = BAM_ERROR); 7035 if (entryNum == BAM_ERROR) { 7036 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY, 7037 BOOTENV_RC_TITLE); 7038 rv = BAM_ERROR; 7039 goto done; 7040 } 7041 save_default_entry(mp, BAM_OLD_RC_DEF); 7042 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum); 7043 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR); 7044 if (ret == BAM_ERROR) { 7045 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum); 7046 } 7047 rv = BAM_WRITE; 7048 goto done; 7049 } 7050 7051 /* 7052 * There was already an bootenv entry which we need to edit. 7053 */ 7054 if (optnum == KERNEL_CMD) { 7055 new_str_len = strlen(new_path) + strlen(old_args) + 2; 7056 new_arg = s_calloc(1, new_str_len); 7057 (void) snprintf(new_arg, new_str_len, "%s %s", new_path, 7058 old_args); 7059 free(kernelp->arg); 7060 kernelp->arg = new_arg; 7061 7062 /* 7063 * If we have changed the kernel line, we may need to update 7064 * the archive line as well. 7065 */ 7066 set_archive_line(entryp, kernelp); 7067 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn, 7068 kernelp->arg)); 7069 } else { 7070 new_str_len = old_kernel_len + strlen(path) + 8; 7071 new_arg = s_calloc(1, new_str_len); 7072 (void) strncpy(new_arg, kernelp->arg, old_kernel_len); 7073 (void) strlcat(new_arg, " ", new_str_len); 7074 (void) strlcat(new_arg, path, new_str_len); 7075 free(kernelp->arg); 7076 kernelp->arg = new_arg; 7077 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn, 7078 kernelp->arg)); 7079 } 7080 rv = BAM_WRITE; 7081 7082 done: 7083 if ((rv == BAM_WRITE) && kernelp) 7084 update_line(kernelp); 7085 if (free_new_path) 7086 free(new_path); 7087 if (rv == BAM_WRITE) { 7088 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7089 } else { 7090 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7091 } 7092 return (rv); 7093 } 7094 7095 static error_t 7096 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize) 7097 { 7098 const char *fcn = "get_kernel()"; 7099 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum])); 7100 return (get_set_kernel(mp, optnum, NULL, buf, bufsize)); 7101 } 7102 7103 static error_t 7104 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize) 7105 { 7106 const char *fcn = "set_kernel()"; 7107 assert(path != NULL); 7108 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path)); 7109 return (get_set_kernel(mp, optnum, path, buf, bufsize)); 7110 } 7111 7112 /*ARGSUSED*/ 7113 static error_t 7114 set_option(menu_t *mp, char *dummy, char *opt) 7115 { 7116 int optnum; 7117 int optval; 7118 char *val; 7119 char buf[BUFSIZ] = ""; 7120 error_t rv; 7121 const char *fcn = "set_option()"; 7122 7123 assert(mp); 7124 assert(opt); 7125 assert(dummy == NULL); 7126 7127 /* opt is set from bam_argv[0] and is always non-NULL */ 7128 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt)); 7129 7130 val = strchr(opt, '='); 7131 if (val != NULL) { 7132 *val = '\0'; 7133 } 7134 7135 if (strcmp(opt, "default") == 0) { 7136 optnum = DEFAULT_CMD; 7137 } else if (strcmp(opt, "timeout") == 0) { 7138 optnum = TIMEOUT_CMD; 7139 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) { 7140 optnum = KERNEL_CMD; 7141 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) { 7142 optnum = ARGS_CMD; 7143 } else { 7144 bam_error(INVALID_OPTION, opt); 7145 return (BAM_ERROR); 7146 } 7147 7148 /* 7149 * kernel and args are allowed without "=new_value" strings. All 7150 * others cause errors 7151 */ 7152 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) { 7153 bam_error(NO_OPTION_ARG, opt); 7154 return (BAM_ERROR); 7155 } else if (val != NULL) { 7156 *val = '='; 7157 } 7158 7159 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) { 7160 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], 7161 val ? val + 1 : "NULL")); 7162 7163 if (val) 7164 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf)); 7165 else 7166 rv = get_kernel(mp, optnum, buf, sizeof (buf)); 7167 if ((rv == BAM_SUCCESS) && (buf[0] != '\0')) 7168 (void) printf("%s\n", buf); 7169 } else { 7170 optval = s_strtol(val + 1); 7171 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1)); 7172 rv = set_global(mp, menu_cmds[optnum], optval); 7173 } 7174 7175 if (rv == BAM_WRITE || rv == BAM_SUCCESS) { 7176 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7177 } else { 7178 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7179 } 7180 7181 return (rv); 7182 } 7183 7184 /* 7185 * The quiet argument suppresses messages. This is used 7186 * when invoked in the context of other commands (e.g. list_entry) 7187 */ 7188 static error_t 7189 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet) 7190 { 7191 line_t *lp; 7192 char *arg; 7193 int done, ret = BAM_SUCCESS; 7194 7195 assert(mp); 7196 assert(menu_path); 7197 assert(globalcmd); 7198 7199 if (mp->start == NULL) { 7200 if (!quiet) 7201 bam_error(NO_MENU, menu_path); 7202 return (BAM_ERROR); 7203 } 7204 7205 done = 0; 7206 for (lp = mp->start; lp; lp = lp->next) { 7207 if (lp->flags != BAM_GLOBAL) 7208 continue; 7209 7210 if (lp->cmd == NULL) { 7211 if (!quiet) 7212 bam_error(NO_CMD, lp->lineNum); 7213 continue; 7214 } 7215 7216 if (strcmp(globalcmd, lp->cmd) != 0) 7217 continue; 7218 7219 /* Found global. Check for duplicates */ 7220 if (done && !quiet) { 7221 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 7222 ret = BAM_ERROR; 7223 } 7224 7225 arg = lp->arg ? lp->arg : ""; 7226 bam_print(GLOBAL_CMD, globalcmd, arg); 7227 done = 1; 7228 } 7229 7230 if (!done && bam_verbose) 7231 bam_print(NO_ENTRY, globalcmd); 7232 7233 return (ret); 7234 } 7235 7236 static error_t 7237 menu_write(char *root, menu_t *mp) 7238 { 7239 const char *fcn = "menu_write()"; 7240 7241 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root)); 7242 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start)); 7243 } 7244 7245 void 7246 line_free(line_t *lp) 7247 { 7248 if (lp == NULL) 7249 return; 7250 7251 if (lp->cmd) 7252 free(lp->cmd); 7253 if (lp->sep) 7254 free(lp->sep); 7255 if (lp->arg) 7256 free(lp->arg); 7257 if (lp->line) 7258 free(lp->line); 7259 free(lp); 7260 } 7261 7262 static void 7263 linelist_free(line_t *start) 7264 { 7265 line_t *lp; 7266 7267 while (start) { 7268 lp = start; 7269 start = start->next; 7270 line_free(lp); 7271 } 7272 } 7273 7274 static void 7275 filelist_free(filelist_t *flistp) 7276 { 7277 linelist_free(flistp->head); 7278 flistp->head = NULL; 7279 flistp->tail = NULL; 7280 } 7281 7282 static void 7283 menu_free(menu_t *mp) 7284 { 7285 entry_t *ent, *tmp; 7286 assert(mp); 7287 7288 if (mp->start) 7289 linelist_free(mp->start); 7290 ent = mp->entries; 7291 while (ent) { 7292 tmp = ent; 7293 ent = tmp->next; 7294 free(tmp); 7295 } 7296 7297 free(mp); 7298 } 7299 7300 /* 7301 * Utility routines 7302 */ 7303 7304 7305 /* 7306 * Returns 0 on success 7307 * Any other value indicates an error 7308 */ 7309 static int 7310 exec_cmd(char *cmdline, filelist_t *flistp) 7311 { 7312 char buf[BUFSIZ]; 7313 int ret; 7314 FILE *ptr; 7315 sigset_t set; 7316 void (*disp)(int); 7317 7318 /* 7319 * For security 7320 * - only absolute paths are allowed 7321 * - set IFS to space and tab 7322 */ 7323 if (*cmdline != '/') { 7324 bam_error(ABS_PATH_REQ, cmdline); 7325 return (-1); 7326 } 7327 (void) putenv("IFS= \t"); 7328 7329 /* 7330 * We may have been exec'ed with SIGCHLD blocked 7331 * unblock it here 7332 */ 7333 (void) sigemptyset(&set); 7334 (void) sigaddset(&set, SIGCHLD); 7335 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 7336 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno)); 7337 return (-1); 7338 } 7339 7340 /* 7341 * Set SIGCHLD disposition to SIG_DFL for popen/pclose 7342 */ 7343 disp = sigset(SIGCHLD, SIG_DFL); 7344 if (disp == SIG_ERR) { 7345 bam_error(FAILED_SIG, strerror(errno)); 7346 return (-1); 7347 } 7348 if (disp == SIG_HOLD) { 7349 bam_error(BLOCKED_SIG, cmdline); 7350 return (-1); 7351 } 7352 7353 ptr = popen(cmdline, "r"); 7354 if (ptr == NULL) { 7355 bam_error(POPEN_FAIL, cmdline, strerror(errno)); 7356 return (-1); 7357 } 7358 7359 /* 7360 * If we simply do a pclose() following a popen(), pclose() 7361 * will close the reader end of the pipe immediately even 7362 * if the child process has not started/exited. pclose() 7363 * does wait for cmd to terminate before returning though. 7364 * When the executed command writes its output to the pipe 7365 * there is no reader process and the command dies with 7366 * SIGPIPE. To avoid this we read repeatedly until read 7367 * terminates with EOF. This indicates that the command 7368 * (writer) has closed the pipe and we can safely do a 7369 * pclose(). 7370 * 7371 * Since pclose() does wait for the command to exit, 7372 * we can safely reap the exit status of the command 7373 * from the value returned by pclose() 7374 */ 7375 while (s_fgets(buf, sizeof (buf), ptr) != NULL) { 7376 if (flistp == NULL) { 7377 /* s_fgets strips newlines, so insert them at the end */ 7378 bam_print(PRINT, buf); 7379 } else { 7380 append_to_flist(flistp, buf); 7381 } 7382 } 7383 7384 ret = pclose(ptr); 7385 if (ret == -1) { 7386 bam_error(PCLOSE_FAIL, cmdline, strerror(errno)); 7387 return (-1); 7388 } 7389 7390 if (WIFEXITED(ret)) { 7391 return (WEXITSTATUS(ret)); 7392 } else { 7393 bam_error(EXEC_FAIL, cmdline, ret); 7394 return (-1); 7395 } 7396 } 7397 7398 /* 7399 * Since this function returns -1 on error 7400 * it cannot be used to convert -1. However, 7401 * that is sufficient for what we need. 7402 */ 7403 static long 7404 s_strtol(char *str) 7405 { 7406 long l; 7407 char *res = NULL; 7408 7409 if (str == NULL) { 7410 return (-1); 7411 } 7412 7413 errno = 0; 7414 l = strtol(str, &res, 10); 7415 if (errno || *res != '\0') { 7416 return (-1); 7417 } 7418 7419 return (l); 7420 } 7421 7422 /* 7423 * Wrapper around fputs, that adds a newline (since fputs doesn't) 7424 */ 7425 static int 7426 s_fputs(char *str, FILE *fp) 7427 { 7428 char linebuf[BAM_MAXLINE]; 7429 7430 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str); 7431 return (fputs(linebuf, fp)); 7432 } 7433 7434 /* 7435 * Wrapper around fgets, that strips newlines returned by fgets 7436 */ 7437 char * 7438 s_fgets(char *buf, int buflen, FILE *fp) 7439 { 7440 int n; 7441 7442 buf = fgets(buf, buflen, fp); 7443 if (buf) { 7444 n = strlen(buf); 7445 if (n == buflen - 1 && buf[n-1] != '\n') 7446 bam_error(TOO_LONG, buflen - 1, buf); 7447 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1]; 7448 } 7449 7450 return (buf); 7451 } 7452 7453 void * 7454 s_calloc(size_t nelem, size_t sz) 7455 { 7456 void *ptr; 7457 7458 ptr = calloc(nelem, sz); 7459 if (ptr == NULL) { 7460 bam_error(NO_MEM, nelem*sz); 7461 bam_exit(1); 7462 } 7463 return (ptr); 7464 } 7465 7466 void * 7467 s_realloc(void *ptr, size_t sz) 7468 { 7469 ptr = realloc(ptr, sz); 7470 if (ptr == NULL) { 7471 bam_error(NO_MEM, sz); 7472 bam_exit(1); 7473 } 7474 return (ptr); 7475 } 7476 7477 char * 7478 s_strdup(char *str) 7479 { 7480 char *ptr; 7481 7482 if (str == NULL) 7483 return (NULL); 7484 7485 ptr = strdup(str); 7486 if (ptr == NULL) { 7487 bam_error(NO_MEM, strlen(str) + 1); 7488 bam_exit(1); 7489 } 7490 return (ptr); 7491 } 7492 7493 /* 7494 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients) 7495 * Returns 0 otherwise 7496 */ 7497 static int 7498 is_amd64(void) 7499 { 7500 static int amd64 = -1; 7501 char isabuf[257]; /* from sysinfo(2) manpage */ 7502 7503 if (amd64 != -1) 7504 return (amd64); 7505 7506 if (bam_alt_platform) { 7507 if (strcmp(bam_platform, "i86pc") == 0) { 7508 amd64 = 1; /* diskless server */ 7509 } 7510 } else { 7511 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 && 7512 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) { 7513 amd64 = 1; 7514 } else if (strstr(isabuf, "i386") == NULL) { 7515 amd64 = 1; /* diskless server */ 7516 } 7517 } 7518 if (amd64 == -1) 7519 amd64 = 0; 7520 7521 return (amd64); 7522 } 7523 7524 static int 7525 is_sun4u(void) 7526 { 7527 static int sun4u = -1; 7528 char mbuf[257]; /* from sysinfo(2) manpage */ 7529 7530 if (sun4u != -1) 7531 return (sun4u); 7532 7533 if (bam_alt_platform) { 7534 if (strcmp(bam_platform, "sun4u") == 0) { 7535 sun4u = 1; 7536 } 7537 } else { 7538 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 && 7539 strncmp(mbuf, "sun4u", strlen("sun4u")) == 0) { 7540 sun4u = 1; 7541 } 7542 } 7543 if (sun4u == -1) 7544 sun4u = 0; 7545 7546 return (sun4u); 7547 } 7548 7549 static int 7550 is_sun4v(void) 7551 { 7552 static int sun4v = -1; 7553 char mbuf[257]; /* from sysinfo(2) manpage */ 7554 7555 if (sun4v != -1) 7556 return (sun4v); 7557 7558 if (bam_alt_platform) { 7559 if (strcmp(bam_platform, "sun4v") == 0) { 7560 sun4v = 1; 7561 } 7562 } else { 7563 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 && 7564 strncmp(mbuf, "sun4v", strlen("sun4v")) == 0) { 7565 sun4v = 1; 7566 } 7567 } 7568 if (sun4v == -1) 7569 sun4v = 0; 7570 7571 return (sun4v); 7572 } 7573 7574 int 7575 is_sparc(void) 7576 { 7577 static int issparc = -1; 7578 char mbuf[257]; /* from sysinfo(2) manpage */ 7579 7580 if (issparc != -1) 7581 return (issparc); 7582 7583 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 && 7584 strcmp(mbuf, "sparc") == 0) 7585 issparc = 1; 7586 else 7587 issparc = 0; 7588 7589 return (issparc); 7590 } 7591 7592 static void 7593 append_to_flist(filelist_t *flistp, char *s) 7594 { 7595 line_t *lp; 7596 7597 lp = s_calloc(1, sizeof (line_t)); 7598 lp->line = s_strdup(s); 7599 if (flistp->head == NULL) 7600 flistp->head = lp; 7601 else 7602 flistp->tail->next = lp; 7603 flistp->tail = lp; 7604 } 7605 7606 #if !defined(_OPB) 7607 7608 UCODE_VENDORS; 7609 7610 /*ARGSUSED*/ 7611 static void 7612 ucode_install(char *root) 7613 { 7614 int i; 7615 7616 for (i = 0; ucode_vendors[i].filestr != NULL; i++) { 7617 int cmd_len = PATH_MAX + 256; 7618 char cmd[PATH_MAX + 256]; 7619 char file[PATH_MAX]; 7620 char timestamp[PATH_MAX]; 7621 struct stat fstatus, tstatus; 7622 struct utimbuf u_times; 7623 7624 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt", 7625 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr); 7626 7627 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode))) 7628 continue; 7629 7630 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file); 7631 7632 if (stat(timestamp, &tstatus) == 0 && 7633 fstatus.st_mtime <= tstatus.st_mtime) 7634 continue; 7635 7636 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R " 7637 "%s/%s/%s %s > /dev/null 2>&1", bam_root, 7638 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file); 7639 if (system(cmd) != 0) 7640 return; 7641 7642 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1) 7643 return; 7644 7645 u_times.actime = fstatus.st_atime; 7646 u_times.modtime = fstatus.st_mtime; 7647 (void) utime(timestamp, &u_times); 7648 } 7649 } 7650 #endif 7651