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