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 * A line in menu.lst looks like 2406 * [ ]*<cmd>[ \t=]*<arg>* 2407 */ 2408 static void 2409 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum) 2410 { 2411 /* 2412 * save state across calls. This is so that 2413 * header gets the right entry# after title has 2414 * been processed 2415 */ 2416 static line_t *prev = NULL; 2417 static entry_t *curr_ent = NULL; 2418 static int in_liveupgrade = 0; 2419 2420 line_t *lp; 2421 char *cmd, *sep, *arg; 2422 char save, *cp, *line; 2423 menu_flag_t flag = BAM_INVALID; 2424 2425 if (str == NULL) { 2426 return; 2427 } 2428 2429 /* 2430 * First save a copy of the entire line. 2431 * We use this later to set the line field. 2432 */ 2433 line = s_strdup(str); 2434 2435 /* Eat up leading whitespace */ 2436 while (*str == ' ' || *str == '\t') 2437 str++; 2438 2439 if (*str == '#') { /* comment */ 2440 cmd = s_strdup("#"); 2441 sep = NULL; 2442 arg = s_strdup(str + 1); 2443 flag = BAM_COMMENT; 2444 if (strstr(arg, BAM_LU_HDR) != NULL) { 2445 in_liveupgrade = 1; 2446 } else if (strstr(arg, BAM_LU_FTR) != NULL) { 2447 in_liveupgrade = 0; 2448 } 2449 } else if (*str == '\0') { /* blank line */ 2450 cmd = sep = arg = NULL; 2451 flag = BAM_EMPTY; 2452 } else { 2453 /* 2454 * '=' is not a documented separator in grub syntax. 2455 * However various development bits use '=' as a 2456 * separator. In addition, external users also 2457 * use = as a separator. So we will allow that usage. 2458 */ 2459 cp = str; 2460 while (*str != ' ' && *str != '\t' && *str != '=') { 2461 if (*str == '\0') { 2462 cmd = s_strdup(cp); 2463 sep = arg = NULL; 2464 break; 2465 } 2466 str++; 2467 } 2468 2469 if (*str != '\0') { 2470 save = *str; 2471 *str = '\0'; 2472 cmd = s_strdup(cp); 2473 *str = save; 2474 2475 str++; 2476 save = *str; 2477 *str = '\0'; 2478 sep = s_strdup(str - 1); 2479 *str = save; 2480 2481 while (*str == ' ' || *str == '\t') 2482 str++; 2483 if (*str == '\0') 2484 arg = NULL; 2485 else 2486 arg = s_strdup(str); 2487 } 2488 } 2489 2490 lp = s_calloc(1, sizeof (line_t)); 2491 2492 lp->cmd = cmd; 2493 lp->sep = sep; 2494 lp->arg = arg; 2495 lp->line = line; 2496 lp->lineNum = ++(*lineNum); 2497 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) { 2498 lp->entryNum = ++(*entryNum); 2499 lp->flags = BAM_TITLE; 2500 if (prev && prev->flags == BAM_COMMENT && 2501 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2502 prev->entryNum = lp->entryNum; 2503 curr_ent = boot_entry_new(mp, prev, lp); 2504 curr_ent->flags = BAM_ENTRY_BOOTADM; 2505 } else { 2506 curr_ent = boot_entry_new(mp, lp, lp); 2507 if (in_liveupgrade) { 2508 curr_ent->flags = BAM_ENTRY_LU; 2509 } 2510 } 2511 curr_ent->entryNum = *entryNum; 2512 } else if (flag != BAM_INVALID) { 2513 /* 2514 * For header comments, the entry# is "fixed up" 2515 * by the subsequent title 2516 */ 2517 lp->entryNum = *entryNum; 2518 lp->flags = flag; 2519 } else { 2520 lp->entryNum = *entryNum; 2521 2522 if (*entryNum == ENTRY_INIT) { 2523 lp->flags = BAM_GLOBAL; 2524 } else { 2525 lp->flags = BAM_ENTRY; 2526 2527 if (cmd && arg) { 2528 /* 2529 * We only compare for the length of "module" 2530 * so that "module$" will also match. 2531 */ 2532 if ((strncmp(cmd, menu_cmds[MODULE_CMD], 2533 strlen(menu_cmds[MODULE_CMD])) == 0) && 2534 (strcmp(arg, MINIROOT) == 0)) 2535 curr_ent->flags |= BAM_ENTRY_MINIROOT; 2536 else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) 2537 curr_ent->flags |= BAM_ENTRY_ROOT; 2538 else if (strcmp(cmd, 2539 menu_cmds[CHAINLOADER_CMD]) == 0) 2540 curr_ent->flags |= 2541 BAM_ENTRY_CHAINLOADER; 2542 } 2543 } 2544 } 2545 2546 /* record default, old default, and entry line ranges */ 2547 if (lp->flags == BAM_GLOBAL && 2548 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) { 2549 mp->curdefault = lp; 2550 } else if (lp->flags == BAM_COMMENT && 2551 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) { 2552 mp->olddefault = lp; 2553 } else if (lp->flags == BAM_COMMENT && 2554 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) { 2555 mp->old_rc_default = lp; 2556 } else if (lp->flags == BAM_ENTRY || 2557 (lp->flags == BAM_COMMENT && 2558 strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) { 2559 boot_entry_addline(curr_ent, lp); 2560 } 2561 append_line(mp, lp); 2562 2563 prev = lp; 2564 } 2565 2566 static void 2567 update_numbering(menu_t *mp) 2568 { 2569 int lineNum; 2570 int entryNum; 2571 int old_default_value; 2572 line_t *lp, *prev, *default_lp, *default_entry; 2573 char buf[PATH_MAX]; 2574 2575 if (mp->start == NULL) { 2576 return; 2577 } 2578 2579 lineNum = LINE_INIT; 2580 entryNum = ENTRY_INIT; 2581 old_default_value = ENTRY_INIT; 2582 lp = default_lp = default_entry = NULL; 2583 2584 prev = NULL; 2585 for (lp = mp->start; lp; prev = lp, lp = lp->next) { 2586 lp->lineNum = ++lineNum; 2587 2588 /* 2589 * Get the value of the default command 2590 */ 2591 if (lp->entryNum == ENTRY_INIT && lp->cmd && 2592 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 && 2593 lp->arg) { 2594 old_default_value = atoi(lp->arg); 2595 default_lp = lp; 2596 } 2597 2598 /* 2599 * If not boot entry, nothing else to fix for this 2600 * entry 2601 */ 2602 if (lp->entryNum == ENTRY_INIT) 2603 continue; 2604 2605 /* 2606 * Record the position of the default entry. 2607 * The following works because global 2608 * commands like default and timeout should precede 2609 * actual boot entries, so old_default_value 2610 * is already known (or default cmd is missing). 2611 */ 2612 if (default_entry == NULL && 2613 old_default_value != ENTRY_INIT && 2614 lp->entryNum == old_default_value) { 2615 default_entry = lp; 2616 } 2617 2618 /* 2619 * Now fixup the entry number 2620 */ 2621 if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) { 2622 lp->entryNum = ++entryNum; 2623 /* fixup the bootadm header */ 2624 if (prev && prev->flags == BAM_COMMENT && 2625 prev->arg && 2626 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2627 prev->entryNum = lp->entryNum; 2628 } 2629 } else { 2630 lp->entryNum = entryNum; 2631 } 2632 } 2633 2634 /* 2635 * No default command in menu, simply return 2636 */ 2637 if (default_lp == NULL) { 2638 return; 2639 } 2640 2641 free(default_lp->arg); 2642 free(default_lp->line); 2643 2644 if (default_entry == NULL) { 2645 default_lp->arg = s_strdup("0"); 2646 } else { 2647 (void) snprintf(buf, sizeof (buf), "%d", 2648 default_entry->entryNum); 2649 default_lp->arg = s_strdup(buf); 2650 } 2651 2652 /* 2653 * The following is required since only the line field gets 2654 * written back to menu.lst 2655 */ 2656 (void) snprintf(buf, sizeof (buf), "%s%s%s", 2657 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg); 2658 default_lp->line = s_strdup(buf); 2659 } 2660 2661 2662 static menu_t * 2663 menu_read(char *menu_path) 2664 { 2665 FILE *fp; 2666 char buf[BAM_MAXLINE], *cp; 2667 menu_t *mp; 2668 int line, entry, len, n; 2669 2670 mp = s_calloc(1, sizeof (menu_t)); 2671 2672 fp = fopen(menu_path, "r"); 2673 if (fp == NULL) { /* Let the caller handle this error */ 2674 return (mp); 2675 } 2676 2677 2678 /* Note: GRUB boot entry number starts with 0 */ 2679 line = LINE_INIT; 2680 entry = ENTRY_INIT; 2681 cp = buf; 2682 len = sizeof (buf); 2683 while (s_fgets(cp, len, fp) != NULL) { 2684 n = strlen(cp); 2685 if (cp[n - 1] == '\\') { 2686 len -= n - 1; 2687 assert(len >= 2); 2688 cp += n - 1; 2689 continue; 2690 } 2691 line_parser(mp, buf, &line, &entry); 2692 cp = buf; 2693 len = sizeof (buf); 2694 } 2695 2696 if (fclose(fp) == EOF) { 2697 bam_error(CLOSE_FAIL, menu_path, strerror(errno)); 2698 } 2699 2700 return (mp); 2701 } 2702 2703 static error_t 2704 selector(menu_t *mp, char *opt, int *entry, char **title) 2705 { 2706 char *eq; 2707 char *opt_dup; 2708 int entryNum; 2709 2710 assert(mp); 2711 assert(mp->start); 2712 assert(opt); 2713 2714 opt_dup = s_strdup(opt); 2715 2716 if (entry) 2717 *entry = ENTRY_INIT; 2718 if (title) 2719 *title = NULL; 2720 2721 eq = strchr(opt_dup, '='); 2722 if (eq == NULL) { 2723 bam_error(INVALID_OPT, opt); 2724 free(opt_dup); 2725 return (BAM_ERROR); 2726 } 2727 2728 *eq = '\0'; 2729 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) { 2730 assert(mp->end); 2731 entryNum = s_strtol(eq + 1); 2732 if (entryNum < 0 || entryNum > mp->end->entryNum) { 2733 bam_error(INVALID_ENTRY, eq + 1); 2734 free(opt_dup); 2735 return (BAM_ERROR); 2736 } 2737 *entry = entryNum; 2738 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) { 2739 *title = opt + (eq - opt_dup) + 1; 2740 } else { 2741 bam_error(INVALID_OPT, opt); 2742 free(opt_dup); 2743 return (BAM_ERROR); 2744 } 2745 2746 free(opt_dup); 2747 return (BAM_SUCCESS); 2748 } 2749 2750 /* 2751 * If invoked with no titles/entries (opt == NULL) 2752 * only title lines in file are printed. 2753 * 2754 * If invoked with a title or entry #, all 2755 * lines in *every* matching entry are listed 2756 */ 2757 static error_t 2758 list_entry(menu_t *mp, char *menu_path, char *opt) 2759 { 2760 line_t *lp; 2761 int entry = ENTRY_INIT; 2762 int found; 2763 char *title = NULL; 2764 2765 assert(mp); 2766 assert(menu_path); 2767 2768 if (mp->start == NULL) { 2769 bam_error(NO_MENU, menu_path); 2770 return (BAM_ERROR); 2771 } 2772 2773 if (opt != NULL) { 2774 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) { 2775 return (BAM_ERROR); 2776 } 2777 assert((entry != ENTRY_INIT) ^ (title != NULL)); 2778 } else { 2779 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0); 2780 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0); 2781 } 2782 2783 found = 0; 2784 for (lp = mp->start; lp; lp = lp->next) { 2785 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY) 2786 continue; 2787 if (opt == NULL && lp->flags == BAM_TITLE) { 2788 bam_print(PRINT_TITLE, lp->entryNum, 2789 lp->arg); 2790 found = 1; 2791 continue; 2792 } 2793 if (entry != ENTRY_INIT && lp->entryNum == entry) { 2794 bam_print(PRINT, lp->line); 2795 found = 1; 2796 continue; 2797 } 2798 2799 /* 2800 * We set the entry value here so that all lines 2801 * in entry get printed. If we subsequently match 2802 * title in other entries, all lines in those 2803 * entries get printed as well. 2804 */ 2805 if (title && lp->flags == BAM_TITLE && lp->arg && 2806 strncmp(title, lp->arg, strlen(title)) == 0) { 2807 bam_print(PRINT, lp->line); 2808 entry = lp->entryNum; 2809 found = 1; 2810 continue; 2811 } 2812 } 2813 2814 if (!found) { 2815 bam_error(NO_MATCH_ENTRY); 2816 return (BAM_ERROR); 2817 } 2818 2819 return (BAM_SUCCESS); 2820 } 2821 2822 static int 2823 add_boot_entry(menu_t *mp, 2824 char *title, 2825 char *root, 2826 char *kernel, 2827 char *module) 2828 { 2829 int lineNum, entryNum; 2830 char linebuf[BAM_MAXLINE]; 2831 menu_cmd_t k_cmd, m_cmd; 2832 2833 assert(mp); 2834 2835 if (title == NULL) { 2836 title = "Solaris"; /* default to Solaris */ 2837 } 2838 if (kernel == NULL) { 2839 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]); 2840 return (BAM_ERROR); 2841 } 2842 if (module == NULL) { 2843 if (bam_direct != BAM_DIRECT_DBOOT) { 2844 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]); 2845 return (BAM_ERROR); 2846 } 2847 2848 /* Figure the commands out from the kernel line */ 2849 if (strstr(kernel, "$ISADIR") != NULL) { 2850 module = DIRECT_BOOT_ARCHIVE; 2851 k_cmd = KERNEL_DOLLAR_CMD; 2852 m_cmd = MODULE_DOLLAR_CMD; 2853 } else if (strstr(kernel, "amd64") != NULL) { 2854 module = DIRECT_BOOT_ARCHIVE_64; 2855 k_cmd = KERNEL_CMD; 2856 m_cmd = MODULE_CMD; 2857 } else { 2858 module = DIRECT_BOOT_ARCHIVE_32; 2859 k_cmd = KERNEL_CMD; 2860 m_cmd = MODULE_CMD; 2861 } 2862 } else if ((bam_direct == BAM_DIRECT_DBOOT) && 2863 (strstr(kernel, "$ISADIR") != NULL)) { 2864 /* 2865 * If it's a non-failsafe dboot kernel, use the "kernel$" 2866 * command. Otherwise, use "kernel". 2867 */ 2868 k_cmd = KERNEL_DOLLAR_CMD; 2869 m_cmd = MODULE_DOLLAR_CMD; 2870 } else { 2871 k_cmd = KERNEL_CMD; 2872 m_cmd = MODULE_CMD; 2873 } 2874 2875 if (mp->start) { 2876 lineNum = mp->end->lineNum; 2877 entryNum = mp->end->entryNum; 2878 } else { 2879 lineNum = LINE_INIT; 2880 entryNum = ENTRY_INIT; 2881 } 2882 2883 /* 2884 * No separator for comment (HDR/FTR) commands 2885 * The syntax for comments is #<comment> 2886 */ 2887 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2888 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR); 2889 line_parser(mp, linebuf, &lineNum, &entryNum); 2890 2891 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2892 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 2893 line_parser(mp, linebuf, &lineNum, &entryNum); 2894 2895 if (root) { 2896 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2897 menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root); 2898 line_parser(mp, linebuf, &lineNum, &entryNum); 2899 } 2900 2901 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2902 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel); 2903 line_parser(mp, linebuf, &lineNum, &entryNum); 2904 2905 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2906 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module); 2907 line_parser(mp, linebuf, &lineNum, &entryNum); 2908 2909 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2910 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR); 2911 line_parser(mp, linebuf, &lineNum, &entryNum); 2912 2913 return (entryNum); 2914 } 2915 2916 static error_t 2917 do_delete(menu_t *mp, int entryNum) 2918 { 2919 line_t *lp, *freed; 2920 entry_t *ent, *tmp; 2921 int deleted; 2922 2923 assert(entryNum != ENTRY_INIT); 2924 2925 ent = mp->entries; 2926 while (ent) { 2927 lp = ent->start; 2928 /* check entry number and make sure it's a bootadm entry */ 2929 if (lp->flags != BAM_COMMENT || 2930 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 || 2931 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) { 2932 ent = ent->next; 2933 continue; 2934 } 2935 2936 /* free the entry content */ 2937 do { 2938 freed = lp; 2939 lp = lp->next; /* prev stays the same */ 2940 unlink_line(mp, freed); 2941 line_free(freed); 2942 } while (freed != ent->end); 2943 2944 /* free the entry_t structure */ 2945 tmp = ent; 2946 ent = ent->next; 2947 if (tmp->prev) 2948 tmp->prev->next = ent; 2949 else 2950 mp->entries = ent; 2951 if (ent) 2952 ent->prev = tmp->prev; 2953 deleted = 1; 2954 } 2955 2956 if (!deleted && entryNum != ALL_ENTRIES) { 2957 bam_error(NO_BOOTADM_MATCH); 2958 return (BAM_ERROR); 2959 } 2960 2961 /* 2962 * Now that we have deleted an entry, update 2963 * the entry numbering and the default cmd. 2964 */ 2965 update_numbering(mp); 2966 2967 return (BAM_SUCCESS); 2968 } 2969 2970 static error_t 2971 delete_all_entries(menu_t *mp, char *menu_path, char *opt) 2972 { 2973 assert(mp); 2974 assert(opt == NULL); 2975 2976 if (mp->start == NULL) { 2977 bam_print(EMPTY_FILE, menu_path); 2978 return (BAM_SUCCESS); 2979 } 2980 2981 if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) { 2982 return (BAM_ERROR); 2983 } 2984 2985 return (BAM_WRITE); 2986 } 2987 2988 static FILE * 2989 open_diskmap(char *root) 2990 { 2991 FILE *fp; 2992 char cmd[PATH_MAX]; 2993 2994 /* make sure we have a map file */ 2995 fp = fopen(GRUBDISK_MAP, "r"); 2996 if (fp == NULL) { 2997 (void) snprintf(cmd, sizeof (cmd), 2998 "%s%s > /dev/null", root, CREATE_DISKMAP); 2999 (void) system(cmd); 3000 fp = fopen(GRUBDISK_MAP, "r"); 3001 } 3002 return (fp); 3003 } 3004 3005 #define SECTOR_SIZE 512 3006 3007 static int 3008 get_partition(char *device) 3009 { 3010 int i, fd, is_pcfs, partno = -1; 3011 struct mboot *mboot; 3012 char boot_sect[SECTOR_SIZE]; 3013 char *wholedisk, *slice; 3014 3015 /* form whole disk (p0) */ 3016 slice = device + strlen(device) - 2; 3017 is_pcfs = (*slice != 's'); 3018 if (!is_pcfs) 3019 *slice = '\0'; 3020 wholedisk = s_calloc(1, strlen(device) + 3); 3021 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device); 3022 if (!is_pcfs) 3023 *slice = 's'; 3024 3025 /* read boot sector */ 3026 fd = open(wholedisk, O_RDONLY); 3027 free(wholedisk); 3028 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 3029 return (partno); 3030 } 3031 (void) close(fd); 3032 3033 /* parse fdisk table */ 3034 mboot = (struct mboot *)((void *)boot_sect); 3035 for (i = 0; i < FD_NUMPART; i++) { 3036 struct ipart *part = 3037 (struct ipart *)(uintptr_t)mboot->parts + i; 3038 if (is_pcfs) { /* looking for solaris boot part */ 3039 if (part->systid == 0xbe) { 3040 partno = i; 3041 break; 3042 } 3043 } else { /* look for solaris partition, old and new */ 3044 if (part->systid == SUNIXOS || 3045 part->systid == SUNIXOS2) { 3046 partno = i; 3047 break; 3048 } 3049 } 3050 } 3051 return (partno); 3052 } 3053 3054 static char * 3055 get_grubdisk(char *rootdev, FILE *fp, int on_bootdev) 3056 { 3057 char *grubdisk; /* (hd#,#,#) */ 3058 char *slice; 3059 char *grubhd; 3060 int fdiskpart; 3061 int found = 0; 3062 char *devname, *ctdname = strstr(rootdev, "dsk/"); 3063 char linebuf[PATH_MAX]; 3064 3065 if (ctdname == NULL) 3066 return (NULL); 3067 3068 ctdname += strlen("dsk/"); 3069 slice = strrchr(ctdname, 's'); 3070 if (slice) 3071 *slice = '\0'; 3072 3073 rewind(fp); 3074 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) { 3075 grubhd = strtok(linebuf, " \t\n"); 3076 if (grubhd) 3077 devname = strtok(NULL, " \t\n"); 3078 else 3079 devname = NULL; 3080 if (devname && strcmp(devname, ctdname) == 0) { 3081 found = 1; 3082 break; 3083 } 3084 } 3085 3086 if (slice) 3087 *slice = 's'; 3088 3089 if (found == 0) { 3090 if (bam_verbose) 3091 bam_print(DISKMAP_FAIL_NONFATAL, rootdev); 3092 grubhd = "0"; /* assume disk 0 if can't match */ 3093 } 3094 3095 fdiskpart = get_partition(rootdev); 3096 if (fdiskpart == -1) 3097 return (NULL); 3098 3099 grubdisk = s_calloc(1, 10); 3100 if (slice) { 3101 (void) snprintf(grubdisk, 10, "(hd%s,%d,%c)", 3102 grubhd, fdiskpart, slice[1] + 'a' - '0'); 3103 } else 3104 (void) snprintf(grubdisk, 10, "(hd%s,%d)", 3105 grubhd, fdiskpart); 3106 3107 /* if root not on bootdev, change GRUB disk to 0 */ 3108 if (!on_bootdev) 3109 grubdisk[3] = '0'; 3110 return (grubdisk); 3111 } 3112 3113 static char * 3114 get_title(char *rootdir) 3115 { 3116 static char title[80]; /* from /etc/release */ 3117 char *cp = NULL, release[PATH_MAX]; 3118 FILE *fp; 3119 3120 /* open the /etc/release file */ 3121 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir); 3122 3123 fp = fopen(release, "r"); 3124 if (fp == NULL) 3125 return (NULL); 3126 3127 while (s_fgets(title, sizeof (title), fp) != NULL) { 3128 cp = strstr(title, "Solaris"); 3129 if (cp) 3130 break; 3131 } 3132 (void) fclose(fp); 3133 return (cp == NULL ? "Solaris" : cp); 3134 } 3135 3136 char * 3137 get_special(char *mountp) 3138 { 3139 FILE *mntfp; 3140 struct mnttab mp = {0}, mpref = {0}; 3141 3142 mntfp = fopen(MNTTAB, "r"); 3143 if (mntfp == NULL) { 3144 return (0); 3145 } 3146 3147 if (*mountp == '\0') 3148 mpref.mnt_mountp = "/"; 3149 else 3150 mpref.mnt_mountp = mountp; 3151 if (getmntany(mntfp, &mp, &mpref) != 0) { 3152 (void) fclose(mntfp); 3153 return (NULL); 3154 } 3155 (void) fclose(mntfp); 3156 3157 return (s_strdup(mp.mnt_special)); 3158 } 3159 3160 char * 3161 os_to_grubdisk(char *osdisk, int on_bootdev) 3162 { 3163 FILE *fp; 3164 char *grubdisk; 3165 3166 /* translate /dev/dsk name to grub disk name */ 3167 fp = open_diskmap(""); 3168 if (fp == NULL) { 3169 bam_error(DISKMAP_FAIL, osdisk); 3170 return (NULL); 3171 } 3172 grubdisk = get_grubdisk(osdisk, fp, on_bootdev); 3173 (void) fclose(fp); 3174 return (grubdisk); 3175 } 3176 3177 /* 3178 * Check if root is on the boot device 3179 * Return 0 (false) on error 3180 */ 3181 static int 3182 menu_on_bootdev(char *menu_root, FILE *fp) 3183 { 3184 int ret; 3185 char *grubhd, *bootp, *special; 3186 3187 special = get_special(menu_root); 3188 if (special == NULL) 3189 return (0); 3190 bootp = strstr(special, "p0:boot"); 3191 if (bootp) 3192 *bootp = '\0'; 3193 grubhd = get_grubdisk(special, fp, 1); 3194 free(special); 3195 3196 if (grubhd == NULL) 3197 return (0); 3198 ret = grubhd[3] == '0'; 3199 free(grubhd); 3200 return (ret); 3201 } 3202 3203 /* 3204 * look for matching bootadm entry with specified parameters 3205 * Here are the rules (based on existing usage): 3206 * - If title is specified, match on title only 3207 * - Else, match on grubdisk and module (don't care about kernel line). 3208 * note that, if root_opt is non-zero, the absence of root line is 3209 * considered a match. 3210 */ 3211 static entry_t * 3212 find_boot_entry(menu_t *mp, char *title, char *root, char *module, 3213 int root_opt, int *entry_num) 3214 { 3215 int i; 3216 line_t *lp; 3217 entry_t *ent; 3218 3219 /* find matching entry */ 3220 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) { 3221 lp = ent->start; 3222 3223 /* first line of entry must be bootadm comment */ 3224 lp = ent->start; 3225 if (lp->flags != BAM_COMMENT || 3226 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) { 3227 continue; 3228 } 3229 3230 /* advance to title line */ 3231 lp = lp->next; 3232 if (title) { 3233 if (lp->flags == BAM_TITLE && lp->arg && 3234 strcmp(lp->arg, title) == 0) 3235 break; 3236 continue; /* check title only */ 3237 } 3238 3239 lp = lp->next; /* advance to root line */ 3240 if (lp == NULL || strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3241 /* root command found, match grub disk */ 3242 if (strcmp(lp->arg, root) != 0) { 3243 continue; 3244 } 3245 lp = lp->next; /* advance to kernel line */ 3246 } else { 3247 /* no root command, see if root is optional */ 3248 if (root_opt == 0) { 3249 continue; 3250 } 3251 } 3252 3253 if (lp == NULL || lp->next == NULL) { 3254 continue; 3255 } 3256 3257 /* 3258 * Check for matching module entry (failsafe or normal). We 3259 * use a strncmp to match "module" or "module$", since we 3260 * don't know which one it should be. If it fails to match, 3261 * we go around the loop again. 3262 */ 3263 lp = lp->next; /* advance to module line */ 3264 if ((strncmp(lp->cmd, menu_cmds[MODULE_CMD], 3265 strlen(menu_cmds[MODULE_CMD])) != 0) || 3266 (strcmp(lp->arg, module) != 0)) { 3267 continue; 3268 } 3269 break; /* match found */ 3270 } 3271 3272 *entry_num = i; 3273 return (ent); 3274 } 3275 3276 static int 3277 update_boot_entry(menu_t *mp, char *title, char *root, char *kernel, 3278 char *module, int root_opt) 3279 { 3280 int i, change_kernel = 0; 3281 entry_t *ent; 3282 line_t *lp; 3283 char linebuf[BAM_MAXLINE]; 3284 3285 /* note: don't match on title, it's updated on upgrade */ 3286 ent = find_boot_entry(mp, NULL, root, module, root_opt, &i); 3287 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) { 3288 /* 3289 * We may be upgrading a kernel from multiboot to 3290 * directboot. Look for a multiboot entry. 3291 */ 3292 ent = find_boot_entry(mp, NULL, root, MULTI_BOOT_ARCHIVE, 3293 root_opt, &i); 3294 if (ent != NULL) { 3295 change_kernel = 1; 3296 } 3297 } 3298 if (ent == NULL) 3299 return (add_boot_entry(mp, title, root_opt ? NULL : root, 3300 kernel, module)); 3301 3302 /* replace title of exiting entry and delete root line */ 3303 lp = ent->start; 3304 lp = lp->next; /* title line */ 3305 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3306 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 3307 free(lp->arg); 3308 free(lp->line); 3309 lp->arg = s_strdup(title); 3310 lp->line = s_strdup(linebuf); 3311 3312 lp = lp->next; /* root line */ 3313 if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3314 if (root_opt) { /* root line not needed */ 3315 line_t *tmp = lp; 3316 lp = lp->next; 3317 unlink_line(mp, tmp); 3318 line_free(tmp); 3319 } else 3320 lp = lp->next; 3321 } 3322 3323 if (change_kernel) { 3324 /* 3325 * We're upgrading from multiboot to directboot. 3326 */ 3327 if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) { 3328 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3329 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 3330 kernel); 3331 free(lp->arg); 3332 free(lp->line); 3333 lp->arg = s_strdup(kernel); 3334 lp->line = s_strdup(linebuf); 3335 lp = lp->next; 3336 } 3337 if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 3338 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3339 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 3340 module); 3341 free(lp->arg); 3342 free(lp->line); 3343 lp->arg = s_strdup(module); 3344 lp->line = s_strdup(linebuf); 3345 lp = lp->next; 3346 } 3347 } 3348 return (i); 3349 } 3350 3351 /*ARGSUSED*/ 3352 static error_t 3353 update_entry(menu_t *mp, char *menu_root, char *opt) 3354 { 3355 FILE *fp; 3356 int entry; 3357 char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL; 3358 struct stat sbuf; 3359 char failsafe[256]; 3360 3361 assert(mp); 3362 assert(opt); 3363 3364 osdev = strtok(opt, ","); 3365 osroot = strtok(NULL, ","); 3366 if (osroot == NULL) 3367 osroot = menu_root; 3368 title = get_title(osroot); 3369 3370 /* translate /dev/dsk name to grub disk name */ 3371 fp = open_diskmap(osroot); 3372 if (fp == NULL) { 3373 bam_error(DISKMAP_FAIL, osdev); 3374 return (BAM_ERROR); 3375 } 3376 grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp)); 3377 (void) fclose(fp); 3378 if (grubdisk == NULL) { 3379 bam_error(DISKMAP_FAIL, osdev); 3380 return (BAM_ERROR); 3381 } 3382 3383 /* add the entry for normal Solaris */ 3384 if (bam_direct == BAM_DIRECT_DBOOT) { 3385 entry = update_boot_entry(mp, title, grubdisk, 3386 DIRECT_BOOT_KERNEL, DIRECT_BOOT_ARCHIVE, 3387 osroot == menu_root); 3388 } else { 3389 entry = update_boot_entry(mp, title, grubdisk, 3390 MULTI_BOOT, MULTI_BOOT_ARCHIVE, 3391 osroot == menu_root); 3392 } 3393 3394 /* add the entry for failsafe archive */ 3395 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT); 3396 if (stat(failsafe, &sbuf) == 0) { 3397 3398 /* Figure out where the kernel line should point */ 3399 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, 3400 DIRECT_BOOT_FAILSAFE_KERNEL); 3401 if (stat(failsafe, &sbuf) == 0) { 3402 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE; 3403 } else { 3404 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 3405 osroot, MULTI_BOOT_FAILSAFE); 3406 if (stat(failsafe, &sbuf) == 0) { 3407 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE; 3408 } 3409 } 3410 if (failsafe_kernel != NULL) { 3411 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk, 3412 failsafe_kernel, MINIROOT, osroot == menu_root); 3413 } else { 3414 bam_error(NO_FAILSAFE_KERNEL); 3415 } 3416 } 3417 free(grubdisk); 3418 3419 if (entry == BAM_ERROR) { 3420 return (BAM_ERROR); 3421 } 3422 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3423 return (BAM_WRITE); 3424 } 3425 3426 static char * 3427 read_grub_root(void) 3428 { 3429 FILE *fp; 3430 struct stat sb; 3431 char buf[BAM_MAXLINE]; 3432 char *rootstr; 3433 3434 if (stat(GRUB_slice, &sb) != 0) { 3435 bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno)); 3436 return (NULL); 3437 } 3438 3439 if (stat(GRUB_root, &sb) != 0) { 3440 bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno)); 3441 return (NULL); 3442 } 3443 3444 fp = fopen(GRUB_root, "r"); 3445 if (fp == NULL) { 3446 bam_error(OPEN_FAIL, GRUB_root, strerror(errno)); 3447 return (NULL); 3448 } 3449 3450 if (s_fgets(buf, sizeof (buf), fp) == NULL) { 3451 bam_error(EMPTY_FILE, GRUB_root, strerror(errno)); 3452 (void) fclose(fp); 3453 return (NULL); 3454 } 3455 3456 /* 3457 * Copy buf here as check below may trash the buffer 3458 */ 3459 rootstr = s_strdup(buf); 3460 3461 if (s_fgets(buf, sizeof (buf), fp) != NULL) { 3462 bam_error(BAD_ROOT_FILE, GRUB_root); 3463 free(rootstr); 3464 rootstr = NULL; 3465 } 3466 3467 (void) fclose(fp); 3468 3469 return (rootstr); 3470 } 3471 3472 static void 3473 save_default_entry(menu_t *mp, const char *which) 3474 { 3475 int lineNum, entryNum; 3476 int entry = 0; /* default is 0 */ 3477 char linebuf[BAM_MAXLINE]; 3478 line_t *lp = mp->curdefault; 3479 3480 if (mp->start) { 3481 lineNum = mp->end->lineNum; 3482 entryNum = mp->end->entryNum; 3483 } else { 3484 lineNum = LINE_INIT; 3485 entryNum = ENTRY_INIT; 3486 } 3487 3488 if (lp) 3489 entry = s_strtol(lp->arg); 3490 3491 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry); 3492 line_parser(mp, linebuf, &lineNum, &entryNum); 3493 } 3494 3495 static void 3496 restore_default_entry(menu_t *mp, const char *which, line_t *lp) 3497 { 3498 int entry; 3499 char *str; 3500 3501 if (lp == NULL) 3502 return; /* nothing to restore */ 3503 3504 str = lp->arg + strlen(which); 3505 entry = s_strtol(str); 3506 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3507 3508 /* delete saved old default line */ 3509 unlink_line(mp, lp); 3510 line_free(lp); 3511 } 3512 3513 /* 3514 * This function is for supporting reboot with args. 3515 * The opt value can be: 3516 * NULL delete temp entry, if present 3517 * entry=# switches default entry to 1 3518 * else treated as boot-args and setup a temperary menu entry 3519 * and make it the default 3520 */ 3521 #define REBOOT_TITLE "Solaris_reboot_transient" 3522 3523 /*ARGSUSED*/ 3524 static error_t 3525 update_temp(menu_t *mp, char *menupath, char *opt) 3526 { 3527 int entry; 3528 char *grubdisk, *rootdev, *path, *opt_ptr; 3529 char kernbuf[BUFSIZ]; 3530 char args_buf[BUFSIZ]; 3531 struct stat sb; 3532 3533 assert(mp); 3534 3535 /* If no option, delete exiting reboot menu entry */ 3536 if (opt == NULL) { 3537 entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL, 3538 0, &entry); 3539 if (ent == NULL) /* not found is ok */ 3540 return (BAM_SUCCESS); 3541 (void) do_delete(mp, entry); 3542 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault); 3543 mp->olddefault = NULL; 3544 return (BAM_WRITE); 3545 } 3546 3547 /* if entry= is specified, set the default entry */ 3548 if (strncmp(opt, "entry=", strlen("entry=")) == 0 && 3549 selector(mp, opt, &entry, NULL) == BAM_SUCCESS) { 3550 /* this is entry=# option */ 3551 return (set_global(mp, menu_cmds[DEFAULT_CMD], entry)); 3552 } 3553 3554 /* 3555 * add a new menu entry base on opt and make it the default 3556 */ 3557 grubdisk = NULL; 3558 if (stat(GRUB_slice, &sb) != 0) { 3559 /* 3560 * 1. First get root disk name from mnttab 3561 * 2. Translate disk name to grub name 3562 * 3. Add the new menu entry 3563 */ 3564 rootdev = get_special("/"); 3565 if (rootdev) { 3566 grubdisk = os_to_grubdisk(rootdev, 1); 3567 free(rootdev); 3568 } 3569 } else { 3570 /* 3571 * This is an LU BE. The GRUB_root file 3572 * contains entry for GRUB's "root" cmd. 3573 */ 3574 grubdisk = read_grub_root(); 3575 } 3576 if (grubdisk == NULL) { 3577 bam_error(REBOOT_WITH_ARGS_FAILED); 3578 return (BAM_ERROR); 3579 } 3580 3581 /* add an entry for Solaris reboot */ 3582 if (bam_direct == BAM_DIRECT_DBOOT) { 3583 if (opt[0] == '-') { 3584 /* It's an option - first see if boot-file is set */ 3585 if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ) 3586 != BAM_SUCCESS) 3587 return (BAM_ERROR); 3588 if (kernbuf[0] == '\0') 3589 (void) strncpy(kernbuf, DIRECT_BOOT_KERNEL, 3590 BUFSIZ); 3591 (void) strlcat(kernbuf, " ", BUFSIZ); 3592 (void) strlcat(kernbuf, opt, BUFSIZ); 3593 } else if (opt[0] == '/') { 3594 /* It's a full path, so write it out. */ 3595 (void) strlcpy(kernbuf, opt, BUFSIZ); 3596 3597 /* 3598 * If someone runs: 3599 * 3600 * # eeprom boot-args='-kd' 3601 * # reboot /platform/i86pc/kernel/unix 3602 * 3603 * we want to use the boot-args as part of the boot 3604 * line. On the other hand, if someone runs: 3605 * 3606 * # reboot "/platform/i86pc/kernel/unix -kd" 3607 * 3608 * we don't need to mess with boot-args. If there's 3609 * no space in the options string, assume we're in the 3610 * first case. 3611 */ 3612 if (strchr(opt, ' ') == NULL) { 3613 if (set_kernel(mp, ARGS_CMD, NULL, args_buf, 3614 BUFSIZ) != BAM_SUCCESS) 3615 return (BAM_ERROR); 3616 3617 if (args_buf[0] != '\0') { 3618 (void) strlcat(kernbuf, " ", BUFSIZ); 3619 (void) strlcat(kernbuf, args_buf, 3620 BUFSIZ); 3621 } 3622 } 3623 } else { 3624 /* 3625 * It may be a partial path, or it may be a partial 3626 * path followed by options. Assume that only options 3627 * follow a space. If someone sends us a kernel path 3628 * that includes a space, they deserve to be broken. 3629 */ 3630 opt_ptr = strchr(opt, ' '); 3631 if (opt_ptr != NULL) { 3632 *opt_ptr = '\0'; 3633 } 3634 3635 path = expand_path(opt); 3636 if (path != NULL) { 3637 (void) strlcpy(kernbuf, path, BUFSIZ); 3638 free(path); 3639 3640 /* 3641 * If there were options given, use those. 3642 * Otherwise, copy over the default options. 3643 */ 3644 if (opt_ptr != NULL) { 3645 /* Restore the space in opt string */ 3646 *opt_ptr = ' '; 3647 (void) strlcat(kernbuf, opt_ptr, 3648 BUFSIZ); 3649 } else { 3650 if (set_kernel(mp, ARGS_CMD, NULL, 3651 args_buf, BUFSIZ) != BAM_SUCCESS) 3652 return (BAM_ERROR); 3653 3654 if (args_buf[0] != '\0') { 3655 (void) strlcat(kernbuf, " ", 3656 BUFSIZ); 3657 (void) strlcat(kernbuf, 3658 args_buf, BUFSIZ); 3659 } 3660 } 3661 } else { 3662 bam_error(UNKNOWN_KERNEL, opt); 3663 bam_print_stderr(UNKNOWN_KERNEL_REBOOT); 3664 return (BAM_ERROR); 3665 } 3666 } 3667 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3668 NULL); 3669 } else { 3670 (void) snprintf(kernbuf, sizeof (kernbuf), "%s %s", 3671 MULTI_BOOT, opt); 3672 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3673 MULTI_BOOT_ARCHIVE); 3674 } 3675 free(grubdisk); 3676 3677 if (entry == BAM_ERROR) { 3678 bam_error(REBOOT_WITH_ARGS_FAILED); 3679 return (BAM_ERROR); 3680 } 3681 3682 save_default_entry(mp, BAM_OLDDEF); 3683 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3684 return (BAM_WRITE); 3685 } 3686 3687 static error_t 3688 set_global(menu_t *mp, char *globalcmd, int val) 3689 { 3690 line_t *lp, *found, *last; 3691 char *cp, *str; 3692 char prefix[BAM_MAXLINE]; 3693 size_t len; 3694 3695 assert(mp); 3696 assert(globalcmd); 3697 3698 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) { 3699 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) { 3700 (void) snprintf(prefix, sizeof (prefix), "%d", val); 3701 bam_error(INVALID_ENTRY, prefix); 3702 return (BAM_ERROR); 3703 } 3704 } 3705 3706 found = last = NULL; 3707 for (lp = mp->start; lp; lp = lp->next) { 3708 if (lp->flags != BAM_GLOBAL) 3709 continue; 3710 3711 last = lp; /* track the last global found */ 3712 3713 if (lp->cmd == NULL) { 3714 bam_error(NO_CMD, lp->lineNum); 3715 continue; 3716 } 3717 if (strcmp(globalcmd, lp->cmd) != 0) 3718 continue; 3719 3720 if (found) { 3721 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 3722 } 3723 found = lp; 3724 } 3725 3726 if (found == NULL) { 3727 lp = s_calloc(1, sizeof (line_t)); 3728 if (last == NULL) { 3729 lp->next = mp->start; 3730 mp->start = lp; 3731 mp->end = (mp->end) ? mp->end : lp; 3732 } else { 3733 lp->next = last->next; 3734 last->next = lp; 3735 if (lp->next == NULL) 3736 mp->end = lp; 3737 } 3738 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */ 3739 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]); 3740 len += 10; /* val < 10 digits */ 3741 lp->line = s_calloc(1, len); 3742 (void) snprintf(lp->line, len, "%s%s%d", 3743 globalcmd, menu_cmds[SEP_CMD], val); 3744 return (BAM_WRITE); 3745 } 3746 3747 /* 3748 * We are changing an existing entry. Retain any prefix whitespace, 3749 * but overwrite everything else. This preserves tabs added for 3750 * readability. 3751 */ 3752 str = found->line; 3753 cp = prefix; 3754 while (*str == ' ' || *str == '\t') 3755 *(cp++) = *(str++); 3756 *cp = '\0'; /* Terminate prefix */ 3757 len = strlen(prefix) + strlen(globalcmd); 3758 len += strlen(menu_cmds[SEP_CMD]) + 10; 3759 3760 free(found->line); 3761 found->line = s_calloc(1, len); 3762 (void) snprintf(found->line, len, 3763 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val); 3764 3765 return (BAM_WRITE); /* need a write to menu */ 3766 } 3767 3768 /* 3769 * partial_path may be anything like "kernel/unix" or "kmdb". Try to 3770 * expand it to a full unix path. The calling function is expected to 3771 * output a message if an error occurs and NULL is returned. 3772 */ 3773 static char * 3774 expand_path(const char *partial_path) 3775 { 3776 int new_path_len; 3777 char *new_path, new_path2[PATH_MAX]; 3778 struct stat sb; 3779 3780 new_path_len = strlen(partial_path) + 64; 3781 new_path = s_calloc(1, new_path_len); 3782 3783 /* First, try the simplest case - something like "kernel/unix" */ 3784 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s", 3785 partial_path); 3786 if (stat(new_path, &sb) == 0) { 3787 return (new_path); 3788 } 3789 3790 if (strcmp(partial_path, "kmdb") == 0) { 3791 (void) snprintf(new_path, new_path_len, "%s -k", 3792 DIRECT_BOOT_KERNEL); 3793 return (new_path); 3794 } 3795 3796 /* 3797 * We've quickly reached unsupported usage. Try once more to 3798 * see if we were just given a glom name. 3799 */ 3800 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix", 3801 partial_path); 3802 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix", 3803 partial_path); 3804 if (stat(new_path, &sb) == 0) { 3805 if (stat(new_path2, &sb) == 0) { 3806 /* 3807 * We matched both, so we actually 3808 * want to write the $ISADIR version. 3809 */ 3810 (void) snprintf(new_path, new_path_len, 3811 "/platform/i86pc/kernel/%s/$ISADIR/unix", 3812 partial_path); 3813 } 3814 return (new_path); 3815 } 3816 3817 free(new_path); 3818 return (NULL); 3819 } 3820 3821 /* 3822 * The kernel cmd and arg have been changed, so 3823 * check whether the archive line needs to change. 3824 */ 3825 static void 3826 set_archive_line(entry_t *entryp, line_t *kernelp) 3827 { 3828 line_t *lp = entryp->start; 3829 char *new_archive; 3830 menu_cmd_t m_cmd; 3831 3832 for (; lp != NULL; lp = lp->next) { 3833 if (strncmp(lp->cmd, menu_cmds[MODULE_CMD], 3834 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) { 3835 break; 3836 } 3837 if (lp == entryp->end) 3838 return; 3839 } 3840 if (lp == NULL) 3841 return; 3842 3843 if (strstr(kernelp->arg, "$ISADIR") != NULL) { 3844 new_archive = DIRECT_BOOT_ARCHIVE; 3845 m_cmd = MODULE_DOLLAR_CMD; 3846 } else if (strstr(kernelp->arg, "amd64") != NULL) { 3847 new_archive = DIRECT_BOOT_ARCHIVE_64; 3848 m_cmd = MODULE_CMD; 3849 } else { 3850 new_archive = DIRECT_BOOT_ARCHIVE_32; 3851 m_cmd = MODULE_CMD; 3852 } 3853 3854 if (strcmp(lp->arg, new_archive) == 0) 3855 return; 3856 3857 if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) { 3858 free(lp->cmd); 3859 lp->cmd = s_strdup(menu_cmds[m_cmd]); 3860 } 3861 3862 free(lp->arg); 3863 lp->arg = s_strdup(new_archive); 3864 update_line(lp); 3865 } 3866 3867 /* 3868 * Title for an entry to set properties that once went in bootenv.rc. 3869 */ 3870 #define BOOTENV_RC_TITLE "Solaris bootenv rc" 3871 3872 /* 3873 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments 3874 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length 3875 * string, reset the value to the default. If path is a non-zero-length 3876 * string, set the kernel or arguments. 3877 */ 3878 static error_t 3879 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize) 3880 { 3881 int entryNum, rv = BAM_SUCCESS, free_new_path = 0; 3882 entry_t *entryp; 3883 line_t *ptr, *kernelp; 3884 char *new_arg, *old_args, *space; 3885 char *grubdisk, *rootdev, *new_path; 3886 char old_space; 3887 size_t old_kernel_len, new_str_len; 3888 struct stat sb; 3889 3890 assert(bufsize > 0); 3891 3892 ptr = kernelp = NULL; 3893 new_arg = old_args = space = NULL; 3894 grubdisk = rootdev = new_path = NULL; 3895 buf[0] = '\0'; 3896 3897 if (bam_direct != BAM_DIRECT_DBOOT) { 3898 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args"); 3899 return (BAM_ERROR); 3900 } 3901 3902 /* 3903 * If a user changed the default entry to a non-bootadm controlled 3904 * one, we don't want to mess with it. Just print an error and 3905 * return. 3906 */ 3907 if (mp->curdefault) { 3908 entryNum = s_strtol(mp->curdefault->arg); 3909 for (entryp = mp->entries; entryp; entryp = entryp->next) { 3910 if (entryp->entryNum == entryNum) 3911 break; 3912 } 3913 if ((entryp != NULL) && 3914 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) { 3915 bam_error(DEFAULT_NOT_BAM); 3916 return (BAM_ERROR); 3917 } 3918 } 3919 3920 entryNum = -1; 3921 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, 0, 3922 &entryNum); 3923 3924 if (entryp != NULL) { 3925 for (ptr = entryp->start; ptr && ptr != entryp->end; 3926 ptr = ptr->next) { 3927 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD], 3928 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) { 3929 kernelp = ptr; 3930 break; 3931 } 3932 } 3933 if (kernelp == NULL) { 3934 bam_error(NO_KERNEL, entryNum); 3935 return (BAM_ERROR); 3936 } 3937 3938 old_kernel_len = strcspn(kernelp->arg, " \t"); 3939 space = old_args = kernelp->arg + old_kernel_len; 3940 while ((*old_args == ' ') || (*old_args == '\t')) 3941 old_args++; 3942 } 3943 3944 if (path == NULL) { 3945 /* Simply report what was found */ 3946 if (kernelp == NULL) 3947 return (BAM_SUCCESS); 3948 3949 if (optnum == ARGS_CMD) { 3950 if (old_args[0] != '\0') 3951 (void) strlcpy(buf, old_args, bufsize); 3952 } else { 3953 /* 3954 * We need to print the kernel, so we just turn the 3955 * first space into a '\0' and print the beginning. 3956 * We don't print anything if it's the default kernel. 3957 */ 3958 old_space = *space; 3959 *space = '\0'; 3960 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) 3961 (void) strlcpy(buf, kernelp->arg, bufsize); 3962 *space = old_space; 3963 } 3964 return (BAM_SUCCESS); 3965 } 3966 3967 /* 3968 * First, check if we're resetting an entry to the default. 3969 */ 3970 if ((path[0] == '\0') || 3971 ((optnum == KERNEL_CMD) && 3972 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) { 3973 if ((entryp == NULL) || (kernelp == NULL)) { 3974 /* No previous entry, it's already the default */ 3975 return (BAM_SUCCESS); 3976 } 3977 3978 /* 3979 * Check if we can delete the entry. If we're resetting the 3980 * kernel command, and the args is already empty, or if we're 3981 * resetting the args command, and the kernel is already the 3982 * default, we can restore the old default and delete the entry. 3983 */ 3984 if (((optnum == KERNEL_CMD) && 3985 ((old_args == NULL) || (old_args[0] == '\0'))) || 3986 ((optnum == ARGS_CMD) && 3987 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL, 3988 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) { 3989 kernelp = NULL; 3990 (void) do_delete(mp, entryNum); 3991 restore_default_entry(mp, BAM_OLD_RC_DEF, 3992 mp->old_rc_default); 3993 mp->old_rc_default = NULL; 3994 rv = BAM_WRITE; 3995 goto done; 3996 } 3997 3998 if (optnum == KERNEL_CMD) { 3999 /* 4000 * At this point, we've already checked that old_args 4001 * and entryp are valid pointers. The "+ 2" is for 4002 * a space a the string termination character. 4003 */ 4004 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) + 4005 strlen(old_args) + 2; 4006 new_arg = s_calloc(1, new_str_len); 4007 (void) snprintf(new_arg, new_str_len, "%s %s", 4008 DIRECT_BOOT_KERNEL, old_args); 4009 free(kernelp->arg); 4010 kernelp->arg = new_arg; 4011 4012 /* 4013 * We have changed the kernel line, so we may need 4014 * to update the archive line as well. 4015 */ 4016 set_archive_line(entryp, kernelp); 4017 } else { 4018 /* 4019 * We're resetting the boot args to nothing, so 4020 * we only need to copy the kernel. We've already 4021 * checked that the kernel is not the default. 4022 */ 4023 new_arg = s_calloc(1, old_kernel_len + 1); 4024 (void) snprintf(new_arg, old_kernel_len + 1, "%s", 4025 kernelp->arg); 4026 free(kernelp->arg); 4027 kernelp->arg = new_arg; 4028 } 4029 rv = BAM_WRITE; 4030 goto done; 4031 } 4032 4033 /* 4034 * Expand the kernel file to a full path, if necessary 4035 */ 4036 if ((optnum == KERNEL_CMD) && (path[0] != '/')) { 4037 new_path = expand_path(path); 4038 if (new_path == NULL) { 4039 bam_error(UNKNOWN_KERNEL, path); 4040 return (BAM_ERROR); 4041 } 4042 free_new_path = 1; 4043 } else { 4044 new_path = path; 4045 free_new_path = 0; 4046 } 4047 4048 /* 4049 * At this point, we know we're setting a new value. First, take care 4050 * of the case where there was no previous entry. 4051 */ 4052 if (entryp == NULL) { 4053 /* Similar to code in update_temp */ 4054 if (stat(GRUB_slice, &sb) != 0) { 4055 /* 4056 * 1. First get root disk name from mnttab 4057 * 2. Translate disk name to grub name 4058 * 3. Add the new menu entry 4059 */ 4060 rootdev = get_special("/"); 4061 if (rootdev) { 4062 grubdisk = os_to_grubdisk(rootdev, 1); 4063 free(rootdev); 4064 } 4065 } else { 4066 /* 4067 * This is an LU BE. The GRUB_root file 4068 * contains entry for GRUB's "root" cmd. 4069 */ 4070 grubdisk = read_grub_root(); 4071 } 4072 if (grubdisk == NULL) { 4073 bam_error(REBOOT_WITH_ARGS_FAILED); 4074 rv = BAM_ERROR; 4075 goto done; 4076 } 4077 if (optnum == KERNEL_CMD) { 4078 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 4079 grubdisk, new_path, NULL); 4080 } else { 4081 new_str_len = strlen(DIRECT_BOOT_KERNEL) + 4082 strlen(path) + 8; 4083 new_arg = s_calloc(1, new_str_len); 4084 4085 (void) snprintf(new_arg, new_str_len, "%s %s", 4086 DIRECT_BOOT_KERNEL, path); 4087 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 4088 grubdisk, new_arg, DIRECT_BOOT_ARCHIVE); 4089 } 4090 save_default_entry(mp, BAM_OLD_RC_DEF); 4091 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum); 4092 rv = BAM_WRITE; 4093 goto done; 4094 } 4095 4096 /* 4097 * There was already an bootenv entry which we need to edit. 4098 */ 4099 if (optnum == KERNEL_CMD) { 4100 new_str_len = strlen(new_path) + strlen(old_args) + 2; 4101 new_arg = s_calloc(1, new_str_len); 4102 (void) snprintf(new_arg, new_str_len, "%s %s", new_path, 4103 old_args); 4104 free(kernelp->arg); 4105 kernelp->arg = new_arg; 4106 4107 /* 4108 * If we have changed the kernel line, we may need to update 4109 * the archive line as well. 4110 */ 4111 set_archive_line(entryp, kernelp); 4112 } else { 4113 new_str_len = old_kernel_len + strlen(path) + 8; 4114 new_arg = s_calloc(1, new_str_len); 4115 (void) strncpy(new_arg, kernelp->arg, old_kernel_len); 4116 (void) strlcat(new_arg, " ", new_str_len); 4117 (void) strlcat(new_arg, path, new_str_len); 4118 free(kernelp->arg); 4119 kernelp->arg = new_arg; 4120 } 4121 rv = BAM_WRITE; 4122 4123 done: 4124 if ((rv == BAM_WRITE) && kernelp) 4125 update_line(kernelp); 4126 if (free_new_path) 4127 free(new_path); 4128 return (rv); 4129 } 4130 4131 /*ARGSUSED*/ 4132 static error_t 4133 set_option(menu_t *mp, char *menu_path, char *opt) 4134 { 4135 int optnum, optval; 4136 char *val; 4137 char buf[BUFSIZ] = ""; 4138 error_t rv; 4139 4140 assert(mp); 4141 assert(opt); 4142 4143 val = strchr(opt, '='); 4144 if (val != NULL) { 4145 *val = '\0'; 4146 } 4147 4148 if (strcmp(opt, "default") == 0) { 4149 optnum = DEFAULT_CMD; 4150 } else if (strcmp(opt, "timeout") == 0) { 4151 optnum = TIMEOUT_CMD; 4152 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) { 4153 optnum = KERNEL_CMD; 4154 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) { 4155 optnum = ARGS_CMD; 4156 } else { 4157 bam_error(INVALID_ENTRY, opt); 4158 return (BAM_ERROR); 4159 } 4160 4161 /* 4162 * kernel and args are allowed without "=new_value" strings. All 4163 * others cause errors 4164 */ 4165 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) { 4166 bam_error(INVALID_ENTRY, opt); 4167 return (BAM_ERROR); 4168 } else if (val != NULL) { 4169 *val = '='; 4170 } 4171 4172 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) { 4173 rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ); 4174 if ((rv == BAM_SUCCESS) && (buf[0] != '\0')) 4175 (void) printf("%s\n", buf); 4176 return (rv); 4177 } else { 4178 optval = s_strtol(val + 1); 4179 return (set_global(mp, menu_cmds[optnum], optval)); 4180 } 4181 } 4182 4183 /* 4184 * The quiet argument suppresses messages. This is used 4185 * when invoked in the context of other commands (e.g. list_entry) 4186 */ 4187 static error_t 4188 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet) 4189 { 4190 line_t *lp; 4191 char *arg; 4192 int done, ret = BAM_SUCCESS; 4193 4194 assert(mp); 4195 assert(menu_path); 4196 assert(globalcmd); 4197 4198 if (mp->start == NULL) { 4199 if (!quiet) 4200 bam_error(NO_MENU, menu_path); 4201 return (BAM_ERROR); 4202 } 4203 4204 done = 0; 4205 for (lp = mp->start; lp; lp = lp->next) { 4206 if (lp->flags != BAM_GLOBAL) 4207 continue; 4208 4209 if (lp->cmd == NULL) { 4210 if (!quiet) 4211 bam_error(NO_CMD, lp->lineNum); 4212 continue; 4213 } 4214 4215 if (strcmp(globalcmd, lp->cmd) != 0) 4216 continue; 4217 4218 /* Found global. Check for duplicates */ 4219 if (done && !quiet) { 4220 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 4221 ret = BAM_ERROR; 4222 } 4223 4224 arg = lp->arg ? lp->arg : ""; 4225 bam_print(GLOBAL_CMD, globalcmd, arg); 4226 done = 1; 4227 } 4228 4229 if (!done && bam_verbose) 4230 bam_print(NO_ENTRY, globalcmd); 4231 4232 return (ret); 4233 } 4234 4235 static error_t 4236 menu_write(char *root, menu_t *mp) 4237 { 4238 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start)); 4239 } 4240 4241 static void 4242 line_free(line_t *lp) 4243 { 4244 if (lp == NULL) 4245 return; 4246 4247 if (lp->cmd) 4248 free(lp->cmd); 4249 if (lp->sep) 4250 free(lp->sep); 4251 if (lp->arg) 4252 free(lp->arg); 4253 if (lp->line) 4254 free(lp->line); 4255 free(lp); 4256 } 4257 4258 static void 4259 linelist_free(line_t *start) 4260 { 4261 line_t *lp; 4262 4263 while (start) { 4264 lp = start; 4265 start = start->next; 4266 line_free(lp); 4267 } 4268 } 4269 4270 static void 4271 filelist_free(filelist_t *flistp) 4272 { 4273 linelist_free(flistp->head); 4274 flistp->head = NULL; 4275 flistp->tail = NULL; 4276 } 4277 4278 static void 4279 menu_free(menu_t *mp) 4280 { 4281 entry_t *ent, *tmp; 4282 assert(mp); 4283 4284 if (mp->start) 4285 linelist_free(mp->start); 4286 ent = mp->entries; 4287 while (ent) { 4288 tmp = ent; 4289 ent = tmp->next; 4290 free(tmp); 4291 } 4292 4293 free(mp); 4294 } 4295 4296 /* 4297 * Utility routines 4298 */ 4299 4300 4301 /* 4302 * Returns 0 on success 4303 * Any other value indicates an error 4304 */ 4305 static int 4306 exec_cmd(char *cmdline, char *output, int64_t osize) 4307 { 4308 char buf[BUFSIZ]; 4309 int ret; 4310 FILE *ptr; 4311 size_t len; 4312 sigset_t set; 4313 void (*disp)(int); 4314 4315 /* 4316 * For security 4317 * - only absolute paths are allowed 4318 * - set IFS to space and tab 4319 */ 4320 if (*cmdline != '/') { 4321 bam_error(ABS_PATH_REQ, cmdline); 4322 return (-1); 4323 } 4324 (void) putenv("IFS= \t"); 4325 4326 /* 4327 * We may have been exec'ed with SIGCHLD blocked 4328 * unblock it here 4329 */ 4330 (void) sigemptyset(&set); 4331 (void) sigaddset(&set, SIGCHLD); 4332 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 4333 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno)); 4334 return (-1); 4335 } 4336 4337 /* 4338 * Set SIGCHLD disposition to SIG_DFL for popen/pclose 4339 */ 4340 disp = sigset(SIGCHLD, SIG_DFL); 4341 if (disp == SIG_ERR) { 4342 bam_error(FAILED_SIG, strerror(errno)); 4343 return (-1); 4344 } 4345 if (disp == SIG_HOLD) { 4346 bam_error(BLOCKED_SIG, cmdline); 4347 return (-1); 4348 } 4349 4350 ptr = popen(cmdline, "r"); 4351 if (ptr == NULL) { 4352 bam_error(POPEN_FAIL, cmdline, strerror(errno)); 4353 return (-1); 4354 } 4355 4356 /* 4357 * If we simply do a pclose() following a popen(), pclose() 4358 * will close the reader end of the pipe immediately even 4359 * if the child process has not started/exited. pclose() 4360 * does wait for cmd to terminate before returning though. 4361 * When the executed command writes its output to the pipe 4362 * there is no reader process and the command dies with 4363 * SIGPIPE. To avoid this we read repeatedly until read 4364 * terminates with EOF. This indicates that the command 4365 * (writer) has closed the pipe and we can safely do a 4366 * pclose(). 4367 * 4368 * Since pclose() does wait for the command to exit, 4369 * we can safely reap the exit status of the command 4370 * from the value returned by pclose() 4371 */ 4372 while (fgets(buf, sizeof (buf), ptr) != NULL) { 4373 /* if (bam_verbose) XXX */ 4374 bam_print(PRINT_NO_NEWLINE, buf); 4375 if (output && osize > 0) { 4376 (void) snprintf(output, osize, "%s", buf); 4377 len = strlen(buf); 4378 output += len; 4379 osize -= len; 4380 } 4381 } 4382 4383 ret = pclose(ptr); 4384 if (ret == -1) { 4385 bam_error(PCLOSE_FAIL, cmdline, strerror(errno)); 4386 return (-1); 4387 } 4388 4389 if (WIFEXITED(ret)) { 4390 return (WEXITSTATUS(ret)); 4391 } else { 4392 bam_error(EXEC_FAIL, cmdline, ret); 4393 return (-1); 4394 } 4395 } 4396 4397 /* 4398 * Since this function returns -1 on error 4399 * it cannot be used to convert -1. However, 4400 * that is sufficient for what we need. 4401 */ 4402 static long 4403 s_strtol(char *str) 4404 { 4405 long l; 4406 char *res = NULL; 4407 4408 if (str == NULL) { 4409 return (-1); 4410 } 4411 4412 errno = 0; 4413 l = strtol(str, &res, 10); 4414 if (errno || *res != '\0') { 4415 return (-1); 4416 } 4417 4418 return (l); 4419 } 4420 4421 /* 4422 * Wrapper around fputs, that adds a newline (since fputs doesn't) 4423 */ 4424 static int 4425 s_fputs(char *str, FILE *fp) 4426 { 4427 char linebuf[BAM_MAXLINE]; 4428 4429 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str); 4430 return (fputs(linebuf, fp)); 4431 } 4432 4433 /* 4434 * Wrapper around fgets, that strips newlines returned by fgets 4435 */ 4436 char * 4437 s_fgets(char *buf, int buflen, FILE *fp) 4438 { 4439 int n; 4440 4441 buf = fgets(buf, buflen, fp); 4442 if (buf) { 4443 n = strlen(buf); 4444 if (n == buflen - 1 && buf[n-1] != '\n') 4445 bam_error(TOO_LONG, buflen - 1, buf); 4446 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1]; 4447 } 4448 4449 return (buf); 4450 } 4451 4452 void * 4453 s_calloc(size_t nelem, size_t sz) 4454 { 4455 void *ptr; 4456 4457 ptr = calloc(nelem, sz); 4458 if (ptr == NULL) { 4459 bam_error(NO_MEM, nelem*sz); 4460 bam_exit(1); 4461 } 4462 return (ptr); 4463 } 4464 4465 void * 4466 s_realloc(void *ptr, size_t sz) 4467 { 4468 ptr = realloc(ptr, sz); 4469 if (ptr == NULL) { 4470 bam_error(NO_MEM, sz); 4471 bam_exit(1); 4472 } 4473 return (ptr); 4474 } 4475 4476 static char * 4477 s_strdup(char *str) 4478 { 4479 char *ptr; 4480 4481 if (str == NULL) 4482 return (NULL); 4483 4484 ptr = strdup(str); 4485 if (ptr == NULL) { 4486 bam_error(NO_MEM, strlen(str) + 1); 4487 bam_exit(1); 4488 } 4489 return (ptr); 4490 } 4491 4492 /* 4493 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients) 4494 * Returns 0 otherwise 4495 */ 4496 static int 4497 is_amd64(void) 4498 { 4499 static int amd64 = -1; 4500 char isabuf[257]; /* from sysinfo(2) manpage */ 4501 4502 if (amd64 != -1) 4503 return (amd64); 4504 4505 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 && 4506 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) 4507 amd64 = 1; 4508 else if (strstr(isabuf, "i386") == NULL) 4509 amd64 = 1; /* diskless server */ 4510 else 4511 amd64 = 0; 4512 4513 return (amd64); 4514 } 4515 4516 static void 4517 append_to_flist(filelist_t *flistp, char *s) 4518 { 4519 line_t *lp; 4520 4521 lp = s_calloc(1, sizeof (line_t)); 4522 lp->line = s_strdup(s); 4523 if (flistp->head == NULL) 4524 flistp->head = lp; 4525 else 4526 flistp->tail->next = lp; 4527 flistp->tail = lp; 4528 } 4529 4530 #if defined(__i386) 4531 4532 UCODE_VENDORS; 4533 4534 /*ARGSUSED*/ 4535 static void 4536 ucode_install(char *root) 4537 { 4538 int i; 4539 4540 for (i = 0; ucode_vendors[i].filestr != NULL; i++) { 4541 int cmd_len = PATH_MAX + 256; 4542 char cmd[PATH_MAX + 256]; 4543 char file[PATH_MAX]; 4544 char timestamp[PATH_MAX]; 4545 struct stat fstatus, tstatus; 4546 struct utimbuf u_times; 4547 4548 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt", 4549 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr); 4550 4551 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode))) 4552 continue; 4553 4554 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file); 4555 4556 if (stat(timestamp, &tstatus) == 0 && 4557 fstatus.st_mtime <= tstatus.st_mtime) 4558 continue; 4559 4560 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R " 4561 "%s/%s/%s %s > /dev/null 2>&1", bam_root, 4562 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file); 4563 if (system(cmd) != 0) 4564 return; 4565 4566 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1) 4567 return; 4568 4569 u_times.actime = fstatus.st_atime; 4570 u_times.modtime = fstatus.st_mtime; 4571 (void) utime(timestamp, &u_times); 4572 } 4573 } 4574 #endif 4575