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