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