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