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 1406 /* 1407 * We need an update if file doesn't exist in old archive 1408 */ 1409 if (walk_arg.old_nvlp == NULL || 1410 nvlist_lookup_uint64_array(walk_arg.old_nvlp, 1411 file + bam_rootlen, &value, &sz) != 0) { 1412 if (bam_smf_check) /* ignore new during smf check */ 1413 return (0); 1414 walk_arg.need_update = 1; 1415 if (bam_verbose) 1416 bam_print(PARSEABLE_NEW_FILE, file); 1417 return (0); 1418 } 1419 1420 /* 1421 * File exists in old archive. Check if file has changed 1422 */ 1423 assert(sz == 2); 1424 bcopy(value, filestat, sizeof (filestat)); 1425 1426 if (filestat[0] != stat->st_size || 1427 filestat[1] != stat->st_mtime) { 1428 if (bam_smf_check) { 1429 safefilep = safefiles; 1430 while (safefilep != NULL) { 1431 if (strcmp(file + bam_rootlen, 1432 safefilep->name) == 0) { 1433 (void) creat(NEED_UPDATE_FILE, 0644); 1434 return (0); 1435 } 1436 safefilep = safefilep->next; 1437 } 1438 } 1439 walk_arg.need_update = 1; 1440 if (bam_verbose) 1441 if (bam_smf_check) 1442 bam_print(" %s\n", file); 1443 else 1444 bam_print(PARSEABLE_OUT_DATE, file); 1445 } 1446 1447 return (0); 1448 } 1449 1450 /* 1451 * Check flags and presence of required files. 1452 * The force flag and/or absence of files should 1453 * trigger an update. 1454 * Suppress stdout output if check (-n) option is set 1455 * (as -n should only produce parseable output.) 1456 */ 1457 static void 1458 check_flags_and_files(char *root) 1459 { 1460 char path[PATH_MAX]; 1461 struct stat sb; 1462 1463 /* 1464 * if force, create archive unconditionally 1465 */ 1466 if (bam_force) { 1467 walk_arg.need_update = 1; 1468 if (bam_verbose && !bam_check) 1469 bam_print(UPDATE_FORCE); 1470 return; 1471 } 1472 1473 /* 1474 * If archive is missing, create archive 1475 */ 1476 (void) snprintf(path, sizeof (path), "%s%s", root, 1477 DIRECT_BOOT_ARCHIVE_32); 1478 if (stat(path, &sb) != 0) { 1479 if (bam_verbose && !bam_check) 1480 bam_print(UPDATE_ARCH_MISS, path); 1481 walk_arg.need_update = 1; 1482 return; 1483 } 1484 if (bam_direct == BAM_DIRECT_DBOOT) { 1485 (void) snprintf(path, sizeof (path), "%s%s", root, 1486 DIRECT_BOOT_ARCHIVE_64); 1487 if (stat(path, &sb) != 0) { 1488 if (bam_verbose && !bam_check) 1489 bam_print(UPDATE_ARCH_MISS, path); 1490 walk_arg.need_update = 1; 1491 return; 1492 } 1493 } 1494 } 1495 1496 static error_t 1497 read_one_list(char *root, filelist_t *flistp, char *filelist) 1498 { 1499 char path[PATH_MAX]; 1500 FILE *fp; 1501 char buf[BAM_MAXLINE]; 1502 1503 (void) snprintf(path, sizeof (path), "%s%s", root, filelist); 1504 1505 fp = fopen(path, "r"); 1506 if (fp == NULL) { 1507 if (bam_debug) 1508 bam_error(FLIST_FAIL, path, strerror(errno)); 1509 return (BAM_ERROR); 1510 } 1511 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 1512 /* skip blank lines */ 1513 if (strspn(buf, " \t") == strlen(buf)) 1514 continue; 1515 append_to_flist(flistp, buf); 1516 } 1517 if (fclose(fp) != 0) { 1518 bam_error(CLOSE_FAIL, path, strerror(errno)); 1519 return (BAM_ERROR); 1520 } 1521 return (BAM_SUCCESS); 1522 } 1523 1524 static error_t 1525 read_list(char *root, filelist_t *flistp) 1526 { 1527 int rval; 1528 1529 flistp->head = flistp->tail = NULL; 1530 1531 /* 1532 * Read current lists of files - only the first is mandatory 1533 */ 1534 rval = read_one_list(root, flistp, BOOT_FILE_LIST); 1535 if (rval != BAM_SUCCESS) 1536 return (rval); 1537 (void) read_one_list(root, flistp, ETC_FILE_LIST); 1538 1539 if (flistp->head == NULL) { 1540 bam_error(NO_FLIST); 1541 return (BAM_ERROR); 1542 } 1543 1544 return (BAM_SUCCESS); 1545 } 1546 1547 static void 1548 getoldstat(char *root) 1549 { 1550 char path[PATH_MAX]; 1551 int fd, error; 1552 struct stat sb; 1553 char *ostat; 1554 1555 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 1556 fd = open(path, O_RDONLY); 1557 if (fd == -1) { 1558 if (bam_verbose) 1559 bam_print(OPEN_FAIL, path, strerror(errno)); 1560 walk_arg.need_update = 1; 1561 return; 1562 } 1563 1564 if (fstat(fd, &sb) != 0) { 1565 bam_error(STAT_FAIL, path, strerror(errno)); 1566 (void) close(fd); 1567 walk_arg.need_update = 1; 1568 return; 1569 } 1570 1571 ostat = s_calloc(1, sb.st_size); 1572 1573 if (read(fd, ostat, sb.st_size) != sb.st_size) { 1574 bam_error(READ_FAIL, path, strerror(errno)); 1575 (void) close(fd); 1576 free(ostat); 1577 walk_arg.need_update = 1; 1578 return; 1579 } 1580 1581 (void) close(fd); 1582 1583 walk_arg.old_nvlp = NULL; 1584 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0); 1585 1586 free(ostat); 1587 1588 if (error) { 1589 bam_error(UNPACK_FAIL, path, strerror(error)); 1590 walk_arg.old_nvlp = NULL; 1591 walk_arg.need_update = 1; 1592 return; 1593 } 1594 } 1595 1596 /* 1597 * Checks if a file in the current (old) archive has 1598 * been deleted from the root filesystem. This is needed for 1599 * software like Trusted Extensions (TX) that switch early 1600 * in boot based on presence/absence of a kernel module. 1601 */ 1602 static void 1603 check4stale(char *root) 1604 { 1605 nvpair_t *nvp; 1606 nvlist_t *nvlp; 1607 char *file; 1608 char path[PATH_MAX]; 1609 struct stat sb; 1610 1611 /* 1612 * Skip stale file check during smf check 1613 */ 1614 if (bam_smf_check) 1615 return; 1616 1617 /* Nothing to do if no old stats */ 1618 if ((nvlp = walk_arg.old_nvlp) == NULL) 1619 return; 1620 1621 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp; 1622 nvp = nvlist_next_nvpair(nvlp, nvp)) { 1623 file = nvpair_name(nvp); 1624 if (file == NULL) 1625 continue; 1626 (void) snprintf(path, sizeof (path), "%s/%s", 1627 root, file); 1628 if (stat(path, &sb) == -1) { 1629 walk_arg.need_update = 1; 1630 if (bam_verbose) 1631 bam_print(PARSEABLE_STALE_FILE, path); 1632 } 1633 } 1634 } 1635 1636 static void 1637 create_newstat(void) 1638 { 1639 int error; 1640 1641 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0); 1642 if (error) { 1643 /* 1644 * Not fatal - we can still create archive 1645 */ 1646 walk_arg.new_nvlp = NULL; 1647 bam_error(NVALLOC_FAIL, strerror(error)); 1648 } 1649 } 1650 1651 static void 1652 walk_list(char *root, filelist_t *flistp) 1653 { 1654 char path[PATH_MAX]; 1655 line_t *lp; 1656 1657 for (lp = flistp->head; lp; lp = lp->next) { 1658 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line); 1659 /* XXX shouldn't we use FTW_MOUNT ? */ 1660 if (nftw(path, cmpstat, 20, 0) == -1) { 1661 /* 1662 * Some files may not exist. 1663 * For example: etc/rtc_config on a x86 diskless system 1664 * Emit verbose message only 1665 */ 1666 if (bam_verbose) 1667 bam_print(NFTW_FAIL, path, strerror(errno)); 1668 } 1669 } 1670 } 1671 1672 static void 1673 savenew(char *root) 1674 { 1675 char path[PATH_MAX]; 1676 char path2[PATH_MAX]; 1677 size_t sz; 1678 char *nstat; 1679 int fd, wrote, error; 1680 1681 nstat = NULL; 1682 sz = 0; 1683 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz, 1684 NV_ENCODE_XDR, 0); 1685 if (error) { 1686 bam_error(PACK_FAIL, strerror(error)); 1687 return; 1688 } 1689 1690 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP); 1691 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE); 1692 if (fd == -1) { 1693 bam_error(OPEN_FAIL, path, strerror(errno)); 1694 free(nstat); 1695 return; 1696 } 1697 wrote = write(fd, nstat, sz); 1698 if (wrote != sz) { 1699 bam_error(WRITE_FAIL, path, strerror(errno)); 1700 (void) close(fd); 1701 free(nstat); 1702 return; 1703 } 1704 (void) close(fd); 1705 free(nstat); 1706 1707 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT); 1708 if (rename(path, path2) != 0) { 1709 bam_error(RENAME_FAIL, path2, strerror(errno)); 1710 } 1711 } 1712 1713 static void 1714 clear_walk_args(void) 1715 { 1716 if (walk_arg.old_nvlp) 1717 nvlist_free(walk_arg.old_nvlp); 1718 if (walk_arg.new_nvlp) 1719 nvlist_free(walk_arg.new_nvlp); 1720 walk_arg.need_update = 0; 1721 walk_arg.old_nvlp = NULL; 1722 walk_arg.new_nvlp = NULL; 1723 } 1724 1725 /* 1726 * Returns: 1727 * 0 - no update necessary 1728 * 1 - update required. 1729 * BAM_ERROR (-1) - An error occurred 1730 * 1731 * Special handling for check (-n): 1732 * ================================ 1733 * The check (-n) option produces parseable output. 1734 * To do this, we suppress all stdout messages unrelated 1735 * to out of sync files. 1736 * All stderr messages are still printed though. 1737 * 1738 */ 1739 static int 1740 update_required(char *root) 1741 { 1742 struct stat sb; 1743 char path[PATH_MAX]; 1744 filelist_t flist; 1745 filelist_t *flistp = &flist; 1746 int need_update; 1747 1748 flistp->head = flistp->tail = NULL; 1749 1750 walk_arg.need_update = 0; 1751 1752 /* 1753 * Without consulting stat data, check if we need update 1754 */ 1755 check_flags_and_files(root); 1756 1757 /* 1758 * In certain deployment scenarios, filestat may not 1759 * exist. Ignore it during boot-archive SMF check. 1760 */ 1761 if (bam_smf_check) { 1762 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 1763 if (stat(path, &sb) != 0) 1764 return (0); 1765 } 1766 1767 /* 1768 * consult stat data only if we haven't made a decision 1769 * about update. If checking (-n) however, we always 1770 * need stat data (since we want to compare old and new) 1771 */ 1772 if (!walk_arg.need_update || bam_check) 1773 getoldstat(root); 1774 1775 /* 1776 * Check if the archive contains files that are no longer 1777 * present on the root filesystem. 1778 */ 1779 if (!walk_arg.need_update || bam_check) 1780 check4stale(root); 1781 1782 /* 1783 * read list of files 1784 */ 1785 if (read_list(root, flistp) != BAM_SUCCESS) { 1786 clear_walk_args(); 1787 return (BAM_ERROR); 1788 } 1789 1790 assert(flistp->head && flistp->tail); 1791 1792 /* 1793 * At this point either the update is required 1794 * or the decision is pending. In either case 1795 * we need to create new stat nvlist 1796 */ 1797 create_newstat(); 1798 1799 /* 1800 * This walk does 2 things: 1801 * - gets new stat data for every file 1802 * - (optional) compare old and new stat data 1803 */ 1804 walk_list(root, &flist); 1805 1806 /* done with the file list */ 1807 filelist_free(flistp); 1808 1809 /* 1810 * if we didn't succeed in creating new stat data above 1811 * just return result of update check so that archive is built. 1812 */ 1813 if (walk_arg.new_nvlp == NULL) { 1814 bam_error(NO_NEW_STAT); 1815 need_update = walk_arg.need_update; 1816 clear_walk_args(); 1817 return (need_update ? 1 : 0); 1818 } 1819 1820 1821 /* 1822 * If no update required, discard newstat 1823 */ 1824 if (!walk_arg.need_update) { 1825 clear_walk_args(); 1826 return (0); 1827 } 1828 1829 /* 1830 * At this point we need an update - so save new stat data 1831 * However, if only checking (-n), don't save new stat data. 1832 */ 1833 if (!bam_check) 1834 savenew(root); 1835 1836 clear_walk_args(); 1837 1838 return (1); 1839 } 1840 1841 static error_t 1842 create_ramdisk(char *root) 1843 { 1844 char *cmdline, path[PATH_MAX]; 1845 size_t len; 1846 struct stat sb; 1847 1848 /* 1849 * Setup command args for create_ramdisk.ksh 1850 */ 1851 (void) snprintf(path, sizeof (path), "%s%s", root, CREATE_RAMDISK); 1852 if (stat(path, &sb) != 0) { 1853 bam_error(ARCH_EXEC_MISS, path, strerror(errno)); 1854 return (BAM_ERROR); 1855 } 1856 1857 len = strlen(path) + strlen(root) + 10; /* room for space + -R */ 1858 cmdline = s_calloc(1, len); 1859 1860 if (strlen(root) > 1) { 1861 (void) snprintf(cmdline, len, "%s -R %s", path, root); 1862 /* chop off / at the end */ 1863 cmdline[strlen(cmdline) - 1] = '\0'; 1864 } else 1865 (void) snprintf(cmdline, len, "%s", path); 1866 1867 if (exec_cmd(cmdline, NULL, 0) != 0) { 1868 bam_error(ARCHIVE_FAIL, cmdline); 1869 free(cmdline); 1870 return (BAM_ERROR); 1871 } 1872 free(cmdline); 1873 1874 /* 1875 * Verify that the archive has been created 1876 */ 1877 (void) snprintf(path, sizeof (path), "%s%s", root, 1878 DIRECT_BOOT_ARCHIVE_32); 1879 if (stat(path, &sb) != 0) { 1880 bam_error(ARCHIVE_NOT_CREATED, path); 1881 return (BAM_ERROR); 1882 } 1883 if (bam_direct == BAM_DIRECT_DBOOT) { 1884 (void) snprintf(path, sizeof (path), "%s%s", root, 1885 DIRECT_BOOT_ARCHIVE_64); 1886 if (stat(path, &sb) != 0) { 1887 bam_error(ARCHIVE_NOT_CREATED, path); 1888 return (BAM_ERROR); 1889 } 1890 } 1891 1892 return (BAM_SUCCESS); 1893 } 1894 1895 /* 1896 * Checks if target filesystem is on a ramdisk 1897 * 1 - is miniroot 1898 * 0 - is not 1899 * When in doubt assume it is not a ramdisk. 1900 */ 1901 static int 1902 is_ramdisk(char *root) 1903 { 1904 struct extmnttab mnt; 1905 FILE *fp; 1906 int found; 1907 char mntpt[PATH_MAX]; 1908 char *cp; 1909 1910 /* 1911 * There are 3 situations where creating archive is 1912 * of dubious value: 1913 * - create boot_archive on a lofi-mounted boot_archive 1914 * - create it on a ramdisk which is the root filesystem 1915 * - create it on a ramdisk mounted somewhere else 1916 * The first is not easy to detect and checking for it is not 1917 * worth it. 1918 * The other two conditions are handled here 1919 */ 1920 1921 fp = fopen(MNTTAB, "r"); 1922 if (fp == NULL) { 1923 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 1924 return (0); 1925 } 1926 1927 resetmnttab(fp); 1928 1929 /* 1930 * Remove any trailing / from the mount point 1931 */ 1932 (void) strlcpy(mntpt, root, sizeof (mntpt)); 1933 if (strcmp(root, "/") != 0) { 1934 cp = mntpt + strlen(mntpt) - 1; 1935 if (*cp == '/') 1936 *cp = '\0'; 1937 } 1938 found = 0; 1939 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 1940 if (strcmp(mnt.mnt_mountp, mntpt) == 0) { 1941 found = 1; 1942 break; 1943 } 1944 } 1945 1946 if (!found) { 1947 if (bam_verbose) 1948 bam_error(NOT_IN_MNTTAB, mntpt); 1949 (void) fclose(fp); 1950 return (0); 1951 } 1952 1953 if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) { 1954 if (bam_verbose) 1955 bam_error(IS_RAMDISK, bam_root); 1956 (void) fclose(fp); 1957 return (1); 1958 } 1959 1960 (void) fclose(fp); 1961 1962 return (0); 1963 } 1964 1965 static int 1966 is_newboot(char *root) 1967 { 1968 char path[PATH_MAX]; 1969 struct stat sb; 1970 1971 /* 1972 * We can't boot without MULTI_BOOT 1973 */ 1974 (void) snprintf(path, sizeof (path), "%s%s", root, MULTI_BOOT); 1975 if (stat(path, &sb) == -1) { 1976 if (bam_verbose) 1977 bam_print(FILE_MISS, path); 1978 return (0); 1979 } 1980 1981 /* 1982 * We can't generate archive without GRUB_DIR 1983 */ 1984 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_DIR); 1985 if (stat(path, &sb) == -1) { 1986 if (bam_verbose) 1987 bam_print(DIR_MISS, path); 1988 return (0); 1989 } 1990 1991 return (1); 1992 } 1993 1994 static int 1995 is_readonly(char *root) 1996 { 1997 struct statvfs vfs; 1998 1999 /* 2000 * Check for RDONLY filesystem 2001 * When in doubt assume it is not readonly 2002 */ 2003 if (statvfs(root, &vfs) != 0) { 2004 if (bam_verbose) 2005 bam_error(STATVFS_FAIL, root, strerror(errno)); 2006 return (0); 2007 } 2008 2009 if (vfs.f_flag & ST_RDONLY) { 2010 return (1); 2011 } 2012 2013 return (0); 2014 } 2015 2016 static error_t 2017 update_archive(char *root, char *opt) 2018 { 2019 error_t ret; 2020 2021 assert(root); 2022 assert(opt == NULL); 2023 2024 /* 2025 * root must belong to a GRUB boot OS, 2026 * don't care on sparc except for diskless clients 2027 */ 2028 if (!is_newboot(root)) { 2029 /* 2030 * Emit message only if not in context of update_all. 2031 * If in update_all, emit only if verbose flag is set. 2032 */ 2033 if (!bam_update_all || bam_verbose) 2034 bam_print(NOT_GRUB_BOOT, root); 2035 return (BAM_SUCCESS); 2036 } 2037 2038 /* 2039 * If smf check is requested when / is writable (can happen 2040 * on first reboot following an upgrade because service 2041 * dependency is messed up), skip the check. 2042 */ 2043 if (bam_smf_check && !bam_root_readonly) 2044 return (BAM_SUCCESS); 2045 2046 /* 2047 * root must be writable. This check applies to alternate 2048 * root (-R option); bam_root_readonly applies to '/' only. 2049 * Note: statvfs() does not always report the truth 2050 */ 2051 if (!bam_smf_check && !bam_check && is_readonly(root)) { 2052 if (bam_verbose) 2053 bam_print(RDONLY_FS, root); 2054 return (BAM_SUCCESS); 2055 } 2056 2057 /* 2058 * Don't generate archive on ramdisk 2059 */ 2060 if (is_ramdisk(root)) { 2061 if (bam_verbose) 2062 bam_print(SKIP_RAMDISK); 2063 return (BAM_SUCCESS); 2064 } 2065 2066 /* 2067 * Now check if updated is really needed 2068 */ 2069 ret = update_required(root); 2070 2071 /* 2072 * The check command (-n) is *not* a dry run 2073 * It only checks if the archive is in sync. 2074 */ 2075 if (bam_check) { 2076 bam_exit((ret != 0) ? 1 : 0); 2077 } 2078 2079 if (ret == 1) { 2080 /* create the ramdisk */ 2081 ret = create_ramdisk(root); 2082 } 2083 return (ret); 2084 } 2085 2086 static void 2087 update_fdisk(void) 2088 { 2089 struct stat sb; 2090 char cmd[PATH_MAX]; 2091 int ret1, ret2; 2092 2093 assert(stat(GRUB_fdisk, &sb) == 0); 2094 assert(stat(GRUB_fdisk_target, &sb) == 0); 2095 2096 (void) snprintf(cmd, sizeof (cmd), "/sbin/fdisk -F %s `/bin/cat %s`", 2097 GRUB_fdisk, GRUB_fdisk_target); 2098 2099 bam_print(UPDATING_FDISK); 2100 if (exec_cmd(cmd, NULL, 0) != 0) { 2101 bam_error(FDISK_UPDATE_FAILED); 2102 } 2103 2104 /* 2105 * We are done, remove the files. 2106 */ 2107 ret1 = unlink(GRUB_fdisk); 2108 ret2 = unlink(GRUB_fdisk_target); 2109 if (ret1 != 0 || ret2 != 0) { 2110 bam_error(FILE_REMOVE_FAILED, GRUB_fdisk, GRUB_fdisk_target); 2111 } 2112 } 2113 2114 static void 2115 restore_grub_slice(void) 2116 { 2117 struct stat sb; 2118 char *mntpt, *physlice; 2119 int mnted; /* set if we did a mount */ 2120 char menupath[PATH_MAX], cmd[PATH_MAX]; 2121 2122 if (stat(GRUB_slice, &sb) != 0) { 2123 bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno)); 2124 return; 2125 } 2126 2127 /* 2128 * If we are doing an luactivate, don't attempt to restore GRUB or else 2129 * we may not be able to get to DCA boot environments. Let luactivate 2130 * handle GRUB/DCA installation 2131 */ 2132 if (stat(LU_ACTIVATE_FILE, &sb) == 0) { 2133 return; 2134 } 2135 2136 mnted = 0; 2137 physlice = NULL; 2138 mntpt = mount_grub_slice(&mnted, &physlice, NULL, NULL); 2139 if (mntpt == NULL) { 2140 bam_error(CANNOT_RESTORE_GRUB_SLICE); 2141 return; 2142 } 2143 2144 (void) snprintf(menupath, sizeof (menupath), "%s%s", mntpt, GRUB_MENU); 2145 if (stat(menupath, &sb) == 0) { 2146 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2147 return; 2148 } 2149 2150 /* 2151 * The menu is missing - we need to do a restore 2152 */ 2153 bam_print(RESTORING_GRUB); 2154 2155 (void) snprintf(cmd, sizeof (cmd), "%s %s %s %s", 2156 INSTALLGRUB, STAGE1, STAGE2, physlice); 2157 2158 if (exec_cmd(cmd, NULL, 0) != 0) { 2159 bam_error(RESTORE_GRUB_FAILED); 2160 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2161 return; 2162 } 2163 2164 if (stat(GRUB_backup_menu, &sb) != 0) { 2165 bam_error(MISSING_BACKUP_MENU, 2166 GRUB_backup_menu, strerror(errno)); 2167 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2168 return; 2169 } 2170 2171 (void) snprintf(cmd, sizeof (cmd), "/bin/cp %s %s", 2172 GRUB_backup_menu, menupath); 2173 2174 if (exec_cmd(cmd, NULL, 0) != 0) { 2175 bam_error(RESTORE_MENU_FAILED, menupath); 2176 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2177 return; 2178 } 2179 2180 /* Success */ 2181 umount_grub_slice(mnted, mntpt, physlice, NULL, NULL); 2182 } 2183 2184 static error_t 2185 update_all(char *root, char *opt) 2186 { 2187 struct extmnttab mnt; 2188 struct stat sb; 2189 FILE *fp; 2190 char multibt[PATH_MAX]; 2191 error_t ret = BAM_SUCCESS; 2192 int ret1, ret2; 2193 2194 assert(root); 2195 assert(opt == NULL); 2196 2197 if (bam_rootlen != 1 || *root != '/') { 2198 elide_trailing_slash(root, multibt, sizeof (multibt)); 2199 bam_error(ALT_ROOT_INVALID, multibt); 2200 return (BAM_ERROR); 2201 } 2202 2203 /* 2204 * First update archive for current root 2205 */ 2206 if (update_archive(root, opt) != BAM_SUCCESS) 2207 ret = BAM_ERROR; 2208 2209 /* 2210 * Now walk the mount table, performing archive update 2211 * for all mounted Newboot root filesystems 2212 */ 2213 fp = fopen(MNTTAB, "r"); 2214 if (fp == NULL) { 2215 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 2216 ret = BAM_ERROR; 2217 goto out; 2218 } 2219 2220 resetmnttab(fp); 2221 2222 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 2223 if (mnt.mnt_special == NULL) 2224 continue; 2225 if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0) 2226 continue; 2227 if (strcmp(mnt.mnt_mountp, "/") == 0) 2228 continue; 2229 2230 (void) snprintf(multibt, sizeof (multibt), "%s%s", 2231 mnt.mnt_mountp, MULTI_BOOT); 2232 2233 if (stat(multibt, &sb) == -1) 2234 continue; 2235 2236 /* 2237 * We put a trailing slash to be consistent with root = "/" 2238 * case, such that we don't have to print // in some cases. 2239 */ 2240 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 2241 mnt.mnt_mountp); 2242 bam_rootlen = strlen(rootbuf); 2243 2244 /* 2245 * It's possible that other mounts may be an alternate boot 2246 * architecture, so check it again. 2247 */ 2248 if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) || 2249 (update_archive(rootbuf, opt) != BAM_SUCCESS)) 2250 ret = BAM_ERROR; 2251 } 2252 2253 (void) fclose(fp); 2254 2255 out: 2256 if (stat(GRUB_slice, &sb) == 0) { 2257 restore_grub_slice(); 2258 } 2259 2260 /* 2261 * Update fdisk table as we go down. Updating it when 2262 * the system is running will confuse biosdev. 2263 */ 2264 ret1 = stat(GRUB_fdisk, &sb); 2265 ret2 = stat(GRUB_fdisk_target, &sb); 2266 if ((ret1 == 0) && (ret2 == 0)) { 2267 update_fdisk(); 2268 } else if ((ret1 == 0) ^ (ret2 == 0)) { 2269 /* 2270 * It is an error for one file to be 2271 * present and the other absent. 2272 * It is normal for both files to be 2273 * absent - it indicates that no fdisk 2274 * update is required. 2275 */ 2276 bam_error(MISSING_FDISK_FILE, 2277 ret1 ? GRUB_fdisk : GRUB_fdisk_target); 2278 ret = BAM_ERROR; 2279 } 2280 2281 return (ret); 2282 } 2283 2284 static void 2285 append_line(menu_t *mp, line_t *lp) 2286 { 2287 if (mp->start == NULL) { 2288 mp->start = lp; 2289 } else { 2290 mp->end->next = lp; 2291 lp->prev = mp->end; 2292 } 2293 mp->end = lp; 2294 } 2295 2296 static void 2297 unlink_line(menu_t *mp, line_t *lp) 2298 { 2299 /* unlink from list */ 2300 if (lp->prev) 2301 lp->prev->next = lp->next; 2302 else 2303 mp->start = lp->next; 2304 if (lp->next) 2305 lp->next->prev = lp->prev; 2306 else 2307 mp->end = lp->prev; 2308 } 2309 2310 static entry_t * 2311 boot_entry_new(menu_t *mp, line_t *start, line_t *end) 2312 { 2313 entry_t *ent, *prev; 2314 2315 ent = s_calloc(1, sizeof (entry_t)); 2316 ent->start = start; 2317 ent->end = end; 2318 2319 if (mp->entries == NULL) { 2320 mp->entries = ent; 2321 return (ent); 2322 } 2323 2324 prev = mp->entries; 2325 while (prev->next) 2326 prev = prev-> next; 2327 prev->next = ent; 2328 ent->prev = prev; 2329 return (ent); 2330 } 2331 2332 static void 2333 boot_entry_addline(entry_t *ent, line_t *lp) 2334 { 2335 if (ent) 2336 ent->end = lp; 2337 } 2338 2339 /* 2340 * A line in menu.lst looks like 2341 * [ ]*<cmd>[ \t=]*<arg>* 2342 */ 2343 static void 2344 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum) 2345 { 2346 /* 2347 * save state across calls. This is so that 2348 * header gets the right entry# after title has 2349 * been processed 2350 */ 2351 static line_t *prev = NULL; 2352 static entry_t *curr_ent = NULL; 2353 static int in_liveupgrade = 0; 2354 2355 line_t *lp; 2356 char *cmd, *sep, *arg; 2357 char save, *cp, *line; 2358 menu_flag_t flag = BAM_INVALID; 2359 2360 if (str == NULL) { 2361 return; 2362 } 2363 2364 /* 2365 * First save a copy of the entire line. 2366 * We use this later to set the line field. 2367 */ 2368 line = s_strdup(str); 2369 2370 /* Eat up leading whitespace */ 2371 while (*str == ' ' || *str == '\t') 2372 str++; 2373 2374 if (*str == '#') { /* comment */ 2375 cmd = s_strdup("#"); 2376 sep = NULL; 2377 arg = s_strdup(str + 1); 2378 flag = BAM_COMMENT; 2379 if (strstr(arg, BAM_LU_HDR) != NULL) { 2380 in_liveupgrade = 1; 2381 } else if (strstr(arg, BAM_LU_FTR) != NULL) { 2382 in_liveupgrade = 0; 2383 } 2384 } else if (*str == '\0') { /* blank line */ 2385 cmd = sep = arg = NULL; 2386 flag = BAM_EMPTY; 2387 } else { 2388 /* 2389 * '=' is not a documented separator in grub syntax. 2390 * However various development bits use '=' as a 2391 * separator. In addition, external users also 2392 * use = as a separator. So we will allow that usage. 2393 */ 2394 cp = str; 2395 while (*str != ' ' && *str != '\t' && *str != '=') { 2396 if (*str == '\0') { 2397 cmd = s_strdup(cp); 2398 sep = arg = NULL; 2399 break; 2400 } 2401 str++; 2402 } 2403 2404 if (*str != '\0') { 2405 save = *str; 2406 *str = '\0'; 2407 cmd = s_strdup(cp); 2408 *str = save; 2409 2410 str++; 2411 save = *str; 2412 *str = '\0'; 2413 sep = s_strdup(str - 1); 2414 *str = save; 2415 2416 while (*str == ' ' || *str == '\t') 2417 str++; 2418 if (*str == '\0') 2419 arg = NULL; 2420 else 2421 arg = s_strdup(str); 2422 } 2423 } 2424 2425 lp = s_calloc(1, sizeof (line_t)); 2426 2427 lp->cmd = cmd; 2428 lp->sep = sep; 2429 lp->arg = arg; 2430 lp->line = line; 2431 lp->lineNum = ++(*lineNum); 2432 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) { 2433 lp->entryNum = ++(*entryNum); 2434 lp->flags = BAM_TITLE; 2435 if (prev && prev->flags == BAM_COMMENT && 2436 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2437 prev->entryNum = lp->entryNum; 2438 curr_ent = boot_entry_new(mp, prev, lp); 2439 curr_ent->flags = BAM_ENTRY_BOOTADM; 2440 } else { 2441 curr_ent = boot_entry_new(mp, lp, lp); 2442 if (in_liveupgrade) { 2443 curr_ent->flags = BAM_ENTRY_LU; 2444 } 2445 } 2446 curr_ent->entryNum = *entryNum; 2447 } else if (flag != BAM_INVALID) { 2448 /* 2449 * For header comments, the entry# is "fixed up" 2450 * by the subsequent title 2451 */ 2452 lp->entryNum = *entryNum; 2453 lp->flags = flag; 2454 } else { 2455 lp->entryNum = *entryNum; 2456 2457 if (*entryNum == ENTRY_INIT) { 2458 lp->flags = BAM_GLOBAL; 2459 } else { 2460 lp->flags = BAM_ENTRY; 2461 2462 if (cmd && arg) { 2463 /* 2464 * We only compare for the length of "module" 2465 * so that "module$" will also match. 2466 */ 2467 if ((strncmp(cmd, menu_cmds[MODULE_CMD], 2468 strlen(menu_cmds[MODULE_CMD])) == 0) && 2469 (strcmp(arg, MINIROOT) == 0)) 2470 curr_ent->flags |= BAM_ENTRY_MINIROOT; 2471 else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) 2472 curr_ent->flags |= BAM_ENTRY_ROOT; 2473 else if (strcmp(cmd, 2474 menu_cmds[CHAINLOADER_CMD]) == 0) 2475 curr_ent->flags |= 2476 BAM_ENTRY_CHAINLOADER; 2477 } 2478 } 2479 } 2480 2481 /* record default, old default, and entry line ranges */ 2482 if (lp->flags == BAM_GLOBAL && 2483 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) { 2484 mp->curdefault = lp; 2485 } else if (lp->flags == BAM_COMMENT && 2486 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) { 2487 mp->olddefault = lp; 2488 } else if (lp->flags == BAM_COMMENT && 2489 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) { 2490 mp->old_rc_default = lp; 2491 } else if (lp->flags == BAM_ENTRY || 2492 (lp->flags == BAM_COMMENT && 2493 strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) { 2494 boot_entry_addline(curr_ent, lp); 2495 } 2496 append_line(mp, lp); 2497 2498 prev = lp; 2499 } 2500 2501 static void 2502 update_numbering(menu_t *mp) 2503 { 2504 int lineNum; 2505 int entryNum; 2506 int old_default_value; 2507 line_t *lp, *prev, *default_lp, *default_entry; 2508 char buf[PATH_MAX]; 2509 2510 if (mp->start == NULL) { 2511 return; 2512 } 2513 2514 lineNum = LINE_INIT; 2515 entryNum = ENTRY_INIT; 2516 old_default_value = ENTRY_INIT; 2517 lp = default_lp = default_entry = NULL; 2518 2519 prev = NULL; 2520 for (lp = mp->start; lp; prev = lp, lp = lp->next) { 2521 lp->lineNum = ++lineNum; 2522 2523 /* 2524 * Get the value of the default command 2525 */ 2526 if (lp->entryNum == ENTRY_INIT && lp->cmd && 2527 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 && 2528 lp->arg) { 2529 old_default_value = atoi(lp->arg); 2530 default_lp = lp; 2531 } 2532 2533 /* 2534 * If not boot entry, nothing else to fix for this 2535 * entry 2536 */ 2537 if (lp->entryNum == ENTRY_INIT) 2538 continue; 2539 2540 /* 2541 * Record the position of the default entry. 2542 * The following works because global 2543 * commands like default and timeout should precede 2544 * actual boot entries, so old_default_value 2545 * is already known (or default cmd is missing). 2546 */ 2547 if (default_entry == NULL && 2548 old_default_value != ENTRY_INIT && 2549 lp->entryNum == old_default_value) { 2550 default_entry = lp; 2551 } 2552 2553 /* 2554 * Now fixup the entry number 2555 */ 2556 if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) { 2557 lp->entryNum = ++entryNum; 2558 /* fixup the bootadm header */ 2559 if (prev && prev->flags == BAM_COMMENT && 2560 prev->arg && 2561 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2562 prev->entryNum = lp->entryNum; 2563 } 2564 } else { 2565 lp->entryNum = entryNum; 2566 } 2567 } 2568 2569 /* 2570 * No default command in menu, simply return 2571 */ 2572 if (default_lp == NULL) { 2573 return; 2574 } 2575 2576 free(default_lp->arg); 2577 free(default_lp->line); 2578 2579 if (default_entry == NULL) { 2580 default_lp->arg = s_strdup("0"); 2581 } else { 2582 (void) snprintf(buf, sizeof (buf), "%d", 2583 default_entry->entryNum); 2584 default_lp->arg = s_strdup(buf); 2585 } 2586 2587 /* 2588 * The following is required since only the line field gets 2589 * written back to menu.lst 2590 */ 2591 (void) snprintf(buf, sizeof (buf), "%s%s%s", 2592 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg); 2593 default_lp->line = s_strdup(buf); 2594 } 2595 2596 2597 static menu_t * 2598 menu_read(char *menu_path) 2599 { 2600 FILE *fp; 2601 char buf[BAM_MAXLINE], *cp; 2602 menu_t *mp; 2603 int line, entry, len, n; 2604 2605 mp = s_calloc(1, sizeof (menu_t)); 2606 2607 fp = fopen(menu_path, "r"); 2608 if (fp == NULL) { /* Let the caller handle this error */ 2609 return (mp); 2610 } 2611 2612 2613 /* Note: GRUB boot entry number starts with 0 */ 2614 line = LINE_INIT; 2615 entry = ENTRY_INIT; 2616 cp = buf; 2617 len = sizeof (buf); 2618 while (s_fgets(cp, len, fp) != NULL) { 2619 n = strlen(cp); 2620 if (cp[n - 1] == '\\') { 2621 len -= n - 1; 2622 assert(len >= 2); 2623 cp += n - 1; 2624 continue; 2625 } 2626 line_parser(mp, buf, &line, &entry); 2627 cp = buf; 2628 len = sizeof (buf); 2629 } 2630 2631 if (fclose(fp) == EOF) { 2632 bam_error(CLOSE_FAIL, menu_path, strerror(errno)); 2633 } 2634 2635 return (mp); 2636 } 2637 2638 static error_t 2639 selector(menu_t *mp, char *opt, int *entry, char **title) 2640 { 2641 char *eq; 2642 char *opt_dup; 2643 int entryNum; 2644 2645 assert(mp); 2646 assert(mp->start); 2647 assert(opt); 2648 2649 opt_dup = s_strdup(opt); 2650 2651 if (entry) 2652 *entry = ENTRY_INIT; 2653 if (title) 2654 *title = NULL; 2655 2656 eq = strchr(opt_dup, '='); 2657 if (eq == NULL) { 2658 bam_error(INVALID_OPT, opt); 2659 free(opt_dup); 2660 return (BAM_ERROR); 2661 } 2662 2663 *eq = '\0'; 2664 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) { 2665 assert(mp->end); 2666 entryNum = s_strtol(eq + 1); 2667 if (entryNum < 0 || entryNum > mp->end->entryNum) { 2668 bam_error(INVALID_ENTRY, eq + 1); 2669 free(opt_dup); 2670 return (BAM_ERROR); 2671 } 2672 *entry = entryNum; 2673 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) { 2674 *title = opt + (eq - opt_dup) + 1; 2675 } else { 2676 bam_error(INVALID_OPT, opt); 2677 free(opt_dup); 2678 return (BAM_ERROR); 2679 } 2680 2681 free(opt_dup); 2682 return (BAM_SUCCESS); 2683 } 2684 2685 /* 2686 * If invoked with no titles/entries (opt == NULL) 2687 * only title lines in file are printed. 2688 * 2689 * If invoked with a title or entry #, all 2690 * lines in *every* matching entry are listed 2691 */ 2692 static error_t 2693 list_entry(menu_t *mp, char *menu_path, char *opt) 2694 { 2695 line_t *lp; 2696 int entry = ENTRY_INIT; 2697 int found; 2698 char *title = NULL; 2699 2700 assert(mp); 2701 assert(menu_path); 2702 2703 if (mp->start == NULL) { 2704 bam_error(NO_MENU, menu_path); 2705 return (BAM_ERROR); 2706 } 2707 2708 if (opt != NULL) { 2709 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) { 2710 return (BAM_ERROR); 2711 } 2712 assert((entry != ENTRY_INIT) ^ (title != NULL)); 2713 } else { 2714 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0); 2715 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0); 2716 } 2717 2718 found = 0; 2719 for (lp = mp->start; lp; lp = lp->next) { 2720 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY) 2721 continue; 2722 if (opt == NULL && lp->flags == BAM_TITLE) { 2723 bam_print(PRINT_TITLE, lp->entryNum, 2724 lp->arg); 2725 found = 1; 2726 continue; 2727 } 2728 if (entry != ENTRY_INIT && lp->entryNum == entry) { 2729 bam_print(PRINT, lp->line); 2730 found = 1; 2731 continue; 2732 } 2733 2734 /* 2735 * We set the entry value here so that all lines 2736 * in entry get printed. If we subsequently match 2737 * title in other entries, all lines in those 2738 * entries get printed as well. 2739 */ 2740 if (title && lp->flags == BAM_TITLE && lp->arg && 2741 strncmp(title, lp->arg, strlen(title)) == 0) { 2742 bam_print(PRINT, lp->line); 2743 entry = lp->entryNum; 2744 found = 1; 2745 continue; 2746 } 2747 } 2748 2749 if (!found) { 2750 bam_error(NO_MATCH_ENTRY); 2751 return (BAM_ERROR); 2752 } 2753 2754 return (BAM_SUCCESS); 2755 } 2756 2757 static int 2758 add_boot_entry(menu_t *mp, 2759 char *title, 2760 char *root, 2761 char *kernel, 2762 char *module) 2763 { 2764 int lineNum, entryNum; 2765 char linebuf[BAM_MAXLINE]; 2766 menu_cmd_t k_cmd, m_cmd; 2767 2768 assert(mp); 2769 2770 if (title == NULL) { 2771 title = "Solaris"; /* default to Solaris */ 2772 } 2773 if (kernel == NULL) { 2774 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]); 2775 return (BAM_ERROR); 2776 } 2777 if (module == NULL) { 2778 if (bam_direct != BAM_DIRECT_DBOOT) { 2779 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]); 2780 return (BAM_ERROR); 2781 } 2782 2783 /* Figure the commands out from the kernel line */ 2784 if (strstr(kernel, "$ISADIR") != NULL) { 2785 module = DIRECT_BOOT_ARCHIVE; 2786 k_cmd = KERNEL_DOLLAR_CMD; 2787 m_cmd = MODULE_DOLLAR_CMD; 2788 } else if (strstr(kernel, "amd64") != NULL) { 2789 module = DIRECT_BOOT_ARCHIVE_64; 2790 k_cmd = KERNEL_CMD; 2791 m_cmd = MODULE_CMD; 2792 } else { 2793 module = DIRECT_BOOT_ARCHIVE_32; 2794 k_cmd = KERNEL_CMD; 2795 m_cmd = MODULE_CMD; 2796 } 2797 } else if ((bam_direct == BAM_DIRECT_DBOOT) && 2798 (strstr(kernel, "$ISADIR") != NULL)) { 2799 /* 2800 * If it's a non-failsafe dboot kernel, use the "kernel$" 2801 * command. Otherwise, use "kernel". 2802 */ 2803 k_cmd = KERNEL_DOLLAR_CMD; 2804 m_cmd = MODULE_DOLLAR_CMD; 2805 } else { 2806 k_cmd = KERNEL_CMD; 2807 m_cmd = MODULE_CMD; 2808 } 2809 2810 if (mp->start) { 2811 lineNum = mp->end->lineNum; 2812 entryNum = mp->end->entryNum; 2813 } else { 2814 lineNum = LINE_INIT; 2815 entryNum = ENTRY_INIT; 2816 } 2817 2818 /* 2819 * No separator for comment (HDR/FTR) commands 2820 * The syntax for comments is #<comment> 2821 */ 2822 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2823 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR); 2824 line_parser(mp, linebuf, &lineNum, &entryNum); 2825 2826 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2827 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 2828 line_parser(mp, linebuf, &lineNum, &entryNum); 2829 2830 if (root) { 2831 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2832 menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root); 2833 line_parser(mp, linebuf, &lineNum, &entryNum); 2834 } 2835 2836 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2837 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel); 2838 line_parser(mp, linebuf, &lineNum, &entryNum); 2839 2840 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2841 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module); 2842 line_parser(mp, linebuf, &lineNum, &entryNum); 2843 2844 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2845 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR); 2846 line_parser(mp, linebuf, &lineNum, &entryNum); 2847 2848 return (entryNum); 2849 } 2850 2851 static error_t 2852 do_delete(menu_t *mp, int entryNum) 2853 { 2854 line_t *lp, *freed; 2855 entry_t *ent, *tmp; 2856 int deleted; 2857 2858 assert(entryNum != ENTRY_INIT); 2859 2860 ent = mp->entries; 2861 while (ent) { 2862 lp = ent->start; 2863 /* check entry number and make sure it's a bootadm entry */ 2864 if (lp->flags != BAM_COMMENT || 2865 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 || 2866 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) { 2867 ent = ent->next; 2868 continue; 2869 } 2870 2871 /* free the entry content */ 2872 do { 2873 freed = lp; 2874 lp = lp->next; /* prev stays the same */ 2875 unlink_line(mp, freed); 2876 line_free(freed); 2877 } while (freed != ent->end); 2878 2879 /* free the entry_t structure */ 2880 tmp = ent; 2881 ent = ent->next; 2882 if (tmp->prev) 2883 tmp->prev->next = ent; 2884 else 2885 mp->entries = ent; 2886 if (ent) 2887 ent->prev = tmp->prev; 2888 deleted = 1; 2889 } 2890 2891 if (!deleted && entryNum != ALL_ENTRIES) { 2892 bam_error(NO_BOOTADM_MATCH); 2893 return (BAM_ERROR); 2894 } 2895 2896 /* 2897 * Now that we have deleted an entry, update 2898 * the entry numbering and the default cmd. 2899 */ 2900 update_numbering(mp); 2901 2902 return (BAM_SUCCESS); 2903 } 2904 2905 static error_t 2906 delete_all_entries(menu_t *mp, char *menu_path, char *opt) 2907 { 2908 assert(mp); 2909 assert(opt == NULL); 2910 2911 if (mp->start == NULL) { 2912 bam_print(EMPTY_FILE, menu_path); 2913 return (BAM_SUCCESS); 2914 } 2915 2916 if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) { 2917 return (BAM_ERROR); 2918 } 2919 2920 return (BAM_WRITE); 2921 } 2922 2923 static FILE * 2924 open_diskmap(char *root) 2925 { 2926 FILE *fp; 2927 char cmd[PATH_MAX]; 2928 2929 /* make sure we have a map file */ 2930 fp = fopen(GRUBDISK_MAP, "r"); 2931 if (fp == NULL) { 2932 (void) snprintf(cmd, sizeof (cmd), 2933 "%s%s > /dev/null", root, CREATE_DISKMAP); 2934 (void) system(cmd); 2935 fp = fopen(GRUBDISK_MAP, "r"); 2936 } 2937 return (fp); 2938 } 2939 2940 #define SECTOR_SIZE 512 2941 2942 static int 2943 get_partition(char *device) 2944 { 2945 int i, fd, is_pcfs, partno = -1; 2946 struct mboot *mboot; 2947 char boot_sect[SECTOR_SIZE]; 2948 char *wholedisk, *slice; 2949 2950 /* form whole disk (p0) */ 2951 slice = device + strlen(device) - 2; 2952 is_pcfs = (*slice != 's'); 2953 if (!is_pcfs) 2954 *slice = '\0'; 2955 wholedisk = s_calloc(1, strlen(device) + 3); 2956 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device); 2957 if (!is_pcfs) 2958 *slice = 's'; 2959 2960 /* read boot sector */ 2961 fd = open(wholedisk, O_RDONLY); 2962 free(wholedisk); 2963 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 2964 return (partno); 2965 } 2966 (void) close(fd); 2967 2968 /* parse fdisk table */ 2969 mboot = (struct mboot *)((void *)boot_sect); 2970 for (i = 0; i < FD_NUMPART; i++) { 2971 struct ipart *part = 2972 (struct ipart *)(uintptr_t)mboot->parts + i; 2973 if (is_pcfs) { /* looking for solaris boot part */ 2974 if (part->systid == 0xbe) { 2975 partno = i; 2976 break; 2977 } 2978 } else { /* look for solaris partition, old and new */ 2979 if (part->systid == SUNIXOS || 2980 part->systid == SUNIXOS2) { 2981 partno = i; 2982 break; 2983 } 2984 } 2985 } 2986 return (partno); 2987 } 2988 2989 static char * 2990 get_grubdisk(char *rootdev, FILE *fp, int on_bootdev) 2991 { 2992 char *grubdisk; /* (hd#,#,#) */ 2993 char *slice; 2994 char *grubhd; 2995 int fdiskpart; 2996 int found = 0; 2997 char *devname, *ctdname = strstr(rootdev, "dsk/"); 2998 char linebuf[PATH_MAX]; 2999 3000 if (ctdname == NULL) 3001 return (NULL); 3002 3003 ctdname += strlen("dsk/"); 3004 slice = strrchr(ctdname, 's'); 3005 if (slice) 3006 *slice = '\0'; 3007 3008 rewind(fp); 3009 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) { 3010 grubhd = strtok(linebuf, " \t\n"); 3011 if (grubhd) 3012 devname = strtok(NULL, " \t\n"); 3013 else 3014 devname = NULL; 3015 if (devname && strcmp(devname, ctdname) == 0) { 3016 found = 1; 3017 break; 3018 } 3019 } 3020 3021 if (slice) 3022 *slice = 's'; 3023 3024 if (found == 0) { 3025 if (bam_verbose) 3026 bam_print(DISKMAP_FAIL_NONFATAL, rootdev); 3027 grubhd = "0"; /* assume disk 0 if can't match */ 3028 } 3029 3030 fdiskpart = get_partition(rootdev); 3031 if (fdiskpart == -1) 3032 return (NULL); 3033 3034 grubdisk = s_calloc(1, 10); 3035 if (slice) { 3036 (void) snprintf(grubdisk, 10, "(hd%s,%d,%c)", 3037 grubhd, fdiskpart, slice[1] + 'a' - '0'); 3038 } else 3039 (void) snprintf(grubdisk, 10, "(hd%s,%d)", 3040 grubhd, fdiskpart); 3041 3042 /* if root not on bootdev, change GRUB disk to 0 */ 3043 if (!on_bootdev) 3044 grubdisk[3] = '0'; 3045 return (grubdisk); 3046 } 3047 3048 static char * 3049 get_title(char *rootdir) 3050 { 3051 static char title[80]; /* from /etc/release */ 3052 char *cp = NULL, release[PATH_MAX]; 3053 FILE *fp; 3054 3055 /* open the /etc/release file */ 3056 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir); 3057 3058 fp = fopen(release, "r"); 3059 if (fp == NULL) 3060 return (NULL); 3061 3062 while (s_fgets(title, sizeof (title), fp) != NULL) { 3063 cp = strstr(title, "Solaris"); 3064 if (cp) 3065 break; 3066 } 3067 (void) fclose(fp); 3068 return (cp == NULL ? "Solaris" : cp); 3069 } 3070 3071 char * 3072 get_special(char *mountp) 3073 { 3074 FILE *mntfp; 3075 struct mnttab mp = {0}, mpref = {0}; 3076 3077 mntfp = fopen(MNTTAB, "r"); 3078 if (mntfp == NULL) { 3079 return (0); 3080 } 3081 3082 if (*mountp == '\0') 3083 mpref.mnt_mountp = "/"; 3084 else 3085 mpref.mnt_mountp = mountp; 3086 if (getmntany(mntfp, &mp, &mpref) != 0) { 3087 (void) fclose(mntfp); 3088 return (NULL); 3089 } 3090 (void) fclose(mntfp); 3091 3092 return (s_strdup(mp.mnt_special)); 3093 } 3094 3095 char * 3096 os_to_grubdisk(char *osdisk, int on_bootdev) 3097 { 3098 FILE *fp; 3099 char *grubdisk; 3100 3101 /* translate /dev/dsk name to grub disk name */ 3102 fp = open_diskmap(""); 3103 if (fp == NULL) { 3104 bam_error(DISKMAP_FAIL, osdisk); 3105 return (NULL); 3106 } 3107 grubdisk = get_grubdisk(osdisk, fp, on_bootdev); 3108 (void) fclose(fp); 3109 return (grubdisk); 3110 } 3111 3112 /* 3113 * Check if root is on the boot device 3114 * Return 0 (false) on error 3115 */ 3116 static int 3117 menu_on_bootdev(char *menu_root, FILE *fp) 3118 { 3119 int ret; 3120 char *grubhd, *bootp, *special; 3121 3122 special = get_special(menu_root); 3123 if (special == NULL) 3124 return (0); 3125 bootp = strstr(special, "p0:boot"); 3126 if (bootp) 3127 *bootp = '\0'; 3128 grubhd = get_grubdisk(special, fp, 1); 3129 free(special); 3130 3131 if (grubhd == NULL) 3132 return (0); 3133 ret = grubhd[3] == '0'; 3134 free(grubhd); 3135 return (ret); 3136 } 3137 3138 /* 3139 * look for matching bootadm entry with specified parameters 3140 * Here are the rules (based on existing usage): 3141 * - If title is specified, match on title only 3142 * - Else, match on grubdisk and module (don't care about kernel line). 3143 * note that, if root_opt is non-zero, the absence of root line is 3144 * considered a match. 3145 */ 3146 static entry_t * 3147 find_boot_entry(menu_t *mp, char *title, char *root, char *module, 3148 int root_opt, int *entry_num) 3149 { 3150 int i; 3151 line_t *lp; 3152 entry_t *ent; 3153 3154 /* find matching entry */ 3155 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) { 3156 lp = ent->start; 3157 3158 /* first line of entry must be bootadm comment */ 3159 lp = ent->start; 3160 if (lp->flags != BAM_COMMENT || 3161 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) { 3162 continue; 3163 } 3164 3165 /* advance to title line */ 3166 lp = lp->next; 3167 if (title) { 3168 if (lp->flags == BAM_TITLE && lp->arg && 3169 strcmp(lp->arg, title) == 0) 3170 break; 3171 continue; /* check title only */ 3172 } 3173 3174 lp = lp->next; /* advance to root line */ 3175 if (lp == NULL || strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3176 /* root command found, match grub disk */ 3177 if (strcmp(lp->arg, root) != 0) { 3178 continue; 3179 } 3180 lp = lp->next; /* advance to kernel line */ 3181 } else { 3182 /* no root command, see if root is optional */ 3183 if (root_opt == 0) { 3184 continue; 3185 } 3186 } 3187 3188 if (lp == NULL || lp->next == NULL) { 3189 continue; 3190 } 3191 3192 /* 3193 * Check for matching module entry (failsafe or normal). We 3194 * use a strncmp to match "module" or "module$", since we 3195 * don't know which one it should be. If it fails to match, 3196 * we go around the loop again. 3197 */ 3198 lp = lp->next; /* advance to module line */ 3199 if ((strncmp(lp->cmd, menu_cmds[MODULE_CMD], 3200 strlen(menu_cmds[MODULE_CMD])) != 0) || 3201 (strcmp(lp->arg, module) != 0)) { 3202 continue; 3203 } 3204 break; /* match found */ 3205 } 3206 3207 *entry_num = i; 3208 return (ent); 3209 } 3210 3211 static int 3212 update_boot_entry(menu_t *mp, char *title, char *root, char *kernel, 3213 char *module, int root_opt) 3214 { 3215 int i, change_kernel = 0; 3216 entry_t *ent; 3217 line_t *lp; 3218 char linebuf[BAM_MAXLINE]; 3219 3220 /* note: don't match on title, it's updated on upgrade */ 3221 ent = find_boot_entry(mp, NULL, root, module, root_opt, &i); 3222 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) { 3223 /* 3224 * We may be upgrading a kernel from multiboot to 3225 * directboot. Look for a multiboot entry. 3226 */ 3227 ent = find_boot_entry(mp, NULL, root, MULTI_BOOT_ARCHIVE, 3228 root_opt, &i); 3229 if (ent != NULL) { 3230 change_kernel = 1; 3231 } 3232 } 3233 if (ent == NULL) 3234 return (add_boot_entry(mp, title, root_opt ? NULL : root, 3235 kernel, module)); 3236 3237 /* replace title of exiting entry and delete root line */ 3238 lp = ent->start; 3239 lp = lp->next; /* title line */ 3240 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3241 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 3242 free(lp->arg); 3243 free(lp->line); 3244 lp->arg = s_strdup(title); 3245 lp->line = s_strdup(linebuf); 3246 3247 lp = lp->next; /* root line */ 3248 if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3249 if (root_opt) { /* root line not needed */ 3250 line_t *tmp = lp; 3251 lp = lp->next; 3252 unlink_line(mp, tmp); 3253 line_free(tmp); 3254 } else 3255 lp = lp->next; 3256 } 3257 3258 if (change_kernel) { 3259 /* 3260 * We're upgrading from multiboot to directboot. 3261 */ 3262 if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) { 3263 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3264 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 3265 kernel); 3266 free(lp->arg); 3267 free(lp->line); 3268 lp->arg = s_strdup(kernel); 3269 lp->line = s_strdup(linebuf); 3270 lp = lp->next; 3271 } 3272 if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 3273 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3274 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 3275 module); 3276 free(lp->arg); 3277 free(lp->line); 3278 lp->arg = s_strdup(module); 3279 lp->line = s_strdup(linebuf); 3280 lp = lp->next; 3281 } 3282 } 3283 return (i); 3284 } 3285 3286 /*ARGSUSED*/ 3287 static error_t 3288 update_entry(menu_t *mp, char *menu_root, char *opt) 3289 { 3290 FILE *fp; 3291 int entry; 3292 char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL; 3293 struct stat sbuf; 3294 char failsafe[256]; 3295 3296 assert(mp); 3297 assert(opt); 3298 3299 osdev = strtok(opt, ","); 3300 osroot = strtok(NULL, ","); 3301 if (osroot == NULL) 3302 osroot = menu_root; 3303 title = get_title(osroot); 3304 3305 /* translate /dev/dsk name to grub disk name */ 3306 fp = open_diskmap(osroot); 3307 if (fp == NULL) { 3308 bam_error(DISKMAP_FAIL, osdev); 3309 return (BAM_ERROR); 3310 } 3311 grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp)); 3312 (void) fclose(fp); 3313 if (grubdisk == NULL) { 3314 bam_error(DISKMAP_FAIL, osdev); 3315 return (BAM_ERROR); 3316 } 3317 3318 /* add the entry for normal Solaris */ 3319 if (bam_direct == BAM_DIRECT_DBOOT) { 3320 entry = update_boot_entry(mp, title, grubdisk, 3321 DIRECT_BOOT_KERNEL, DIRECT_BOOT_ARCHIVE, 3322 osroot == menu_root); 3323 } else { 3324 entry = update_boot_entry(mp, title, grubdisk, 3325 MULTI_BOOT, MULTI_BOOT_ARCHIVE, 3326 osroot == menu_root); 3327 } 3328 3329 /* add the entry for failsafe archive */ 3330 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT); 3331 if (stat(failsafe, &sbuf) == 0) { 3332 3333 /* Figure out where the kernel line should point */ 3334 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, 3335 DIRECT_BOOT_FAILSAFE_KERNEL); 3336 if (stat(failsafe, &sbuf) == 0) { 3337 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE; 3338 } else { 3339 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 3340 osroot, MULTI_BOOT_FAILSAFE); 3341 if (stat(failsafe, &sbuf) == 0) { 3342 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE; 3343 } 3344 } 3345 if (failsafe_kernel != NULL) { 3346 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk, 3347 failsafe_kernel, MINIROOT, osroot == menu_root); 3348 } else { 3349 bam_error(NO_FAILSAFE_KERNEL); 3350 } 3351 } 3352 free(grubdisk); 3353 3354 if (entry == BAM_ERROR) { 3355 return (BAM_ERROR); 3356 } 3357 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3358 return (BAM_WRITE); 3359 } 3360 3361 static char * 3362 read_grub_root(void) 3363 { 3364 FILE *fp; 3365 struct stat sb; 3366 char buf[BAM_MAXLINE]; 3367 char *rootstr; 3368 3369 if (stat(GRUB_slice, &sb) != 0) { 3370 bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno)); 3371 return (NULL); 3372 } 3373 3374 if (stat(GRUB_root, &sb) != 0) { 3375 bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno)); 3376 return (NULL); 3377 } 3378 3379 fp = fopen(GRUB_root, "r"); 3380 if (fp == NULL) { 3381 bam_error(OPEN_FAIL, GRUB_root, strerror(errno)); 3382 return (NULL); 3383 } 3384 3385 if (s_fgets(buf, sizeof (buf), fp) == NULL) { 3386 bam_error(EMPTY_FILE, GRUB_root, strerror(errno)); 3387 (void) fclose(fp); 3388 return (NULL); 3389 } 3390 3391 /* 3392 * Copy buf here as check below may trash the buffer 3393 */ 3394 rootstr = s_strdup(buf); 3395 3396 if (s_fgets(buf, sizeof (buf), fp) != NULL) { 3397 bam_error(BAD_ROOT_FILE, GRUB_root); 3398 free(rootstr); 3399 rootstr = NULL; 3400 } 3401 3402 (void) fclose(fp); 3403 3404 return (rootstr); 3405 } 3406 3407 static void 3408 save_default_entry(menu_t *mp, const char *which) 3409 { 3410 int lineNum, entryNum; 3411 int entry = 0; /* default is 0 */ 3412 char linebuf[BAM_MAXLINE]; 3413 line_t *lp = mp->curdefault; 3414 3415 if (mp->start) { 3416 lineNum = mp->end->lineNum; 3417 entryNum = mp->end->entryNum; 3418 } else { 3419 lineNum = LINE_INIT; 3420 entryNum = ENTRY_INIT; 3421 } 3422 3423 if (lp) 3424 entry = s_strtol(lp->arg); 3425 3426 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry); 3427 line_parser(mp, linebuf, &lineNum, &entryNum); 3428 } 3429 3430 static void 3431 restore_default_entry(menu_t *mp, const char *which, line_t *lp) 3432 { 3433 int entry; 3434 char *str; 3435 3436 if (lp == NULL) 3437 return; /* nothing to restore */ 3438 3439 str = lp->arg + strlen(which); 3440 entry = s_strtol(str); 3441 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3442 3443 /* delete saved old default line */ 3444 unlink_line(mp, lp); 3445 line_free(lp); 3446 } 3447 3448 /* 3449 * This function is for supporting reboot with args. 3450 * The opt value can be: 3451 * NULL delete temp entry, if present 3452 * entry=# switches default entry to 1 3453 * else treated as boot-args and setup a temperary menu entry 3454 * and make it the default 3455 */ 3456 #define REBOOT_TITLE "Solaris_reboot_transient" 3457 3458 /*ARGSUSED*/ 3459 static error_t 3460 update_temp(menu_t *mp, char *menupath, char *opt) 3461 { 3462 int entry; 3463 char *grubdisk, *rootdev, *path; 3464 char kernbuf[BUFSIZ]; 3465 char args_buf[BUFSIZ]; 3466 struct stat sb; 3467 3468 assert(mp); 3469 3470 /* If no option, delete exiting reboot menu entry */ 3471 if (opt == NULL) { 3472 entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL, 3473 0, &entry); 3474 if (ent == NULL) /* not found is ok */ 3475 return (BAM_SUCCESS); 3476 (void) do_delete(mp, entry); 3477 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault); 3478 mp->olddefault = NULL; 3479 return (BAM_WRITE); 3480 } 3481 3482 /* if entry= is specified, set the default entry */ 3483 if (strncmp(opt, "entry=", strlen("entry=")) == 0 && 3484 selector(mp, opt, &entry, NULL) == BAM_SUCCESS) { 3485 /* this is entry=# option */ 3486 return (set_global(mp, menu_cmds[DEFAULT_CMD], entry)); 3487 } 3488 3489 /* 3490 * add a new menu entry base on opt and make it the default 3491 */ 3492 grubdisk = NULL; 3493 if (stat(GRUB_slice, &sb) != 0) { 3494 /* 3495 * 1. First get root disk name from mnttab 3496 * 2. Translate disk name to grub name 3497 * 3. Add the new menu entry 3498 */ 3499 rootdev = get_special("/"); 3500 if (rootdev) { 3501 grubdisk = os_to_grubdisk(rootdev, 1); 3502 free(rootdev); 3503 } 3504 } else { 3505 /* 3506 * This is an LU BE. The GRUB_root file 3507 * contains entry for GRUB's "root" cmd. 3508 */ 3509 grubdisk = read_grub_root(); 3510 } 3511 if (grubdisk == NULL) { 3512 bam_error(REBOOT_WITH_ARGS_FAILED); 3513 return (BAM_ERROR); 3514 } 3515 3516 /* add an entry for Solaris reboot */ 3517 if (bam_direct == BAM_DIRECT_DBOOT) { 3518 if (opt[0] == '-') { 3519 /* It's an option - first see if boot-file is set */ 3520 if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ) 3521 != BAM_SUCCESS) 3522 return (BAM_ERROR); 3523 if (kernbuf[0] == '\0') 3524 (void) strncpy(kernbuf, DIRECT_BOOT_KERNEL, 3525 BUFSIZ); 3526 (void) strlcat(kernbuf, " ", BUFSIZ); 3527 (void) strlcat(kernbuf, opt, BUFSIZ); 3528 } else if (opt[0] == '/') { 3529 /* It's a full path - write it out and go home */ 3530 (void) strlcpy(kernbuf, opt, BUFSIZ); 3531 } else { 3532 path = expand_path(opt); 3533 if (path != NULL) { 3534 (void) strlcpy(kernbuf, path, BUFSIZ); 3535 free(path); 3536 if (strcmp(opt, "kmdb") == 0) { 3537 if (set_kernel(mp, ARGS_CMD, NULL, 3538 args_buf, BUFSIZ) != BAM_SUCCESS) 3539 return (BAM_ERROR); 3540 3541 if (args_buf[0] != '\0') { 3542 (void) strlcat(kernbuf, " ", 3543 BUFSIZ); 3544 (void) strlcat(kernbuf, 3545 args_buf, BUFSIZ); 3546 } 3547 } 3548 } 3549 } 3550 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3551 NULL); 3552 } else { 3553 (void) snprintf(kernbuf, sizeof (kernbuf), "%s %s", 3554 MULTI_BOOT, opt); 3555 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3556 MULTI_BOOT_ARCHIVE); 3557 } 3558 free(grubdisk); 3559 3560 if (entry == BAM_ERROR) { 3561 bam_error(REBOOT_WITH_ARGS_FAILED); 3562 return (BAM_ERROR); 3563 } 3564 3565 save_default_entry(mp, BAM_OLDDEF); 3566 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3567 return (BAM_WRITE); 3568 } 3569 3570 static error_t 3571 set_global(menu_t *mp, char *globalcmd, int val) 3572 { 3573 line_t *lp, *found, *last; 3574 char *cp, *str; 3575 char prefix[BAM_MAXLINE]; 3576 size_t len; 3577 3578 assert(mp); 3579 assert(globalcmd); 3580 3581 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) { 3582 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) { 3583 (void) snprintf(prefix, sizeof (prefix), "%d", val); 3584 bam_error(INVALID_ENTRY, prefix); 3585 return (BAM_ERROR); 3586 } 3587 } 3588 3589 found = last = NULL; 3590 for (lp = mp->start; lp; lp = lp->next) { 3591 if (lp->flags != BAM_GLOBAL) 3592 continue; 3593 3594 last = lp; /* track the last global found */ 3595 3596 if (lp->cmd == NULL) { 3597 bam_error(NO_CMD, lp->lineNum); 3598 continue; 3599 } 3600 if (strcmp(globalcmd, lp->cmd) != 0) 3601 continue; 3602 3603 if (found) { 3604 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 3605 } 3606 found = lp; 3607 } 3608 3609 if (found == NULL) { 3610 lp = s_calloc(1, sizeof (line_t)); 3611 if (last == NULL) { 3612 lp->next = mp->start; 3613 mp->start = lp; 3614 mp->end = (mp->end) ? mp->end : lp; 3615 } else { 3616 lp->next = last->next; 3617 last->next = lp; 3618 if (lp->next == NULL) 3619 mp->end = lp; 3620 } 3621 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */ 3622 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]); 3623 len += 10; /* val < 10 digits */ 3624 lp->line = s_calloc(1, len); 3625 (void) snprintf(lp->line, len, "%s%s%d", 3626 globalcmd, menu_cmds[SEP_CMD], val); 3627 return (BAM_WRITE); 3628 } 3629 3630 /* 3631 * We are changing an existing entry. Retain any prefix whitespace, 3632 * but overwrite everything else. This preserves tabs added for 3633 * readability. 3634 */ 3635 str = found->line; 3636 cp = prefix; 3637 while (*str == ' ' || *str == '\t') 3638 *(cp++) = *(str++); 3639 *cp = '\0'; /* Terminate prefix */ 3640 len = strlen(prefix) + strlen(globalcmd); 3641 len += strlen(menu_cmds[SEP_CMD]) + 10; 3642 3643 free(found->line); 3644 found->line = s_calloc(1, len); 3645 (void) snprintf(found->line, len, 3646 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val); 3647 3648 return (BAM_WRITE); /* need a write to menu */ 3649 } 3650 3651 /* 3652 * partial_path may be anything like "kernel/unix" or "kmdb". Try to 3653 * expand it to a full unix path. 3654 */ 3655 static char * 3656 expand_path(const char *partial_path) 3657 { 3658 int new_path_len; 3659 char *new_path, new_path2[PATH_MAX]; 3660 struct stat sb; 3661 3662 new_path_len = strlen(partial_path) + 64; 3663 new_path = s_calloc(1, new_path_len); 3664 3665 /* First, try the simplest case - something like "kernel/unix" */ 3666 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s", 3667 partial_path); 3668 if (stat(new_path, &sb) == 0) { 3669 return (new_path); 3670 } 3671 3672 if (strcmp(partial_path, "kmdb") == 0) { 3673 (void) snprintf(new_path, new_path_len, "%s -k", 3674 DIRECT_BOOT_KERNEL); 3675 return (new_path); 3676 } 3677 3678 /* 3679 * We've quickly reached unsupported usage. Try once more to 3680 * see if we were just given a glom name. 3681 */ 3682 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix", 3683 partial_path); 3684 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix", 3685 partial_path); 3686 if (stat(new_path, &sb) == 0) { 3687 if (stat(new_path2, &sb) == 0) { 3688 /* 3689 * We matched both, so we actually 3690 * want to write the $ISADIR version. 3691 */ 3692 (void) snprintf(new_path, new_path_len, 3693 "/platform/i86pc/kernel/%s/$ISADIR/unix", 3694 partial_path); 3695 } 3696 return (new_path); 3697 } 3698 3699 bam_error(UNKNOWN_KERNEL, partial_path); 3700 free(new_path); 3701 return (NULL); 3702 } 3703 3704 /* 3705 * The kernel cmd and arg have been changed, so 3706 * check whether the archive line needs to change. 3707 */ 3708 static void 3709 set_archive_line(entry_t *entryp, line_t *kernelp) 3710 { 3711 line_t *lp = entryp->start; 3712 char *new_archive; 3713 menu_cmd_t m_cmd; 3714 3715 for (; lp != NULL; lp = lp->next) { 3716 if (strncmp(lp->cmd, menu_cmds[MODULE_CMD], 3717 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) { 3718 break; 3719 } 3720 if (lp == entryp->end) 3721 return; 3722 } 3723 if (lp == NULL) 3724 return; 3725 3726 if (strstr(kernelp->arg, "$ISADIR") != NULL) { 3727 new_archive = DIRECT_BOOT_ARCHIVE; 3728 m_cmd = MODULE_DOLLAR_CMD; 3729 } else if (strstr(kernelp->arg, "amd64") != NULL) { 3730 new_archive = DIRECT_BOOT_ARCHIVE_64; 3731 m_cmd = MODULE_CMD; 3732 } else { 3733 new_archive = DIRECT_BOOT_ARCHIVE_32; 3734 m_cmd = MODULE_CMD; 3735 } 3736 3737 if (strcmp(lp->arg, new_archive) == 0) 3738 return; 3739 3740 if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) { 3741 free(lp->cmd); 3742 lp->cmd = s_strdup(menu_cmds[m_cmd]); 3743 } 3744 3745 free(lp->arg); 3746 lp->arg = s_strdup(new_archive); 3747 update_line(lp); 3748 } 3749 3750 /* 3751 * Title for an entry to set properties that once went in bootenv.rc. 3752 */ 3753 #define BOOTENV_RC_TITLE "Solaris bootenv rc" 3754 3755 /* 3756 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments 3757 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length 3758 * string, reset the value to the default. If path is a non-zero-length 3759 * string, set the kernel or arguments. 3760 */ 3761 static error_t 3762 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize) 3763 { 3764 int entryNum, rv = BAM_SUCCESS, free_new_path = 0; 3765 entry_t *entryp; 3766 line_t *ptr, *kernelp; 3767 char *new_arg, *old_args, *space; 3768 char *grubdisk, *rootdev, *new_path; 3769 char old_space; 3770 size_t old_kernel_len, new_str_len; 3771 struct stat sb; 3772 3773 assert(bufsize > 0); 3774 3775 ptr = kernelp = NULL; 3776 new_arg = old_args = space = NULL; 3777 grubdisk = rootdev = new_path = NULL; 3778 buf[0] = '\0'; 3779 3780 if (bam_direct != BAM_DIRECT_DBOOT) { 3781 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args"); 3782 return (BAM_ERROR); 3783 } 3784 3785 /* 3786 * If a user changed the default entry to a non-bootadm controlled 3787 * one, we don't want to mess with it. Just print an error and 3788 * return. 3789 */ 3790 if (mp->curdefault) { 3791 entryNum = s_strtol(mp->curdefault->arg); 3792 for (entryp = mp->entries; entryp; entryp = entryp->next) { 3793 if (entryp->entryNum == entryNum) 3794 break; 3795 } 3796 if ((entryp != NULL) && 3797 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) { 3798 bam_error(DEFAULT_NOT_BAM); 3799 return (BAM_ERROR); 3800 } 3801 } 3802 3803 entryNum = -1; 3804 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, 0, 3805 &entryNum); 3806 3807 if (entryp != NULL) { 3808 for (ptr = entryp->start; ptr && ptr != entryp->end; 3809 ptr = ptr->next) { 3810 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD], 3811 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) { 3812 kernelp = ptr; 3813 break; 3814 } 3815 } 3816 if (kernelp == NULL) { 3817 bam_error(NO_KERNEL, entryNum); 3818 return (BAM_ERROR); 3819 } 3820 3821 old_kernel_len = strcspn(kernelp->arg, " \t"); 3822 space = old_args = kernelp->arg + old_kernel_len; 3823 while ((*old_args == ' ') || (*old_args == '\t')) 3824 old_args++; 3825 } 3826 3827 if (path == NULL) { 3828 /* Simply report what was found */ 3829 if (kernelp == NULL) 3830 return (BAM_SUCCESS); 3831 3832 if (optnum == ARGS_CMD) { 3833 if (old_args[0] != '\0') 3834 (void) strlcpy(buf, old_args, bufsize); 3835 } else { 3836 /* 3837 * We need to print the kernel, so we just turn the 3838 * first space into a '\0' and print the beginning. 3839 * We don't print anything if it's the default kernel. 3840 */ 3841 old_space = *space; 3842 *space = '\0'; 3843 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) 3844 (void) strlcpy(buf, kernelp->arg, bufsize); 3845 *space = old_space; 3846 } 3847 return (BAM_SUCCESS); 3848 } 3849 3850 /* 3851 * First, check if we're resetting an entry to the default. 3852 */ 3853 if ((path[0] == '\0') || 3854 ((optnum == KERNEL_CMD) && 3855 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) { 3856 if ((entryp == NULL) || (kernelp == NULL)) { 3857 /* No previous entry, it's already the default */ 3858 return (BAM_SUCCESS); 3859 } 3860 3861 /* 3862 * Check if we can delete the entry. If we're resetting the 3863 * kernel command, and the args is already empty, or if we're 3864 * resetting the args command, and the kernel is already the 3865 * default, we can restore the old default and delete the entry. 3866 */ 3867 if (((optnum == KERNEL_CMD) && 3868 ((old_args == NULL) || (old_args[0] == '\0'))) || 3869 ((optnum == ARGS_CMD) && 3870 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL, 3871 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) { 3872 kernelp = NULL; 3873 (void) do_delete(mp, entryNum); 3874 restore_default_entry(mp, BAM_OLD_RC_DEF, 3875 mp->old_rc_default); 3876 mp->old_rc_default = NULL; 3877 rv = BAM_WRITE; 3878 goto done; 3879 } 3880 3881 if (optnum == KERNEL_CMD) { 3882 /* 3883 * At this point, we've already checked that old_args 3884 * and entryp are valid pointers. The "+ 2" is for 3885 * a space a the string termination character. 3886 */ 3887 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) + 3888 strlen(old_args) + 2; 3889 new_arg = s_calloc(1, new_str_len); 3890 (void) snprintf(new_arg, new_str_len, "%s %s", 3891 DIRECT_BOOT_KERNEL, old_args); 3892 free(kernelp->arg); 3893 kernelp->arg = new_arg; 3894 3895 /* 3896 * We have changed the kernel line, so we may need 3897 * to update the archive line as well. 3898 */ 3899 set_archive_line(entryp, kernelp); 3900 } else { 3901 /* 3902 * We're resetting the boot args to nothing, so 3903 * we only need to copy the kernel. We've already 3904 * checked that the kernel is not the default. 3905 */ 3906 new_arg = s_calloc(1, old_kernel_len + 1); 3907 (void) snprintf(new_arg, old_kernel_len + 1, "%s", 3908 kernelp->arg); 3909 free(kernelp->arg); 3910 kernelp->arg = new_arg; 3911 } 3912 rv = BAM_WRITE; 3913 goto done; 3914 } 3915 3916 /* 3917 * Expand the kernel file to a full path, if necessary 3918 */ 3919 if ((optnum == KERNEL_CMD) && (path[0] != '/')) { 3920 new_path = expand_path(path); 3921 if (new_path == NULL) { 3922 return (BAM_ERROR); 3923 } 3924 free_new_path = 1; 3925 } else { 3926 new_path = path; 3927 free_new_path = 0; 3928 } 3929 3930 /* 3931 * At this point, we know we're setting a new value. First, take care 3932 * of the case where there was no previous entry. 3933 */ 3934 if (entryp == NULL) { 3935 /* Similar to code in update_temp */ 3936 if (stat(GRUB_slice, &sb) != 0) { 3937 /* 3938 * 1. First get root disk name from mnttab 3939 * 2. Translate disk name to grub name 3940 * 3. Add the new menu entry 3941 */ 3942 rootdev = get_special("/"); 3943 if (rootdev) { 3944 grubdisk = os_to_grubdisk(rootdev, 1); 3945 free(rootdev); 3946 } 3947 } else { 3948 /* 3949 * This is an LU BE. The GRUB_root file 3950 * contains entry for GRUB's "root" cmd. 3951 */ 3952 grubdisk = read_grub_root(); 3953 } 3954 if (grubdisk == NULL) { 3955 bam_error(REBOOT_WITH_ARGS_FAILED); 3956 rv = BAM_ERROR; 3957 goto done; 3958 } 3959 if (optnum == KERNEL_CMD) { 3960 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 3961 grubdisk, new_path, NULL); 3962 } else { 3963 new_str_len = strlen(DIRECT_BOOT_KERNEL) + 3964 strlen(path) + 8; 3965 new_arg = s_calloc(1, new_str_len); 3966 3967 (void) snprintf(new_arg, new_str_len, "%s %s", 3968 DIRECT_BOOT_KERNEL, path); 3969 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 3970 grubdisk, new_arg, DIRECT_BOOT_ARCHIVE); 3971 } 3972 save_default_entry(mp, BAM_OLD_RC_DEF); 3973 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum); 3974 rv = BAM_WRITE; 3975 goto done; 3976 } 3977 3978 /* 3979 * There was already an bootenv entry which we need to edit. 3980 */ 3981 if (optnum == KERNEL_CMD) { 3982 new_str_len = strlen(new_path) + strlen(old_args) + 2; 3983 new_arg = s_calloc(1, new_str_len); 3984 (void) snprintf(new_arg, new_str_len, "%s %s", new_path, 3985 old_args); 3986 free(kernelp->arg); 3987 kernelp->arg = new_arg; 3988 3989 /* 3990 * If we have changed the kernel line, we may need to update 3991 * the archive line as well. 3992 */ 3993 set_archive_line(entryp, kernelp); 3994 } else { 3995 new_str_len = old_kernel_len + strlen(path) + 8; 3996 new_arg = s_calloc(1, new_str_len); 3997 (void) strncpy(new_arg, kernelp->arg, old_kernel_len); 3998 (void) strlcat(new_arg, " ", new_str_len); 3999 (void) strlcat(new_arg, path, new_str_len); 4000 free(kernelp->arg); 4001 kernelp->arg = new_arg; 4002 } 4003 rv = BAM_WRITE; 4004 4005 done: 4006 if ((rv == BAM_WRITE) && kernelp) 4007 update_line(kernelp); 4008 if (free_new_path) 4009 free(new_path); 4010 return (rv); 4011 } 4012 4013 /*ARGSUSED*/ 4014 static error_t 4015 set_option(menu_t *mp, char *menu_path, char *opt) 4016 { 4017 int optnum, optval; 4018 char *val; 4019 char buf[BUFSIZ] = ""; 4020 error_t rv; 4021 4022 assert(mp); 4023 assert(opt); 4024 4025 val = strchr(opt, '='); 4026 if (val != NULL) { 4027 *val = '\0'; 4028 } 4029 4030 if (strcmp(opt, "default") == 0) { 4031 optnum = DEFAULT_CMD; 4032 } else if (strcmp(opt, "timeout") == 0) { 4033 optnum = TIMEOUT_CMD; 4034 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) { 4035 optnum = KERNEL_CMD; 4036 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) { 4037 optnum = ARGS_CMD; 4038 } else { 4039 bam_error(INVALID_ENTRY, opt); 4040 return (BAM_ERROR); 4041 } 4042 4043 /* 4044 * kernel and args are allowed without "=new_value" strings. All 4045 * others cause errors 4046 */ 4047 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) { 4048 bam_error(INVALID_ENTRY, opt); 4049 return (BAM_ERROR); 4050 } else if (val != NULL) { 4051 *val = '='; 4052 } 4053 4054 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) { 4055 rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ); 4056 if ((rv == BAM_SUCCESS) && (buf[0] != '\0')) 4057 (void) printf("%s\n", buf); 4058 return (rv); 4059 } else { 4060 optval = s_strtol(val + 1); 4061 return (set_global(mp, menu_cmds[optnum], optval)); 4062 } 4063 } 4064 4065 /* 4066 * The quiet argument suppresses messages. This is used 4067 * when invoked in the context of other commands (e.g. list_entry) 4068 */ 4069 static error_t 4070 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet) 4071 { 4072 line_t *lp; 4073 char *arg; 4074 int done, ret = BAM_SUCCESS; 4075 4076 assert(mp); 4077 assert(menu_path); 4078 assert(globalcmd); 4079 4080 if (mp->start == NULL) { 4081 if (!quiet) 4082 bam_error(NO_MENU, menu_path); 4083 return (BAM_ERROR); 4084 } 4085 4086 done = 0; 4087 for (lp = mp->start; lp; lp = lp->next) { 4088 if (lp->flags != BAM_GLOBAL) 4089 continue; 4090 4091 if (lp->cmd == NULL) { 4092 if (!quiet) 4093 bam_error(NO_CMD, lp->lineNum); 4094 continue; 4095 } 4096 4097 if (strcmp(globalcmd, lp->cmd) != 0) 4098 continue; 4099 4100 /* Found global. Check for duplicates */ 4101 if (done && !quiet) { 4102 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 4103 ret = BAM_ERROR; 4104 } 4105 4106 arg = lp->arg ? lp->arg : ""; 4107 bam_print(GLOBAL_CMD, globalcmd, arg); 4108 done = 1; 4109 } 4110 4111 if (!done && bam_verbose) 4112 bam_print(NO_ENTRY, globalcmd); 4113 4114 return (ret); 4115 } 4116 4117 static error_t 4118 menu_write(char *root, menu_t *mp) 4119 { 4120 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start)); 4121 } 4122 4123 static void 4124 line_free(line_t *lp) 4125 { 4126 if (lp == NULL) 4127 return; 4128 4129 if (lp->cmd) 4130 free(lp->cmd); 4131 if (lp->sep) 4132 free(lp->sep); 4133 if (lp->arg) 4134 free(lp->arg); 4135 if (lp->line) 4136 free(lp->line); 4137 free(lp); 4138 } 4139 4140 static void 4141 linelist_free(line_t *start) 4142 { 4143 line_t *lp; 4144 4145 while (start) { 4146 lp = start; 4147 start = start->next; 4148 line_free(lp); 4149 } 4150 } 4151 4152 static void 4153 filelist_free(filelist_t *flistp) 4154 { 4155 linelist_free(flistp->head); 4156 flistp->head = NULL; 4157 flistp->tail = NULL; 4158 } 4159 4160 static void 4161 menu_free(menu_t *mp) 4162 { 4163 entry_t *ent, *tmp; 4164 assert(mp); 4165 4166 if (mp->start) 4167 linelist_free(mp->start); 4168 ent = mp->entries; 4169 while (ent) { 4170 tmp = ent; 4171 ent = tmp->next; 4172 free(tmp); 4173 } 4174 4175 free(mp); 4176 } 4177 4178 /* 4179 * Utility routines 4180 */ 4181 4182 4183 /* 4184 * Returns 0 on success 4185 * Any other value indicates an error 4186 */ 4187 static int 4188 exec_cmd(char *cmdline, char *output, int64_t osize) 4189 { 4190 char buf[BUFSIZ]; 4191 int ret; 4192 FILE *ptr; 4193 size_t len; 4194 sigset_t set; 4195 void (*disp)(int); 4196 4197 /* 4198 * For security 4199 * - only absolute paths are allowed 4200 * - set IFS to space and tab 4201 */ 4202 if (*cmdline != '/') { 4203 bam_error(ABS_PATH_REQ, cmdline); 4204 return (-1); 4205 } 4206 (void) putenv("IFS= \t"); 4207 4208 /* 4209 * We may have been exec'ed with SIGCHLD blocked 4210 * unblock it here 4211 */ 4212 (void) sigemptyset(&set); 4213 (void) sigaddset(&set, SIGCHLD); 4214 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 4215 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno)); 4216 return (-1); 4217 } 4218 4219 /* 4220 * Set SIGCHLD disposition to SIG_DFL for popen/pclose 4221 */ 4222 disp = sigset(SIGCHLD, SIG_DFL); 4223 if (disp == SIG_ERR) { 4224 bam_error(FAILED_SIG, strerror(errno)); 4225 return (-1); 4226 } 4227 if (disp == SIG_HOLD) { 4228 bam_error(BLOCKED_SIG, cmdline); 4229 return (-1); 4230 } 4231 4232 ptr = popen(cmdline, "r"); 4233 if (ptr == NULL) { 4234 bam_error(POPEN_FAIL, cmdline, strerror(errno)); 4235 return (-1); 4236 } 4237 4238 /* 4239 * If we simply do a pclose() following a popen(), pclose() 4240 * will close the reader end of the pipe immediately even 4241 * if the child process has not started/exited. pclose() 4242 * does wait for cmd to terminate before returning though. 4243 * When the executed command writes its output to the pipe 4244 * there is no reader process and the command dies with 4245 * SIGPIPE. To avoid this we read repeatedly until read 4246 * terminates with EOF. This indicates that the command 4247 * (writer) has closed the pipe and we can safely do a 4248 * pclose(). 4249 * 4250 * Since pclose() does wait for the command to exit, 4251 * we can safely reap the exit status of the command 4252 * from the value returned by pclose() 4253 */ 4254 while (fgets(buf, sizeof (buf), ptr) != NULL) { 4255 /* if (bam_verbose) XXX */ 4256 bam_print(PRINT_NO_NEWLINE, buf); 4257 if (output && osize > 0) { 4258 (void) snprintf(output, osize, "%s", buf); 4259 len = strlen(buf); 4260 output += len; 4261 osize -= len; 4262 } 4263 } 4264 4265 ret = pclose(ptr); 4266 if (ret == -1) { 4267 bam_error(PCLOSE_FAIL, cmdline, strerror(errno)); 4268 return (-1); 4269 } 4270 4271 if (WIFEXITED(ret)) { 4272 return (WEXITSTATUS(ret)); 4273 } else { 4274 bam_error(EXEC_FAIL, cmdline, ret); 4275 return (-1); 4276 } 4277 } 4278 4279 /* 4280 * Since this function returns -1 on error 4281 * it cannot be used to convert -1. However, 4282 * that is sufficient for what we need. 4283 */ 4284 static long 4285 s_strtol(char *str) 4286 { 4287 long l; 4288 char *res = NULL; 4289 4290 if (str == NULL) { 4291 return (-1); 4292 } 4293 4294 errno = 0; 4295 l = strtol(str, &res, 10); 4296 if (errno || *res != '\0') { 4297 return (-1); 4298 } 4299 4300 return (l); 4301 } 4302 4303 /* 4304 * Wrapper around fputs, that adds a newline (since fputs doesn't) 4305 */ 4306 static int 4307 s_fputs(char *str, FILE *fp) 4308 { 4309 char linebuf[BAM_MAXLINE]; 4310 4311 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str); 4312 return (fputs(linebuf, fp)); 4313 } 4314 4315 /* 4316 * Wrapper around fgets, that strips newlines returned by fgets 4317 */ 4318 char * 4319 s_fgets(char *buf, int buflen, FILE *fp) 4320 { 4321 int n; 4322 4323 buf = fgets(buf, buflen, fp); 4324 if (buf) { 4325 n = strlen(buf); 4326 if (n == buflen - 1 && buf[n-1] != '\n') 4327 bam_error(TOO_LONG, buflen - 1, buf); 4328 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1]; 4329 } 4330 4331 return (buf); 4332 } 4333 4334 void * 4335 s_calloc(size_t nelem, size_t sz) 4336 { 4337 void *ptr; 4338 4339 ptr = calloc(nelem, sz); 4340 if (ptr == NULL) { 4341 bam_error(NO_MEM, nelem*sz); 4342 bam_exit(1); 4343 } 4344 return (ptr); 4345 } 4346 4347 void * 4348 s_realloc(void *ptr, size_t sz) 4349 { 4350 ptr = realloc(ptr, sz); 4351 if (ptr == NULL) { 4352 bam_error(NO_MEM, sz); 4353 bam_exit(1); 4354 } 4355 return (ptr); 4356 } 4357 4358 static char * 4359 s_strdup(char *str) 4360 { 4361 char *ptr; 4362 4363 if (str == NULL) 4364 return (NULL); 4365 4366 ptr = strdup(str); 4367 if (ptr == NULL) { 4368 bam_error(NO_MEM, strlen(str) + 1); 4369 bam_exit(1); 4370 } 4371 return (ptr); 4372 } 4373 4374 /* 4375 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients) 4376 * Returns 0 otherwise 4377 */ 4378 static int 4379 is_amd64(void) 4380 { 4381 static int amd64 = -1; 4382 char isabuf[257]; /* from sysinfo(2) manpage */ 4383 4384 if (amd64 != -1) 4385 return (amd64); 4386 4387 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 && 4388 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) 4389 amd64 = 1; 4390 else if (strstr(isabuf, "i386") == NULL) 4391 amd64 = 1; /* diskless server */ 4392 else 4393 amd64 = 0; 4394 4395 return (amd64); 4396 } 4397 4398 static void 4399 append_to_flist(filelist_t *flistp, char *s) 4400 { 4401 line_t *lp; 4402 4403 lp = s_calloc(1, sizeof (line_t)); 4404 lp->line = s_strdup(s); 4405 if (flistp->head == NULL) 4406 flistp->head = lp; 4407 else 4408 flistp->tail->next = lp; 4409 flistp->tail = lp; 4410 } 4411