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 } else { 2242 /* 2243 * First update archive for current root 2244 */ 2245 if (update_archive(root, opt) != BAM_SUCCESS) 2246 ret = BAM_ERROR; 2247 } 2248 2249 if (ret == BAM_ERROR) 2250 goto out; 2251 2252 /* 2253 * Now walk the mount table, performing archive update 2254 * for all mounted Newboot root filesystems 2255 */ 2256 fp = fopen(MNTTAB, "r"); 2257 if (fp == NULL) { 2258 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 2259 ret = BAM_ERROR; 2260 goto out; 2261 } 2262 2263 resetmnttab(fp); 2264 2265 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 2266 if (mnt.mnt_special == NULL) 2267 continue; 2268 if (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0) 2269 continue; 2270 if (strcmp(mnt.mnt_mountp, "/") == 0) 2271 continue; 2272 2273 (void) snprintf(multibt, sizeof (multibt), "%s%s", 2274 mnt.mnt_mountp, MULTI_BOOT); 2275 2276 if (stat(multibt, &sb) == -1) 2277 continue; 2278 2279 /* 2280 * We put a trailing slash to be consistent with root = "/" 2281 * case, such that we don't have to print // in some cases. 2282 */ 2283 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 2284 mnt.mnt_mountp); 2285 bam_rootlen = strlen(rootbuf); 2286 2287 /* 2288 * It's possible that other mounts may be an alternate boot 2289 * architecture, so check it again. 2290 */ 2291 if ((dboot_or_multiboot(rootbuf) != BAM_SUCCESS) || 2292 (update_archive(rootbuf, opt) != BAM_SUCCESS)) 2293 ret = BAM_ERROR; 2294 } 2295 2296 (void) fclose(fp); 2297 2298 out: 2299 if (stat(GRUB_slice, &sb) == 0) { 2300 restore_grub_slice(); 2301 } 2302 2303 /* 2304 * Update fdisk table as we go down. Updating it when 2305 * the system is running will confuse biosdev. 2306 */ 2307 ret1 = stat(GRUB_fdisk, &sb); 2308 ret2 = stat(GRUB_fdisk_target, &sb); 2309 if ((ret1 == 0) && (ret2 == 0)) { 2310 update_fdisk(); 2311 } else if ((ret1 == 0) ^ (ret2 == 0)) { 2312 /* 2313 * It is an error for one file to be 2314 * present and the other absent. 2315 * It is normal for both files to be 2316 * absent - it indicates that no fdisk 2317 * update is required. 2318 */ 2319 bam_error(MISSING_FDISK_FILE, 2320 ret1 ? GRUB_fdisk : GRUB_fdisk_target); 2321 ret = BAM_ERROR; 2322 } 2323 2324 return (ret); 2325 } 2326 2327 static void 2328 append_line(menu_t *mp, line_t *lp) 2329 { 2330 if (mp->start == NULL) { 2331 mp->start = lp; 2332 } else { 2333 mp->end->next = lp; 2334 lp->prev = mp->end; 2335 } 2336 mp->end = lp; 2337 } 2338 2339 static void 2340 unlink_line(menu_t *mp, line_t *lp) 2341 { 2342 /* unlink from list */ 2343 if (lp->prev) 2344 lp->prev->next = lp->next; 2345 else 2346 mp->start = lp->next; 2347 if (lp->next) 2348 lp->next->prev = lp->prev; 2349 else 2350 mp->end = lp->prev; 2351 } 2352 2353 static entry_t * 2354 boot_entry_new(menu_t *mp, line_t *start, line_t *end) 2355 { 2356 entry_t *ent, *prev; 2357 2358 ent = s_calloc(1, sizeof (entry_t)); 2359 ent->start = start; 2360 ent->end = end; 2361 2362 if (mp->entries == NULL) { 2363 mp->entries = ent; 2364 return (ent); 2365 } 2366 2367 prev = mp->entries; 2368 while (prev->next) 2369 prev = prev-> next; 2370 prev->next = ent; 2371 ent->prev = prev; 2372 return (ent); 2373 } 2374 2375 static void 2376 boot_entry_addline(entry_t *ent, line_t *lp) 2377 { 2378 if (ent) 2379 ent->end = lp; 2380 } 2381 2382 /* 2383 * A line in menu.lst looks like 2384 * [ ]*<cmd>[ \t=]*<arg>* 2385 */ 2386 static void 2387 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum) 2388 { 2389 /* 2390 * save state across calls. This is so that 2391 * header gets the right entry# after title has 2392 * been processed 2393 */ 2394 static line_t *prev = NULL; 2395 static entry_t *curr_ent = NULL; 2396 static int in_liveupgrade = 0; 2397 2398 line_t *lp; 2399 char *cmd, *sep, *arg; 2400 char save, *cp, *line; 2401 menu_flag_t flag = BAM_INVALID; 2402 2403 if (str == NULL) { 2404 return; 2405 } 2406 2407 /* 2408 * First save a copy of the entire line. 2409 * We use this later to set the line field. 2410 */ 2411 line = s_strdup(str); 2412 2413 /* Eat up leading whitespace */ 2414 while (*str == ' ' || *str == '\t') 2415 str++; 2416 2417 if (*str == '#') { /* comment */ 2418 cmd = s_strdup("#"); 2419 sep = NULL; 2420 arg = s_strdup(str + 1); 2421 flag = BAM_COMMENT; 2422 if (strstr(arg, BAM_LU_HDR) != NULL) { 2423 in_liveupgrade = 1; 2424 } else if (strstr(arg, BAM_LU_FTR) != NULL) { 2425 in_liveupgrade = 0; 2426 } 2427 } else if (*str == '\0') { /* blank line */ 2428 cmd = sep = arg = NULL; 2429 flag = BAM_EMPTY; 2430 } else { 2431 /* 2432 * '=' is not a documented separator in grub syntax. 2433 * However various development bits use '=' as a 2434 * separator. In addition, external users also 2435 * use = as a separator. So we will allow that usage. 2436 */ 2437 cp = str; 2438 while (*str != ' ' && *str != '\t' && *str != '=') { 2439 if (*str == '\0') { 2440 cmd = s_strdup(cp); 2441 sep = arg = NULL; 2442 break; 2443 } 2444 str++; 2445 } 2446 2447 if (*str != '\0') { 2448 save = *str; 2449 *str = '\0'; 2450 cmd = s_strdup(cp); 2451 *str = save; 2452 2453 str++; 2454 save = *str; 2455 *str = '\0'; 2456 sep = s_strdup(str - 1); 2457 *str = save; 2458 2459 while (*str == ' ' || *str == '\t') 2460 str++; 2461 if (*str == '\0') 2462 arg = NULL; 2463 else 2464 arg = s_strdup(str); 2465 } 2466 } 2467 2468 lp = s_calloc(1, sizeof (line_t)); 2469 2470 lp->cmd = cmd; 2471 lp->sep = sep; 2472 lp->arg = arg; 2473 lp->line = line; 2474 lp->lineNum = ++(*lineNum); 2475 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) { 2476 lp->entryNum = ++(*entryNum); 2477 lp->flags = BAM_TITLE; 2478 if (prev && prev->flags == BAM_COMMENT && 2479 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2480 prev->entryNum = lp->entryNum; 2481 curr_ent = boot_entry_new(mp, prev, lp); 2482 curr_ent->flags = BAM_ENTRY_BOOTADM; 2483 } else { 2484 curr_ent = boot_entry_new(mp, lp, lp); 2485 if (in_liveupgrade) { 2486 curr_ent->flags = BAM_ENTRY_LU; 2487 } 2488 } 2489 curr_ent->entryNum = *entryNum; 2490 } else if (flag != BAM_INVALID) { 2491 /* 2492 * For header comments, the entry# is "fixed up" 2493 * by the subsequent title 2494 */ 2495 lp->entryNum = *entryNum; 2496 lp->flags = flag; 2497 } else { 2498 lp->entryNum = *entryNum; 2499 2500 if (*entryNum == ENTRY_INIT) { 2501 lp->flags = BAM_GLOBAL; 2502 } else { 2503 lp->flags = BAM_ENTRY; 2504 2505 if (cmd && arg) { 2506 /* 2507 * We only compare for the length of "module" 2508 * so that "module$" will also match. 2509 */ 2510 if ((strncmp(cmd, menu_cmds[MODULE_CMD], 2511 strlen(menu_cmds[MODULE_CMD])) == 0) && 2512 (strcmp(arg, MINIROOT) == 0)) 2513 curr_ent->flags |= BAM_ENTRY_MINIROOT; 2514 else if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) 2515 curr_ent->flags |= BAM_ENTRY_ROOT; 2516 else if (strcmp(cmd, 2517 menu_cmds[CHAINLOADER_CMD]) == 0) 2518 curr_ent->flags |= 2519 BAM_ENTRY_CHAINLOADER; 2520 } 2521 } 2522 } 2523 2524 /* record default, old default, and entry line ranges */ 2525 if (lp->flags == BAM_GLOBAL && 2526 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) { 2527 mp->curdefault = lp; 2528 } else if (lp->flags == BAM_COMMENT && 2529 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) { 2530 mp->olddefault = lp; 2531 } else if (lp->flags == BAM_COMMENT && 2532 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) { 2533 mp->old_rc_default = lp; 2534 } else if (lp->flags == BAM_ENTRY || 2535 (lp->flags == BAM_COMMENT && 2536 strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) { 2537 boot_entry_addline(curr_ent, lp); 2538 } 2539 append_line(mp, lp); 2540 2541 prev = lp; 2542 } 2543 2544 static void 2545 update_numbering(menu_t *mp) 2546 { 2547 int lineNum; 2548 int entryNum; 2549 int old_default_value; 2550 line_t *lp, *prev, *default_lp, *default_entry; 2551 char buf[PATH_MAX]; 2552 2553 if (mp->start == NULL) { 2554 return; 2555 } 2556 2557 lineNum = LINE_INIT; 2558 entryNum = ENTRY_INIT; 2559 old_default_value = ENTRY_INIT; 2560 lp = default_lp = default_entry = NULL; 2561 2562 prev = NULL; 2563 for (lp = mp->start; lp; prev = lp, lp = lp->next) { 2564 lp->lineNum = ++lineNum; 2565 2566 /* 2567 * Get the value of the default command 2568 */ 2569 if (lp->entryNum == ENTRY_INIT && lp->cmd && 2570 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 && 2571 lp->arg) { 2572 old_default_value = atoi(lp->arg); 2573 default_lp = lp; 2574 } 2575 2576 /* 2577 * If not boot entry, nothing else to fix for this 2578 * entry 2579 */ 2580 if (lp->entryNum == ENTRY_INIT) 2581 continue; 2582 2583 /* 2584 * Record the position of the default entry. 2585 * The following works because global 2586 * commands like default and timeout should precede 2587 * actual boot entries, so old_default_value 2588 * is already known (or default cmd is missing). 2589 */ 2590 if (default_entry == NULL && 2591 old_default_value != ENTRY_INIT && 2592 lp->entryNum == old_default_value) { 2593 default_entry = lp; 2594 } 2595 2596 /* 2597 * Now fixup the entry number 2598 */ 2599 if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) { 2600 lp->entryNum = ++entryNum; 2601 /* fixup the bootadm header */ 2602 if (prev && prev->flags == BAM_COMMENT && 2603 prev->arg && 2604 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 2605 prev->entryNum = lp->entryNum; 2606 } 2607 } else { 2608 lp->entryNum = entryNum; 2609 } 2610 } 2611 2612 /* 2613 * No default command in menu, simply return 2614 */ 2615 if (default_lp == NULL) { 2616 return; 2617 } 2618 2619 free(default_lp->arg); 2620 free(default_lp->line); 2621 2622 if (default_entry == NULL) { 2623 default_lp->arg = s_strdup("0"); 2624 } else { 2625 (void) snprintf(buf, sizeof (buf), "%d", 2626 default_entry->entryNum); 2627 default_lp->arg = s_strdup(buf); 2628 } 2629 2630 /* 2631 * The following is required since only the line field gets 2632 * written back to menu.lst 2633 */ 2634 (void) snprintf(buf, sizeof (buf), "%s%s%s", 2635 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg); 2636 default_lp->line = s_strdup(buf); 2637 } 2638 2639 2640 static menu_t * 2641 menu_read(char *menu_path) 2642 { 2643 FILE *fp; 2644 char buf[BAM_MAXLINE], *cp; 2645 menu_t *mp; 2646 int line, entry, len, n; 2647 2648 mp = s_calloc(1, sizeof (menu_t)); 2649 2650 fp = fopen(menu_path, "r"); 2651 if (fp == NULL) { /* Let the caller handle this error */ 2652 return (mp); 2653 } 2654 2655 2656 /* Note: GRUB boot entry number starts with 0 */ 2657 line = LINE_INIT; 2658 entry = ENTRY_INIT; 2659 cp = buf; 2660 len = sizeof (buf); 2661 while (s_fgets(cp, len, fp) != NULL) { 2662 n = strlen(cp); 2663 if (cp[n - 1] == '\\') { 2664 len -= n - 1; 2665 assert(len >= 2); 2666 cp += n - 1; 2667 continue; 2668 } 2669 line_parser(mp, buf, &line, &entry); 2670 cp = buf; 2671 len = sizeof (buf); 2672 } 2673 2674 if (fclose(fp) == EOF) { 2675 bam_error(CLOSE_FAIL, menu_path, strerror(errno)); 2676 } 2677 2678 return (mp); 2679 } 2680 2681 static error_t 2682 selector(menu_t *mp, char *opt, int *entry, char **title) 2683 { 2684 char *eq; 2685 char *opt_dup; 2686 int entryNum; 2687 2688 assert(mp); 2689 assert(mp->start); 2690 assert(opt); 2691 2692 opt_dup = s_strdup(opt); 2693 2694 if (entry) 2695 *entry = ENTRY_INIT; 2696 if (title) 2697 *title = NULL; 2698 2699 eq = strchr(opt_dup, '='); 2700 if (eq == NULL) { 2701 bam_error(INVALID_OPT, opt); 2702 free(opt_dup); 2703 return (BAM_ERROR); 2704 } 2705 2706 *eq = '\0'; 2707 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) { 2708 assert(mp->end); 2709 entryNum = s_strtol(eq + 1); 2710 if (entryNum < 0 || entryNum > mp->end->entryNum) { 2711 bam_error(INVALID_ENTRY, eq + 1); 2712 free(opt_dup); 2713 return (BAM_ERROR); 2714 } 2715 *entry = entryNum; 2716 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) { 2717 *title = opt + (eq - opt_dup) + 1; 2718 } else { 2719 bam_error(INVALID_OPT, opt); 2720 free(opt_dup); 2721 return (BAM_ERROR); 2722 } 2723 2724 free(opt_dup); 2725 return (BAM_SUCCESS); 2726 } 2727 2728 /* 2729 * If invoked with no titles/entries (opt == NULL) 2730 * only title lines in file are printed. 2731 * 2732 * If invoked with a title or entry #, all 2733 * lines in *every* matching entry are listed 2734 */ 2735 static error_t 2736 list_entry(menu_t *mp, char *menu_path, char *opt) 2737 { 2738 line_t *lp; 2739 int entry = ENTRY_INIT; 2740 int found; 2741 char *title = NULL; 2742 2743 assert(mp); 2744 assert(menu_path); 2745 2746 if (mp->start == NULL) { 2747 bam_error(NO_MENU, menu_path); 2748 return (BAM_ERROR); 2749 } 2750 2751 if (opt != NULL) { 2752 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) { 2753 return (BAM_ERROR); 2754 } 2755 assert((entry != ENTRY_INIT) ^ (title != NULL)); 2756 } else { 2757 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0); 2758 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0); 2759 } 2760 2761 found = 0; 2762 for (lp = mp->start; lp; lp = lp->next) { 2763 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY) 2764 continue; 2765 if (opt == NULL && lp->flags == BAM_TITLE) { 2766 bam_print(PRINT_TITLE, lp->entryNum, 2767 lp->arg); 2768 found = 1; 2769 continue; 2770 } 2771 if (entry != ENTRY_INIT && lp->entryNum == entry) { 2772 bam_print(PRINT, lp->line); 2773 found = 1; 2774 continue; 2775 } 2776 2777 /* 2778 * We set the entry value here so that all lines 2779 * in entry get printed. If we subsequently match 2780 * title in other entries, all lines in those 2781 * entries get printed as well. 2782 */ 2783 if (title && lp->flags == BAM_TITLE && lp->arg && 2784 strncmp(title, lp->arg, strlen(title)) == 0) { 2785 bam_print(PRINT, lp->line); 2786 entry = lp->entryNum; 2787 found = 1; 2788 continue; 2789 } 2790 } 2791 2792 if (!found) { 2793 bam_error(NO_MATCH_ENTRY); 2794 return (BAM_ERROR); 2795 } 2796 2797 return (BAM_SUCCESS); 2798 } 2799 2800 static int 2801 add_boot_entry(menu_t *mp, 2802 char *title, 2803 char *root, 2804 char *kernel, 2805 char *module) 2806 { 2807 int lineNum, entryNum; 2808 char linebuf[BAM_MAXLINE]; 2809 menu_cmd_t k_cmd, m_cmd; 2810 2811 assert(mp); 2812 2813 if (title == NULL) { 2814 title = "Solaris"; /* default to Solaris */ 2815 } 2816 if (kernel == NULL) { 2817 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]); 2818 return (BAM_ERROR); 2819 } 2820 if (module == NULL) { 2821 if (bam_direct != BAM_DIRECT_DBOOT) { 2822 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]); 2823 return (BAM_ERROR); 2824 } 2825 2826 /* Figure the commands out from the kernel line */ 2827 if (strstr(kernel, "$ISADIR") != NULL) { 2828 module = DIRECT_BOOT_ARCHIVE; 2829 k_cmd = KERNEL_DOLLAR_CMD; 2830 m_cmd = MODULE_DOLLAR_CMD; 2831 } else if (strstr(kernel, "amd64") != NULL) { 2832 module = DIRECT_BOOT_ARCHIVE_64; 2833 k_cmd = KERNEL_CMD; 2834 m_cmd = MODULE_CMD; 2835 } else { 2836 module = DIRECT_BOOT_ARCHIVE_32; 2837 k_cmd = KERNEL_CMD; 2838 m_cmd = MODULE_CMD; 2839 } 2840 } else if ((bam_direct == BAM_DIRECT_DBOOT) && 2841 (strstr(kernel, "$ISADIR") != NULL)) { 2842 /* 2843 * If it's a non-failsafe dboot kernel, use the "kernel$" 2844 * command. Otherwise, use "kernel". 2845 */ 2846 k_cmd = KERNEL_DOLLAR_CMD; 2847 m_cmd = MODULE_DOLLAR_CMD; 2848 } else { 2849 k_cmd = KERNEL_CMD; 2850 m_cmd = MODULE_CMD; 2851 } 2852 2853 if (mp->start) { 2854 lineNum = mp->end->lineNum; 2855 entryNum = mp->end->entryNum; 2856 } else { 2857 lineNum = LINE_INIT; 2858 entryNum = ENTRY_INIT; 2859 } 2860 2861 /* 2862 * No separator for comment (HDR/FTR) commands 2863 * The syntax for comments is #<comment> 2864 */ 2865 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2866 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR); 2867 line_parser(mp, linebuf, &lineNum, &entryNum); 2868 2869 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2870 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 2871 line_parser(mp, linebuf, &lineNum, &entryNum); 2872 2873 if (root) { 2874 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2875 menu_cmds[ROOT_CMD], menu_cmds[SEP_CMD], root); 2876 line_parser(mp, linebuf, &lineNum, &entryNum); 2877 } 2878 2879 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2880 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel); 2881 line_parser(mp, linebuf, &lineNum, &entryNum); 2882 2883 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 2884 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module); 2885 line_parser(mp, linebuf, &lineNum, &entryNum); 2886 2887 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 2888 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR); 2889 line_parser(mp, linebuf, &lineNum, &entryNum); 2890 2891 return (entryNum); 2892 } 2893 2894 static error_t 2895 do_delete(menu_t *mp, int entryNum) 2896 { 2897 line_t *lp, *freed; 2898 entry_t *ent, *tmp; 2899 int deleted; 2900 2901 assert(entryNum != ENTRY_INIT); 2902 2903 ent = mp->entries; 2904 while (ent) { 2905 lp = ent->start; 2906 /* check entry number and make sure it's a bootadm entry */ 2907 if (lp->flags != BAM_COMMENT || 2908 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 || 2909 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) { 2910 ent = ent->next; 2911 continue; 2912 } 2913 2914 /* free the entry content */ 2915 do { 2916 freed = lp; 2917 lp = lp->next; /* prev stays the same */ 2918 unlink_line(mp, freed); 2919 line_free(freed); 2920 } while (freed != ent->end); 2921 2922 /* free the entry_t structure */ 2923 tmp = ent; 2924 ent = ent->next; 2925 if (tmp->prev) 2926 tmp->prev->next = ent; 2927 else 2928 mp->entries = ent; 2929 if (ent) 2930 ent->prev = tmp->prev; 2931 deleted = 1; 2932 } 2933 2934 if (!deleted && entryNum != ALL_ENTRIES) { 2935 bam_error(NO_BOOTADM_MATCH); 2936 return (BAM_ERROR); 2937 } 2938 2939 /* 2940 * Now that we have deleted an entry, update 2941 * the entry numbering and the default cmd. 2942 */ 2943 update_numbering(mp); 2944 2945 return (BAM_SUCCESS); 2946 } 2947 2948 static error_t 2949 delete_all_entries(menu_t *mp, char *menu_path, char *opt) 2950 { 2951 assert(mp); 2952 assert(opt == NULL); 2953 2954 if (mp->start == NULL) { 2955 bam_print(EMPTY_FILE, menu_path); 2956 return (BAM_SUCCESS); 2957 } 2958 2959 if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) { 2960 return (BAM_ERROR); 2961 } 2962 2963 return (BAM_WRITE); 2964 } 2965 2966 static FILE * 2967 open_diskmap(char *root) 2968 { 2969 FILE *fp; 2970 char cmd[PATH_MAX]; 2971 2972 /* make sure we have a map file */ 2973 fp = fopen(GRUBDISK_MAP, "r"); 2974 if (fp == NULL) { 2975 (void) snprintf(cmd, sizeof (cmd), 2976 "%s%s > /dev/null", root, CREATE_DISKMAP); 2977 (void) system(cmd); 2978 fp = fopen(GRUBDISK_MAP, "r"); 2979 } 2980 return (fp); 2981 } 2982 2983 #define SECTOR_SIZE 512 2984 2985 static int 2986 get_partition(char *device) 2987 { 2988 int i, fd, is_pcfs, partno = -1; 2989 struct mboot *mboot; 2990 char boot_sect[SECTOR_SIZE]; 2991 char *wholedisk, *slice; 2992 2993 /* form whole disk (p0) */ 2994 slice = device + strlen(device) - 2; 2995 is_pcfs = (*slice != 's'); 2996 if (!is_pcfs) 2997 *slice = '\0'; 2998 wholedisk = s_calloc(1, strlen(device) + 3); 2999 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device); 3000 if (!is_pcfs) 3001 *slice = 's'; 3002 3003 /* read boot sector */ 3004 fd = open(wholedisk, O_RDONLY); 3005 free(wholedisk); 3006 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 3007 return (partno); 3008 } 3009 (void) close(fd); 3010 3011 /* parse fdisk table */ 3012 mboot = (struct mboot *)((void *)boot_sect); 3013 for (i = 0; i < FD_NUMPART; i++) { 3014 struct ipart *part = 3015 (struct ipart *)(uintptr_t)mboot->parts + i; 3016 if (is_pcfs) { /* looking for solaris boot part */ 3017 if (part->systid == 0xbe) { 3018 partno = i; 3019 break; 3020 } 3021 } else { /* look for solaris partition, old and new */ 3022 if (part->systid == SUNIXOS || 3023 part->systid == SUNIXOS2) { 3024 partno = i; 3025 break; 3026 } 3027 } 3028 } 3029 return (partno); 3030 } 3031 3032 static char * 3033 get_grubdisk(char *rootdev, FILE *fp, int on_bootdev) 3034 { 3035 char *grubdisk; /* (hd#,#,#) */ 3036 char *slice; 3037 char *grubhd; 3038 int fdiskpart; 3039 int found = 0; 3040 char *devname, *ctdname = strstr(rootdev, "dsk/"); 3041 char linebuf[PATH_MAX]; 3042 3043 if (ctdname == NULL) 3044 return (NULL); 3045 3046 ctdname += strlen("dsk/"); 3047 slice = strrchr(ctdname, 's'); 3048 if (slice) 3049 *slice = '\0'; 3050 3051 rewind(fp); 3052 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) { 3053 grubhd = strtok(linebuf, " \t\n"); 3054 if (grubhd) 3055 devname = strtok(NULL, " \t\n"); 3056 else 3057 devname = NULL; 3058 if (devname && strcmp(devname, ctdname) == 0) { 3059 found = 1; 3060 break; 3061 } 3062 } 3063 3064 if (slice) 3065 *slice = 's'; 3066 3067 if (found == 0) { 3068 if (bam_verbose) 3069 bam_print(DISKMAP_FAIL_NONFATAL, rootdev); 3070 grubhd = "0"; /* assume disk 0 if can't match */ 3071 } 3072 3073 fdiskpart = get_partition(rootdev); 3074 if (fdiskpart == -1) 3075 return (NULL); 3076 3077 grubdisk = s_calloc(1, 10); 3078 if (slice) { 3079 (void) snprintf(grubdisk, 10, "(hd%s,%d,%c)", 3080 grubhd, fdiskpart, slice[1] + 'a' - '0'); 3081 } else 3082 (void) snprintf(grubdisk, 10, "(hd%s,%d)", 3083 grubhd, fdiskpart); 3084 3085 /* if root not on bootdev, change GRUB disk to 0 */ 3086 if (!on_bootdev) 3087 grubdisk[3] = '0'; 3088 return (grubdisk); 3089 } 3090 3091 static char * 3092 get_title(char *rootdir) 3093 { 3094 static char title[80]; /* from /etc/release */ 3095 char *cp = NULL, release[PATH_MAX]; 3096 FILE *fp; 3097 3098 /* open the /etc/release file */ 3099 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir); 3100 3101 fp = fopen(release, "r"); 3102 if (fp == NULL) 3103 return (NULL); 3104 3105 while (s_fgets(title, sizeof (title), fp) != NULL) { 3106 cp = strstr(title, "Solaris"); 3107 if (cp) 3108 break; 3109 } 3110 (void) fclose(fp); 3111 return (cp == NULL ? "Solaris" : cp); 3112 } 3113 3114 char * 3115 get_special(char *mountp) 3116 { 3117 FILE *mntfp; 3118 struct mnttab mp = {0}, mpref = {0}; 3119 3120 mntfp = fopen(MNTTAB, "r"); 3121 if (mntfp == NULL) { 3122 return (0); 3123 } 3124 3125 if (*mountp == '\0') 3126 mpref.mnt_mountp = "/"; 3127 else 3128 mpref.mnt_mountp = mountp; 3129 if (getmntany(mntfp, &mp, &mpref) != 0) { 3130 (void) fclose(mntfp); 3131 return (NULL); 3132 } 3133 (void) fclose(mntfp); 3134 3135 return (s_strdup(mp.mnt_special)); 3136 } 3137 3138 char * 3139 os_to_grubdisk(char *osdisk, int on_bootdev) 3140 { 3141 FILE *fp; 3142 char *grubdisk; 3143 3144 /* translate /dev/dsk name to grub disk name */ 3145 fp = open_diskmap(""); 3146 if (fp == NULL) { 3147 bam_error(DISKMAP_FAIL, osdisk); 3148 return (NULL); 3149 } 3150 grubdisk = get_grubdisk(osdisk, fp, on_bootdev); 3151 (void) fclose(fp); 3152 return (grubdisk); 3153 } 3154 3155 /* 3156 * Check if root is on the boot device 3157 * Return 0 (false) on error 3158 */ 3159 static int 3160 menu_on_bootdev(char *menu_root, FILE *fp) 3161 { 3162 int ret; 3163 char *grubhd, *bootp, *special; 3164 3165 special = get_special(menu_root); 3166 if (special == NULL) 3167 return (0); 3168 bootp = strstr(special, "p0:boot"); 3169 if (bootp) 3170 *bootp = '\0'; 3171 grubhd = get_grubdisk(special, fp, 1); 3172 free(special); 3173 3174 if (grubhd == NULL) 3175 return (0); 3176 ret = grubhd[3] == '0'; 3177 free(grubhd); 3178 return (ret); 3179 } 3180 3181 /* 3182 * look for matching bootadm entry with specified parameters 3183 * Here are the rules (based on existing usage): 3184 * - If title is specified, match on title only 3185 * - Else, match on grubdisk and module (don't care about kernel line). 3186 * note that, if root_opt is non-zero, the absence of root line is 3187 * considered a match. 3188 */ 3189 static entry_t * 3190 find_boot_entry(menu_t *mp, char *title, char *root, char *module, 3191 int root_opt, int *entry_num) 3192 { 3193 int i; 3194 line_t *lp; 3195 entry_t *ent; 3196 3197 /* find matching entry */ 3198 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) { 3199 lp = ent->start; 3200 3201 /* first line of entry must be bootadm comment */ 3202 lp = ent->start; 3203 if (lp->flags != BAM_COMMENT || 3204 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) { 3205 continue; 3206 } 3207 3208 /* advance to title line */ 3209 lp = lp->next; 3210 if (title) { 3211 if (lp->flags == BAM_TITLE && lp->arg && 3212 strcmp(lp->arg, title) == 0) 3213 break; 3214 continue; /* check title only */ 3215 } 3216 3217 lp = lp->next; /* advance to root line */ 3218 if (lp == NULL || strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3219 /* root command found, match grub disk */ 3220 if (strcmp(lp->arg, root) != 0) { 3221 continue; 3222 } 3223 lp = lp->next; /* advance to kernel line */ 3224 } else { 3225 /* no root command, see if root is optional */ 3226 if (root_opt == 0) { 3227 continue; 3228 } 3229 } 3230 3231 if (lp == NULL || lp->next == NULL) { 3232 continue; 3233 } 3234 3235 /* 3236 * Check for matching module entry (failsafe or normal). We 3237 * use a strncmp to match "module" or "module$", since we 3238 * don't know which one it should be. If it fails to match, 3239 * we go around the loop again. 3240 */ 3241 lp = lp->next; /* advance to module line */ 3242 if ((strncmp(lp->cmd, menu_cmds[MODULE_CMD], 3243 strlen(menu_cmds[MODULE_CMD])) != 0) || 3244 (strcmp(lp->arg, module) != 0)) { 3245 continue; 3246 } 3247 break; /* match found */ 3248 } 3249 3250 *entry_num = i; 3251 return (ent); 3252 } 3253 3254 static int 3255 update_boot_entry(menu_t *mp, char *title, char *root, char *kernel, 3256 char *module, int root_opt) 3257 { 3258 int i, change_kernel = 0; 3259 entry_t *ent; 3260 line_t *lp; 3261 char linebuf[BAM_MAXLINE]; 3262 3263 /* note: don't match on title, it's updated on upgrade */ 3264 ent = find_boot_entry(mp, NULL, root, module, root_opt, &i); 3265 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) { 3266 /* 3267 * We may be upgrading a kernel from multiboot to 3268 * directboot. Look for a multiboot entry. 3269 */ 3270 ent = find_boot_entry(mp, NULL, root, MULTI_BOOT_ARCHIVE, 3271 root_opt, &i); 3272 if (ent != NULL) { 3273 change_kernel = 1; 3274 } 3275 } 3276 if (ent == NULL) 3277 return (add_boot_entry(mp, title, root_opt ? NULL : root, 3278 kernel, module)); 3279 3280 /* replace title of exiting entry and delete root line */ 3281 lp = ent->start; 3282 lp = lp->next; /* title line */ 3283 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3284 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 3285 free(lp->arg); 3286 free(lp->line); 3287 lp->arg = s_strdup(title); 3288 lp->line = s_strdup(linebuf); 3289 3290 lp = lp->next; /* root line */ 3291 if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 3292 if (root_opt) { /* root line not needed */ 3293 line_t *tmp = lp; 3294 lp = lp->next; 3295 unlink_line(mp, tmp); 3296 line_free(tmp); 3297 } else 3298 lp = lp->next; 3299 } 3300 3301 if (change_kernel) { 3302 /* 3303 * We're upgrading from multiboot to directboot. 3304 */ 3305 if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) { 3306 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3307 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 3308 kernel); 3309 free(lp->arg); 3310 free(lp->line); 3311 lp->arg = s_strdup(kernel); 3312 lp->line = s_strdup(linebuf); 3313 lp = lp->next; 3314 } 3315 if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 3316 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 3317 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 3318 module); 3319 free(lp->arg); 3320 free(lp->line); 3321 lp->arg = s_strdup(module); 3322 lp->line = s_strdup(linebuf); 3323 lp = lp->next; 3324 } 3325 } 3326 return (i); 3327 } 3328 3329 /*ARGSUSED*/ 3330 static error_t 3331 update_entry(menu_t *mp, char *menu_root, char *opt) 3332 { 3333 FILE *fp; 3334 int entry; 3335 char *grubdisk, *title, *osdev, *osroot, *failsafe_kernel = NULL; 3336 struct stat sbuf; 3337 char failsafe[256]; 3338 3339 assert(mp); 3340 assert(opt); 3341 3342 osdev = strtok(opt, ","); 3343 osroot = strtok(NULL, ","); 3344 if (osroot == NULL) 3345 osroot = menu_root; 3346 title = get_title(osroot); 3347 3348 /* translate /dev/dsk name to grub disk name */ 3349 fp = open_diskmap(osroot); 3350 if (fp == NULL) { 3351 bam_error(DISKMAP_FAIL, osdev); 3352 return (BAM_ERROR); 3353 } 3354 grubdisk = get_grubdisk(osdev, fp, menu_on_bootdev(menu_root, fp)); 3355 (void) fclose(fp); 3356 if (grubdisk == NULL) { 3357 bam_error(DISKMAP_FAIL, osdev); 3358 return (BAM_ERROR); 3359 } 3360 3361 /* add the entry for normal Solaris */ 3362 if (bam_direct == BAM_DIRECT_DBOOT) { 3363 entry = update_boot_entry(mp, title, grubdisk, 3364 DIRECT_BOOT_KERNEL, DIRECT_BOOT_ARCHIVE, 3365 osroot == menu_root); 3366 } else { 3367 entry = update_boot_entry(mp, title, grubdisk, 3368 MULTI_BOOT, MULTI_BOOT_ARCHIVE, 3369 osroot == menu_root); 3370 } 3371 3372 /* add the entry for failsafe archive */ 3373 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, MINIROOT); 3374 if (stat(failsafe, &sbuf) == 0) { 3375 3376 /* Figure out where the kernel line should point */ 3377 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, 3378 DIRECT_BOOT_FAILSAFE_KERNEL); 3379 if (stat(failsafe, &sbuf) == 0) { 3380 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE; 3381 } else { 3382 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 3383 osroot, MULTI_BOOT_FAILSAFE); 3384 if (stat(failsafe, &sbuf) == 0) { 3385 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE; 3386 } 3387 } 3388 if (failsafe_kernel != NULL) { 3389 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubdisk, 3390 failsafe_kernel, MINIROOT, osroot == menu_root); 3391 } else { 3392 bam_error(NO_FAILSAFE_KERNEL); 3393 } 3394 } 3395 free(grubdisk); 3396 3397 if (entry == BAM_ERROR) { 3398 return (BAM_ERROR); 3399 } 3400 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3401 return (BAM_WRITE); 3402 } 3403 3404 static char * 3405 read_grub_root(void) 3406 { 3407 FILE *fp; 3408 struct stat sb; 3409 char buf[BAM_MAXLINE]; 3410 char *rootstr; 3411 3412 if (stat(GRUB_slice, &sb) != 0) { 3413 bam_error(MISSING_SLICE_FILE, GRUB_slice, strerror(errno)); 3414 return (NULL); 3415 } 3416 3417 if (stat(GRUB_root, &sb) != 0) { 3418 bam_error(MISSING_ROOT_FILE, GRUB_root, strerror(errno)); 3419 return (NULL); 3420 } 3421 3422 fp = fopen(GRUB_root, "r"); 3423 if (fp == NULL) { 3424 bam_error(OPEN_FAIL, GRUB_root, strerror(errno)); 3425 return (NULL); 3426 } 3427 3428 if (s_fgets(buf, sizeof (buf), fp) == NULL) { 3429 bam_error(EMPTY_FILE, GRUB_root, strerror(errno)); 3430 (void) fclose(fp); 3431 return (NULL); 3432 } 3433 3434 /* 3435 * Copy buf here as check below may trash the buffer 3436 */ 3437 rootstr = s_strdup(buf); 3438 3439 if (s_fgets(buf, sizeof (buf), fp) != NULL) { 3440 bam_error(BAD_ROOT_FILE, GRUB_root); 3441 free(rootstr); 3442 rootstr = NULL; 3443 } 3444 3445 (void) fclose(fp); 3446 3447 return (rootstr); 3448 } 3449 3450 static void 3451 save_default_entry(menu_t *mp, const char *which) 3452 { 3453 int lineNum, entryNum; 3454 int entry = 0; /* default is 0 */ 3455 char linebuf[BAM_MAXLINE]; 3456 line_t *lp = mp->curdefault; 3457 3458 if (mp->start) { 3459 lineNum = mp->end->lineNum; 3460 entryNum = mp->end->entryNum; 3461 } else { 3462 lineNum = LINE_INIT; 3463 entryNum = ENTRY_INIT; 3464 } 3465 3466 if (lp) 3467 entry = s_strtol(lp->arg); 3468 3469 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry); 3470 line_parser(mp, linebuf, &lineNum, &entryNum); 3471 } 3472 3473 static void 3474 restore_default_entry(menu_t *mp, const char *which, line_t *lp) 3475 { 3476 int entry; 3477 char *str; 3478 3479 if (lp == NULL) 3480 return; /* nothing to restore */ 3481 3482 str = lp->arg + strlen(which); 3483 entry = s_strtol(str); 3484 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3485 3486 /* delete saved old default line */ 3487 unlink_line(mp, lp); 3488 line_free(lp); 3489 } 3490 3491 /* 3492 * This function is for supporting reboot with args. 3493 * The opt value can be: 3494 * NULL delete temp entry, if present 3495 * entry=# switches default entry to 1 3496 * else treated as boot-args and setup a temperary menu entry 3497 * and make it the default 3498 */ 3499 #define REBOOT_TITLE "Solaris_reboot_transient" 3500 3501 /*ARGSUSED*/ 3502 static error_t 3503 update_temp(menu_t *mp, char *menupath, char *opt) 3504 { 3505 int entry; 3506 char *grubdisk, *rootdev, *path, *opt_ptr; 3507 char kernbuf[BUFSIZ]; 3508 char args_buf[BUFSIZ]; 3509 struct stat sb; 3510 3511 assert(mp); 3512 3513 /* If no option, delete exiting reboot menu entry */ 3514 if (opt == NULL) { 3515 entry_t *ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL, 3516 0, &entry); 3517 if (ent == NULL) /* not found is ok */ 3518 return (BAM_SUCCESS); 3519 (void) do_delete(mp, entry); 3520 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault); 3521 mp->olddefault = NULL; 3522 return (BAM_WRITE); 3523 } 3524 3525 /* if entry= is specified, set the default entry */ 3526 if (strncmp(opt, "entry=", strlen("entry=")) == 0 && 3527 selector(mp, opt, &entry, NULL) == BAM_SUCCESS) { 3528 /* this is entry=# option */ 3529 return (set_global(mp, menu_cmds[DEFAULT_CMD], entry)); 3530 } 3531 3532 /* 3533 * add a new menu entry base on opt and make it the default 3534 */ 3535 grubdisk = NULL; 3536 if (stat(GRUB_slice, &sb) != 0) { 3537 /* 3538 * 1. First get root disk name from mnttab 3539 * 2. Translate disk name to grub name 3540 * 3. Add the new menu entry 3541 */ 3542 rootdev = get_special("/"); 3543 if (rootdev) { 3544 grubdisk = os_to_grubdisk(rootdev, 1); 3545 free(rootdev); 3546 } 3547 } else { 3548 /* 3549 * This is an LU BE. The GRUB_root file 3550 * contains entry for GRUB's "root" cmd. 3551 */ 3552 grubdisk = read_grub_root(); 3553 } 3554 if (grubdisk == NULL) { 3555 bam_error(REBOOT_WITH_ARGS_FAILED); 3556 return (BAM_ERROR); 3557 } 3558 3559 /* add an entry for Solaris reboot */ 3560 if (bam_direct == BAM_DIRECT_DBOOT) { 3561 if (opt[0] == '-') { 3562 /* It's an option - first see if boot-file is set */ 3563 if (set_kernel(mp, KERNEL_CMD, NULL, kernbuf, BUFSIZ) 3564 != BAM_SUCCESS) 3565 return (BAM_ERROR); 3566 if (kernbuf[0] == '\0') 3567 (void) strncpy(kernbuf, DIRECT_BOOT_KERNEL, 3568 BUFSIZ); 3569 (void) strlcat(kernbuf, " ", BUFSIZ); 3570 (void) strlcat(kernbuf, opt, BUFSIZ); 3571 } else if (opt[0] == '/') { 3572 /* It's a full path, so write it out. */ 3573 (void) strlcpy(kernbuf, opt, BUFSIZ); 3574 3575 /* 3576 * If someone runs: 3577 * 3578 * # eeprom boot-args='-kd' 3579 * # reboot /platform/i86pc/kernel/unix 3580 * 3581 * we want to use the boot-args as part of the boot 3582 * line. On the other hand, if someone runs: 3583 * 3584 * # reboot "/platform/i86pc/kernel/unix -kd" 3585 * 3586 * we don't need to mess with boot-args. If there's 3587 * no space in the options string, assume we're in the 3588 * first case. 3589 */ 3590 if (strchr(opt, ' ') == NULL) { 3591 if (set_kernel(mp, ARGS_CMD, NULL, args_buf, 3592 BUFSIZ) != BAM_SUCCESS) 3593 return (BAM_ERROR); 3594 3595 if (args_buf[0] != '\0') { 3596 (void) strlcat(kernbuf, " ", BUFSIZ); 3597 (void) strlcat(kernbuf, args_buf, 3598 BUFSIZ); 3599 } 3600 } 3601 } else { 3602 /* 3603 * It may be a partial path, or it may be a partial 3604 * path followed by options. Assume that only options 3605 * follow a space. If someone sends us a kernel path 3606 * that includes a space, they deserve to be broken. 3607 */ 3608 opt_ptr = strchr(opt, ' '); 3609 if (opt_ptr != NULL) { 3610 *opt_ptr = '\0'; 3611 } 3612 3613 path = expand_path(opt); 3614 if (path != NULL) { 3615 (void) strlcpy(kernbuf, path, BUFSIZ); 3616 free(path); 3617 3618 /* 3619 * If there were options given, use those. 3620 * Otherwise, copy over the default options. 3621 */ 3622 if (opt_ptr != NULL) { 3623 /* Restore the space in opt string */ 3624 *opt_ptr = ' '; 3625 (void) strlcat(kernbuf, opt_ptr, 3626 BUFSIZ); 3627 } else { 3628 if (set_kernel(mp, ARGS_CMD, NULL, 3629 args_buf, BUFSIZ) != BAM_SUCCESS) 3630 return (BAM_ERROR); 3631 3632 if (args_buf[0] != '\0') { 3633 (void) strlcat(kernbuf, " ", 3634 BUFSIZ); 3635 (void) strlcat(kernbuf, 3636 args_buf, BUFSIZ); 3637 } 3638 } 3639 } else { 3640 bam_error(UNKNOWN_KERNEL, opt); 3641 bam_print_stderr(UNKNOWN_KERNEL_REBOOT); 3642 return (BAM_ERROR); 3643 } 3644 } 3645 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3646 NULL); 3647 } else { 3648 (void) snprintf(kernbuf, sizeof (kernbuf), "%s %s", 3649 MULTI_BOOT, opt); 3650 entry = add_boot_entry(mp, REBOOT_TITLE, grubdisk, kernbuf, 3651 MULTI_BOOT_ARCHIVE); 3652 } 3653 free(grubdisk); 3654 3655 if (entry == BAM_ERROR) { 3656 bam_error(REBOOT_WITH_ARGS_FAILED); 3657 return (BAM_ERROR); 3658 } 3659 3660 save_default_entry(mp, BAM_OLDDEF); 3661 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 3662 return (BAM_WRITE); 3663 } 3664 3665 static error_t 3666 set_global(menu_t *mp, char *globalcmd, int val) 3667 { 3668 line_t *lp, *found, *last; 3669 char *cp, *str; 3670 char prefix[BAM_MAXLINE]; 3671 size_t len; 3672 3673 assert(mp); 3674 assert(globalcmd); 3675 3676 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) { 3677 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) { 3678 (void) snprintf(prefix, sizeof (prefix), "%d", val); 3679 bam_error(INVALID_ENTRY, prefix); 3680 return (BAM_ERROR); 3681 } 3682 } 3683 3684 found = last = NULL; 3685 for (lp = mp->start; lp; lp = lp->next) { 3686 if (lp->flags != BAM_GLOBAL) 3687 continue; 3688 3689 last = lp; /* track the last global found */ 3690 3691 if (lp->cmd == NULL) { 3692 bam_error(NO_CMD, lp->lineNum); 3693 continue; 3694 } 3695 if (strcmp(globalcmd, lp->cmd) != 0) 3696 continue; 3697 3698 if (found) { 3699 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 3700 } 3701 found = lp; 3702 } 3703 3704 if (found == NULL) { 3705 lp = s_calloc(1, sizeof (line_t)); 3706 if (last == NULL) { 3707 lp->next = mp->start; 3708 mp->start = lp; 3709 mp->end = (mp->end) ? mp->end : lp; 3710 } else { 3711 lp->next = last->next; 3712 last->next = lp; 3713 if (lp->next == NULL) 3714 mp->end = lp; 3715 } 3716 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */ 3717 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]); 3718 len += 10; /* val < 10 digits */ 3719 lp->line = s_calloc(1, len); 3720 (void) snprintf(lp->line, len, "%s%s%d", 3721 globalcmd, menu_cmds[SEP_CMD], val); 3722 return (BAM_WRITE); 3723 } 3724 3725 /* 3726 * We are changing an existing entry. Retain any prefix whitespace, 3727 * but overwrite everything else. This preserves tabs added for 3728 * readability. 3729 */ 3730 str = found->line; 3731 cp = prefix; 3732 while (*str == ' ' || *str == '\t') 3733 *(cp++) = *(str++); 3734 *cp = '\0'; /* Terminate prefix */ 3735 len = strlen(prefix) + strlen(globalcmd); 3736 len += strlen(menu_cmds[SEP_CMD]) + 10; 3737 3738 free(found->line); 3739 found->line = s_calloc(1, len); 3740 (void) snprintf(found->line, len, 3741 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val); 3742 3743 return (BAM_WRITE); /* need a write to menu */ 3744 } 3745 3746 /* 3747 * partial_path may be anything like "kernel/unix" or "kmdb". Try to 3748 * expand it to a full unix path. The calling function is expected to 3749 * output a message if an error occurs and NULL is returned. 3750 */ 3751 static char * 3752 expand_path(const char *partial_path) 3753 { 3754 int new_path_len; 3755 char *new_path, new_path2[PATH_MAX]; 3756 struct stat sb; 3757 3758 new_path_len = strlen(partial_path) + 64; 3759 new_path = s_calloc(1, new_path_len); 3760 3761 /* First, try the simplest case - something like "kernel/unix" */ 3762 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s", 3763 partial_path); 3764 if (stat(new_path, &sb) == 0) { 3765 return (new_path); 3766 } 3767 3768 if (strcmp(partial_path, "kmdb") == 0) { 3769 (void) snprintf(new_path, new_path_len, "%s -k", 3770 DIRECT_BOOT_KERNEL); 3771 return (new_path); 3772 } 3773 3774 /* 3775 * We've quickly reached unsupported usage. Try once more to 3776 * see if we were just given a glom name. 3777 */ 3778 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix", 3779 partial_path); 3780 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix", 3781 partial_path); 3782 if (stat(new_path, &sb) == 0) { 3783 if (stat(new_path2, &sb) == 0) { 3784 /* 3785 * We matched both, so we actually 3786 * want to write the $ISADIR version. 3787 */ 3788 (void) snprintf(new_path, new_path_len, 3789 "/platform/i86pc/kernel/%s/$ISADIR/unix", 3790 partial_path); 3791 } 3792 return (new_path); 3793 } 3794 3795 free(new_path); 3796 return (NULL); 3797 } 3798 3799 /* 3800 * The kernel cmd and arg have been changed, so 3801 * check whether the archive line needs to change. 3802 */ 3803 static void 3804 set_archive_line(entry_t *entryp, line_t *kernelp) 3805 { 3806 line_t *lp = entryp->start; 3807 char *new_archive; 3808 menu_cmd_t m_cmd; 3809 3810 for (; lp != NULL; lp = lp->next) { 3811 if (strncmp(lp->cmd, menu_cmds[MODULE_CMD], 3812 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) { 3813 break; 3814 } 3815 if (lp == entryp->end) 3816 return; 3817 } 3818 if (lp == NULL) 3819 return; 3820 3821 if (strstr(kernelp->arg, "$ISADIR") != NULL) { 3822 new_archive = DIRECT_BOOT_ARCHIVE; 3823 m_cmd = MODULE_DOLLAR_CMD; 3824 } else if (strstr(kernelp->arg, "amd64") != NULL) { 3825 new_archive = DIRECT_BOOT_ARCHIVE_64; 3826 m_cmd = MODULE_CMD; 3827 } else { 3828 new_archive = DIRECT_BOOT_ARCHIVE_32; 3829 m_cmd = MODULE_CMD; 3830 } 3831 3832 if (strcmp(lp->arg, new_archive) == 0) 3833 return; 3834 3835 if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) { 3836 free(lp->cmd); 3837 lp->cmd = s_strdup(menu_cmds[m_cmd]); 3838 } 3839 3840 free(lp->arg); 3841 lp->arg = s_strdup(new_archive); 3842 update_line(lp); 3843 } 3844 3845 /* 3846 * Title for an entry to set properties that once went in bootenv.rc. 3847 */ 3848 #define BOOTENV_RC_TITLE "Solaris bootenv rc" 3849 3850 /* 3851 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments 3852 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length 3853 * string, reset the value to the default. If path is a non-zero-length 3854 * string, set the kernel or arguments. 3855 */ 3856 static error_t 3857 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize) 3858 { 3859 int entryNum, rv = BAM_SUCCESS, free_new_path = 0; 3860 entry_t *entryp; 3861 line_t *ptr, *kernelp; 3862 char *new_arg, *old_args, *space; 3863 char *grubdisk, *rootdev, *new_path; 3864 char old_space; 3865 size_t old_kernel_len, new_str_len; 3866 struct stat sb; 3867 3868 assert(bufsize > 0); 3869 3870 ptr = kernelp = NULL; 3871 new_arg = old_args = space = NULL; 3872 grubdisk = rootdev = new_path = NULL; 3873 buf[0] = '\0'; 3874 3875 if (bam_direct != BAM_DIRECT_DBOOT) { 3876 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args"); 3877 return (BAM_ERROR); 3878 } 3879 3880 /* 3881 * If a user changed the default entry to a non-bootadm controlled 3882 * one, we don't want to mess with it. Just print an error and 3883 * return. 3884 */ 3885 if (mp->curdefault) { 3886 entryNum = s_strtol(mp->curdefault->arg); 3887 for (entryp = mp->entries; entryp; entryp = entryp->next) { 3888 if (entryp->entryNum == entryNum) 3889 break; 3890 } 3891 if ((entryp != NULL) && 3892 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) { 3893 bam_error(DEFAULT_NOT_BAM); 3894 return (BAM_ERROR); 3895 } 3896 } 3897 3898 entryNum = -1; 3899 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, 0, 3900 &entryNum); 3901 3902 if (entryp != NULL) { 3903 for (ptr = entryp->start; ptr && ptr != entryp->end; 3904 ptr = ptr->next) { 3905 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD], 3906 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) { 3907 kernelp = ptr; 3908 break; 3909 } 3910 } 3911 if (kernelp == NULL) { 3912 bam_error(NO_KERNEL, entryNum); 3913 return (BAM_ERROR); 3914 } 3915 3916 old_kernel_len = strcspn(kernelp->arg, " \t"); 3917 space = old_args = kernelp->arg + old_kernel_len; 3918 while ((*old_args == ' ') || (*old_args == '\t')) 3919 old_args++; 3920 } 3921 3922 if (path == NULL) { 3923 /* Simply report what was found */ 3924 if (kernelp == NULL) 3925 return (BAM_SUCCESS); 3926 3927 if (optnum == ARGS_CMD) { 3928 if (old_args[0] != '\0') 3929 (void) strlcpy(buf, old_args, bufsize); 3930 } else { 3931 /* 3932 * We need to print the kernel, so we just turn the 3933 * first space into a '\0' and print the beginning. 3934 * We don't print anything if it's the default kernel. 3935 */ 3936 old_space = *space; 3937 *space = '\0'; 3938 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) 3939 (void) strlcpy(buf, kernelp->arg, bufsize); 3940 *space = old_space; 3941 } 3942 return (BAM_SUCCESS); 3943 } 3944 3945 /* 3946 * First, check if we're resetting an entry to the default. 3947 */ 3948 if ((path[0] == '\0') || 3949 ((optnum == KERNEL_CMD) && 3950 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) { 3951 if ((entryp == NULL) || (kernelp == NULL)) { 3952 /* No previous entry, it's already the default */ 3953 return (BAM_SUCCESS); 3954 } 3955 3956 /* 3957 * Check if we can delete the entry. If we're resetting the 3958 * kernel command, and the args is already empty, or if we're 3959 * resetting the args command, and the kernel is already the 3960 * default, we can restore the old default and delete the entry. 3961 */ 3962 if (((optnum == KERNEL_CMD) && 3963 ((old_args == NULL) || (old_args[0] == '\0'))) || 3964 ((optnum == ARGS_CMD) && 3965 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL, 3966 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) { 3967 kernelp = NULL; 3968 (void) do_delete(mp, entryNum); 3969 restore_default_entry(mp, BAM_OLD_RC_DEF, 3970 mp->old_rc_default); 3971 mp->old_rc_default = NULL; 3972 rv = BAM_WRITE; 3973 goto done; 3974 } 3975 3976 if (optnum == KERNEL_CMD) { 3977 /* 3978 * At this point, we've already checked that old_args 3979 * and entryp are valid pointers. The "+ 2" is for 3980 * a space a the string termination character. 3981 */ 3982 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) + 3983 strlen(old_args) + 2; 3984 new_arg = s_calloc(1, new_str_len); 3985 (void) snprintf(new_arg, new_str_len, "%s %s", 3986 DIRECT_BOOT_KERNEL, old_args); 3987 free(kernelp->arg); 3988 kernelp->arg = new_arg; 3989 3990 /* 3991 * We have changed the kernel line, so we may need 3992 * to update the archive line as well. 3993 */ 3994 set_archive_line(entryp, kernelp); 3995 } else { 3996 /* 3997 * We're resetting the boot args to nothing, so 3998 * we only need to copy the kernel. We've already 3999 * checked that the kernel is not the default. 4000 */ 4001 new_arg = s_calloc(1, old_kernel_len + 1); 4002 (void) snprintf(new_arg, old_kernel_len + 1, "%s", 4003 kernelp->arg); 4004 free(kernelp->arg); 4005 kernelp->arg = new_arg; 4006 } 4007 rv = BAM_WRITE; 4008 goto done; 4009 } 4010 4011 /* 4012 * Expand the kernel file to a full path, if necessary 4013 */ 4014 if ((optnum == KERNEL_CMD) && (path[0] != '/')) { 4015 new_path = expand_path(path); 4016 if (new_path == NULL) { 4017 bam_error(UNKNOWN_KERNEL, path); 4018 return (BAM_ERROR); 4019 } 4020 free_new_path = 1; 4021 } else { 4022 new_path = path; 4023 free_new_path = 0; 4024 } 4025 4026 /* 4027 * At this point, we know we're setting a new value. First, take care 4028 * of the case where there was no previous entry. 4029 */ 4030 if (entryp == NULL) { 4031 /* Similar to code in update_temp */ 4032 if (stat(GRUB_slice, &sb) != 0) { 4033 /* 4034 * 1. First get root disk name from mnttab 4035 * 2. Translate disk name to grub name 4036 * 3. Add the new menu entry 4037 */ 4038 rootdev = get_special("/"); 4039 if (rootdev) { 4040 grubdisk = os_to_grubdisk(rootdev, 1); 4041 free(rootdev); 4042 } 4043 } else { 4044 /* 4045 * This is an LU BE. The GRUB_root file 4046 * contains entry for GRUB's "root" cmd. 4047 */ 4048 grubdisk = read_grub_root(); 4049 } 4050 if (grubdisk == NULL) { 4051 bam_error(REBOOT_WITH_ARGS_FAILED); 4052 rv = BAM_ERROR; 4053 goto done; 4054 } 4055 if (optnum == KERNEL_CMD) { 4056 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 4057 grubdisk, new_path, NULL); 4058 } else { 4059 new_str_len = strlen(DIRECT_BOOT_KERNEL) + 4060 strlen(path) + 8; 4061 new_arg = s_calloc(1, new_str_len); 4062 4063 (void) snprintf(new_arg, new_str_len, "%s %s", 4064 DIRECT_BOOT_KERNEL, path); 4065 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 4066 grubdisk, new_arg, DIRECT_BOOT_ARCHIVE); 4067 } 4068 save_default_entry(mp, BAM_OLD_RC_DEF); 4069 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entryNum); 4070 rv = BAM_WRITE; 4071 goto done; 4072 } 4073 4074 /* 4075 * There was already an bootenv entry which we need to edit. 4076 */ 4077 if (optnum == KERNEL_CMD) { 4078 new_str_len = strlen(new_path) + strlen(old_args) + 2; 4079 new_arg = s_calloc(1, new_str_len); 4080 (void) snprintf(new_arg, new_str_len, "%s %s", new_path, 4081 old_args); 4082 free(kernelp->arg); 4083 kernelp->arg = new_arg; 4084 4085 /* 4086 * If we have changed the kernel line, we may need to update 4087 * the archive line as well. 4088 */ 4089 set_archive_line(entryp, kernelp); 4090 } else { 4091 new_str_len = old_kernel_len + strlen(path) + 8; 4092 new_arg = s_calloc(1, new_str_len); 4093 (void) strncpy(new_arg, kernelp->arg, old_kernel_len); 4094 (void) strlcat(new_arg, " ", new_str_len); 4095 (void) strlcat(new_arg, path, new_str_len); 4096 free(kernelp->arg); 4097 kernelp->arg = new_arg; 4098 } 4099 rv = BAM_WRITE; 4100 4101 done: 4102 if ((rv == BAM_WRITE) && kernelp) 4103 update_line(kernelp); 4104 if (free_new_path) 4105 free(new_path); 4106 return (rv); 4107 } 4108 4109 /*ARGSUSED*/ 4110 static error_t 4111 set_option(menu_t *mp, char *menu_path, char *opt) 4112 { 4113 int optnum, optval; 4114 char *val; 4115 char buf[BUFSIZ] = ""; 4116 error_t rv; 4117 4118 assert(mp); 4119 assert(opt); 4120 4121 val = strchr(opt, '='); 4122 if (val != NULL) { 4123 *val = '\0'; 4124 } 4125 4126 if (strcmp(opt, "default") == 0) { 4127 optnum = DEFAULT_CMD; 4128 } else if (strcmp(opt, "timeout") == 0) { 4129 optnum = TIMEOUT_CMD; 4130 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) { 4131 optnum = KERNEL_CMD; 4132 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) { 4133 optnum = ARGS_CMD; 4134 } else { 4135 bam_error(INVALID_ENTRY, opt); 4136 return (BAM_ERROR); 4137 } 4138 4139 /* 4140 * kernel and args are allowed without "=new_value" strings. All 4141 * others cause errors 4142 */ 4143 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) { 4144 bam_error(INVALID_ENTRY, opt); 4145 return (BAM_ERROR); 4146 } else if (val != NULL) { 4147 *val = '='; 4148 } 4149 4150 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) { 4151 rv = set_kernel(mp, optnum, val ? val + 1 : NULL, buf, BUFSIZ); 4152 if ((rv == BAM_SUCCESS) && (buf[0] != '\0')) 4153 (void) printf("%s\n", buf); 4154 return (rv); 4155 } else { 4156 optval = s_strtol(val + 1); 4157 return (set_global(mp, menu_cmds[optnum], optval)); 4158 } 4159 } 4160 4161 /* 4162 * The quiet argument suppresses messages. This is used 4163 * when invoked in the context of other commands (e.g. list_entry) 4164 */ 4165 static error_t 4166 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet) 4167 { 4168 line_t *lp; 4169 char *arg; 4170 int done, ret = BAM_SUCCESS; 4171 4172 assert(mp); 4173 assert(menu_path); 4174 assert(globalcmd); 4175 4176 if (mp->start == NULL) { 4177 if (!quiet) 4178 bam_error(NO_MENU, menu_path); 4179 return (BAM_ERROR); 4180 } 4181 4182 done = 0; 4183 for (lp = mp->start; lp; lp = lp->next) { 4184 if (lp->flags != BAM_GLOBAL) 4185 continue; 4186 4187 if (lp->cmd == NULL) { 4188 if (!quiet) 4189 bam_error(NO_CMD, lp->lineNum); 4190 continue; 4191 } 4192 4193 if (strcmp(globalcmd, lp->cmd) != 0) 4194 continue; 4195 4196 /* Found global. Check for duplicates */ 4197 if (done && !quiet) { 4198 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 4199 ret = BAM_ERROR; 4200 } 4201 4202 arg = lp->arg ? lp->arg : ""; 4203 bam_print(GLOBAL_CMD, globalcmd, arg); 4204 done = 1; 4205 } 4206 4207 if (!done && bam_verbose) 4208 bam_print(NO_ENTRY, globalcmd); 4209 4210 return (ret); 4211 } 4212 4213 static error_t 4214 menu_write(char *root, menu_t *mp) 4215 { 4216 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start)); 4217 } 4218 4219 static void 4220 line_free(line_t *lp) 4221 { 4222 if (lp == NULL) 4223 return; 4224 4225 if (lp->cmd) 4226 free(lp->cmd); 4227 if (lp->sep) 4228 free(lp->sep); 4229 if (lp->arg) 4230 free(lp->arg); 4231 if (lp->line) 4232 free(lp->line); 4233 free(lp); 4234 } 4235 4236 static void 4237 linelist_free(line_t *start) 4238 { 4239 line_t *lp; 4240 4241 while (start) { 4242 lp = start; 4243 start = start->next; 4244 line_free(lp); 4245 } 4246 } 4247 4248 static void 4249 filelist_free(filelist_t *flistp) 4250 { 4251 linelist_free(flistp->head); 4252 flistp->head = NULL; 4253 flistp->tail = NULL; 4254 } 4255 4256 static void 4257 menu_free(menu_t *mp) 4258 { 4259 entry_t *ent, *tmp; 4260 assert(mp); 4261 4262 if (mp->start) 4263 linelist_free(mp->start); 4264 ent = mp->entries; 4265 while (ent) { 4266 tmp = ent; 4267 ent = tmp->next; 4268 free(tmp); 4269 } 4270 4271 free(mp); 4272 } 4273 4274 /* 4275 * Utility routines 4276 */ 4277 4278 4279 /* 4280 * Returns 0 on success 4281 * Any other value indicates an error 4282 */ 4283 static int 4284 exec_cmd(char *cmdline, char *output, int64_t osize) 4285 { 4286 char buf[BUFSIZ]; 4287 int ret; 4288 FILE *ptr; 4289 size_t len; 4290 sigset_t set; 4291 void (*disp)(int); 4292 4293 /* 4294 * For security 4295 * - only absolute paths are allowed 4296 * - set IFS to space and tab 4297 */ 4298 if (*cmdline != '/') { 4299 bam_error(ABS_PATH_REQ, cmdline); 4300 return (-1); 4301 } 4302 (void) putenv("IFS= \t"); 4303 4304 /* 4305 * We may have been exec'ed with SIGCHLD blocked 4306 * unblock it here 4307 */ 4308 (void) sigemptyset(&set); 4309 (void) sigaddset(&set, SIGCHLD); 4310 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 4311 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno)); 4312 return (-1); 4313 } 4314 4315 /* 4316 * Set SIGCHLD disposition to SIG_DFL for popen/pclose 4317 */ 4318 disp = sigset(SIGCHLD, SIG_DFL); 4319 if (disp == SIG_ERR) { 4320 bam_error(FAILED_SIG, strerror(errno)); 4321 return (-1); 4322 } 4323 if (disp == SIG_HOLD) { 4324 bam_error(BLOCKED_SIG, cmdline); 4325 return (-1); 4326 } 4327 4328 ptr = popen(cmdline, "r"); 4329 if (ptr == NULL) { 4330 bam_error(POPEN_FAIL, cmdline, strerror(errno)); 4331 return (-1); 4332 } 4333 4334 /* 4335 * If we simply do a pclose() following a popen(), pclose() 4336 * will close the reader end of the pipe immediately even 4337 * if the child process has not started/exited. pclose() 4338 * does wait for cmd to terminate before returning though. 4339 * When the executed command writes its output to the pipe 4340 * there is no reader process and the command dies with 4341 * SIGPIPE. To avoid this we read repeatedly until read 4342 * terminates with EOF. This indicates that the command 4343 * (writer) has closed the pipe and we can safely do a 4344 * pclose(). 4345 * 4346 * Since pclose() does wait for the command to exit, 4347 * we can safely reap the exit status of the command 4348 * from the value returned by pclose() 4349 */ 4350 while (fgets(buf, sizeof (buf), ptr) != NULL) { 4351 /* if (bam_verbose) XXX */ 4352 bam_print(PRINT_NO_NEWLINE, buf); 4353 if (output && osize > 0) { 4354 (void) snprintf(output, osize, "%s", buf); 4355 len = strlen(buf); 4356 output += len; 4357 osize -= len; 4358 } 4359 } 4360 4361 ret = pclose(ptr); 4362 if (ret == -1) { 4363 bam_error(PCLOSE_FAIL, cmdline, strerror(errno)); 4364 return (-1); 4365 } 4366 4367 if (WIFEXITED(ret)) { 4368 return (WEXITSTATUS(ret)); 4369 } else { 4370 bam_error(EXEC_FAIL, cmdline, ret); 4371 return (-1); 4372 } 4373 } 4374 4375 /* 4376 * Since this function returns -1 on error 4377 * it cannot be used to convert -1. However, 4378 * that is sufficient for what we need. 4379 */ 4380 static long 4381 s_strtol(char *str) 4382 { 4383 long l; 4384 char *res = NULL; 4385 4386 if (str == NULL) { 4387 return (-1); 4388 } 4389 4390 errno = 0; 4391 l = strtol(str, &res, 10); 4392 if (errno || *res != '\0') { 4393 return (-1); 4394 } 4395 4396 return (l); 4397 } 4398 4399 /* 4400 * Wrapper around fputs, that adds a newline (since fputs doesn't) 4401 */ 4402 static int 4403 s_fputs(char *str, FILE *fp) 4404 { 4405 char linebuf[BAM_MAXLINE]; 4406 4407 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str); 4408 return (fputs(linebuf, fp)); 4409 } 4410 4411 /* 4412 * Wrapper around fgets, that strips newlines returned by fgets 4413 */ 4414 char * 4415 s_fgets(char *buf, int buflen, FILE *fp) 4416 { 4417 int n; 4418 4419 buf = fgets(buf, buflen, fp); 4420 if (buf) { 4421 n = strlen(buf); 4422 if (n == buflen - 1 && buf[n-1] != '\n') 4423 bam_error(TOO_LONG, buflen - 1, buf); 4424 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1]; 4425 } 4426 4427 return (buf); 4428 } 4429 4430 void * 4431 s_calloc(size_t nelem, size_t sz) 4432 { 4433 void *ptr; 4434 4435 ptr = calloc(nelem, sz); 4436 if (ptr == NULL) { 4437 bam_error(NO_MEM, nelem*sz); 4438 bam_exit(1); 4439 } 4440 return (ptr); 4441 } 4442 4443 void * 4444 s_realloc(void *ptr, size_t sz) 4445 { 4446 ptr = realloc(ptr, sz); 4447 if (ptr == NULL) { 4448 bam_error(NO_MEM, sz); 4449 bam_exit(1); 4450 } 4451 return (ptr); 4452 } 4453 4454 static char * 4455 s_strdup(char *str) 4456 { 4457 char *ptr; 4458 4459 if (str == NULL) 4460 return (NULL); 4461 4462 ptr = strdup(str); 4463 if (ptr == NULL) { 4464 bam_error(NO_MEM, strlen(str) + 1); 4465 bam_exit(1); 4466 } 4467 return (ptr); 4468 } 4469 4470 /* 4471 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients) 4472 * Returns 0 otherwise 4473 */ 4474 static int 4475 is_amd64(void) 4476 { 4477 static int amd64 = -1; 4478 char isabuf[257]; /* from sysinfo(2) manpage */ 4479 4480 if (amd64 != -1) 4481 return (amd64); 4482 4483 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 && 4484 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) 4485 amd64 = 1; 4486 else if (strstr(isabuf, "i386") == NULL) 4487 amd64 = 1; /* diskless server */ 4488 else 4489 amd64 = 0; 4490 4491 return (amd64); 4492 } 4493 4494 static void 4495 append_to_flist(filelist_t *flistp, char *s) 4496 { 4497 line_t *lp; 4498 4499 lp = s_calloc(1, sizeof (line_t)); 4500 lp->line = s_strdup(s); 4501 if (flistp->head == NULL) 4502 flistp->head = lp; 4503 else 4504 flistp->tail->next = lp; 4505 flistp->tail = lp; 4506 } 4507