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