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