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