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