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