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