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