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