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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * bootadm(1M) is a new utility for managing bootability of 30 * Solaris *Newboot* environments. It has two primary tasks: 31 * - Allow end users to manage bootability of Newboot Solaris instances 32 * - Provide services to other subsystems in Solaris (primarily Install) 33 */ 34 35 /* Headers */ 36 #include <stdio.h> 37 #include <errno.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <stdarg.h> 44 #include <limits.h> 45 #include <signal.h> 46 #include <sys/wait.h> 47 #include <sys/mnttab.h> 48 #include <sys/statvfs.h> 49 #include <libnvpair.h> 50 #include <ftw.h> 51 #include <fcntl.h> 52 #include <strings.h> 53 #include <utime.h> 54 #include <sys/systeminfo.h> 55 #include <sys/dktp/fdisk.h> 56 #include <sys/param.h> 57 58 #if !defined(_OPB) 59 #include <sys/ucode.h> 60 #endif 61 62 #include <pwd.h> 63 #include <grp.h> 64 #include <device_info.h> 65 66 #include <locale.h> 67 68 #include <assert.h> 69 70 #include "message.h" 71 #include "bootadm.h" 72 73 #ifndef TEXT_DOMAIN 74 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 75 #endif /* TEXT_DOMAIN */ 76 77 /* Type definitions */ 78 79 /* Primary subcmds */ 80 typedef enum { 81 BAM_MENU = 3, 82 BAM_ARCHIVE 83 } subcmd_t; 84 85 typedef enum { 86 OPT_ABSENT = 0, /* No option */ 87 OPT_REQ, /* option required */ 88 OPT_OPTIONAL /* option may or may not be present */ 89 } option_t; 90 91 typedef struct { 92 char *subcmd; 93 option_t option; 94 error_t (*handler)(); 95 int unpriv; /* is this an unprivileged command */ 96 } subcmd_defn_t; 97 98 #define LINE_INIT 0 /* lineNum initial value */ 99 #define ENTRY_INIT -1 /* entryNum initial value */ 100 #define ALL_ENTRIES -2 /* selects all boot entries */ 101 102 #define GRUB_DIR "/boot/grub" 103 #define GRUB_MENU "/boot/grub/menu.lst" 104 #define MENU_TMP "/boot/grub/menu.lst.tmp" 105 #define RAMDISK_SPECIAL "/ramdisk" 106 #define STUBBOOT "/stubboot" 107 108 /* lock related */ 109 #define BAM_LOCK_FILE "/var/run/bootadm.lock" 110 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 111 112 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk" 113 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap" 114 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist" 115 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map" 116 117 #define GRUB_slice "/etc/lu/GRUB_slice" 118 #define GRUB_root "/etc/lu/GRUB_root" 119 #define GRUB_backup_menu "/etc/lu/GRUB_backup_menu" 120 #define GRUB_slice_mntpt "/tmp/GRUB_slice_mntpt" 121 #define LU_ACTIVATE_FILE "/etc/lu/DelayUpdate/activate.sh" 122 #define GRUB_fdisk "/etc/lu/GRUB_fdisk" 123 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target" 124 125 #define INSTALLGRUB "/sbin/installgrub" 126 #define STAGE1 "/boot/grub/stage1" 127 #define STAGE2 "/boot/grub/stage2" 128 129 /* 130 * The following two defines are used to detect and create the correct 131 * boot archive when safemode patching is underway. LOFS_PATCH_FILE is a 132 * contracted private interface between bootadm and the install 133 * consolidation. It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE 134 * is applied. 135 */ 136 137 #define LOFS_PATCH_FILE "/var/run/.patch_loopback_mode" 138 #define LOFS_PATCH_MNT "/var/run/.patch_root_loopbackmnt" 139 140 /* 141 * Default file attributes 142 */ 143 #define DEFAULT_DEV_MODE 0644 /* default permissions */ 144 #define DEFAULT_DEV_UID 0 /* user root */ 145 #define DEFAULT_DEV_GID 3 /* group sys */ 146 147 /* 148 * Menu related 149 * menu_cmd_t and menu_cmds must be kept in sync 150 */ 151 char *menu_cmds[] = { 152 "default", /* DEFAULT_CMD */ 153 "timeout", /* TIMEOUT_CMD */ 154 "title", /* TITLE_CMD */ 155 "root", /* ROOT_CMD */ 156 "kernel", /* KERNEL_CMD */ 157 "kernel$", /* KERNEL_DOLLAR_CMD */ 158 "module", /* MODULE_CMD */ 159 "module$", /* MODULE_DOLLAR_CMD */ 160 " ", /* SEP_CMD */ 161 "#", /* COMMENT_CMD */ 162 "chainloader", /* CHAINLOADER_CMD */ 163 "args", /* ARGS_CMD */ 164 NULL 165 }; 166 167 #define OPT_ENTRY_NUM "entry" 168 169 /* 170 * archive related 171 */ 172 typedef struct { 173 line_t *head; 174 line_t *tail; 175 } filelist_t; 176 177 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk" 178 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk" 179 180 #define FILE_STAT "boot/solaris/filestat.ramdisk" 181 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp" 182 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 183 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 184 185 /* Globals */ 186 int bam_verbose; 187 int bam_force; 188 static char *prog; 189 static subcmd_t bam_cmd; 190 static char *bam_root; 191 static int bam_rootlen; 192 static int bam_root_readonly; 193 static int bam_alt_root; 194 static char *bam_subcmd; 195 static char *bam_opt; 196 static int bam_debug; 197 static char **bam_argv; 198 static int bam_argc; 199 static int bam_check; 200 static int bam_smf_check; 201 static int bam_lock_fd = -1; 202 static char rootbuf[PATH_MAX] = "/"; 203 static int bam_update_all; 204 205 /* function prototypes */ 206 static void parse_args_internal(int, char *[]); 207 static void parse_args(int, char *argv[]); 208 static error_t bam_menu(char *, char *, int, char *[]); 209 static error_t bam_archive(char *, char *); 210 211 static void bam_print(char *, ...); 212 static void bam_exit(int); 213 static void bam_lock(void); 214 static void bam_unlock(void); 215 216 static int exec_cmd(char *, filelist_t *); 217 static error_t read_globals(menu_t *, char *, char *, int); 218 219 static menu_t *menu_read(char *); 220 static error_t menu_write(char *, menu_t *); 221 static void linelist_free(line_t *); 222 static void menu_free(menu_t *); 223 static void line_free(line_t *); 224 static void filelist_free(filelist_t *); 225 static error_t list2file(char *, char *, char *, line_t *); 226 static error_t list_entry(menu_t *, char *, char *); 227 static error_t delete_all_entries(menu_t *, char *, char *); 228 static error_t update_entry(menu_t *, char *, char *); 229 static error_t update_temp(menu_t *, char *, char *); 230 231 static error_t update_archive(char *, char *); 232 static error_t list_archive(char *, char *); 233 static error_t update_all(char *, char *); 234 static error_t read_list(char *, filelist_t *); 235 static error_t set_global(menu_t *, char *, int); 236 static error_t set_option(menu_t *, char *, char *); 237 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t); 238 static char *expand_path(const char *); 239 240 static long s_strtol(char *); 241 static int s_fputs(char *, FILE *); 242 243 static char *s_strdup(char *); 244 static int is_readonly(char *); 245 static int is_amd64(void); 246 static int is_sun4u(void); 247 static int is_sun4v(void); 248 static void append_to_flist(filelist_t *, char *); 249 250 #if !defined(_OPB) 251 static void ucode_install(); 252 #endif 253 254 /* Menu related sub commands */ 255 static subcmd_defn_t menu_subcmds[] = { 256 "set_option", OPT_OPTIONAL, set_option, 0, /* PUB */ 257 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */ 258 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */ 259 "update_entry", OPT_REQ, update_entry, 0, /* menu */ 260 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */ 261 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */ 262 NULL, 0, NULL, 0 /* must be last */ 263 }; 264 265 /* Archive related sub commands */ 266 static subcmd_defn_t arch_subcmds[] = { 267 "update", OPT_ABSENT, update_archive, 0, /* PUB */ 268 "update_all", OPT_ABSENT, update_all, 0, /* PVT */ 269 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */ 270 NULL, 0, NULL, 0 /* must be last */ 271 }; 272 273 static struct { 274 nvlist_t *new_nvlp; 275 nvlist_t *old_nvlp; 276 int need_update; 277 } walk_arg; 278 279 280 struct safefile { 281 char *name; 282 struct safefile *next; 283 }; 284 285 static struct safefile *safefiles = NULL; 286 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update" 287 288 static void 289 usage(void) 290 { 291 (void) fprintf(stderr, "USAGE:\n"); 292 293 294 /* archive usage */ 295 (void) fprintf(stderr, "\t%s update-archive [-vn] [-R altroot]\n", 296 prog); 297 (void) fprintf(stderr, "\t%s list-archive [-R altroot]\n", prog); 298 #if !defined(_OPB) 299 /* x86 only */ 300 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog); 301 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog); 302 #endif 303 } 304 305 int 306 main(int argc, char *argv[]) 307 { 308 error_t ret; 309 310 (void) setlocale(LC_ALL, ""); 311 (void) textdomain(TEXT_DOMAIN); 312 313 if ((prog = strrchr(argv[0], '/')) == NULL) { 314 prog = argv[0]; 315 } else { 316 prog++; 317 } 318 319 320 /* 321 * Don't depend on caller's umask 322 */ 323 (void) umask(0022); 324 325 parse_args(argc, argv); 326 327 switch (bam_cmd) { 328 case BAM_MENU: 329 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv); 330 break; 331 case BAM_ARCHIVE: 332 ret = bam_archive(bam_subcmd, bam_opt); 333 break; 334 default: 335 usage(); 336 bam_exit(1); 337 } 338 339 if (ret != BAM_SUCCESS) 340 bam_exit(1); 341 342 bam_unlock(); 343 return (0); 344 } 345 346 /* 347 * Equivalence of public and internal commands: 348 * update-archive -- -a update 349 * list-archive -- -a list 350 * set-menu -- -m set_option 351 * list-menu -- -m list_entry 352 * update-menu -- -m update_entry 353 */ 354 static struct cmd_map { 355 char *bam_cmdname; 356 int bam_cmd; 357 char *bam_subcmd; 358 } cmd_map[] = { 359 { "update-archive", BAM_ARCHIVE, "update"}, 360 { "list-archive", BAM_ARCHIVE, "list"}, 361 { "set-menu", BAM_MENU, "set_option"}, 362 { "list-menu", BAM_MENU, "list_entry"}, 363 { "update-menu", BAM_MENU, "update_entry"}, 364 { NULL, 0, NULL} 365 }; 366 367 /* 368 * Commands syntax published in bootadm(1M) are parsed here 369 */ 370 static void 371 parse_args(int argc, char *argv[]) 372 { 373 struct cmd_map *cmp = cmd_map; 374 375 /* command conforming to the final spec */ 376 if (argc > 1 && argv[1][0] != '-') { 377 /* 378 * Map commands to internal table. 379 */ 380 while (cmp->bam_cmdname) { 381 if (strcmp(argv[1], cmp->bam_cmdname) == 0) { 382 bam_cmd = cmp->bam_cmd; 383 bam_subcmd = cmp->bam_subcmd; 384 break; 385 } 386 cmp++; 387 } 388 if (cmp->bam_cmdname == NULL) { 389 usage(); 390 bam_exit(1); 391 } 392 argc--; 393 argv++; 394 } 395 396 parse_args_internal(argc, argv); 397 } 398 399 /* 400 * A combination of public and private commands are parsed here. 401 * The internal syntax and the corresponding functionality are: 402 * -a update -- update-archive 403 * -a list -- list-archive 404 * -a update-all -- (reboot to sync all mounted OS archive) 405 * -m update_entry -- update-menu 406 * -m list_entry -- list-menu 407 * -m update_temp -- (reboot -- [boot-args]) 408 * -m delete_all_entries -- (called from install) 409 */ 410 static void 411 parse_args_internal(int argc, char *argv[]) 412 { 413 int c, error; 414 extern char *optarg; 415 extern int optind, opterr; 416 417 /* Suppress error message from getopt */ 418 opterr = 0; 419 420 error = 0; 421 while ((c = getopt(argc, argv, "a:d:fm:no:vCR:")) != -1) { 422 switch (c) { 423 case 'a': 424 if (bam_cmd) { 425 error = 1; 426 bam_error(MULT_CMDS, c); 427 } 428 bam_cmd = BAM_ARCHIVE; 429 bam_subcmd = optarg; 430 break; 431 case 'd': 432 if (bam_debug) { 433 error = 1; 434 bam_error(DUP_OPT, c); 435 } 436 bam_debug = s_strtol(optarg); 437 break; 438 case 'f': 439 if (bam_force) { 440 error = 1; 441 bam_error(DUP_OPT, c); 442 } 443 bam_force = 1; 444 break; 445 case 'm': 446 if (bam_cmd) { 447 error = 1; 448 bam_error(MULT_CMDS, c); 449 } 450 bam_cmd = BAM_MENU; 451 bam_subcmd = optarg; 452 break; 453 case 'n': 454 if (bam_check) { 455 error = 1; 456 bam_error(DUP_OPT, c); 457 } 458 bam_check = 1; 459 break; 460 case 'o': 461 if (bam_opt) { 462 error = 1; 463 bam_error(DUP_OPT, c); 464 } 465 bam_opt = optarg; 466 break; 467 case 'v': 468 if (bam_verbose) { 469 error = 1; 470 bam_error(DUP_OPT, c); 471 } 472 bam_verbose = 1; 473 break; 474 case 'C': 475 bam_smf_check = 1; 476 break; 477 case 'R': 478 if (bam_root) { 479 error = 1; 480 bam_error(DUP_OPT, c); 481 break; 482 } else if (realpath(optarg, rootbuf) == NULL) { 483 error = 1; 484 bam_error(CANT_RESOLVE, optarg, 485 strerror(errno)); 486 break; 487 } 488 bam_alt_root = 1; 489 bam_root = rootbuf; 490 bam_rootlen = strlen(rootbuf); 491 break; 492 case '?': 493 error = 1; 494 bam_error(BAD_OPT, optopt); 495 break; 496 default : 497 error = 1; 498 bam_error(BAD_OPT, c); 499 break; 500 } 501 } 502 503 /* 504 * A command option must be specfied 505 */ 506 if (!bam_cmd) { 507 if (bam_opt && strcmp(bam_opt, "all") == 0) { 508 usage(); 509 bam_exit(0); 510 } 511 bam_error(NEED_CMD); 512 error = 1; 513 } 514 515 if (error) { 516 usage(); 517 bam_exit(1); 518 } 519 520 if (optind > argc) { 521 bam_error(INT_ERROR, "parse_args"); 522 bam_exit(1); 523 } else if (optind < argc) { 524 bam_argv = &argv[optind]; 525 bam_argc = argc - optind; 526 } 527 528 /* 529 * -n implies verbose mode 530 */ 531 if (bam_check) 532 bam_verbose = 1; 533 } 534 535 static error_t 536 check_subcmd_and_options( 537 char *subcmd, 538 char *opt, 539 subcmd_defn_t *table, 540 error_t (**fp)()) 541 { 542 int i; 543 544 if (subcmd == NULL) { 545 bam_error(NEED_SUBCMD); 546 return (BAM_ERROR); 547 } 548 549 if (bam_argc != 0 || bam_argv) { 550 if (strcmp(subcmd, "set_option") != 0 || bam_argc != 1) { 551 bam_error(TRAILING_ARGS); 552 usage(); 553 return (BAM_ERROR); 554 } 555 } 556 557 if (bam_root == NULL) { 558 bam_root = rootbuf; 559 bam_rootlen = 1; 560 } 561 562 /* verify that subcmd is valid */ 563 for (i = 0; table[i].subcmd != NULL; i++) { 564 if (strcmp(table[i].subcmd, subcmd) == 0) 565 break; 566 } 567 568 if (table[i].subcmd == NULL) { 569 bam_error(INVALID_SUBCMD, subcmd); 570 return (BAM_ERROR); 571 } 572 573 if (table[i].unpriv == 0 && geteuid() != 0) { 574 bam_error(MUST_BE_ROOT); 575 return (BAM_ERROR); 576 } 577 578 /* 579 * Currently only privileged commands need a lock 580 */ 581 if (table[i].unpriv == 0) 582 bam_lock(); 583 584 /* subcmd verifies that opt is appropriate */ 585 if (table[i].option != OPT_OPTIONAL) { 586 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) { 587 if (opt) 588 bam_error(NO_OPT_REQ, subcmd); 589 else 590 bam_error(MISS_OPT, subcmd); 591 return (BAM_ERROR); 592 } 593 } 594 595 *fp = table[i].handler; 596 597 return (BAM_SUCCESS); 598 } 599 600 601 static char * 602 mount_grub_slice(int *mnted, char **physlice, char **logslice, char **fs_type) 603 { 604 struct extmnttab mnt; 605 struct stat sb; 606 char buf[BAM_MAXLINE], dev[PATH_MAX], phys[PATH_MAX], fstype[32]; 607 char cmd[PATH_MAX]; 608 char *mntpt; 609 int p, l, f; 610 FILE *fp; 611 612 assert(mnted); 613 *mnted = 0; 614 615 /* 616 * physlice, logslice, fs_type args may be NULL 617 */ 618 if (physlice) 619 *physlice = NULL; 620 if (logslice) 621 *logslice = NULL; 622 if (fs_type) 623 *fs_type = NULL; 624 625 if (stat(GRUB_slice, &sb) != 0) { 626 bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno)); 627 return (NULL); 628 } 629 630 fp = fopen(GRUB_slice, "r"); 631 if (fp == NULL) { 632 bam_error(OPEN_FAIL, GRUB_slice, strerror(errno)); 633 return (NULL); 634 } 635 636 dev[0] = fstype[0] = phys[0] = '\0'; 637 p = sizeof ("PHYS_SLICE=") - 1; 638 l = sizeof ("LOG_SLICE=") - 1; 639 f = sizeof ("LOG_FSTYP=") - 1; 640 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 641 if (strncmp(buf, "PHYS_SLICE=", p) == 0) { 642 (void) strlcpy(phys, buf + p, sizeof (phys)); 643 continue; 644 } 645 if (strncmp(buf, "LOG_SLICE=", l) == 0) { 646 (void) strlcpy(dev, buf + l, sizeof (dev)); 647 continue; 648 } 649 if (strncmp(buf, "LOG_FSTYP=", f) == 0) { 650 (void) strlcpy(fstype, buf + f, sizeof (fstype)); 651 continue; 652 } 653 } 654 (void) fclose(fp); 655 656 if (dev[0] == '\0' || fstype[0] == '\0' || phys[0] == '\0') { 657 bam_error(BAD_SLICE_FILE, GRUB_slice); 658 return (NULL); 659 } 660 661 if (physlice) { 662 *physlice = s_strdup(phys); 663 } 664 if (logslice) { 665 *logslice = s_strdup(dev); 666 } 667 if (fs_type) { 668 *fs_type = s_strdup(fstype); 669 } 670 671 /* 672 * Check if the slice is already mounted 673 */ 674 fp = fopen(MNTTAB, "r"); 675 if (fp == NULL) { 676 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 677 goto error; 678 } 679 680 resetmnttab(fp); 681 682 mntpt = NULL; 683 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 684 if (strcmp(mnt.mnt_special, dev) == 0) { 685 mntpt = s_strdup(mnt.mnt_mountp); 686 break; 687 } 688 } 689 690 (void) fclose(fp); 691 692 if (mntpt) { 693 return (mntpt); 694 } 695 696 697 /* 698 * GRUB slice is not mounted, we need to mount it now. 699 * First create the mountpoint 700 */ 701 mntpt = s_calloc(1, PATH_MAX); 702 (void) snprintf(mntpt, PATH_MAX, "%s.%d", GRUB_slice_mntpt, getpid()); 703 if (mkdir(mntpt, 0755) == -1 && errno != EEXIST) { 704 bam_error(MKDIR_FAILED, mntpt, strerror(errno)); 705 free(mntpt); 706 goto error; 707 } 708 709 (void) snprintf(cmd, sizeof (cmd), "/sbin/mount -F %s %s %s", 710 fstype, dev, mntpt); 711 712 if (exec_cmd(cmd, NULL) != 0) { 713 bam_error(MOUNT_FAILED, dev, fstype); 714 if (rmdir(mntpt) != 0) { 715 bam_error(RMDIR_FAILED, mntpt, strerror(errno)); 716 } 717 free(mntpt); 718 goto error; 719 } 720 721 *mnted = 1; 722 return (mntpt); 723 724 error: 725 if (physlice) { 726 free(*physlice); 727 *physlice = NULL; 728 } 729 if (logslice) { 730 free(*logslice); 731 *logslice = NULL; 732 } 733 if (fs_type) { 734 free(*fs_type); 735 *fs_type = NULL; 736 } 737 return (NULL); 738 } 739 740 static void 741 umount_grub_slice( 742 int mnted, 743 char *mntpt, 744 char *physlice, 745 char *logslice, 746 char *fs_type) 747 { 748 char cmd[PATH_MAX]; 749 750 /* 751 * If we have not dealt with GRUB slice 752 * we have nothing to do - just return. 753 */ 754 if (mntpt == NULL) 755 return; 756 757 758 /* 759 * If we mounted the filesystem earlier in mount_grub_slice() 760 * unmount it now. 761 */ 762 if (mnted) { 763 (void) snprintf(cmd, sizeof (cmd), "/sbin/umount %s", 764 mntpt); 765 if (exec_cmd(cmd, NULL) != 0) { 766 bam_error(UMOUNT_FAILED, mntpt); 767 } 768 if (rmdir(mntpt) != 0) { 769 bam_error(RMDIR_FAILED, mntpt, strerror(errno)); 770 } 771 } 772 773 if (physlice) 774 free(physlice); 775 if (logslice) 776 free(logslice); 777 if (fs_type) 778 free(fs_type); 779 780 free(mntpt); 781 } 782 783 static char * 784 use_stubboot(void) 785 { 786 int mnted; 787 struct stat sb; 788 struct extmnttab mnt; 789 FILE *fp; 790 char cmd[PATH_MAX]; 791 792 if (stat(STUBBOOT, &sb) != 0) { 793 bam_error(STUBBOOT_DIR_NOT_FOUND); 794 return (NULL); 795 } 796 797 /* 798 * Check if stubboot is mounted. If not, mount it 799 */ 800 fp = fopen(MNTTAB, "r"); 801 if (fp == NULL) { 802 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 803 return (NULL); 804 } 805 806 resetmnttab(fp); 807 808 mnted = 0; 809 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 810 if (strcmp(mnt.mnt_mountp, STUBBOOT) == 0) { 811 mnted = 1; 812 break; 813 } 814 } 815 816 (void) fclose(fp); 817 818 if (mnted) 819 return (STUBBOOT); 820 821 /* 822 * Stubboot is not mounted, mount it now. 823 * It should exist in /etc/vfstab 824 */ 825 (void) snprintf(cmd, sizeof (cmd), "/sbin/mount %s", 826 STUBBOOT); 827 if (exec_cmd(cmd, NULL) != 0) { 828 bam_error(MOUNT_MNTPT_FAILED, STUBBOOT); 829 return (NULL); 830 } 831 832 return (STUBBOOT); 833 } 834 835 static void 836 disp_active_menu_locn(char *menu_path, char *logslice, char *fstype, int mnted) 837 { 838 /* 839 * Check if we did a temp mount of an unmounted device. 840 * If yes, print the block device and fstype for that device 841 * else it is already mounted, so we print the path to the GRUB menu. 842 */ 843 if (mnted) { 844 bam_print(GRUB_MENU_DEVICE, logslice); 845 bam_print(GRUB_MENU_FSTYPE, fstype); 846 } else { 847 bam_print(GRUB_MENU_PATH, menu_path); 848 } 849 } 850 851 /* 852 * NOTE: A single "/" is also considered a trailing slash and will 853 * be deleted. 854 */ 855 static void 856 elide_trailing_slash(const char *src, char *dst, size_t dstsize) 857 { 858 size_t dstlen; 859 860 assert(src); 861 assert(dst); 862 863 (void) strlcpy(dst, src, dstsize); 864 865 dstlen = strlen(dst); 866 if (dst[dstlen - 1] == '/') { 867 dst[dstlen - 1] = '\0'; 868 } 869 } 870 871 static error_t 872 bam_menu(char *subcmd, char *opt, int largc, char *largv[]) 873 { 874 error_t ret; 875 char menu_path[PATH_MAX]; 876 char path[PATH_MAX]; 877 menu_t *menu; 878 char *mntpt, *menu_root, *logslice, *fstype; 879 struct stat sb; 880 int mnted; /* set if we did a mount */ 881 error_t (*f)(menu_t *mp, char *menu_path, char *opt); 882 char *rootpath; 883 884 /* 885 * Menu sub-command only applies to GRUB (i.e. x86) 886 */ 887 rootpath = (bam_alt_root) ? bam_root : "/"; 888 if (!is_grub((const char *)rootpath)) { 889 bam_error(NOT_ON_SPARC); 890 return (BAM_ERROR); 891 } 892 893 /* 894 * Check arguments 895 */ 896 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f); 897 if (ret == BAM_ERROR) { 898 return (BAM_ERROR); 899 } 900 901 mntpt = NULL; 902 mnted = 0; 903 logslice = fstype = NULL; 904 905 /* 906 * Check for the menu.list file: 907 * 908 * 1. Check for a GRUB_slice file, be it on / or 909 * on the user-provided alternate root. 910 * 2. Use the alternate root, if given. 911 * 3. Check /stubboot 912 * 4. Use / 913 */ 914 if (bam_alt_root) { 915 (void) snprintf(path, sizeof (path), "%s%s", bam_root, 916 GRUB_slice); 917 } else { 918 (void) snprintf(path, sizeof (path), "%s", GRUB_slice); 919 } 920 921 if (stat(path, &sb) == 0) { 922 mntpt = mount_grub_slice(&mnted, NULL, &logslice, &fstype); 923 menu_root = mntpt; 924 } else if (bam_alt_root) { 925 menu_root = bam_root; 926 } else if (stat(STUBBOOT, &sb) == 0) { 927 menu_root = use_stubboot(); 928 } else { 929 menu_root = bam_root; 930 } 931 932 if (menu_root == NULL) { 933 bam_error(CANNOT_LOCATE_GRUB_MENU); 934 return (BAM_ERROR); 935 } 936 937 elide_trailing_slash(menu_root, menu_path, sizeof (menu_path)); 938 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path)); 939 940 /* 941 * If listing the menu, display the active menu 942 * location 943 */ 944 if (strcmp(subcmd, "list_entry") == 0) { 945 disp_active_menu_locn(menu_path, logslice, fstype, mnted); 946 } 947 948 menu = menu_read(menu_path); 949 assert(menu); 950 951 /* 952 * Special handling for setting timeout and default 953 */ 954 if (strcmp(subcmd, "set_option") == 0) { 955 if (largc != 1 || largv[0] == NULL) { 956 usage(); 957 menu_free(menu); 958 umount_grub_slice(mnted, mntpt, NULL, logslice, fstype); 959 return (BAM_ERROR); 960 } 961 opt = largv[0]; 962 } else if (largc != 0) { 963 usage(); 964 menu_free(menu); 965 umount_grub_slice(mnted, mntpt, NULL, logslice, fstype); 966 return (BAM_ERROR); 967 } 968 969 ret = dboot_or_multiboot(bam_root); 970 if (ret != BAM_SUCCESS) 971 return (ret); 972 973 /* 974 * Once the sub-cmd handler has run 975 * only the line field is guaranteed to have valid values 976 */ 977 if ((strcmp(subcmd, "update_entry") == 0) || 978 (strcmp(subcmd, "upgrade") == 0)) 979 ret = f(menu, bam_root, opt); 980 else 981 ret = f(menu, menu_path, opt); 982 if (ret == BAM_WRITE) { 983 ret = menu_write(menu_root, menu); 984 } 985 986 menu_free(menu); 987 988 umount_grub_slice(mnted, mntpt, NULL, logslice, fstype); 989 990 return (ret); 991 } 992 993 994 static error_t 995 bam_archive( 996 char *subcmd, 997 char *opt) 998 { 999 error_t ret; 1000 error_t (*f)(char *root, char *opt); 1001 1002 /* 1003 * Add trailing / for archive subcommands 1004 */ 1005 if (rootbuf[strlen(rootbuf) - 1] != '/') 1006 (void) strcat(rootbuf, "/"); 1007 bam_rootlen = strlen(rootbuf); 1008 1009 /* 1010 * Check arguments 1011 */ 1012 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f); 1013 if (ret != BAM_SUCCESS) { 1014 return (BAM_ERROR); 1015 } 1016 1017 ret = dboot_or_multiboot(rootbuf); 1018 if (ret != BAM_SUCCESS) 1019 return (ret); 1020 1021 /* 1022 * Check archive not supported with update_all 1023 * since it is awkward to display out-of-sync 1024 * information for each BE. 1025 */ 1026 if (bam_check && strcmp(subcmd, "update_all") == 0) { 1027 bam_error(CHECK_NOT_SUPPORTED, subcmd); 1028 return (BAM_ERROR); 1029 } 1030 1031 if (strcmp(subcmd, "update_all") == 0) 1032 bam_update_all = 1; 1033 1034 #if !defined(_OPB) 1035 ucode_install(bam_root); 1036 #endif 1037 1038 ret = f(bam_root, opt); 1039 1040 bam_update_all = 0; 1041 1042 return (ret); 1043 } 1044 1045 /*PRINTFLIKE1*/ 1046 void 1047 bam_error(char *format, ...) 1048 { 1049 va_list ap; 1050 1051 va_start(ap, format); 1052 (void) fprintf(stderr, "%s: ", prog); 1053 (void) vfprintf(stderr, format, ap); 1054 va_end(ap); 1055 } 1056 1057 /*PRINTFLIKE1*/ 1058 static void 1059 bam_print(char *format, ...) 1060 { 1061 va_list ap; 1062 1063 va_start(ap, format); 1064 (void) vfprintf(stdout, format, ap); 1065 va_end(ap); 1066 } 1067 1068 /*PRINTFLIKE1*/ 1069 void 1070 bam_print_stderr(char *format, ...) 1071 { 1072 va_list ap; 1073 1074 va_start(ap, format); 1075 (void) vfprintf(stderr, format, ap); 1076 va_end(ap); 1077 } 1078 1079 static void 1080 bam_exit(int excode) 1081 { 1082 bam_unlock(); 1083 exit(excode); 1084 } 1085 1086 static void 1087 bam_lock(void) 1088 { 1089 struct flock lock; 1090 pid_t pid; 1091 1092 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS); 1093 if (bam_lock_fd < 0) { 1094 /* 1095 * We may be invoked early in boot for archive verification. 1096 * In this case, root is readonly and /var/run may not exist. 1097 * Proceed without the lock 1098 */ 1099 if (errno == EROFS || errno == ENOENT) { 1100 bam_root_readonly = 1; 1101 return; 1102 } 1103 1104 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno)); 1105 bam_exit(1); 1106 } 1107 1108 lock.l_type = F_WRLCK; 1109 lock.l_whence = SEEK_SET; 1110 lock.l_start = 0; 1111 lock.l_len = 0; 1112 1113 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) { 1114 if (errno != EACCES && errno != EAGAIN) { 1115 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1116 (void) close(bam_lock_fd); 1117 bam_lock_fd = -1; 1118 bam_exit(1); 1119 } 1120 pid = 0; 1121 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0); 1122 bam_print(FILE_LOCKED, pid); 1123 1124 lock.l_type = F_WRLCK; 1125 lock.l_whence = SEEK_SET; 1126 lock.l_start = 0; 1127 lock.l_len = 0; 1128 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) { 1129 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1130 (void) close(bam_lock_fd); 1131 bam_lock_fd = -1; 1132 bam_exit(1); 1133 } 1134 } 1135 1136 /* We own the lock now */ 1137 pid = getpid(); 1138 (void) write(bam_lock_fd, &pid, sizeof (pid)); 1139 } 1140 1141 static void 1142 bam_unlock(void) 1143 { 1144 struct flock unlock; 1145 1146 /* 1147 * NOP if we don't hold the lock 1148 */ 1149 if (bam_lock_fd < 0) { 1150 return; 1151 } 1152 1153 unlock.l_type = F_UNLCK; 1154 unlock.l_whence = SEEK_SET; 1155 unlock.l_start = 0; 1156 unlock.l_len = 0; 1157 1158 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) { 1159 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1160 } 1161 1162 if (close(bam_lock_fd) == -1) { 1163 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno)); 1164 } 1165 bam_lock_fd = -1; 1166 } 1167 1168 static error_t 1169 list_archive(char *root, char *opt) 1170 { 1171 filelist_t flist; 1172 filelist_t *flistp = &flist; 1173 line_t *lp; 1174 1175 assert(root); 1176 assert(opt == NULL); 1177 1178 flistp->head = flistp->tail = NULL; 1179 if (read_list(root, flistp) != BAM_SUCCESS) { 1180 return (BAM_ERROR); 1181 } 1182 assert(flistp->head && flistp->tail); 1183 1184 for (lp = flistp->head; lp; lp = lp->next) { 1185 bam_print(PRINT, lp->line); 1186 } 1187 1188 filelist_free(flistp); 1189 1190 return (BAM_SUCCESS); 1191 } 1192 1193 /* 1194 * This routine writes a list of lines to a file. 1195 * The list is *not* freed 1196 */ 1197 static error_t 1198 list2file(char *root, char *tmp, char *final, line_t *start) 1199 { 1200 char tmpfile[PATH_MAX]; 1201 char path[PATH_MAX]; 1202 FILE *fp; 1203 int ret; 1204 struct stat sb; 1205 mode_t mode; 1206 uid_t root_uid; 1207 gid_t sys_gid; 1208 struct passwd *pw; 1209 struct group *gp; 1210 1211 1212 (void) snprintf(path, sizeof (path), "%s%s", root, final); 1213 1214 if (start == NULL) { 1215 if (stat(path, &sb) != -1) { 1216 bam_print(UNLINK_EMPTY, path); 1217 if (unlink(path) != 0) { 1218 bam_error(UNLINK_FAIL, path, strerror(errno)); 1219 return (BAM_ERROR); 1220 } else { 1221 return (BAM_SUCCESS); 1222 } 1223 } 1224 } 1225 1226 /* 1227 * Preserve attributes of existing file if possible, 1228 * otherwise ask the system for uid/gid of root/sys. 1229 * If all fails, fall back on hard-coded defaults. 1230 */ 1231 if (stat(path, &sb) != -1) { 1232 mode = sb.st_mode; 1233 root_uid = sb.st_uid; 1234 sys_gid = sb.st_gid; 1235 } else { 1236 mode = DEFAULT_DEV_MODE; 1237 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) { 1238 root_uid = pw->pw_uid; 1239 } else { 1240 if (bam_verbose) 1241 bam_error(CANT_FIND_USER, 1242 DEFAULT_DEV_USER, DEFAULT_DEV_UID); 1243 root_uid = (uid_t)DEFAULT_DEV_UID; 1244 } 1245 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) { 1246 sys_gid = gp->gr_gid; 1247 } else { 1248 if (bam_verbose) 1249 bam_error(CANT_FIND_GROUP, 1250 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID); 1251 sys_gid = (gid_t)DEFAULT_DEV_GID; 1252 } 1253 } 1254 1255 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp); 1256 1257 /* Truncate tmpfile first */ 1258 fp = fopen(tmpfile, "w"); 1259 if (fp == NULL) { 1260 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1261 return (BAM_ERROR); 1262 } 1263 ret = fclose(fp); 1264 if (ret == EOF) { 1265 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1266 return (BAM_ERROR); 1267 } 1268 1269 /* Now open it in append mode */ 1270 fp = fopen(tmpfile, "a"); 1271 if (fp == NULL) { 1272 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1273 return (BAM_ERROR); 1274 } 1275 1276 for (; start; start = start->next) { 1277 ret = s_fputs(start->line, fp); 1278 if (ret == EOF) { 1279 bam_error(WRITE_FAIL, tmpfile, strerror(errno)); 1280 (void) fclose(fp); 1281 return (BAM_ERROR); 1282 } 1283 } 1284 1285 ret = fclose(fp); 1286 if (ret == EOF) { 1287 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1288 return (BAM_ERROR); 1289 } 1290 1291 /* 1292 * Set up desired attributes. Ignore failures on filesystems 1293 * not supporting these operations - pcfs reports unsupported 1294 * operations as EINVAL. 1295 */ 1296 ret = chmod(tmpfile, mode); 1297 if (ret == -1 && 1298 errno != EINVAL && errno != ENOTSUP) { 1299 bam_error(CHMOD_FAIL, tmpfile, strerror(errno)); 1300 return (BAM_ERROR); 1301 } 1302 1303 ret = chown(tmpfile, root_uid, sys_gid); 1304 if (ret == -1 && 1305 errno != EINVAL && errno != ENOTSUP) { 1306 bam_error(CHOWN_FAIL, tmpfile, strerror(errno)); 1307 return (BAM_ERROR); 1308 } 1309 1310 1311 /* 1312 * Do an atomic rename 1313 */ 1314 ret = rename(tmpfile, path); 1315 if (ret != 0) { 1316 bam_error(RENAME_FAIL, path, strerror(errno)); 1317 return (BAM_ERROR); 1318 } 1319 1320 return (BAM_SUCCESS); 1321 } 1322 1323 /* 1324 * This function should always return 0 - since we want 1325 * to create stat data for *all* files in the list. 1326 */ 1327 /*ARGSUSED*/ 1328 static int 1329 cmpstat( 1330 const char *file, 1331 const struct stat *stat, 1332 int flags, 1333 struct FTW *ftw) 1334 { 1335 uint_t sz; 1336 uint64_t *value; 1337 uint64_t filestat[2]; 1338 int error; 1339 1340 struct safefile *safefilep; 1341 FILE *fp; 1342 1343 /* 1344 * We only want regular files 1345 */ 1346 if (!S_ISREG(stat->st_mode)) 1347 return (0); 1348 1349 /* 1350 * new_nvlp may be NULL if there were errors earlier 1351 * but this is not fatal to update determination. 1352 */ 1353 if (walk_arg.new_nvlp) { 1354 filestat[0] = stat->st_size; 1355 filestat[1] = stat->st_mtime; 1356 error = nvlist_add_uint64_array(walk_arg.new_nvlp, 1357 file + bam_rootlen, filestat, 2); 1358 if (error) 1359 bam_error(NVADD_FAIL, file, strerror(error)); 1360 } 1361 1362 /* 1363 * The remaining steps are only required if we haven't made a 1364 * decision about update or if we are checking (-n) 1365 */ 1366 if (walk_arg.need_update && !bam_check) 1367 return (0); 1368 1369 /* 1370 * If we are invoked as part of system/filesyste/boot-archive, then 1371 * there are a number of things we should not worry about 1372 */ 1373 if (bam_smf_check) { 1374 /* ignore amd64 modules unless we are booted amd64. */ 1375 if (!is_amd64() && strstr(file, "/amd64/") != 0) 1376 return (0); 1377 1378 /* read in list of safe files */ 1379 if (safefiles == NULL) 1380 if (fp = fopen("/boot/solaris/filelist.safe", "r")) { 1381 safefiles = s_calloc(1, 1382 sizeof (struct safefile)); 1383 safefilep = safefiles; 1384 safefilep->name = s_calloc(1, MAXPATHLEN + 1385 MAXNAMELEN); 1386 safefilep->next = NULL; 1387 while (s_fgets(safefilep->name, MAXPATHLEN + 1388 MAXNAMELEN, fp) != NULL) { 1389 safefilep->next = s_calloc(1, 1390 sizeof (struct safefile)); 1391 safefilep = safefilep->next; 1392 safefilep->name = s_calloc(1, 1393 MAXPATHLEN + MAXNAMELEN); 1394 safefilep->next = NULL; 1395 } 1396 (void) fclose(fp); 1397 } 1398 } 1399 1400 /* 1401 * We need an update if file doesn't exist in old archive 1402 */ 1403 if (walk_arg.old_nvlp == NULL || 1404 nvlist_lookup_uint64_array(walk_arg.old_nvlp, 1405 file + bam_rootlen, &value, &sz) != 0) { 1406 if (bam_smf_check) /* ignore new during smf check */ 1407 return (0); 1408 walk_arg.need_update = 1; 1409 if (bam_verbose) 1410 bam_print(PARSEABLE_NEW_FILE, file); 1411 return (0); 1412 } 1413 1414 /* 1415 * File exists in old archive. Check if file has changed 1416 */ 1417 assert(sz == 2); 1418 bcopy(value, filestat, sizeof (filestat)); 1419 1420 if (filestat[0] != stat->st_size || 1421 filestat[1] != stat->st_mtime) { 1422 if (bam_smf_check) { 1423 safefilep = safefiles; 1424 while (safefilep != NULL) { 1425 if (strcmp(file + bam_rootlen, 1426 safefilep->name) == 0) { 1427 (void) creat(NEED_UPDATE_FILE, 0644); 1428 return (0); 1429 } 1430 safefilep = safefilep->next; 1431 } 1432 } 1433 walk_arg.need_update = 1; 1434 if (bam_verbose) 1435 if (bam_smf_check) 1436 bam_print(" %s\n", file); 1437 else 1438 bam_print(PARSEABLE_OUT_DATE, file); 1439 } 1440 1441 return (0); 1442 } 1443 1444 /* 1445 * Check flags and presence of required files. 1446 * The force flag and/or absence of files should 1447 * trigger an update. 1448 * Suppress stdout output if check (-n) option is set 1449 * (as -n should only produce parseable output.) 1450 */ 1451 static void 1452 check_flags_and_files(char *root) 1453 { 1454 char path[PATH_MAX]; 1455 struct stat sb; 1456 1457 /* 1458 * if force, create archive unconditionally 1459 */ 1460 if (bam_force) { 1461 walk_arg.need_update = 1; 1462 if (bam_verbose && !bam_check) 1463 bam_print(UPDATE_FORCE); 1464 return; 1465 } 1466 1467 /* 1468 * If archive is missing, create archive 1469 */ 1470 1471 if (is_sun4u()) 1472 (void) snprintf(path, sizeof (path), "%s%s", root, 1473 SUN4U__ARCHIVE); 1474 else if (is_sun4v()) 1475 (void) snprintf(path, sizeof (path), "%s%s", root, 1476 SUN4V__ARCHIVE); 1477 else { 1478 if (bam_direct == BAM_DIRECT_DBOOT) { 1479 (void) snprintf(path, sizeof (path), "%s%s", root, 1480 DIRECT_BOOT_ARCHIVE_64); 1481 if (stat(path, &sb) != 0) { 1482 if (bam_verbose && !bam_check) 1483 bam_print(UPDATE_ARCH_MISS, path); 1484 walk_arg.need_update = 1; 1485 return; 1486 } 1487 } 1488 (void) snprintf(path, sizeof (path), "%s%s", root, 1489 DIRECT_BOOT_ARCHIVE_32); 1490 } 1491 1492 if (stat(path, &sb) != 0) { 1493 if (bam_verbose && !bam_check) 1494 bam_print(UPDATE_ARCH_MISS, path); 1495 walk_arg.need_update = 1; 1496 return; 1497 } 1498 } 1499 1500 static error_t 1501 read_one_list(char *root, filelist_t *flistp, char *filelist) 1502 { 1503 char path[PATH_MAX]; 1504 FILE *fp; 1505 char buf[BAM_MAXLINE]; 1506 1507 (void) snprintf(path, sizeof (path), "%s%s", root, filelist); 1508 1509 fp = fopen(path, "r"); 1510 if (fp == NULL) { 1511 if (bam_debug) 1512 bam_error(FLIST_FAIL, path, strerror(errno)); 1513 return (BAM_ERROR); 1514 } 1515 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 1516 /* skip blank lines */ 1517 if (strspn(buf, " \t") == strlen(buf)) 1518 continue; 1519 append_to_flist(flistp, buf); 1520 } 1521 if (fclose(fp) != 0) { 1522 bam_error(CLOSE_FAIL, path, strerror(errno)); 1523 return (BAM_ERROR); 1524 } 1525 return (BAM_SUCCESS); 1526 } 1527 1528 static error_t 1529 read_list(char *root, filelist_t *flistp) 1530 { 1531 char path[PATH_MAX]; 1532 char cmd[PATH_MAX]; 1533 struct stat sb; 1534 int n, rval; 1535 1536 flistp->head = flistp->tail = NULL; 1537 1538 /* 1539 * build and check path to extract_boot_filelist.ksh 1540 */ 1541 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST); 1542 if (n >= sizeof (path)) { 1543 bam_error(NO_FLIST); 1544 return (BAM_ERROR); 1545 } 1546 1547 /* 1548 * If extract_boot_filelist is present, exec it, otherwise read 1549 * the filelists directly, for compatibility with older images. 1550 */ 1551 if (stat(path, &sb) == 0) { 1552 /* 1553 * build arguments to exec extract_boot_filelist.ksh 1554 */ 1555 if (strlen(root) > 1) { 1556 n = snprintf(cmd, sizeof (cmd), "%s -R %s /%s /%s", 1557 path, root, BOOT_FILE_LIST, ETC_FILE_LIST); 1558 } else { 1559 n = snprintf(cmd, sizeof (cmd), "%s /%s /%s", 1560 path, BOOT_FILE_LIST, ETC_FILE_LIST); 1561 } 1562 if (n >= sizeof (cmd)) { 1563 bam_error(NO_FLIST); 1564 return (BAM_ERROR); 1565 } 1566 if (exec_cmd(cmd, flistp) != 0) { 1567 if (bam_debug) 1568 bam_error(FLIST_FAIL, path, strerror(errno)); 1569 return (BAM_ERROR); 1570 } 1571 } else { 1572 /* 1573 * Read current lists of files - only the first is mandatory 1574 */ 1575 rval = read_one_list(root, flistp, BOOT_FILE_LIST); 1576 if (rval != BAM_SUCCESS) 1577 return (rval); 1578 (void) read_one_list(root, flistp, ETC_FILE_LIST); 1579 } 1580 1581 if (flistp->head == NULL) { 1582 bam_error(NO_FLIST); 1583 return (BAM_ERROR); 1584 } 1585 1586 return (BAM_SUCCESS); 1587 } 1588 1589 static void 1590 getoldstat(char *root) 1591 { 1592 char path[PATH_MAX]; 1593 int fd, error; 1594 struct stat sb; 1595 char *ostat; 1596 1597 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 1598 fd = open(path, O_RDONLY); 1599 if (fd == -1) { 1600 if (bam_verbose) 1601 bam_print(OPEN_FAIL, path, strerror(errno)); 1602 walk_arg.need_update = 1; 1603 return; 1604 } 1605 1606 if (fstat(fd, &sb) != 0) { 1607 bam_error(STAT_FAIL, path, strerror(errno)); 1608 (void) close(fd); 1609 walk_arg.need_update = 1; 1610 return; 1611 } 1612 1613 ostat = s_calloc(1, sb.st_size); 1614 1615 if (read(fd, ostat, sb.st_size) != sb.st_size) { 1616 bam_error(READ_FAIL, path, strerror(errno)); 1617 (void) close(fd); 1618 free(ostat); 1619 walk_arg.need_update = 1; 1620 return; 1621 } 1622 1623 (void) close(fd); 1624 1625 walk_arg.old_nvlp = NULL; 1626 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0); 1627 1628 free(ostat); 1629 1630 if (error) { 1631 bam_error(UNPACK_FAIL, path, strerror(error)); 1632 walk_arg.old_nvlp = NULL; 1633 walk_arg.need_update = 1; 1634 return; 1635 } 1636 } 1637 1638 /* 1639 * Checks if a file in the current (old) archive has 1640 * been deleted from the root filesystem. This is needed for 1641 * software like Trusted Extensions (TX) that switch early 1642 * in boot based on presence/absence of a kernel module. 1643 */ 1644 static void 1645 check4stale(char *root) 1646 { 1647 nvpair_t *nvp; 1648 nvlist_t *nvlp; 1649 char *file; 1650 char path[PATH_MAX]; 1651 struct stat sb; 1652 1653 /* 1654 * Skip stale file check during smf check 1655 */ 1656 if (bam_smf_check) 1657 return; 1658 1659 /* Nothing to do if no old stats */ 1660 if ((nvlp = walk_arg.old_nvlp) == NULL) 1661 return; 1662 1663 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp; 1664 nvp = nvlist_next_nvpair(nvlp, nvp)) { 1665 file = nvpair_name(nvp); 1666 if (file == NULL) 1667 continue; 1668 (void) snprintf(path, sizeof (path), "%s/%s", 1669 root, file); 1670 if (stat(path, &sb) == -1) { 1671 walk_arg.need_update = 1; 1672 if (bam_verbose) 1673 bam_print(PARSEABLE_STALE_FILE, path); 1674 } 1675 } 1676 } 1677 1678 static void 1679 create_newstat(void) 1680 { 1681 int error; 1682 1683 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0); 1684 if (error) { 1685 /* 1686 * Not fatal - we can still create archive 1687 */ 1688 walk_arg.new_nvlp = NULL; 1689 bam_error(NVALLOC_FAIL, strerror(error)); 1690 } 1691 } 1692 1693 static void 1694 walk_list(char *root, filelist_t *flistp) 1695 { 1696 char path[PATH_MAX]; 1697 line_t *lp; 1698 1699 for (lp = flistp->head; lp; lp = lp->next) { 1700 /* 1701 * Don't follow symlinks. A symlink must refer to 1702 * a file that would appear in the archive through 1703 * a direct reference. This matches the archive 1704 * construction behavior. 1705 */ 1706 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line); 1707 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) { 1708 /* 1709 * Some files may not exist. 1710 * For example: etc/rtc_config on a x86 diskless system 1711 * Emit verbose message only 1712 */ 1713 if (bam_verbose) 1714 bam_print(NFTW_FAIL, path, strerror(errno)); 1715 } 1716 } 1717 } 1718 1719 static void 1720 savenew(char *root) 1721 { 1722 char path[PATH_MAX]; 1723 char path2[PATH_MAX]; 1724 size_t sz; 1725 char *nstat; 1726 int fd, wrote, error; 1727 1728 nstat = NULL; 1729 sz = 0; 1730 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz, 1731 NV_ENCODE_XDR, 0); 1732 if (error) { 1733 bam_error(PACK_FAIL, strerror(error)); 1734 return; 1735 } 1736 1737 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP); 1738 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE); 1739 if (fd == -1) { 1740 bam_error(OPEN_FAIL, path, strerror(errno)); 1741 free(nstat); 1742 return; 1743 } 1744 wrote = write(fd, nstat, sz); 1745 if (wrote != sz) { 1746 bam_error(WRITE_FAIL, path, strerror(errno)); 1747 (void) close(fd); 1748 free(nstat); 1749 return; 1750 } 1751 (void) close(fd); 1752 free(nstat); 1753 1754 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT); 1755 if (rename(path, path2) != 0) { 1756 bam_error(RENAME_FAIL, path2, strerror(errno)); 1757 } 1758 } 1759 1760 static void 1761 clear_walk_args(void) 1762 { 1763 if (walk_arg.old_nvlp) 1764 nvlist_free(walk_arg.old_nvlp); 1765 if (walk_arg.new_nvlp) 1766 nvlist_free(walk_arg.new_nvlp); 1767 walk_arg.need_update = 0; 1768 walk_arg.old_nvlp = NULL; 1769 walk_arg.new_nvlp = NULL; 1770 } 1771 1772 /* 1773 * Returns: 1774 * 0 - no update necessary 1775 * 1 - update required. 1776 * BAM_ERROR (-1) - An error occurred 1777 * 1778 * Special handling for check (-n): 1779 * ================================ 1780 * The check (-n) option produces parseable output. 1781 * To do this, we suppress all stdout messages unrelated 1782 * to out of sync files. 1783 * All stderr messages are still printed though. 1784 * 1785 */ 1786 static int 1787 update_required(char *root) 1788 { 1789 struct stat sb; 1790 char path[PATH_MAX]; 1791 filelist_t flist; 1792 filelist_t *flistp = &flist; 1793 int need_update; 1794 1795 flistp->head = flistp->tail = NULL; 1796 1797 walk_arg.need_update = 0; 1798 1799 /* 1800 * Without consulting stat data, check if we need update 1801 */ 1802 check_flags_and_files(root); 1803 1804 /* 1805 * In certain deployment scenarios, filestat may not 1806 * exist. Ignore it during boot-archive SMF check. 1807 */ 1808 if (bam_smf_check) { 1809 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 1810 if (stat(path, &sb) != 0) 1811 return (0); 1812 } 1813 1814 /* 1815 * consult stat data only if we haven't made a decision 1816 * about update. If checking (-n) however, we always 1817 * need stat data (since we want to compare old and new) 1818 */ 1819 if (!walk_arg.need_update || bam_check) 1820 getoldstat(root); 1821 1822 /* 1823 * Check if the archive contains files that are no longer 1824 * present on the root filesystem. 1825 */ 1826 if (!walk_arg.need_update || bam_check) 1827 check4stale(root); 1828 1829 /* 1830 * read list of files 1831 */ 1832 if (read_list(root, flistp) != BAM_SUCCESS) { 1833 clear_walk_args(); 1834 return (BAM_ERROR); 1835 } 1836 1837 assert(flistp->head && flistp->tail); 1838 1839 /* 1840 * At this point either the update is required 1841 * or the decision is pending. In either case 1842 * we need to create new stat nvlist 1843 */ 1844 create_newstat(); 1845 1846 /* 1847 * This walk does 2 things: 1848 * - gets new stat data for every file 1849 * - (optional) compare old and new stat data 1850 */ 1851 walk_list(root, &flist); 1852 1853 /* done with the file list */ 1854 filelist_free(flistp); 1855 1856 /* 1857 * if we didn't succeed in creating new stat data above 1858 * just return result of update check so that archive is built. 1859 */ 1860 if (walk_arg.new_nvlp == NULL) { 1861 bam_error(NO_NEW_STAT); 1862 need_update = walk_arg.need_update; 1863 clear_walk_args(); 1864 return (need_update ? 1 : 0); 1865 } 1866 1867 1868 /* 1869 * If no update required, discard newstat 1870 */ 1871 if (!walk_arg.need_update) { 1872 clear_walk_args(); 1873 return (0); 1874 } 1875 1876 return (1); 1877 } 1878 1879 static error_t 1880 create_ramdisk(char *root) 1881 { 1882 char *cmdline, path[PATH_MAX]; 1883 size_t len; 1884 struct stat sb; 1885 1886 /* 1887 * Setup command args for create_ramdisk.ksh 1888 */ 1889 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 1890 if (stat(path, &sb) != 0) { 1891 bam_error(ARCH_EXEC_MISS, path, strerror(errno)); 1892 return (BAM_ERROR); 1893 } 1894 1895 len = strlen(path) + strlen(root) + 10; /* room for space + -R */ 1896 cmdline = s_calloc(1, len); 1897 1898 if (strlen(root) > 1) { 1899 (void) snprintf(cmdline, len, "%s -R %s", path, root); 1900 /* chop off / at the end */ 1901 cmdline[strlen(cmdline) - 1] = '\0'; 1902 } else 1903 (void) snprintf(cmdline, len, "%s", path); 1904 1905 if (exec_cmd(cmdline, NULL) != 0) { 1906 bam_error(ARCHIVE_FAIL, cmdline); 1907 free(cmdline); 1908 return (BAM_ERROR); 1909 } 1910 free(cmdline); 1911 1912 /* 1913 * The existence of the expected archives used to be 1914 * verified here. This check is done in create_ramdisk as 1915 * it needs to be in sync with the altroot operated upon. 1916 */ 1917 1918 return (BAM_SUCCESS); 1919 } 1920 1921 /* 1922 * Checks if target filesystem is on a ramdisk 1923 * 1 - is miniroot 1924 * 0 - is not 1925 * When in doubt assume it is not a ramdisk. 1926 */ 1927 static int 1928 is_ramdisk(char *root) 1929 { 1930 struct extmnttab mnt; 1931 FILE *fp; 1932 int found; 1933 char mntpt[PATH_MAX]; 1934 char *cp; 1935 1936 /* 1937 * There are 3 situations where creating archive is 1938 * of dubious value: 1939 * - create boot_archive on a lofi-mounted boot_archive 1940 * - create it on a ramdisk which is the root filesystem 1941 * - create it on a ramdisk mounted somewhere else 1942 * The first is not easy to detect and checking for it is not 1943 * worth it. 1944 * The other two conditions are handled here 1945 */ 1946 1947 fp = fopen(MNTTAB, "r"); 1948 if (fp == NULL) { 1949 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 1950 return (0); 1951 } 1952 1953 resetmnttab(fp); 1954 1955 /* 1956 * Remove any trailing / from the mount point 1957 */ 1958 (void) strlcpy(mntpt, root, sizeof (mntpt)); 1959 if (strcmp(root, "/") != 0) { 1960 cp = mntpt + strlen(mntpt) - 1; 1961 if (*cp == '/') 1962 *cp = '\0'; 1963 } 1964 found = 0; 1965 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 1966 if (strcmp(mnt.mnt_mountp, mntpt) == 0) { 1967 found = 1; 1968 break; 1969 } 1970 } 1971 1972 if (!found) { 1973 if (bam_verbose) 1974 bam_error(NOT_IN_MNTTAB, mntpt); 1975 (void) fclose(fp); 1976 return (0); 1977 } 1978 1979 if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) { 1980 if (bam_verbose) 1981 bam_error(IS_RAMDISK, bam_root); 1982 (void) fclose(fp); 1983 return (1); 1984 } 1985 1986 (void) fclose(fp); 1987 1988 return (0); 1989 } 1990 1991 static int 1992 is_boot_archive(char *root) 1993 { 1994 char path[PATH_MAX]; 1995 struct stat sb; 1996 1997 /* 1998 * We can't create an archive without the create_ramdisk script 1999 */ 2000 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 2001 if (stat(path, &sb) == -1) { 2002 if (bam_verbose) 2003 bam_print(FILE_MISS, path); 2004 return (0); 2005 } 2006 2007 return (1); 2008 } 2009 2010 /* 2011 * Need to call this for anything that operates on the GRUB menu 2012 */ 2013 int 2014 is_grub(const char *root) 2015 { 2016 char path[PATH_MAX]; 2017 struct stat sb; 2018 2019 /* 2020 * GRUB_DIR is required to modify the menu 2021 */ 2022 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR); 2023 if (stat(path, &sb) == -1) { 2024 if (bam_debug) 2025 bam_print(DIR_MISS, path); 2026 return (0); 2027 } 2028 2029 return (1); 2030 } 2031 2032 static int 2033 is_readonly(char *root) 2034 { 2035 struct statvfs vfs; 2036 2037 /* 2038 * Check for RDONLY filesystem 2039 * When in doubt assume it is not readonly 2040 */ 2041 if (statvfs(root, &vfs) != 0) { 2042 if (bam_verbose) 2043 bam_error(STATVFS_FAIL, root, strerror(errno)); 2044 return (0); 2045 } 2046 2047 if (vfs.f_flag & ST_RDONLY) { 2048 return (1); 2049 } 2050 2051 return (0); 2052 } 2053 2054 static error_t 2055 update_archive(char *root, char *opt) 2056 { 2057 error_t ret; 2058 2059 assert(root); 2060 assert(opt == NULL); 2061 2062 /* 2063 * root must belong to a boot archive based OS, 2064 */ 2065 if (!is_boot_archive(root)) { 2066 /* 2067 * Emit message only if not in context of update_all. 2068 * If in update_all, emit only if verbose flag is set. 2069 */ 2070 if (!bam_update_all || bam_verbose) 2071 bam_print(NOT_GRUB_BOOT, root); 2072 return (BAM_SUCCESS); 2073 } 2074 2075 /* 2076 * If smf check is requested when / is writable (can happen 2077 * on first reboot following an upgrade because service 2078 * dependency is messed up), skip the check. 2079 */ 2080 if (bam_smf_check && !bam_root_readonly) 2081 return (BAM_SUCCESS); 2082 2083 /* 2084 * root must be writable. This check applies to alternate 2085 * root (-R option); bam_root_readonly applies to '/' only. 2086 * Note: statvfs() does not always report the truth 2087 */ 2088 if (!bam_smf_check && !bam_check && is_readonly(root)) { 2089 if (bam_verbose) 2090 bam_print(RDONLY_FS, root); 2091 return (BAM_SUCCESS); 2092 } 2093 2094 /* 2095 * Don't generate archive on ramdisk 2096 */ 2097 if (is_ramdisk(root)) { 2098 if (bam_verbose) 2099 bam_print(SKIP_RAMDISK); 2100 return (BAM_SUCCESS); 2101 } 2102 2103 /* 2104 * Now check if updated is really needed 2105 */ 2106 ret = update_required(root); 2107 2108 /* 2109 * The check command (-n) is *not* a dry run 2110 * It only checks if the archive is in sync. 2111 */ 2112 if (bam_check) { 2113 bam_exit((ret != 0) ? 1 : 0); 2114 } 2115 2116 if (ret == 1) { 2117 /* create the ramdisk */ 2118 ret = create_ramdisk(root); 2119 } 2120 2121 /* if the archive is updated, save the new stat data */ 2122 if (ret == 0 && walk_arg.new_nvlp != NULL) { 2123 savenew(root); 2124 } 2125 2126 clear_walk_args(); 2127 2128 return (ret); 2129 } 2130 2131 static void 2132 update_fdisk(void) 2133 { 2134 struct stat sb; 2135 char cmd[PATH_MAX]; 2136 int ret1, ret2; 2137 2138 assert(stat(GRUB_fdisk, &sb) == 0); 2139 assert(stat(GRUB_fdisk_target, &sb) == 0); 2140 2141 (void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`", 2142 GRUB_fdisk, GRUB_fdisk_target); 2143 2144 bam_print(UPDATING_FDISK); 2145 if (exec_cmd(cmd, NULL) != 0) { 2146 bam_error(FDISK_UPDATE_FAILED); 2147 } 2148 2149 /* 2150 * We are done, remove the files. 2151 */ 2152 ret1 = unlink(GRUB_fdisk); 2153 ret2 = unlink(GRUB_fdisk_target); 2154 if (ret1 != 0 || ret2 != 0) { 2155 bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target); 2156 } 2157 } 2158 2159 static void 2160 restore_grub_slice(void) 2161 { 2162 struct stat sb; 2163 char *mntpt, *physlice; 2164 int mnted; /* set if we did a mount */ 2165 char menupath[PATH_MAX], cmd[PATH_MAX]; 2166 2167 if (stat(GRUB_slice, &sb) != 0) { 2168 bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno)); 2169 return; 2170 } 2171 2172 /* 2173 * If we are doing an luactivate, don't attempt to restore GRUB or else 2174 * we may not be able to get to DCA boot environments. Let luactivate 2175 * handle GRUB/DCA installation 2176 */ 2177 if (stat(LU_ACTIVATE_FILE, &sb) == 0) { 2178 return; 2179 } 2180 2181 mnted = 0; 2182 physlice = NULL; 2183 mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL); 2184 if (mntpt == NULL) { 2185 bam_error(CANNOT_RESTORE_GRUB_SLICE); 2186 return; 2187 } 2188 2189 (void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU); 2190 if (stat(menupath, &sb) == 0) { 2191 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2192 return; 2193 } 2194 2195 /* 2196 * The menu is missing - we need to do a restore 2197 */ 2198 bam_print(RESTORING_GRUB); 2199 2200 (void) snprintf(cmd, sizeof (cmd), "%s %s %s %s", 2201 INSTALLGRUB, STAGE1, STAGE2, physlice); 2202 2203 if (exec_cmd(cmd, NULL) != 0) { 2204 bam_error(RESTORE_GRUB_FAILED); 2205 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2206 return; 2207 } 2208 2209 if (stat(GRUB_backup_menu, &sb) != 0) { 2210 bam_error(MISSING_BACKUP_MENU, 2211 GRUB_backup_menu, strerror(errno)); 2212 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2213 return; 2214 } 2215 2216 (void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s", 2217 GRUB_backup_menu, menupath); 2218 2219 if (exec_cmd(cmd, NULL) != 0) { 2220 bam_error(RESTORE_MENU_FAILED, menupath); 2221 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2222 return; 2223 } 2224 2225 /* Success */ 2226 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2227 } 2228 2229 static error_t 2230 update_all(char *root, char *opt) 2231 { 2232 struct extmnttab mnt; 2233 struct stat sb; 2234 FILE *fp; 2235 char multibt[PATH_MAX]; 2236 char creatram[PATH_MAX]; 2237 error_t ret = BAM_SUCCESS; 2238 int ret1, ret2; 2239 2240 assert(root); 2241 assert(opt == NULL); 2242 2243 if (bam_rootlen != 1 || *root != '/') { 2244 elide_trailing_slash(root, multibt, sizeof (multibt)); 2245 bam_error(ALT_ROOT_INVALID, multibt); 2246 return (BAM_ERROR); 2247 } 2248 2249 /* 2250 * Check to see if we are in the midst of safemode patching 2251 * If so skip building the archive for /. Instead build it 2252 * against the latest bits obtained by creating a fresh lofs 2253 * mount of root. 2254 */ 2255 if (stat(LOFS_PATCH_FILE, &sb) == 0) { 2256 if (mkdir(LOFS_PATCH_MNT, 0755) == -1 && 2257 errno != EEXIST) { 2258 bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT, 2259 strerror(errno)); 2260 ret = BAM_ERROR; 2261 goto out; 2262 } 2263 (void) snprintf(multibt, sizeof (multibt), 2264 "/sbin/mount -F lofs -o nosub / %s", LOFS_PATCH_MNT); 2265 if (exec_cmd(multibt, NULL) != 0) { 2266 bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs"); 2267 ret = BAM_ERROR; 2268 } 2269 if (ret != BAM_ERROR) { 2270 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 2271 LOFS_PATCH_MNT); 2272 bam_rootlen = strlen(rootbuf); 2273 if (update_archive(rootbuf, opt) != BAM_SUCCESS) 2274 ret = BAM_ERROR; 2275 /* 2276 * unmount the lofs mount since there could be 2277 * multiple invocations of bootadm -a update_all 2278 */ 2279 (void) snprintf(multibt, sizeof (multibt), 2280 "/sbin/umount %s", LOFS_PATCH_MNT); 2281 if (exec_cmd(multibt, NULL) != 0) { 2282 bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT); 2283 ret = BAM_ERROR; 2284 } 2285 } 2286 } else { 2287 /* 2288 * First update archive for current root 2289 */ 2290 if (update_archive(root, opt) != BAM_SUCCESS) 2291 ret = BAM_ERROR; 2292 } 2293 2294 if (ret == BAM_ERROR) 2295 goto out; 2296 2297 /* 2298 * Now walk the mount table, performing archive update 2299 * for all mounted Newboot root filesystems 2300 */ 2301 fp = fopen(MNTTAB, "r"); 2302 if (fp == NULL) { 2303 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 2304 ret = BAM_ERROR; 2305 goto out; 2306 } 2307 2308 resetmnttab(fp); 2309 2310 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 2311 if (mnt.mnt_special == NULL) 2312 continue; 2313 if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0) 2314 continue; 2315 if (strcmp(mnt.mnt_mountp, "/") == 0) 2316 continue; 2317 2318 (void) snprintf(creatram, sizeof (creatram), "%s/%s", 2319 mnt.mnt_mountp, CREATE_RAMDISK); 2320 2321 if (stat(creatram, &sb) == -1) 2322 continue; 2323 2324 /* 2325 * We put a trailing slash to be consistent with root = "/" 2326 * case, such that we don't have to print // in some cases. 2327 */ 2328 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 2329 mnt.mnt_mountp); 2330 bam_rootlen = strlen(rootbuf); 2331 2332 /* 2333 * It's possible that other mounts may be an alternate boot 2334 * architecture, so check it again. 2335 */ 2336 if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) || 2337 (update_archive(rootbuf, opt) != BAM_SUCCESS)) 2338 ret = BAM_ERROR; 2339 } 2340 2341 (void) fclose(fp); 2342 2343 out: 2344 if (stat(GRUB_slice, &sb) == 0) { 2345 restore_grub_slice(); 2346 } 2347 2348 /* 2349 * Update fdisk table as we go down. Updating it when 2350 * the system is running will confuse biosdev. 2351 */ 2352 ret1 = stat(GRUB_fdisk, &sb); 2353 ret2 = stat(GRUB_fdisk_target, &sb); 2354 if ((ret1 == 0) && (ret2 == 0)) { 2355 update_fdisk(); 2356 } else if ((ret1 == 0) ^ (ret2 == 0)) { 2357 /* 2358 * It is an error for one file to be 2359 * present and the other absent. 2360 * It is normal for both files to be 2361 * absent - it indicates that no fdisk 2362 * update is required. 2363 */ 2364 bam_error(MISSING_FDISK_FILE, 2365 ret1 ? GRUB_fdisk : GRUB_fdisk_target); 2366 ret = BAM_ERROR; 2367 } 2368 2369 return (ret); 2370 } 2371 2372 static void 2373 append_line(menu_t *mp, line_t *lp) 2374 { 2375 if (mp->start == NULL) { 2376 mp->start = lp; 2377 } else { 2378 mp->end->next = lp; 2379 lp->prev = mp->end; 2380 } 2381 mp->end = lp; 2382 } 2383 2384 static void 2385 unlink_line(menu_t *mp, line_t *lp) 2386 { 2387 /* unlink from list */ 2388 if (lp->prev) 2389 lp->prev->next = lp->next; 2390 else 2391 mp->start = lp->next; 2392 if (lp->next) 2393 lp->next->prev = lp->prev; 2394 else 2395 mp->end = lp->prev; 2396 } 2397 2398 static entry_t * 2399 boot_entry_new(menu_t *mp, line_t *start, line_t *end) 2400 { 2401 entry_t *ent, *prev; 2402 2403 ent = s_calloc(1, sizeof (entry_t)); 2404 ent->start = start; 2405 ent->end = end; 2406 2407 if (mp->entries == NULL) { 2408 mp->entries = ent; 2409 return (ent); 2410 } 2411 2412 prev = mp->entries; 2413 while (prev->next) 2414 prev = prev-> next; 2415 prev->next = ent; 2416 ent->prev = prev; 2417 return (ent); 2418 } 2419 2420 static void 2421 boot_entry_addline(entry_t *ent, line_t *lp) 2422 { 2423 if (ent) 2424 ent->end = lp; 2425 } 2426 2427 /* 2428 * Check whether cmd matches the one indexed by which, and whether arg matches 2429 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the 2430 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using 2431 * strstr(), so it can be a partial match. 2432 */ 2433 static int 2434 check_cmd(const char *cmd, const int which, const char *arg, const char *str) 2435 { 2436 if ((strcmp(cmd, menu_cmds[which]) != 0) && 2437 (strcmp(cmd, menu_cmds[which + 1]) != 0)) { 2438 return (0); 2439 } 2440 return (strstr(arg, str) != NULL); 2441 } 2442 2443 /* 2444 * A line in menu.lst looks like 2445 * [ ]*<cmd>[ \t=]*<arg>* 2446 */ 2447 static void 2448 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum) 2449 { 2450 /* 2451 * save state across calls. This is so that 2452 * header gets the right entry# after title has 2453 * been processed 2454 */ 2455 static line_t *prev = NULL; 2456 static entry_t *curr_ent = NULL; 2457 static int in_liveupgrade = 0; 2458 2459 line_t *lp; 2460 char *cmd, *sep, *arg; 2461 char save, *cp, *line; 2462 menu_flag_t flag = BAM_INVALID; 2463 2464 if (str == NULL) { 2465 return; 2466 } 2467 2468 /* 2469 * First save a copy of the entire line. 2470 * We use this later to set the line field. 2471 */ 2472 line = s_strdup(str); 2473 2474 /* Eat up leading whitespace */ 2475 while (*str == ' ' || *str == '\t') 2476 str++; 2477 2478 if (*str == '#') { /* comment */ 2479 cmd = s_strdup("#"); 2480 sep = NULL; 2481 arg = s_strdup(str + 1); 2482 flag = BAM_COMMENT; 2483 if (strstr(arg, BAM_LU_HDR) != NULL) { 2484 in_liveupgrade = 1; 2485 } else if (strstr(arg, BAM_LU_FTR) != NULL) { 2486 in_liveupgrade = 0; 2487 } 2488 } else if (*str == '\0') { /* blank line */ 2489 cmd = sep = arg = NULL; 2490 flag = BAM_EMPTY; 2491 } else { 2492 /* 2493 * '=' is not a documented separator in grub syntax. 2494 * However various development bits use '=' as a 2495 * separator. In addition, external users also 2496 * use = as a separator. So we will allow that usage. 2497 */ 2498 cp = str; 2499 while (*str != ' ' && *str != '\t' && *str != '=') { 2500 if (*str == '\0') { 2501 cmd = s_strdup(cp); 2502 sep = arg = NULL; 2503 break; 2504 } 2505 str++; 2506 } 2507 2508 if (*str != '\0') { 2509 save = *str; 2510 *str = '\0'; 2511 cmd = s_strdup(cp); 2512 *str = save; 2513 2514 str++; 2515 save = *str; 2516 *str = '\0'; 2517 sep = s_strdup(str - 1); 2518 *str = save; 2519 2520 while (*str == ' ' || *str == '\t') 2521 str++; 2522 if (*str == '\0') 2523 arg = NULL; 2524 else 2525 arg = s_strdup(str); 2526 } 2527 } 2528 2529 lp = s_calloc(1, sizeof (line_t)); 2530 2531 lp->cmd = cmd; 2532 lp->sep = sep; 2533 lp->arg = arg; 2534 lp->line = line; 2535 lp->lineNum = ++(*lineNum); 2536 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) { 2537 lp->entryNum = ++(*entryNum); 2538 lp->flags = BAM_TITLE; 2539 if (prev && prev->flags == BAM_COMMENT && 2540 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2541 prev->entryNum = lp->entryNum; 2542 curr_ent = boot_entry_new(mp, prev, lp); 2543 curr_ent->flags = BAM_ENTRY_BOOTADM; 2544 } else { 2545 curr_ent = boot_entry_new(mp, lp, lp); 2546 if (in_liveupgrade) { 2547 curr_ent->flags = BAM_ENTRY_LU; 2548 } 2549 } 2550 curr_ent->entryNum = *entryNum; 2551 } else if (flag != BAM_INVALID) { 2552 /* 2553 * For header comments, the entry# is "fixed up" 2554 * by the subsequent title 2555 */ 2556 lp->entryNum = *entryNum; 2557 lp->flags = flag; 2558 } else { 2559 lp->entryNum = *entryNum; 2560 2561 if (*entryNum == ENTRY_INIT) { 2562 lp->flags = BAM_GLOBAL; 2563 } else { 2564 lp->flags = BAM_ENTRY; 2565 2566 if (cmd && arg) { 2567 /* 2568 * We only compare for the length of "module" 2569 * so that "module$" will also match. 2570 */ 2571 if (check_cmd(cmd, MODULE_CMD, arg, MINIROOT)) 2572 curr_ent->flags |= BAM_ENTRY_MINIROOT; 2573 else if (check_cmd(cmd, KERNEL_CMD, arg, 2574 "xen.gz")) 2575 curr_ent->flags |= BAM_ENTRY_HV; 2576 else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) 2577 curr_ent->flags |= BAM_ENTRY_ROOT; 2578 else if (strcmp(cmd, 2579 menu_cmds[CHAINLOADER_CMD]) == 0) 2580 curr_ent->flags |= 2581 BAM_ENTRY_CHAINLOADER; 2582 } 2583 } 2584 } 2585 2586 /* record default, old default, and entry line ranges */ 2587 if (lp->flags == BAM_GLOBAL && 2588 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) { 2589 mp->curdefault = lp; 2590 } else if (lp->flags == BAM_COMMENT && 2591 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) { 2592 mp->olddefault = lp; 2593 } else if (lp->flags == BAM_COMMENT && 2594 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) { 2595 mp->old_rc_default = lp; 2596 } else if (lp->flags == BAM_ENTRY || 2597 (lp->flags == BAM_COMMENT && 2598 strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) { 2599 boot_entry_addline(curr_ent, lp); 2600 } 2601 append_line(mp, lp); 2602 2603 prev = lp; 2604 } 2605 2606 static void 2607 update_numbering(menu_t *mp) 2608 { 2609 int lineNum; 2610 int entryNum; 2611 int old_default_value; 2612 line_t *lp, *prev, *default_lp, *default_entry; 2613 char buf[PATH_MAX]; 2614 2615 if (mp->start == NULL) { 2616 return; 2617 } 2618 2619 lineNum = LINE_INIT; 2620 entryNum = ENTRY_INIT; 2621 old_default_value = ENTRY_INIT; 2622 lp = default_lp = default_entry = NULL; 2623 2624 prev = NULL; 2625 for (lp = mp->start; lp; prev = lp, lp = lp->next) { 2626 lp->lineNum = ++lineNum; 2627 2628 /* 2629 * Get the value of the default command 2630 */ 2631 if (lp->entryNum == ENTRY_INIT && lp->cmd && 2632 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 && 2633 lp->arg) { 2634 old_default_value = atoi(lp->arg); 2635 default_lp = lp; 2636 } 2637 2638 /* 2639 * If not boot entry, nothing else to fix for this 2640 * entry 2641 */ 2642 if (lp->entryNum == ENTRY_INIT) 2643 continue; 2644 2645 /* 2646 * Record the position of the default entry. 2647 * The following works because global 2648 * commands like default and timeout should precede 2649 * actual boot entries, so old_default_value 2650 * is already known (or default cmd is missing). 2651 */ 2652 if (default_entry == NULL && 2653 old_default_value != ENTRY_INIT && 2654 lp->entryNum == old_default_value) { 2655 default_entry = lp; 2656 } 2657 2658 /* 2659 * Now fixup the entry number 2660 */ 2661 if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) { 2662 lp->entryNum = ++entryNum; 2663 /* fixup the bootadm header */ 2664 if (prev && prev->flags == BAM_COMMENT && 2665 prev->arg && 2666 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2667 prev->entryNum = lp->entryNum; 2668 } 2669 } else { 2670 lp->entryNum = entryNum; 2671 } 2672 } 2673 2674 /* 2675 * No default command in menu, simply return 2676 */ 2677 if (default_lp == NULL) { 2678 return; 2679 } 2680 2681 free(default_lp->arg); 2682 free(default_lp->line); 2683 2684 if (default_entry == NULL) { 2685 default_lp->arg = s_strdup("0"); 2686 } else { 2687 (void) snprintf(buf, sizeof (buf), "%d", 2688 default_entry->entryNum); 2689 default_lp->arg = s_strdup(buf); 2690 } 2691 2692 /* 2693 * The following is required since only the line field gets 2694 * written back to menu.lst 2695 */ 2696 (void) snprintf(buf, sizeof (buf), "%s%s%s", 2697 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg); 2698 default_lp->line = s_strdup(buf); 2699 } 2700 2701 2702 static menu_t * 2703 menu_read(char *menu_path) 2704 { 2705 FILE *fp; 2706 char buf[BAM_MAXLINE], *cp; 2707 menu_t *mp; 2708 int line, entry, len, n; 2709 2710 mp = s_calloc(1, sizeof (menu_t)); 2711 2712 fp = fopen(menu_path, "r"); 2713 if (fp == NULL) { /* Let the caller handle this error */ 2714 return (mp); 2715 } 2716 2717 2718 /* Note: GRUB boot entry number starts with 0 */ 2719 line = LINE_INIT; 2720 entry = ENTRY_INIT; 2721 cp = buf; 2722 len = sizeof (buf); 2723 while (s_fgets(cp, len, fp) != NULL) { 2724 n = strlen(cp); 2725 if (cp[n - 1] == '\\') { 2726 len -= n - 1; 2727 assert(len >= 2); 2728 cp += n - 1; 2729 continue; 2730 } 2731 line_parser(mp, buf, &line, &entry); 2732 cp = buf; 2733 len = sizeof (buf); 2734 } 2735 2736 if (fclose(fp) == EOF) { 2737 bam_error(CLOSE_FAIL, menu_path, strerror(errno)); 2738 } 2739 2740 return (mp); 2741 } 2742 2743 static error_t 2744 selector(menu_t *mp, char *opt, int *entry, char **title) 2745 { 2746 char *eq; 2747 char *opt_dup; 2748 int entryNum; 2749 2750 assert(mp); 2751 assert(mp->start); 2752 assert(opt); 2753 2754 opt_dup = s_strdup(opt); 2755 2756 if (entry) 2757 *entry = ENTRY_INIT; 2758 if (title) 2759 *title = NULL; 2760 2761 eq = strchr(opt_dup, '='); 2762 if (eq == NULL) { 2763 bam_error(INVALID_OPT, opt); 2764 free(opt_dup); 2765 return (BAM_ERROR); 2766 } 2767 2768 *eq = '\0'; 2769 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) { 2770 assert(mp->end); 2771 entryNum = s_strtol(eq + 1); 2772 if (entryNum < 0 || entryNum > mp->end->entryNum) { 2773 bam_error(INVALID_ENTRY, eq + 1); 2774 free(opt_dup); 2775 return (BAM_ERROR); 2776 } 2777 *entry = entryNum; 2778 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) { 2779 *title = opt + (eq - opt_dup) + 1; 2780 } else { 2781 bam_error(INVALID_OPT, opt); 2782 free(opt_dup); 2783 return (BAM_ERROR); 2784 } 2785 2786 free(opt_dup); 2787 return (BAM_SUCCESS); 2788 } 2789 2790 /* 2791 * If invoked with no titles/entries (opt == NULL) 2792 * only title lines in file are printed. 2793 * 2794 * If invoked with a title or entry #, all 2795 * lines in *every* matching entry are listed 2796 */ 2797 static error_t 2798 list_entry(menu_t *mp, char *menu_path, char *opt) 2799 { 2800 line_t *lp; 2801 int entry = ENTRY_INIT; 2802 int found; 2803 char *title = NULL; 2804 2805 assert(mp); 2806 assert(menu_path); 2807 2808 if (mp->start == NULL) { 2809 bam_error(NO_MENU, menu_path); 2810 return (BAM_ERROR); 2811 } 2812 2813 if (opt != NULL) { 2814 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) { 2815 return (BAM_ERROR); 2816 } 2817 assert((entry != ENTRY_INIT) ^ (title != NULL)); 2818 } else { 2819 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0); 2820 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0); 2821 } 2822 2823 found = 0; 2824 for (lp = mp->start; lp; lp = lp->next) { 2825 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY) 2826 continue; 2827 if (opt == NULL && lp->flags == BAM_TITLE) { 2828 bam_print(PRINT_TITLE, lp->entryNum, 2829 lp->arg); 2830 found = 1; 2831 continue; 2832 } 2833 if (entry != ENTRY_INIT && lp->entryNum == entry) { 2834 bam_print(PRINT, lp->line); 2835 found = 1; 2836 continue; 2837 } 2838 2839 /* 2840 * We set the entry value here so that all lines 2841 * in entry get printed. If we subsequently match 2842 * title in other entries, all lines in those 2843 * entries get printed as well. 2844 */ 2845 if (title && lp->flags == BAM_TITLE && lp->arg && 2846 strncmp(title, lp->arg, strlen(title)) == 0) { 2847 bam_print(PRINT, lp->line); 2848 entry = lp->entryNum; 2849 found = 1; 2850 continue; 2851 } 2852 } 2853 2854 if (!found) { 2855 bam_error(NO_MATCH_ENTRY); 2856 return (BAM_ERROR); 2857 } 2858 2859 return (BAM_SUCCESS); 2860 } 2861 2862 int 2863 add_boot_entry(menu_t *mp, 2864 char *title, 2865 char *root, 2866 char *kernel, 2867 char *mod_kernel, 2868 char *module) 2869 { 2870 int lineNum, entryNum; 2871 char linebuf[BAM_MAXLINE]; 2872 menu_cmd_t k_cmd, m_cmd; 2873 2874 assert(mp); 2875 2876 if (title == NULL) { 2877 title = "Solaris"; /* default to Solaris */ 2878 } 2879 if (kernel == NULL) { 2880 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]); 2881 return (BAM_ERROR); 2882 } 2883 if (module == NULL) { 2884 if (bam_direct != BAM_DIRECT_DBOOT) { 2885 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]); 2886 return (BAM_ERROR); 2887 } 2888 2889 /* Figure the commands out from the kernel line */ 2890 if (strstr(kernel, "$ISADIR") != NULL) { 2891 module = DIRECT_BOOT_ARCHIVE; 2892 k_cmd = KERNEL_DOLLAR_CMD; 2893 m_cmd = MODULE_DOLLAR_CMD; 2894 } else if (strstr(kernel, "amd64") != NULL) { 2895 module = DIRECT_BOOT_ARCHIVE_64; 2896 k_cmd = KERNEL_CMD; 2897 m_cmd = MODULE_CMD; 2898 } else { 2899 module = DIRECT_BOOT_ARCHIVE_32; 2900 k_cmd = KERNEL_CMD; 2901 m_cmd = MODULE_CMD; 2902 } 2903 } else if ((bam_direct == BAM_DIRECT_DBOOT) && 2904 (strstr(kernel, "$ISADIR") != NULL)) { 2905 /* 2906 * If it's a non-failsafe dboot kernel, use the "kernel$" 2907 * command. Otherwise, use "kernel". 2908 */ 2909 k_cmd = KERNEL_DOLLAR_CMD; 2910 m_cmd = MODULE_DOLLAR_CMD; 2911 } else { 2912 k_cmd = KERNEL_CMD; 2913 m_cmd = MODULE_CMD; 2914 } 2915 2916 if (mp->start) { 2917 lineNum = mp->end->lineNum; 2918 entryNum = mp->end->entryNum; 2919 } else { 2920 lineNum = LINE_INIT; 2921 entryNum = ENTRY_INIT; 2922 } 2923 2924 /* 2925 * No separator for comment (HDR/FTR) commands 2926 * The syntax for comments is #<comment> 2927 */ 2928 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2929 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR); 2930 line_parser(mp, linebuf, &lineNum, &entryNum); 2931 2932 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2933 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 2934 line_parser(mp, linebuf, &lineNum, &entryNum); 2935 2936 if (root) { 2937 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2938 menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root); 2939 line_parser(mp, linebuf, &lineNum, &entryNum); 2940 } 2941 2942 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2943 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel); 2944 line_parser(mp, linebuf, &lineNum, &entryNum); 2945 2946 if (mod_kernel != NULL) { 2947 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2948 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel); 2949 line_parser(mp, linebuf, &lineNum, &entryNum); 2950 } 2951 2952 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2953 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module); 2954 line_parser(mp, linebuf, &lineNum, &entryNum); 2955 2956 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2957 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR); 2958 line_parser(mp, linebuf, &lineNum, &entryNum); 2959 2960 return (entryNum); 2961 } 2962 2963 static error_t 2964 do_delete(menu_t *mp, int entryNum) 2965 { 2966 line_t *lp, *freed; 2967 entry_t *ent, *tmp; 2968 int deleted; 2969 2970 assert(entryNum != ENTRY_INIT); 2971 2972 ent = mp->entries; 2973 while (ent) { 2974 lp = ent->start; 2975 /* check entry number and make sure it's a bootadm entry */ 2976 if (lp->flags != BAM_COMMENT || 2977 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 || 2978 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) { 2979 ent = ent->next; 2980 continue; 2981 } 2982 2983 /* free the entry content */ 2984 do { 2985 freed = lp; 2986 lp = lp->next; /* prev stays the same */ 2987 unlink_line(mp, freed); 2988 line_free(freed); 2989 } while (freed != ent->end); 2990 2991 /* free the entry_t structure */ 2992 tmp = ent; 2993 ent = ent->next; 2994 if (tmp->prev) 2995 tmp->prev->next = ent; 2996 else 2997 mp->entries = ent; 2998 if (ent) 2999 ent->prev = tmp->prev; 3000 deleted = 1; 3001 } 3002 3003 if (!deleted && entryNum != ALL_ENTRIES) { 3004 bam_error(NO_BOOTADM_MATCH); 3005 return (BAM_ERROR); 3006 } 3007 3008 /* 3009 * Now that we have deleted an entry, update 3010 * the entry numbering and the default cmd. 3011 */ 3012 update_numbering(mp); 3013 3014 return (BAM_SUCCESS); 3015 } 3016 3017 static error_t 3018 delete_all_entries(menu_t *mp, char *menu_path, char *opt) 3019 { 3020 assert(mp); 3021 assert(opt == NULL); 3022 3023 if (mp->start == NULL) { 3024 bam_print(EMPTY_FILE, menu_path); 3025 return (BAM_SUCCESS); 3026 } 3027 3028 if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) { 3029 return (BAM_ERROR); 3030 } 3031 3032 return (BAM_WRITE); 3033 } 3034 3035 static FILE * 3036 open_diskmap(char *root) 3037 { 3038 FILE *fp; 3039 char cmd[PATH_MAX]; 3040 3041 /* make sure we have a map file */ 3042 fp = fopen(GRUBDISK_MAP, "r"); 3043 if (fp == NULL) { 3044 (void) snprintf(cmd, sizeof (cmd), 3045 "%s/%s > /dev/null", root, CREATE_DISKMAP); 3046 (void) system(cmd); 3047 fp = fopen(GRUBDISK_MAP, "r"); 3048 } 3049 return (fp); 3050 } 3051 3052 #define SECTOR_SIZE 512 3053 3054 static int 3055 get_partition(char *device) 3056 { 3057 int i, fd, is_pcfs, partno = -1; 3058 struct mboot *mboot; 3059 char boot_sect[SECTOR_SIZE]; 3060 char *wholedisk, *slice; 3061 3062 /* form whole disk (p0) */ 3063 slice = device + strlen(device) - 2; 3064 is_pcfs = (*slice != 's'); 3065 if (!is_pcfs) 3066 *slice = '\0'; 3067 wholedisk = s_calloc(1, strlen(device) + 3); 3068 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device); 3069 if (!is_pcfs) 3070 *slice = 's'; 3071 3072 /* read boot sector */ 3073 fd = open(wholedisk, O_RDONLY); 3074 free(wholedisk); 3075 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 3076 return (partno); 3077 } 3078 (void) close(fd); 3079 3080 /* parse fdisk table */ 3081 mboot = (struct mboot *)((void *)boot_sect); 3082 for (i = 0; i < FD_NUMPART; i++) { 3083 struct ipart *part = 3084 (struct ipart *)(uintptr_t)mboot->parts + i; 3085 if (is_pcfs) { /* looking for solaris boot part */ 3086 if (part->systid == 0xbe) { 3087 partno = i; 3088 break; 3089 } 3090 } else { /* look for solaris partition, old and new */ 3091 if (part->systid == SUNIXOS || 3092 part->systid == SUNIXOS2) { 3093 partno = i; 3094 break; 3095 } 3096 } 3097 } 3098 return (partno); 3099 } 3100 3101 static char * 3102 get_grubdisk(char *rootdev, FILE *fp, int on_bootdev) 3103 { 3104 char *grubdisk; /* (hd#,#,#) */ 3105 char *slice; 3106 char *grubhd; 3107 int fdiskpart; 3108 int found = 0; 3109 char *devname, *ctdname = strstr(rootdev, "dsk/"); 3110 char linebuf[PATH_MAX]; 3111 3112 if (ctdname == NULL) 3113 return (NULL); 3114 3115 ctdname += strlen("dsk/"); 3116 slice = strrchr(ctdname, 's'); 3117 if (slice) 3118 *slice = '\0'; 3119 3120 rewind(fp); 3121 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) { 3122 grubhd = strtok(linebuf, " \t\n"); 3123 if (grubhd) 3124 devname = strtok(NULL, " \t\n"); 3125 else 3126 devname = NULL; 3127 if (devname && strcmp(devname, ctdname) == 0) { 3128 found = 1; 3129 break; 3130 } 3131 } 3132 3133 if (slice) 3134 *slice = 's'; 3135 3136 if (found == 0) { 3137 if (bam_verbose) 3138 bam_print(DISKMAP_FAIL_NONFATAL, rootdev); 3139 grubhd = "0"; /* assume disk 0 if can't match */ 3140 } 3141 3142 fdiskpart = get_partition(rootdev); 3143 if (fdiskpart == -1) 3144 return (NULL); 3145 3146 grubdisk = s_calloc(1, 10); 3147 if (slice) { 3148 (void) snprintf(grubdisk, 10, "(hd%s,%d,%c)", 3149 grubhd, fdiskpart, slice[1] + 'a' - '0'); 3150 } else 3151 (void) snprintf(grubdisk, 10, "(hd%s,%d)", 3152 grubhd, fdiskpart); 3153 3154 /* if root not on bootdev, change GRUB disk to 0 */ 3155 if (!on_bootdev) 3156 grubdisk[3] = '0'; 3157 return (grubdisk); 3158 } 3159 3160 static char * 3161 get_title(char *rootdir) 3162 { 3163 static char title[80]; /* from /etc/release */ 3164 char *cp = NULL, release[PATH_MAX]; 3165 FILE *fp; 3166 3167 /* open the /etc/release file */ 3168 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir); 3169 3170 fp = fopen(release, "r"); 3171 if (fp == NULL) 3172 return (NULL); 3173 3174 while (s_fgets(title, sizeof (title), fp) != NULL) { 3175 cp = strstr(title, "Solaris"); 3176 if (cp) 3177 break; 3178 } 3179 (void) fclose(fp); 3180 return (cp == NULL ? "Solaris" : cp); 3181 } 3182 3183 char * 3184 get_special(char *mountp) 3185 { 3186 FILE *mntfp; 3187 struct mnttab mp = {0}, mpref = {0}; 3188 3189 mntfp = fopen(MNTTAB, "r"); 3190 if (mntfp == NULL) { 3191 return (0); 3192 } 3193 3194 if (*mountp == '\0') 3195 mpref.mnt_mountp = "/"; 3196 else 3197 mpref.mnt_mountp = mountp; 3198 if (getmntany(mntfp, &mp, &mpref) != 0) { 3199 (void) fclose(mntfp); 3200 return (NULL); 3201 } 3202 (void) fclose(mntfp); 3203 3204 return (s_strdup(mp.mnt_special)); 3205 } 3206 3207 char * 3208 os_to_grubdisk(char *osdisk, int on_bootdev) 3209 { 3210 FILE *fp; 3211 char *grubdisk; 3212 3213 /* translate /dev/dsk name to grub disk name */ 3214 fp = open_diskmap(""); 3215 if (fp == NULL) { 3216 bam_error(DISKMAP_FAIL, osdisk); 3217 return (NULL); 3218 } 3219 grubdisk = get_grubdisk(osdisk, fp, on_bootdev); 3220 (void) fclose(fp); 3221 return (grubdisk); 3222 } 3223 3224 /* 3225 * Check if root is on the boot device 3226 * Return 0 (false) on error 3227 */ 3228 static int 3229 menu_on_bootdev(char *menu_root, FILE *fp) 3230 { 3231 int ret; 3232 char *grubhd, *bootp, *special; 3233 3234 special = get_special(menu_root); 3235 if (special == NULL) 3236 return (0); 3237 bootp = strstr(special, "p0:boot"); 3238 if (bootp) 3239 *bootp = '\0'; 3240 grubhd = get_grubdisk(special, fp, 1); 3241 free(special); 3242 3243 if (grubhd == NULL) 3244 return (0); 3245 ret = grubhd[3] == '0'; 3246 free(grubhd); 3247 return (ret); 3248 } 3249 3250 /* 3251 * look for matching bootadm entry with specified parameters 3252 * Here are the rules (based on existing usage): 3253 * - If title is specified, match on title only 3254 * - Else, match on kernel, grubdisk and module. Note that, if root_opt is 3255 * non-zero, the absence of root line is considered a match. 3256 */ 3257 static entry_t * 3258 find_boot_entry(menu_t *mp, char *title, char *kernel, char *root, 3259 char *module, int root_opt, int *entry_num) 3260 { 3261 int i; 3262 line_t *lp; 3263 entry_t *ent; 3264 3265 /* find matching entry */ 3266 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) { 3267 lp = ent->start; 3268 3269 /* first line of entry must be bootadm comment */ 3270 lp = ent->start; 3271 if (lp->flags != BAM_COMMENT || 3272 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) { 3273 continue; 3274 } 3275 3276 /* advance to title line */ 3277 lp = lp->next; 3278 if (title) { 3279 if (lp->flags == BAM_TITLE && lp->arg && 3280 strcmp(lp->arg, title) == 0) 3281 break; 3282 continue; /* check title only */ 3283 } 3284 3285 lp = lp->next; /* advance to root line */ 3286 if (lp == NULL) { 3287 continue; 3288 } else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3289 /* root command found, match grub disk */ 3290 if (strcmp(lp->arg, root) != 0) { 3291 continue; 3292 } 3293 lp = lp->next; /* advance to kernel line */ 3294 } else { 3295 /* no root command, see if root is optional */ 3296 if (root_opt == 0) { 3297 continue; 3298 } 3299 } 3300 3301 if (lp == NULL || lp->next == NULL) { 3302 continue; 3303 } 3304 3305 if (kernel && 3306 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) { 3307 continue; 3308 } 3309 3310 /* 3311 * Check for matching module entry (failsafe or normal). 3312 * If it fails to match, we go around the loop again. 3313 * For xpv entries, there are two module lines, so we 3314 * do the check twice. 3315 */ 3316 lp = lp->next; /* advance to module line */ 3317 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) || 3318 (((lp = lp->next) != NULL) && 3319 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) { 3320 /* match found */ 3321 break; 3322 } 3323 } 3324 3325 if (entry_num && ent) { 3326 *entry_num = i; 3327 } 3328 return (ent); 3329 } 3330 3331 static int 3332 update_boot_entry(menu_t *mp, char *title, char *root, char *kernel, 3333 char *mod_kernel, char *module, int root_opt) 3334 { 3335 int i, change_kernel = 0; 3336 entry_t *ent; 3337 line_t *lp; 3338 char linebuf[BAM_MAXLINE]; 3339 3340 /* note: don't match on title, it's updated on upgrade */ 3341 ent = find_boot_entry(mp, NULL, kernel, root, module, root_opt, &i); 3342 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) { 3343 /* 3344 * We may be upgrading a kernel from multiboot to 3345 * directboot. Look for a multiboot entry. 3346 */ 3347 ent = find_boot_entry(mp, NULL, "multiboot", root, 3348 MULTI_BOOT_ARCHIVE, root_opt, NULL); 3349 if (ent != NULL) { 3350 change_kernel = 1; 3351 } 3352 } 3353 if (ent == NULL) 3354 return (add_boot_entry(mp, title, root_opt ? NULL : root, 3355 kernel, mod_kernel, module)); 3356 3357 /* replace title of exiting entry and delete root line */ 3358 lp = ent->start; 3359 lp = lp->next; /* title line */ 3360 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3361 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 3362 free(lp->arg); 3363 free(lp->line); 3364 lp->arg = s_strdup(title); 3365 lp->line = s_strdup(linebuf); 3366 3367 lp = lp->next; /* root line */ 3368 if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3369 if (root_opt) { /* root line not needed */ 3370 line_t *tmp = lp; 3371 lp = lp->next; 3372 unlink_line(mp, tmp); 3373 line_free(tmp); 3374 } else 3375 lp = lp->next; 3376 } 3377 3378 if (change_kernel) { 3379 /* 3380 * We're upgrading from multiboot to directboot. 3381 */ 3382 if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) { 3383 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3384 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 3385 kernel); 3386 free(lp->arg); 3387 free(lp->line); 3388 lp->arg = s_strdup(kernel); 3389 lp->line = s_strdup(linebuf); 3390 lp = lp->next; 3391 } 3392 if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 3393 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3394 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 3395 module); 3396 free(lp->arg); 3397 free(lp->line); 3398 lp->arg = s_strdup(module); 3399 lp->line = s_strdup(linebuf); 3400 lp = lp->next; 3401 } 3402 } 3403 return (i); 3404 } 3405 3406 /*ARGSUSED*/ 3407 static error_t 3408 update_entry(menu_t *mp, char *menu_root, char *opt) 3409 { 3410 FILE *fp; 3411 int entry; 3412 char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL; 3413 struct stat sbuf; 3414 char failsafe[256]; 3415 3416 assert(mp); 3417 assert(opt); 3418 3419 osdev = strtok(opt, ","); 3420 osroot = strtok(NULL, ","); 3421 if (osroot == NULL) 3422 osroot = menu_root; 3423 title = get_title(osroot); 3424 3425 /* translate /dev/dsk name to grub disk name */ 3426 fp = open_diskmap(osroot); 3427 if (fp == NULL) { 3428 bam_error(DISKMAP_FAIL, osdev); 3429 return (BAM_ERROR); 3430 } 3431 grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp)); 3432 (void) fclose(fp); 3433 if (grubdisk == NULL) { 3434 bam_error(DISKMAP_FAIL, osdev); 3435 return (BAM_ERROR); 3436 } 3437 3438 /* add the entry for normal Solaris */ 3439 if (bam_direct == BAM_DIRECT_DBOOT) { 3440 entry = update_boot_entry(mp, title, grubdisk, 3441 DIRECT_BOOT_KERNEL, NULL, DIRECT_BOOT_ARCHIVE, 3442 osroot == menu_root); 3443 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) { 3444 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubdisk, 3445 XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE, 3446 osroot == menu_root); 3447 } 3448 } else { 3449 entry = update_boot_entry(mp, title, grubdisk, MULTI_BOOT, 3450 NULL, MULTI_BOOT_ARCHIVE, osroot == menu_root); 3451 } 3452 3453 /* 3454 * Add the entry for failsafe archive. On a bfu'd system, the 3455 * failsafe may be different than the installed kernel. 3456 */ 3457 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT); 3458 if (stat(failsafe, &sbuf) == 0) { 3459 3460 /* Figure out where the kernel line should point */ 3461 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, 3462 DIRECT_BOOT_FAILSAFE_KERNEL); 3463 if (stat(failsafe, &sbuf) == 0) { 3464 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE; 3465 } else { 3466 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 3467 osroot, MULTI_BOOT_FAILSAFE); 3468 if (stat(failsafe, &sbuf) == 0) { 3469 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE; 3470 } 3471 } 3472 if (failsafe_kernel != NULL) { 3473 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk, 3474 failsafe_kernel, NULL, MINIROOT, 3475 osroot == menu_root); 3476 } 3477 } 3478 free(grubdisk); 3479 3480 if (entry == BAM_ERROR) { 3481 return (BAM_ERROR); 3482 } 3483 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3484 return (BAM_WRITE); 3485 } 3486 3487 static char * 3488 read_grub_root(void) 3489 { 3490 FILE *fp; 3491 struct stat sb; 3492 char buf[BAM_MAXLINE]; 3493 char *rootstr; 3494 3495 if (stat(GRUB_slice, &sb) != 0) { 3496 bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno)); 3497 return (NULL); 3498 } 3499 3500 if (stat(GRUB_root, &sb) != 0) { 3501 bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno)); 3502 return (NULL); 3503 } 3504 3505 fp = fopen(GRUB_root, "r"); 3506 if (fp == NULL) { 3507 bam_error(OPEN_FAIL, GRUB_root, strerror(errno)); 3508 return (NULL); 3509 } 3510 3511 if (s_fgets(buf, sizeof (buf), fp) == NULL) { 3512 bam_error(EMPTY_FILE, GRUB_root, strerror(errno)); 3513 (void) fclose(fp); 3514 return (NULL); 3515 } 3516 3517 /* 3518 * Copy buf here as check below may trash the buffer 3519 */ 3520 rootstr = s_strdup(buf); 3521 3522 if (s_fgets(buf, sizeof (buf), fp) != NULL) { 3523 bam_error(BAD_ROOT_FILE, GRUB_root); 3524 free(rootstr); 3525 rootstr = NULL; 3526 } 3527 3528 (void) fclose(fp); 3529 3530 return (rootstr); 3531 } 3532 3533 static void 3534 save_default_entry(menu_t *mp, const char *which) 3535 { 3536 int lineNum, entryNum; 3537 int entry = 0; /* default is 0 */ 3538 char linebuf[BAM_MAXLINE]; 3539 line_t *lp = mp->curdefault; 3540 3541 if (mp->start) { 3542 lineNum = mp->end->lineNum; 3543 entryNum = mp->end->entryNum; 3544 } else { 3545 lineNum = LINE_INIT; 3546 entryNum = ENTRY_INIT; 3547 } 3548 3549 if (lp) 3550 entry = s_strtol(lp->arg); 3551 3552 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry); 3553 line_parser(mp, linebuf, &lineNum, &entryNum); 3554 } 3555 3556 static void 3557 restore_default_entry(menu_t *mp, const char *which, line_t *lp) 3558 { 3559 int entry; 3560 char *str; 3561 3562 if (lp == NULL) 3563 return; /* nothing to restore */ 3564 3565 str = lp->arg + strlen(which); 3566 entry = s_strtol(str); 3567 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3568 3569 /* delete saved old default line */ 3570 unlink_line(mp, lp); 3571 line_free(lp); 3572 } 3573 3574 /* 3575 * This function is for supporting reboot with args. 3576 * The opt value can be: 3577 * NULL delete temp entry, if present 3578 * entry=# switches default entry to 1 3579 * else treated as boot-args and setup a temperary menu entry 3580 * and make it the default 3581 */ 3582 #define REBOOT_TITLE "Solaris_reboot_transient" 3583 3584 /*ARGSUSED*/ 3585 static error_t 3586 update_temp(menu_t *mp, char *menupath, char *opt) 3587 { 3588 int entry; 3589 char *grubdisk, *rootdev, *path, *opt_ptr; 3590 char kernbuf[BUFSIZ]; 3591 char args_buf[BUFSIZ]; 3592 struct stat sb; 3593 3594 assert(mp); 3595 3596 /* If no option, delete exiting reboot menu entry */ 3597 if (opt == NULL) { 3598 entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL, 3599 NULL, 0, &entry); 3600 if (ent == NULL) /* not found is ok */ 3601 return (BAM_SUCCESS); 3602 (void) do_delete(mp, entry); 3603 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault); 3604 mp->olddefault = NULL; 3605 return (BAM_WRITE); 3606 } 3607 3608 /* if entry= is specified, set the default entry */ 3609 if (strncmp(opt, "entry=", strlen("entry=")) == 0 && 3610 selector(mp, opt, &entry, NULL) == BAM_SUCCESS) { 3611 /* this is entry=# option */ 3612 return (set_global(mp, menu_cmds[DEFAULT_CMD], entry)); 3613 } 3614 3615 /* 3616 * add a new menu entry base on opt and make it the default 3617 */ 3618 grubdisk = NULL; 3619 if (stat(GRUB_slice, &sb) != 0) { 3620 /* 3621 * 1. First get root disk name from mnttab 3622 * 2. Translate disk name to grub name 3623 * 3. Add the new menu entry 3624 */ 3625 rootdev = get_special("/"); 3626 if (rootdev) { 3627 grubdisk = os_to_grubdisk(rootdev, 1); 3628 free(rootdev); 3629 } 3630 } else { 3631 /* 3632 * This is an LU BE. The GRUB_root file 3633 * contains entry for GRUB's "root" cmd. 3634 */ 3635 grubdisk = read_grub_root(); 3636 } 3637 if (grubdisk == NULL) { 3638 bam_error(REBOOT_WITH_ARGS_FAILED); 3639 return (BAM_ERROR); 3640 } 3641 3642 /* add an entry for Solaris reboot */ 3643 if (bam_direct == BAM_DIRECT_DBOOT) { 3644 if (opt[0] == '-') { 3645 /* It's an option - first see if boot-file is set */ 3646 if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ) 3647 != BAM_SUCCESS) 3648 return (BAM_ERROR); 3649 if (kernbuf[0] == '\0') 3650 (void) strncpy(kernbuf, DIRECT_BOOT_KERNEL, 3651 BUFSIZ); 3652 (void) strlcat(kernbuf, " ", BUFSIZ); 3653 (void) strlcat(kernbuf, opt, BUFSIZ); 3654 } else if (opt[0] == '/') { 3655 /* It's a full path, so write it out. */ 3656 (void) strlcpy(kernbuf, opt, BUFSIZ); 3657 3658 /* 3659 * If someone runs: 3660 * 3661 * # eeprom boot-args='-kd' 3662 * # reboot /platform/i86pc/kernel/unix 3663 * 3664 * we want to use the boot-args as part of the boot 3665 * line. On the other hand, if someone runs: 3666 * 3667 * # reboot "/platform/i86pc/kernel/unix -kd" 3668 * 3669 * we don't need to mess with boot-args. If there's 3670 * no space in the options string, assume we're in the 3671 * first case. 3672 */ 3673 if (strchr(opt, ' ') == NULL) { 3674 if (set_kernel(mp, ARGS_CMD, NULL, args_buf, 3675 BUFSIZ) != BAM_SUCCESS) 3676 return (BAM_ERROR); 3677 3678 if (args_buf[0] != '\0') { 3679 (void) strlcat(kernbuf, " ", BUFSIZ); 3680 (void) strlcat(kernbuf, args_buf, 3681 BUFSIZ); 3682 } 3683 } 3684 } else { 3685 /* 3686 * It may be a partial path, or it may be a partial 3687 * path followed by options. Assume that only options 3688 * follow a space. If someone sends us a kernel path 3689 * that includes a space, they deserve to be broken. 3690 */ 3691 opt_ptr = strchr(opt, ' '); 3692 if (opt_ptr != NULL) { 3693 *opt_ptr = '\0'; 3694 } 3695 3696 path = expand_path(opt); 3697 if (path != NULL) { 3698 (void) strlcpy(kernbuf, path, BUFSIZ); 3699 free(path); 3700 3701 /* 3702 * If there were options given, use those. 3703 * Otherwise, copy over the default options. 3704 */ 3705 if (opt_ptr != NULL) { 3706 /* Restore the space in opt string */ 3707 *opt_ptr = ' '; 3708 (void) strlcat(kernbuf, opt_ptr, 3709 BUFSIZ); 3710 } else { 3711 if (set_kernel(mp, ARGS_CMD, NULL, 3712 args_buf, BUFSIZ) != BAM_SUCCESS) 3713 return (BAM_ERROR); 3714 3715 if (args_buf[0] != '\0') { 3716 (void) strlcat(kernbuf, " ", 3717 BUFSIZ); 3718 (void) strlcat(kernbuf, 3719 args_buf, BUFSIZ); 3720 } 3721 } 3722 } else { 3723 bam_error(UNKNOWN_KERNEL, opt); 3724 bam_print_stderr(UNKNOWN_KERNEL_REBOOT); 3725 return (BAM_ERROR); 3726 } 3727 } 3728 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3729 NULL, NULL); 3730 } else { 3731 (void) snprintf(kernbuf, sizeof (kernbuf), "%s %s", 3732 MULTI_BOOT, opt); 3733 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3734 NULL, MULTI_BOOT_ARCHIVE); 3735 } 3736 free(grubdisk); 3737 3738 if (entry == BAM_ERROR) { 3739 bam_error(REBOOT_WITH_ARGS_FAILED); 3740 return (BAM_ERROR); 3741 } 3742 3743 save_default_entry(mp, BAM_OLDDEF); 3744 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3745 return (BAM_WRITE); 3746 } 3747 3748 static error_t 3749 set_global(menu_t *mp, char *globalcmd, int val) 3750 { 3751 line_t *lp, *found, *last; 3752 char *cp, *str; 3753 char prefix[BAM_MAXLINE]; 3754 size_t len; 3755 3756 assert(mp); 3757 assert(globalcmd); 3758 3759 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) { 3760 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) { 3761 (void) snprintf(prefix, sizeof (prefix), "%d", val); 3762 bam_error(INVALID_ENTRY, prefix); 3763 return (BAM_ERROR); 3764 } 3765 } 3766 3767 found = last = NULL; 3768 for (lp = mp->start; lp; lp = lp->next) { 3769 if (lp->flags != BAM_GLOBAL) 3770 continue; 3771 3772 last = lp; /* track the last global found */ 3773 3774 if (lp->cmd == NULL) { 3775 bam_error(NO_CMD, lp->lineNum); 3776 continue; 3777 } 3778 if (strcmp(globalcmd, lp->cmd) != 0) 3779 continue; 3780 3781 if (found) { 3782 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 3783 } 3784 found = lp; 3785 } 3786 3787 if (found == NULL) { 3788 lp = s_calloc(1, sizeof (line_t)); 3789 if (last == NULL) { 3790 lp->next = mp->start; 3791 mp->start = lp; 3792 mp->end = (mp->end) ? mp->end : lp; 3793 } else { 3794 lp->next = last->next; 3795 last->next = lp; 3796 if (lp->next == NULL) 3797 mp->end = lp; 3798 } 3799 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */ 3800 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]); 3801 len += 10; /* val < 10 digits */ 3802 lp->line = s_calloc(1, len); 3803 (void) snprintf(lp->line, len, "%s%s%d", 3804 globalcmd, menu_cmds[SEP_CMD], val); 3805 return (BAM_WRITE); 3806 } 3807 3808 /* 3809 * We are changing an existing entry. Retain any prefix whitespace, 3810 * but overwrite everything else. This preserves tabs added for 3811 * readability. 3812 */ 3813 str = found->line; 3814 cp = prefix; 3815 while (*str == ' ' || *str == '\t') 3816 *(cp++) = *(str++); 3817 *cp = '\0'; /* Terminate prefix */ 3818 len = strlen(prefix) + strlen(globalcmd); 3819 len += strlen(menu_cmds[SEP_CMD]) + 10; 3820 3821 free(found->line); 3822 found->line = s_calloc(1, len); 3823 (void) snprintf(found->line, len, 3824 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val); 3825 3826 return (BAM_WRITE); /* need a write to menu */ 3827 } 3828 3829 /* 3830 * partial_path may be anything like "kernel/unix" or "kmdb". Try to 3831 * expand it to a full unix path. The calling function is expected to 3832 * output a message if an error occurs and NULL is returned. 3833 */ 3834 static char * 3835 expand_path(const char *partial_path) 3836 { 3837 int new_path_len; 3838 char *new_path, new_path2[PATH_MAX]; 3839 struct stat sb; 3840 3841 new_path_len = strlen(partial_path) + 64; 3842 new_path = s_calloc(1, new_path_len); 3843 3844 /* First, try the simplest case - something like "kernel/unix" */ 3845 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s", 3846 partial_path); 3847 if (stat(new_path, &sb) == 0) { 3848 return (new_path); 3849 } 3850 3851 if (strcmp(partial_path, "kmdb") == 0) { 3852 (void) snprintf(new_path, new_path_len, "%s -k", 3853 DIRECT_BOOT_KERNEL); 3854 return (new_path); 3855 } 3856 3857 /* 3858 * We've quickly reached unsupported usage. Try once more to 3859 * see if we were just given a glom name. 3860 */ 3861 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix", 3862 partial_path); 3863 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix", 3864 partial_path); 3865 if (stat(new_path, &sb) == 0) { 3866 if (stat(new_path2, &sb) == 0) { 3867 /* 3868 * We matched both, so we actually 3869 * want to write the $ISADIR version. 3870 */ 3871 (void) snprintf(new_path, new_path_len, 3872 "/platform/i86pc/kernel/%s/$ISADIR/unix", 3873 partial_path); 3874 } 3875 return (new_path); 3876 } 3877 3878 free(new_path); 3879 return (NULL); 3880 } 3881 3882 /* 3883 * The kernel cmd and arg have been changed, so 3884 * check whether the archive line needs to change. 3885 */ 3886 static void 3887 set_archive_line(entry_t *entryp, line_t *kernelp) 3888 { 3889 line_t *lp = entryp->start; 3890 char *new_archive; 3891 menu_cmd_t m_cmd; 3892 3893 for (; lp != NULL; lp = lp->next) { 3894 if (strncmp(lp->cmd, menu_cmds[MODULE_CMD], 3895 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) { 3896 break; 3897 } 3898 if (lp == entryp->end) 3899 return; 3900 } 3901 if (lp == NULL) 3902 return; 3903 3904 if (strstr(kernelp->arg, "$ISADIR") != NULL) { 3905 new_archive = DIRECT_BOOT_ARCHIVE; 3906 m_cmd = MODULE_DOLLAR_CMD; 3907 } else if (strstr(kernelp->arg, "amd64") != NULL) { 3908 new_archive = DIRECT_BOOT_ARCHIVE_64; 3909 m_cmd = MODULE_CMD; 3910 } else { 3911 new_archive = DIRECT_BOOT_ARCHIVE_32; 3912 m_cmd = MODULE_CMD; 3913 } 3914 3915 if (strcmp(lp->arg, new_archive) == 0) 3916 return; 3917 3918 if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) { 3919 free(lp->cmd); 3920 lp->cmd = s_strdup(menu_cmds[m_cmd]); 3921 } 3922 3923 free(lp->arg); 3924 lp->arg = s_strdup(new_archive); 3925 update_line(lp); 3926 } 3927 3928 /* 3929 * Title for an entry to set properties that once went in bootenv.rc. 3930 */ 3931 #define BOOTENV_RC_TITLE "Solaris bootenv rc" 3932 3933 /* 3934 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments 3935 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length 3936 * string, reset the value to the default. If path is a non-zero-length 3937 * string, set the kernel or arguments. 3938 */ 3939 static error_t 3940 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize) 3941 { 3942 int entryNum, rv = BAM_SUCCESS, free_new_path = 0; 3943 entry_t *entryp; 3944 line_t *ptr, *kernelp; 3945 char *new_arg, *old_args, *space; 3946 char *grubdisk, *rootdev, *new_path; 3947 char old_space; 3948 size_t old_kernel_len, new_str_len; 3949 struct stat sb; 3950 3951 assert(bufsize > 0); 3952 3953 ptr = kernelp = NULL; 3954 new_arg = old_args = space = NULL; 3955 grubdisk = rootdev = new_path = NULL; 3956 buf[0] = '\0'; 3957 3958 if (bam_direct != BAM_DIRECT_DBOOT) { 3959 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args"); 3960 return (BAM_ERROR); 3961 } 3962 3963 /* 3964 * If a user changed the default entry to a non-bootadm controlled 3965 * one, we don't want to mess with it. Just print an error and 3966 * return. 3967 */ 3968 if (mp->curdefault) { 3969 entryNum = s_strtol(mp->curdefault->arg); 3970 for (entryp = mp->entries; entryp; entryp = entryp->next) { 3971 if (entryp->entryNum == entryNum) 3972 break; 3973 } 3974 if ((entryp != NULL) && 3975 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) { 3976 bam_error(DEFAULT_NOT_BAM); 3977 return (BAM_ERROR); 3978 } 3979 } 3980 3981 entryNum = -1; 3982 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, 0, 3983 &entryNum); 3984 3985 if (entryp != NULL) { 3986 for (ptr = entryp->start; ptr && ptr != entryp->end; 3987 ptr = ptr->next) { 3988 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD], 3989 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) { 3990 kernelp = ptr; 3991 break; 3992 } 3993 } 3994 if (kernelp == NULL) { 3995 bam_error(NO_KERNEL, entryNum); 3996 return (BAM_ERROR); 3997 } 3998 3999 old_kernel_len = strcspn(kernelp->arg, " \t"); 4000 space = old_args = kernelp->arg + old_kernel_len; 4001 while ((*old_args == ' ') || (*old_args == '\t')) 4002 old_args++; 4003 } 4004 4005 if (path == NULL) { 4006 /* Simply report what was found */ 4007 if (kernelp == NULL) 4008 return (BAM_SUCCESS); 4009 4010 if (optnum == ARGS_CMD) { 4011 if (old_args[0] != '\0') 4012 (void) strlcpy(buf, old_args, bufsize); 4013 } else { 4014 /* 4015 * We need to print the kernel, so we just turn the 4016 * first space into a '\0' and print the beginning. 4017 * We don't print anything if it's the default kernel. 4018 */ 4019 old_space = *space; 4020 *space = '\0'; 4021 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) 4022 (void) strlcpy(buf, kernelp->arg, bufsize); 4023 *space = old_space; 4024 } 4025 return (BAM_SUCCESS); 4026 } 4027 4028 /* 4029 * First, check if we're resetting an entry to the default. 4030 */ 4031 if ((path[0] == '\0') || 4032 ((optnum == KERNEL_CMD) && 4033 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) { 4034 if ((entryp == NULL) || (kernelp == NULL)) { 4035 /* No previous entry, it's already the default */ 4036 return (BAM_SUCCESS); 4037 } 4038 4039 /* 4040 * Check if we can delete the entry. If we're resetting the 4041 * kernel command, and the args is already empty, or if we're 4042 * resetting the args command, and the kernel is already the 4043 * default, we can restore the old default and delete the entry. 4044 */ 4045 if (((optnum == KERNEL_CMD) && 4046 ((old_args == NULL) || (old_args[0] == '\0'))) || 4047 ((optnum == ARGS_CMD) && 4048 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL, 4049 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) { 4050 kernelp = NULL; 4051 (void) do_delete(mp, entryNum); 4052 restore_default_entry(mp, BAM_OLD_RC_DEF, 4053 mp->old_rc_default); 4054 mp->old_rc_default = NULL; 4055 rv = BAM_WRITE; 4056 goto done; 4057 } 4058 4059 if (optnum == KERNEL_CMD) { 4060 /* 4061 * At this point, we've already checked that old_args 4062 * and entryp are valid pointers. The "+ 2" is for 4063 * a space a the string termination character. 4064 */ 4065 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) + 4066 strlen(old_args) + 2; 4067 new_arg = s_calloc(1, new_str_len); 4068 (void) snprintf(new_arg, new_str_len, "%s %s", 4069 DIRECT_BOOT_KERNEL, old_args); 4070 free(kernelp->arg); 4071 kernelp->arg = new_arg; 4072 4073 /* 4074 * We have changed the kernel line, so we may need 4075 * to update the archive line as well. 4076 */ 4077 set_archive_line(entryp, kernelp); 4078 } else { 4079 /* 4080 * We're resetting the boot args to nothing, so 4081 * we only need to copy the kernel. We've already 4082 * checked that the kernel is not the default. 4083 */ 4084 new_arg = s_calloc(1, old_kernel_len + 1); 4085 (void) snprintf(new_arg, old_kernel_len + 1, "%s", 4086 kernelp->arg); 4087 free(kernelp->arg); 4088 kernelp->arg = new_arg; 4089 } 4090 rv = BAM_WRITE; 4091 goto done; 4092 } 4093 4094 /* 4095 * Expand the kernel file to a full path, if necessary 4096 */ 4097 if ((optnum == KERNEL_CMD) && (path[0] != '/')) { 4098 new_path = expand_path(path); 4099 if (new_path == NULL) { 4100 bam_error(UNKNOWN_KERNEL, path); 4101 return (BAM_ERROR); 4102 } 4103 free_new_path = 1; 4104 } else { 4105 new_path = path; 4106 free_new_path = 0; 4107 } 4108 4109 /* 4110 * At this point, we know we're setting a new value. First, take care 4111 * of the case where there was no previous entry. 4112 */ 4113 if (entryp == NULL) { 4114 /* Similar to code in update_temp */ 4115 if (stat(GRUB_slice, &sb) != 0) { 4116 /* 4117 * 1. First get root disk name from mnttab 4118 * 2. Translate disk name to grub name 4119 * 3. Add the new menu entry 4120 */ 4121 rootdev = get_special("/"); 4122 if (rootdev) { 4123 grubdisk = os_to_grubdisk(rootdev, 1); 4124 free(rootdev); 4125 } 4126 } else { 4127 /* 4128 * This is an LU BE. The GRUB_root file 4129 * contains entry for GRUB's "root" cmd. 4130 */ 4131 grubdisk = read_grub_root(); 4132 } 4133 if (grubdisk == NULL) { 4134 bam_error(REBOOT_WITH_ARGS_FAILED); 4135 rv = BAM_ERROR; 4136 goto done; 4137 } 4138 if (optnum == KERNEL_CMD) { 4139 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 4140 grubdisk, new_path, NULL, NULL); 4141 } else { 4142 new_str_len = strlen(DIRECT_BOOT_KERNEL) + 4143 strlen(path) + 8; 4144 new_arg = s_calloc(1, new_str_len); 4145 4146 (void) snprintf(new_arg, new_str_len, "%s %s", 4147 DIRECT_BOOT_KERNEL, path); 4148 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 4149 grubdisk, new_arg, NULL, DIRECT_BOOT_ARCHIVE); 4150 } 4151 save_default_entry(mp, BAM_OLD_RC_DEF); 4152 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum); 4153 rv = BAM_WRITE; 4154 goto done; 4155 } 4156 4157 /* 4158 * There was already an bootenv entry which we need to edit. 4159 */ 4160 if (optnum == KERNEL_CMD) { 4161 new_str_len = strlen(new_path) + strlen(old_args) + 2; 4162 new_arg = s_calloc(1, new_str_len); 4163 (void) snprintf(new_arg, new_str_len, "%s %s", new_path, 4164 old_args); 4165 free(kernelp->arg); 4166 kernelp->arg = new_arg; 4167 4168 /* 4169 * If we have changed the kernel line, we may need to update 4170 * the archive line as well. 4171 */ 4172 set_archive_line(entryp, kernelp); 4173 } else { 4174 new_str_len = old_kernel_len + strlen(path) + 8; 4175 new_arg = s_calloc(1, new_str_len); 4176 (void) strncpy(new_arg, kernelp->arg, old_kernel_len); 4177 (void) strlcat(new_arg, " ", new_str_len); 4178 (void) strlcat(new_arg, path, new_str_len); 4179 free(kernelp->arg); 4180 kernelp->arg = new_arg; 4181 } 4182 rv = BAM_WRITE; 4183 4184 done: 4185 if ((rv == BAM_WRITE) && kernelp) 4186 update_line(kernelp); 4187 if (free_new_path) 4188 free(new_path); 4189 return (rv); 4190 } 4191 4192 /*ARGSUSED*/ 4193 static error_t 4194 set_option(menu_t *mp, char *menu_path, char *opt) 4195 { 4196 int optnum, optval; 4197 char *val; 4198 char buf[BUFSIZ] = ""; 4199 error_t rv; 4200 4201 assert(mp); 4202 assert(opt); 4203 4204 val = strchr(opt, '='); 4205 if (val != NULL) { 4206 *val = '\0'; 4207 } 4208 4209 if (strcmp(opt, "default") == 0) { 4210 optnum = DEFAULT_CMD; 4211 } else if (strcmp(opt, "timeout") == 0) { 4212 optnum = TIMEOUT_CMD; 4213 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) { 4214 optnum = KERNEL_CMD; 4215 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) { 4216 optnum = ARGS_CMD; 4217 } else { 4218 bam_error(INVALID_ENTRY, opt); 4219 return (BAM_ERROR); 4220 } 4221 4222 /* 4223 * kernel and args are allowed without "=new_value" strings. All 4224 * others cause errors 4225 */ 4226 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) { 4227 bam_error(INVALID_ENTRY, opt); 4228 return (BAM_ERROR); 4229 } else if (val != NULL) { 4230 *val = '='; 4231 } 4232 4233 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) { 4234 rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ); 4235 if ((rv == BAM_SUCCESS) && (buf[0] != '\0')) 4236 (void) printf("%s\n", buf); 4237 return (rv); 4238 } else { 4239 optval = s_strtol(val + 1); 4240 return (set_global(mp, menu_cmds[optnum], optval)); 4241 } 4242 } 4243 4244 /* 4245 * The quiet argument suppresses messages. This is used 4246 * when invoked in the context of other commands (e.g. list_entry) 4247 */ 4248 static error_t 4249 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet) 4250 { 4251 line_t *lp; 4252 char *arg; 4253 int done, ret = BAM_SUCCESS; 4254 4255 assert(mp); 4256 assert(menu_path); 4257 assert(globalcmd); 4258 4259 if (mp->start == NULL) { 4260 if (!quiet) 4261 bam_error(NO_MENU, menu_path); 4262 return (BAM_ERROR); 4263 } 4264 4265 done = 0; 4266 for (lp = mp->start; lp; lp = lp->next) { 4267 if (lp->flags != BAM_GLOBAL) 4268 continue; 4269 4270 if (lp->cmd == NULL) { 4271 if (!quiet) 4272 bam_error(NO_CMD, lp->lineNum); 4273 continue; 4274 } 4275 4276 if (strcmp(globalcmd, lp->cmd) != 0) 4277 continue; 4278 4279 /* Found global. Check for duplicates */ 4280 if (done && !quiet) { 4281 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 4282 ret = BAM_ERROR; 4283 } 4284 4285 arg = lp->arg ? lp->arg : ""; 4286 bam_print(GLOBAL_CMD, globalcmd, arg); 4287 done = 1; 4288 } 4289 4290 if (!done && bam_verbose) 4291 bam_print(NO_ENTRY, globalcmd); 4292 4293 return (ret); 4294 } 4295 4296 static error_t 4297 menu_write(char *root, menu_t *mp) 4298 { 4299 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start)); 4300 } 4301 4302 static void 4303 line_free(line_t *lp) 4304 { 4305 if (lp == NULL) 4306 return; 4307 4308 if (lp->cmd) 4309 free(lp->cmd); 4310 if (lp->sep) 4311 free(lp->sep); 4312 if (lp->arg) 4313 free(lp->arg); 4314 if (lp->line) 4315 free(lp->line); 4316 free(lp); 4317 } 4318 4319 static void 4320 linelist_free(line_t *start) 4321 { 4322 line_t *lp; 4323 4324 while (start) { 4325 lp = start; 4326 start = start->next; 4327 line_free(lp); 4328 } 4329 } 4330 4331 static void 4332 filelist_free(filelist_t *flistp) 4333 { 4334 linelist_free(flistp->head); 4335 flistp->head = NULL; 4336 flistp->tail = NULL; 4337 } 4338 4339 static void 4340 menu_free(menu_t *mp) 4341 { 4342 entry_t *ent, *tmp; 4343 assert(mp); 4344 4345 if (mp->start) 4346 linelist_free(mp->start); 4347 ent = mp->entries; 4348 while (ent) { 4349 tmp = ent; 4350 ent = tmp->next; 4351 free(tmp); 4352 } 4353 4354 free(mp); 4355 } 4356 4357 /* 4358 * Utility routines 4359 */ 4360 4361 4362 /* 4363 * Returns 0 on success 4364 * Any other value indicates an error 4365 */ 4366 static int 4367 exec_cmd(char *cmdline, filelist_t *flistp) 4368 { 4369 char buf[BUFSIZ]; 4370 int ret; 4371 FILE *ptr; 4372 sigset_t set; 4373 void (*disp)(int); 4374 4375 /* 4376 * For security 4377 * - only absolute paths are allowed 4378 * - set IFS to space and tab 4379 */ 4380 if (*cmdline != '/') { 4381 bam_error(ABS_PATH_REQ, cmdline); 4382 return (-1); 4383 } 4384 (void) putenv("IFS= \t"); 4385 4386 /* 4387 * We may have been exec'ed with SIGCHLD blocked 4388 * unblock it here 4389 */ 4390 (void) sigemptyset(&set); 4391 (void) sigaddset(&set, SIGCHLD); 4392 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 4393 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno)); 4394 return (-1); 4395 } 4396 4397 /* 4398 * Set SIGCHLD disposition to SIG_DFL for popen/pclose 4399 */ 4400 disp = sigset(SIGCHLD, SIG_DFL); 4401 if (disp == SIG_ERR) { 4402 bam_error(FAILED_SIG, strerror(errno)); 4403 return (-1); 4404 } 4405 if (disp == SIG_HOLD) { 4406 bam_error(BLOCKED_SIG, cmdline); 4407 return (-1); 4408 } 4409 4410 ptr = popen(cmdline, "r"); 4411 if (ptr == NULL) { 4412 bam_error(POPEN_FAIL, cmdline, strerror(errno)); 4413 return (-1); 4414 } 4415 4416 /* 4417 * If we simply do a pclose() following a popen(), pclose() 4418 * will close the reader end of the pipe immediately even 4419 * if the child process has not started/exited. pclose() 4420 * does wait for cmd to terminate before returning though. 4421 * When the executed command writes its output to the pipe 4422 * there is no reader process and the command dies with 4423 * SIGPIPE. To avoid this we read repeatedly until read 4424 * terminates with EOF. This indicates that the command 4425 * (writer) has closed the pipe and we can safely do a 4426 * pclose(). 4427 * 4428 * Since pclose() does wait for the command to exit, 4429 * we can safely reap the exit status of the command 4430 * from the value returned by pclose() 4431 */ 4432 while (s_fgets(buf, sizeof (buf), ptr) != NULL) { 4433 if (flistp == NULL) { 4434 /* s_fgets strips newlines, so insert them at the end */ 4435 bam_print(PRINT, buf); 4436 } else { 4437 append_to_flist(flistp, buf); 4438 } 4439 } 4440 4441 ret = pclose(ptr); 4442 if (ret == -1) { 4443 bam_error(PCLOSE_FAIL, cmdline, strerror(errno)); 4444 return (-1); 4445 } 4446 4447 if (WIFEXITED(ret)) { 4448 return (WEXITSTATUS(ret)); 4449 } else { 4450 bam_error(EXEC_FAIL, cmdline, ret); 4451 return (-1); 4452 } 4453 } 4454 4455 /* 4456 * Since this function returns -1 on error 4457 * it cannot be used to convert -1. However, 4458 * that is sufficient for what we need. 4459 */ 4460 static long 4461 s_strtol(char *str) 4462 { 4463 long l; 4464 char *res = NULL; 4465 4466 if (str == NULL) { 4467 return (-1); 4468 } 4469 4470 errno = 0; 4471 l = strtol(str, &res, 10); 4472 if (errno || *res != '\0') { 4473 return (-1); 4474 } 4475 4476 return (l); 4477 } 4478 4479 /* 4480 * Wrapper around fputs, that adds a newline (since fputs doesn't) 4481 */ 4482 static int 4483 s_fputs(char *str, FILE *fp) 4484 { 4485 char linebuf[BAM_MAXLINE]; 4486 4487 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str); 4488 return (fputs(linebuf, fp)); 4489 } 4490 4491 /* 4492 * Wrapper around fgets, that strips newlines returned by fgets 4493 */ 4494 char * 4495 s_fgets(char *buf, int buflen, FILE *fp) 4496 { 4497 int n; 4498 4499 buf = fgets(buf, buflen, fp); 4500 if (buf) { 4501 n = strlen(buf); 4502 if (n == buflen - 1 && buf[n-1] != '\n') 4503 bam_error(TOO_LONG, buflen - 1, buf); 4504 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1]; 4505 } 4506 4507 return (buf); 4508 } 4509 4510 void * 4511 s_calloc(size_t nelem, size_t sz) 4512 { 4513 void *ptr; 4514 4515 ptr = calloc(nelem, sz); 4516 if (ptr == NULL) { 4517 bam_error(NO_MEM, nelem*sz); 4518 bam_exit(1); 4519 } 4520 return (ptr); 4521 } 4522 4523 void * 4524 s_realloc(void *ptr, size_t sz) 4525 { 4526 ptr = realloc(ptr, sz); 4527 if (ptr == NULL) { 4528 bam_error(NO_MEM, sz); 4529 bam_exit(1); 4530 } 4531 return (ptr); 4532 } 4533 4534 static char * 4535 s_strdup(char *str) 4536 { 4537 char *ptr; 4538 4539 if (str == NULL) 4540 return (NULL); 4541 4542 ptr = strdup(str); 4543 if (ptr == NULL) { 4544 bam_error(NO_MEM, strlen(str) + 1); 4545 bam_exit(1); 4546 } 4547 return (ptr); 4548 } 4549 4550 /* 4551 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients) 4552 * Returns 0 otherwise 4553 */ 4554 static int 4555 is_amd64(void) 4556 { 4557 static int amd64 = -1; 4558 char isabuf[257]; /* from sysinfo(2) manpage */ 4559 4560 if (amd64 != -1) 4561 return (amd64); 4562 4563 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 && 4564 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) 4565 amd64 = 1; 4566 else if (strstr(isabuf, "i386") == NULL) 4567 amd64 = 1; /* diskless server */ 4568 else 4569 amd64 = 0; 4570 4571 return (amd64); 4572 } 4573 4574 static int 4575 is_sun4u(void) 4576 { 4577 static int sun4u = 0; 4578 char mbuf[257]; /* from sysinfo(2) manpage */ 4579 4580 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 && 4581 strncmp(mbuf, "sun4u", strlen("sun4u")) == 0) 4582 sun4u = 1; 4583 4584 return (sun4u); 4585 } 4586 4587 static int 4588 is_sun4v(void) 4589 { 4590 static int sun4v = 0; 4591 char mbuf[257]; /* from sysinfo(2) manpage */ 4592 4593 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0 && 4594 strncmp(mbuf, "sun4v", strlen("sun4v")) == 0) 4595 sun4v = 1; 4596 4597 return (sun4v); 4598 } 4599 4600 4601 static void 4602 append_to_flist(filelist_t *flistp, char *s) 4603 { 4604 line_t *lp; 4605 4606 lp = s_calloc(1, sizeof (line_t)); 4607 lp->line = s_strdup(s); 4608 if (flistp->head == NULL) 4609 flistp->head = lp; 4610 else 4611 flistp->tail->next = lp; 4612 flistp->tail = lp; 4613 } 4614 4615 #if !defined(_OPB) 4616 4617 UCODE_VENDORS; 4618 4619 /*ARGSUSED*/ 4620 static void 4621 ucode_install(char *root) 4622 { 4623 int i; 4624 4625 for (i = 0; ucode_vendors[i].filestr != NULL; i++) { 4626 int cmd_len = PATH_MAX + 256; 4627 char cmd[PATH_MAX + 256]; 4628 char file[PATH_MAX]; 4629 char timestamp[PATH_MAX]; 4630 struct stat fstatus, tstatus; 4631 struct utimbuf u_times; 4632 4633 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt", 4634 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr); 4635 4636 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode))) 4637 continue; 4638 4639 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file); 4640 4641 if (stat(timestamp, &tstatus) == 0 && 4642 fstatus.st_mtime <= tstatus.st_mtime) 4643 continue; 4644 4645 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R " 4646 "%s/%s/%s %s > /dev/null 2>&1", bam_root, 4647 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file); 4648 if (system(cmd) != 0) 4649 return; 4650 4651 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1) 4652 return; 4653 4654 u_times.actime = fstatus.st_atime; 4655 u_times.modtime = fstatus.st_mtime; 4656 (void) utime(timestamp, &u_times); 4657 } 4658 } 4659 #endif 4660