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