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