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