8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / boot / bootadm / bootadm.c
blobb2bfc4e849e70cf6c6b47a12843b792f2480e61a
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 * Copyright (c) 2015 by Delphix. All rights reserved.
26 * Copyright 2016 Toomas Soome <tsoome@me.com>
27 * Copyright 2016 Nexenta Systems, Inc.
31 * bootadm(1M) is a new utility for managing bootability of
32 * Solaris *Newboot* environments. It has two primary tasks:
33 * - Allow end users to manage bootability of Newboot Solaris instances
34 * - Provide services to other subsystems in Solaris (primarily Install)
37 /* Headers */
38 #include <stdio.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <alloca.h>
46 #include <stdarg.h>
47 #include <limits.h>
48 #include <signal.h>
49 #include <sys/wait.h>
50 #include <sys/mnttab.h>
51 #include <sys/mntent.h>
52 #include <sys/statvfs.h>
53 #include <libnvpair.h>
54 #include <ftw.h>
55 #include <fcntl.h>
56 #include <strings.h>
57 #include <utime.h>
58 #include <sys/systeminfo.h>
59 #include <sys/dktp/fdisk.h>
60 #include <sys/param.h>
61 #include <dirent.h>
62 #include <ctype.h>
63 #include <libgen.h>
64 #include <sys/sysmacros.h>
65 #include <sys/elf.h>
66 #include <libscf.h>
67 #include <zlib.h>
68 #include <sys/lockfs.h>
69 #include <sys/filio.h>
70 #include <libbe.h>
71 #include <deflt.h>
72 #ifdef i386
73 #include <libfdisk.h>
74 #endif
76 #if !defined(_OBP)
77 #include <sys/ucode.h>
78 #endif
80 #include <pwd.h>
81 #include <grp.h>
82 #include <device_info.h>
83 #include <sys/vtoc.h>
84 #include <sys/efi_partition.h>
85 #include <regex.h>
86 #include <locale.h>
87 #include <sys/mkdev.h>
89 #include "bootadm.h"
91 #ifndef TEXT_DOMAIN
92 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
93 #endif /* TEXT_DOMAIN */
95 /* Type definitions */
97 /* Primary subcmds */
98 typedef enum {
99 BAM_MENU = 3,
100 BAM_ARCHIVE,
101 BAM_INSTALL
102 } subcmd_t;
104 #define LINE_INIT 0 /* lineNum initial value */
105 #define ENTRY_INIT -1 /* entryNum initial value */
106 #define ALL_ENTRIES -2 /* selects all boot entries */
108 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
109 #define PARTNO_EFI -2 /* EFI partition table found */
111 #define GRUB_DIR "/boot/grub"
112 #define GRUB_STAGE2 GRUB_DIR "/stage2"
113 #define GRUB_MENU "/boot/grub/menu.lst"
114 #define MENU_TMP "/boot/grub/menu.lst.tmp"
115 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
116 #define RAMDISK_SPECIAL "/devices/ramdisk"
117 #define STUBBOOT "/stubboot"
118 #define MULTIBOOT "/platform/i86pc/multiboot"
119 #define GRUBSIGN_DIR "/boot/grub/bootsign"
120 #define GRUBSIGN_BACKUP "/etc/bootsign"
121 #define GRUBSIGN_UFS_PREFIX "rootfs"
122 #define GRUBSIGN_ZFS_PREFIX "pool_"
123 #define GRUBSIGN_LU_PREFIX "BE_"
124 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
125 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
127 /* BE defaults */
128 #define BE_DEFAULTS "/etc/default/be"
129 #define BE_DFLT_BE_HAS_GRUB "BE_HAS_GRUB="
131 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
133 /* lock related */
134 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
135 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
137 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
138 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
139 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
140 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
142 #define GRUB_slice "/etc/lu/GRUB_slice"
143 #define GRUB_root "/etc/lu/GRUB_root"
144 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
145 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
146 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
147 #define LULIB "/usr/lib/lu/lulib"
148 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
149 #define CKSUM "/usr/bin/cksum"
150 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
151 #define BOOTADM "/sbin/bootadm"
153 #define INSTALLGRUB "/sbin/installgrub"
154 #define STAGE1 "/boot/grub/stage1"
155 #define STAGE2 "/boot/grub/stage2"
158 * Default file attributes
160 #define DEFAULT_DEV_MODE 0644 /* default permissions */
161 #define DEFAULT_DEV_UID 0 /* user root */
162 #define DEFAULT_DEV_GID 3 /* group sys */
165 * Menu related
166 * menu_cmd_t and menu_cmds must be kept in sync
168 char *menu_cmds[] = {
169 "default", /* DEFAULT_CMD */
170 "timeout", /* TIMEOUT_CMD */
171 "title", /* TITLE_CMD */
172 "root", /* ROOT_CMD */
173 "kernel", /* KERNEL_CMD */
174 "kernel$", /* KERNEL_DOLLAR_CMD */
175 "module", /* MODULE_CMD */
176 "module$", /* MODULE_DOLLAR_CMD */
177 " ", /* SEP_CMD */
178 "#", /* COMMENT_CMD */
179 "chainloader", /* CHAINLOADER_CMD */
180 "args", /* ARGS_CMD */
181 "findroot", /* FINDROOT_CMD */
182 "bootfs", /* BOOTFS_CMD */
183 NULL
186 #define OPT_ENTRY_NUM "entry"
189 * exec_cmd related
191 typedef struct {
192 line_t *head;
193 line_t *tail;
194 } filelist_t;
196 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
197 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
199 #define FILE_STAT "boot/solaris/filestat.ramdisk"
200 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
201 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
202 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
204 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
206 /* Globals */
207 int bam_verbose;
208 int bam_force;
209 int bam_debug;
210 static char *prog;
211 static subcmd_t bam_cmd;
212 char *bam_root;
213 int bam_rootlen;
214 static int bam_root_readonly;
215 int bam_alt_root;
216 static int bam_extend = 0;
217 static int bam_purge = 0;
218 static char *bam_subcmd;
219 static char *bam_opt;
220 static char **bam_argv;
221 static char *bam_pool;
222 static int bam_argc;
223 static int bam_check;
224 static int bam_saved_check;
225 static int bam_smf_check;
226 static int bam_lock_fd = -1;
227 static int bam_zfs;
228 static int bam_mbr;
229 char rootbuf[PATH_MAX] = "/";
230 static int bam_update_all;
231 static int bam_alt_platform;
232 static char *bam_platform;
233 static char *bam_home_env = NULL;
235 /* function prototypes */
236 static void parse_args_internal(int, char *[]);
237 static void parse_args(int, char *argv[]);
238 static error_t bam_menu(char *, char *, int, char *[]);
239 static error_t bam_install(char *, char *);
240 static error_t bam_archive(char *, char *);
242 static void bam_lock(void);
243 static void bam_unlock(void);
245 static int exec_cmd(char *, filelist_t *);
246 static error_t read_globals(menu_t *, char *, char *, int);
247 static int menu_on_bootdisk(char *os_root, char *menu_root);
248 static menu_t *menu_read(char *);
249 static error_t menu_write(char *, menu_t *);
250 static void linelist_free(line_t *);
251 static void menu_free(menu_t *);
252 static void filelist_free(filelist_t *);
253 static error_t list2file(char *, char *, char *, line_t *);
254 static error_t list_entry(menu_t *, char *, char *);
255 static error_t list_setting(menu_t *, char *, char *);
256 static error_t delete_all_entries(menu_t *, char *, char *);
257 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
258 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
260 static error_t install_bootloader(void);
261 static error_t update_archive(char *, char *);
262 static error_t list_archive(char *, char *);
263 static error_t update_all(char *, char *);
264 static error_t read_list(char *, filelist_t *);
265 static error_t set_option(menu_t *, char *, char *);
266 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
267 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
268 static char *expand_path(const char *);
270 static long s_strtol(char *);
271 static int s_fputs(char *, FILE *);
273 static int is_amd64(void);
274 static char *get_machine(void);
275 static void append_to_flist(filelist_t *, char *);
276 static int ufs_add_to_sign_list(char *sign);
277 static error_t synchronize_BE_menu(void);
279 #if !defined(_OBP)
280 static void ucode_install();
281 #endif
283 /* Menu related sub commands */
284 static subcmd_defn_t menu_subcmds[] = {
285 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
286 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
287 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
288 "update_entry", OPT_REQ, update_entry, 0, /* menu */
289 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
290 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
291 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
292 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
293 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
294 NULL, 0, NULL, 0 /* must be last */
297 /* Archive related sub commands */
298 static subcmd_defn_t arch_subcmds[] = {
299 "update", OPT_ABSENT, update_archive, 0, /* PUB */
300 "update_all", OPT_ABSENT, update_all, 0, /* PVT */
301 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */
302 NULL, 0, NULL, 0 /* must be last */
305 /* Install related sub commands */
306 static subcmd_defn_t inst_subcmds[] = {
307 "install_bootloader", OPT_ABSENT, install_bootloader, 0, /* PUB */
308 NULL, 0, NULL, 0 /* must be last */
311 enum dircache_copy_opt {
312 FILE32 = 0,
313 FILE64,
314 CACHEDIR_NUM
318 * Directory specific flags:
319 * NEED_UPDATE : the specified archive needs to be updated
320 * NO_MULTI : don't extend the specified archive, but recreate it
322 #define NEED_UPDATE 0x00000001
323 #define NO_MULTI 0x00000002
325 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
326 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
327 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
329 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
330 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
331 #define get_count(id) (walk_arg.dirinfo[id].count)
332 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
333 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
336 * dirinfo_t (specific cache directory information):
337 * cdir_path: path to the archive cache directory
338 * update_path: path to the update directory (contains the files that will be
339 * used to extend the archive)
340 * has_dir: the specified cache directory is active
341 * count: the number of files to update
342 * flags: directory specific flags
344 typedef struct _dirinfo {
345 char cdir_path[PATH_MAX];
346 char update_path[PATH_MAX];
347 int has_dir;
348 int count;
349 int flags;
350 } dirinfo_t;
353 * Update flags:
354 * NEED_CACHE_DIR : cache directory is missing and needs to be created
355 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
356 * UPDATE_ERROR : an error occourred while traversing the list of files
357 * RDONLY_FSCHK : the target filesystem is read-only
358 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
360 #define NEED_CACHE_DIR 0x00000001
361 #define IS_SPARC_TARGET 0x00000002
362 #define UPDATE_ERROR 0x00000004
363 #define RDONLY_FSCHK 0x00000008
364 #define INVALIDATE_CACHE 0x00000010
366 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
367 #define set_flag(flag) (walk_arg.update_flags |= flag)
368 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
371 * struct walk_arg :
372 * update_flags: flags related to the current updating process
373 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
374 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
376 static struct {
377 int update_flags;
378 nvlist_t *new_nvlp;
379 nvlist_t *old_nvlp;
380 FILE *sparcfile;
381 dirinfo_t dirinfo[CACHEDIR_NUM];
382 } walk_arg;
384 struct safefile {
385 char *name;
386 struct safefile *next;
389 static struct safefile *safefiles = NULL;
392 * svc:/system/filesystem/usr:default service checks for this file and
393 * does a boot archive update and then reboot the system.
395 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
398 * svc:/system/boot-archive-update:default checks for this file and
399 * updates the boot archive.
401 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
403 /* Thanks growisofs */
404 #define CD_BLOCK ((off64_t)2048)
405 #define VOLDESC_OFF 16
406 #define DVD_BLOCK (32*1024)
407 #define MAX_IVDs 16
409 struct iso_pdesc {
410 unsigned char type [1];
411 unsigned char id [5];
412 unsigned char void1 [80-5-1];
413 unsigned char volume_space_size [8];
414 unsigned char void2 [2048-80-8];
418 * COUNT_MAX: maximum number of changed files to justify a multisession update
419 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
420 * update
422 #define COUNT_MAX 50
423 #define BA_SIZE_MAX (50 * 1024 * 1024)
425 #define bam_nowrite() (bam_check || bam_smf_check)
427 static int sync_menu = 1; /* whether we need to sync the BE menus */
429 static void
430 usage(void)
432 (void) fprintf(stderr, "USAGE:\n");
434 /* archive usage */
435 (void) fprintf(stderr,
436 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
437 (void) fprintf(stderr,
438 "\t%s list-archive [-R altroot [-p platform]]\n", prog);
439 #if defined(_OBP)
440 (void) fprintf(stderr,
441 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
442 #else
443 (void) fprintf(stderr,
444 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
445 #endif
446 #if !defined(_OBP)
447 /* x86 only */
448 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
449 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
450 #endif
454 * Best effort attempt to restore the $HOME value.
456 static void
457 restore_env()
459 char home_env[PATH_MAX];
461 if (bam_home_env) {
462 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
463 bam_home_env);
464 (void) putenv(home_env);
469 #define SLEEP_TIME 5
470 #define MAX_TRIES 4
473 * Sanitize the environment in which bootadm will execute its sub-processes
474 * (ex. mkisofs). This is done to prevent those processes from attempting
475 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
476 * or, potentially, insecure.
478 static void
479 sanitize_env()
481 int stry = 0;
483 /* don't depend on caller umask */
484 (void) umask(0022);
486 /* move away from a potential unsafe current working directory */
487 while (chdir("/") == -1) {
488 if (errno != EINTR) {
489 bam_print("WARNING: unable to chdir to /");
490 break;
494 bam_home_env = getenv("HOME");
495 while (bam_home_env != NULL && putenv("HOME=/") == -1) {
496 if (errno == ENOMEM) {
497 /* retry no more than MAX_TRIES times */
498 if (++stry > MAX_TRIES) {
499 bam_print("WARNING: unable to recover from "
500 "system memory pressure... aborting \n");
501 bam_exit(EXIT_FAILURE);
503 /* memory is tight, try to sleep */
504 bam_print("Attempting to recover from memory pressure: "
505 "sleeping for %d seconds\n", SLEEP_TIME * stry);
506 (void) sleep(SLEEP_TIME * stry);
507 } else {
508 bam_print("WARNING: unable to sanitize HOME\n");
514 main(int argc, char *argv[])
516 error_t ret = BAM_SUCCESS;
518 (void) setlocale(LC_ALL, "");
519 (void) textdomain(TEXT_DOMAIN);
521 if ((prog = strrchr(argv[0], '/')) == NULL) {
522 prog = argv[0];
523 } else {
524 prog++;
527 INJECT_ERROR1("ASSERT_ON", assert(0))
529 sanitize_env();
531 parse_args(argc, argv);
533 switch (bam_cmd) {
534 case BAM_MENU:
535 if (is_grub(bam_alt_root ? bam_root : "/")) {
536 ret = bam_menu(bam_subcmd, bam_opt,
537 bam_argc, bam_argv);
538 } else {
539 ret = bam_loader_menu(bam_subcmd, bam_opt,
540 bam_argc, bam_argv);
542 break;
543 case BAM_ARCHIVE:
544 ret = bam_archive(bam_subcmd, bam_opt);
545 break;
546 case BAM_INSTALL:
547 ret = bam_install(bam_subcmd, bam_opt);
548 break;
549 default:
550 usage();
551 bam_exit(1);
554 if (ret != BAM_SUCCESS)
555 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
557 bam_unlock();
558 return (0);
562 * Equivalence of public and internal commands:
563 * update-archive -- -a update
564 * list-archive -- -a list
565 * set-menu -- -m set_option
566 * list-menu -- -m list_entry
567 * update-menu -- -m update_entry
568 * install-bootloader -- -i install_bootloader
570 static struct cmd_map {
571 char *bam_cmdname;
572 int bam_cmd;
573 char *bam_subcmd;
574 } cmd_map[] = {
575 { "update-archive", BAM_ARCHIVE, "update"},
576 { "list-archive", BAM_ARCHIVE, "list"},
577 { "set-menu", BAM_MENU, "set_option"},
578 { "list-menu", BAM_MENU, "list_entry"},
579 { "update-menu", BAM_MENU, "update_entry"},
580 { "install-bootloader", BAM_INSTALL, "install_bootloader"},
581 { NULL, 0, NULL}
585 * Commands syntax published in bootadm(1M) are parsed here
587 static void
588 parse_args(int argc, char *argv[])
590 struct cmd_map *cmp = cmd_map;
592 /* command conforming to the final spec */
593 if (argc > 1 && argv[1][0] != '-') {
595 * Map commands to internal table.
597 while (cmp->bam_cmdname) {
598 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
599 bam_cmd = cmp->bam_cmd;
600 bam_subcmd = cmp->bam_subcmd;
601 break;
603 cmp++;
605 if (cmp->bam_cmdname == NULL) {
606 usage();
607 bam_exit(1);
609 argc--;
610 argv++;
613 parse_args_internal(argc, argv);
617 * A combination of public and private commands are parsed here.
618 * The internal syntax and the corresponding functionality are:
619 * -a update -- update-archive
620 * -a list -- list-archive
621 * -a update-all -- (reboot to sync all mnted OS archive)
622 * -i install_bootloader -- install-bootloader
623 * -m update_entry -- update-menu
624 * -m list_entry -- list-menu
625 * -m update_temp -- (reboot -- [boot-args])
626 * -m delete_all_entries -- (called from install)
627 * -m enable_hypervisor [args] -- cvt_to_hyper
628 * -m disable_hypervisor -- cvt_to_metal
629 * -m list_setting [entry] [value] -- list_setting
631 * A set of private flags is there too:
632 * -F -- purge the cache directories and rebuild them
633 * -e -- use the (faster) archive update approach (used by
634 * reboot)
636 static void
637 parse_args_internal(int argc, char *argv[])
639 int c, error;
640 extern char *optarg;
641 extern int optind, opterr;
642 #if defined(_OBP)
643 const char *optstring = "a:d:fi:m:no:veFCR:p:P:XZ";
644 #else
645 const char *optstring = "a:d:fi:m:no:veFCMR:p:P:XZ";
646 #endif
648 /* Suppress error message from getopt */
649 opterr = 0;
651 error = 0;
652 while ((c = getopt(argc, argv, optstring)) != -1) {
653 switch (c) {
654 case 'a':
655 if (bam_cmd) {
656 error = 1;
657 bam_error(
658 _("multiple commands specified: -%c\n"), c);
660 bam_cmd = BAM_ARCHIVE;
661 bam_subcmd = optarg;
662 break;
663 case 'd':
664 if (bam_debug) {
665 error = 1;
666 bam_error(
667 _("duplicate options specified: -%c\n"), c);
669 bam_debug = s_strtol(optarg);
670 break;
671 case 'f':
672 bam_force = 1;
673 break;
674 case 'F':
675 bam_purge = 1;
676 break;
677 case 'i':
678 if (bam_cmd) {
679 error = 1;
680 bam_error(
681 _("multiple commands specified: -%c\n"), c);
683 bam_cmd = BAM_INSTALL;
684 bam_subcmd = optarg;
685 break;
686 case 'm':
687 if (bam_cmd) {
688 error = 1;
689 bam_error(
690 _("multiple commands specified: -%c\n"), c);
692 bam_cmd = BAM_MENU;
693 bam_subcmd = optarg;
694 break;
695 #if !defined(_OBP)
696 case 'M':
697 bam_mbr = 1;
698 break;
699 #endif
700 case 'n':
701 bam_check = 1;
703 * We save the original value of bam_check. The new
704 * approach in case of a read-only filesystem is to
705 * behave as a check, so we need a way to restore the
706 * original value after the evaluation of the read-only
707 * filesystem has been done.
708 * Even if we don't allow at the moment a check with
709 * update_all, this approach is more robust than
710 * simply resetting bam_check to zero.
712 bam_saved_check = 1;
713 break;
714 case 'o':
715 if (bam_opt) {
716 error = 1;
717 bam_error(
718 _("duplicate options specified: -%c\n"), c);
720 bam_opt = optarg;
721 break;
722 case 'v':
723 bam_verbose = 1;
724 break;
725 case 'C':
726 bam_smf_check = 1;
727 break;
728 case 'P':
729 if (bam_pool != NULL) {
730 error = 1;
731 bam_error(
732 _("duplicate options specified: -%c\n"), c);
734 bam_pool = optarg;
735 break;
736 case 'R':
737 if (bam_root) {
738 error = 1;
739 bam_error(
740 _("duplicate options specified: -%c\n"), c);
741 break;
742 } else if (realpath(optarg, rootbuf) == NULL) {
743 error = 1;
744 bam_error(_("cannot resolve path %s: %s\n"),
745 optarg, strerror(errno));
746 break;
748 bam_alt_root = 1;
749 bam_root = rootbuf;
750 bam_rootlen = strlen(rootbuf);
751 break;
752 case 'p':
753 bam_alt_platform = 1;
754 bam_platform = optarg;
755 if ((strcmp(bam_platform, "i86pc") != 0) &&
756 (strcmp(bam_platform, "sun4u") != 0) &&
757 (strcmp(bam_platform, "sun4v") != 0)) {
758 error = 1;
759 bam_error(_("invalid platform %s - must be "
760 "one of sun4u, sun4v or i86pc\n"),
761 bam_platform);
763 break;
764 case 'X':
765 bam_is_hv = BAM_HV_PRESENT;
766 break;
767 case 'Z':
768 bam_zfs = 1;
769 break;
770 case 'e':
771 bam_extend = 1;
772 break;
773 case '?':
774 error = 1;
775 bam_error(_("invalid option or missing option "
776 "argument: -%c\n"), optopt);
777 break;
778 default :
779 error = 1;
780 bam_error(_("invalid option or missing option "
781 "argument: -%c\n"), c);
782 break;
787 * An alternate platform requires an alternate root
789 if (bam_alt_platform && bam_alt_root == 0) {
790 usage();
791 bam_exit(0);
795 * A command option must be specfied
797 if (!bam_cmd) {
798 if (bam_opt && strcmp(bam_opt, "all") == 0) {
799 usage();
800 bam_exit(0);
802 bam_error(_("a command option must be specified\n"));
803 error = 1;
806 if (error) {
807 usage();
808 bam_exit(1);
811 if (optind > argc) {
812 bam_error(_("Internal error: %s\n"), "parse_args");
813 bam_exit(1);
814 } else if (optind < argc) {
815 bam_argv = &argv[optind];
816 bam_argc = argc - optind;
820 * mbr and pool are options for install_bootloader
822 if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
823 usage();
824 bam_exit(0);
828 * -n implies verbose mode
830 if (bam_check)
831 bam_verbose = 1;
834 error_t
835 check_subcmd_and_options(
836 char *subcmd,
837 char *opt,
838 subcmd_defn_t *table,
839 error_t (**fp)())
841 int i;
843 if (subcmd == NULL) {
844 bam_error(_("this command requires a sub-command\n"));
845 return (BAM_ERROR);
848 if (strcmp(subcmd, "set_option") == 0) {
849 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
850 bam_error(_("missing argument for sub-command\n"));
851 usage();
852 return (BAM_ERROR);
853 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
854 bam_error(_("invalid trailing arguments\n"));
855 usage();
856 return (BAM_ERROR);
858 } else if (strcmp(subcmd, "update_all") == 0) {
860 * The only option we accept for the "update_all"
861 * subcmd is "fastboot".
863 if (bam_argc > 1 || (bam_argc == 1 &&
864 strcmp(bam_argv[0], "fastboot") != 0)) {
865 bam_error(_("invalid trailing arguments\n"));
866 usage();
867 return (BAM_ERROR);
869 if (bam_argc == 1)
870 sync_menu = 0;
871 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
872 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
874 * Of the remaining subcommands, only "enable_hypervisor" and
875 * "list_setting" take trailing arguments.
877 bam_error(_("invalid trailing arguments\n"));
878 usage();
879 return (BAM_ERROR);
882 if (bam_root == NULL) {
883 bam_root = rootbuf;
884 bam_rootlen = 1;
887 /* verify that subcmd is valid */
888 for (i = 0; table[i].subcmd != NULL; i++) {
889 if (strcmp(table[i].subcmd, subcmd) == 0)
890 break;
893 if (table[i].subcmd == NULL) {
894 bam_error(_("invalid sub-command specified: %s\n"), subcmd);
895 return (BAM_ERROR);
898 if (table[i].unpriv == 0 && geteuid() != 0) {
899 bam_error(_("you must be root to run this command\n"));
900 return (BAM_ERROR);
904 * Currently only privileged commands need a lock
906 if (table[i].unpriv == 0)
907 bam_lock();
909 /* subcmd verifies that opt is appropriate */
910 if (table[i].option != OPT_OPTIONAL) {
911 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
912 if (opt)
913 bam_error(_("this sub-command (%s) does not "
914 "take options\n"), subcmd);
915 else
916 bam_error(_("an option is required for this "
917 "sub-command: %s\n"), subcmd);
918 return (BAM_ERROR);
922 *fp = table[i].handler;
924 return (BAM_SUCCESS);
928 * NOTE: A single "/" is also considered a trailing slash and will
929 * be deleted.
931 void
932 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
934 size_t dstlen;
936 assert(src);
937 assert(dst);
939 (void) strlcpy(dst, src, dstsize);
941 dstlen = strlen(dst);
942 if (dst[dstlen - 1] == '/') {
943 dst[dstlen - 1] = '\0';
947 static int
948 is_safe_exec(char *path)
950 struct stat sb;
952 if (lstat(path, &sb) != 0) {
953 bam_error(_("stat of file failed: %s: %s\n"), path,
954 strerror(errno));
955 return (BAM_ERROR);
958 if (!S_ISREG(sb.st_mode)) {
959 bam_error(_("%s is not a regular file, skipping\n"), path);
960 return (BAM_ERROR);
963 if (sb.st_uid != getuid()) {
964 bam_error(_("%s is not owned by %d, skipping\n"),
965 path, getuid());
966 return (BAM_ERROR);
969 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
970 bam_error(_("%s is others or group writable, skipping\n"),
971 path);
972 return (BAM_ERROR);
975 return (BAM_SUCCESS);
978 static error_t
979 list_setting(menu_t *mp, char *which, char *setting)
981 line_t *lp;
982 entry_t *ent;
984 char *p = which;
985 int entry;
987 int found;
989 assert(which);
990 assert(setting);
992 if (*which != NULL) {
994 * If "which" is not a number, assume it's a setting we want
995 * to look for and so set up the routine to look for "which"
996 * in the default entry.
998 while (*p != NULL)
999 if (!(isdigit((int)*p++))) {
1000 setting = which;
1001 which = mp->curdefault->arg;
1002 break;
1004 } else {
1005 which = mp->curdefault->arg;
1008 entry = atoi(which);
1010 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1011 ent = ent->next)
1014 if (!ent) {
1015 bam_error(_("no matching entry found\n"));
1016 return (BAM_ERROR);
1019 found = (*setting == NULL);
1021 for (lp = ent->start; lp != NULL; lp = lp->next) {
1022 if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
1023 bam_print("%s\n", lp->line);
1024 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1025 bam_print("%s\n", lp->arg);
1026 found = 1;
1029 if (lp == ent->end)
1030 break;
1033 if (!found) {
1034 bam_error(_("no matching entry found\n"));
1035 return (BAM_ERROR);
1038 return (BAM_SUCCESS);
1041 static error_t
1042 install_bootloader(void)
1044 nvlist_t *nvl;
1045 uint16_t flags = 0;
1046 int found = 0;
1047 struct extmnttab mnt;
1048 struct stat statbuf = {0};
1049 be_node_list_t *be_nodes, *node;
1050 FILE *fp;
1051 char *root_ds = NULL;
1052 int ret = BAM_ERROR;
1054 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1055 bam_error(_("out of memory\n"));
1056 return (ret);
1060 * if bam_alt_root is set, the stage files are used from alt root.
1061 * if pool is set, the target devices are pool devices, stage files
1062 * are read from pool bootfs unless alt root is set.
1064 * use arguments as targets, stage files are from alt or current root
1065 * if no arguments and no pool, install on current boot pool.
1068 if (bam_alt_root) {
1069 if (stat(bam_root, &statbuf) != 0) {
1070 bam_error(_("stat of file failed: %s: %s\n"), bam_root,
1071 strerror(errno));
1072 goto done;
1074 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1075 bam_error(_("failed to open file: %s: %s\n"),
1076 MNTTAB, strerror(errno));
1077 goto done;
1079 resetmnttab(fp);
1080 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1081 if (mnt.mnt_major == major(statbuf.st_dev) &&
1082 mnt.mnt_minor == minor(statbuf.st_dev)) {
1083 found = 1;
1084 root_ds = strdup(mnt.mnt_special);
1085 break;
1088 (void) fclose(fp);
1090 if (found == 0) {
1091 bam_error(_("alternate root %s not in mnttab\n"),
1092 bam_root);
1093 goto done;
1095 if (root_ds == NULL) {
1096 bam_error(_("out of memory\n"));
1097 goto done;
1100 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1101 bam_error(_("No BE's found\n"));
1102 goto done;
1104 for (node = be_nodes; node != NULL; node = node->be_next_node)
1105 if (strcmp(root_ds, node->be_root_ds) == 0)
1106 break;
1108 if (node == NULL)
1109 bam_error(_("BE (%s) does not exist\n"), root_ds);
1111 free(root_ds);
1112 root_ds = NULL;
1113 if (node == NULL) {
1114 be_free_list(be_nodes);
1115 goto done;
1117 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1118 node->be_node_name);
1119 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1120 node->be_root_ds);
1121 be_free_list(be_nodes);
1122 if (ret != 0) {
1123 ret = BAM_ERROR;
1124 goto done;
1128 if (bam_force)
1129 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1130 if (bam_mbr)
1131 flags |= BE_INSTALLBOOT_FLAG_MBR;
1132 if (bam_verbose)
1133 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1135 if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1136 bam_error(_("out of memory\n"));
1137 ret = BAM_ERROR;
1138 goto done;
1142 * if altroot was set, we got be name and be root, only need
1143 * to set pool name as target.
1144 * if no altroot, need to find be name and root from pool.
1146 if (bam_pool != NULL) {
1147 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1148 if (ret != 0) {
1149 ret = BAM_ERROR;
1150 goto done;
1152 if (found) {
1153 ret = be_installboot(nvl);
1154 if (ret != 0)
1155 ret = BAM_ERROR;
1156 goto done;
1160 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1161 bam_error(_("No BE's found\n"));
1162 ret = BAM_ERROR;
1163 goto done;
1166 if (bam_pool != NULL) {
1168 * find active be_node in bam_pool
1170 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1171 if (strcmp(bam_pool, node->be_rpool) != 0)
1172 continue;
1173 if (node->be_active_on_boot)
1174 break;
1176 if (node == NULL) {
1177 bam_error(_("No active BE in %s\n"), bam_pool);
1178 be_free_list(be_nodes);
1179 ret = BAM_ERROR;
1180 goto done;
1182 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1183 node->be_node_name);
1184 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1185 node->be_root_ds);
1186 be_free_list(be_nodes);
1187 if (ret != 0) {
1188 ret = BAM_ERROR;
1189 goto done;
1191 ret = be_installboot(nvl);
1192 if (ret != 0)
1193 ret = BAM_ERROR;
1194 goto done;
1198 * get dataset for "/" and fill up the args.
1200 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1201 bam_error(_("failed to open file: %s: %s\n"),
1202 MNTTAB, strerror(errno));
1203 ret = BAM_ERROR;
1204 be_free_list(be_nodes);
1205 goto done;
1207 resetmnttab(fp);
1208 found = 0;
1209 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1210 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1211 found = 1;
1212 root_ds = strdup(mnt.mnt_special);
1213 break;
1216 (void) fclose(fp);
1218 if (found == 0) {
1219 bam_error(_("alternate root %s not in mnttab\n"), "/");
1220 ret = BAM_ERROR;
1221 be_free_list(be_nodes);
1222 goto done;
1224 if (root_ds == NULL) {
1225 bam_error(_("out of memory\n"));
1226 ret = BAM_ERROR;
1227 be_free_list(be_nodes);
1228 goto done;
1231 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1232 if (strcmp(root_ds, node->be_root_ds) == 0)
1233 break;
1236 if (node == NULL) {
1237 bam_error(_("No such BE: %s\n"), root_ds);
1238 free(root_ds);
1239 be_free_list(be_nodes);
1240 ret = BAM_ERROR;
1241 goto done;
1243 free(root_ds);
1245 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1246 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1247 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1248 be_free_list(be_nodes);
1250 if (ret != 0)
1251 ret = BAM_ERROR;
1252 else
1253 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1254 done:
1255 nvlist_free(nvl);
1257 return (ret);
1260 static error_t
1261 bam_install(char *subcmd, char *opt)
1263 error_t (*f)(void);
1266 * Check arguments
1268 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1269 BAM_ERROR)
1270 return (BAM_ERROR);
1272 return (f());
1275 static error_t
1276 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1278 error_t ret;
1279 char menu_path[PATH_MAX];
1280 char clean_menu_root[PATH_MAX];
1281 char path[PATH_MAX];
1282 menu_t *menu;
1283 char menu_root[PATH_MAX];
1284 struct stat sb;
1285 error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1286 char *special = NULL;
1287 char *pool = NULL;
1288 zfs_mnted_t zmnted;
1289 char *zmntpt = NULL;
1290 char *osdev;
1291 char *osroot;
1292 const char *fcn = "bam_menu()";
1295 * Menu sub-command only applies to GRUB (i.e. x86)
1297 if (!is_grub(bam_alt_root ? bam_root : "/")) {
1298 bam_error(_("not a GRUB 0.97 based Illumos instance. "
1299 "Operation not supported\n"));
1300 return (BAM_ERROR);
1304 * Check arguments
1306 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1307 if (ret == BAM_ERROR) {
1308 return (BAM_ERROR);
1311 assert(bam_root);
1313 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1314 osdev = osroot = NULL;
1316 if (strcmp(subcmd, "update_entry") == 0) {
1317 assert(opt);
1319 osdev = strtok(opt, ",");
1320 assert(osdev);
1321 osroot = strtok(NULL, ",");
1322 if (osroot) {
1323 /* fixup bam_root so that it points at osroot */
1324 if (realpath(osroot, rootbuf) == NULL) {
1325 bam_error(_("cannot resolve path %s: %s\n"),
1326 osroot, strerror(errno));
1327 return (BAM_ERROR);
1329 bam_alt_root = 1;
1330 bam_root = rootbuf;
1331 bam_rootlen = strlen(rootbuf);
1336 * We support menu on PCFS (under certain conditions), but
1337 * not the OS root
1339 if (is_pcfs(bam_root)) {
1340 bam_error(_("root <%s> on PCFS is not supported\n"), bam_root);
1341 return (BAM_ERROR);
1344 if (stat(menu_root, &sb) == -1) {
1345 bam_error(_("cannot find GRUB menu\n"));
1346 return (BAM_ERROR);
1349 BAM_DPRINTF(("%s: menu root is %s\n", fcn, menu_root));
1352 * We no longer use the GRUB slice file. If it exists, then
1353 * the user is doing something that is unsupported (such as
1354 * standard upgrading an old Live Upgrade BE). If that
1355 * happens, mimic existing behavior i.e. pretend that it is
1356 * not a BE. Emit a warning though.
1358 if (bam_alt_root) {
1359 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1360 GRUB_slice);
1361 } else {
1362 (void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1365 if (bam_verbose && stat(path, &sb) == 0)
1366 bam_error(_("unsupported GRUB slice file (%s) exists - "
1367 "ignoring.\n"), path);
1369 if (is_zfs(menu_root)) {
1370 assert(strcmp(menu_root, bam_root) == 0);
1371 special = get_special(menu_root);
1372 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1373 if (special == NULL) {
1374 bam_error(_("cant find special file for "
1375 "mount-point %s\n"), menu_root);
1376 return (BAM_ERROR);
1378 pool = strtok(special, "/");
1379 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1380 if (pool == NULL) {
1381 free(special);
1382 bam_error(_("cant find pool for mount-point %s\n"),
1383 menu_root);
1384 return (BAM_ERROR);
1386 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
1388 zmntpt = mount_top_dataset(pool, &zmnted);
1389 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1390 if (zmntpt == NULL) {
1391 bam_error(_("cannot mount pool dataset for pool: %s\n"),
1392 pool);
1393 free(special);
1394 return (BAM_ERROR);
1396 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
1398 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1399 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
1402 elide_trailing_slash(menu_root, clean_menu_root,
1403 sizeof (clean_menu_root));
1405 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
1407 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1408 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1410 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
1413 * If listing the menu, display the menu location
1415 if (strcmp(subcmd, "list_entry") == 0)
1416 bam_print(_("the location for the active GRUB menu is: %s\n"),
1417 menu_path);
1419 if ((menu = menu_read(menu_path)) == NULL) {
1420 bam_error(_("cannot find GRUB menu file: %s\n"), menu_path);
1421 free(special);
1423 return (BAM_ERROR);
1427 * We already checked the following case in
1428 * check_subcmd_and_suboptions() above. Complete the
1429 * final step now.
1431 if (strcmp(subcmd, "set_option") == 0) {
1432 assert(largc == 1 && largv[0] && largv[1] == NULL);
1433 opt = largv[0];
1434 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1435 (strcmp(subcmd, "list_setting") != 0)) {
1436 assert(largc == 0 && largv == NULL);
1439 ret = get_boot_cap(bam_root);
1440 if (ret != BAM_SUCCESS) {
1441 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1442 goto out;
1446 * Once the sub-cmd handler has run
1447 * only the line field is guaranteed to have valid values
1449 if (strcmp(subcmd, "update_entry") == 0) {
1450 ret = f(menu, menu_root, osdev);
1451 } else if (strcmp(subcmd, "upgrade") == 0) {
1452 ret = f(menu, bam_root, menu_root);
1453 } else if (strcmp(subcmd, "list_entry") == 0) {
1454 ret = f(menu, menu_path, opt);
1455 } else if (strcmp(subcmd, "list_setting") == 0) {
1456 ret = f(menu, ((largc > 0) ? largv[0] : ""),
1457 ((largc > 1) ? largv[1] : ""));
1458 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1459 if (is_sparc()) {
1460 bam_error(_("%s operation unsupported on SPARC "
1461 "machines\n"), subcmd);
1462 ret = BAM_ERROR;
1463 } else {
1464 ret = f(menu, bam_root, NULL);
1466 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1467 if (is_sparc()) {
1468 bam_error(_("%s operation unsupported on SPARC "
1469 "machines\n"), subcmd);
1470 ret = BAM_ERROR;
1471 } else {
1472 char *extra_args = NULL;
1475 * Compress all arguments passed in the largv[] array
1476 * into one string that can then be appended to the
1477 * end of the kernel$ string the routine to enable the
1478 * hypervisor will build.
1480 * This allows the caller to supply arbitrary unparsed
1481 * arguments, such as dom0 memory settings or APIC
1482 * options.
1484 * This concatenation will be done without ANY syntax
1485 * checking whatsoever, so it's the responsibility of
1486 * the caller to make sure the arguments are valid and
1487 * do not duplicate arguments the conversion routines
1488 * may create.
1490 if (largc > 0) {
1491 int extra_len, i;
1493 for (extra_len = 0, i = 0; i < largc; i++)
1494 extra_len += strlen(largv[i]);
1497 * Allocate space for argument strings,
1498 * intervening spaces and terminating NULL.
1500 extra_args = alloca(extra_len + largc);
1502 (void) strcpy(extra_args, largv[0]);
1504 for (i = 1; i < largc; i++) {
1505 (void) strcat(extra_args, " ");
1506 (void) strcat(extra_args, largv[i]);
1510 ret = f(menu, bam_root, extra_args);
1512 } else
1513 ret = f(menu, NULL, opt);
1515 if (ret == BAM_WRITE) {
1516 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
1517 fcn, clean_menu_root));
1518 ret = menu_write(clean_menu_root, menu);
1521 out:
1522 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1523 assert((is_zfs(menu_root)) ^ (pool == NULL));
1524 if (pool) {
1525 (void) umount_top_dataset(pool, zmnted, zmntpt);
1526 free(special);
1528 menu_free(menu);
1529 return (ret);
1533 static error_t
1534 bam_archive(
1535 char *subcmd,
1536 char *opt)
1538 error_t ret;
1539 error_t (*f)(char *root, char *opt);
1540 const char *fcn = "bam_archive()";
1543 * Add trailing / for archive subcommands
1545 if (rootbuf[strlen(rootbuf) - 1] != '/')
1546 (void) strcat(rootbuf, "/");
1547 bam_rootlen = strlen(rootbuf);
1550 * Check arguments
1552 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1553 if (ret != BAM_SUCCESS) {
1554 return (BAM_ERROR);
1557 ret = get_boot_cap(rootbuf);
1558 if (ret != BAM_SUCCESS) {
1559 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1560 return (ret);
1564 * Check archive not supported with update_all
1565 * since it is awkward to display out-of-sync
1566 * information for each BE.
1568 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1569 bam_error(_("the check option is not supported with "
1570 "subcmd: %s\n"), subcmd);
1571 return (BAM_ERROR);
1574 if (strcmp(subcmd, "update_all") == 0)
1575 bam_update_all = 1;
1577 #if !defined(_OBP)
1578 ucode_install(bam_root);
1579 #endif
1581 ret = f(bam_root, opt);
1583 bam_update_all = 0;
1585 return (ret);
1588 /*PRINTFLIKE1*/
1589 void
1590 bam_error(char *format, ...)
1592 va_list ap;
1594 va_start(ap, format);
1595 (void) fprintf(stderr, "%s: ", prog);
1596 (void) vfprintf(stderr, format, ap);
1597 va_end(ap);
1600 /*PRINTFLIKE1*/
1601 void
1602 bam_derror(char *format, ...)
1604 va_list ap;
1606 assert(bam_debug);
1608 va_start(ap, format);
1609 (void) fprintf(stderr, "DEBUG: ");
1610 (void) vfprintf(stderr, format, ap);
1611 va_end(ap);
1614 /*PRINTFLIKE1*/
1615 void
1616 bam_print(char *format, ...)
1618 va_list ap;
1620 va_start(ap, format);
1621 (void) vfprintf(stdout, format, ap);
1622 va_end(ap);
1625 /*PRINTFLIKE1*/
1626 void
1627 bam_print_stderr(char *format, ...)
1629 va_list ap;
1631 va_start(ap, format);
1632 (void) vfprintf(stderr, format, ap);
1633 va_end(ap);
1636 void
1637 bam_exit(int excode)
1639 restore_env();
1640 bam_unlock();
1641 exit(excode);
1644 static void
1645 bam_lock(void)
1647 struct flock lock;
1648 pid_t pid;
1650 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1651 if (bam_lock_fd < 0) {
1653 * We may be invoked early in boot for archive verification.
1654 * In this case, root is readonly and /var/run may not exist.
1655 * Proceed without the lock
1657 if (errno == EROFS || errno == ENOENT) {
1658 bam_root_readonly = 1;
1659 return;
1662 bam_error(_("failed to open file: %s: %s\n"),
1663 BAM_LOCK_FILE, strerror(errno));
1664 bam_exit(1);
1667 lock.l_type = F_WRLCK;
1668 lock.l_whence = SEEK_SET;
1669 lock.l_start = 0;
1670 lock.l_len = 0;
1672 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1673 if (errno != EACCES && errno != EAGAIN) {
1674 bam_error(_("failed to lock file: %s: %s\n"),
1675 BAM_LOCK_FILE, strerror(errno));
1676 (void) close(bam_lock_fd);
1677 bam_lock_fd = -1;
1678 bam_exit(1);
1680 pid = 0;
1681 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1682 bam_print(
1683 _("another instance of bootadm (pid %lu) is running\n"),
1684 pid);
1686 lock.l_type = F_WRLCK;
1687 lock.l_whence = SEEK_SET;
1688 lock.l_start = 0;
1689 lock.l_len = 0;
1690 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1691 bam_error(_("failed to lock file: %s: %s\n"),
1692 BAM_LOCK_FILE, strerror(errno));
1693 (void) close(bam_lock_fd);
1694 bam_lock_fd = -1;
1695 bam_exit(1);
1699 /* We own the lock now */
1700 pid = getpid();
1701 (void) write(bam_lock_fd, &pid, sizeof (pid));
1704 static void
1705 bam_unlock(void)
1707 struct flock unlock;
1710 * NOP if we don't hold the lock
1712 if (bam_lock_fd < 0) {
1713 return;
1716 unlock.l_type = F_UNLCK;
1717 unlock.l_whence = SEEK_SET;
1718 unlock.l_start = 0;
1719 unlock.l_len = 0;
1721 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1722 bam_error(_("failed to unlock file: %s: %s\n"),
1723 BAM_LOCK_FILE, strerror(errno));
1726 if (close(bam_lock_fd) == -1) {
1727 bam_error(_("failed to close file: %s: %s\n"),
1728 BAM_LOCK_FILE, strerror(errno));
1730 bam_lock_fd = -1;
1733 static error_t
1734 list_archive(char *root, char *opt)
1736 filelist_t flist;
1737 filelist_t *flistp = &flist;
1738 line_t *lp;
1740 assert(root);
1741 assert(opt == NULL);
1743 flistp->head = flistp->tail = NULL;
1744 if (read_list(root, flistp) != BAM_SUCCESS) {
1745 return (BAM_ERROR);
1747 assert(flistp->head && flistp->tail);
1749 for (lp = flistp->head; lp; lp = lp->next) {
1750 bam_print(_("%s\n"), lp->line);
1753 filelist_free(flistp);
1755 return (BAM_SUCCESS);
1759 * This routine writes a list of lines to a file.
1760 * The list is *not* freed
1762 static error_t
1763 list2file(char *root, char *tmp, char *final, line_t *start)
1765 char tmpfile[PATH_MAX];
1766 char path[PATH_MAX];
1767 FILE *fp;
1768 int ret;
1769 struct stat sb;
1770 mode_t mode;
1771 uid_t root_uid;
1772 gid_t sys_gid;
1773 struct passwd *pw;
1774 struct group *gp;
1775 const char *fcn = "list2file()";
1777 (void) snprintf(path, sizeof (path), "%s%s", root, final);
1779 if (start == NULL) {
1780 /* Empty GRUB menu */
1781 if (stat(path, &sb) != -1) {
1782 bam_print(_("file is empty, deleting file: %s\n"),
1783 path);
1784 if (unlink(path) != 0) {
1785 bam_error(_("failed to unlink file: %s: %s\n"),
1786 path, strerror(errno));
1787 return (BAM_ERROR);
1788 } else {
1789 return (BAM_SUCCESS);
1792 return (BAM_SUCCESS);
1796 * Preserve attributes of existing file if possible,
1797 * otherwise ask the system for uid/gid of root/sys.
1798 * If all fails, fall back on hard-coded defaults.
1800 if (stat(path, &sb) != -1) {
1801 mode = sb.st_mode;
1802 root_uid = sb.st_uid;
1803 sys_gid = sb.st_gid;
1804 } else {
1805 mode = DEFAULT_DEV_MODE;
1806 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1807 root_uid = pw->pw_uid;
1808 } else {
1809 bam_error(_("getpwnam: uid for %s failed, "
1810 "defaulting to %d\n"),
1811 DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1812 root_uid = (uid_t)DEFAULT_DEV_UID;
1814 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1815 sys_gid = gp->gr_gid;
1816 } else {
1817 bam_error(_("getgrnam: gid for %s failed, "
1818 "defaulting to %d\n"),
1819 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1820 sys_gid = (gid_t)DEFAULT_DEV_GID;
1824 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1826 /* Truncate tmpfile first */
1827 fp = fopen(tmpfile, "w");
1828 if (fp == NULL) {
1829 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1830 strerror(errno));
1831 return (BAM_ERROR);
1833 ret = fclose(fp);
1834 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1835 if (ret == EOF) {
1836 bam_error(_("failed to close file: %s: %s\n"),
1837 tmpfile, strerror(errno));
1838 return (BAM_ERROR);
1841 /* Now open it in append mode */
1842 fp = fopen(tmpfile, "a");
1843 if (fp == NULL) {
1844 bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1845 strerror(errno));
1846 return (BAM_ERROR);
1849 for (; start; start = start->next) {
1850 ret = s_fputs(start->line, fp);
1851 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1852 if (ret == EOF) {
1853 bam_error(_("write to file failed: %s: %s\n"),
1854 tmpfile, strerror(errno));
1855 (void) fclose(fp);
1856 return (BAM_ERROR);
1860 ret = fclose(fp);
1861 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1862 if (ret == EOF) {
1863 bam_error(_("failed to close file: %s: %s\n"),
1864 tmpfile, strerror(errno));
1865 return (BAM_ERROR);
1869 * Set up desired attributes. Ignore failures on filesystems
1870 * not supporting these operations - pcfs reports unsupported
1871 * operations as EINVAL.
1873 ret = chmod(tmpfile, mode);
1874 if (ret == -1 &&
1875 errno != EINVAL && errno != ENOTSUP) {
1876 bam_error(_("chmod operation on %s failed - %s\n"),
1877 tmpfile, strerror(errno));
1878 return (BAM_ERROR);
1881 ret = chown(tmpfile, root_uid, sys_gid);
1882 if (ret == -1 &&
1883 errno != EINVAL && errno != ENOTSUP) {
1884 bam_error(_("chgrp operation on %s failed - %s\n"),
1885 tmpfile, strerror(errno));
1886 return (BAM_ERROR);
1890 * Do an atomic rename
1892 ret = rename(tmpfile, path);
1893 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1894 if (ret != 0) {
1895 bam_error(_("rename to file failed: %s: %s\n"), path,
1896 strerror(errno));
1897 return (BAM_ERROR);
1900 BAM_DPRINTF(("%s: wrote file successfully: %s\n", fcn, path));
1901 return (BAM_SUCCESS);
1905 * Checks if the path specified (without the file name at the end) exists
1906 * and creates it if not. If the path exists and is not a directory, an attempt
1907 * to unlink is made.
1909 static int
1910 setup_path(char *path)
1912 char *p;
1913 int ret;
1914 struct stat sb;
1916 p = strrchr(path, '/');
1917 if (p != NULL) {
1918 *p = '\0';
1919 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1920 /* best effort attempt, mkdirp will catch the error */
1921 (void) unlink(path);
1922 if (bam_verbose)
1923 bam_print(_("need to create directory "
1924 "path for %s\n"), path);
1925 ret = mkdirp(path, DIR_PERMS);
1926 if (ret == -1) {
1927 bam_error(_("mkdir of %s failed: %s\n"),
1928 path, strerror(errno));
1929 *p = '/';
1930 return (BAM_ERROR);
1933 *p = '/';
1934 return (BAM_SUCCESS);
1936 return (BAM_SUCCESS);
1939 typedef union {
1940 gzFile gzfile;
1941 int fdfile;
1942 } outfile;
1944 typedef struct {
1945 char path[PATH_MAX];
1946 outfile out;
1947 } cachefile;
1949 static int
1950 setup_file(char *base, const char *path, cachefile *cf)
1952 int ret;
1953 char *strip;
1955 /* init gzfile or fdfile in case we fail before opening */
1956 if (bam_direct == BAM_DIRECT_DBOOT)
1957 cf->out.gzfile = NULL;
1958 else
1959 cf->out.fdfile = -1;
1961 /* strip the trailing altroot path */
1962 strip = (char *)path + strlen(rootbuf);
1964 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1965 if (ret >= sizeof (cf->path)) {
1966 bam_error(_("unable to create path on mountpoint %s, "
1967 "path too long\n"), rootbuf);
1968 return (BAM_ERROR);
1971 /* Check if path is present in the archive cache directory */
1972 if (setup_path(cf->path) == BAM_ERROR)
1973 return (BAM_ERROR);
1975 if (bam_direct == BAM_DIRECT_DBOOT) {
1976 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1977 bam_error(_("failed to open file: %s: %s\n"),
1978 cf->path, strerror(errno));
1979 return (BAM_ERROR);
1981 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1982 Z_DEFAULT_STRATEGY);
1983 } else {
1984 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1985 == -1) {
1986 bam_error(_("failed to open file: %s: %s\n"),
1987 cf->path, strerror(errno));
1988 return (BAM_ERROR);
1992 return (BAM_SUCCESS);
1995 static int
1996 cache_write(cachefile cf, char *buf, int size)
1998 int err;
2000 if (bam_direct == BAM_DIRECT_DBOOT) {
2001 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
2002 bam_error(_("failed to write to %s\n"),
2003 gzerror(cf.out.gzfile, &err));
2004 if (err == Z_ERRNO && bam_verbose) {
2005 bam_error(_("write to file failed: %s: %s\n"),
2006 cf.path, strerror(errno));
2008 return (BAM_ERROR);
2010 } else {
2011 if (write(cf.out.fdfile, buf, size) < 1) {
2012 bam_error(_("write to file failed: %s: %s\n"),
2013 cf.path, strerror(errno));
2014 return (BAM_ERROR);
2017 return (BAM_SUCCESS);
2020 static int
2021 cache_close(cachefile cf)
2023 int ret;
2025 if (bam_direct == BAM_DIRECT_DBOOT) {
2026 if (cf.out.gzfile) {
2027 ret = gzclose(cf.out.gzfile);
2028 if (ret != Z_OK) {
2029 bam_error(_("failed to close file: %s: %s\n"),
2030 cf.path, strerror(errno));
2031 return (BAM_ERROR);
2034 } else {
2035 if (cf.out.fdfile != -1) {
2036 ret = close(cf.out.fdfile);
2037 if (ret != 0) {
2038 bam_error(_("failed to close file: %s: %s\n"),
2039 cf.path, strerror(errno));
2040 return (BAM_ERROR);
2045 return (BAM_SUCCESS);
2048 static int
2049 dircache_updatefile(const char *path, int what)
2051 int ret, exitcode;
2052 char buf[4096 * 4];
2053 FILE *infile;
2054 cachefile outfile, outupdt;
2056 if (bam_nowrite()) {
2057 set_dir_flag(what, NEED_UPDATE);
2058 return (BAM_SUCCESS);
2061 if (!has_cachedir(what))
2062 return (BAM_SUCCESS);
2064 if ((infile = fopen(path, "rb")) == NULL) {
2065 bam_error(_("failed to open file: %s: %s\n"), path,
2066 strerror(errno));
2067 return (BAM_ERROR);
2070 ret = setup_file(get_cachedir(what), path, &outfile);
2071 if (ret == BAM_ERROR) {
2072 exitcode = BAM_ERROR;
2073 goto out;
2075 if (!is_dir_flag_on(what, NO_MULTI)) {
2076 ret = setup_file(get_updatedir(what), path, &outupdt);
2077 if (ret == BAM_ERROR)
2078 set_dir_flag(what, NO_MULTI);
2081 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2082 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2083 exitcode = BAM_ERROR;
2084 goto out;
2086 if (!is_dir_flag_on(what, NO_MULTI))
2087 if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2088 set_dir_flag(what, NO_MULTI);
2091 set_dir_flag(what, NEED_UPDATE);
2092 get_count(what)++;
2093 if (get_count(what) > COUNT_MAX)
2094 set_dir_flag(what, NO_MULTI);
2095 exitcode = BAM_SUCCESS;
2096 out:
2097 (void) fclose(infile);
2098 if (cache_close(outfile) == BAM_ERROR)
2099 exitcode = BAM_ERROR;
2100 if (!is_dir_flag_on(what, NO_MULTI) &&
2101 cache_close(outupdt) == BAM_ERROR)
2102 exitcode = BAM_ERROR;
2103 if (exitcode == BAM_ERROR)
2104 set_flag(UPDATE_ERROR);
2105 return (exitcode);
2108 static int
2109 dircache_updatedir(const char *path, int what, int updt)
2111 int ret;
2112 char dpath[PATH_MAX];
2113 char *strip;
2114 struct stat sb;
2116 strip = (char *)path + strlen(rootbuf);
2118 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2119 get_updatedir(what) : get_cachedir(what), strip);
2121 if (ret >= sizeof (dpath)) {
2122 bam_error(_("unable to create path on mountpoint %s, "
2123 "path too long\n"), rootbuf);
2124 set_flag(UPDATE_ERROR);
2125 return (BAM_ERROR);
2128 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2129 return (BAM_SUCCESS);
2131 if (updt) {
2132 if (!is_dir_flag_on(what, NO_MULTI))
2133 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2134 set_dir_flag(what, NO_MULTI);
2135 } else {
2136 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2137 set_flag(UPDATE_ERROR);
2138 return (BAM_ERROR);
2142 set_dir_flag(what, NEED_UPDATE);
2143 return (BAM_SUCCESS);
2146 #define DO_CACHE_DIR 0
2147 #define DO_UPDATE_DIR 1
2149 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2150 typedef Elf64_Ehdr _elfhdr;
2151 #else
2152 typedef Elf32_Ehdr _elfhdr;
2153 #endif
2156 * This routine updates the contents of the cache directory
2158 static int
2159 update_dircache(const char *path, int flags)
2161 int rc = BAM_SUCCESS;
2163 switch (flags) {
2164 case FTW_F:
2166 int fd;
2167 _elfhdr elf;
2169 if ((fd = open(path, O_RDONLY)) < 0) {
2170 bam_error(_("failed to open file: %s: %s\n"),
2171 path, strerror(errno));
2172 set_flag(UPDATE_ERROR);
2173 rc = BAM_ERROR;
2174 break;
2178 * libelf and gelf would be a cleaner and easier way to handle
2179 * this, but libelf fails compilation if _ILP32 is defined &&
2180 * _FILE_OFFSET_BITS is != 32 ...
2182 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2183 bam_error(_("read failed for file: %s: %s\n"),
2184 path, strerror(errno));
2185 set_flag(UPDATE_ERROR);
2186 (void) close(fd);
2187 rc = BAM_ERROR;
2188 break;
2190 (void) close(fd);
2193 * If the file is not an executable and is not inside an amd64
2194 * directory, we copy it in both the cache directories,
2195 * otherwise, we only copy it inside the 64-bit one.
2197 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2198 if (strstr(path, "/amd64")) {
2199 rc = dircache_updatefile(path, FILE64);
2200 } else {
2201 rc = dircache_updatefile(path, FILE32);
2202 if (rc == BAM_SUCCESS)
2203 rc = dircache_updatefile(path, FILE64);
2205 } else {
2207 * Based on the ELF class we copy the file in the 32-bit
2208 * or the 64-bit cache directory.
2210 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
2211 rc = dircache_updatefile(path, FILE32);
2212 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
2213 rc = dircache_updatefile(path, FILE64);
2214 } else {
2215 bam_print(_("WARNING: file %s is neither a "
2216 "32-bit nor a 64-bit ELF\n"), path);
2217 /* paranoid */
2218 rc = dircache_updatefile(path, FILE32);
2219 if (rc == BAM_SUCCESS)
2220 rc = dircache_updatefile(path, FILE64);
2223 break;
2225 case FTW_D:
2226 if (strstr(path, "/amd64") == NULL) {
2227 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
2228 if (rc == BAM_SUCCESS)
2229 rc = dircache_updatedir(path, FILE32,
2230 DO_CACHE_DIR);
2231 } else {
2232 if (has_cachedir(FILE64)) {
2233 rc = dircache_updatedir(path, FILE64,
2234 DO_UPDATE_DIR);
2235 if (rc == BAM_SUCCESS)
2236 rc = dircache_updatedir(path, FILE64,
2237 DO_CACHE_DIR);
2240 break;
2241 default:
2242 rc = BAM_ERROR;
2243 break;
2246 return (rc);
2249 /*ARGSUSED*/
2250 static int
2251 cmpstat(
2252 const char *file,
2253 const struct stat *st,
2254 int flags,
2255 struct FTW *ftw)
2257 uint_t sz;
2258 uint64_t *value;
2259 uint64_t filestat[2];
2260 int error, ret, status;
2262 struct safefile *safefilep;
2263 FILE *fp;
2264 struct stat sb;
2265 regex_t re;
2268 * On SPARC we create/update links too.
2270 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2271 !is_flag_on(IS_SPARC_TARGET)))
2272 return (0);
2275 * Ignore broken links
2277 if (flags == FTW_SL && stat(file, &sb) < 0)
2278 return (0);
2281 * new_nvlp may be NULL if there were errors earlier
2282 * but this is not fatal to update determination.
2284 if (walk_arg.new_nvlp) {
2285 filestat[0] = st->st_size;
2286 filestat[1] = st->st_mtime;
2287 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2288 file + bam_rootlen, filestat, 2);
2289 if (error)
2290 bam_error(_("failed to update stat data for: %s: %s\n"),
2291 file, strerror(error));
2295 * If we are invoked as part of system/filesystem/boot-archive, then
2296 * there are a number of things we should not worry about
2298 if (bam_smf_check) {
2299 /* ignore amd64 modules unless we are booted amd64. */
2300 if (!is_amd64() && strstr(file, "/amd64/") != 0)
2301 return (0);
2303 /* read in list of safe files */
2304 if (safefiles == NULL) {
2305 fp = fopen("/boot/solaris/filelist.safe", "r");
2306 if (fp != NULL) {
2307 safefiles = s_calloc(1,
2308 sizeof (struct safefile));
2309 safefilep = safefiles;
2310 safefilep->name = s_calloc(1, MAXPATHLEN +
2311 MAXNAMELEN);
2312 safefilep->next = NULL;
2313 while (s_fgets(safefilep->name, MAXPATHLEN +
2314 MAXNAMELEN, fp) != NULL) {
2315 safefilep->next = s_calloc(1,
2316 sizeof (struct safefile));
2317 safefilep = safefilep->next;
2318 safefilep->name = s_calloc(1,
2319 MAXPATHLEN + MAXNAMELEN);
2320 safefilep->next = NULL;
2322 (void) fclose(fp);
2328 * On SPARC we create a -path-list file for mkisofs
2330 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2331 if (flags != FTW_D) {
2332 char *strip;
2334 strip = (char *)file + strlen(rootbuf);
2335 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2336 file);
2341 * We are transitioning from the old model to the dircache or the cache
2342 * directory was removed: create the entry without further checkings.
2344 if (is_flag_on(NEED_CACHE_DIR)) {
2345 if (bam_verbose)
2346 bam_print(_(" new %s\n"), file);
2348 if (is_flag_on(IS_SPARC_TARGET)) {
2349 set_dir_flag(FILE64, NEED_UPDATE);
2350 return (0);
2353 ret = update_dircache(file, flags);
2354 if (ret == BAM_ERROR) {
2355 bam_error(_("directory cache update failed for %s\n"),
2356 file);
2357 return (-1);
2360 return (0);
2364 * We need an update if file doesn't exist in old archive
2366 if (walk_arg.old_nvlp == NULL ||
2367 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2368 file + bam_rootlen, &value, &sz) != 0) {
2369 if (bam_smf_check) /* ignore new during smf check */
2370 return (0);
2372 if (is_flag_on(IS_SPARC_TARGET)) {
2373 set_dir_flag(FILE64, NEED_UPDATE);
2374 } else {
2375 ret = update_dircache(file, flags);
2376 if (ret == BAM_ERROR) {
2377 bam_error(_("directory cache update "
2378 "failed for %s\n"), file);
2379 return (-1);
2383 if (bam_verbose)
2384 bam_print(_(" new %s\n"), file);
2385 return (0);
2389 * If we got there, the file is already listed as to be included in the
2390 * iso image. We just need to know if we are going to rebuild it or not
2392 if (is_flag_on(IS_SPARC_TARGET) &&
2393 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
2394 return (0);
2396 * File exists in old archive. Check if file has changed
2398 assert(sz == 2);
2399 bcopy(value, filestat, sizeof (filestat));
2401 if (flags != FTW_D && (filestat[0] != st->st_size ||
2402 filestat[1] != st->st_mtime)) {
2403 if (bam_smf_check) {
2404 safefilep = safefiles;
2405 while (safefilep != NULL &&
2406 safefilep->name[0] != '\0') {
2407 if (regcomp(&re, safefilep->name,
2408 REG_EXTENDED|REG_NOSUB) == 0) {
2409 status = regexec(&re,
2410 file + bam_rootlen, 0, NULL, 0);
2411 regfree(&re);
2412 if (status == 0) {
2413 (void) creat(
2414 NEED_UPDATE_SAFE_FILE,
2415 0644);
2416 return (0);
2419 safefilep = safefilep->next;
2423 if (is_flag_on(IS_SPARC_TARGET)) {
2424 set_dir_flag(FILE64, NEED_UPDATE);
2425 } else {
2426 ret = update_dircache(file, flags);
2427 if (ret == BAM_ERROR) {
2428 bam_error(_("directory cache update failed "
2429 "for %s\n"), file);
2430 return (-1);
2434 if (bam_verbose) {
2435 if (bam_smf_check)
2436 bam_print(" %s\n", file);
2437 else
2438 bam_print(_(" changed %s\n"), file);
2442 return (0);
2446 * Remove a directory path recursively
2448 static int
2449 rmdir_r(char *path)
2451 struct dirent *d = NULL;
2452 DIR *dir = NULL;
2453 char tpath[PATH_MAX];
2454 struct stat sb;
2456 if ((dir = opendir(path)) == NULL)
2457 return (-1);
2459 while ((d = readdir(dir)) != NULL) {
2460 if ((strcmp(d->d_name, ".") != 0) &&
2461 (strcmp(d->d_name, "..") != 0)) {
2462 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
2463 path, d->d_name);
2464 if (stat(tpath, &sb) == 0) {
2465 if (sb.st_mode & S_IFDIR)
2466 (void) rmdir_r(tpath);
2467 else
2468 (void) remove(tpath);
2472 return (remove(path));
2476 * Check if cache directory exists and, if not, create it and update flags
2477 * accordingly. If the path exists, but it's not a directory, a best effort
2478 * attempt to remove and recreate it is made.
2479 * If the user requested a 'purge', always recreate the directory from scratch.
2481 static int
2482 set_cache_dir(char *root, int what)
2484 struct stat sb;
2485 int ret = 0;
2487 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
2488 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
2489 "/amd64" : "", CACHEDIR_SUFFIX);
2491 if (ret >= sizeof (get_cachedir(what))) {
2492 bam_error(_("unable to create path on mountpoint %s, "
2493 "path too long\n"), rootbuf);
2494 return (BAM_ERROR);
2497 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2498 (void) rmdir_r(get_cachedir(what));
2500 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2501 /* best effort unlink attempt, mkdir will catch errors */
2502 (void) unlink(get_cachedir(what));
2504 if (bam_verbose)
2505 bam_print(_("archive cache directory not found: %s\n"),
2506 get_cachedir(what));
2507 ret = mkdir(get_cachedir(what), DIR_PERMS);
2508 if (ret < 0) {
2509 bam_error(_("mkdir of %s failed: %s\n"),
2510 get_cachedir(what), strerror(errno));
2511 get_cachedir(what)[0] = '\0';
2512 return (ret);
2514 set_flag(NEED_CACHE_DIR);
2515 set_dir_flag(what, NO_MULTI);
2518 return (BAM_SUCCESS);
2521 static int
2522 set_update_dir(char *root, int what)
2524 struct stat sb;
2525 int ret;
2527 if (is_dir_flag_on(what, NO_MULTI))
2528 return (BAM_SUCCESS);
2530 if (!bam_extend) {
2531 set_dir_flag(what, NO_MULTI);
2532 return (BAM_SUCCESS);
2535 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2536 ret = snprintf(get_updatedir(what),
2537 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2538 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2539 else
2540 ret = snprintf(get_updatedir(what),
2541 sizeof (get_updatedir(what)), "%s%s%s%s", root,
2542 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2544 if (ret >= sizeof (get_updatedir(what))) {
2545 bam_error(_("unable to create path on mountpoint %s, "
2546 "path too long\n"), rootbuf);
2547 return (BAM_ERROR);
2550 if (stat(get_updatedir(what), &sb) == 0) {
2551 if (S_ISDIR(sb.st_mode))
2552 ret = rmdir_r(get_updatedir(what));
2553 else
2554 ret = unlink(get_updatedir(what));
2556 if (ret != 0)
2557 set_dir_flag(what, NO_MULTI);
2560 if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2561 set_dir_flag(what, NO_MULTI);
2563 return (BAM_SUCCESS);
2566 static int
2567 is_valid_archive(char *root, int what)
2569 char archive_path[PATH_MAX];
2570 char timestamp_path[PATH_MAX];
2571 struct stat sb, timestamp;
2572 int ret;
2574 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2575 ret = snprintf(archive_path, sizeof (archive_path),
2576 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2577 ARCHIVE_SUFFIX);
2578 else
2579 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2580 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2582 if (ret >= sizeof (archive_path)) {
2583 bam_error(_("unable to create path on mountpoint %s, "
2584 "path too long\n"), rootbuf);
2585 return (BAM_ERROR);
2588 if (stat(archive_path, &sb) != 0) {
2589 if (bam_verbose && !bam_check)
2590 bam_print(_("archive not found: %s\n"), archive_path);
2591 set_dir_flag(what, NEED_UPDATE);
2592 set_dir_flag(what, NO_MULTI);
2593 return (BAM_SUCCESS);
2597 * The timestamp file is used to prevent stale files in the archive
2598 * cache.
2599 * Stale files can happen if the system is booted back and forth across
2600 * the transition from bootadm-before-the-cache to
2601 * bootadm-after-the-cache, since older versions of bootadm don't know
2602 * about the existence of the archive cache.
2604 * Since only bootadm-after-the-cache versions know about about this
2605 * file, we require that the boot archive be older than this file.
2607 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2608 FILE_STAT_TIMESTAMP);
2610 if (ret >= sizeof (timestamp_path)) {
2611 bam_error(_("unable to create path on mountpoint %s, "
2612 "path too long\n"), rootbuf);
2613 return (BAM_ERROR);
2616 if (stat(timestamp_path, &timestamp) != 0 ||
2617 sb.st_mtime > timestamp.st_mtime) {
2618 if (bam_verbose && !bam_check)
2619 bam_print(
2620 _("archive cache is out of sync. Rebuilding.\n"));
2622 * Don't generate a false positive for the boot-archive service
2623 * but trigger an update of the archive cache in
2624 * boot-archive-update.
2626 if (bam_smf_check) {
2627 (void) creat(NEED_UPDATE_FILE, 0644);
2628 return (BAM_SUCCESS);
2631 set_flag(INVALIDATE_CACHE);
2632 set_dir_flag(what, NEED_UPDATE);
2633 set_dir_flag(what, NO_MULTI);
2634 return (BAM_SUCCESS);
2637 if (is_flag_on(IS_SPARC_TARGET))
2638 return (BAM_SUCCESS);
2640 if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2641 if (bam_verbose && !bam_check)
2642 bam_print(_("archive %s is bigger than %d bytes and "
2643 "will be rebuilt\n"), archive_path, BA_SIZE_MAX);
2644 set_dir_flag(what, NO_MULTI);
2647 return (BAM_SUCCESS);
2651 * Check flags and presence of required files and directories.
2652 * The force flag and/or absence of files should
2653 * trigger an update.
2654 * Suppress stdout output if check (-n) option is set
2655 * (as -n should only produce parseable output.)
2657 static int
2658 check_flags_and_files(char *root)
2661 struct stat sb;
2662 int ret;
2665 * If archive is missing, create archive
2667 if (is_flag_on(IS_SPARC_TARGET)) {
2668 ret = is_valid_archive(root, FILE64);
2669 if (ret == BAM_ERROR)
2670 return (BAM_ERROR);
2671 } else {
2672 int what = FILE32;
2673 do {
2674 ret = is_valid_archive(root, what);
2675 if (ret == BAM_ERROR)
2676 return (BAM_ERROR);
2677 what++;
2678 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2681 if (bam_nowrite())
2682 return (BAM_SUCCESS);
2686 * check if cache directories exist on x86.
2687 * check (and always open) the cache file on SPARC.
2689 if (is_sparc()) {
2690 ret = snprintf(get_cachedir(FILE64),
2691 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2692 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2694 if (ret >= sizeof (get_cachedir(FILE64))) {
2695 bam_error(_("unable to create path on mountpoint %s, "
2696 "path too long\n"), rootbuf);
2697 return (BAM_ERROR);
2700 if (stat(get_cachedir(FILE64), &sb) != 0) {
2701 set_flag(NEED_CACHE_DIR);
2702 set_dir_flag(FILE64, NEED_UPDATE);
2705 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2706 if (walk_arg.sparcfile == NULL) {
2707 bam_error(_("failed to open file: %s: %s\n"),
2708 get_cachedir(FILE64), strerror(errno));
2709 return (BAM_ERROR);
2712 set_dir_present(FILE64);
2713 } else {
2714 int what = FILE32;
2716 do {
2717 if (set_cache_dir(root, what) != 0)
2718 return (BAM_ERROR);
2720 set_dir_present(what);
2722 if (set_update_dir(root, what) != 0)
2723 return (BAM_ERROR);
2724 what++;
2725 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2729 * if force, create archive unconditionally
2731 if (bam_force) {
2732 if (!is_sparc())
2733 set_dir_flag(FILE32, NEED_UPDATE);
2734 set_dir_flag(FILE64, NEED_UPDATE);
2735 if (bam_verbose)
2736 bam_print(_("forced update of archive requested\n"));
2737 return (BAM_SUCCESS);
2740 return (BAM_SUCCESS);
2743 static error_t
2744 read_one_list(char *root, filelist_t *flistp, char *filelist)
2746 char path[PATH_MAX];
2747 FILE *fp;
2748 char buf[BAM_MAXLINE];
2749 const char *fcn = "read_one_list()";
2751 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2753 fp = fopen(path, "r");
2754 if (fp == NULL) {
2755 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2756 fcn, path, strerror(errno)));
2757 return (BAM_ERROR);
2759 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2760 /* skip blank lines */
2761 if (strspn(buf, " \t") == strlen(buf))
2762 continue;
2763 append_to_flist(flistp, buf);
2765 if (fclose(fp) != 0) {
2766 bam_error(_("failed to close file: %s: %s\n"),
2767 path, strerror(errno));
2768 return (BAM_ERROR);
2770 return (BAM_SUCCESS);
2773 static error_t
2774 read_list(char *root, filelist_t *flistp)
2776 char path[PATH_MAX];
2777 char cmd[PATH_MAX];
2778 struct stat sb;
2779 int n, rval;
2780 const char *fcn = "read_list()";
2782 flistp->head = flistp->tail = NULL;
2785 * build and check path to extract_boot_filelist.ksh
2787 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2788 if (n >= sizeof (path)) {
2789 bam_error(_("archive filelist is empty\n"));
2790 return (BAM_ERROR);
2793 if (is_safe_exec(path) == BAM_ERROR)
2794 return (BAM_ERROR);
2797 * If extract_boot_filelist is present, exec it, otherwise read
2798 * the filelists directly, for compatibility with older images.
2800 if (stat(path, &sb) == 0) {
2802 * build arguments to exec extract_boot_filelist.ksh
2804 char *rootarg, *platarg;
2805 int platarglen = 1, rootarglen = 1;
2806 if (strlen(root) > 1)
2807 rootarglen += strlen(root) + strlen("-R ");
2808 if (bam_alt_platform)
2809 platarglen += strlen(bam_platform) + strlen("-p ");
2810 platarg = s_calloc(1, platarglen);
2811 rootarg = s_calloc(1, rootarglen);
2812 *platarg = 0;
2813 *rootarg = 0;
2815 if (strlen(root) > 1) {
2816 (void) snprintf(rootarg, rootarglen,
2817 "-R %s", root);
2819 if (bam_alt_platform) {
2820 (void) snprintf(platarg, platarglen,
2821 "-p %s", bam_platform);
2823 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2824 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2825 free(platarg);
2826 free(rootarg);
2827 if (n >= sizeof (cmd)) {
2828 bam_error(_("archive filelist is empty\n"));
2829 return (BAM_ERROR);
2831 if (exec_cmd(cmd, flistp) != 0) {
2832 BAM_DPRINTF(("%s: failed to open archive "
2833 "filelist: %s: %s\n", fcn, path, strerror(errno)));
2834 return (BAM_ERROR);
2836 } else {
2838 * Read current lists of files - only the first is mandatory
2840 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2841 if (rval != BAM_SUCCESS)
2842 return (rval);
2843 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2846 if (flistp->head == NULL) {
2847 bam_error(_("archive filelist is empty\n"));
2848 return (BAM_ERROR);
2851 return (BAM_SUCCESS);
2854 static void
2855 getoldstat(char *root)
2857 char path[PATH_MAX];
2858 int fd, error;
2859 struct stat sb;
2860 char *ostat;
2862 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2863 fd = open(path, O_RDONLY);
2864 if (fd == -1) {
2865 if (bam_verbose)
2866 bam_print(_("failed to open file: %s: %s\n"),
2867 path, strerror(errno));
2868 goto out_err;
2871 if (fstat(fd, &sb) != 0) {
2872 bam_error(_("stat of file failed: %s: %s\n"), path,
2873 strerror(errno));
2874 goto out_err;
2877 ostat = s_calloc(1, sb.st_size);
2879 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2880 bam_error(_("read failed for file: %s: %s\n"), path,
2881 strerror(errno));
2882 free(ostat);
2883 goto out_err;
2886 (void) close(fd);
2887 fd = -1;
2889 walk_arg.old_nvlp = NULL;
2890 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2892 free(ostat);
2894 if (error) {
2895 bam_error(_("failed to unpack stat data: %s: %s\n"),
2896 path, strerror(error));
2897 walk_arg.old_nvlp = NULL;
2898 goto out_err;
2899 } else {
2900 return;
2903 out_err:
2904 if (fd != -1)
2905 (void) close(fd);
2906 if (!is_flag_on(IS_SPARC_TARGET))
2907 set_dir_flag(FILE32, NEED_UPDATE);
2908 set_dir_flag(FILE64, NEED_UPDATE);
2911 /* Best effort stale entry removal */
2912 static void
2913 delete_stale(char *file, int what)
2915 char path[PATH_MAX];
2916 struct stat sb;
2918 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2919 if (!bam_check && stat(path, &sb) == 0) {
2920 if (sb.st_mode & S_IFDIR)
2921 (void) rmdir_r(path);
2922 else
2923 (void) unlink(path);
2925 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2930 * Checks if a file in the current (old) archive has
2931 * been deleted from the root filesystem. This is needed for
2932 * software like Trusted Extensions (TX) that switch early
2933 * in boot based on presence/absence of a kernel module.
2935 static void
2936 check4stale(char *root)
2938 nvpair_t *nvp;
2939 nvlist_t *nvlp;
2940 char *file;
2941 char path[PATH_MAX];
2944 * Skip stale file check during smf check
2946 if (bam_smf_check)
2947 return;
2950 * If we need to (re)create the cache, there's no need to check for
2951 * stale files
2953 if (is_flag_on(NEED_CACHE_DIR))
2954 return;
2956 /* Nothing to do if no old stats */
2957 if ((nvlp = walk_arg.old_nvlp) == NULL)
2958 return;
2960 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2961 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2962 file = nvpair_name(nvp);
2963 if (file == NULL)
2964 continue;
2965 (void) snprintf(path, sizeof (path), "%s/%s",
2966 root, file);
2967 if (access(path, F_OK) < 0) {
2968 int what;
2970 if (bam_verbose)
2971 bam_print(_(" stale %s\n"), path);
2973 if (is_flag_on(IS_SPARC_TARGET)) {
2974 set_dir_flag(FILE64, NEED_UPDATE);
2975 } else {
2976 for (what = FILE32; what < CACHEDIR_NUM; what++)
2977 if (has_cachedir(what))
2978 delete_stale(file, what);
2984 static void
2985 create_newstat(void)
2987 int error;
2989 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2990 if (error) {
2992 * Not fatal - we can still create archive
2994 walk_arg.new_nvlp = NULL;
2995 bam_error(_("failed to create stat data: %s\n"),
2996 strerror(error));
3000 static int
3001 walk_list(char *root, filelist_t *flistp)
3003 char path[PATH_MAX];
3004 line_t *lp;
3006 for (lp = flistp->head; lp; lp = lp->next) {
3008 * Don't follow symlinks. A symlink must refer to
3009 * a file that would appear in the archive through
3010 * a direct reference. This matches the archive
3011 * construction behavior.
3013 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
3014 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
3015 if (is_flag_on(UPDATE_ERROR))
3016 return (BAM_ERROR);
3018 * Some files may not exist.
3019 * For example: etc/rtc_config on a x86 diskless system
3020 * Emit verbose message only
3022 if (bam_verbose)
3023 bam_print(_("cannot find: %s: %s\n"),
3024 path, strerror(errno));
3028 return (BAM_SUCCESS);
3032 * Update the timestamp file.
3034 static void
3035 update_timestamp(char *root)
3037 char timestamp_path[PATH_MAX];
3039 /* this path length has already been checked in check_flags_and_files */
3040 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
3041 FILE_STAT_TIMESTAMP);
3044 * recreate the timestamp file. Since an outdated or absent timestamp
3045 * file translates in a complete rebuild of the archive cache, notify
3046 * the user of the performance issue.
3048 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
3049 bam_error(_("failed to open file: %s: %s\n"), timestamp_path,
3050 strerror(errno));
3051 bam_error(_("failed to update the timestamp file, next"
3052 " archive update may experience reduced performance\n"));
3057 static void
3058 savenew(char *root)
3060 char path[PATH_MAX];
3061 char path2[PATH_MAX];
3062 size_t sz;
3063 char *nstat;
3064 int fd, wrote, error;
3066 nstat = NULL;
3067 sz = 0;
3068 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3069 NV_ENCODE_XDR, 0);
3070 if (error) {
3071 bam_error(_("failed to pack stat data: %s\n"),
3072 strerror(error));
3073 return;
3076 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3077 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3078 if (fd == -1) {
3079 bam_error(_("failed to open file: %s: %s\n"), path,
3080 strerror(errno));
3081 free(nstat);
3082 return;
3084 wrote = write(fd, nstat, sz);
3085 if (wrote != sz) {
3086 bam_error(_("write to file failed: %s: %s\n"), path,
3087 strerror(errno));
3088 (void) close(fd);
3089 free(nstat);
3090 return;
3092 (void) close(fd);
3093 free(nstat);
3095 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3096 if (rename(path, path2) != 0) {
3097 bam_error(_("rename to file failed: %s: %s\n"), path2,
3098 strerror(errno));
3102 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3104 static void
3105 clear_walk_args(void)
3107 nvlist_free(walk_arg.old_nvlp);
3108 nvlist_free(walk_arg.new_nvlp);
3109 if (walk_arg.sparcfile)
3110 (void) fclose(walk_arg.sparcfile);
3111 walk_arg.old_nvlp = NULL;
3112 walk_arg.new_nvlp = NULL;
3113 walk_arg.sparcfile = NULL;
3117 * Returns:
3118 * 0 - no update necessary
3119 * 1 - update required.
3120 * BAM_ERROR (-1) - An error occurred
3122 * Special handling for check (-n):
3123 * ================================
3124 * The check (-n) option produces parseable output.
3125 * To do this, we suppress all stdout messages unrelated
3126 * to out of sync files.
3127 * All stderr messages are still printed though.
3130 static int
3131 update_required(char *root)
3133 struct stat sb;
3134 char path[PATH_MAX];
3135 filelist_t flist;
3136 filelist_t *flistp = &flist;
3137 int ret;
3139 flistp->head = flistp->tail = NULL;
3141 if (is_sparc())
3142 set_flag(IS_SPARC_TARGET);
3145 * Check if cache directories and archives are present
3148 ret = check_flags_and_files(root);
3149 if (ret < 0)
3150 return (BAM_ERROR);
3153 * In certain deployment scenarios, filestat may not
3154 * exist. Do not stop the boot process, but trigger an update
3155 * of the archives (which will recreate filestat.ramdisk).
3157 if (bam_smf_check) {
3158 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3159 if (stat(path, &sb) != 0) {
3160 (void) creat(NEED_UPDATE_FILE, 0644);
3161 return (0);
3165 getoldstat(root);
3168 * Check if the archive contains files that are no longer
3169 * present on the root filesystem.
3171 check4stale(root);
3174 * read list of files
3176 if (read_list(root, flistp) != BAM_SUCCESS) {
3177 clear_walk_args();
3178 return (BAM_ERROR);
3181 assert(flistp->head && flistp->tail);
3184 * At this point either the update is required
3185 * or the decision is pending. In either case
3186 * we need to create new stat nvlist
3188 create_newstat();
3190 * This walk does 2 things:
3191 * - gets new stat data for every file
3192 * - (optional) compare old and new stat data
3194 ret = walk_list(root, &flist);
3196 /* done with the file list */
3197 filelist_free(flistp);
3199 /* something went wrong */
3201 if (ret == BAM_ERROR) {
3202 bam_error(_("Failed to gather cache files, archives "
3203 "generation aborted\n"));
3204 return (BAM_ERROR);
3207 if (walk_arg.new_nvlp == NULL) {
3208 if (walk_arg.sparcfile != NULL)
3209 (void) fclose(walk_arg.sparcfile);
3210 bam_error(_("cannot create new stat data\n"));
3213 /* If nothing was updated, discard newstat. */
3215 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
3216 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
3217 clear_walk_args();
3218 return (0);
3221 if (walk_arg.sparcfile != NULL)
3222 (void) fclose(walk_arg.sparcfile);
3224 return (1);
3227 static int
3228 flushfs(char *root)
3230 char cmd[PATH_MAX + 30];
3232 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3233 LOCKFS_PATH, root);
3235 return (exec_cmd(cmd, NULL));
3238 static int
3239 do_archive_copy(char *source, char *dest)
3242 sync();
3244 /* the equivalent of mv archive-new-$pid boot_archive */
3245 if (rename(source, dest) != 0) {
3246 (void) unlink(source);
3247 return (BAM_ERROR);
3250 if (flushfs(bam_root) != 0)
3251 sync();
3253 return (BAM_SUCCESS);
3256 static int
3257 check_cmdline(filelist_t flist)
3259 line_t *lp;
3261 for (lp = flist.head; lp; lp = lp->next) {
3262 if (strstr(lp->line, "Error:") != NULL ||
3263 strstr(lp->line, "Inode number overflow") != NULL) {
3264 (void) fprintf(stderr, "%s\n", lp->line);
3265 return (BAM_ERROR);
3269 return (BAM_SUCCESS);
3272 static void
3273 dump_errormsg(filelist_t flist)
3275 line_t *lp;
3277 for (lp = flist.head; lp; lp = lp->next)
3278 (void) fprintf(stderr, "%s\n", lp->line);
3281 static int
3282 check_archive(char *dest)
3284 struct stat sb;
3286 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3287 sb.st_size < 10000) {
3288 bam_error(_("archive file %s not generated correctly\n"), dest);
3289 (void) unlink(dest);
3290 return (BAM_ERROR);
3293 return (BAM_SUCCESS);
3296 static boolean_t
3297 is_be(char *root)
3299 zfs_handle_t *zhp;
3300 libzfs_handle_t *hdl;
3301 be_node_list_t *be_nodes = NULL;
3302 be_node_list_t *cur_be;
3303 boolean_t be_exist = B_FALSE;
3304 char ds_path[ZFS_MAX_DATASET_NAME_LEN];
3306 if (!is_zfs(root))
3307 return (B_FALSE);
3309 * Get dataset for mountpoint
3311 if ((hdl = libzfs_init()) == NULL)
3312 return (B_FALSE);
3314 if ((zhp = zfs_path_to_zhandle(hdl, root,
3315 ZFS_TYPE_FILESYSTEM)) == NULL) {
3316 libzfs_fini(hdl);
3317 return (B_FALSE);
3320 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3323 * Check if the current dataset is BE
3325 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
3326 for (cur_be = be_nodes; cur_be != NULL;
3327 cur_be = cur_be->be_next_node) {
3330 * Because we guarantee that cur_be->be_root_ds
3331 * is null-terminated by internal data structure,
3332 * we can safely use strcmp()
3334 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3335 be_exist = B_TRUE;
3336 break;
3339 be_free_list(be_nodes);
3341 zfs_close(zhp);
3342 libzfs_fini(hdl);
3344 return (be_exist);
3348 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
3350 static int
3351 is_mkisofs()
3353 if (access(MKISOFS_PATH, X_OK) == 0)
3354 return (1);
3355 return (0);
3358 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3360 static int
3361 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3363 int ret;
3364 char cmdline[3 * PATH_MAX + 64];
3365 filelist_t flist = {0};
3366 const char *func = "create_sparc_archive()";
3368 if (access(bootblk, R_OK) == 1) {
3369 bam_error(_("unable to access bootblk file : %s\n"), bootblk);
3370 return (BAM_ERROR);
3374 * Prepare mkisofs command line and execute it
3376 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3377 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3378 tempname, list);
3380 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3382 ret = exec_cmd(cmdline, &flist);
3383 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3384 dump_errormsg(flist);
3385 goto out_err;
3388 filelist_free(&flist);
3391 * Prepare dd command line to copy the bootblk on the new archive and
3392 * execute it
3394 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3395 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3396 bootblk, tempname);
3398 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3400 ret = exec_cmd(cmdline, &flist);
3401 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3402 goto out_err;
3404 filelist_free(&flist);
3406 /* Did we get a valid archive ? */
3407 if (check_archive(tempname) == BAM_ERROR)
3408 return (BAM_ERROR);
3410 return (do_archive_copy(tempname, archive));
3412 out_err:
3413 filelist_free(&flist);
3414 bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline);
3415 (void) unlink(tempname);
3416 return (BAM_ERROR);
3419 static unsigned int
3420 from_733(unsigned char *s)
3422 int i;
3423 unsigned int ret = 0;
3425 for (i = 0; i < 4; i++)
3426 ret |= s[i] << (8 * i);
3428 return (ret);
3431 static void
3432 to_733(unsigned char *s, unsigned int val)
3434 int i;
3436 for (i = 0; i < 4; i++)
3437 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3441 * creates sha1 hash of archive
3443 static int
3444 digest_archive(const char *archive)
3446 char *archive_hash;
3447 char *hash;
3448 int ret;
3449 FILE *fp;
3451 (void) asprintf(&archive_hash, "%s.hash", archive);
3452 if (archive_hash == NULL)
3453 return (BAM_ERROR);
3455 if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
3456 free(archive_hash);
3457 return (ret);
3460 fp = fopen(archive_hash, "w");
3461 if (fp == NULL) {
3462 free(archive_hash);
3463 free(hash);
3464 return (BAM_ERROR);
3467 (void) fprintf(fp, "%s\n", hash);
3468 (void) fclose(fp);
3469 free(hash);
3470 free(archive_hash);
3471 return (BAM_SUCCESS);
3475 * Extends the current boot archive without recreating it from scratch
3477 static int
3478 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3480 int fd = -1, newfd = -1, ret, i;
3481 int next_session = 0, new_size = 0;
3482 char cmdline[3 * PATH_MAX + 64];
3483 const char *func = "extend_iso_archive()";
3484 filelist_t flist = {0};
3485 struct iso_pdesc saved_desc[MAX_IVDs];
3487 fd = open(archive, O_RDWR);
3488 if (fd == -1) {
3489 if (bam_verbose)
3490 bam_error(_("failed to open file: %s: %s\n"),
3491 archive, strerror(errno));
3492 goto out_err;
3496 * A partial read is likely due to a corrupted file
3498 ret = pread64(fd, saved_desc, sizeof (saved_desc),
3499 VOLDESC_OFF * CD_BLOCK);
3500 if (ret != sizeof (saved_desc)) {
3501 if (bam_verbose)
3502 bam_error(_("read failed for file: %s: %s\n"),
3503 archive, strerror(errno));
3504 goto out_err;
3507 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3508 if (bam_verbose)
3509 bam_error(_("iso descriptor signature for %s is "
3510 "invalid\n"), archive);
3511 goto out_err;
3515 * Read primary descriptor and locate next_session offset (it should
3516 * point to the end of the archive)
3518 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3520 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3521 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3522 MKISO_PARAMS, tempname, update_dir);
3524 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3526 ret = exec_cmd(cmdline, &flist);
3527 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3528 if (bam_verbose) {
3529 bam_error(_("Command '%s' failed while generating "
3530 "multisession archive\n"), cmdline);
3531 dump_errormsg(flist);
3533 goto out_flist_err;
3535 filelist_free(&flist);
3537 newfd = open(tempname, O_RDONLY);
3538 if (newfd == -1) {
3539 if (bam_verbose)
3540 bam_error(_("failed to open file: %s: %s\n"),
3541 archive, strerror(errno));
3542 goto out_err;
3545 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3546 VOLDESC_OFF * CD_BLOCK);
3547 if (ret != sizeof (saved_desc)) {
3548 if (bam_verbose)
3549 bam_error(_("read failed for file: %s: %s\n"),
3550 archive, strerror(errno));
3551 goto out_err;
3554 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3555 if (bam_verbose)
3556 bam_error(_("iso descriptor signature for %s is "
3557 "invalid\n"), archive);
3558 goto out_err;
3561 new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3562 to_733(saved_desc[0].volume_space_size, new_size);
3564 for (i = 1; i < MAX_IVDs; i++) {
3565 if (saved_desc[i].type[0] == (unsigned char)255)
3566 break;
3567 if (memcmp(saved_desc[i].id, "CD001", 5))
3568 break;
3570 if (bam_verbose)
3571 bam_print("%s: Updating descriptor entry [%d]\n", func,
3574 to_733(saved_desc[i].volume_space_size, new_size);
3577 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3578 if (ret != DVD_BLOCK) {
3579 if (bam_verbose)
3580 bam_error(_("write to file failed: %s: %s\n"),
3581 archive, strerror(errno));
3582 goto out_err;
3584 (void) close(newfd);
3585 newfd = -1;
3587 ret = fsync(fd);
3588 if (ret != 0)
3589 sync();
3591 ret = close(fd);
3592 if (ret != 0) {
3593 if (bam_verbose)
3594 bam_error(_("failed to close file: %s: %s\n"),
3595 archive, strerror(errno));
3596 return (BAM_ERROR);
3598 fd = -1;
3600 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3601 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3602 (next_session/16));
3604 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3606 ret = exec_cmd(cmdline, &flist);
3607 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3608 if (bam_verbose)
3609 bam_error(_("Command '%s' failed while generating "
3610 "multisession archive\n"), cmdline);
3611 goto out_flist_err;
3613 filelist_free(&flist);
3615 (void) unlink(tempname);
3617 if (digest_archive(archive) == BAM_ERROR && bam_verbose)
3618 bam_print("boot archive hashing failed\n");
3620 if (flushfs(bam_root) != 0)
3621 sync();
3623 if (bam_verbose)
3624 bam_print("boot archive updated successfully\n");
3626 return (BAM_SUCCESS);
3628 out_flist_err:
3629 filelist_free(&flist);
3630 out_err:
3631 if (fd != -1)
3632 (void) close(fd);
3633 if (newfd != -1)
3634 (void) close(newfd);
3635 return (BAM_ERROR);
3638 static int
3639 create_x86_archive(char *archive, char *tempname, char *update_dir)
3641 int ret;
3642 char cmdline[3 * PATH_MAX + 64];
3643 filelist_t flist = {0};
3644 const char *func = "create_x86_archive()";
3646 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3647 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3649 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3651 ret = exec_cmd(cmdline, &flist);
3652 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3653 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3654 cmdline);
3655 dump_errormsg(flist);
3656 filelist_free(&flist);
3657 (void) unlink(tempname);
3658 return (BAM_ERROR);
3661 filelist_free(&flist);
3663 if (check_archive(tempname) == BAM_ERROR)
3664 return (BAM_ERROR);
3666 return (do_archive_copy(tempname, archive));
3669 static int
3670 mkisofs_archive(char *root, int what)
3672 int ret;
3673 char temp[PATH_MAX];
3674 char bootblk[PATH_MAX];
3675 char boot_archive[PATH_MAX];
3677 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3678 ret = snprintf(temp, sizeof (temp),
3679 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3680 get_machine(), getpid());
3681 else
3682 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3683 root, ARCHIVE_PREFIX, get_machine(), getpid());
3685 if (ret >= sizeof (temp))
3686 goto out_path_err;
3688 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3689 ret = snprintf(boot_archive, sizeof (boot_archive),
3690 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3691 ARCHIVE_SUFFIX);
3692 else
3693 ret = snprintf(boot_archive, sizeof (boot_archive),
3694 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3695 ARCHIVE_SUFFIX);
3697 if (ret >= sizeof (boot_archive))
3698 goto out_path_err;
3700 bam_print("updating %s\n", boot_archive);
3702 if (is_flag_on(IS_SPARC_TARGET)) {
3703 ret = snprintf(bootblk, sizeof (bootblk),
3704 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3705 if (ret >= sizeof (bootblk))
3706 goto out_path_err;
3708 ret = create_sparc_archive(boot_archive, temp, bootblk,
3709 get_cachedir(what));
3710 } else {
3711 if (!is_dir_flag_on(what, NO_MULTI)) {
3712 if (bam_verbose)
3713 bam_print("Attempting to extend x86 archive: "
3714 "%s\n", boot_archive);
3716 ret = extend_iso_archive(boot_archive, temp,
3717 get_updatedir(what));
3718 if (ret == BAM_SUCCESS) {
3719 if (bam_verbose)
3720 bam_print("Successfully extended %s\n",
3721 boot_archive);
3723 (void) rmdir_r(get_updatedir(what));
3724 return (BAM_SUCCESS);
3728 * The boot archive will be recreated from scratch. We get here
3729 * if at least one of these conditions is true:
3730 * - bootadm was called without the -e switch
3731 * - the archive (or the archive cache) doesn't exist
3732 * - archive size is bigger than BA_SIZE_MAX
3733 * - more than COUNT_MAX files need to be updated
3734 * - an error occourred either populating the /updates directory
3735 * or extend_iso_archive() failed
3737 if (bam_verbose)
3738 bam_print("Unable to extend %s... rebuilding archive\n",
3739 boot_archive);
3741 if (get_updatedir(what)[0] != '\0')
3742 (void) rmdir_r(get_updatedir(what));
3745 ret = create_x86_archive(boot_archive, temp,
3746 get_cachedir(what));
3749 if (digest_archive(boot_archive) == BAM_ERROR && bam_verbose)
3750 bam_print("boot archive hashing failed\n");
3752 if (ret == BAM_SUCCESS && bam_verbose)
3753 bam_print("Successfully created %s\n", boot_archive);
3755 return (ret);
3757 out_path_err:
3758 bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3759 root);
3760 return (BAM_ERROR);
3763 static error_t
3764 create_ramdisk(char *root)
3766 char *cmdline, path[PATH_MAX];
3767 size_t len;
3768 struct stat sb;
3769 int ret, what, status = BAM_SUCCESS;
3771 /* If there is mkisofs, use it to create the required archives */
3772 if (is_mkisofs()) {
3773 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3774 if (has_cachedir(what) && is_dir_flag_on(what,
3775 NEED_UPDATE)) {
3776 ret = mkisofs_archive(root, what);
3777 if (ret != 0)
3778 status = BAM_ERROR;
3781 return (status);
3785 * Else setup command args for create_ramdisk.ksh for the UFS archives
3786 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3788 if (bam_verbose)
3789 bam_print("mkisofs not found, creating UFS archive\n");
3791 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3792 if (stat(path, &sb) != 0) {
3793 bam_error(_("archive creation file not found: %s: %s\n"),
3794 path, strerror(errno));
3795 return (BAM_ERROR);
3798 if (is_safe_exec(path) == BAM_ERROR)
3799 return (BAM_ERROR);
3801 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3802 if (bam_alt_platform)
3803 len += strlen(bam_platform) + strlen("-p ");
3804 cmdline = s_calloc(1, len);
3806 if (bam_alt_platform) {
3807 assert(strlen(root) > 1);
3808 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3809 path, bam_platform, root);
3810 /* chop off / at the end */
3811 cmdline[strlen(cmdline) - 1] = '\0';
3812 } else if (strlen(root) > 1) {
3813 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3814 /* chop off / at the end */
3815 cmdline[strlen(cmdline) - 1] = '\0';
3816 } else
3817 (void) snprintf(cmdline, len, "%s", path);
3819 if (exec_cmd(cmdline, NULL) != 0) {
3820 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3821 cmdline);
3822 free(cmdline);
3823 return (BAM_ERROR);
3825 free(cmdline);
3827 * The existence of the expected archives used to be
3828 * verified here. This check is done in create_ramdisk as
3829 * it needs to be in sync with the altroot operated upon.
3831 return (BAM_SUCCESS);
3835 * Checks if target filesystem is on a ramdisk
3836 * 1 - is miniroot
3837 * 0 - is not
3838 * When in doubt assume it is not a ramdisk.
3840 static int
3841 is_ramdisk(char *root)
3843 struct extmnttab mnt;
3844 FILE *fp;
3845 int found;
3846 char mntpt[PATH_MAX];
3847 char *cp;
3850 * There are 3 situations where creating archive is
3851 * of dubious value:
3852 * - create boot_archive on a lofi-mounted boot_archive
3853 * - create it on a ramdisk which is the root filesystem
3854 * - create it on a ramdisk mounted somewhere else
3855 * The first is not easy to detect and checking for it is not
3856 * worth it.
3857 * The other two conditions are handled here
3859 fp = fopen(MNTTAB, "r");
3860 if (fp == NULL) {
3861 bam_error(_("failed to open file: %s: %s\n"),
3862 MNTTAB, strerror(errno));
3863 return (0);
3866 resetmnttab(fp);
3869 * Remove any trailing / from the mount point
3871 (void) strlcpy(mntpt, root, sizeof (mntpt));
3872 if (strcmp(root, "/") != 0) {
3873 cp = mntpt + strlen(mntpt) - 1;
3874 if (*cp == '/')
3875 *cp = '\0';
3877 found = 0;
3878 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3879 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3880 found = 1;
3881 break;
3885 if (!found) {
3886 if (bam_verbose)
3887 bam_error(_("alternate root %s not in mnttab\n"),
3888 mntpt);
3889 (void) fclose(fp);
3890 return (0);
3893 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3894 strlen(RAMDISK_SPECIAL)) == 0) {
3895 if (bam_verbose)
3896 bam_error(_("%s is on a ramdisk device\n"), bam_root);
3897 (void) fclose(fp);
3898 return (1);
3901 (void) fclose(fp);
3903 return (0);
3906 static int
3907 is_boot_archive(char *root)
3909 char path[PATH_MAX];
3910 struct stat sb;
3911 int error;
3912 const char *fcn = "is_boot_archive()";
3915 * We can't create an archive without the create_ramdisk script
3917 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3918 error = stat(path, &sb);
3919 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3920 if (error == -1) {
3921 if (bam_verbose)
3922 bam_print(_("file not found: %s\n"), path);
3923 BAM_DPRINTF(("%s: not a boot archive based Solaris "
3924 "instance: %s\n", fcn, root));
3925 return (0);
3928 BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
3929 fcn, root));
3930 return (1);
3934 * Need to call this for anything that operates on the GRUB menu
3935 * In the x86 live upgrade case the directory /boot/grub may be present
3936 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3937 * is to check for the presence of the stage2 binary which is present
3938 * only on GRUB targets (even on x86 boot partitions). Checking for the
3939 * presence of the multiboot binary is not correct as it is not present
3940 * on x86 boot partitions.
3943 is_grub(const char *root)
3945 char path[PATH_MAX];
3946 struct stat sb;
3947 void *defp;
3948 boolean_t grub = B_FALSE;
3949 const char *res = NULL;
3950 const char *fcn = "is_grub()";
3952 /* grub is disabled by default */
3953 if ((defp = defopen_r(BE_DEFAULTS)) == NULL) {
3954 return (0);
3955 } else {
3956 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
3957 if (res != NULL && res[0] != '\0') {
3958 if (strcasecmp(res, "true") == 0)
3959 grub = B_TRUE;
3961 defclose_r(defp);
3964 if (grub == B_TRUE) {
3965 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3966 if (stat(path, &sb) == -1) {
3967 BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
3968 fcn, path));
3969 return (0);
3970 } else
3971 return (1);
3974 return (0);
3978 is_zfs(char *root)
3980 struct statvfs vfs;
3981 int ret;
3982 const char *fcn = "is_zfs()";
3984 ret = statvfs(root, &vfs);
3985 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3986 if (ret != 0) {
3987 bam_error(_("statvfs failed for %s: %s\n"), root,
3988 strerror(errno));
3989 return (0);
3992 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3993 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
3994 return (1);
3995 } else {
3996 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
3997 return (0);
4002 is_pcfs(char *root)
4004 struct statvfs vfs;
4005 int ret;
4006 const char *fcn = "is_pcfs()";
4008 ret = statvfs(root, &vfs);
4009 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
4010 if (ret != 0) {
4011 bam_error(_("statvfs failed for %s: %s\n"), root,
4012 strerror(errno));
4013 return (0);
4016 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
4017 BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn, root));
4018 return (1);
4019 } else {
4020 BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4021 fcn, root));
4022 return (0);
4026 static int
4027 is_readonly(char *root)
4029 int fd;
4030 int error;
4031 char testfile[PATH_MAX];
4032 const char *fcn = "is_readonly()";
4035 * Using statvfs() to check for a read-only filesystem is not
4036 * reliable. The only way to reliably test is to attempt to
4037 * create a file
4039 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
4040 root, BOOTADM_RDONLY_TEST, getpid());
4042 (void) unlink(testfile);
4044 errno = 0;
4045 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
4046 error = errno;
4047 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
4048 if (fd == -1 && error == EROFS) {
4049 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
4050 return (1);
4051 } else if (fd == -1) {
4052 bam_error(_("error during read-only test on %s: %s\n"),
4053 root, strerror(error));
4056 (void) close(fd);
4057 (void) unlink(testfile);
4059 BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
4060 return (0);
4063 static error_t
4064 update_archive(char *root, char *opt)
4066 error_t ret;
4068 assert(root);
4069 assert(opt == NULL);
4071 init_walk_args();
4072 (void) umask(022);
4075 * Never update non-BE root in update_all
4077 if (!is_be(root) && bam_update_all)
4078 return (BAM_SUCCESS);
4080 * root must belong to a boot archive based OS,
4082 if (!is_boot_archive(root)) {
4084 * Emit message only if not in context of update_all.
4085 * If in update_all, emit only if verbose flag is set.
4087 if (!bam_update_all || bam_verbose)
4088 bam_print(_("%s: not a boot archive based Solaris "
4089 "instance\n"), root);
4090 return (BAM_ERROR);
4094 * If smf check is requested when / is writable (can happen
4095 * on first reboot following an upgrade because service
4096 * dependency is messed up), skip the check.
4098 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
4099 return (BAM_SUCCESS);
4102 * Don't generate archive on ramdisk.
4104 if (is_ramdisk(root))
4105 return (BAM_SUCCESS);
4108 * root must be writable. This check applies to alternate
4109 * root (-R option); bam_root_readonly applies to '/' only.
4110 * The behaviour translates into being the one of a 'check'.
4112 if (!bam_smf_check && !bam_check && is_readonly(root)) {
4113 set_flag(RDONLY_FSCHK);
4114 bam_check = 1;
4118 * Now check if an update is really needed.
4120 ret = update_required(root);
4123 * The check command (-n) is *not* a dry run.
4124 * It only checks if the archive is in sync.
4125 * A readonly filesystem has to be considered an error only if an update
4126 * is required.
4128 if (bam_nowrite()) {
4129 if (is_flag_on(RDONLY_FSCHK)) {
4130 bam_check = bam_saved_check;
4131 if (ret > 0)
4132 bam_error(_("%s filesystem is read-only, "
4133 "skipping archives update\n"), root);
4134 if (bam_update_all)
4135 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4138 bam_exit((ret != 0) ? 1 : 0);
4141 if (ret == 1) {
4142 /* create the ramdisk */
4143 ret = create_ramdisk(root);
4147 * if the archive is updated, save the new stat data and update the
4148 * timestamp file
4150 if (ret == 0 && walk_arg.new_nvlp != NULL) {
4151 savenew(root);
4152 update_timestamp(root);
4155 clear_walk_args();
4157 return (ret);
4160 static char *
4161 find_root_pool()
4163 char *special = get_special("/");
4164 char *p;
4166 if (special == NULL)
4167 return (NULL);
4169 if (*special == '/') {
4170 free(special);
4171 return (NULL);
4174 if ((p = strchr(special, '/')) != NULL)
4175 *p = '\0';
4177 return (special);
4180 static error_t
4181 synchronize_BE_menu(void)
4183 struct stat sb;
4184 char cmdline[PATH_MAX];
4185 char cksum_line[PATH_MAX];
4186 filelist_t flist = {0};
4187 char *old_cksum_str;
4188 char *old_size_str;
4189 char *old_file;
4190 char *curr_cksum_str;
4191 char *curr_size_str;
4192 char *curr_file;
4193 char *pool = NULL;
4194 char *mntpt = NULL;
4195 zfs_mnted_t mnted;
4196 FILE *cfp;
4197 int found;
4198 int ret;
4199 const char *fcn = "synchronize_BE_menu()";
4201 BAM_DPRINTF(("%s: entered. No args\n", fcn));
4203 /* Check if findroot enabled LU BE */
4204 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4205 BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn));
4206 return (BAM_SUCCESS);
4209 if (stat(LU_MENU_CKSUM, &sb) != 0) {
4210 BAM_DPRINTF(("%s: checksum file absent: %s\n",
4211 fcn, LU_MENU_CKSUM));
4212 goto menu_sync;
4215 cfp = fopen(LU_MENU_CKSUM, "r");
4216 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4217 if (cfp == NULL) {
4218 bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4219 LU_MENU_CKSUM);
4220 goto menu_sync;
4222 BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn, LU_MENU_CKSUM));
4224 found = 0;
4225 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4226 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4227 if (found) {
4228 bam_error(_("multiple checksums for GRUB menu in "
4229 "checksum file: %s\n"), LU_MENU_CKSUM);
4230 (void) fclose(cfp);
4231 goto menu_sync;
4233 found = 1;
4235 BAM_DPRINTF(("%s: read checksum file: %s\n", fcn, LU_MENU_CKSUM));
4238 old_cksum_str = strtok(cksum_line, " \t");
4239 old_size_str = strtok(NULL, " \t");
4240 old_file = strtok(NULL, " \t");
4242 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4243 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4244 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4245 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4246 bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4247 LU_MENU_CKSUM);
4248 goto menu_sync;
4250 BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn, LU_MENU_CKSUM));
4252 /* Get checksum of current menu */
4253 pool = find_root_pool();
4254 if (pool) {
4255 mntpt = mount_top_dataset(pool, &mnted);
4256 if (mntpt == NULL) {
4257 bam_error(_("failed to mount top dataset for %s\n"),
4258 pool);
4259 free(pool);
4260 return (BAM_ERROR);
4262 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4263 CKSUM, mntpt, GRUB_MENU);
4264 } else {
4265 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4266 CKSUM, GRUB_MENU);
4268 ret = exec_cmd(cmdline, &flist);
4269 if (pool) {
4270 (void) umount_top_dataset(pool, mnted, mntpt);
4271 free(pool);
4273 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4274 if (ret != 0) {
4275 bam_error(_("error generating checksum of GRUB menu\n"));
4276 return (BAM_ERROR);
4278 BAM_DPRINTF(("%s: successfully generated checksum\n", fcn));
4280 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4281 if ((flist.head == NULL) || (flist.head != flist.tail)) {
4282 bam_error(_("bad checksum generated for GRUB menu\n"));
4283 filelist_free(&flist);
4284 return (BAM_ERROR);
4286 BAM_DPRINTF(("%s: generated checksum output valid\n", fcn));
4288 curr_cksum_str = strtok(flist.head->line, " \t");
4289 curr_size_str = strtok(NULL, " \t");
4290 curr_file = strtok(NULL, " \t");
4292 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4293 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4294 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4295 if (curr_cksum_str == NULL || curr_size_str == NULL ||
4296 curr_file == NULL) {
4297 bam_error(_("error parsing checksum generated "
4298 "for GRUB menu\n"));
4299 filelist_free(&flist);
4300 return (BAM_ERROR);
4302 BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn));
4304 if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4305 strcmp(old_size_str, curr_size_str) == 0 &&
4306 strcmp(old_file, curr_file) == 0) {
4307 filelist_free(&flist);
4308 BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn));
4309 return (BAM_SUCCESS);
4312 filelist_free(&flist);
4314 /* cksum doesn't match - the menu has changed */
4315 BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn));
4317 menu_sync:
4318 bam_print(_("propagating updated GRUB menu\n"));
4320 (void) snprintf(cmdline, sizeof (cmdline),
4321 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4322 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4323 ret = exec_cmd(cmdline, NULL);
4324 INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4325 if (ret != 0) {
4326 bam_error(_("error propagating updated GRUB menu\n"));
4327 return (BAM_ERROR);
4329 BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn));
4331 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4332 GRUB_MENU, GRUB_BACKUP_MENU);
4333 ret = exec_cmd(cmdline, NULL);
4334 INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4335 if (ret != 0) {
4336 bam_error(_("failed to create backup for GRUB menu: %s\n"),
4337 GRUB_BACKUP_MENU);
4338 return (BAM_ERROR);
4340 BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4341 fcn, GRUB_BACKUP_MENU));
4343 (void) snprintf(cmdline, sizeof (cmdline),
4344 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4345 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4346 ret = exec_cmd(cmdline, NULL);
4347 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4348 if (ret != 0) {
4349 bam_error(_("error propagating backup GRUB menu: %s\n"),
4350 GRUB_BACKUP_MENU);
4351 return (BAM_ERROR);
4353 BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4354 fcn, GRUB_BACKUP_MENU));
4356 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4357 CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4358 ret = exec_cmd(cmdline, NULL);
4359 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4360 if (ret != 0) {
4361 bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4362 LU_MENU_CKSUM);
4363 return (BAM_ERROR);
4365 BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4366 fcn, LU_MENU_CKSUM));
4368 (void) snprintf(cmdline, sizeof (cmdline),
4369 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4370 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4371 ret = exec_cmd(cmdline, NULL);
4372 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4373 if (ret != 0) {
4374 bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4375 LU_MENU_CKSUM);
4376 return (BAM_ERROR);
4378 BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4379 fcn, LU_MENU_CKSUM));
4381 return (BAM_SUCCESS);
4384 static error_t
4385 update_all(char *root, char *opt)
4387 struct extmnttab mnt;
4388 struct stat sb;
4389 FILE *fp;
4390 char multibt[PATH_MAX];
4391 char creatram[PATH_MAX];
4392 error_t ret = BAM_SUCCESS;
4394 assert(root);
4395 assert(opt == NULL);
4397 if (bam_rootlen != 1 || *root != '/') {
4398 elide_trailing_slash(root, multibt, sizeof (multibt));
4399 bam_error(_("an alternate root (%s) cannot be used with this "
4400 "sub-command\n"), multibt);
4401 return (BAM_ERROR);
4405 * First update archive for current root
4407 if (update_archive(root, opt) != BAM_SUCCESS)
4408 ret = BAM_ERROR;
4410 if (ret == BAM_ERROR)
4411 goto out;
4414 * Now walk the mount table, performing archive update
4415 * for all mounted Newboot root filesystems
4417 fp = fopen(MNTTAB, "r");
4418 if (fp == NULL) {
4419 bam_error(_("failed to open file: %s: %s\n"),
4420 MNTTAB, strerror(errno));
4421 ret = BAM_ERROR;
4422 goto out;
4425 resetmnttab(fp);
4427 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4428 if (mnt.mnt_special == NULL)
4429 continue;
4430 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4431 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4432 continue;
4433 if (strcmp(mnt.mnt_mountp, "/") == 0)
4434 continue;
4436 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4437 mnt.mnt_mountp, CREATE_RAMDISK);
4439 if (stat(creatram, &sb) == -1)
4440 continue;
4443 * We put a trailing slash to be consistent with root = "/"
4444 * case, such that we don't have to print // in some cases.
4446 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4447 mnt.mnt_mountp);
4448 bam_rootlen = strlen(rootbuf);
4451 * It's possible that other mounts may be an alternate boot
4452 * architecture, so check it again.
4454 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4455 (update_archive(rootbuf, opt) != BAM_SUCCESS))
4456 ret = BAM_ERROR;
4459 (void) fclose(fp);
4461 out:
4463 * We no longer use biosdev for Live Upgrade. Hence
4464 * there is no need to defer (to shutdown time) any fdisk
4465 * updates
4467 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4468 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4469 "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
4473 * If user has updated menu in current BE, propagate the
4474 * updates to all BEs.
4476 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4477 ret = BAM_ERROR;
4479 return (ret);
4482 static void
4483 append_line(menu_t *mp, line_t *lp)
4485 if (mp->start == NULL) {
4486 mp->start = lp;
4487 } else {
4488 mp->end->next = lp;
4489 lp->prev = mp->end;
4491 mp->end = lp;
4494 void
4495 unlink_line(menu_t *mp, line_t *lp)
4497 /* unlink from list */
4498 if (lp->prev)
4499 lp->prev->next = lp->next;
4500 else
4501 mp->start = lp->next;
4502 if (lp->next)
4503 lp->next->prev = lp->prev;
4504 else
4505 mp->end = lp->prev;
4508 static entry_t *
4509 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4511 entry_t *ent, *prev;
4512 const char *fcn = "boot_entry_new()";
4514 assert(mp);
4515 assert(start);
4516 assert(end);
4518 ent = s_calloc(1, sizeof (entry_t));
4519 BAM_DPRINTF(("%s: new boot entry alloced\n", fcn));
4520 ent->start = start;
4521 ent->end = end;
4523 if (mp->entries == NULL) {
4524 mp->entries = ent;
4525 BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn));
4526 return (ent);
4529 prev = mp->entries;
4530 while (prev->next)
4531 prev = prev->next;
4532 prev->next = ent;
4533 ent->prev = prev;
4534 BAM_DPRINTF(("%s: new boot entry linked in\n", fcn));
4535 return (ent);
4538 static void
4539 boot_entry_addline(entry_t *ent, line_t *lp)
4541 if (ent)
4542 ent->end = lp;
4546 * Check whether cmd matches the one indexed by which, and whether arg matches
4547 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4548 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4549 * strstr(), so it can be a partial match.
4551 static int
4552 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4554 int ret;
4555 const char *fcn = "check_cmd()";
4557 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, arg, str));
4559 if (cmd != NULL) {
4560 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4561 (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4562 BAM_DPRINTF(("%s: command %s does not match %s\n",
4563 fcn, cmd, menu_cmds[which]));
4564 return (0);
4566 ret = (strstr(arg, str) != NULL);
4567 } else
4568 ret = 0;
4570 if (ret) {
4571 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
4572 } else {
4573 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
4576 return (ret);
4579 static error_t
4580 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4582 const char *fcn = "kernel_parser()";
4584 assert(entry);
4585 assert(cmd);
4586 assert(arg);
4588 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4589 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4590 BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn, cmd));
4591 return (BAM_ERROR);
4594 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4595 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4596 fcn, arg));
4597 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4598 } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4599 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4600 BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn, arg));
4601 entry->flags |= BAM_ENTRY_DBOOT;
4602 } else if (strncmp(arg, DIRECT_BOOT_64,
4603 sizeof (DIRECT_BOOT_64) - 1) == 0) {
4604 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4605 fcn, arg));
4606 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4607 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4608 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4609 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4610 fcn, arg));
4611 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4612 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4613 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4614 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4615 "flag: %s\n", fcn, arg));
4616 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4617 | BAM_ENTRY_32BIT;
4618 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4619 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4620 BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4621 "flag: %s\n", fcn, arg));
4622 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4623 | BAM_ENTRY_64BIT;
4624 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4625 BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn, arg));
4626 entry->flags |= BAM_ENTRY_MULTIBOOT;
4627 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4628 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4629 BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4630 "flag: %s\n", fcn, arg));
4631 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4632 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4633 BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn, arg));
4634 entry->flags |= BAM_ENTRY_HV;
4635 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4636 BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn, arg));
4637 return (BAM_ERROR);
4638 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4639 strstr(arg, UNIX_SPACE)) {
4640 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4641 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4642 strstr(arg, AMD_UNIX_SPACE)) {
4643 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4644 } else {
4645 BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn, arg));
4646 bam_error(_("kernel command on line %d not recognized.\n"),
4647 linenum);
4648 return (BAM_ERROR);
4651 return (BAM_SUCCESS);
4654 static error_t
4655 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4657 const char *fcn = "module_parser()";
4659 assert(entry);
4660 assert(cmd);
4661 assert(arg);
4663 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4664 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4665 BAM_DPRINTF(("%s: not module cmd: %s\n", fcn, cmd));
4666 return (BAM_ERROR);
4669 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4670 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4671 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4672 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4673 strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4674 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4675 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4676 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4677 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4678 BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn, arg));
4679 return (BAM_SUCCESS);
4680 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4681 !(entry->flags & BAM_ENTRY_LU)) {
4682 /* don't emit warning for hand entries */
4683 BAM_DPRINTF(("%s: is HAND module: %s\n", fcn, arg));
4684 return (BAM_ERROR);
4685 } else {
4686 BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn, arg));
4687 bam_error(_("module command on line %d not recognized.\n"),
4688 linenum);
4689 return (BAM_ERROR);
4694 * A line in menu.lst looks like
4695 * [ ]*<cmd>[ \t=]*<arg>*
4697 static void
4698 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4701 * save state across calls. This is so that
4702 * header gets the right entry# after title has
4703 * been processed
4705 static line_t *prev = NULL;
4706 static entry_t *curr_ent = NULL;
4707 static int in_liveupgrade = 0;
4708 static int is_libbe_ent = 0;
4710 line_t *lp;
4711 char *cmd, *sep, *arg;
4712 char save, *cp, *line;
4713 menu_flag_t flag = BAM_INVALID;
4714 const char *fcn = "line_parser()";
4716 cmd = NULL;
4717 if (str == NULL) {
4718 return;
4722 * First save a copy of the entire line.
4723 * We use this later to set the line field.
4725 line = s_strdup(str);
4727 /* Eat up leading whitespace */
4728 while (*str == ' ' || *str == '\t')
4729 str++;
4731 if (*str == '#') { /* comment */
4732 cmd = s_strdup("#");
4733 sep = NULL;
4734 arg = s_strdup(str + 1);
4735 flag = BAM_COMMENT;
4736 if (strstr(arg, BAM_LU_HDR) != NULL) {
4737 in_liveupgrade = 1;
4738 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4739 in_liveupgrade = 0;
4740 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4741 is_libbe_ent = 1;
4743 } else if (*str == '\0') { /* blank line */
4744 cmd = sep = arg = NULL;
4745 flag = BAM_EMPTY;
4746 } else {
4748 * '=' is not a documented separator in grub syntax.
4749 * However various development bits use '=' as a
4750 * separator. In addition, external users also
4751 * use = as a separator. So we will allow that usage.
4753 cp = str;
4754 while (*str != ' ' && *str != '\t' && *str != '=') {
4755 if (*str == '\0') {
4756 cmd = s_strdup(cp);
4757 sep = arg = NULL;
4758 break;
4760 str++;
4763 if (*str != '\0') {
4764 save = *str;
4765 *str = '\0';
4766 cmd = s_strdup(cp);
4767 *str = save;
4769 str++;
4770 save = *str;
4771 *str = '\0';
4772 sep = s_strdup(str - 1);
4773 *str = save;
4775 while (*str == ' ' || *str == '\t')
4776 str++;
4777 if (*str == '\0')
4778 arg = NULL;
4779 else
4780 arg = s_strdup(str);
4784 lp = s_calloc(1, sizeof (line_t));
4786 lp->cmd = cmd;
4787 lp->sep = sep;
4788 lp->arg = arg;
4789 lp->line = line;
4790 lp->lineNum = ++(*lineNum);
4791 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4792 lp->entryNum = ++(*entryNum);
4793 lp->flags = BAM_TITLE;
4794 if (prev && prev->flags == BAM_COMMENT &&
4795 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4796 prev->entryNum = lp->entryNum;
4797 curr_ent = boot_entry_new(mp, prev, lp);
4798 curr_ent->flags |= BAM_ENTRY_BOOTADM;
4799 BAM_DPRINTF(("%s: is bootadm(1M) entry: %s\n",
4800 fcn, arg));
4801 } else {
4802 curr_ent = boot_entry_new(mp, lp, lp);
4803 if (in_liveupgrade) {
4804 curr_ent->flags |= BAM_ENTRY_LU;
4805 BAM_DPRINTF(("%s: is LU entry: %s\n",
4806 fcn, arg));
4809 curr_ent->entryNum = *entryNum;
4810 } else if (flag != BAM_INVALID) {
4812 * For header comments, the entry# is "fixed up"
4813 * by the subsequent title
4815 lp->entryNum = *entryNum;
4816 lp->flags = flag;
4817 } else {
4818 lp->entryNum = *entryNum;
4820 if (*entryNum == ENTRY_INIT) {
4821 lp->flags = BAM_GLOBAL;
4822 } else {
4823 lp->flags = BAM_ENTRY;
4825 if (cmd && arg) {
4826 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4827 BAM_DPRINTF(("%s: setting ROOT: %s\n",
4828 fcn, arg));
4829 curr_ent->flags |= BAM_ENTRY_ROOT;
4830 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4831 == 0) {
4832 BAM_DPRINTF(("%s: setting "
4833 "FINDROOT: %s\n", fcn, arg));
4834 curr_ent->flags |= BAM_ENTRY_FINDROOT;
4835 } else if (strcmp(cmd,
4836 menu_cmds[CHAINLOADER_CMD]) == 0) {
4837 BAM_DPRINTF(("%s: setting "
4838 "CHAINLOADER: %s\n", fcn, arg));
4839 curr_ent->flags |=
4840 BAM_ENTRY_CHAINLOADER;
4841 } else if (kernel_parser(curr_ent, cmd, arg,
4842 lp->lineNum) != BAM_SUCCESS) {
4843 (void) module_parser(curr_ent, cmd,
4844 arg, lp->lineNum);
4850 /* record default, old default, and entry line ranges */
4851 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4852 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4853 mp->curdefault = lp;
4854 } else if (lp->flags == BAM_COMMENT &&
4855 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4856 mp->olddefault = lp;
4857 } else if (lp->flags == BAM_COMMENT &&
4858 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4859 mp->old_rc_default = lp;
4860 } else if (lp->flags == BAM_ENTRY ||
4861 (lp->flags == BAM_COMMENT &&
4862 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4863 if (is_libbe_ent) {
4864 curr_ent->flags |= BAM_ENTRY_LIBBE;
4865 is_libbe_ent = 0;
4868 boot_entry_addline(curr_ent, lp);
4870 append_line(mp, lp);
4872 prev = lp;
4875 void
4876 update_numbering(menu_t *mp)
4878 int lineNum;
4879 int entryNum;
4880 int old_default_value;
4881 line_t *lp, *prev, *default_lp, *default_entry;
4882 char buf[PATH_MAX];
4884 if (mp->start == NULL) {
4885 return;
4888 lineNum = LINE_INIT;
4889 entryNum = ENTRY_INIT;
4890 old_default_value = ENTRY_INIT;
4891 lp = default_lp = default_entry = NULL;
4893 prev = NULL;
4894 for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4895 lp->lineNum = ++lineNum;
4898 * Get the value of the default command
4900 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4901 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4902 lp->arg) {
4903 old_default_value = atoi(lp->arg);
4904 default_lp = lp;
4908 * If not a booting entry, nothing else to fix for this
4909 * entry
4911 if (lp->entryNum == ENTRY_INIT)
4912 continue;
4915 * Record the position of the default entry.
4916 * The following works because global
4917 * commands like default and timeout should precede
4918 * actual boot entries, so old_default_value
4919 * is already known (or default cmd is missing).
4921 if (default_entry == NULL &&
4922 old_default_value != ENTRY_INIT &&
4923 lp->entryNum == old_default_value) {
4924 default_entry = lp;
4928 * Now fixup the entry number
4930 if (lp->cmd != NULL &&
4931 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4932 lp->entryNum = ++entryNum;
4933 /* fixup the bootadm header */
4934 if (prev && prev->flags == BAM_COMMENT &&
4935 prev->arg &&
4936 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4937 prev->entryNum = lp->entryNum;
4939 } else {
4940 lp->entryNum = entryNum;
4945 * No default command in menu, simply return
4947 if (default_lp == NULL) {
4948 return;
4951 free(default_lp->arg);
4952 free(default_lp->line);
4954 if (default_entry == NULL) {
4955 default_lp->arg = s_strdup("0");
4956 } else {
4957 (void) snprintf(buf, sizeof (buf), "%d",
4958 default_entry->entryNum);
4959 default_lp->arg = s_strdup(buf);
4963 * The following is required since only the line field gets
4964 * written back to menu.lst
4966 (void) snprintf(buf, sizeof (buf), "%s%s%s",
4967 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4968 default_lp->line = s_strdup(buf);
4972 static menu_t *
4973 menu_read(char *menu_path)
4975 FILE *fp;
4976 char buf[BAM_MAXLINE], *cp;
4977 menu_t *mp;
4978 int line, entry, len, n;
4980 mp = s_calloc(1, sizeof (menu_t));
4982 fp = fopen(menu_path, "r");
4983 if (fp == NULL) { /* Let the caller handle this error */
4984 free(mp);
4985 return (NULL);
4988 /* Note: GRUB boot entry number starts with 0 */
4989 line = LINE_INIT;
4990 entry = ENTRY_INIT;
4991 cp = buf;
4992 len = sizeof (buf);
4993 while (s_fgets(cp, len, fp) != NULL) {
4994 n = strlen(cp);
4995 if (cp[n - 1] == '\\') {
4996 len -= n - 1;
4997 assert(len >= 2);
4998 cp += n - 1;
4999 continue;
5001 line_parser(mp, buf, &line, &entry);
5002 cp = buf;
5003 len = sizeof (buf);
5006 if (fclose(fp) == EOF) {
5007 bam_error(_("failed to close file: %s: %s\n"), menu_path,
5008 strerror(errno));
5011 return (mp);
5014 static error_t
5015 selector(menu_t *mp, char *opt, int *entry, char **title)
5017 char *eq;
5018 char *opt_dup;
5019 int entryNum;
5021 assert(mp);
5022 assert(mp->start);
5023 assert(opt);
5025 opt_dup = s_strdup(opt);
5027 if (entry)
5028 *entry = ENTRY_INIT;
5029 if (title)
5030 *title = NULL;
5032 eq = strchr(opt_dup, '=');
5033 if (eq == NULL) {
5034 bam_error(_("invalid option: %s\n"), opt);
5035 free(opt_dup);
5036 return (BAM_ERROR);
5039 *eq = '\0';
5040 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
5041 assert(mp->end);
5042 entryNum = s_strtol(eq + 1);
5043 if (entryNum < 0 || entryNum > mp->end->entryNum) {
5044 bam_error(_("invalid boot entry number: %s\n"), eq + 1);
5045 free(opt_dup);
5046 return (BAM_ERROR);
5048 *entry = entryNum;
5049 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
5050 *title = opt + (eq - opt_dup) + 1;
5051 } else {
5052 bam_error(_("invalid option: %s\n"), opt);
5053 free(opt_dup);
5054 return (BAM_ERROR);
5057 free(opt_dup);
5058 return (BAM_SUCCESS);
5062 * If invoked with no titles/entries (opt == NULL)
5063 * only title lines in file are printed.
5065 * If invoked with a title or entry #, all
5066 * lines in *every* matching entry are listed
5068 static error_t
5069 list_entry(menu_t *mp, char *menu_path, char *opt)
5071 line_t *lp;
5072 int entry = ENTRY_INIT;
5073 int found;
5074 char *title = NULL;
5076 assert(mp);
5077 assert(menu_path);
5079 /* opt is optional */
5080 BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path,
5081 opt ? opt : "<NULL>"));
5083 if (mp->start == NULL) {
5084 bam_error(_("menu file not found: %s\n"), menu_path);
5085 return (BAM_ERROR);
5088 if (opt != NULL) {
5089 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
5090 return (BAM_ERROR);
5092 assert((entry != ENTRY_INIT) ^ (title != NULL));
5093 } else {
5094 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
5095 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
5098 found = 0;
5099 for (lp = mp->start; lp; lp = lp->next) {
5100 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
5101 continue;
5102 if (opt == NULL && lp->flags == BAM_TITLE) {
5103 bam_print(_("%d %s\n"), lp->entryNum,
5104 lp->arg);
5105 found = 1;
5106 continue;
5108 if (entry != ENTRY_INIT && lp->entryNum == entry) {
5109 bam_print(_("%s\n"), lp->line);
5110 found = 1;
5111 continue;
5115 * We set the entry value here so that all lines
5116 * in entry get printed. If we subsequently match
5117 * title in other entries, all lines in those
5118 * entries get printed as well.
5120 if (title && lp->flags == BAM_TITLE && lp->arg &&
5121 strncmp(title, lp->arg, strlen(title)) == 0) {
5122 bam_print(_("%s\n"), lp->line);
5123 entry = lp->entryNum;
5124 found = 1;
5125 continue;
5129 if (!found) {
5130 bam_error(_("no matching entry found\n"));
5131 return (BAM_ERROR);
5134 return (BAM_SUCCESS);
5138 add_boot_entry(menu_t *mp,
5139 char *title,
5140 char *findroot,
5141 char *kernel,
5142 char *mod_kernel,
5143 char *module,
5144 char *bootfs)
5146 int lineNum;
5147 int entryNum;
5148 char linebuf[BAM_MAXLINE];
5149 menu_cmd_t k_cmd;
5150 menu_cmd_t m_cmd;
5151 const char *fcn = "add_boot_entry()";
5153 assert(mp);
5155 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
5156 if (findroot == NULL) {
5157 bam_error(_("can't find argument for findroot command\n"));
5158 return (BAM_ERROR);
5161 if (title == NULL) {
5162 title = "Solaris"; /* default to Solaris */
5164 if (kernel == NULL) {
5165 bam_error(_("missing suboption: %s\n"), menu_cmds[KERNEL_CMD]);
5166 return (BAM_ERROR);
5168 if (module == NULL) {
5169 if (bam_direct != BAM_DIRECT_DBOOT) {
5170 bam_error(_("missing suboption: %s\n"),
5171 menu_cmds[MODULE_CMD]);
5172 return (BAM_ERROR);
5175 /* Figure the commands out from the kernel line */
5176 if (strstr(kernel, "$ISADIR") != NULL) {
5177 module = DIRECT_BOOT_ARCHIVE;
5178 } else if (strstr(kernel, "amd64") != NULL) {
5179 module = DIRECT_BOOT_ARCHIVE_64;
5180 } else {
5181 module = DIRECT_BOOT_ARCHIVE_32;
5185 k_cmd = KERNEL_DOLLAR_CMD;
5186 m_cmd = MODULE_DOLLAR_CMD;
5188 if (mp->start) {
5189 lineNum = mp->end->lineNum;
5190 entryNum = mp->end->entryNum;
5191 } else {
5192 lineNum = LINE_INIT;
5193 entryNum = ENTRY_INIT;
5197 * No separator for comment (HDR/FTR) commands
5198 * The syntax for comments is #<comment>
5200 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5201 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5202 line_parser(mp, linebuf, &lineNum, &entryNum);
5204 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5205 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5206 line_parser(mp, linebuf, &lineNum, &entryNum);
5208 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5209 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5210 line_parser(mp, linebuf, &lineNum, &entryNum);
5211 BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5212 fcn, lineNum, entryNum));
5214 if (bootfs != NULL) {
5215 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5216 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5217 line_parser(mp, linebuf, &lineNum, &entryNum);
5220 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5221 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5222 line_parser(mp, linebuf, &lineNum, &entryNum);
5224 if (mod_kernel != NULL) {
5225 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5226 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5227 line_parser(mp, linebuf, &lineNum, &entryNum);
5230 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5231 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5232 line_parser(mp, linebuf, &lineNum, &entryNum);
5234 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5235 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5236 line_parser(mp, linebuf, &lineNum, &entryNum);
5238 return (entryNum);
5241 error_t
5242 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5244 line_t *lp;
5245 line_t *freed;
5246 entry_t *ent;
5247 entry_t *tmp;
5248 int deleted = 0;
5249 const char *fcn = "delete_boot_entry()";
5251 assert(entryNum != ENTRY_INIT);
5253 tmp = NULL;
5255 ent = mp->entries;
5256 while (ent) {
5257 lp = ent->start;
5260 * Check entry number and make sure it's a modifiable entry.
5262 * Guidelines:
5263 * + We can modify a bootadm-created entry
5264 * + We can modify a libbe-created entry
5266 if ((lp->flags != BAM_COMMENT &&
5267 (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5268 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5269 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5270 ent = ent->next;
5271 continue;
5274 /* free the entry content */
5275 do {
5276 freed = lp;
5277 lp = lp->next; /* prev stays the same */
5278 BAM_DPRINTF(("%s: freeing line: %d\n",
5279 fcn, freed->lineNum));
5280 unlink_line(mp, freed);
5281 line_free(freed);
5282 } while (freed != ent->end);
5284 /* free the entry_t structure */
5285 assert(tmp == NULL);
5286 tmp = ent;
5287 ent = ent->next;
5288 if (tmp->prev)
5289 tmp->prev->next = ent;
5290 else
5291 mp->entries = ent;
5292 if (ent)
5293 ent->prev = tmp->prev;
5294 BAM_DPRINTF(("%s: freeing entry: %d\n", fcn, tmp->entryNum));
5295 free(tmp);
5296 tmp = NULL;
5297 deleted = 1;
5300 assert(tmp == NULL);
5302 if (!deleted && entryNum != ALL_ENTRIES) {
5303 if (quiet == DBE_PRINTERR)
5304 bam_error(_("no matching bootadm entry found\n"));
5305 return (BAM_ERROR);
5309 * Now that we have deleted an entry, update
5310 * the entry numbering and the default cmd.
5312 update_numbering(mp);
5314 return (BAM_SUCCESS);
5317 static error_t
5318 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5320 assert(mp);
5321 assert(dummy == NULL);
5322 assert(opt == NULL);
5324 BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5326 if (mp->start == NULL) {
5327 bam_print(_("the GRUB menu is empty\n"));
5328 return (BAM_SUCCESS);
5331 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5332 return (BAM_ERROR);
5335 return (BAM_WRITE);
5338 static FILE *
5339 create_diskmap(char *osroot)
5341 FILE *fp;
5342 char cmd[PATH_MAX + 16];
5343 char path[PATH_MAX];
5344 const char *fcn = "create_diskmap()";
5346 /* make sure we have a map file */
5347 fp = fopen(GRUBDISK_MAP, "r");
5348 if (fp == NULL) {
5349 int ret;
5351 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5352 CREATE_DISKMAP);
5353 if (ret >= sizeof (path)) {
5354 bam_error(_("unable to create path on mountpoint %s, "
5355 "path too long\n"), osroot);
5356 return (NULL);
5358 if (is_safe_exec(path) == BAM_ERROR)
5359 return (NULL);
5361 (void) snprintf(cmd, sizeof (cmd),
5362 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5363 if (exec_cmd(cmd, NULL) != 0)
5364 return (NULL);
5365 fp = fopen(GRUBDISK_MAP, "r");
5366 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5367 if (fp) {
5368 BAM_DPRINTF(("%s: created diskmap file: %s\n",
5369 fcn, GRUBDISK_MAP));
5370 } else {
5371 BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5372 fcn, GRUBDISK_MAP));
5375 return (fp);
5378 #define SECTOR_SIZE 512
5380 static int
5381 get_partition(char *device)
5383 int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5384 struct mboot *mboot;
5385 char boot_sect[SECTOR_SIZE];
5386 char *wholedisk, *slice;
5387 #ifdef i386
5388 ext_part_t *epp;
5389 uint32_t secnum, numsec;
5390 int rval, pno, ext_partno = PARTNO_NOTFOUND;
5391 #endif
5393 /* form whole disk (p0) */
5394 slice = device + strlen(device) - 2;
5395 is_pcfs = (*slice != 's');
5396 if (!is_pcfs)
5397 *slice = '\0';
5398 wholedisk = s_calloc(1, strlen(device) + 3);
5399 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5400 if (!is_pcfs)
5401 *slice = 's';
5403 /* read boot sector */
5404 fd = open(wholedisk, O_RDONLY);
5405 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5406 return (partno);
5408 (void) close(fd);
5410 #ifdef i386
5411 /* Read/Initialize extended partition information */
5412 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5413 != FDISK_SUCCESS) {
5414 switch (rval) {
5416 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5417 * be considered as soft errors and hence
5418 * we do not return
5420 case FDISK_EBADLOGDRIVE:
5421 break;
5422 case FDISK_ENOLOGDRIVE:
5423 break;
5424 case FDISK_EBADMAGIC:
5425 /*FALLTHROUGH*/
5426 default:
5427 free(wholedisk);
5428 libfdisk_fini(&epp);
5429 return (partno);
5432 #endif
5433 free(wholedisk);
5435 /* parse fdisk table */
5436 mboot = (struct mboot *)((void *)boot_sect);
5437 for (i = 0; i < FD_NUMPART; i++) {
5438 struct ipart *part =
5439 (struct ipart *)(uintptr_t)mboot->parts + i;
5440 if (is_pcfs) { /* looking for solaris boot part */
5441 if (part->systid == 0xbe) {
5442 partno = i;
5443 break;
5445 } else { /* look for solaris partition, old and new */
5446 if (part->systid == EFI_PMBR) {
5447 partno = PARTNO_EFI;
5448 break;
5451 #ifdef i386
5452 if ((part->systid == SUNIXOS &&
5453 (fdisk_is_linux_swap(epp, part->relsect,
5454 NULL) != 0)) || part->systid == SUNIXOS2) {
5455 #else
5456 if (part->systid == SUNIXOS ||
5457 part->systid == SUNIXOS2) {
5458 #endif
5459 partno = i;
5460 break;
5463 #ifdef i386
5464 if (fdisk_is_dos_extended(part->systid))
5465 ext_partno = i;
5466 #endif
5469 #ifdef i386
5470 /* If no primary solaris partition, check extended partition */
5471 if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5472 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5473 if (rval == FDISK_SUCCESS) {
5474 partno = pno - 1;
5477 libfdisk_fini(&epp);
5478 #endif
5479 return (partno);
5482 char *
5483 get_grubroot(char *osroot, char *osdev, char *menu_root)
5485 char *grubroot; /* (hd#,#,#) */
5486 char *slice;
5487 char *grubhd = NULL;
5488 int fdiskpart;
5489 int found = 0;
5490 char *devname;
5491 char *ctdname = strstr(osdev, "dsk/");
5492 char linebuf[PATH_MAX];
5493 FILE *fp;
5495 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5496 if (ctdname == NULL) {
5497 bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev);
5498 return (NULL);
5501 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5502 /* menu bears no resemblance to our reality */
5503 bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5504 "bootdisk: %s\n"), osdev);
5505 return (NULL);
5508 ctdname += strlen("dsk/");
5509 slice = strrchr(ctdname, 's');
5510 if (slice)
5511 *slice = '\0';
5513 fp = create_diskmap(osroot);
5514 if (fp == NULL) {
5515 bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5516 osroot);
5517 return (NULL);
5520 rewind(fp);
5521 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5522 grubhd = strtok(linebuf, " \t\n");
5523 if (grubhd)
5524 devname = strtok(NULL, " \t\n");
5525 else
5526 devname = NULL;
5527 if (devname && strcmp(devname, ctdname) == 0) {
5528 found = 1;
5529 break;
5533 if (slice)
5534 *slice = 's';
5536 (void) fclose(fp);
5537 fp = NULL;
5539 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5540 if (found == 0) {
5541 bam_error(_("not using biosdev command for disk: %s.\n"),
5542 osdev);
5543 return (NULL);
5546 fdiskpart = get_partition(osdev);
5547 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5548 if (fdiskpart == PARTNO_NOTFOUND) {
5549 bam_error(_("failed to determine fdisk partition: %s\n"),
5550 osdev);
5551 return (NULL);
5554 grubroot = s_calloc(1, 10);
5555 if (fdiskpart == PARTNO_EFI) {
5556 fdiskpart = atoi(&slice[1]);
5557 slice = NULL;
5560 if (slice) {
5561 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5562 grubhd, fdiskpart, slice[1] + 'a' - '0');
5563 } else
5564 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5565 grubhd, fdiskpart);
5567 assert(fp == NULL);
5568 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5569 return (grubroot);
5572 static char *
5573 find_primary_common(char *mntpt, char *fstype)
5575 char signdir[PATH_MAX];
5576 char tmpsign[MAXNAMELEN + 1];
5577 char *lu;
5578 char *ufs;
5579 char *zfs;
5580 DIR *dirp = NULL;
5581 struct dirent *entp;
5582 struct stat sb;
5583 const char *fcn = "find_primary_common()";
5585 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5586 mntpt, GRUBSIGN_DIR);
5588 if (stat(signdir, &sb) == -1) {
5589 BAM_DPRINTF(("%s: no sign dir: %s\n", fcn, signdir));
5590 return (NULL);
5593 dirp = opendir(signdir);
5594 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5595 if (dirp == NULL) {
5596 bam_error(_("opendir of %s failed: %s\n"), signdir,
5597 strerror(errno));
5598 return (NULL);
5601 ufs = zfs = lu = NULL;
5603 while ((entp = readdir(dirp)) != NULL) {
5604 if (strcmp(entp->d_name, ".") == 0 ||
5605 strcmp(entp->d_name, "..") == 0)
5606 continue;
5608 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5610 if (lu == NULL &&
5611 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5612 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5613 lu = s_strdup(tmpsign);
5616 if (ufs == NULL &&
5617 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5618 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5619 ufs = s_strdup(tmpsign);
5622 if (zfs == NULL &&
5623 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5624 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5625 zfs = s_strdup(tmpsign);
5629 BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn,
5630 zfs ? zfs : "NULL",
5631 ufs ? ufs : "NULL",
5632 lu ? lu : "NULL"));
5634 if (dirp) {
5635 (void) closedir(dirp);
5636 dirp = NULL;
5639 if (strcmp(fstype, "ufs") == 0 && zfs) {
5640 bam_error(_("found mismatched boot signature %s for "
5641 "filesystem type: %s.\n"), zfs, "ufs");
5642 free(zfs);
5643 zfs = NULL;
5644 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5645 bam_error(_("found mismatched boot signature %s for "
5646 "filesystem type: %s.\n"), ufs, "zfs");
5647 free(ufs);
5648 ufs = NULL;
5651 assert(dirp == NULL);
5653 /* For now, we let Live Upgrade take care of its signature itself */
5654 if (lu) {
5655 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5656 free(lu);
5657 lu = NULL;
5660 return (zfs ? zfs : ufs);
5663 static char *
5664 find_backup_common(char *mntpt, char *fstype)
5666 FILE *bfp = NULL;
5667 char tmpsign[MAXNAMELEN + 1];
5668 char backup[PATH_MAX];
5669 char *ufs;
5670 char *zfs;
5671 char *lu;
5672 int error;
5673 const char *fcn = "find_backup_common()";
5676 * We didn't find it in the primary directory.
5677 * Look at the backup
5679 (void) snprintf(backup, sizeof (backup), "%s%s",
5680 mntpt, GRUBSIGN_BACKUP);
5682 bfp = fopen(backup, "r");
5683 if (bfp == NULL) {
5684 error = errno;
5685 if (bam_verbose) {
5686 bam_error(_("failed to open file: %s: %s\n"),
5687 backup, strerror(error));
5689 BAM_DPRINTF(("%s: failed to open %s: %s\n",
5690 fcn, backup, strerror(error)));
5691 return (NULL);
5694 ufs = zfs = lu = NULL;
5696 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5698 if (lu == NULL &&
5699 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5700 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5701 lu = s_strdup(tmpsign);
5704 if (ufs == NULL &&
5705 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5706 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5707 ufs = s_strdup(tmpsign);
5710 if (zfs == NULL &&
5711 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5712 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5713 zfs = s_strdup(tmpsign);
5717 BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn,
5718 zfs ? zfs : "NULL",
5719 ufs ? ufs : "NULL",
5720 lu ? lu : "NULL"));
5722 if (bfp) {
5723 (void) fclose(bfp);
5724 bfp = NULL;
5727 if (strcmp(fstype, "ufs") == 0 && zfs) {
5728 bam_error(_("found mismatched boot signature %s for "
5729 "filesystem type: %s.\n"), zfs, "ufs");
5730 free(zfs);
5731 zfs = NULL;
5732 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5733 bam_error(_("found mismatched boot signature %s for "
5734 "filesystem type: %s.\n"), ufs, "zfs");
5735 free(ufs);
5736 ufs = NULL;
5739 assert(bfp == NULL);
5741 /* For now, we let Live Upgrade take care of its signature itself */
5742 if (lu) {
5743 BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5744 free(lu);
5745 lu = NULL;
5748 return (zfs ? zfs : ufs);
5751 static char *
5752 find_ufs_existing(char *osroot)
5754 char *sign;
5755 const char *fcn = "find_ufs_existing()";
5757 sign = find_primary_common(osroot, "ufs");
5758 if (sign == NULL) {
5759 sign = find_backup_common(osroot, "ufs");
5760 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
5761 sign ? sign : "NULL"));
5762 } else {
5763 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
5766 return (sign);
5769 char *
5770 get_mountpoint(char *special, char *fstype)
5772 FILE *mntfp;
5773 struct mnttab mp = {0};
5774 struct mnttab mpref = {0};
5775 int error;
5776 int ret;
5777 const char *fcn = "get_mountpoint()";
5779 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
5781 mntfp = fopen(MNTTAB, "r");
5782 error = errno;
5783 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5784 if (mntfp == NULL) {
5785 bam_error(_("failed to open file: %s: %s\n"),
5786 MNTTAB, strerror(error));
5787 return (NULL);
5790 mpref.mnt_special = special;
5791 mpref.mnt_fstype = fstype;
5793 ret = getmntany(mntfp, &mp, &mpref);
5794 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5795 if (ret != 0) {
5796 (void) fclose(mntfp);
5797 BAM_DPRINTF(("%s: no mount-point for special=%s and "
5798 "fstype=%s\n", fcn, special, fstype));
5799 return (NULL);
5801 (void) fclose(mntfp);
5803 assert(mp.mnt_mountp);
5805 BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5806 fcn, special, mp.mnt_mountp));
5808 return (s_strdup(mp.mnt_mountp));
5812 * Mounts a "legacy" top dataset (if needed)
5813 * Returns: The mountpoint of the legacy top dataset or NULL on error
5814 * mnted returns one of the above values defined for zfs_mnted_t
5816 static char *
5817 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5819 char cmd[PATH_MAX];
5820 char tmpmnt[PATH_MAX];
5821 filelist_t flist = {0};
5822 char *is_mounted;
5823 struct stat sb;
5824 int ret;
5825 const char *fcn = "mount_legacy_dataset()";
5827 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5829 *mnted = ZFS_MNT_ERROR;
5831 (void) snprintf(cmd, sizeof (cmd),
5832 "/sbin/zfs get -Ho value mounted %s",
5833 pool);
5835 ret = exec_cmd(cmd, &flist);
5836 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5837 if (ret != 0) {
5838 bam_error(_("failed to determine mount status of ZFS "
5839 "pool %s\n"), pool);
5840 return (NULL);
5843 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5844 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5845 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
5846 filelist_free(&flist);
5847 return (NULL);
5850 is_mounted = strtok(flist.head->line, " \t\n");
5851 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5852 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5853 if (strcmp(is_mounted, "no") != 0) {
5854 filelist_free(&flist);
5855 *mnted = LEGACY_ALREADY;
5856 /* get_mountpoint returns a strdup'ed string */
5857 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
5858 fcn, pool));
5859 return (get_mountpoint(pool, "zfs"));
5862 filelist_free(&flist);
5865 * legacy top dataset is not mounted. Mount it now
5866 * First create a mountpoint.
5868 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5869 ZFS_LEGACY_MNTPT, getpid());
5871 ret = stat(tmpmnt, &sb);
5872 if (ret == -1) {
5873 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
5874 fcn, pool, tmpmnt));
5875 ret = mkdirp(tmpmnt, DIR_PERMS);
5876 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5877 if (ret == -1) {
5878 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
5879 strerror(errno));
5880 return (NULL);
5882 } else {
5883 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
5884 "present\n", fcn, pool, tmpmnt));
5887 (void) snprintf(cmd, sizeof (cmd),
5888 "/sbin/mount -F zfs %s %s",
5889 pool, tmpmnt);
5891 ret = exec_cmd(cmd, NULL);
5892 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5893 if (ret != 0) {
5894 bam_error(_("mount of ZFS pool %s failed\n"), pool);
5895 (void) rmdir(tmpmnt);
5896 return (NULL);
5899 *mnted = LEGACY_MOUNTED;
5900 BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
5901 fcn, pool, tmpmnt));
5902 return (s_strdup(tmpmnt));
5906 * Mounts the top dataset (if needed)
5907 * Returns: The mountpoint of the top dataset or NULL on error
5908 * mnted returns one of the above values defined for zfs_mnted_t
5910 char *
5911 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5913 char cmd[PATH_MAX];
5914 filelist_t flist = {0};
5915 char *is_mounted;
5916 char *mntpt;
5917 char *zmntpt;
5918 int ret;
5919 const char *fcn = "mount_top_dataset()";
5921 *mnted = ZFS_MNT_ERROR;
5923 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
5926 * First check if the top dataset is a "legacy" dataset
5928 (void) snprintf(cmd, sizeof (cmd),
5929 "/sbin/zfs get -Ho value mountpoint %s",
5930 pool);
5931 ret = exec_cmd(cmd, &flist);
5932 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5933 if (ret != 0) {
5934 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
5935 pool);
5936 return (NULL);
5939 if (flist.head && (flist.head == flist.tail)) {
5940 char *legacy = strtok(flist.head->line, " \t\n");
5941 if (legacy && strcmp(legacy, "legacy") == 0) {
5942 filelist_free(&flist);
5943 BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
5944 return (mount_legacy_dataset(pool, mnted));
5948 filelist_free(&flist);
5950 BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
5952 (void) snprintf(cmd, sizeof (cmd),
5953 "/sbin/zfs get -Ho value mounted %s",
5954 pool);
5956 ret = exec_cmd(cmd, &flist);
5957 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5958 if (ret != 0) {
5959 bam_error(_("failed to determine mount status of ZFS "
5960 "pool %s\n"), pool);
5961 return (NULL);
5964 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5965 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5966 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
5967 filelist_free(&flist);
5968 return (NULL);
5971 is_mounted = strtok(flist.head->line, " \t\n");
5972 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5973 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5974 if (strcmp(is_mounted, "no") != 0) {
5975 filelist_free(&flist);
5976 *mnted = ZFS_ALREADY;
5977 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
5978 fcn, pool));
5979 goto mounted;
5982 filelist_free(&flist);
5983 BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
5984 fcn, pool));
5986 /* top dataset is not mounted. Mount it now */
5987 (void) snprintf(cmd, sizeof (cmd),
5988 "/sbin/zfs mount %s", pool);
5989 ret = exec_cmd(cmd, NULL);
5990 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5991 if (ret != 0) {
5992 bam_error(_("mount of ZFS pool %s failed\n"), pool);
5993 return (NULL);
5995 *mnted = ZFS_MOUNTED;
5996 BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
5997 /*FALLTHRU*/
5998 mounted:
6000 * Now get the mountpoint
6002 (void) snprintf(cmd, sizeof (cmd),
6003 "/sbin/zfs get -Ho value mountpoint %s",
6004 pool);
6006 ret = exec_cmd(cmd, &flist);
6007 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
6008 if (ret != 0) {
6009 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6010 pool);
6011 goto error;
6014 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
6015 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6016 bam_error(_("ZFS pool %s has no mount-point\n"), pool);
6017 goto error;
6020 mntpt = strtok(flist.head->line, " \t\n");
6021 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
6022 if (*mntpt != '/') {
6023 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6024 pool, mntpt);
6025 goto error;
6027 zmntpt = s_strdup(mntpt);
6029 filelist_free(&flist);
6031 BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6032 fcn, pool, zmntpt));
6034 return (zmntpt);
6036 error:
6037 filelist_free(&flist);
6038 (void) umount_top_dataset(pool, *mnted, NULL);
6039 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6040 return (NULL);
6044 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
6046 char cmd[PATH_MAX];
6047 int ret;
6048 const char *fcn = "umount_top_dataset()";
6050 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
6051 switch (mnted) {
6052 case LEGACY_ALREADY:
6053 case ZFS_ALREADY:
6054 /* nothing to do */
6055 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6056 "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
6057 free(mntpt);
6058 return (BAM_SUCCESS);
6059 case LEGACY_MOUNTED:
6060 (void) snprintf(cmd, sizeof (cmd),
6061 "/sbin/umount %s", pool);
6062 ret = exec_cmd(cmd, NULL);
6063 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
6064 if (ret != 0) {
6065 bam_error(_("umount of %s failed\n"), pool);
6066 free(mntpt);
6067 return (BAM_ERROR);
6069 if (mntpt)
6070 (void) rmdir(mntpt);
6071 free(mntpt);
6072 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6073 "successfully unmounted\n", fcn, pool));
6074 return (BAM_SUCCESS);
6075 case ZFS_MOUNTED:
6076 free(mntpt);
6077 (void) snprintf(cmd, sizeof (cmd),
6078 "/sbin/zfs unmount %s", pool);
6079 ret = exec_cmd(cmd, NULL);
6080 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
6081 if (ret != 0) {
6082 bam_error(_("umount of %s failed\n"), pool);
6083 return (BAM_ERROR);
6085 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6086 "successfully unmounted\n", fcn, pool));
6087 return (BAM_SUCCESS);
6088 default:
6089 bam_error(_("Internal error: bad saved mount state for "
6090 "pool %s\n"), pool);
6091 return (BAM_ERROR);
6093 /*NOTREACHED*/
6097 * For ZFS, osdev can be one of two forms
6098 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6099 * It can be a /dev/[r]dsk special file. We handle both instances
6101 static char *
6102 get_pool(char *osdev)
6104 char cmd[PATH_MAX];
6105 char buf[PATH_MAX];
6106 filelist_t flist = {0};
6107 char *pool;
6108 char *cp;
6109 char *slash;
6110 int ret;
6111 const char *fcn = "get_pool()";
6113 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
6114 if (osdev == NULL) {
6115 bam_error(_("NULL device: cannot determine pool name\n"));
6116 return (NULL);
6119 BAM_DPRINTF(("%s: osdev arg = %s\n", fcn, osdev));
6121 if (osdev[0] != '/') {
6122 (void) strlcpy(buf, osdev, sizeof (buf));
6123 slash = strchr(buf, '/');
6124 if (slash)
6125 *slash = '\0';
6126 pool = s_strdup(buf);
6127 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6128 return (pool);
6129 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6130 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6131 bam_error(_("invalid device %s: cannot determine pool name\n"),
6132 osdev);
6133 return (NULL);
6137 * Call the zfs fstyp directly since this is a zpool. This avoids
6138 * potential pcfs conflicts if the first block wasn't cleared.
6140 (void) snprintf(cmd, sizeof (cmd),
6141 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6142 osdev);
6144 ret = exec_cmd(cmd, &flist);
6145 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
6146 if (ret != 0) {
6147 bam_error(_("fstyp -a on device %s failed\n"), osdev);
6148 return (NULL);
6151 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
6152 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6153 bam_error(_("NULL fstyp -a output for device %s\n"), osdev);
6154 filelist_free(&flist);
6155 return (NULL);
6158 (void) strtok(flist.head->line, "'");
6159 cp = strtok(NULL, "'");
6160 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6161 if (cp == NULL) {
6162 bam_error(_("bad fstyp -a output for device %s\n"), osdev);
6163 filelist_free(&flist);
6164 return (NULL);
6167 pool = s_strdup(cp);
6169 filelist_free(&flist);
6171 BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6173 return (pool);
6176 static char *
6177 find_zfs_existing(char *osdev)
6179 char *pool;
6180 zfs_mnted_t mnted;
6181 char *mntpt;
6182 char *sign;
6183 const char *fcn = "find_zfs_existing()";
6185 pool = get_pool(osdev);
6186 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6187 if (pool == NULL) {
6188 bam_error(_("failed to get pool for device: %s\n"), osdev);
6189 return (NULL);
6192 mntpt = mount_top_dataset(pool, &mnted);
6193 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
6194 if (mntpt == NULL) {
6195 bam_error(_("failed to mount top dataset for pool: %s\n"),
6196 pool);
6197 free(pool);
6198 return (NULL);
6201 sign = find_primary_common(mntpt, "zfs");
6202 if (sign == NULL) {
6203 sign = find_backup_common(mntpt, "zfs");
6204 BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
6205 sign ? sign : "NULL"));
6206 } else {
6207 BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
6210 (void) umount_top_dataset(pool, mnted, mntpt);
6212 free(pool);
6214 return (sign);
6217 static char *
6218 find_existing_sign(char *osroot, char *osdev, char *fstype)
6220 const char *fcn = "find_existing_sign()";
6222 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6223 if (strcmp(fstype, "ufs") == 0) {
6224 BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn));
6225 return (find_ufs_existing(osroot));
6226 } else if (strcmp(fstype, "zfs") == 0) {
6227 BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn));
6228 return (find_zfs_existing(osdev));
6229 } else {
6230 bam_error(_("boot signature not supported for fstype: %s\n"),
6231 fstype);
6232 return (NULL);
6236 #define MH_HASH_SZ 16
6238 typedef enum {
6239 MH_ERROR = -1,
6240 MH_NOMATCH,
6241 MH_MATCH
6242 } mh_search_t;
6244 typedef struct mcache {
6245 char *mc_special;
6246 char *mc_mntpt;
6247 char *mc_fstype;
6248 struct mcache *mc_next;
6249 } mcache_t;
6251 typedef struct mhash {
6252 mcache_t *mh_hash[MH_HASH_SZ];
6253 } mhash_t;
6255 static int
6256 mhash_fcn(char *key)
6258 int i;
6259 uint64_t sum = 0;
6261 for (i = 0; key[i] != '\0'; i++) {
6262 sum += (uchar_t)key[i];
6265 sum %= MH_HASH_SZ;
6267 assert(sum < MH_HASH_SZ);
6269 return (sum);
6272 static mhash_t *
6273 cache_mnttab(void)
6275 FILE *mfp;
6276 struct extmnttab mnt;
6277 mcache_t *mcp;
6278 mhash_t *mhp;
6279 char *ctds;
6280 int idx;
6281 int error;
6282 char *special_dup;
6283 const char *fcn = "cache_mnttab()";
6285 mfp = fopen(MNTTAB, "r");
6286 error = errno;
6287 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6288 if (mfp == NULL) {
6289 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
6290 strerror(error));
6291 return (NULL);
6294 mhp = s_calloc(1, sizeof (mhash_t));
6296 resetmnttab(mfp);
6298 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6299 /* only cache ufs */
6300 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6301 continue;
6303 /* basename() modifies its arg, so dup it */
6304 special_dup = s_strdup(mnt.mnt_special);
6305 ctds = basename(special_dup);
6307 mcp = s_calloc(1, sizeof (mcache_t));
6308 mcp->mc_special = s_strdup(ctds);
6309 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6310 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6311 BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6312 "fstype=%s\n", fcn, ctds, mnt.mnt_mountp, mnt.mnt_fstype));
6313 idx = mhash_fcn(ctds);
6314 mcp->mc_next = mhp->mh_hash[idx];
6315 mhp->mh_hash[idx] = mcp;
6316 free(special_dup);
6319 (void) fclose(mfp);
6321 return (mhp);
6324 static void
6325 free_mnttab(mhash_t *mhp)
6327 mcache_t *mcp;
6328 int i;
6330 for (i = 0; i < MH_HASH_SZ; i++) {
6331 while ((mcp = mhp->mh_hash[i]) != NULL) {
6332 mhp->mh_hash[i] = mcp->mc_next;
6333 free(mcp->mc_special);
6334 free(mcp->mc_mntpt);
6335 free(mcp->mc_fstype);
6336 free(mcp);
6340 for (i = 0; i < MH_HASH_SZ; i++) {
6341 assert(mhp->mh_hash[i] == NULL);
6343 free(mhp);
6346 static mh_search_t
6347 search_hash(mhash_t *mhp, char *special, char **mntpt)
6349 int idx;
6350 mcache_t *mcp;
6351 const char *fcn = "search_hash()";
6353 assert(mntpt);
6355 *mntpt = NULL;
6357 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6358 if (strchr(special, '/')) {
6359 bam_error(_("invalid key for mnttab hash: %s\n"), special);
6360 return (MH_ERROR);
6363 idx = mhash_fcn(special);
6365 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6366 if (strcmp(mcp->mc_special, special) == 0)
6367 break;
6370 if (mcp == NULL) {
6371 BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn, special));
6372 return (MH_NOMATCH);
6375 assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6376 *mntpt = mcp->mc_mntpt;
6377 BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn, special));
6378 return (MH_MATCH);
6381 static int
6382 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6384 char *sign;
6385 char *signline;
6386 char signbuf[MAXNAMELEN];
6387 int len;
6388 int error;
6389 const char *fcn = "check_add_ufs_sign_to_list()";
6391 /* safe to specify NULL as "osdev" arg for UFS */
6392 sign = find_existing_sign(mntpt, NULL, "ufs");
6393 if (sign == NULL) {
6394 /* No existing signature, nothing to add to list */
6395 BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6396 fcn, mntpt));
6397 return (0);
6400 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6401 signline = signbuf;
6403 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6404 if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6405 strlen(GRUBSIGN_UFS_PREFIX))) {
6406 bam_error(_("invalid UFS boot signature %s\n"), sign);
6407 free(sign);
6408 /* ignore invalid signatures */
6409 return (0);
6412 len = fputs(signline, tfp);
6413 error = errno;
6414 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6415 if (len != strlen(signline)) {
6416 bam_error(_("failed to write signature %s to signature "
6417 "list: %s\n"), sign, strerror(error));
6418 free(sign);
6419 return (-1);
6422 free(sign);
6424 BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6425 fcn, mntpt));
6426 return (0);
6430 * slice is a basename not a full pathname
6432 static int
6433 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6435 int ret;
6436 char cmd[PATH_MAX];
6437 char path[PATH_MAX];
6438 struct stat sbuf;
6439 char *mntpt;
6440 filelist_t flist = {0};
6441 char *fstype;
6442 char blkslice[PATH_MAX];
6443 const char *fcn = "process_slice_common()";
6446 ret = search_hash(mhp, slice, &mntpt);
6447 switch (ret) {
6448 case MH_MATCH:
6449 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6450 return (-1);
6451 else
6452 return (0);
6453 case MH_NOMATCH:
6454 break;
6455 case MH_ERROR:
6456 default:
6457 return (-1);
6460 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6461 if (stat(path, &sbuf) == -1) {
6462 BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn, path));
6463 return (0);
6466 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6467 (void) snprintf(cmd, sizeof (cmd),
6468 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6469 slice);
6471 if (exec_cmd(cmd, &flist) != 0) {
6472 if (bam_verbose)
6473 bam_print(_("fstyp failed for slice: %s\n"), slice);
6474 return (0);
6477 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6478 if (bam_verbose)
6479 bam_print(_("bad output from fstyp for slice: %s\n"),
6480 slice);
6481 filelist_free(&flist);
6482 return (0);
6485 fstype = strtok(flist.head->line, " \t\n");
6486 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6487 if (bam_verbose)
6488 bam_print(_("%s is not a ufs slice: %s\n"),
6489 slice, fstype);
6490 filelist_free(&flist);
6491 return (0);
6494 filelist_free(&flist);
6497 * Since we are mounting the filesystem read-only, the
6498 * the last mount field of the superblock is unchanged
6499 * and does not need to be fixed up post-mount;
6502 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6503 slice);
6505 (void) snprintf(cmd, sizeof (cmd),
6506 "/usr/sbin/mount -F ufs -o ro %s %s "
6507 "> /dev/null 2>&1", blkslice, tmpmnt);
6509 if (exec_cmd(cmd, NULL) != 0) {
6510 if (bam_verbose)
6511 bam_print(_("mount of %s (fstype %s) failed\n"),
6512 blkslice, "ufs");
6513 return (0);
6516 ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6518 (void) snprintf(cmd, sizeof (cmd),
6519 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6520 tmpmnt);
6522 if (exec_cmd(cmd, NULL) != 0) {
6523 bam_print(_("umount of %s failed\n"), slice);
6524 return (0);
6527 return (ret);
6530 static int
6531 process_vtoc_slices(
6532 char *s0,
6533 struct vtoc *vtoc,
6534 FILE *tfp,
6535 mhash_t *mhp,
6536 char *tmpmnt)
6538 int idx;
6539 char slice[PATH_MAX];
6540 size_t len;
6541 char *cp;
6542 const char *fcn = "process_vtoc_slices()";
6544 len = strlen(s0);
6546 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6548 s0[len - 1] = '\0';
6550 (void) strlcpy(slice, s0, sizeof (slice));
6552 s0[len - 1] = '0';
6554 cp = slice + len - 1;
6556 for (idx = 0; idx < vtoc->v_nparts; idx++) {
6558 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6560 if (vtoc->v_part[idx].p_size == 0) {
6561 BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6562 fcn, slice));
6563 continue;
6566 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6567 switch (vtoc->v_part[idx].p_tag) {
6568 case V_SWAP:
6569 case V_USR:
6570 case V_BACKUP:
6571 case V_VAR:
6572 case V_HOME:
6573 case V_ALTSCTR:
6574 BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6575 "skipping: %s\n", fcn, slice));
6576 continue;
6577 default:
6578 BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6579 fcn, slice));
6580 break;
6583 /* skip unmountable and readonly slices */
6584 switch (vtoc->v_part[idx].p_flag) {
6585 case V_UNMNT:
6586 case V_RONLY:
6587 BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6588 fcn, slice));
6589 continue;
6590 default:
6591 BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6592 fcn, slice));
6593 break;
6596 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6597 return (-1);
6601 return (0);
6604 static int
6605 process_efi_slices(
6606 char *s0,
6607 struct dk_gpt *efi,
6608 FILE *tfp,
6609 mhash_t *mhp,
6610 char *tmpmnt)
6612 int idx;
6613 char slice[PATH_MAX];
6614 size_t len;
6615 char *cp;
6616 const char *fcn = "process_efi_slices()";
6618 len = strlen(s0);
6620 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6622 s0[len - 1] = '\0';
6624 (void) strlcpy(slice, s0, sizeof (slice));
6626 s0[len - 1] = '0';
6628 cp = slice + len - 1;
6630 for (idx = 0; idx < efi->efi_nparts; idx++) {
6632 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6634 if (efi->efi_parts[idx].p_size == 0) {
6635 BAM_DPRINTF(("%s: EFI: skipping 0-length slice: %s\n",
6636 fcn, slice));
6637 continue;
6640 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6641 switch (efi->efi_parts[idx].p_tag) {
6642 case V_SWAP:
6643 case V_USR:
6644 case V_BACKUP:
6645 case V_VAR:
6646 case V_HOME:
6647 case V_ALTSCTR:
6648 BAM_DPRINTF(("%s: EFI: unsupported tag, skipping: %s\n",
6649 fcn, slice));
6650 continue;
6651 default:
6652 BAM_DPRINTF(("%s: EFI: supported tag, checking: %s\n",
6653 fcn, slice));
6654 break;
6657 /* skip unmountable and readonly slices */
6658 switch (efi->efi_parts[idx].p_flag) {
6659 case V_UNMNT:
6660 case V_RONLY:
6661 BAM_DPRINTF(("%s: EFI: non-RDWR flag, skipping: %s\n",
6662 fcn, slice));
6663 continue;
6664 default:
6665 BAM_DPRINTF(("%s: EFI: RDWR flag, checking: %s\n",
6666 fcn, slice));
6667 break;
6670 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6671 return (-1);
6675 return (0);
6679 * s0 is a basename not a full path
6681 static int
6682 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6684 struct vtoc vtoc;
6685 struct dk_gpt *efi;
6686 char s0path[PATH_MAX];
6687 struct stat sbuf;
6688 int e_flag;
6689 int v_flag;
6690 int retval;
6691 int err;
6692 int fd;
6693 const char *fcn = "process_slice0()";
6695 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6697 if (stat(s0path, &sbuf) == -1) {
6698 BAM_DPRINTF(("%s: slice 0 does not exist: %s\n", fcn, s0path));
6699 return (0);
6702 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6703 if (fd == -1) {
6704 bam_error(_("failed to open file: %s: %s\n"), s0path,
6705 strerror(errno));
6706 return (0);
6709 e_flag = v_flag = 0;
6710 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6711 switch (retval) {
6712 case VT_EIO:
6713 BAM_DPRINTF(("%s: VTOC: failed to read: %s\n",
6714 fcn, s0path));
6715 break;
6716 case VT_EINVAL:
6717 BAM_DPRINTF(("%s: VTOC: is INVALID: %s\n",
6718 fcn, s0path));
6719 break;
6720 case VT_ERROR:
6721 BAM_DPRINTF(("%s: VTOC: unknown error while "
6722 "reading: %s\n", fcn, s0path));
6723 break;
6724 case VT_ENOTSUP:
6725 e_flag = 1;
6726 BAM_DPRINTF(("%s: VTOC: not supported: %s\n",
6727 fcn, s0path));
6728 break;
6729 case 0:
6730 v_flag = 1;
6731 BAM_DPRINTF(("%s: VTOC: SUCCESS reading: %s\n",
6732 fcn, s0path));
6733 break;
6734 default:
6735 BAM_DPRINTF(("%s: VTOC: READ: unknown return "
6736 "code: %s\n", fcn, s0path));
6737 break;
6741 if (e_flag) {
6742 e_flag = 0;
6743 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6744 switch (retval) {
6745 case VT_EIO:
6746 BAM_DPRINTF(("%s: EFI: failed to read: %s\n",
6747 fcn, s0path));
6748 break;
6749 case VT_EINVAL:
6750 BAM_DPRINTF(("%s: EFI: is INVALID: %s\n", fcn, s0path));
6751 break;
6752 case VT_ERROR:
6753 BAM_DPRINTF(("%s: EFI: unknown error while "
6754 "reading: %s\n", fcn, s0path));
6755 break;
6756 case VT_ENOTSUP:
6757 BAM_DPRINTF(("%s: EFI: not supported: %s\n",
6758 fcn, s0path));
6759 break;
6760 case 0:
6761 e_flag = 1;
6762 BAM_DPRINTF(("%s: EFI: SUCCESS reading: %s\n",
6763 fcn, s0path));
6764 break;
6765 default:
6766 BAM_DPRINTF(("%s: EFI: READ: unknown return code: %s\n",
6767 fcn, s0path));
6768 break;
6772 (void) close(fd);
6774 if (v_flag) {
6775 retval = process_vtoc_slices(s0,
6776 &vtoc, tfp, mhp, tmpmnt);
6777 } else if (e_flag) {
6778 retval = process_efi_slices(s0,
6779 efi, tfp, mhp, tmpmnt);
6780 } else {
6781 BAM_DPRINTF(("%s: disk has neither VTOC nor EFI: %s\n",
6782 fcn, s0path));
6783 return (0);
6786 return (retval);
6790 * Find and create a list of all existing UFS boot signatures
6792 static int
6793 FindAllUfsSignatures(void)
6795 mhash_t *mnttab_hash;
6796 DIR *dirp = NULL;
6797 struct dirent *dp;
6798 char tmpmnt[PATH_MAX];
6799 char cmd[PATH_MAX];
6800 struct stat sb;
6801 int fd;
6802 FILE *tfp;
6803 size_t len;
6804 int ret;
6805 int error;
6806 const char *fcn = "FindAllUfsSignatures()";
6808 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) {
6809 bam_print(_(" - signature list %s exists\n"),
6810 UFS_SIGNATURE_LIST);
6811 return (0);
6814 fd = open(UFS_SIGNATURE_LIST".tmp",
6815 O_RDWR|O_CREAT|O_TRUNC, 0644);
6816 error = errno;
6817 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6818 if (fd == -1) {
6819 bam_error(_("failed to open file: %s: %s\n"),
6820 UFS_SIGNATURE_LIST".tmp", strerror(error));
6821 return (-1);
6824 ret = close(fd);
6825 error = errno;
6826 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6827 if (ret == -1) {
6828 bam_error(_("failed to close file: %s: %s\n"),
6829 UFS_SIGNATURE_LIST".tmp", strerror(error));
6830 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6831 return (-1);
6834 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6835 error = errno;
6836 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6837 if (tfp == NULL) {
6838 bam_error(_("failed to open file: %s: %s\n"),
6839 UFS_SIGNATURE_LIST".tmp", strerror(error));
6840 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6841 return (-1);
6844 mnttab_hash = cache_mnttab();
6845 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6846 if (mnttab_hash == NULL) {
6847 (void) fclose(tfp);
6848 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6849 bam_error(_("%s: failed to cache /etc/mnttab\n"), fcn);
6850 return (-1);
6853 (void) snprintf(tmpmnt, sizeof (tmpmnt),
6854 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6855 (void) unlink(tmpmnt);
6857 ret = mkdirp(tmpmnt, DIR_PERMS);
6858 error = errno;
6859 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6860 if (ret == -1) {
6861 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
6862 strerror(error));
6863 free_mnttab(mnttab_hash);
6864 (void) fclose(tfp);
6865 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6866 return (-1);
6869 dirp = opendir("/dev/rdsk");
6870 error = errno;
6871 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6872 if (dirp == NULL) {
6873 bam_error(_("opendir of %s failed: %s\n"), "/dev/rdsk",
6874 strerror(error));
6875 goto fail;
6878 while ((dp = readdir(dirp)) != NULL) {
6879 if (strcmp(dp->d_name, ".") == 0 ||
6880 strcmp(dp->d_name, "..") == 0)
6881 continue;
6884 * we only look for the s0 slice. This is guranteed to
6885 * have 's' at len - 2.
6887 len = strlen(dp->d_name);
6888 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6889 BAM_DPRINTF(("%s: skipping non-s0 slice: %s\n",
6890 fcn, dp->d_name));
6891 continue;
6894 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6895 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6896 if (ret == -1)
6897 goto fail;
6900 (void) closedir(dirp);
6901 free_mnttab(mnttab_hash);
6902 (void) rmdir(tmpmnt);
6904 ret = fclose(tfp);
6905 error = errno;
6906 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6907 if (ret == EOF) {
6908 bam_error(_("failed to close file: %s: %s\n"),
6909 UFS_SIGNATURE_LIST".tmp", strerror(error));
6910 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6911 return (-1);
6914 /* We have a list of existing GRUB signatures. Sort it first */
6915 (void) snprintf(cmd, sizeof (cmd),
6916 "/usr/bin/sort -u %s.tmp > %s.sorted",
6917 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6919 ret = exec_cmd(cmd, NULL);
6920 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6921 if (ret != 0) {
6922 bam_error(_("error sorting GRUB UFS boot signatures\n"));
6923 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6924 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6925 return (-1);
6928 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6930 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6931 error = errno;
6932 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6933 if (ret == -1) {
6934 bam_error(_("rename to file failed: %s: %s\n"),
6935 UFS_SIGNATURE_LIST, strerror(error));
6936 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6937 return (-1);
6940 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6941 BAM_DPRINTF(("%s: generated zero length signlist: %s.\n",
6942 fcn, UFS_SIGNATURE_LIST));
6945 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
6946 return (0);
6948 fail:
6949 if (dirp)
6950 (void) closedir(dirp);
6951 free_mnttab(mnttab_hash);
6952 (void) rmdir(tmpmnt);
6953 (void) fclose(tfp);
6954 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6955 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6956 return (-1);
6959 static char *
6960 create_ufs_sign(void)
6962 struct stat sb;
6963 int signnum = -1;
6964 char tmpsign[MAXNAMELEN + 1];
6965 char *numstr;
6966 int i;
6967 FILE *tfp;
6968 int ret;
6969 int error;
6970 const char *fcn = "create_ufs_sign()";
6972 bam_print(_(" - searching for UFS boot signatures\n"));
6974 ret = FindAllUfsSignatures();
6975 INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6976 if (ret == -1) {
6977 bam_error(_("search for UFS boot signatures failed\n"));
6978 return (NULL);
6981 /* Make sure the list exists and is owned by root */
6982 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6983 (void) unlink(UFS_SIGNATURE_LIST));
6984 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6985 (void) unlink(UFS_SIGNATURE_LIST);
6986 bam_error(_("missing UFS signature list file: %s\n"),
6987 UFS_SIGNATURE_LIST);
6988 return (NULL);
6991 if (sb.st_size == 0) {
6992 bam_print(_(" - no existing UFS boot signatures\n"));
6993 i = 0;
6994 goto found;
6997 /* The signature list was sorted when it was created */
6998 tfp = fopen(UFS_SIGNATURE_LIST, "r");
6999 error = errno;
7000 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
7001 if (tfp == NULL) {
7002 bam_error(_("error opening UFS boot signature list "
7003 "file %s: %s\n"), UFS_SIGNATURE_LIST, strerror(error));
7004 (void) unlink(UFS_SIGNATURE_LIST);
7005 return (NULL);
7008 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
7010 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
7011 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7012 (void) fclose(tfp);
7013 (void) unlink(UFS_SIGNATURE_LIST);
7014 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7015 return (NULL);
7017 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
7019 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
7020 (void) fclose(tfp);
7021 (void) unlink(UFS_SIGNATURE_LIST);
7022 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7023 return (NULL);
7026 signnum = atoi(numstr);
7027 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
7028 if (signnum < 0) {
7029 (void) fclose(tfp);
7030 (void) unlink(UFS_SIGNATURE_LIST);
7031 bam_error(_("bad UFS boot signature: %s\n"), tmpsign);
7032 return (NULL);
7035 if (i != signnum) {
7036 BAM_DPRINTF(("%s: found hole %d in sign list.\n",
7037 fcn, i));
7038 break;
7042 (void) fclose(tfp);
7044 found:
7045 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
7047 /* add the ufs signature to the /var/run list of signatures */
7048 ret = ufs_add_to_sign_list(tmpsign);
7049 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
7050 if (ret == -1) {
7051 (void) unlink(UFS_SIGNATURE_LIST);
7052 bam_error(_("failed to add sign %s to signlist.\n"), tmpsign);
7053 return (NULL);
7056 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7058 return (s_strdup(tmpsign));
7061 static char *
7062 get_fstype(char *osroot)
7064 FILE *mntfp;
7065 struct mnttab mp = {0};
7066 struct mnttab mpref = {0};
7067 int error;
7068 int ret;
7069 const char *fcn = "get_fstype()";
7071 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
7072 if (osroot == NULL) {
7073 bam_error(_("no OS mountpoint. Cannot determine fstype\n"));
7074 return (NULL);
7077 mntfp = fopen(MNTTAB, "r");
7078 error = errno;
7079 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
7080 if (mntfp == NULL) {
7081 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7082 strerror(error));
7083 return (NULL);
7086 if (*osroot == '\0')
7087 mpref.mnt_mountp = "/";
7088 else
7089 mpref.mnt_mountp = osroot;
7091 ret = getmntany(mntfp, &mp, &mpref);
7092 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
7093 if (ret != 0) {
7094 bam_error(_("failed to find OS mountpoint %s in %s\n"),
7095 osroot, MNTTAB);
7096 (void) fclose(mntfp);
7097 return (NULL);
7099 (void) fclose(mntfp);
7101 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
7102 if (mp.mnt_fstype == NULL) {
7103 bam_error(_("NULL fstype found for OS root %s\n"), osroot);
7104 return (NULL);
7107 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7109 return (s_strdup(mp.mnt_fstype));
7112 static char *
7113 create_zfs_sign(char *osdev)
7115 char tmpsign[PATH_MAX];
7116 char *pool;
7117 const char *fcn = "create_zfs_sign()";
7119 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, osdev));
7122 * First find the pool name
7124 pool = get_pool(osdev);
7125 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
7126 if (pool == NULL) {
7127 bam_error(_("failed to get pool name from %s\n"), osdev);
7128 return (NULL);
7131 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
7133 BAM_DPRINTF(("%s: created ZFS sign: %s\n", fcn, tmpsign));
7135 free(pool);
7137 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7139 return (s_strdup(tmpsign));
7142 static char *
7143 create_new_sign(char *osdev, char *fstype)
7145 char *sign;
7146 const char *fcn = "create_new_sign()";
7148 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
7150 if (strcmp(fstype, "zfs") == 0) {
7151 BAM_DPRINTF(("%s: created new ZFS sign\n", fcn));
7152 sign = create_zfs_sign(osdev);
7153 } else if (strcmp(fstype, "ufs") == 0) {
7154 BAM_DPRINTF(("%s: created new UFS sign\n", fcn));
7155 sign = create_ufs_sign();
7156 } else {
7157 bam_error(_("boot signature not supported for fstype: %s\n"),
7158 fstype);
7159 sign = NULL;
7162 BAM_DPRINTF(("%s: created new sign: %s\n", fcn,
7163 sign ? sign : "<NULL>"));
7164 return (sign);
7167 static int
7168 set_backup_common(char *mntpt, char *sign)
7170 FILE *bfp;
7171 char backup[PATH_MAX];
7172 char tmpsign[PATH_MAX];
7173 int error;
7174 char *bdir;
7175 char *backup_dup;
7176 struct stat sb;
7177 int ret;
7178 const char *fcn = "set_backup_common()";
7180 (void) snprintf(backup, sizeof (backup), "%s%s",
7181 mntpt, GRUBSIGN_BACKUP);
7183 /* First read the backup */
7184 bfp = fopen(backup, "r");
7185 if (bfp != NULL) {
7186 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
7187 if (strcmp(tmpsign, sign) == 0) {
7188 BAM_DPRINTF(("%s: found sign (%s) in backup.\n",
7189 fcn, sign));
7190 (void) fclose(bfp);
7191 return (0);
7194 (void) fclose(bfp);
7195 BAM_DPRINTF(("%s: backup exists but sign %s not found\n",
7196 fcn, sign));
7197 } else {
7198 BAM_DPRINTF(("%s: no backup file (%s) found.\n", fcn, backup));
7202 * Didn't find the correct signature. First create
7203 * the directory if necessary.
7206 /* dirname() modifies its argument so dup it */
7207 backup_dup = s_strdup(backup);
7208 bdir = dirname(backup_dup);
7209 assert(bdir);
7211 ret = stat(bdir, &sb);
7212 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
7213 if (ret == -1) {
7214 BAM_DPRINTF(("%s: backup dir (%s) does not exist.\n",
7215 fcn, bdir));
7216 ret = mkdirp(bdir, DIR_PERMS);
7217 error = errno;
7218 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
7219 if (ret == -1) {
7220 bam_error(_("mkdirp() of backup dir failed: %s: %s\n"),
7221 GRUBSIGN_BACKUP, strerror(error));
7222 free(backup_dup);
7223 return (-1);
7226 free(backup_dup);
7229 * Open the backup in append mode to add the correct
7230 * signature;
7232 bfp = fopen(backup, "a");
7233 error = errno;
7234 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
7235 if (bfp == NULL) {
7236 bam_error(_("error opening boot signature backup "
7237 "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7238 return (-1);
7241 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
7243 ret = fputs(tmpsign, bfp);
7244 error = errno;
7245 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
7246 if (ret != strlen(tmpsign)) {
7247 bam_error(_("error writing boot signature backup "
7248 "file %s: %s\n"), GRUBSIGN_BACKUP, strerror(error));
7249 (void) fclose(bfp);
7250 return (-1);
7253 (void) fclose(bfp);
7255 if (bam_verbose)
7256 bam_print(_("updated boot signature backup file %s\n"),
7257 GRUBSIGN_BACKUP);
7259 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7261 return (0);
7264 static int
7265 set_backup_ufs(char *osroot, char *sign)
7267 const char *fcn = "set_backup_ufs()";
7269 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7270 return (set_backup_common(osroot, sign));
7273 static int
7274 set_backup_zfs(char *osdev, char *sign)
7276 char *pool;
7277 char *mntpt;
7278 zfs_mnted_t mnted;
7279 int ret;
7280 const char *fcn = "set_backup_zfs()";
7282 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7284 pool = get_pool(osdev);
7285 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7286 if (pool == NULL) {
7287 bam_error(_("failed to get pool name from %s\n"), osdev);
7288 return (-1);
7291 mntpt = mount_top_dataset(pool, &mnted);
7292 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7293 if (mntpt == NULL) {
7294 bam_error(_("failed to mount top dataset for %s\n"), pool);
7295 free(pool);
7296 return (-1);
7299 ret = set_backup_common(mntpt, sign);
7301 (void) umount_top_dataset(pool, mnted, mntpt);
7303 free(pool);
7305 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7306 if (ret == 0) {
7307 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7308 } else {
7309 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7312 return (ret);
7315 static int
7316 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7318 const char *fcn = "set_backup()";
7319 int ret;
7321 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7323 if (strcmp(fstype, "ufs") == 0) {
7324 BAM_DPRINTF(("%s: setting UFS backup sign\n", fcn));
7325 ret = set_backup_ufs(osroot, sign);
7326 } else if (strcmp(fstype, "zfs") == 0) {
7327 BAM_DPRINTF(("%s: setting ZFS backup sign\n", fcn));
7328 ret = set_backup_zfs(osdev, sign);
7329 } else {
7330 bam_error(_("boot signature not supported for fstype: %s\n"),
7331 fstype);
7332 ret = -1;
7335 if (ret == 0) {
7336 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7337 } else {
7338 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7341 return (ret);
7344 static int
7345 set_primary_common(char *mntpt, char *sign)
7347 char signfile[PATH_MAX];
7348 char signdir[PATH_MAX];
7349 struct stat sb;
7350 int fd;
7351 int error;
7352 int ret;
7353 const char *fcn = "set_primary_common()";
7355 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7356 mntpt, GRUBSIGN_DIR, sign);
7358 if (stat(signfile, &sb) != -1) {
7359 if (bam_verbose)
7360 bam_print(_("primary sign %s exists\n"), sign);
7361 return (0);
7362 } else {
7363 BAM_DPRINTF(("%s: primary sign (%s) does not exist\n",
7364 fcn, signfile));
7367 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
7368 mntpt, GRUBSIGN_DIR);
7370 if (stat(signdir, &sb) == -1) {
7371 BAM_DPRINTF(("%s: primary signdir (%s) does not exist\n",
7372 fcn, signdir));
7373 ret = mkdirp(signdir, DIR_PERMS);
7374 error = errno;
7375 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7376 if (ret == -1) {
7377 bam_error(_("error creating boot signature "
7378 "directory %s: %s\n"), signdir, strerror(errno));
7379 return (-1);
7383 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7384 error = errno;
7385 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7386 if (fd == -1) {
7387 bam_error(_("error creating primary boot signature %s: %s\n"),
7388 signfile, strerror(error));
7389 return (-1);
7392 ret = fsync(fd);
7393 error = errno;
7394 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7395 if (ret != 0) {
7396 bam_error(_("error syncing primary boot signature %s: %s\n"),
7397 signfile, strerror(error));
7400 (void) close(fd);
7402 if (bam_verbose)
7403 bam_print(_("created primary GRUB boot signature: %s\n"),
7404 signfile);
7406 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7408 return (0);
7411 static int
7412 set_primary_ufs(char *osroot, char *sign)
7414 const char *fcn = "set_primary_ufs()";
7416 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, sign));
7417 return (set_primary_common(osroot, sign));
7420 static int
7421 set_primary_zfs(char *osdev, char *sign)
7423 char *pool;
7424 char *mntpt;
7425 zfs_mnted_t mnted;
7426 int ret;
7427 const char *fcn = "set_primary_zfs()";
7429 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osdev, sign));
7431 pool = get_pool(osdev);
7432 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7433 if (pool == NULL) {
7434 bam_error(_("failed to get pool name from %s\n"), osdev);
7435 return (-1);
7438 /* Pool name must exist in the sign */
7439 ret = (strstr(sign, pool) != NULL);
7440 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7441 if (ret == 0) {
7442 bam_error(_("pool name %s not present in signature %s\n"),
7443 pool, sign);
7444 free(pool);
7445 return (-1);
7448 mntpt = mount_top_dataset(pool, &mnted);
7449 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7450 if (mntpt == NULL) {
7451 bam_error(_("failed to mount top dataset for %s\n"), pool);
7452 free(pool);
7453 return (-1);
7456 ret = set_primary_common(mntpt, sign);
7458 (void) umount_top_dataset(pool, mnted, mntpt);
7460 free(pool);
7462 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7463 if (ret == 0) {
7464 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7465 } else {
7466 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7469 return (ret);
7472 static int
7473 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7475 const char *fcn = "set_primary()";
7476 int ret;
7478 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7479 if (strcmp(fstype, "ufs") == 0) {
7480 BAM_DPRINTF(("%s: setting UFS primary sign\n", fcn));
7481 ret = set_primary_ufs(osroot, sign);
7482 } else if (strcmp(fstype, "zfs") == 0) {
7483 BAM_DPRINTF(("%s: setting ZFS primary sign\n", fcn));
7484 ret = set_primary_zfs(osdev, sign);
7485 } else {
7486 bam_error(_("boot signature not supported for fstype: %s\n"),
7487 fstype);
7488 ret = -1;
7491 if (ret == 0) {
7492 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7493 } else {
7494 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7497 return (ret);
7500 static int
7501 ufs_add_to_sign_list(char *sign)
7503 FILE *tfp;
7504 char signline[MAXNAMELEN];
7505 char cmd[PATH_MAX];
7506 int ret;
7507 int error;
7508 const char *fcn = "ufs_add_to_sign_list()";
7510 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7511 if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7512 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7513 bam_error(_("invalid UFS boot signature %s\n"), sign);
7514 (void) unlink(UFS_SIGNATURE_LIST);
7515 return (-1);
7519 * most failures in this routine are not a fatal error
7520 * We simply unlink the /var/run file and continue
7523 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7524 error = errno;
7525 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7526 if (ret == -1) {
7527 bam_error(_("rename to file failed: %s: %s\n"),
7528 UFS_SIGNATURE_LIST".tmp", strerror(error));
7529 (void) unlink(UFS_SIGNATURE_LIST);
7530 return (0);
7533 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7534 error = errno;
7535 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7536 if (tfp == NULL) {
7537 bam_error(_("failed to open file: %s: %s\n"),
7538 UFS_SIGNATURE_LIST".tmp", strerror(error));
7539 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7540 return (0);
7543 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7545 ret = fputs(signline, tfp);
7546 error = errno;
7547 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7548 if (ret != strlen(signline)) {
7549 bam_error(_("failed to write signature %s to signature "
7550 "list: %s\n"), sign, strerror(error));
7551 (void) fclose(tfp);
7552 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7553 return (0);
7556 ret = fclose(tfp);
7557 error = errno;
7558 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7559 if (ret == EOF) {
7560 bam_error(_("failed to close file: %s: %s\n"),
7561 UFS_SIGNATURE_LIST".tmp", strerror(error));
7562 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7563 return (0);
7566 /* Sort the list again */
7567 (void) snprintf(cmd, sizeof (cmd),
7568 "/usr/bin/sort -u %s.tmp > %s.sorted",
7569 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7571 ret = exec_cmd(cmd, NULL);
7572 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7573 if (ret != 0) {
7574 bam_error(_("error sorting GRUB UFS boot signatures\n"));
7575 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7576 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7577 return (0);
7580 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7582 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7583 error = errno;
7584 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7585 if (ret == -1) {
7586 bam_error(_("rename to file failed: %s: %s\n"),
7587 UFS_SIGNATURE_LIST, strerror(error));
7588 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7589 return (0);
7592 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7594 return (0);
7597 static int
7598 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7600 int ret;
7601 const char *fcn = "set_signature()";
7603 BAM_DPRINTF(("%s: entered. args: %s %s %s %s\n", fcn,
7604 osroot, osdev, sign, fstype));
7606 ret = set_backup(osroot, osdev, sign, fstype);
7607 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7608 if (ret == -1) {
7609 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7610 bam_error(_("failed to set backup sign (%s) for %s: %s\n"),
7611 sign, osroot, osdev);
7612 return (-1);
7615 ret = set_primary(osroot, osdev, sign, fstype);
7616 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7618 if (ret == 0) {
7619 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7620 } else {
7621 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7622 bam_error(_("failed to set primary sign (%s) for %s: %s\n"),
7623 sign, osroot, osdev);
7626 return (ret);
7629 char *
7630 get_grubsign(char *osroot, char *osdev)
7632 char *grubsign; /* (<sign>,#,#) */
7633 char *slice;
7634 int fdiskpart;
7635 char *sign;
7636 char *fstype;
7637 int ret;
7638 const char *fcn = "get_grubsign()";
7640 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, osdev));
7641 fstype = get_fstype(osroot);
7642 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7643 if (fstype == NULL) {
7644 bam_error(_("failed to get fstype for %s\n"), osroot);
7645 return (NULL);
7648 sign = find_existing_sign(osroot, osdev, fstype);
7649 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7650 if (sign == NULL) {
7651 BAM_DPRINTF(("%s: no existing grubsign for %s: %s\n",
7652 fcn, osroot, osdev));
7653 sign = create_new_sign(osdev, fstype);
7654 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7655 if (sign == NULL) {
7656 bam_error(_("failed to create GRUB boot signature for "
7657 "device: %s\n"), osdev);
7658 free(fstype);
7659 return (NULL);
7663 ret = set_signature(osroot, osdev, sign, fstype);
7664 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7665 if (ret == -1) {
7666 bam_error(_("failed to write GRUB boot signature for "
7667 "device: %s\n"), osdev);
7668 free(sign);
7669 free(fstype);
7670 (void) unlink(UFS_SIGNATURE_LIST);
7671 return (NULL);
7674 free(fstype);
7676 if (bam_verbose)
7677 bam_print(_("found or created GRUB signature %s for %s\n"),
7678 sign, osdev);
7680 fdiskpart = get_partition(osdev);
7681 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7682 if (fdiskpart == PARTNO_NOTFOUND) {
7683 bam_error(_("failed to determine fdisk partition: %s\n"),
7684 osdev);
7685 free(sign);
7686 return (NULL);
7689 slice = strrchr(osdev, 's');
7691 if (fdiskpart == PARTNO_EFI) {
7692 fdiskpart = atoi(&slice[1]);
7693 slice = NULL;
7696 grubsign = s_calloc(1, MAXNAMELEN + 10);
7697 if (slice) {
7698 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7699 sign, fdiskpart, slice[1] + 'a' - '0');
7700 } else
7701 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7702 sign, fdiskpart);
7704 free(sign);
7706 BAM_DPRINTF(("%s: successfully created grubsign %s\n", fcn, grubsign));
7708 return (grubsign);
7711 static char *
7712 get_title(char *rootdir)
7714 static char title[80];
7715 char *cp = NULL;
7716 char release[PATH_MAX];
7717 FILE *fp;
7718 const char *fcn = "get_title()";
7720 /* open the /etc/release file */
7721 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7723 fp = fopen(release, "r");
7724 if (fp == NULL) {
7725 bam_error(_("failed to open file: %s: %s\n"), release,
7726 strerror(errno));
7727 cp = NULL;
7728 goto out;
7731 /* grab first line of /etc/release */
7732 cp = s_fgets(title, sizeof (title), fp);
7733 if (cp) {
7734 while (isspace(*cp)) /* remove leading spaces */
7735 cp++;
7738 (void) fclose(fp);
7740 out:
7741 cp = cp ? cp : "Oracle Solaris";
7743 BAM_DPRINTF(("%s: got title: %s\n", fcn, cp));
7745 return (cp);
7748 char *
7749 get_special(char *mountp)
7751 FILE *mntfp;
7752 struct mnttab mp = {0};
7753 struct mnttab mpref = {0};
7754 int error;
7755 int ret;
7756 const char *fcn = "get_special()";
7758 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7759 if (mountp == NULL) {
7760 bam_error(_("cannot get special file: NULL mount-point\n"));
7761 return (NULL);
7764 mntfp = fopen(MNTTAB, "r");
7765 error = errno;
7766 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7767 if (mntfp == NULL) {
7768 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
7769 strerror(error));
7770 return (NULL);
7773 if (*mountp == '\0')
7774 mpref.mnt_mountp = "/";
7775 else
7776 mpref.mnt_mountp = mountp;
7778 ret = getmntany(mntfp, &mp, &mpref);
7779 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7780 if (ret != 0) {
7781 (void) fclose(mntfp);
7782 BAM_DPRINTF(("%s: Cannot get special file: mount-point %s "
7783 "not in mnttab\n", fcn, mountp));
7784 return (NULL);
7786 (void) fclose(mntfp);
7788 BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
7790 return (s_strdup(mp.mnt_special));
7793 static void
7794 free_physarray(char **physarray, int n)
7796 int i;
7797 const char *fcn = "free_physarray()";
7799 assert(physarray);
7800 assert(n);
7802 BAM_DPRINTF(("%s: entering args: %d\n", fcn, n));
7804 for (i = 0; i < n; i++) {
7805 free(physarray[i]);
7807 free(physarray);
7809 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7812 static int
7813 zfs_get_physical(char *special, char ***physarray, int *n)
7815 char sdup[PATH_MAX];
7816 char cmd[PATH_MAX];
7817 char dsk[PATH_MAX];
7818 char *pool;
7819 filelist_t flist = {0};
7820 line_t *lp;
7821 line_t *startlp;
7822 char *comp1;
7823 int i;
7824 int ret;
7825 const char *fcn = "zfs_get_physical()";
7827 assert(special);
7829 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, special));
7831 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7832 if (special[0] == '/') {
7833 bam_error(_("invalid device for ZFS filesystem: %s\n"),
7834 special);
7835 return (-1);
7838 (void) strlcpy(sdup, special, sizeof (sdup));
7840 pool = strtok(sdup, "/");
7841 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7842 if (pool == NULL) {
7843 bam_error(_("cannot derive ZFS pool from special: %s\n"),
7844 special);
7845 return (-1);
7848 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7850 ret = exec_cmd(cmd, &flist);
7851 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7852 if (ret != 0) {
7853 bam_error(_("cannot get zpool status for pool: %s\n"), pool);
7854 return (-1);
7857 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7858 if (flist.head == NULL) {
7859 bam_error(_("bad zpool status for pool=%s\n"), pool);
7860 filelist_free(&flist);
7861 return (-1);
7864 for (lp = flist.head; lp; lp = lp->next) {
7865 BAM_DPRINTF(("%s: strtok() zpool status line=%s\n",
7866 fcn, lp->line));
7867 comp1 = strtok(lp->line, " \t");
7868 if (comp1 == NULL) {
7869 free(lp->line);
7870 lp->line = NULL;
7871 } else {
7872 comp1 = s_strdup(comp1);
7873 free(lp->line);
7874 lp->line = comp1;
7878 for (lp = flist.head; lp; lp = lp->next) {
7879 if (lp->line == NULL)
7880 continue;
7881 if (strcmp(lp->line, pool) == 0) {
7882 BAM_DPRINTF(("%s: found pool name: %s in zpool "
7883 "status\n", fcn, pool));
7884 break;
7888 if (lp == NULL) {
7889 bam_error(_("no pool name %s in zpool status\n"), pool);
7890 filelist_free(&flist);
7891 return (-1);
7894 startlp = lp->next;
7895 for (i = 0, lp = startlp; lp; lp = lp->next) {
7896 if (lp->line == NULL)
7897 continue;
7898 if (strcmp(lp->line, "mirror") == 0)
7899 continue;
7900 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7901 break;
7902 i++;
7903 BAM_DPRINTF(("%s: counting phys slices in zpool status: %d\n",
7904 fcn, i));
7907 if (i == 0) {
7908 bam_error(_("no physical device in zpool status for pool=%s\n"),
7909 pool);
7910 filelist_free(&flist);
7911 return (-1);
7914 *n = i;
7915 *physarray = s_calloc(*n, sizeof (char *));
7916 for (i = 0, lp = startlp; lp; lp = lp->next) {
7917 if (lp->line == NULL)
7918 continue;
7919 if (strcmp(lp->line, "mirror") == 0)
7920 continue;
7921 if (strcmp(lp->line, "errors:") == 0)
7922 break;
7923 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7924 strncmp(lp->line, "/dev/rdsk/",
7925 strlen("/dev/rdsk/")) != 0) {
7926 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7927 lp->line);
7928 } else {
7929 (void) strlcpy(dsk, lp->line, sizeof (dsk));
7931 BAM_DPRINTF(("%s: adding phys slice=%s from pool %s status\n",
7932 fcn, dsk, pool));
7933 (*physarray)[i++] = s_strdup(dsk);
7936 assert(i == *n);
7938 filelist_free(&flist);
7940 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7941 return (0);
7944 static int
7945 get_physical(char *menu_root, char ***physarray, int *n)
7947 char *special;
7948 int ret;
7949 const char *fcn = "get_physical()";
7951 assert(menu_root);
7952 assert(physarray);
7953 assert(n);
7955 *physarray = NULL;
7956 *n = 0;
7958 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_root));
7960 /* First get the device special file from /etc/mnttab */
7961 special = get_special(menu_root);
7962 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7963 if (special == NULL) {
7964 bam_error(_("cannot get special file for mount-point: %s\n"),
7965 menu_root);
7966 return (-1);
7969 /* If already a physical device nothing to do */
7970 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7971 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7972 BAM_DPRINTF(("%s: got physical device already directly for "
7973 "menu_root=%s special=%s\n", fcn, menu_root, special));
7974 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
7975 *physarray = s_calloc(1, sizeof (char *));
7976 (*physarray)[0] = special;
7977 *n = 1;
7978 return (0);
7981 if (is_zfs(menu_root)) {
7982 ret = zfs_get_physical(special, physarray, n);
7983 } else {
7984 bam_error(_("cannot derive physical device for %s (%s), "
7985 "unsupported filesystem\n"), menu_root, special);
7986 ret = -1;
7989 free(special);
7991 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7992 if (ret == -1) {
7993 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
7994 } else {
7995 int i;
7996 assert (*n > 0);
7997 for (i = 0; i < *n; i++) {
7998 BAM_DPRINTF(("%s: returning physical=%s\n",
7999 fcn, (*physarray)[i]));
8003 return (ret);
8006 static int
8007 is_bootdisk(char *osroot, char *physical)
8009 int ret;
8010 char *grubroot;
8011 char *bootp;
8012 const char *fcn = "is_bootdisk()";
8014 assert(osroot);
8015 assert(physical);
8017 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, physical));
8019 bootp = strstr(physical, "p0:boot");
8020 if (bootp)
8021 *bootp = '\0';
8023 * We just want the BIOS mapping for menu disk.
8024 * Don't pass menu_root to get_grubroot() as the
8025 * check that it is used for is not relevant here.
8026 * The osroot is immaterial as well - it is only used to
8027 * to find create_diskmap script. Everything hinges on
8028 * "physical"
8030 grubroot = get_grubroot(osroot, physical, NULL);
8032 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
8033 if (grubroot == NULL) {
8034 if (bam_verbose)
8035 bam_error(_("cannot determine BIOS disk ID 'hd?' for "
8036 "disk: %s\n"), physical);
8037 return (0);
8039 ret = grubroot[3] == '0';
8040 free(grubroot);
8042 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
8044 return (ret);
8048 * Check if menu is on the boot device
8049 * Return 0 (false) on error
8051 static int
8052 menu_on_bootdisk(char *osroot, char *menu_root)
8054 char **physarray;
8055 int ret;
8056 int n;
8057 int i;
8058 int on_bootdisk;
8059 const char *fcn = "menu_on_bootdisk()";
8061 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8063 ret = get_physical(menu_root, &physarray, &n);
8064 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
8065 if (ret != 0) {
8066 bam_error(_("cannot get physical device special file for menu "
8067 "root: %s\n"), menu_root);
8068 return (0);
8071 assert(physarray);
8072 assert(n > 0);
8074 on_bootdisk = 0;
8075 for (i = 0; i < n; i++) {
8076 assert(strncmp(physarray[i], "/dev/dsk/",
8077 strlen("/dev/dsk/")) == 0 ||
8078 strncmp(physarray[i], "/dev/rdsk/",
8079 strlen("/dev/rdsk/")) == 0);
8081 BAM_DPRINTF(("%s: checking if phys-device=%s is on bootdisk\n",
8082 fcn, physarray[i]));
8083 if (is_bootdisk(osroot, physarray[i])) {
8084 on_bootdisk = 1;
8085 BAM_DPRINTF(("%s: phys-device=%s *IS* on bootdisk\n",
8086 fcn, physarray[i]));
8090 free_physarray(physarray, n);
8092 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8093 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8094 if (on_bootdisk) {
8095 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8096 } else {
8097 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8100 return (on_bootdisk);
8103 void
8104 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8106 const char *fcn = "bam_add_line()";
8108 assert(mp);
8109 assert(entry);
8110 assert(prev);
8111 assert(lp);
8113 lp->next = prev->next;
8114 if (prev->next) {
8115 BAM_DPRINTF(("%s: previous next exists\n", fcn));
8116 prev->next->prev = lp;
8117 } else {
8118 BAM_DPRINTF(("%s: previous next does not exist\n", fcn));
8120 prev->next = lp;
8121 lp->prev = prev;
8123 if (entry->end == prev) {
8124 BAM_DPRINTF(("%s: last line in entry\n", fcn));
8125 entry->end = lp;
8127 if (mp->end == prev) {
8128 assert(lp->next == NULL);
8129 mp->end = lp;
8130 BAM_DPRINTF(("%s: last line in menu\n", fcn));
8135 * look for matching bootadm entry with specified parameters
8136 * Here are the rules (based on existing usage):
8137 * - If title is specified, match on title only
8138 * - Else, match on root/findroot, kernel, and module.
8139 * Note that, if root_opt is non-zero, the absence of
8140 * root line is considered a match.
8142 static entry_t *
8143 find_boot_entry(
8144 menu_t *mp,
8145 char *title,
8146 char *kernel,
8147 char *findroot,
8148 char *root,
8149 char *module,
8150 int root_opt,
8151 int *entry_num)
8153 int i;
8154 line_t *lp;
8155 entry_t *ent;
8156 const char *fcn = "find_boot_entry()";
8158 if (entry_num)
8159 *entry_num = BAM_ERROR;
8161 /* find matching entry */
8162 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8163 lp = ent->start;
8165 /* first line of entry must be bootadm comment */
8166 lp = ent->start;
8167 if (lp->flags != BAM_COMMENT ||
8168 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8169 continue;
8172 /* advance to title line */
8173 lp = lp->next;
8174 if (title) {
8175 if (lp->flags == BAM_TITLE && lp->arg &&
8176 strcmp(lp->arg, title) == 0) {
8177 BAM_DPRINTF(("%s: matched title: %s\n",
8178 fcn, title));
8179 break;
8181 BAM_DPRINTF(("%s: no match title: %s, %s\n",
8182 fcn, title, lp->arg));
8183 continue; /* check title only */
8186 lp = lp->next; /* advance to root line */
8187 if (lp == NULL) {
8188 continue;
8189 } else if (lp->cmd != NULL &&
8190 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8191 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8192 findroot = NULL);
8193 if (findroot == NULL) {
8194 BAM_DPRINTF(("%s: no match line has findroot, "
8195 "we don't: %s\n", fcn, lp->arg));
8196 continue;
8198 /* findroot command found, try match */
8199 if (strcmp(lp->arg, findroot) != 0) {
8200 BAM_DPRINTF(("%s: no match findroot: %s, %s\n",
8201 fcn, findroot, lp->arg));
8202 continue;
8204 BAM_DPRINTF(("%s: matched findroot: %s\n",
8205 fcn, findroot));
8206 lp = lp->next; /* advance to kernel line */
8207 } else if (lp->cmd != NULL &&
8208 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8209 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8210 if (root == NULL) {
8211 BAM_DPRINTF(("%s: no match, line has root, we "
8212 "don't: %s\n", fcn, lp->arg));
8213 continue;
8215 /* root cmd found, try match */
8216 if (strcmp(lp->arg, root) != 0) {
8217 BAM_DPRINTF(("%s: no match root: %s, %s\n",
8218 fcn, root, lp->arg));
8219 continue;
8221 BAM_DPRINTF(("%s: matched root: %s\n", fcn, root));
8222 lp = lp->next; /* advance to kernel line */
8223 } else {
8224 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8225 root_opt = 0);
8226 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8227 root_opt = 1);
8228 /* no root command, see if root is optional */
8229 if (root_opt == 0) {
8230 BAM_DPRINTF(("%s: root NOT optional\n", fcn));
8231 continue;
8233 BAM_DPRINTF(("%s: root IS optional\n", fcn));
8236 if (lp == NULL || lp->next == NULL) {
8237 continue;
8240 if (kernel &&
8241 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8242 if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8243 !(ent->flags & BAM_ENTRY_DBOOT) ||
8244 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8245 continue;
8247 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8250 BAM_DPRINTF(("%s: kernel match: %s, %s\n", fcn,
8251 kernel, lp->arg));
8254 * Check for matching module entry (failsafe or normal).
8255 * If it fails to match, we go around the loop again.
8256 * For xpv entries, there are two module lines, so we
8257 * do the check twice.
8259 lp = lp->next; /* advance to module line */
8260 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8261 (((lp = lp->next) != NULL) &&
8262 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8263 /* match found */
8264 BAM_DPRINTF(("%s: module match: %s, %s\n", fcn,
8265 module, lp->arg));
8266 break;
8269 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8270 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8271 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8272 ent->flags |= BAM_ENTRY_UPGFSMODULE;
8273 break;
8278 if (ent && entry_num) {
8279 *entry_num = i;
8282 if (ent) {
8283 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8284 } else {
8285 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_ERROR));
8287 return (ent);
8290 static int
8291 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8292 char *kernel, char *mod_kernel, char *module, int root_opt)
8294 int i;
8295 int change_kernel = 0;
8296 entry_t *ent;
8297 line_t *lp;
8298 line_t *tlp;
8299 char linebuf[BAM_MAXLINE];
8300 const char *fcn = "update_boot_entry()";
8302 /* note: don't match on title, it's updated on upgrade */
8303 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8304 root_opt, &i);
8305 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8307 * We may be upgrading a kernel from multiboot to
8308 * directboot. Look for a multiboot entry. A multiboot
8309 * entry will not have a findroot line.
8311 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8312 MULTIBOOT_ARCHIVE, root_opt, &i);
8313 if (ent != NULL) {
8314 BAM_DPRINTF(("%s: upgrading entry from dboot to "
8315 "multiboot: root = %s\n", fcn, root));
8316 change_kernel = 1;
8318 } else if (ent) {
8319 BAM_DPRINTF(("%s: found entry with matching findroot: %s\n",
8320 fcn, findroot));
8323 if (ent == NULL) {
8324 BAM_DPRINTF(("%s: boot entry not found in menu. Creating "
8325 "new entry, findroot = %s\n", fcn, findroot));
8326 return (add_boot_entry(mp, title, findroot,
8327 kernel, mod_kernel, module, NULL));
8330 /* replace title of existing entry and update findroot line */
8331 lp = ent->start;
8332 lp = lp->next; /* title line */
8333 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8334 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8335 free(lp->arg);
8336 free(lp->line);
8337 lp->arg = s_strdup(title);
8338 lp->line = s_strdup(linebuf);
8339 BAM_DPRINTF(("%s: changing title to: %s\n", fcn, title));
8341 tlp = lp; /* title line */
8342 lp = lp->next; /* root line */
8344 /* if no root or findroot command, create a new line_t */
8345 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8346 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8347 lp = s_calloc(1, sizeof (line_t));
8348 bam_add_line(mp, ent, tlp, lp);
8349 } else {
8350 if (lp->cmd != NULL)
8351 free(lp->cmd);
8353 free(lp->sep);
8354 free(lp->arg);
8355 free(lp->line);
8358 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8359 lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8360 lp->arg = s_strdup(findroot);
8361 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8362 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8363 lp->line = s_strdup(linebuf);
8364 BAM_DPRINTF(("%s: adding findroot line: %s\n", fcn, findroot));
8366 /* kernel line */
8367 lp = lp->next;
8369 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8370 char *params = NULL;
8372 params = strstr(lp->line, "-s");
8373 if (params != NULL)
8374 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8375 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8376 kernel, params+2);
8377 else
8378 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8379 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8380 kernel);
8382 if (lp->cmd != NULL)
8383 free(lp->cmd);
8385 free(lp->arg);
8386 free(lp->line);
8387 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8388 lp->arg = s_strdup(strstr(linebuf, "/"));
8389 lp->line = s_strdup(linebuf);
8390 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8391 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8392 fcn, lp->prev->cmd));
8395 if (change_kernel) {
8397 * We're upgrading from multiboot to directboot.
8399 if (lp->cmd != NULL &&
8400 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8401 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8402 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8403 kernel);
8404 free(lp->cmd);
8405 free(lp->arg);
8406 free(lp->line);
8407 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8408 lp->arg = s_strdup(kernel);
8409 lp->line = s_strdup(linebuf);
8410 lp = lp->next;
8411 BAM_DPRINTF(("%s: adding new kernel$ line: %s\n",
8412 fcn, kernel));
8414 if (lp->cmd != NULL &&
8415 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8416 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8417 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8418 module);
8419 free(lp->cmd);
8420 free(lp->arg);
8421 free(lp->line);
8422 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8423 lp->arg = s_strdup(module);
8424 lp->line = s_strdup(linebuf);
8425 lp = lp->next;
8426 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8427 fcn, module));
8431 /* module line */
8432 lp = lp->next;
8434 if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8435 if (lp->cmd != NULL &&
8436 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8437 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8438 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8439 module);
8440 free(lp->cmd);
8441 free(lp->arg);
8442 free(lp->line);
8443 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8444 lp->arg = s_strdup(module);
8445 lp->line = s_strdup(linebuf);
8446 lp = lp->next;
8447 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8448 BAM_DPRINTF(("%s: adding new module$ line: %s\n",
8449 fcn, module));
8453 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, i));
8454 return (i);
8458 root_optional(char *osroot, char *menu_root)
8460 char *ospecial;
8461 char *mspecial;
8462 char *slash;
8463 int root_opt;
8464 int ret1;
8465 int ret2;
8466 const char *fcn = "root_optional()";
8468 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
8471 * For all filesystems except ZFS, a straight compare of osroot
8472 * and menu_root will tell us if root is optional.
8473 * For ZFS, the situation is complicated by the fact that
8474 * menu_root and osroot are always different
8476 ret1 = is_zfs(osroot);
8477 ret2 = is_zfs(menu_root);
8478 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8479 if (!ret1 || !ret2) {
8480 BAM_DPRINTF(("%s: one or more non-ZFS filesystems (%s, %s)\n",
8481 fcn, osroot, menu_root));
8482 root_opt = (strcmp(osroot, menu_root) == 0);
8483 goto out;
8486 ospecial = get_special(osroot);
8487 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8488 if (ospecial == NULL) {
8489 bam_error(_("failed to get special file for osroot: %s\n"),
8490 osroot);
8491 return (0);
8493 BAM_DPRINTF(("%s: ospecial=%s for osroot=%s\n", fcn, ospecial, osroot));
8495 mspecial = get_special(menu_root);
8496 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8497 if (mspecial == NULL) {
8498 bam_error(_("failed to get special file for menu_root: %s\n"),
8499 menu_root);
8500 free(ospecial);
8501 return (0);
8503 BAM_DPRINTF(("%s: mspecial=%s for menu_root=%s\n",
8504 fcn, mspecial, menu_root));
8506 slash = strchr(ospecial, '/');
8507 if (slash)
8508 *slash = '\0';
8509 BAM_DPRINTF(("%s: FIXED ospecial=%s for osroot=%s\n",
8510 fcn, ospecial, osroot));
8512 root_opt = (strcmp(ospecial, mspecial) == 0);
8514 free(ospecial);
8515 free(mspecial);
8517 out:
8518 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8519 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8520 if (root_opt) {
8521 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8522 } else {
8523 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
8526 return (root_opt);
8529 /*ARGSUSED*/
8530 static error_t
8531 update_entry(menu_t *mp, char *menu_root, char *osdev)
8533 int entry;
8534 char *grubsign;
8535 char *grubroot;
8536 char *title;
8537 char osroot[PATH_MAX];
8538 char *failsafe_kernel = NULL;
8539 struct stat sbuf;
8540 char failsafe[256];
8541 char failsafe_64[256];
8542 int ret;
8543 const char *fcn = "update_entry()";
8545 assert(mp);
8546 assert(menu_root);
8547 assert(osdev);
8548 assert(bam_root);
8550 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, menu_root, osdev,
8551 bam_root));
8553 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8555 title = get_title(osroot);
8556 assert(title);
8558 grubsign = get_grubsign(osroot, osdev);
8559 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8560 if (grubsign == NULL) {
8561 bam_error(_("failed to get grubsign for root: %s, device %s\n"),
8562 osroot, osdev);
8563 return (BAM_ERROR);
8567 * It is not a fatal error if get_grubroot() fails
8568 * We no longer rely on biosdev to populate the
8569 * menu
8571 grubroot = get_grubroot(osroot, osdev, menu_root);
8572 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8573 if (grubroot) {
8574 BAM_DPRINTF(("%s: get_grubroot success. osroot=%s, osdev=%s, "
8575 "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8576 } else {
8577 BAM_DPRINTF(("%s: get_grubroot failed. osroot=%s, osdev=%s, "
8578 "menu_root=%s\n", fcn, osroot, osdev, menu_root));
8581 /* add the entry for normal Solaris */
8582 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8583 bam_direct = BAM_DIRECT_MULTIBOOT);
8584 if (bam_direct == BAM_DIRECT_DBOOT) {
8585 entry = update_boot_entry(mp, title, grubsign, grubroot,
8586 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8587 NULL, DIRECT_BOOT_ARCHIVE,
8588 root_optional(osroot, menu_root));
8589 BAM_DPRINTF(("%s: updated boot entry bam_zfs=%d, "
8590 "grubsign = %s\n", fcn, bam_zfs, grubsign));
8591 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8592 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8593 grubroot, XEN_MENU, bam_zfs ?
8594 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8595 DIRECT_BOOT_ARCHIVE,
8596 root_optional(osroot, menu_root));
8597 BAM_DPRINTF(("%s: updated HV entry bam_zfs=%d, "
8598 "grubsign = %s\n", fcn, bam_zfs, grubsign));
8600 } else {
8601 entry = update_boot_entry(mp, title, grubsign, grubroot,
8602 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8603 root_optional(osroot, menu_root));
8605 BAM_DPRINTF(("%s: updated MULTIBOOT entry grubsign = %s\n",
8606 fcn, grubsign));
8610 * Add the entry for failsafe archive. On a bfu'd system, the
8611 * failsafe may be different than the installed kernel.
8613 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8614 osroot, FAILSAFE_ARCHIVE_32);
8615 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8616 osroot, FAILSAFE_ARCHIVE_64);
8619 * Check if at least one of the two archives exists
8620 * Using $ISADIR as the default line, we have an entry which works
8621 * for both the cases.
8624 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8626 /* Figure out where the kernel line should point */
8627 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8628 DIRECT_BOOT_FAILSAFE_32);
8629 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8630 osroot, DIRECT_BOOT_FAILSAFE_64);
8631 if (stat(failsafe, &sbuf) == 0 ||
8632 stat(failsafe_64, &sbuf) == 0) {
8633 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8634 } else {
8635 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8636 osroot, MULTI_BOOT_FAILSAFE);
8637 if (stat(failsafe, &sbuf) == 0) {
8638 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8641 if (failsafe_kernel != NULL) {
8642 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8643 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8644 root_optional(osroot, menu_root));
8645 BAM_DPRINTF(("%s: updated FAILSAFE entry "
8646 "failsafe_kernel = %s\n", fcn, failsafe_kernel));
8649 free(grubroot);
8651 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8652 if (entry == BAM_ERROR) {
8653 bam_error(_("failed to add boot entry with title=%s, grub "
8654 "signature=%s\n"), title, grubsign);
8655 free(grubsign);
8656 return (BAM_ERROR);
8658 free(grubsign);
8660 update_numbering(mp);
8661 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8662 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8663 if (ret == BAM_ERROR) {
8664 bam_error(_("failed to set GRUB menu default to %d\n"), entry);
8666 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8667 return (BAM_WRITE);
8670 static void
8671 save_default_entry(menu_t *mp, const char *which)
8673 int lineNum;
8674 int entryNum;
8675 int entry = 0; /* default is 0 */
8676 char linebuf[BAM_MAXLINE];
8677 line_t *lp = mp->curdefault;
8678 const char *fcn = "save_default_entry()";
8680 if (mp->start) {
8681 lineNum = mp->end->lineNum;
8682 entryNum = mp->end->entryNum;
8683 } else {
8684 lineNum = LINE_INIT;
8685 entryNum = ENTRY_INIT;
8688 if (lp)
8689 entry = s_strtol(lp->arg);
8691 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8692 BAM_DPRINTF(("%s: saving default to: %s\n", fcn, linebuf));
8693 line_parser(mp, linebuf, &lineNum, &entryNum);
8694 BAM_DPRINTF(("%s: saved default to lineNum=%d, entryNum=%d\n", fcn,
8695 lineNum, entryNum));
8698 static void
8699 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8701 int entry;
8702 char *str;
8703 const char *fcn = "restore_default_entry()";
8705 if (lp == NULL) {
8706 BAM_DPRINTF(("%s: NULL saved default\n", fcn));
8707 return; /* nothing to restore */
8710 BAM_DPRINTF(("%s: saved default string: %s\n", fcn, which));
8712 str = lp->arg + strlen(which);
8713 entry = s_strtol(str);
8714 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8716 BAM_DPRINTF(("%s: restored default to entryNum: %d\n", fcn, entry));
8718 /* delete saved old default line */
8719 unlink_line(mp, lp);
8720 line_free(lp);
8724 * This function is for supporting reboot with args.
8725 * The opt value can be:
8726 * NULL delete temp entry, if present
8727 * entry=<n> switches default entry to <n>
8728 * else treated as boot-args and setup a temperary menu entry
8729 * and make it the default
8730 * Note that we are always rebooting the current OS instance
8731 * so osroot == / always.
8733 #define REBOOT_TITLE "Solaris_reboot_transient"
8735 /*ARGSUSED*/
8736 static error_t
8737 update_temp(menu_t *mp, char *dummy, char *opt)
8739 int entry;
8740 char *osdev;
8741 char *fstype;
8742 char *sign;
8743 char *opt_ptr;
8744 char *path;
8745 char kernbuf[BUFSIZ];
8746 char args_buf[BUFSIZ];
8747 char signbuf[PATH_MAX];
8748 int ret;
8749 const char *fcn = "update_temp()";
8751 assert(mp);
8752 assert(dummy == NULL);
8754 /* opt can be NULL */
8755 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt ? opt : "<NULL>"));
8756 BAM_DPRINTF(("%s: bam_alt_root: %d, bam_root: %s\n", fcn,
8757 bam_alt_root, bam_root));
8759 if (bam_alt_root || bam_rootlen != 1 ||
8760 strcmp(bam_root, "/") != 0 ||
8761 strcmp(rootbuf, "/") != 0) {
8762 bam_error(_("an alternate root (%s) cannot be used with this "
8763 "sub-command\n"), bam_root);
8764 return (BAM_ERROR);
8767 /* If no option, delete exiting reboot menu entry */
8768 if (opt == NULL) {
8769 entry_t *ent;
8770 BAM_DPRINTF(("%s: opt is NULL\n", fcn));
8771 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8772 NULL, NULL, 0, &entry);
8773 if (ent == NULL) { /* not found is ok */
8774 BAM_DPRINTF(("%s: transient entry not found\n", fcn));
8775 return (BAM_SUCCESS);
8777 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8778 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8779 mp->olddefault = NULL;
8780 BAM_DPRINTF(("%s: restored old default\n", fcn));
8781 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8782 return (BAM_WRITE);
8785 /* if entry= is specified, set the default entry */
8786 if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8787 int entryNum = s_strtol(opt + strlen("entry="));
8788 BAM_DPRINTF(("%s: opt has entry=: %s\n", fcn, opt));
8789 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8790 /* this is entry=# option */
8791 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8792 BAM_DPRINTF(("%s: default set to %d, "
8793 "set_default ret=%d\n", fcn, entry, ret));
8794 return (ret);
8795 } else {
8796 bam_error(_("failed to set GRUB menu default to %d\n"),
8797 entryNum);
8798 return (BAM_ERROR);
8803 * add a new menu entry based on opt and make it the default
8806 fstype = get_fstype("/");
8807 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8808 if (fstype == NULL) {
8809 bam_error(_("failed to determine filesystem type for \"/\". "
8810 "Reboot with \narguments failed.\n"));
8811 return (BAM_ERROR);
8814 osdev = get_special("/");
8815 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8816 if (osdev == NULL) {
8817 free(fstype);
8818 bam_error(_("failed to find device special file for \"/\". "
8819 "Reboot with \narguments failed.\n"));
8820 return (BAM_ERROR);
8823 sign = find_existing_sign("/", osdev, fstype);
8824 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8825 if (sign == NULL) {
8826 free(fstype);
8827 free(osdev);
8828 bam_error(_("failed to find boot signature. Reboot with "
8829 "arguments failed.\n"));
8830 return (BAM_ERROR);
8833 free(osdev);
8834 (void) strlcpy(signbuf, sign, sizeof (signbuf));
8835 free(sign);
8837 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8838 strchr(signbuf, ')') == NULL);
8841 * There is no alternate root while doing reboot with args
8842 * This version of bootadm is only delivered with a DBOOT
8843 * version of Solaris.
8845 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8846 if (bam_direct != BAM_DIRECT_DBOOT) {
8847 free(fstype);
8848 bam_error(_("the root filesystem is not a dboot Solaris "
8849 "instance. \nThis version of bootadm is not supported "
8850 "on this version of Solaris.\n"));
8851 return (BAM_ERROR);
8854 /* add an entry for Solaris reboot */
8855 if (opt[0] == '-') {
8856 /* It's an option - first see if boot-file is set */
8857 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8858 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8859 if (ret != BAM_SUCCESS) {
8860 free(fstype);
8861 bam_error(_("reboot with arguments: error querying "
8862 "current boot-file settings\n"));
8863 return (BAM_ERROR);
8865 if (kernbuf[0] == '\0')
8866 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8867 sizeof (kernbuf));
8869 * If this is a zfs file system and kernbuf does not
8870 * have "-B $ZFS-BOOTFS" string yet, add it.
8872 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8873 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8874 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8876 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8877 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8878 BAM_DPRINTF(("%s: reboot with args, option specified: "
8879 "kern=%s\n", fcn, kernbuf));
8880 } else if (opt[0] == '/') {
8881 /* It's a full path, so write it out. */
8882 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8885 * If someone runs:
8887 * # eeprom boot-args='-kd'
8888 * # reboot /platform/i86pc/kernel/unix
8890 * we want to use the boot-args as part of the boot
8891 * line. On the other hand, if someone runs:
8893 * # reboot "/platform/i86pc/kernel/unix -kd"
8895 * we don't need to mess with boot-args. If there's
8896 * no space in the options string, assume we're in the
8897 * first case.
8899 if (strchr(opt, ' ') == NULL) {
8900 ret = get_kernel(mp, ARGS_CMD, args_buf,
8901 sizeof (args_buf));
8902 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8903 if (ret != BAM_SUCCESS) {
8904 free(fstype);
8905 bam_error(_("reboot with arguments: error "
8906 "querying current boot-args settings\n"));
8907 return (BAM_ERROR);
8910 if (args_buf[0] != '\0') {
8911 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8912 (void) strlcat(kernbuf, args_buf,
8913 sizeof (kernbuf));
8916 BAM_DPRINTF(("%s: reboot with args, abspath specified: "
8917 "kern=%s\n", fcn, kernbuf));
8918 } else {
8920 * It may be a partial path, or it may be a partial
8921 * path followed by options. Assume that only options
8922 * follow a space. If someone sends us a kernel path
8923 * that includes a space, they deserve to be broken.
8925 opt_ptr = strchr(opt, ' ');
8926 if (opt_ptr != NULL) {
8927 *opt_ptr = '\0';
8930 path = expand_path(opt);
8931 if (path != NULL) {
8932 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8933 free(path);
8936 * If there were options given, use those.
8937 * Otherwise, copy over the default options.
8939 if (opt_ptr != NULL) {
8940 /* Restore the space in opt string */
8941 *opt_ptr = ' ';
8942 (void) strlcat(kernbuf, opt_ptr,
8943 sizeof (kernbuf));
8944 } else {
8945 ret = get_kernel(mp, ARGS_CMD, args_buf,
8946 sizeof (args_buf));
8947 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8948 ret = BAM_ERROR);
8949 if (ret != BAM_SUCCESS) {
8950 free(fstype);
8951 bam_error(_("reboot with arguments: "
8952 "error querying current boot-args "
8953 "settings\n"));
8954 return (BAM_ERROR);
8957 if (args_buf[0] != '\0') {
8958 (void) strlcat(kernbuf, " ",
8959 sizeof (kernbuf));
8960 (void) strlcat(kernbuf,
8961 args_buf, sizeof (kernbuf));
8964 BAM_DPRINTF(("%s: resolved partial path: %s\n",
8965 fcn, kernbuf));
8966 } else {
8967 free(fstype);
8968 bam_error(_("unable to expand %s to a full file"
8969 " path.\n"), opt);
8970 bam_print_stderr(_("Rebooting with default kernel "
8971 "and options.\n"));
8972 return (BAM_ERROR);
8975 free(fstype);
8976 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8977 NULL, NULL, NULL);
8978 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8979 if (entry == BAM_ERROR) {
8980 bam_error(_("Cannot update menu. Cannot reboot with "
8981 "requested arguments\n"));
8982 return (BAM_ERROR);
8985 save_default_entry(mp, BAM_OLDDEF);
8986 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8987 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8988 if (ret == BAM_ERROR) {
8989 bam_error(_("reboot with arguments: setting GRUB menu default "
8990 "to %d failed\n"), entry);
8992 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
8993 return (BAM_WRITE);
8996 error_t
8997 set_global(menu_t *mp, char *globalcmd, int val)
8999 line_t *lp;
9000 line_t *found;
9001 line_t *last;
9002 char *cp;
9003 char *str;
9004 char prefix[BAM_MAXLINE];
9005 size_t len;
9006 const char *fcn = "set_global()";
9008 assert(mp);
9009 assert(globalcmd);
9011 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
9012 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
9013 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
9014 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
9015 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
9016 (void) snprintf(prefix, sizeof (prefix), "%d", val);
9017 bam_error(_("invalid boot entry number: %s\n"), prefix);
9018 return (BAM_ERROR);
9022 found = last = NULL;
9023 for (lp = mp->start; lp; lp = lp->next) {
9024 if (lp->flags != BAM_GLOBAL)
9025 continue;
9027 last = lp; /* track the last global found */
9029 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
9030 if (lp->cmd == NULL) {
9031 bam_error(_("no command at line %d\n"), lp->lineNum);
9032 continue;
9034 if (strcmp(globalcmd, lp->cmd) != 0)
9035 continue;
9037 BAM_DPRINTF(("%s: found matching global command: %s\n",
9038 fcn, globalcmd));
9040 if (found) {
9041 bam_error(_("duplicate command %s at line %d of "
9042 "%sboot/grub/menu.lst\n"), globalcmd,
9043 lp->lineNum, bam_root);
9045 found = lp;
9048 if (found == NULL) {
9049 lp = s_calloc(1, sizeof (line_t));
9050 if (last == NULL) {
9051 lp->next = mp->start;
9052 mp->start = lp;
9053 mp->end = (mp->end) ? mp->end : lp;
9054 } else {
9055 lp->next = last->next;
9056 last->next = lp;
9057 if (lp->next == NULL)
9058 mp->end = lp;
9060 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
9061 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
9062 len += 10; /* val < 10 digits */
9063 lp->line = s_calloc(1, len);
9064 (void) snprintf(lp->line, len, "%s%s%d",
9065 globalcmd, menu_cmds[SEP_CMD], val);
9066 BAM_DPRINTF(("%s: wrote new global line: %s\n", fcn, lp->line));
9067 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9068 return (BAM_WRITE);
9072 * We are changing an existing entry. Retain any prefix whitespace,
9073 * but overwrite everything else. This preserves tabs added for
9074 * readability.
9076 str = found->line;
9077 cp = prefix;
9078 while (*str == ' ' || *str == '\t')
9079 *(cp++) = *(str++);
9080 *cp = '\0'; /* Terminate prefix */
9081 len = strlen(prefix) + strlen(globalcmd);
9082 len += strlen(menu_cmds[SEP_CMD]) + 10;
9084 free(found->line);
9085 found->line = s_calloc(1, len);
9086 (void) snprintf(found->line, len,
9087 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
9089 BAM_DPRINTF(("%s: replaced global line with: %s\n", fcn, found->line));
9090 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9091 return (BAM_WRITE); /* need a write to menu */
9095 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
9096 * expand it to a full unix path. The calling function is expected to
9097 * output a message if an error occurs and NULL is returned.
9099 static char *
9100 expand_path(const char *partial_path)
9102 int new_path_len;
9103 char *new_path;
9104 char new_path2[PATH_MAX];
9105 struct stat sb;
9106 const char *fcn = "expand_path()";
9108 new_path_len = strlen(partial_path) + 64;
9109 new_path = s_calloc(1, new_path_len);
9111 /* First, try the simplest case - something like "kernel/unix" */
9112 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
9113 partial_path);
9114 if (stat(new_path, &sb) == 0) {
9115 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9116 return (new_path);
9119 if (strcmp(partial_path, "kmdb") == 0) {
9120 (void) snprintf(new_path, new_path_len, "%s -k",
9121 DIRECT_BOOT_KERNEL);
9122 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9123 return (new_path);
9127 * We've quickly reached unsupported usage. Try once more to
9128 * see if we were just given a glom name.
9130 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9131 partial_path);
9132 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9133 partial_path);
9134 if (stat(new_path, &sb) == 0) {
9135 if (stat(new_path2, &sb) == 0) {
9137 * We matched both, so we actually
9138 * want to write the $ISADIR version.
9140 (void) snprintf(new_path, new_path_len,
9141 "/platform/i86pc/kernel/%s/$ISADIR/unix",
9142 partial_path);
9144 BAM_DPRINTF(("%s: expanded path: %s\n", fcn, new_path));
9145 return (new_path);
9148 free(new_path);
9149 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9150 return (NULL);
9154 * The kernel cmd and arg have been changed, so
9155 * check whether the archive line needs to change.
9157 static void
9158 set_archive_line(entry_t *entryp, line_t *kernelp)
9160 line_t *lp = entryp->start;
9161 char *new_archive;
9162 menu_cmd_t m_cmd;
9163 const char *fcn = "set_archive_line()";
9165 for (; lp != NULL; lp = lp->next) {
9166 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9167 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9168 break;
9171 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9172 if (lp == entryp->end) {
9173 BAM_DPRINTF(("%s: no module/archive line for entry: "
9174 "%d\n", fcn, entryp->entryNum));
9175 return;
9178 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9179 if (lp == NULL) {
9180 BAM_DPRINTF(("%s: no module/archive line for entry: %d\n",
9181 fcn, entryp->entryNum));
9182 return;
9185 if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9186 new_archive = DIRECT_BOOT_ARCHIVE;
9187 m_cmd = MODULE_DOLLAR_CMD;
9188 } else if (strstr(kernelp->arg, "amd64") != NULL) {
9189 new_archive = DIRECT_BOOT_ARCHIVE_64;
9190 m_cmd = MODULE_CMD;
9191 } else {
9192 new_archive = DIRECT_BOOT_ARCHIVE_32;
9193 m_cmd = MODULE_CMD;
9196 if (strcmp(lp->arg, new_archive) == 0) {
9197 BAM_DPRINTF(("%s: no change for line: %s\n", fcn, lp->arg));
9198 return;
9201 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9202 free(lp->cmd);
9203 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9206 free(lp->arg);
9207 lp->arg = s_strdup(new_archive);
9208 update_line(lp);
9209 BAM_DPRINTF(("%s: replaced for line: %s\n", fcn, lp->line));
9213 * Title for an entry to set properties that once went in bootenv.rc.
9215 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
9218 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9219 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
9220 * string, reset the value to the default. If path is a non-zero-length
9221 * string, set the kernel or arguments.
9223 static error_t
9224 get_set_kernel(
9225 menu_t *mp,
9226 menu_cmd_t optnum,
9227 char *path,
9228 char *buf,
9229 size_t bufsize)
9231 int entryNum;
9232 int rv = BAM_SUCCESS;
9233 int free_new_path = 0;
9234 entry_t *entryp;
9235 line_t *ptr;
9236 line_t *kernelp;
9237 char *new_arg;
9238 char *old_args;
9239 char *space;
9240 char *new_path;
9241 char old_space;
9242 size_t old_kernel_len = 0;
9243 size_t new_str_len;
9244 char *fstype;
9245 char *osdev;
9246 char *sign;
9247 char signbuf[PATH_MAX];
9248 int ret;
9249 const char *fcn = "get_set_kernel()";
9251 assert(bufsize > 0);
9253 ptr = kernelp = NULL;
9254 new_arg = old_args = space = NULL;
9255 new_path = NULL;
9256 buf[0] = '\0';
9258 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9259 bam_direct = BAM_DIRECT_MULTIBOOT);
9260 if (bam_direct != BAM_DIRECT_DBOOT) {
9261 bam_error(_("bootadm set-menu %s may only be run on "
9262 "directboot kernels.\n"),
9263 optnum == KERNEL_CMD ? "kernel" : "args");
9264 return (BAM_ERROR);
9268 * If a user changed the default entry to a non-bootadm controlled
9269 * one, we don't want to mess with it. Just print an error and
9270 * return.
9272 if (mp->curdefault) {
9273 entryNum = s_strtol(mp->curdefault->arg);
9274 for (entryp = mp->entries; entryp; entryp = entryp->next) {
9275 if (entryp->entryNum == entryNum)
9276 break;
9278 if ((entryp != NULL) &&
9279 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9280 bam_error(_("Default /boot/grub/menu.lst entry is not "
9281 "controlled by bootadm. Exiting\n"));
9282 return (BAM_ERROR);
9286 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9287 0, &entryNum);
9289 if (entryp != NULL) {
9290 for (ptr = entryp->start; ptr && ptr != entryp->end;
9291 ptr = ptr->next) {
9292 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9293 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9294 kernelp = ptr;
9295 break;
9298 if (kernelp == NULL) {
9299 bam_error(_("no kernel line found in entry %d\n"),
9300 entryNum);
9301 return (BAM_ERROR);
9304 old_kernel_len = strcspn(kernelp->arg, " \t");
9305 space = old_args = kernelp->arg + old_kernel_len;
9306 while ((*old_args == ' ') || (*old_args == '\t'))
9307 old_args++;
9310 if (path == NULL) {
9311 if (entryp == NULL) {
9312 BAM_DPRINTF(("%s: no RC entry, nothing to report\n",
9313 fcn));
9314 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9315 return (BAM_SUCCESS);
9317 assert(kernelp);
9318 if (optnum == ARGS_CMD) {
9319 if (old_args[0] != '\0') {
9320 (void) strlcpy(buf, old_args, bufsize);
9321 BAM_DPRINTF(("%s: read menu boot-args: %s\n",
9322 fcn, buf));
9324 } else {
9326 * We need to print the kernel, so we just turn the
9327 * first space into a '\0' and print the beginning.
9328 * We don't print anything if it's the default kernel.
9330 old_space = *space;
9331 *space = '\0';
9332 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9333 (void) strlcpy(buf, kernelp->arg, bufsize);
9334 BAM_DPRINTF(("%s: read menu boot-file: %s\n",
9335 fcn, buf));
9337 *space = old_space;
9339 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9340 return (BAM_SUCCESS);
9344 * First, check if we're resetting an entry to the default.
9346 if ((path[0] == '\0') ||
9347 ((optnum == KERNEL_CMD) &&
9348 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9349 if ((entryp == NULL) || (kernelp == NULL)) {
9350 /* No previous entry, it's already the default */
9351 BAM_DPRINTF(("%s: no reset, already has default\n",
9352 fcn));
9353 return (BAM_SUCCESS);
9357 * Check if we can delete the entry. If we're resetting the
9358 * kernel command, and the args is already empty, or if we're
9359 * resetting the args command, and the kernel is already the
9360 * default, we can restore the old default and delete the entry.
9362 if (((optnum == KERNEL_CMD) &&
9363 ((old_args == NULL) || (old_args[0] == '\0'))) ||
9364 ((optnum == ARGS_CMD) &&
9365 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9366 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9367 kernelp = NULL;
9368 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9369 restore_default_entry(mp, BAM_OLD_RC_DEF,
9370 mp->old_rc_default);
9371 mp->old_rc_default = NULL;
9372 rv = BAM_WRITE;
9373 BAM_DPRINTF(("%s: resetting to default\n", fcn));
9374 goto done;
9377 if (optnum == KERNEL_CMD) {
9379 * At this point, we've already checked that old_args
9380 * and entryp are valid pointers. The "+ 2" is for
9381 * a space a the string termination character.
9383 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9384 strlen(old_args) + 2;
9385 new_arg = s_calloc(1, new_str_len);
9386 (void) snprintf(new_arg, new_str_len, "%s %s",
9387 DIRECT_BOOT_KERNEL, old_args);
9388 free(kernelp->arg);
9389 kernelp->arg = new_arg;
9392 * We have changed the kernel line, so we may need
9393 * to update the archive line as well.
9395 set_archive_line(entryp, kernelp);
9396 BAM_DPRINTF(("%s: reset kernel to default, but "
9397 "retained old args: %s\n", fcn, kernelp->arg));
9398 } else {
9400 * We're resetting the boot args to nothing, so
9401 * we only need to copy the kernel. We've already
9402 * checked that the kernel is not the default.
9404 new_arg = s_calloc(1, old_kernel_len + 1);
9405 (void) snprintf(new_arg, old_kernel_len + 1, "%s",
9406 kernelp->arg);
9407 free(kernelp->arg);
9408 kernelp->arg = new_arg;
9409 BAM_DPRINTF(("%s: reset args to default, but retained "
9410 "old kernel: %s\n", fcn, kernelp->arg));
9412 rv = BAM_WRITE;
9413 goto done;
9417 * Expand the kernel file to a full path, if necessary
9419 if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9420 new_path = expand_path(path);
9421 if (new_path == NULL) {
9422 bam_error(_("unable to expand %s to a full file "
9423 "path.\n"), path);
9424 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9425 return (BAM_ERROR);
9427 free_new_path = 1;
9428 } else {
9429 new_path = path;
9430 free_new_path = 0;
9434 * At this point, we know we're setting a new value. First, take care
9435 * of the case where there was no previous entry.
9437 if (entryp == NULL) {
9439 /* Similar to code in update_temp */
9440 fstype = get_fstype("/");
9441 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9442 if (fstype == NULL) {
9443 bam_error(_("cannot determine filesystem type for "
9444 "\"/\".\nCannot generate GRUB menu entry with "
9445 "EEPROM arguments.\n"));
9446 rv = BAM_ERROR;
9447 goto done;
9450 osdev = get_special("/");
9451 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9452 if (osdev == NULL) {
9453 free(fstype);
9454 bam_error(_("cannot determine device special file for "
9455 "\"/\".\nCannot generate GRUB menu entry with "
9456 "EEPROM arguments.\n"));
9457 rv = BAM_ERROR;
9458 goto done;
9461 sign = find_existing_sign("/", osdev, fstype);
9462 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9463 if (sign == NULL) {
9464 free(fstype);
9465 free(osdev);
9466 bam_error(_("cannot determine boot signature for "
9467 "\"/\".\nCannot generate GRUB menu entry with "
9468 "EEPROM arguments.\n"));
9469 rv = BAM_ERROR;
9470 goto done;
9473 free(osdev);
9474 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9475 free(sign);
9476 assert(strchr(signbuf, '(') == NULL &&
9477 strchr(signbuf, ',') == NULL &&
9478 strchr(signbuf, ')') == NULL);
9480 if (optnum == KERNEL_CMD) {
9481 if (strcmp(fstype, "zfs") == 0) {
9482 new_str_len = strlen(new_path) +
9483 strlen(ZFS_BOOT) + 8;
9484 new_arg = s_calloc(1, new_str_len);
9485 (void) snprintf(new_arg, new_str_len, "%s %s",
9486 new_path, ZFS_BOOT);
9487 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9488 new_arg));
9489 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9490 signbuf, new_arg, NULL, NULL, NULL);
9491 free(new_arg);
9492 } else {
9493 BAM_DPRINTF(("%s: new kernel=%s\n", fcn,
9494 new_path));
9495 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9496 signbuf, new_path, NULL, NULL, NULL);
9498 } else {
9499 new_str_len = strlen(path) + 8;
9500 if (strcmp(fstype, "zfs") == 0) {
9501 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9502 new_arg = s_calloc(1, new_str_len);
9503 (void) snprintf(new_arg, new_str_len, "%s %s",
9504 DIRECT_BOOT_KERNEL_ZFS, path);
9505 } else {
9506 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9507 new_arg = s_calloc(1, new_str_len);
9508 (void) snprintf(new_arg, new_str_len, "%s %s",
9509 DIRECT_BOOT_KERNEL, path);
9512 BAM_DPRINTF(("%s: new args=%s\n", fcn, new_arg));
9513 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9514 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9515 free(new_arg);
9517 free(fstype);
9518 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9519 entryNum = BAM_ERROR);
9520 if (entryNum == BAM_ERROR) {
9521 bam_error(_("failed to add boot entry: %s\n"),
9522 BOOTENV_RC_TITLE);
9523 rv = BAM_ERROR;
9524 goto done;
9526 save_default_entry(mp, BAM_OLD_RC_DEF);
9527 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9528 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9529 if (ret == BAM_ERROR) {
9530 bam_error(_("failed to set default to: %d\n"),
9531 entryNum);
9533 rv = BAM_WRITE;
9534 goto done;
9538 * There was already an bootenv entry which we need to edit.
9540 if (optnum == KERNEL_CMD) {
9541 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9542 new_arg = s_calloc(1, new_str_len);
9543 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9544 old_args);
9545 free(kernelp->arg);
9546 kernelp->arg = new_arg;
9549 * If we have changed the kernel line, we may need to update
9550 * the archive line as well.
9552 set_archive_line(entryp, kernelp);
9553 BAM_DPRINTF(("%s: rc line exists, replaced kernel, same "
9554 "args: %s\n", fcn, kernelp->arg));
9555 } else {
9556 new_str_len = old_kernel_len + strlen(path) + 8;
9557 new_arg = s_calloc(1, new_str_len);
9558 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9559 (void) strlcat(new_arg, " ", new_str_len);
9560 (void) strlcat(new_arg, path, new_str_len);
9561 free(kernelp->arg);
9562 kernelp->arg = new_arg;
9563 BAM_DPRINTF(("%s: rc line exists, same kernel, but new "
9564 "args: %s\n", fcn, kernelp->arg));
9566 rv = BAM_WRITE;
9568 done:
9569 if ((rv == BAM_WRITE) && kernelp)
9570 update_line(kernelp);
9571 if (free_new_path)
9572 free(new_path);
9573 if (rv == BAM_WRITE) {
9574 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9575 } else {
9576 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9578 return (rv);
9581 static error_t
9582 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9584 const char *fcn = "get_kernel()";
9585 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, menu_cmds[optnum]));
9586 return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9589 static error_t
9590 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9592 const char *fcn = "set_kernel()";
9593 assert(path != NULL);
9594 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn,
9595 menu_cmds[optnum], path));
9596 return (get_set_kernel(mp, optnum, path, buf, bufsize));
9599 /*ARGSUSED*/
9600 static error_t
9601 set_option(menu_t *mp, char *dummy, char *opt)
9603 int optnum;
9604 int optval;
9605 char *val;
9606 char buf[BUFSIZ] = "";
9607 error_t rv;
9608 const char *fcn = "set_option()";
9610 assert(mp);
9611 assert(opt);
9612 assert(dummy == NULL);
9614 /* opt is set from bam_argv[0] and is always non-NULL */
9615 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, opt));
9617 val = strchr(opt, '=');
9618 if (val != NULL) {
9619 *val = '\0';
9622 if (strcmp(opt, "default") == 0) {
9623 optnum = DEFAULT_CMD;
9624 } else if (strcmp(opt, "timeout") == 0) {
9625 optnum = TIMEOUT_CMD;
9626 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9627 optnum = KERNEL_CMD;
9628 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9629 optnum = ARGS_CMD;
9630 } else {
9631 bam_error(_("invalid option: %s\n"), opt);
9632 return (BAM_ERROR);
9636 * kernel and args are allowed without "=new_value" strings. All
9637 * others cause errors
9639 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9640 bam_error(_("option has no argument: %s\n"), opt);
9641 return (BAM_ERROR);
9642 } else if (val != NULL) {
9643 *val = '=';
9646 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9647 BAM_DPRINTF(("%s: setting %s option to %s\n",
9648 fcn, menu_cmds[optnum], val ? val + 1 : "NULL"));
9650 if (val)
9651 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9652 else
9653 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9654 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9655 (void) printf("%s\n", buf);
9656 } else {
9657 optval = s_strtol(val + 1);
9658 BAM_DPRINTF(("%s: setting %s option to %s\n", fcn,
9659 menu_cmds[optnum], val + 1));
9660 rv = set_global(mp, menu_cmds[optnum], optval);
9663 if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9664 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
9665 } else {
9666 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
9669 return (rv);
9673 * The quiet argument suppresses messages. This is used
9674 * when invoked in the context of other commands (e.g. list_entry)
9676 static error_t
9677 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9679 line_t *lp;
9680 char *arg;
9681 int done, ret = BAM_SUCCESS;
9683 assert(mp);
9684 assert(menu_path);
9685 assert(globalcmd);
9687 if (mp->start == NULL) {
9688 if (!quiet)
9689 bam_error(_("menu file not found: %s\n"), menu_path);
9690 return (BAM_ERROR);
9693 done = 0;
9694 for (lp = mp->start; lp; lp = lp->next) {
9695 if (lp->flags != BAM_GLOBAL)
9696 continue;
9698 if (lp->cmd == NULL) {
9699 if (!quiet)
9700 bam_error(_("no command at line %d\n"),
9701 lp->lineNum);
9702 continue;
9705 if (strcmp(globalcmd, lp->cmd) != 0)
9706 continue;
9708 /* Found global. Check for duplicates */
9709 if (done && !quiet) {
9710 bam_error(_("duplicate command %s at line %d of "
9711 "%sboot/grub/menu.lst\n"), globalcmd,
9712 lp->lineNum, bam_root);
9713 ret = BAM_ERROR;
9716 arg = lp->arg ? lp->arg : "";
9717 bam_print(_("%s %s\n"), globalcmd, arg);
9718 done = 1;
9721 if (!done && bam_verbose)
9722 bam_print(_("no %s entry found\n"), globalcmd);
9724 return (ret);
9727 static error_t
9728 menu_write(char *root, menu_t *mp)
9730 const char *fcn = "menu_write()";
9732 BAM_DPRINTF(("%s: entered menu_write() for root: <%s>\n", fcn, root));
9733 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9736 void
9737 line_free(line_t *lp)
9739 if (lp == NULL)
9740 return;
9742 if (lp->cmd != NULL)
9743 free(lp->cmd);
9744 if (lp->sep)
9745 free(lp->sep);
9746 if (lp->arg)
9747 free(lp->arg);
9748 if (lp->line)
9749 free(lp->line);
9750 free(lp);
9753 static void
9754 linelist_free(line_t *start)
9756 line_t *lp;
9758 while (start) {
9759 lp = start;
9760 start = start->next;
9761 line_free(lp);
9765 static void
9766 filelist_free(filelist_t *flistp)
9768 linelist_free(flistp->head);
9769 flistp->head = NULL;
9770 flistp->tail = NULL;
9773 static void
9774 menu_free(menu_t *mp)
9776 entry_t *ent, *tmp;
9777 assert(mp);
9779 if (mp->start)
9780 linelist_free(mp->start);
9781 ent = mp->entries;
9782 while (ent) {
9783 tmp = ent;
9784 ent = tmp->next;
9785 free(tmp);
9788 free(mp);
9792 * Utility routines
9797 * Returns 0 on success
9798 * Any other value indicates an error
9800 static int
9801 exec_cmd(char *cmdline, filelist_t *flistp)
9803 char buf[BUFSIZ];
9804 int ret;
9805 FILE *ptr;
9806 sigset_t set;
9807 void (*disp)(int);
9810 * For security
9811 * - only absolute paths are allowed
9812 * - set IFS to space and tab
9814 if (*cmdline != '/') {
9815 bam_error(_("path is not absolute: %s\n"), cmdline);
9816 return (-1);
9818 (void) putenv("IFS= \t");
9821 * We may have been exec'ed with SIGCHLD blocked
9822 * unblock it here
9824 (void) sigemptyset(&set);
9825 (void) sigaddset(&set, SIGCHLD);
9826 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9827 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno));
9828 return (-1);
9832 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9834 disp = sigset(SIGCHLD, SIG_DFL);
9835 if (disp == SIG_ERR) {
9836 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
9837 strerror(errno));
9838 return (-1);
9840 if (disp == SIG_HOLD) {
9841 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
9842 cmdline);
9843 return (-1);
9846 ptr = popen(cmdline, "r");
9847 if (ptr == NULL) {
9848 bam_error(_("popen failed: %s: %s\n"), cmdline,
9849 strerror(errno));
9850 return (-1);
9854 * If we simply do a pclose() following a popen(), pclose()
9855 * will close the reader end of the pipe immediately even
9856 * if the child process has not started/exited. pclose()
9857 * does wait for cmd to terminate before returning though.
9858 * When the executed command writes its output to the pipe
9859 * there is no reader process and the command dies with
9860 * SIGPIPE. To avoid this we read repeatedly until read
9861 * terminates with EOF. This indicates that the command
9862 * (writer) has closed the pipe and we can safely do a
9863 * pclose().
9865 * Since pclose() does wait for the command to exit,
9866 * we can safely reap the exit status of the command
9867 * from the value returned by pclose()
9869 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9870 if (flistp == NULL) {
9871 /* s_fgets strips newlines, so insert them at the end */
9872 bam_print(_("%s\n"), buf);
9873 } else {
9874 append_to_flist(flistp, buf);
9878 ret = pclose(ptr);
9879 if (ret == -1) {
9880 bam_error(_("pclose failed: %s: %s\n"), cmdline,
9881 strerror(errno));
9882 return (-1);
9885 if (WIFEXITED(ret)) {
9886 return (WEXITSTATUS(ret));
9887 } else {
9888 bam_error(_("command terminated abnormally: %s: %d\n"),
9889 cmdline, ret);
9890 return (-1);
9895 * Since this function returns -1 on error
9896 * it cannot be used to convert -1. However,
9897 * that is sufficient for what we need.
9899 static long
9900 s_strtol(char *str)
9902 long l;
9903 char *res = NULL;
9905 if (str == NULL) {
9906 return (-1);
9909 errno = 0;
9910 l = strtol(str, &res, 10);
9911 if (errno || *res != '\0') {
9912 return (-1);
9915 return (l);
9919 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9921 static int
9922 s_fputs(char *str, FILE *fp)
9924 char linebuf[BAM_MAXLINE];
9926 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9927 return (fputs(linebuf, fp));
9931 * Wrapper around fgets, that strips newlines returned by fgets
9933 char *
9934 s_fgets(char *buf, int buflen, FILE *fp)
9936 int n;
9938 buf = fgets(buf, buflen, fp);
9939 if (buf) {
9940 n = strlen(buf);
9941 if (n == buflen - 1 && buf[n-1] != '\n')
9942 bam_error(_("the following line is too long "
9943 "(> %d chars)\n\t%s\n"), buflen - 1, buf);
9944 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9947 return (buf);
9950 void *
9951 s_calloc(size_t nelem, size_t sz)
9953 void *ptr;
9955 ptr = calloc(nelem, sz);
9956 if (ptr == NULL) {
9957 bam_error(_("could not allocate memory: size = %u\n"),
9958 nelem*sz);
9959 bam_exit(1);
9961 return (ptr);
9964 void *
9965 s_realloc(void *ptr, size_t sz)
9967 ptr = realloc(ptr, sz);
9968 if (ptr == NULL) {
9969 bam_error(_("could not allocate memory: size = %u\n"), sz);
9970 bam_exit(1);
9972 return (ptr);
9975 char *
9976 s_strdup(char *str)
9978 char *ptr;
9980 if (str == NULL)
9981 return (NULL);
9983 ptr = strdup(str);
9984 if (ptr == NULL) {
9985 bam_error(_("could not allocate memory: size = %u\n"),
9986 strlen(str) + 1);
9987 bam_exit(1);
9989 return (ptr);
9993 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9994 * Returns 0 otherwise
9996 static int
9997 is_amd64(void)
9999 static int amd64 = -1;
10000 char isabuf[257]; /* from sysinfo(2) manpage */
10002 if (amd64 != -1)
10003 return (amd64);
10005 if (bam_alt_platform) {
10006 if (strcmp(bam_platform, "i86pc") == 0) {
10007 amd64 = 1; /* diskless server */
10009 } else {
10010 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
10011 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
10012 amd64 = 1;
10013 } else if (strstr(isabuf, "i386") == NULL) {
10014 amd64 = 1; /* diskless server */
10017 if (amd64 == -1)
10018 amd64 = 0;
10020 return (amd64);
10023 static char *
10024 get_machine(void)
10026 static int cached = -1;
10027 static char mbuf[257]; /* from sysinfo(2) manpage */
10029 if (cached == 0)
10030 return (mbuf);
10032 if (bam_alt_platform) {
10033 return (bam_platform);
10034 } else {
10035 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
10036 cached = 1;
10039 if (cached == -1) {
10040 mbuf[0] = '\0';
10041 cached = 0;
10044 return (mbuf);
10048 is_sparc(void)
10050 static int issparc = -1;
10051 char mbuf[257]; /* from sysinfo(2) manpage */
10053 if (issparc != -1)
10054 return (issparc);
10056 if (bam_alt_platform) {
10057 if (strncmp(bam_platform, "sun4", 4) == 0) {
10058 issparc = 1;
10060 } else {
10061 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
10062 strcmp(mbuf, "sparc") == 0) {
10063 issparc = 1;
10066 if (issparc == -1)
10067 issparc = 0;
10069 return (issparc);
10072 static void
10073 append_to_flist(filelist_t *flistp, char *s)
10075 line_t *lp;
10077 lp = s_calloc(1, sizeof (line_t));
10078 lp->line = s_strdup(s);
10079 if (flistp->head == NULL)
10080 flistp->head = lp;
10081 else
10082 flistp->tail->next = lp;
10083 flistp->tail = lp;
10086 #if !defined(_OBP)
10088 UCODE_VENDORS;
10090 /*ARGSUSED*/
10091 static void
10092 ucode_install(char *root)
10094 int i;
10096 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
10097 int cmd_len = PATH_MAX + 256;
10098 char cmd[PATH_MAX + 256];
10099 char file[PATH_MAX];
10100 char timestamp[PATH_MAX];
10101 struct stat fstatus, tstatus;
10102 struct utimbuf u_times;
10104 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
10105 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
10106 ucode_vendors[i].extstr);
10108 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
10109 continue;
10111 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
10113 if (stat(timestamp, &tstatus) == 0 &&
10114 fstatus.st_mtime <= tstatus.st_mtime)
10115 continue;
10117 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
10118 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
10119 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
10120 if (system(cmd) != 0)
10121 return;
10123 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
10124 return;
10126 u_times.actime = fstatus.st_atime;
10127 u_times.modtime = fstatus.st_mtime;
10128 (void) utime(timestamp, &u_times);
10131 #endif