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