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