1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
24 */
25
26 /*
27 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
28 */
29
30 /*
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)
35 */
36
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 #ifdef i386
72 #include <libfdisk.h>
73 #endif
74
75 #if !defined(_OPB)
76 #include <sys/ucode.h>
77 #endif
78
79 #include <pwd.h>
80 #include <grp.h>
81 #include <device_info.h>
82 #include <sys/vtoc.h>
83 #include <sys/efi_partition.h>
84 #include <regex.h>
85 #include <locale.h>
86
87 #include "message.h"
88 #include "bootadm.h"
89
90 #ifndef TEXT_DOMAIN
91 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
92 #endif /* TEXT_DOMAIN */
93
94 /* Type definitions */
95
96 /* Primary subcmds */
97 typedef enum {
98 BAM_MENU = 3,
99 BAM_ARCHIVE
100 } subcmd_t;
101
102 typedef enum {
103 OPT_ABSENT = 0, /* No option */
104 OPT_REQ, /* option required */
105 OPT_OPTIONAL /* option may or may not be present */
106 } option_t;
107
108 typedef struct {
109 char *subcmd;
110 option_t option;
111 error_t (*handler)();
112 int unpriv; /* is this an unprivileged command */
113 } subcmd_defn_t;
114
115 #define LINE_INIT 0 /* lineNum initial value */
116 #define ENTRY_INIT -1 /* entryNum initial value */
117 #define ALL_ENTRIES -2 /* selects all boot entries */
118
119 #define GRUB_DIR "/boot/grub"
120 #define GRUB_STAGE2 GRUB_DIR "/stage2"
121 #define GRUB_MENU "/boot/grub/menu.lst"
122 #define MENU_TMP "/boot/grub/menu.lst.tmp"
123 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
124 #define RAMDISK_SPECIAL "/ramdisk"
125 #define STUBBOOT "/stubboot"
126 #define MULTIBOOT "/platform/i86pc/multiboot"
127 #define GRUBSIGN_DIR "/boot/grub/bootsign"
128 #define GRUBSIGN_BACKUP "/etc/bootsign"
129 #define GRUBSIGN_UFS_PREFIX "rootfs"
130 #define GRUBSIGN_ZFS_PREFIX "pool_"
131 #define GRUBSIGN_LU_PREFIX "BE_"
132 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
133 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
134
135 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
136
137 /* lock related */
138 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
139 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
140
141 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
142 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
143 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
144 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
145
146 #define GRUB_slice "/etc/lu/GRUB_slice"
147 #define GRUB_root "/etc/lu/GRUB_root"
148 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
149 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
150 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
151 #define LULIB "/usr/lib/lu/lulib"
152 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
153 #define CKSUM "/usr/bin/cksum"
154 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
155 #define BOOTADM "/sbin/bootadm"
156
157 #define INSTALLGRUB "/sbin/installgrub"
158 #define STAGE1 "/boot/grub/stage1"
159 #define STAGE2 "/boot/grub/stage2"
160
161 typedef enum zfs_mnted {
162 ZFS_MNT_ERROR = -1,
163 LEGACY_MOUNTED = 1,
164 LEGACY_ALREADY,
165 ZFS_MOUNTED,
166 ZFS_ALREADY
167 } zfs_mnted_t;
168
169 /*
170 * Default file attributes
171 */
172 #define DEFAULT_DEV_MODE 0644 /* default permissions */
173 #define DEFAULT_DEV_UID 0 /* user root */
174 #define DEFAULT_DEV_GID 3 /* group sys */
175
176 /*
177 * Menu related
178 * menu_cmd_t and menu_cmds must be kept in sync
179 */
180 char *menu_cmds[] = {
181 "default", /* DEFAULT_CMD */
182 "timeout", /* TIMEOUT_CMD */
183 "title", /* TITLE_CMD */
184 "root", /* ROOT_CMD */
185 "kernel", /* KERNEL_CMD */
186 "kernel$", /* KERNEL_DOLLAR_CMD */
187 "module", /* MODULE_CMD */
188 "module$", /* MODULE_DOLLAR_CMD */
189 " ", /* SEP_CMD */
190 "#", /* COMMENT_CMD */
191 "chainloader", /* CHAINLOADER_CMD */
192 "args", /* ARGS_CMD */
193 "findroot", /* FINDROOT_CMD */
194 "bootfs", /* BOOTFS_CMD */
195 NULL
196 };
197
198 #define OPT_ENTRY_NUM "entry"
199
200 /*
201 * exec_cmd related
202 */
203 typedef struct {
204 line_t *head;
205 line_t *tail;
206 } filelist_t;
207
208 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
209 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
210
211 #define FILE_STAT "boot/solaris/filestat.ramdisk"
212 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
213 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
214 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
215
216 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
217
218 /* Globals */
219 int bam_verbose;
220 int bam_force;
221 int bam_debug;
222 static char *prog;
223 static subcmd_t bam_cmd;
224 static char *bam_root;
225 static int bam_rootlen;
226 static int bam_root_readonly;
227 static int bam_alt_root;
228 static int bam_extend = 0;
229 static int bam_purge = 0;
230 static char *bam_subcmd;
231 static char *bam_opt;
232 static char **bam_argv;
233 static int bam_argc;
234 static int bam_check;
235 static int bam_saved_check;
236 static int bam_smf_check;
237 static int bam_lock_fd = -1;
238 static int bam_zfs;
239 static char rootbuf[PATH_MAX] = "/";
240 static int bam_update_all;
241 static int bam_alt_platform;
242 static char *bam_platform;
243 static char *bam_home_env = NULL;
244
245 /* function prototypes */
246 static void parse_args_internal(int, char *[]);
247 static void parse_args(int, char *argv[]);
248 static error_t bam_menu(char *, char *, int, char *[]);
249 static error_t bam_archive(char *, char *);
250
251 static void bam_lock(void);
252 static void bam_unlock(void);
253
254 static int exec_cmd(char *, filelist_t *);
255 static error_t read_globals(menu_t *, char *, char *, int);
256 static int menu_on_bootdisk(char *os_root, char *menu_root);
257 static menu_t *menu_read(char *);
258 static error_t menu_write(char *, menu_t *);
259 static void linelist_free(line_t *);
260 static void menu_free(menu_t *);
261 static void filelist_free(filelist_t *);
262 static error_t list2file(char *, char *, char *, line_t *);
263 static error_t list_entry(menu_t *, char *, char *);
264 static error_t list_setting(menu_t *, char *, char *);
265 static error_t delete_all_entries(menu_t *, char *, char *);
266 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
267 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
268
269 static error_t update_archive(char *, char *);
270 static error_t list_archive(char *, char *);
271 static error_t update_all(char *, char *);
272 static error_t read_list(char *, filelist_t *);
273 static error_t set_option(menu_t *, char *, char *);
274 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
275 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
276 static char *expand_path(const char *);
277
278 static long s_strtol(char *);
279 static int s_fputs(char *, FILE *);
280
281 static int is_zfs(char *root);
282 static int is_ufs(char *root);
283 static int is_pcfs(char *root);
284 static int is_amd64(void);
285 static char *get_machine(void);
286 static void append_to_flist(filelist_t *, char *);
287 static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted);
288 static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt);
289 static int ufs_add_to_sign_list(char *sign);
290 static error_t synchronize_BE_menu(void);
291
292 #if !defined(_OPB)
293 static void ucode_install();
294 #endif
295
296 /* Menu related sub commands */
297 static subcmd_defn_t menu_subcmds[] = {
298 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
299 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
300 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
301 "update_entry", OPT_REQ, update_entry, 0, /* menu */
302 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
303 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
304 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
305 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
306 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
307 NULL, 0, NULL, 0 /* must be last */
308 };
309
310 /* Archive related sub commands */
311 static subcmd_defn_t arch_subcmds[] = {
312 "update", OPT_ABSENT, update_archive, 0, /* PUB */
313 "update_all", OPT_ABSENT, update_all, 0, /* PVT */
314 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */
315 NULL, 0, NULL, 0 /* must be last */
316 };
317
318 enum dircache_copy_opt {
319 FILE32 = 0,
320 FILE64,
321 CACHEDIR_NUM
322 };
323
324 /*
325 * Directory specific flags:
326 * NEED_UPDATE : the specified archive needs to be updated
327 * NO_MULTI : don't extend the specified archive, but recreate it
328 */
329 #define NEED_UPDATE 0x00000001
330 #define NO_MULTI 0x00000002
331
332 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
333 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
334 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
335
336 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
337 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
338 #define get_count(id) (walk_arg.dirinfo[id].count)
339 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
340 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
341
342 /*
343 * dirinfo_t (specific cache directory information):
344 * cdir_path: path to the archive cache directory
345 * update_path: path to the update directory (contains the files that will be
346 * used to extend the archive)
347 * has_dir: the specified cache directory is active
348 * count: the number of files to update
349 * flags: directory specific flags
350 */
351 typedef struct _dirinfo {
352 char cdir_path[PATH_MAX];
353 char update_path[PATH_MAX];
354 int has_dir;
355 int count;
356 int flags;
357 } dirinfo_t;
358
359 /*
360 * Update flags:
361 * NEED_CACHE_DIR : cache directory is missing and needs to be created
362 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
363 * UPDATE_ERROR : an error occourred while traversing the list of files
364 * RDONLY_FSCHK : the target filesystem is read-only
365 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
366 */
367 #define NEED_CACHE_DIR 0x00000001
368 #define IS_SPARC_TARGET 0x00000002
369 #define UPDATE_ERROR 0x00000004
370 #define RDONLY_FSCHK 0x00000008
371 #define INVALIDATE_CACHE 0x00000010
372
373 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
374 #define set_flag(flag) (walk_arg.update_flags |= flag)
375 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
376
377 /*
378 * struct walk_arg :
379 * update_flags: flags related to the current updating process
380 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
381 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
382 */
383 static struct {
384 int update_flags;
385 nvlist_t *new_nvlp;
386 nvlist_t *old_nvlp;
387 FILE *sparcfile;
388 dirinfo_t dirinfo[CACHEDIR_NUM];
389 } walk_arg;
390
391 struct safefile {
392 char *name;
393 struct safefile *next;
394 };
395
396 static struct safefile *safefiles = NULL;
397
398 /*
399 * svc:/system/filesystem/usr:default service checks for this file and
400 * does a boot archive update and then reboot the system.
401 */
402 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
403
404 /*
405 * svc:/system/boot-archive-update:default checks for this file and
406 * updates the boot archive.
407 */
408 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
409
410 /* Thanks growisofs */
411 #define CD_BLOCK ((off64_t)2048)
412 #define VOLDESC_OFF 16
413 #define DVD_BLOCK (32*1024)
414 #define MAX_IVDs 16
415
416 struct iso_pdesc {
417 unsigned char type [1];
418 unsigned char id [5];
419 unsigned char void1 [80-5-1];
420 unsigned char volume_space_size [8];
421 unsigned char void2 [2048-80-8];
422 };
423
424 /*
425 * COUNT_MAX: maximum number of changed files to justify a multisession update
426 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
427 * update
428 */
429 #define COUNT_MAX 50
430 #define BA_SIZE_MAX (50 * 1024 * 1024)
431
432 #define bam_nowrite() (bam_check || bam_smf_check)
433
434 static int sync_menu = 1; /* whether we need to sync the BE menus */
435
436 static void
437 usage(void)
438 {
439 (void) fprintf(stderr, "USAGE:\n");
440
441 /* archive usage */
442 (void) fprintf(stderr,
443 "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog);
444 (void) fprintf(stderr,
445 "\t%s list-archive [-R altroot [-p platform>]]\n", prog);
446 #if !defined(_OPB)
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
451 }
452
453 /*
454 * Best effort attempt to restore the $HOME value.
455 */
456 static void
457 restore_env()
458 {
459 char home_env[PATH_MAX];
460
461 if (bam_home_env) {
462 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
463 bam_home_env);
464 (void) putenv(home_env);
465 }
466 }
467
468
469 #define SLEEP_TIME 5
470 #define MAX_TRIES 4
471
472 /*
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.
477 */
478 static void
479 sanitize_env()
480 {
481 int stry = 0;
482
483 /* don't depend on caller umask */
484 (void) umask(0022);
485
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;
491 }
492 }
493
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);
502 }
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");
509 }
510 }
511 }
512
513 int
514 main(int argc, char *argv[])
515 {
516 error_t ret;
517
518 (void) setlocale(LC_ALL, "");
519 (void) textdomain(TEXT_DOMAIN);
520
521 if ((prog = strrchr(argv[0], '/')) == NULL) {
522 prog = argv[0];
523 } else {
524 prog++;
525 }
526
527 INJECT_ERROR1("ASSERT_ON", assert(0))
528
529 sanitize_env();
530
531 parse_args(argc, argv);
532
533 switch (bam_cmd) {
534 case BAM_MENU:
535 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
536 break;
537 case BAM_ARCHIVE:
538 ret = bam_archive(bam_subcmd, bam_opt);
539 break;
540 default:
541 usage();
542 bam_exit(1);
543 }
544
545 if (ret != BAM_SUCCESS)
546 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
547
548 bam_unlock();
549 return (0);
550 }
551
552 /*
553 * Equivalence of public and internal commands:
554 * update-archive -- -a update
555 * list-archive -- -a list
556 * set-menu -- -m set_option
557 * list-menu -- -m list_entry
558 * update-menu -- -m update_entry
559 */
560 static struct cmd_map {
561 char *bam_cmdname;
562 int bam_cmd;
563 char *bam_subcmd;
564 } cmd_map[] = {
565 { "update-archive", BAM_ARCHIVE, "update"},
566 { "list-archive", BAM_ARCHIVE, "list"},
567 { "set-menu", BAM_MENU, "set_option"},
568 { "list-menu", BAM_MENU, "list_entry"},
569 { "update-menu", BAM_MENU, "update_entry"},
570 { NULL, 0, NULL}
571 };
572
573 /*
574 * Commands syntax published in bootadm(1M) are parsed here
575 */
576 static void
577 parse_args(int argc, char *argv[])
578 {
579 struct cmd_map *cmp = cmd_map;
580
581 /* command conforming to the final spec */
582 if (argc > 1 && argv[1][0] != '-') {
583 /*
584 * Map commands to internal table.
585 */
586 while (cmp->bam_cmdname) {
587 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
588 bam_cmd = cmp->bam_cmd;
589 bam_subcmd = cmp->bam_subcmd;
590 break;
591 }
592 cmp++;
593 }
594 if (cmp->bam_cmdname == NULL) {
595 usage();
596 bam_exit(1);
597 }
598 argc--;
599 argv++;
600 }
601
602 parse_args_internal(argc, argv);
603 }
604
605 /*
606 * A combination of public and private commands are parsed here.
607 * The internal syntax and the corresponding functionality are:
608 * -a update -- update-archive
609 * -a list -- list-archive
610 * -a update-all -- (reboot to sync all mnted OS archive)
611 * -m update_entry -- update-menu
612 * -m list_entry -- list-menu
613 * -m update_temp -- (reboot -- [boot-args])
614 * -m delete_all_entries -- (called from install)
615 * -m enable_hypervisor [args] -- cvt_to_hyper
616 * -m disable_hypervisor -- cvt_to_metal
617 * -m list_setting [entry] [value] -- list_setting
618 *
619 * A set of private flags is there too:
620 * -F -- purge the cache directories and rebuild them
621 * -e -- use the (faster) archive update approach (used by
622 * reboot)
623 */
624 static void
625 parse_args_internal(int argc, char *argv[])
626 {
627 int c, error;
628 extern char *optarg;
629 extern int optind, opterr;
630
631 /* Suppress error message from getopt */
632 opterr = 0;
633
634 error = 0;
635 while ((c = getopt(argc, argv, "a:d:fm:no:veFCR:p:XZ")) != -1) {
636 switch (c) {
637 case 'a':
638 if (bam_cmd) {
639 error = 1;
640 bam_error(MULT_CMDS, c);
641 }
642 bam_cmd = BAM_ARCHIVE;
643 bam_subcmd = optarg;
644 break;
645 case 'd':
646 if (bam_debug) {
647 error = 1;
648 bam_error(DUP_OPT, c);
649 }
650 bam_debug = s_strtol(optarg);
651 break;
652 case 'f':
653 bam_force = 1;
654 break;
655 case 'F':
656 bam_purge = 1;
657 break;
658 case 'm':
659 if (bam_cmd) {
660 error = 1;
661 bam_error(MULT_CMDS, c);
662 }
663 bam_cmd = BAM_MENU;
664 bam_subcmd = optarg;
665 break;
666 case 'n':
667 bam_check = 1;
668 /*
669 * We save the original value of bam_check. The new
670 * approach in case of a read-only filesystem is to
671 * behave as a check, so we need a way to restore the
672 * original value after the evaluation of the read-only
673 * filesystem has been done.
674 * Even if we don't allow at the moment a check with
675 * update_all, this approach is more robust than
676 * simply resetting bam_check to zero.
677 */
678 bam_saved_check = 1;
679 break;
680 case 'o':
681 if (bam_opt) {
682 error = 1;
683 bam_error(DUP_OPT, c);
684 }
685 bam_opt = optarg;
686 break;
687 case 'v':
688 bam_verbose = 1;
689 break;
690 case 'C':
691 bam_smf_check = 1;
692 break;
693 case 'R':
694 if (bam_root) {
695 error = 1;
696 bam_error(DUP_OPT, c);
697 break;
698 } else if (realpath(optarg, rootbuf) == NULL) {
699 error = 1;
700 bam_error(CANT_RESOLVE, optarg,
701 strerror(errno));
702 break;
703 }
704 bam_alt_root = 1;
705 bam_root = rootbuf;
706 bam_rootlen = strlen(rootbuf);
707 break;
708 case 'p':
709 bam_alt_platform = 1;
710 bam_platform = optarg;
711 if ((strcmp(bam_platform, "i86pc") != 0) &&
712 (strcmp(bam_platform, "sun4u") != 0) &&
713 (strcmp(bam_platform, "sun4v") != 0)) {
714 error = 1;
715 bam_error(INVALID_PLAT, bam_platform);
716 }
717 break;
718 case 'X':
719 bam_is_hv = BAM_HV_PRESENT;
720 break;
721 case 'Z':
722 bam_zfs = 1;
723 break;
724 case 'e':
725 bam_extend = 1;
726 break;
727 case '?':
728 error = 1;
729 bam_error(BAD_OPT, optopt);
730 break;
731 default :
732 error = 1;
733 bam_error(BAD_OPT, c);
734 break;
735 }
736 }
737
738 /*
739 * An alternate platform requires an alternate root
740 */
741 if (bam_alt_platform && bam_alt_root == 0) {
742 usage();
743 bam_exit(0);
744 }
745
746 /*
747 * A command option must be specfied
748 */
749 if (!bam_cmd) {
750 if (bam_opt && strcmp(bam_opt, "all") == 0) {
751 usage();
752 bam_exit(0);
753 }
754 bam_error(NEED_CMD);
755 error = 1;
756 }
757
758 if (error) {
759 usage();
760 bam_exit(1);
761 }
762
763 if (optind > argc) {
764 bam_error(INT_ERROR, "parse_args");
765 bam_exit(1);
766 } else if (optind < argc) {
767 bam_argv = &argv[optind];
768 bam_argc = argc - optind;
769 }
770
771 /*
772 * -n implies verbose mode
773 */
774 if (bam_check)
775 bam_verbose = 1;
776 }
777
778 static error_t
779 check_subcmd_and_options(
780 char *subcmd,
781 char *opt,
782 subcmd_defn_t *table,
783 error_t (**fp)())
784 {
785 int i;
786
787 if (subcmd == NULL) {
788 bam_error(NEED_SUBCMD);
789 return (BAM_ERROR);
790 }
791
792 if (strcmp(subcmd, "set_option") == 0) {
793 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
794 bam_error(MISSING_ARG);
795 usage();
796 return (BAM_ERROR);
797 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
798 bam_error(TRAILING_ARGS);
799 usage();
800 return (BAM_ERROR);
801 }
802 } else if (strcmp(subcmd, "update_all") == 0) {
803 /*
804 * The only option we accept for the "update_all"
805 * subcmd is "fastboot".
806 */
807 if (bam_argc > 1 || (bam_argc == 1 &&
808 strcmp(bam_argv[0], "fastboot") != 0)) {
809 bam_error(TRAILING_ARGS);
810 usage();
811 return (BAM_ERROR);
812 }
813 if (bam_argc == 1)
814 sync_menu = 0;
815 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
816 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
817 /*
818 * Of the remaining subcommands, only "enable_hypervisor" and
819 * "list_setting" take trailing arguments.
820 */
821 bam_error(TRAILING_ARGS);
822 usage();
823 return (BAM_ERROR);
824 }
825
826 if (bam_root == NULL) {
827 bam_root = rootbuf;
828 bam_rootlen = 1;
829 }
830
831 /* verify that subcmd is valid */
832 for (i = 0; table[i].subcmd != NULL; i++) {
833 if (strcmp(table[i].subcmd, subcmd) == 0)
834 break;
835 }
836
837 if (table[i].subcmd == NULL) {
838 bam_error(INVALID_SUBCMD, subcmd);
839 return (BAM_ERROR);
840 }
841
842 if (table[i].unpriv == 0 && geteuid() != 0) {
843 bam_error(MUST_BE_ROOT);
844 return (BAM_ERROR);
845 }
846
847 /*
848 * Currently only privileged commands need a lock
849 */
850 if (table[i].unpriv == 0)
851 bam_lock();
852
853 /* subcmd verifies that opt is appropriate */
854 if (table[i].option != OPT_OPTIONAL) {
855 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
856 if (opt)
857 bam_error(NO_OPT_REQ, subcmd);
858 else
859 bam_error(MISS_OPT, subcmd);
860 return (BAM_ERROR);
861 }
862 }
863
864 *fp = table[i].handler;
865
866 return (BAM_SUCCESS);
867 }
868
869 /*
870 * NOTE: A single "/" is also considered a trailing slash and will
871 * be deleted.
872 */
873 static void
874 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
875 {
876 size_t dstlen;
877
878 assert(src);
879 assert(dst);
880
881 (void) strlcpy(dst, src, dstsize);
882
883 dstlen = strlen(dst);
884 if (dst[dstlen - 1] == '/') {
885 dst[dstlen - 1] = '\0';
886 }
887 }
888
889 static int
890 is_safe_exec(char *path)
891 {
892 struct stat sb;
893
894 if (lstat(path, &sb) != 0) {
895 bam_error(STAT_FAIL, path, strerror(errno));
896 return (BAM_ERROR);
897 }
898
899 if (!S_ISREG(sb.st_mode)) {
900 bam_error(PATH_EXEC_LINK, path);
901 return (BAM_ERROR);
902 }
903
904 if (sb.st_uid != getuid()) {
905 bam_error(PATH_EXEC_OWNER, path, getuid());
906 return (BAM_ERROR);
907 }
908
909 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
910 bam_error(PATH_EXEC_PERMS, path);
911 return (BAM_ERROR);
912 }
913
914 return (BAM_SUCCESS);
915 }
916
917 static error_t
918 list_setting(menu_t *mp, char *which, char *setting)
919 {
920 line_t *lp;
921 entry_t *ent;
922
923 char *p = which;
924 int entry;
925
926 int found;
927
928 assert(which);
929 assert(setting);
930
931 if (*which != NULL) {
932 /*
933 * If "which" is not a number, assume it's a setting we want
934 * to look for and so set up the routine to look for "which"
935 * in the default entry.
936 */
937 while (*p != NULL)
938 if (!(isdigit((int)*p++))) {
939 setting = which;
940 which = mp->curdefault->arg;
941 break;
942 }
943 } else {
944 which = mp->curdefault->arg;
945 }
946
947 entry = atoi(which);
948
949 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
950 ent = ent->next)
951 ;
952
953 if (!ent) {
954 bam_error(NO_MATCH_ENTRY);
955 return (BAM_ERROR);
956 }
957
958 found = (*setting == NULL);
959
960 for (lp = ent->start; lp != NULL; lp = lp->next) {
961 if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
962 bam_print(PRINT, lp->line);
963 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
964 bam_print(PRINT, lp->arg);
965 found = 1;
966 }
967
968 if (lp == ent->end)
969 break;
970 }
971
972 if (!found) {
973 bam_error(NO_MATCH_ENTRY);
974 return (BAM_ERROR);
975 }
976
977 return (BAM_SUCCESS);
978 }
979
980 static error_t
981 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
982 {
983 error_t ret;
984 char menu_path[PATH_MAX];
985 char clean_menu_root[PATH_MAX];
986 char path[PATH_MAX];
987 menu_t *menu;
988 char menu_root[PATH_MAX];
989 struct stat sb;
990 error_t (*f)(menu_t *mp, char *menu_path, char *opt);
991 char *special;
992 char *pool = NULL;
993 zfs_mnted_t zmnted;
994 char *zmntpt;
995 char *osdev;
996 char *osroot;
997 const char *fcn = "bam_menu()";
998
999 /*
1000 * Menu sub-command only applies to GRUB (i.e. x86)
1001 */
1002 if (!is_grub(bam_alt_root ? bam_root : "/")) {
1003 bam_error(NOT_GRUB_BOOT);
1004 return (BAM_ERROR);
1005 }
1006
1007 /*
1008 * Check arguments
1009 */
1010 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1011 if (ret == BAM_ERROR) {
1012 return (BAM_ERROR);
1013 }
1014
1015 assert(bam_root);
1016
1017 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1018 osdev = osroot = NULL;
1019
1020 if (strcmp(subcmd, "update_entry") == 0) {
1021 assert(opt);
1022
1023 osdev = strtok(opt, ",");
1024 assert(osdev);
1025 osroot = strtok(NULL, ",");
1026 if (osroot) {
1027 /* fixup bam_root so that it points at osroot */
1028 if (realpath(osroot, rootbuf) == NULL) {
1029 bam_error(CANT_RESOLVE, osroot,
1030 strerror(errno));
1031 return (BAM_ERROR);
1032 }
1033 bam_alt_root = 1;
1034 bam_root = rootbuf;
1035 bam_rootlen = strlen(rootbuf);
1036 }
1037 }
1038
1039 /*
1040 * We support menu on PCFS (under certain conditions), but
1041 * not the OS root
1042 */
1043 if (is_pcfs(bam_root)) {
1044 bam_error(PCFS_ROOT_NOTSUP, bam_root);
1045 return (BAM_ERROR);
1046 }
1047
1048 if (stat(menu_root, &sb) == -1) {
1049 bam_error(CANNOT_LOCATE_GRUB_MENU);
1050 return (BAM_ERROR);
1051 }
1052
1053 BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root));
1054
1055 /*
1056 * We no longer use the GRUB slice file. If it exists, then
1057 * the user is doing something that is unsupported (such as
1058 * standard upgrading an old Live Upgrade BE). If that
1059 * happens, mimic existing behavior i.e. pretend that it is
1060 * not a BE. Emit a warning though.
1061 */
1062 if (bam_alt_root) {
1063 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1064 GRUB_slice);
1065 } else {
1066 (void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1067 }
1068
1069 if (bam_verbose && stat(path, &sb) == 0)
1070 bam_error(GRUB_SLICE_FILE_EXISTS, path);
1071
1072 if (is_zfs(menu_root)) {
1073 assert(strcmp(menu_root, bam_root) == 0);
1074 special = get_special(menu_root);
1075 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1076 if (special == NULL) {
1077 bam_error(CANT_FIND_SPECIAL, menu_root);
1078 return (BAM_ERROR);
1079 }
1080 pool = strtok(special, "/");
1081 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1082 if (pool == NULL) {
1083 free(special);
1084 bam_error(CANT_FIND_POOL, menu_root);
1085 return (BAM_ERROR);
1086 }
1087 BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool));
1088
1089 zmntpt = mount_top_dataset(pool, &zmnted);
1090 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1091 if (zmntpt == NULL) {
1092 bam_error(CANT_MOUNT_POOL_DATASET, pool);
1093 free(special);
1094 return (BAM_ERROR);
1095 }
1096 BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt));
1097
1098 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1099 BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root));
1100 }
1101
1102 elide_trailing_slash(menu_root, clean_menu_root,
1103 sizeof (clean_menu_root));
1104
1105 BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root));
1106
1107 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1108 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1109
1110 BAM_DPRINTF((D_MENU_PATH, fcn, menu_path));
1111
1112 /*
1113 * If listing the menu, display the menu location
1114 */
1115 if (strcmp(subcmd, "list_entry") == 0)
1116 bam_print(GRUB_MENU_PATH, menu_path);
1117
1118 if ((menu = menu_read(menu_path)) == NULL) {
1119 bam_error(CANNOT_LOCATE_GRUB_MENU_FILE, menu_path);
1120 if (special != NULL)
1121 free(special);
1122
1123 return (BAM_ERROR);
1124 }
1125
1126 /*
1127 * We already checked the following case in
1128 * check_subcmd_and_suboptions() above. Complete the
1129 * final step now.
1130 */
1131 if (strcmp(subcmd, "set_option") == 0) {
1132 assert(largc == 1 && largv[0] && largv[1] == NULL);
1133 opt = largv[0];
1134 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1135 (strcmp(subcmd, "list_setting") != 0)) {
1136 assert(largc == 0 && largv == NULL);
1137 }
1138
1139 ret = get_boot_cap(bam_root);
1140 if (ret != BAM_SUCCESS) {
1141 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1142 goto out;
1143 }
1144
1145 /*
1146 * Once the sub-cmd handler has run
1147 * only the line field is guaranteed to have valid values
1148 */
1149 if (strcmp(subcmd, "update_entry") == 0) {
1150 ret = f(menu, menu_root, osdev);
1151 } else if (strcmp(subcmd, "upgrade") == 0) {
1152 ret = f(menu, bam_root, menu_root);
1153 } else if (strcmp(subcmd, "list_entry") == 0) {
1154 ret = f(menu, menu_path, opt);
1155 } else if (strcmp(subcmd, "list_setting") == 0) {
1156 ret = f(menu, ((largc > 0) ? largv[0] : ""),
1157 ((largc > 1) ? largv[1] : ""));
1158 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1159 if (is_sparc()) {
1160 bam_error(NO_SPARC, subcmd);
1161 ret = BAM_ERROR;
1162 } else {
1163 ret = f(menu, bam_root, NULL);
1164 }
1165 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1166 if (is_sparc()) {
1167 bam_error(NO_SPARC, subcmd);
1168 ret = BAM_ERROR;
1169 } else {
1170 char *extra_args = NULL;
1171
1172 /*
1173 * Compress all arguments passed in the largv[] array
1174 * into one string that can then be appended to the
1175 * end of the kernel$ string the routine to enable the
1176 * hypervisor will build.
1177 *
1178 * This allows the caller to supply arbitrary unparsed
1179 * arguments, such as dom0 memory settings or APIC
1180 * options.
1181 *
1182 * This concatenation will be done without ANY syntax
1183 * checking whatsoever, so it's the responsibility of
1184 * the caller to make sure the arguments are valid and
1185 * do not duplicate arguments the conversion routines
1186 * may create.
1187 */
1188 if (largc > 0) {
1189 int extra_len, i;
1190
1191 for (extra_len = 0, i = 0; i < largc; i++)
1192 extra_len += strlen(largv[i]);
1193
1194 /*
1195 * Allocate space for argument strings,
1196 * intervening spaces and terminating NULL.
1197 */
1198 extra_args = alloca(extra_len + largc);
1199
1200 (void) strcpy(extra_args, largv[0]);
1201
1202 for (i = 1; i < largc; i++) {
1203 (void) strcat(extra_args, " ");
1204 (void) strcat(extra_args, largv[i]);
1205 }
1206 }
1207
1208 ret = f(menu, bam_root, extra_args);
1209 }
1210 } else
1211 ret = f(menu, NULL, opt);
1212
1213 if (ret == BAM_WRITE) {
1214 BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root));
1215 ret = menu_write(clean_menu_root, menu);
1216 }
1217
1218 out:
1219 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1220 assert((is_zfs(menu_root)) ^ (pool == NULL));
1221 if (pool) {
1222 (void) umount_top_dataset(pool, zmnted, zmntpt);
1223 free(special);
1224 }
1225 menu_free(menu);
1226 return (ret);
1227 }
1228
1229
1230 static error_t
1231 bam_archive(
1232 char *subcmd,
1233 char *opt)
1234 {
1235 error_t ret;
1236 error_t (*f)(char *root, char *opt);
1237 const char *fcn = "bam_archive()";
1238
1239 /*
1240 * Add trailing / for archive subcommands
1241 */
1242 if (rootbuf[strlen(rootbuf) - 1] != '/')
1243 (void) strcat(rootbuf, "/");
1244 bam_rootlen = strlen(rootbuf);
1245
1246 /*
1247 * Check arguments
1248 */
1249 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1250 if (ret != BAM_SUCCESS) {
1251 return (BAM_ERROR);
1252 }
1253
1254 ret = get_boot_cap(rootbuf);
1255 if (ret != BAM_SUCCESS) {
1256 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1257 return (ret);
1258 }
1259
1260 /*
1261 * Check archive not supported with update_all
1262 * since it is awkward to display out-of-sync
1263 * information for each BE.
1264 */
1265 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1266 bam_error(CHECK_NOT_SUPPORTED, subcmd);
1267 return (BAM_ERROR);
1268 }
1269
1270 if (strcmp(subcmd, "update_all") == 0)
1271 bam_update_all = 1;
1272
1273 #if !defined(_OPB)
1274 ucode_install(bam_root);
1275 #endif
1276
1277 ret = f(bam_root, opt);
1278
1279 bam_update_all = 0;
1280
1281 return (ret);
1282 }
1283
1284 /*PRINTFLIKE1*/
1285 void
1286 bam_error(char *format, ...)
1287 {
1288 va_list ap;
1289
1290 va_start(ap, format);
1291 (void) fprintf(stderr, "%s: ", prog);
1292 (void) vfprintf(stderr, format, ap);
1293 va_end(ap);
1294 }
1295
1296 /*PRINTFLIKE1*/
1297 void
1298 bam_derror(char *format, ...)
1299 {
1300 va_list ap;
1301
1302 assert(bam_debug);
1303
1304 va_start(ap, format);
1305 (void) fprintf(stderr, "DEBUG: ");
1306 (void) vfprintf(stderr, format, ap);
1307 va_end(ap);
1308 }
1309
1310 /*PRINTFLIKE1*/
1311 void
1312 bam_print(char *format, ...)
1313 {
1314 va_list ap;
1315
1316 va_start(ap, format);
1317 (void) vfprintf(stdout, format, ap);
1318 va_end(ap);
1319 }
1320
1321 /*PRINTFLIKE1*/
1322 void
1323 bam_print_stderr(char *format, ...)
1324 {
1325 va_list ap;
1326
1327 va_start(ap, format);
1328 (void) vfprintf(stderr, format, ap);
1329 va_end(ap);
1330 }
1331
1332 void
1333 bam_exit(int excode)
1334 {
1335 restore_env();
1336 bam_unlock();
1337 exit(excode);
1338 }
1339
1340 static void
1341 bam_lock(void)
1342 {
1343 struct flock lock;
1344 pid_t pid;
1345
1346 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1347 if (bam_lock_fd < 0) {
1348 /*
1349 * We may be invoked early in boot for archive verification.
1350 * In this case, root is readonly and /var/run may not exist.
1351 * Proceed without the lock
1352 */
1353 if (errno == EROFS || errno == ENOENT) {
1354 bam_root_readonly = 1;
1355 return;
1356 }
1357
1358 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
1359 bam_exit(1);
1360 }
1361
1362 lock.l_type = F_WRLCK;
1363 lock.l_whence = SEEK_SET;
1364 lock.l_start = 0;
1365 lock.l_len = 0;
1366
1367 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1368 if (errno != EACCES && errno != EAGAIN) {
1369 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1370 (void) close(bam_lock_fd);
1371 bam_lock_fd = -1;
1372 bam_exit(1);
1373 }
1374 pid = 0;
1375 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1376 bam_print(FILE_LOCKED, pid);
1377
1378 lock.l_type = F_WRLCK;
1379 lock.l_whence = SEEK_SET;
1380 lock.l_start = 0;
1381 lock.l_len = 0;
1382 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1383 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1384 (void) close(bam_lock_fd);
1385 bam_lock_fd = -1;
1386 bam_exit(1);
1387 }
1388 }
1389
1390 /* We own the lock now */
1391 pid = getpid();
1392 (void) write(bam_lock_fd, &pid, sizeof (pid));
1393 }
1394
1395 static void
1396 bam_unlock(void)
1397 {
1398 struct flock unlock;
1399
1400 /*
1401 * NOP if we don't hold the lock
1402 */
1403 if (bam_lock_fd < 0) {
1404 return;
1405 }
1406
1407 unlock.l_type = F_UNLCK;
1408 unlock.l_whence = SEEK_SET;
1409 unlock.l_start = 0;
1410 unlock.l_len = 0;
1411
1412 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1413 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1414 }
1415
1416 if (close(bam_lock_fd) == -1) {
1417 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
1418 }
1419 bam_lock_fd = -1;
1420 }
1421
1422 static error_t
1423 list_archive(char *root, char *opt)
1424 {
1425 filelist_t flist;
1426 filelist_t *flistp = &flist;
1427 line_t *lp;
1428
1429 assert(root);
1430 assert(opt == NULL);
1431
1432 flistp->head = flistp->tail = NULL;
1433 if (read_list(root, flistp) != BAM_SUCCESS) {
1434 return (BAM_ERROR);
1435 }
1436 assert(flistp->head && flistp->tail);
1437
1438 for (lp = flistp->head; lp; lp = lp->next) {
1439 bam_print(PRINT, lp->line);
1440 }
1441
1442 filelist_free(flistp);
1443
1444 return (BAM_SUCCESS);
1445 }
1446
1447 /*
1448 * This routine writes a list of lines to a file.
1449 * The list is *not* freed
1450 */
1451 static error_t
1452 list2file(char *root, char *tmp, char *final, line_t *start)
1453 {
1454 char tmpfile[PATH_MAX];
1455 char path[PATH_MAX];
1456 FILE *fp;
1457 int ret;
1458 struct stat sb;
1459 mode_t mode;
1460 uid_t root_uid;
1461 gid_t sys_gid;
1462 struct passwd *pw;
1463 struct group *gp;
1464 const char *fcn = "list2file()";
1465
1466 (void) snprintf(path, sizeof (path), "%s%s", root, final);
1467
1468 if (start == NULL) {
1469 /* Empty GRUB menu */
1470 if (stat(path, &sb) != -1) {
1471 bam_print(UNLINK_EMPTY, path);
1472 if (unlink(path) != 0) {
1473 bam_error(UNLINK_FAIL, path, strerror(errno));
1474 return (BAM_ERROR);
1475 } else {
1476 return (BAM_SUCCESS);
1477 }
1478 }
1479 return (BAM_SUCCESS);
1480 }
1481
1482 /*
1483 * Preserve attributes of existing file if possible,
1484 * otherwise ask the system for uid/gid of root/sys.
1485 * If all fails, fall back on hard-coded defaults.
1486 */
1487 if (stat(path, &sb) != -1) {
1488 mode = sb.st_mode;
1489 root_uid = sb.st_uid;
1490 sys_gid = sb.st_gid;
1491 } else {
1492 mode = DEFAULT_DEV_MODE;
1493 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1494 root_uid = pw->pw_uid;
1495 } else {
1496 bam_error(CANT_FIND_USER,
1497 DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1498 root_uid = (uid_t)DEFAULT_DEV_UID;
1499 }
1500 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1501 sys_gid = gp->gr_gid;
1502 } else {
1503 bam_error(CANT_FIND_GROUP,
1504 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1505 sys_gid = (gid_t)DEFAULT_DEV_GID;
1506 }
1507 }
1508
1509 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1510
1511 /* Truncate tmpfile first */
1512 fp = fopen(tmpfile, "w");
1513 if (fp == NULL) {
1514 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1515 return (BAM_ERROR);
1516 }
1517 ret = fclose(fp);
1518 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1519 if (ret == EOF) {
1520 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1521 return (BAM_ERROR);
1522 }
1523
1524 /* Now open it in append mode */
1525 fp = fopen(tmpfile, "a");
1526 if (fp == NULL) {
1527 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1528 return (BAM_ERROR);
1529 }
1530
1531 for (; start; start = start->next) {
1532 ret = s_fputs(start->line, fp);
1533 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1534 if (ret == EOF) {
1535 bam_error(WRITE_FAIL, tmpfile, strerror(errno));
1536 (void) fclose(fp);
1537 return (BAM_ERROR);
1538 }
1539 }
1540
1541 ret = fclose(fp);
1542 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1543 if (ret == EOF) {
1544 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1545 return (BAM_ERROR);
1546 }
1547
1548 /*
1549 * Set up desired attributes. Ignore failures on filesystems
1550 * not supporting these operations - pcfs reports unsupported
1551 * operations as EINVAL.
1552 */
1553 ret = chmod(tmpfile, mode);
1554 if (ret == -1 &&
1555 errno != EINVAL && errno != ENOTSUP) {
1556 bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
1557 return (BAM_ERROR);
1558 }
1559
1560 ret = chown(tmpfile, root_uid, sys_gid);
1561 if (ret == -1 &&
1562 errno != EINVAL && errno != ENOTSUP) {
1563 bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
1564 return (BAM_ERROR);
1565 }
1566
1567 /*
1568 * Do an atomic rename
1569 */
1570 ret = rename(tmpfile, path);
1571 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1572 if (ret != 0) {
1573 bam_error(RENAME_FAIL, path, strerror(errno));
1574 return (BAM_ERROR);
1575 }
1576
1577 BAM_DPRINTF((D_WROTE_FILE, fcn, path));
1578 return (BAM_SUCCESS);
1579 }
1580
1581 /*
1582 * Checks if the path specified (without the file name at the end) exists
1583 * and creates it if not. If the path exists and is not a directory, an attempt
1584 * to unlink is made.
1585 */
1586 static int
1587 setup_path(char *path)
1588 {
1589 char *p;
1590 int ret;
1591 struct stat sb;
1592
1593 p = strrchr(path, '/');
1594 if (p != NULL) {
1595 *p = '\0';
1596 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1597 /* best effort attempt, mkdirp will catch the error */
1598 (void) unlink(path);
1599 if (bam_verbose)
1600 bam_print(NEED_DIRPATH, path);
1601 ret = mkdirp(path, DIR_PERMS);
1602 if (ret == -1) {
1603 bam_error(MKDIR_FAILED, path, strerror(errno));
1604 *p = '/';
1605 return (BAM_ERROR);
1606 }
1607 }
1608 *p = '/';
1609 return (BAM_SUCCESS);
1610 }
1611 return (BAM_SUCCESS);
1612 }
1613
1614 typedef union {
1615 gzFile gzfile;
1616 int fdfile;
1617 } outfile;
1618
1619 typedef struct {
1620 char path[PATH_MAX];
1621 outfile out;
1622 } cachefile;
1623
1624 static int
1625 setup_file(char *base, const char *path, cachefile *cf)
1626 {
1627 int ret;
1628 char *strip;
1629
1630 /* init gzfile or fdfile in case we fail before opening */
1631 if (bam_direct == BAM_DIRECT_DBOOT)
1632 cf->out.gzfile = NULL;
1633 else
1634 cf->out.fdfile = -1;
1635
1636 /* strip the trailing altroot path */
1637 strip = (char *)path + strlen(rootbuf);
1638
1639 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1640 if (ret >= sizeof (cf->path)) {
1641 bam_error(PATH_TOO_LONG, rootbuf);
1642 return (BAM_ERROR);
1643 }
1644
1645 /* Check if path is present in the archive cache directory */
1646 if (setup_path(cf->path) == BAM_ERROR)
1647 return (BAM_ERROR);
1648
1649 if (bam_direct == BAM_DIRECT_DBOOT) {
1650 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1651 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1652 return (BAM_ERROR);
1653 }
1654 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1655 Z_DEFAULT_STRATEGY);
1656 } else {
1657 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1658 == -1) {
1659 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1660 return (BAM_ERROR);
1661 }
1662 }
1663
1664 return (BAM_SUCCESS);
1665 }
1666
1667 static int
1668 cache_write(cachefile cf, char *buf, int size)
1669 {
1670 int err;
1671
1672 if (bam_direct == BAM_DIRECT_DBOOT) {
1673 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
1674 bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err));
1675 if (err == Z_ERRNO && bam_verbose) {
1676 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1677 }
1678 return (BAM_ERROR);
1679 }
1680 } else {
1681 if (write(cf.out.fdfile, buf, size) < 1) {
1682 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1683 return (BAM_ERROR);
1684 }
1685 }
1686 return (BAM_SUCCESS);
1687 }
1688
1689 static int
1690 cache_close(cachefile cf)
1691 {
1692 int ret;
1693
1694 if (bam_direct == BAM_DIRECT_DBOOT) {
1695 if (cf.out.gzfile) {
1696 ret = gzclose(cf.out.gzfile);
1697 if (ret != Z_OK) {
1698 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
1699 return (BAM_ERROR);
1700 }
1701 }
1702 } else {
1703 if (cf.out.fdfile != -1) {
1704 ret = close(cf.out.fdfile);
1705 if (ret != 0) {
1706 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
1707 return (BAM_ERROR);
1708 }
1709 }
1710 }
1711
1712 return (BAM_SUCCESS);
1713 }
1714
1715 static int
1716 dircache_updatefile(const char *path, int what)
1717 {
1718 int ret, exitcode;
1719 char buf[4096 * 4];
1720 FILE *infile;
1721 cachefile outfile, outupdt;
1722
1723 if (bam_nowrite()) {
1724 set_dir_flag(what, NEED_UPDATE);
1725 return (BAM_SUCCESS);
1726 }
1727
1728 if (!has_cachedir(what))
1729 return (BAM_SUCCESS);
1730
1731 if ((infile = fopen(path, "rb")) == NULL) {
1732 bam_error(OPEN_FAIL, path, strerror(errno));
1733 return (BAM_ERROR);
1734 }
1735
1736 ret = setup_file(get_cachedir(what), path, &outfile);
1737 if (ret == BAM_ERROR) {
1738 exitcode = BAM_ERROR;
1739 goto out;
1740 }
1741 if (!is_dir_flag_on(what, NO_MULTI)) {
1742 ret = setup_file(get_updatedir(what), path, &outupdt);
1743 if (ret == BAM_ERROR)
1744 set_dir_flag(what, NO_MULTI);
1745 }
1746
1747 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
1748 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
1749 exitcode = BAM_ERROR;
1750 goto out;
1751 }
1752 if (!is_dir_flag_on(what, NO_MULTI))
1753 if (cache_write(outupdt, buf, ret) == BAM_ERROR)
1754 set_dir_flag(what, NO_MULTI);
1755 }
1756
1757 set_dir_flag(what, NEED_UPDATE);
1758 get_count(what)++;
1759 if (get_count(what) > COUNT_MAX)
1760 set_dir_flag(what, NO_MULTI);
1761 exitcode = BAM_SUCCESS;
1762 out:
1763 (void) fclose(infile);
1764 if (cache_close(outfile) == BAM_ERROR)
1765 exitcode = BAM_ERROR;
1766 if (!is_dir_flag_on(what, NO_MULTI) &&
1767 cache_close(outupdt) == BAM_ERROR)
1768 exitcode = BAM_ERROR;
1769 if (exitcode == BAM_ERROR)
1770 set_flag(UPDATE_ERROR);
1771 return (exitcode);
1772 }
1773
1774 static int
1775 dircache_updatedir(const char *path, int what, int updt)
1776 {
1777 int ret;
1778 char dpath[PATH_MAX];
1779 char *strip;
1780 struct stat sb;
1781
1782 strip = (char *)path + strlen(rootbuf);
1783
1784 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
1785 get_updatedir(what) : get_cachedir(what), strip);
1786
1787 if (ret >= sizeof (dpath)) {
1788 bam_error(PATH_TOO_LONG, rootbuf);
1789 set_flag(UPDATE_ERROR);
1790 return (BAM_ERROR);
1791 }
1792
1793 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
1794 return (BAM_SUCCESS);
1795
1796 if (updt) {
1797 if (!is_dir_flag_on(what, NO_MULTI))
1798 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
1799 set_dir_flag(what, NO_MULTI);
1800 } else {
1801 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
1802 set_flag(UPDATE_ERROR);
1803 return (BAM_ERROR);
1804 }
1805 }
1806
1807 set_dir_flag(what, NEED_UPDATE);
1808 return (BAM_SUCCESS);
1809 }
1810
1811 #define DO_CACHE_DIR 0
1812 #define DO_UPDATE_DIR 1
1813
1814 #if defined(_LP64) || defined(_LONGLONG_TYPE)
1815 typedef Elf64_Ehdr _elfhdr;
1816 #else
1817 typedef Elf32_Ehdr _elfhdr;
1818 #endif
1819
1820 /*
1821 * This routine updates the contents of the cache directory
1822 */
1823 static int
1824 update_dircache(const char *path, int flags)
1825 {
1826 int rc = BAM_SUCCESS;
1827
1828 switch (flags) {
1829 case FTW_F:
1830 {
1831 int fd;
1832 _elfhdr elf;
1833
1834 if ((fd = open(path, O_RDONLY)) < 0) {
1835 bam_error(OPEN_FAIL, path, strerror(errno));
1836 set_flag(UPDATE_ERROR);
1837 rc = BAM_ERROR;
1838 break;
1839 }
1840
1841 /*
1842 * libelf and gelf would be a cleaner and easier way to handle
1843 * this, but libelf fails compilation if _ILP32 is defined &&
1844 * _FILE_OFFSET_BITS is != 32 ...
1845 */
1846 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
1847 bam_error(READ_FAIL, path, strerror(errno));
1848 set_flag(UPDATE_ERROR);
1849 (void) close(fd);
1850 rc = BAM_ERROR;
1851 break;
1852 }
1853 (void) close(fd);
1854
1855 /*
1856 * If the file is not an executable and is not inside an amd64
1857 * directory, we copy it in both the cache directories,
1858 * otherwise, we only copy it inside the 64-bit one.
1859 */
1860 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
1861 if (strstr(path, "/amd64")) {
1862 rc = dircache_updatefile(path, FILE64);
1863 } else {
1864 rc = dircache_updatefile(path, FILE32);
1865 if (rc == BAM_SUCCESS)
1866 rc = dircache_updatefile(path, FILE64);
1867 }
1868 } else {
1869 /*
1870 * Based on the ELF class we copy the file in the 32-bit
1871 * or the 64-bit cache directory.
1872 */
1873 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
1874 rc = dircache_updatefile(path, FILE32);
1875 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
1876 rc = dircache_updatefile(path, FILE64);
1877 } else {
1878 bam_print(NO3264ELF, path);
1879 /* paranoid */
1880 rc = dircache_updatefile(path, FILE32);
1881 if (rc == BAM_SUCCESS)
1882 rc = dircache_updatefile(path, FILE64);
1883 }
1884 }
1885 break;
1886 }
1887 case FTW_D:
1888 if (strstr(path, "/amd64") == NULL) {
1889 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
1890 if (rc == BAM_SUCCESS)
1891 rc = dircache_updatedir(path, FILE32,
1892 DO_CACHE_DIR);
1893 } else {
1894 if (has_cachedir(FILE64)) {
1895 rc = dircache_updatedir(path, FILE64,
1896 DO_UPDATE_DIR);
1897 if (rc == BAM_SUCCESS)
1898 rc = dircache_updatedir(path, FILE64,
1899 DO_CACHE_DIR);
1900 }
1901 }
1902 break;
1903 default:
1904 rc = BAM_ERROR;
1905 break;
1906 }
1907
1908 return (rc);
1909 }
1910
1911 /*ARGSUSED*/
1912 static int
1913 cmpstat(
1914 const char *file,
1915 const struct stat *st,
1916 int flags,
1917 struct FTW *ftw)
1918 {
1919 uint_t sz;
1920 uint64_t *value;
1921 uint64_t filestat[2];
1922 int error, ret, status;
1923
1924 struct safefile *safefilep;
1925 FILE *fp;
1926 struct stat sb;
1927 regex_t re;
1928
1929 /*
1930 * On SPARC we create/update links too.
1931 */
1932 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
1933 !is_flag_on(IS_SPARC_TARGET)))
1934 return (0);
1935
1936 /*
1937 * Ignore broken links
1938 */
1939 if (flags == FTW_SL && stat(file, &sb) < 0)
1940 return (0);
1941
1942 /*
1943 * new_nvlp may be NULL if there were errors earlier
1944 * but this is not fatal to update determination.
1945 */
1946 if (walk_arg.new_nvlp) {
1947 filestat[0] = st->st_size;
1948 filestat[1] = st->st_mtime;
1949 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
1950 file + bam_rootlen, filestat, 2);
1951 if (error)
1952 bam_error(NVADD_FAIL, file, strerror(error));
1953 }
1954
1955 /*
1956 * If we are invoked as part of system/filesystem/boot-archive, then
1957 * there are a number of things we should not worry about
1958 */
1959 if (bam_smf_check) {
1960 /* ignore amd64 modules unless we are booted amd64. */
1961 if (!is_amd64() && strstr(file, "/amd64/") != 0)
1962 return (0);
1963
1964 /* read in list of safe files */
1965 if (safefiles == NULL)
1966 if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
1967 safefiles = s_calloc(1,
1968 sizeof (struct safefile));
1969 safefilep = safefiles;
1970 safefilep->name = s_calloc(1, MAXPATHLEN +
1971 MAXNAMELEN);
1972 safefilep->next = NULL;
1973 while (s_fgets(safefilep->name, MAXPATHLEN +
1974 MAXNAMELEN, fp) != NULL) {
1975 safefilep->next = s_calloc(1,
1976 sizeof (struct safefile));
1977 safefilep = safefilep->next;
1978 safefilep->name = s_calloc(1,
1979 MAXPATHLEN + MAXNAMELEN);
1980 safefilep->next = NULL;
1981 }
1982 (void) fclose(fp);
1983 }
1984 }
1985
1986 /*
1987 * On SPARC we create a -path-list file for mkisofs
1988 */
1989 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
1990 if (flags != FTW_D) {
1991 char *strip;
1992
1993 strip = (char *)file + strlen(rootbuf);
1994 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
1995 file);
1996 }
1997 }
1998
1999 /*
2000 * We are transitioning from the old model to the dircache or the cache
2001 * directory was removed: create the entry without further checkings.
2002 */
2003 if (is_flag_on(NEED_CACHE_DIR)) {
2004 if (bam_verbose)
2005 bam_print(PARSEABLE_NEW_FILE, file);
2006
2007 if (is_flag_on(IS_SPARC_TARGET)) {
2008 set_dir_flag(FILE64, NEED_UPDATE);
2009 return (0);
2010 }
2011
2012 ret = update_dircache(file, flags);
2013 if (ret == BAM_ERROR) {
2014 bam_error(UPDT_CACHE_FAIL, file);
2015 return (-1);
2016 }
2017
2018 return (0);
2019 }
2020
2021 /*
2022 * We need an update if file doesn't exist in old archive
2023 */
2024 if (walk_arg.old_nvlp == NULL ||
2025 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2026 file + bam_rootlen, &value, &sz) != 0) {
2027 if (bam_smf_check) /* ignore new during smf check */
2028 return (0);
2029
2030 if (is_flag_on(IS_SPARC_TARGET)) {
2031 set_dir_flag(FILE64, NEED_UPDATE);
2032 } else {
2033 ret = update_dircache(file, flags);
2034 if (ret == BAM_ERROR) {
2035 bam_error(UPDT_CACHE_FAIL, file);
2036 return (-1);
2037 }
2038 }
2039
2040 if (bam_verbose)
2041 bam_print(PARSEABLE_NEW_FILE, file);
2042 return (0);
2043 }
2044
2045 /*
2046 * If we got there, the file is already listed as to be included in the
2047 * iso image. We just need to know if we are going to rebuild it or not
2048 */
2049 if (is_flag_on(IS_SPARC_TARGET) &&
2050 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
2051 return (0);
2052 /*
2053 * File exists in old archive. Check if file has changed
2054 */
2055 assert(sz == 2);
2056 bcopy(value, filestat, sizeof (filestat));
2057
2058 if (flags != FTW_D && (filestat[0] != st->st_size ||
2059 filestat[1] != st->st_mtime)) {
2060 if (bam_smf_check) {
2061 safefilep = safefiles;
2062 while (safefilep != NULL &&
2063 safefilep->name[0] != '\0') {
2064 if (regcomp(&re, safefilep->name,
2065 REG_EXTENDED|REG_NOSUB) == 0) {
2066 status = regexec(&re,
2067 file + bam_rootlen, 0, NULL, 0);
2068 regfree(&re);
2069 if (status == 0) {
2070 (void) creat(
2071 NEED_UPDATE_SAFE_FILE,
2072 0644);
2073 return (0);
2074 }
2075 }
2076 safefilep = safefilep->next;
2077 }
2078 }
2079
2080 if (is_flag_on(IS_SPARC_TARGET)) {
2081 set_dir_flag(FILE64, NEED_UPDATE);
2082 } else {
2083 ret = update_dircache(file, flags);
2084 if (ret == BAM_ERROR) {
2085 bam_error(UPDT_CACHE_FAIL, file);
2086 return (-1);
2087 }
2088 }
2089
2090 if (bam_verbose)
2091 if (bam_smf_check)
2092 bam_print(" %s\n", file);
2093 else
2094 bam_print(PARSEABLE_OUT_DATE, file);
2095 }
2096
2097 return (0);
2098 }
2099
2100 /*
2101 * Remove a directory path recursively
2102 */
2103 static int
2104 rmdir_r(char *path)
2105 {
2106 struct dirent *d = NULL;
2107 DIR *dir = NULL;
2108 char tpath[PATH_MAX];
2109 struct stat sb;
2110
2111 if ((dir = opendir(path)) == NULL)
2112 return (-1);
2113
2114 while (d = readdir(dir)) {
2115 if ((strcmp(d->d_name, ".") != 0) &&
2116 (strcmp(d->d_name, "..") != 0)) {
2117 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
2118 path, d->d_name);
2119 if (stat(tpath, &sb) == 0) {
2120 if (sb.st_mode & S_IFDIR)
2121 (void) rmdir_r(tpath);
2122 else
2123 (void) remove(tpath);
2124 }
2125 }
2126 }
2127 return (remove(path));
2128 }
2129
2130 /*
2131 * Check if cache directory exists and, if not, create it and update flags
2132 * accordingly. If the path exists, but it's not a directory, a best effort
2133 * attempt to remove and recreate it is made.
2134 * If the user requested a 'purge', always recreate the directory from scratch.
2135 */
2136 static int
2137 set_cache_dir(char *root, int what)
2138 {
2139 struct stat sb;
2140 int ret = 0;
2141
2142 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
2143 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
2144 "/amd64" : "", CACHEDIR_SUFFIX);
2145
2146 if (ret >= sizeof (get_cachedir(what))) {
2147 bam_error(PATH_TOO_LONG, rootbuf);
2148 return (BAM_ERROR);
2149 }
2150
2151 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2152 (void) rmdir_r(get_cachedir(what));
2153
2154 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2155 /* best effort unlink attempt, mkdir will catch errors */
2156 (void) unlink(get_cachedir(what));
2157
2158 if (bam_verbose)
2159 bam_print(UPDATE_CDIR_MISS, get_cachedir(what));
2160 ret = mkdir(get_cachedir(what), DIR_PERMS);
2161 if (ret < 0) {
2162 bam_error(MKDIR_FAILED, get_cachedir(what),
2163 strerror(errno));
2164 get_cachedir(what)[0] = '\0';
2165 return (ret);
2166 }
2167 set_flag(NEED_CACHE_DIR);
2168 set_dir_flag(what, NO_MULTI);
2169 }
2170
2171 return (BAM_SUCCESS);
2172 }
2173
2174 static int
2175 set_update_dir(char *root, int what)
2176 {
2177 struct stat sb;
2178 int ret;
2179
2180 if (is_dir_flag_on(what, NO_MULTI))
2181 return (BAM_SUCCESS);
2182
2183 if (!bam_extend) {
2184 set_dir_flag(what, NO_MULTI);
2185 return (BAM_SUCCESS);
2186 }
2187
2188 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2189 ret = snprintf(get_updatedir(what),
2190 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2191 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2192 else
2193 ret = snprintf(get_updatedir(what),
2194 sizeof (get_updatedir(what)), "%s%s%s%s", root,
2195 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2196
2197 if (ret >= sizeof (get_updatedir(what))) {
2198 bam_error(PATH_TOO_LONG, rootbuf);
2199 return (BAM_ERROR);
2200 }
2201
2202 if (stat(get_updatedir(what), &sb) == 0) {
2203 if (S_ISDIR(sb.st_mode))
2204 ret = rmdir_r(get_updatedir(what));
2205 else
2206 ret = unlink(get_updatedir(what));
2207
2208 if (ret != 0)
2209 set_dir_flag(what, NO_MULTI);
2210 }
2211
2212 if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2213 set_dir_flag(what, NO_MULTI);
2214
2215 return (BAM_SUCCESS);
2216 }
2217
2218 static int
2219 is_valid_archive(char *root, int what)
2220 {
2221 char archive_path[PATH_MAX];
2222 char timestamp_path[PATH_MAX];
2223 struct stat sb, timestamp;
2224 int ret;
2225
2226 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2227 ret = snprintf(archive_path, sizeof (archive_path),
2228 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2229 ARCHIVE_SUFFIX);
2230 else
2231 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2232 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2233
2234 if (ret >= sizeof (archive_path)) {
2235 bam_error(PATH_TOO_LONG, rootbuf);
2236 return (BAM_ERROR);
2237 }
2238
2239 if (stat(archive_path, &sb) != 0) {
2240 if (bam_verbose && !bam_check)
2241 bam_print(UPDATE_ARCH_MISS, archive_path);
2242 set_dir_flag(what, NEED_UPDATE);
2243 set_dir_flag(what, NO_MULTI);
2244 return (BAM_SUCCESS);
2245 }
2246
2247 /*
2248 * The timestamp file is used to prevent stale files in the archive
2249 * cache.
2250 * Stale files can happen if the system is booted back and forth across
2251 * the transition from bootadm-before-the-cache to
2252 * bootadm-after-the-cache, since older versions of bootadm don't know
2253 * about the existence of the archive cache.
2254 *
2255 * Since only bootadm-after-the-cache versions know about about this
2256 * file, we require that the boot archive be older than this file.
2257 */
2258 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2259 FILE_STAT_TIMESTAMP);
2260
2261 if (ret >= sizeof (timestamp_path)) {
2262 bam_error(PATH_TOO_LONG, rootbuf);
2263 return (BAM_ERROR);
2264 }
2265
2266 if (stat(timestamp_path, ×tamp) != 0 ||
2267 sb.st_mtime > timestamp.st_mtime) {
2268 if (bam_verbose && !bam_check)
2269 bam_print(UPDATE_CACHE_OLD, timestamp);
2270 /*
2271 * Don't generate a false positive for the boot-archive service
2272 * but trigger an update of the archive cache in
2273 * boot-archive-update.
2274 */
2275 if (bam_smf_check) {
2276 (void) creat(NEED_UPDATE_FILE, 0644);
2277 return (BAM_SUCCESS);
2278 }
2279
2280 set_flag(INVALIDATE_CACHE);
2281 set_dir_flag(what, NEED_UPDATE);
2282 set_dir_flag(what, NO_MULTI);
2283 return (BAM_SUCCESS);
2284 }
2285
2286 if (is_flag_on(IS_SPARC_TARGET))
2287 return (BAM_SUCCESS);
2288
2289 if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2290 if (bam_verbose && !bam_check)
2291 bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX);
2292 set_dir_flag(what, NO_MULTI);
2293 }
2294
2295 return (BAM_SUCCESS);
2296 }
2297
2298 /*
2299 * Check flags and presence of required files and directories.
2300 * The force flag and/or absence of files should
2301 * trigger an update.
2302 * Suppress stdout output if check (-n) option is set
2303 * (as -n should only produce parseable output.)
2304 */
2305 static int
2306 check_flags_and_files(char *root)
2307 {
2308
2309 struct stat sb;
2310 int ret;
2311
2312 /*
2313 * If archive is missing, create archive
2314 */
2315 if (is_flag_on(IS_SPARC_TARGET)) {
2316 ret = is_valid_archive(root, FILE64);
2317 if (ret == BAM_ERROR)
2318 return (BAM_ERROR);
2319 } else {
2320 int what = FILE32;
2321 do {
2322 ret = is_valid_archive(root, what);
2323 if (ret == BAM_ERROR)
2324 return (BAM_ERROR);
2325 what++;
2326 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2327 }
2328
2329 if (bam_nowrite())
2330 return (BAM_SUCCESS);
2331
2332
2333 /*
2334 * check if cache directories exist on x86.
2335 * check (and always open) the cache file on SPARC.
2336 */
2337 if (is_sparc()) {
2338 ret = snprintf(get_cachedir(FILE64),
2339 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2340 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2341
2342 if (ret >= sizeof (get_cachedir(FILE64))) {
2343 bam_error(PATH_TOO_LONG, rootbuf);
2344 return (BAM_ERROR);
2345 }
2346
2347 if (stat(get_cachedir(FILE64), &sb) != 0) {
2348 set_flag(NEED_CACHE_DIR);
2349 set_dir_flag(FILE64, NEED_UPDATE);
2350 }
2351
2352 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2353 if (walk_arg.sparcfile == NULL) {
2354 bam_error(OPEN_FAIL, get_cachedir(FILE64),
2355 strerror(errno));
2356 return (BAM_ERROR);
2357 }
2358
2359 set_dir_present(FILE64);
2360 } else {
2361 int what = FILE32;
2362
2363 do {
2364 if (set_cache_dir(root, what) != 0)
2365 return (BAM_ERROR);
2366
2367 set_dir_present(what);
2368
2369 if (set_update_dir(root, what) != 0)
2370 return (BAM_ERROR);
2371 what++;
2372 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2373 }
2374
2375 /*
2376 * if force, create archive unconditionally
2377 */
2378 if (bam_force) {
2379 if (!is_sparc())
2380 set_dir_flag(FILE32, NEED_UPDATE);
2381 set_dir_flag(FILE64, NEED_UPDATE);
2382 if (bam_verbose)
2383 bam_print(UPDATE_FORCE);
2384 return (BAM_SUCCESS);
2385 }
2386
2387 return (BAM_SUCCESS);
2388 }
2389
2390 static error_t
2391 read_one_list(char *root, filelist_t *flistp, char *filelist)
2392 {
2393 char path[PATH_MAX];
2394 FILE *fp;
2395 char buf[BAM_MAXLINE];
2396 const char *fcn = "read_one_list()";
2397
2398 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2399
2400 fp = fopen(path, "r");
2401 if (fp == NULL) {
2402 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2403 return (BAM_ERROR);
2404 }
2405 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2406 /* skip blank lines */
2407 if (strspn(buf, " \t") == strlen(buf))
2408 continue;
2409 append_to_flist(flistp, buf);
2410 }
2411 if (fclose(fp) != 0) {
2412 bam_error(CLOSE_FAIL, path, strerror(errno));
2413 return (BAM_ERROR);
2414 }
2415 return (BAM_SUCCESS);
2416 }
2417
2418 static error_t
2419 read_list(char *root, filelist_t *flistp)
2420 {
2421 char path[PATH_MAX];
2422 char cmd[PATH_MAX];
2423 struct stat sb;
2424 int n, rval;
2425 const char *fcn = "read_list()";
2426
2427 flistp->head = flistp->tail = NULL;
2428
2429 /*
2430 * build and check path to extract_boot_filelist.ksh
2431 */
2432 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2433 if (n >= sizeof (path)) {
2434 bam_error(NO_FLIST);
2435 return (BAM_ERROR);
2436 }
2437
2438 if (is_safe_exec(path) == BAM_ERROR)
2439 return (BAM_ERROR);
2440
2441 /*
2442 * If extract_boot_filelist is present, exec it, otherwise read
2443 * the filelists directly, for compatibility with older images.
2444 */
2445 if (stat(path, &sb) == 0) {
2446 /*
2447 * build arguments to exec extract_boot_filelist.ksh
2448 */
2449 char *rootarg, *platarg;
2450 int platarglen = 1, rootarglen = 1;
2451 if (strlen(root) > 1)
2452 rootarglen += strlen(root) + strlen("-R ");
2453 if (bam_alt_platform)
2454 platarglen += strlen(bam_platform) + strlen("-p ");
2455 platarg = s_calloc(1, platarglen);
2456 rootarg = s_calloc(1, rootarglen);
2457 *platarg = 0;
2458 *rootarg = 0;
2459
2460 if (strlen(root) > 1) {
2461 (void) snprintf(rootarg, rootarglen,
2462 "-R %s", root);
2463 }
2464 if (bam_alt_platform) {
2465 (void) snprintf(platarg, platarglen,
2466 "-p %s", bam_platform);
2467 }
2468 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2469 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2470 free(platarg);
2471 free(rootarg);
2472 if (n >= sizeof (cmd)) {
2473 bam_error(NO_FLIST);
2474 return (BAM_ERROR);
2475 }
2476 if (exec_cmd(cmd, flistp) != 0) {
2477 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2478 return (BAM_ERROR);
2479 }
2480 } else {
2481 /*
2482 * Read current lists of files - only the first is mandatory
2483 */
2484 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2485 if (rval != BAM_SUCCESS)
2486 return (rval);
2487 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2488 }
2489
2490 if (flistp->head == NULL) {
2491 bam_error(NO_FLIST);
2492 return (BAM_ERROR);
2493 }
2494
2495 return (BAM_SUCCESS);
2496 }
2497
2498 static void
2499 getoldstat(char *root)
2500 {
2501 char path[PATH_MAX];
2502 int fd, error;
2503 struct stat sb;
2504 char *ostat;
2505
2506 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2507 fd = open(path, O_RDONLY);
2508 if (fd == -1) {
2509 if (bam_verbose)
2510 bam_print(OPEN_FAIL, path, strerror(errno));
2511 goto out_err;
2512 }
2513
2514 if (fstat(fd, &sb) != 0) {
2515 bam_error(STAT_FAIL, path, strerror(errno));
2516 goto out_err;
2517 }
2518
2519 ostat = s_calloc(1, sb.st_size);
2520
2521 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2522 bam_error(READ_FAIL, path, strerror(errno));
2523 free(ostat);
2524 goto out_err;
2525 }
2526
2527 (void) close(fd);
2528 fd = -1;
2529
2530 walk_arg.old_nvlp = NULL;
2531 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2532
2533 free(ostat);
2534
2535 if (error) {
2536 bam_error(UNPACK_FAIL, path, strerror(error));
2537 walk_arg.old_nvlp = NULL;
2538 goto out_err;
2539 } else {
2540 return;
2541 }
2542
2543 out_err:
2544 if (fd != -1)
2545 (void) close(fd);
2546 if (!is_flag_on(IS_SPARC_TARGET))
2547 set_dir_flag(FILE32, NEED_UPDATE);
2548 set_dir_flag(FILE64, NEED_UPDATE);
2549 }
2550
2551 /* Best effort stale entry removal */
2552 static void
2553 delete_stale(char *file, int what)
2554 {
2555 char path[PATH_MAX];
2556 struct stat sb;
2557
2558 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2559 if (!bam_check && stat(path, &sb) == 0) {
2560 if (sb.st_mode & S_IFDIR)
2561 (void) rmdir_r(path);
2562 else
2563 (void) unlink(path);
2564
2565 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2566 }
2567 }
2568
2569 /*
2570 * Checks if a file in the current (old) archive has
2571 * been deleted from the root filesystem. This is needed for
2572 * software like Trusted Extensions (TX) that switch early
2573 * in boot based on presence/absence of a kernel module.
2574 */
2575 static void
2576 check4stale(char *root)
2577 {
2578 nvpair_t *nvp;
2579 nvlist_t *nvlp;
2580 char *file;
2581 char path[PATH_MAX];
2582
2583 /*
2584 * Skip stale file check during smf check
2585 */
2586 if (bam_smf_check)
2587 return;
2588
2589 /*
2590 * If we need to (re)create the cache, there's no need to check for
2591 * stale files
2592 */
2593 if (is_flag_on(NEED_CACHE_DIR))
2594 return;
2595
2596 /* Nothing to do if no old stats */
2597 if ((nvlp = walk_arg.old_nvlp) == NULL)
2598 return;
2599
2600 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2601 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2602 file = nvpair_name(nvp);
2603 if (file == NULL)
2604 continue;
2605 (void) snprintf(path, sizeof (path), "%s/%s",
2606 root, file);
2607 if (access(path, F_OK) < 0) {
2608 int what;
2609
2610 if (bam_verbose)
2611 bam_print(PARSEABLE_STALE_FILE, path);
2612
2613 if (is_flag_on(IS_SPARC_TARGET)) {
2614 set_dir_flag(FILE64, NEED_UPDATE);
2615 } else {
2616 for (what = FILE32; what < CACHEDIR_NUM; what++)
2617 if (has_cachedir(what))
2618 delete_stale(file, what);
2619 }
2620 }
2621 }
2622 }
2623
2624 static void
2625 create_newstat(void)
2626 {
2627 int error;
2628
2629 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2630 if (error) {
2631 /*
2632 * Not fatal - we can still create archive
2633 */
2634 walk_arg.new_nvlp = NULL;
2635 bam_error(NVALLOC_FAIL, strerror(error));
2636 }
2637 }
2638
2639 static int
2640 walk_list(char *root, filelist_t *flistp)
2641 {
2642 char path[PATH_MAX];
2643 line_t *lp;
2644
2645 for (lp = flistp->head; lp; lp = lp->next) {
2646 /*
2647 * Don't follow symlinks. A symlink must refer to
2648 * a file that would appear in the archive through
2649 * a direct reference. This matches the archive
2650 * construction behavior.
2651 */
2652 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
2653 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
2654 if (is_flag_on(UPDATE_ERROR))
2655 return (BAM_ERROR);
2656 /*
2657 * Some files may not exist.
2658 * For example: etc/rtc_config on a x86 diskless system
2659 * Emit verbose message only
2660 */
2661 if (bam_verbose)
2662 bam_print(NFTW_FAIL, path, strerror(errno));
2663 }
2664 }
2665
2666 return (BAM_SUCCESS);
2667 }
2668
2669 /*
2670 * Update the timestamp file.
2671 */
2672 static void
2673 update_timestamp(char *root)
2674 {
2675 char timestamp_path[PATH_MAX];
2676
2677 /* this path length has already been checked in check_flags_and_files */
2678 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2679 FILE_STAT_TIMESTAMP);
2680
2681 /*
2682 * recreate the timestamp file. Since an outdated or absent timestamp
2683 * file translates in a complete rebuild of the archive cache, notify
2684 * the user of the performance issue.
2685 */
2686 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
2687 bam_error(OPEN_FAIL, timestamp_path, strerror(errno));
2688 bam_error(TIMESTAMP_FAIL, rootbuf);
2689 }
2690 }
2691
2692
2693 static void
2694 savenew(char *root)
2695 {
2696 char path[PATH_MAX];
2697 char path2[PATH_MAX];
2698 size_t sz;
2699 char *nstat;
2700 int fd, wrote, error;
2701
2702 nstat = NULL;
2703 sz = 0;
2704 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
2705 NV_ENCODE_XDR, 0);
2706 if (error) {
2707 bam_error(PACK_FAIL, strerror(error));
2708 return;
2709 }
2710
2711 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
2712 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
2713 if (fd == -1) {
2714 bam_error(OPEN_FAIL, path, strerror(errno));
2715 free(nstat);
2716 return;
2717 }
2718 wrote = write(fd, nstat, sz);
2719 if (wrote != sz) {
2720 bam_error(WRITE_FAIL, path, strerror(errno));
2721 (void) close(fd);
2722 free(nstat);
2723 return;
2724 }
2725 (void) close(fd);
2726 free(nstat);
2727
2728 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
2729 if (rename(path, path2) != 0) {
2730 bam_error(RENAME_FAIL, path2, strerror(errno));
2731 }
2732 }
2733
2734 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
2735
2736 static void
2737 clear_walk_args(void)
2738 {
2739 if (walk_arg.old_nvlp)
2740 nvlist_free(walk_arg.old_nvlp);
2741 if (walk_arg.new_nvlp)
2742 nvlist_free(walk_arg.new_nvlp);
2743 if (walk_arg.sparcfile)
2744 (void) fclose(walk_arg.sparcfile);
2745 walk_arg.old_nvlp = NULL;
2746 walk_arg.new_nvlp = NULL;
2747 walk_arg.sparcfile = NULL;
2748 }
2749
2750 /*
2751 * Returns:
2752 * 0 - no update necessary
2753 * 1 - update required.
2754 * BAM_ERROR (-1) - An error occurred
2755 *
2756 * Special handling for check (-n):
2757 * ================================
2758 * The check (-n) option produces parseable output.
2759 * To do this, we suppress all stdout messages unrelated
2760 * to out of sync files.
2761 * All stderr messages are still printed though.
2762 *
2763 */
2764 static int
2765 update_required(char *root)
2766 {
2767 struct stat sb;
2768 char path[PATH_MAX];
2769 filelist_t flist;
2770 filelist_t *flistp = &flist;
2771 int ret;
2772
2773 flistp->head = flistp->tail = NULL;
2774
2775 if (is_sparc())
2776 set_flag(IS_SPARC_TARGET);
2777
2778 /*
2779 * Check if cache directories and archives are present
2780 */
2781
2782 ret = check_flags_and_files(root);
2783 if (ret < 0)
2784 return (BAM_ERROR);
2785
2786 /*
2787 * In certain deployment scenarios, filestat may not
2788 * exist. Do not stop the boot process, but trigger an update
2789 * of the archives (which will recreate filestat.ramdisk).
2790 */
2791 if (bam_smf_check) {
2792 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2793 if (stat(path, &sb) != 0) {
2794 (void) creat(NEED_UPDATE_FILE, 0644);
2795 return (0);
2796 }
2797 }
2798
2799 getoldstat(root);
2800
2801 /*
2802 * Check if the archive contains files that are no longer
2803 * present on the root filesystem.
2804 */
2805 check4stale(root);
2806
2807 /*
2808 * read list of files
2809 */
2810 if (read_list(root, flistp) != BAM_SUCCESS) {
2811 clear_walk_args();
2812 return (BAM_ERROR);
2813 }
2814
2815 assert(flistp->head && flistp->tail);
2816
2817 /*
2818 * At this point either the update is required
2819 * or the decision is pending. In either case
2820 * we need to create new stat nvlist
2821 */
2822 create_newstat();
2823 /*
2824 * This walk does 2 things:
2825 * - gets new stat data for every file
2826 * - (optional) compare old and new stat data
2827 */
2828 ret = walk_list(root, &flist);
2829
2830 /* done with the file list */
2831 filelist_free(flistp);
2832
2833 /* something went wrong */
2834
2835 if (ret == BAM_ERROR) {
2836 bam_error(CACHE_FAIL);
2837 return (BAM_ERROR);
2838 }
2839
2840 if (walk_arg.new_nvlp == NULL) {
2841 if (walk_arg.sparcfile != NULL)
2842 (void) fclose(walk_arg.sparcfile);
2843 bam_error(NO_NEW_STAT);
2844 }
2845
2846 /* If nothing was updated, discard newstat. */
2847
2848 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
2849 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
2850 clear_walk_args();
2851 return (0);
2852 }
2853
2854 if (walk_arg.sparcfile != NULL)
2855 (void) fclose(walk_arg.sparcfile);
2856
2857 return (1);
2858 }
2859
2860 static int
2861 flushfs(char *root)
2862 {
2863 char cmd[PATH_MAX + 30];
2864
2865 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
2866 LOCKFS_PATH, root);
2867
2868 return (exec_cmd(cmd, NULL));
2869 }
2870
2871 static int
2872 do_archive_copy(char *source, char *dest)
2873 {
2874
2875 sync();
2876
2877 /* the equivalent of mv archive-new-$pid boot_archive */
2878 if (rename(source, dest) != 0) {
2879 (void) unlink(source);
2880 return (BAM_ERROR);
2881 }
2882
2883 if (flushfs(bam_root) != 0)
2884 sync();
2885
2886 return (BAM_SUCCESS);
2887 }
2888
2889 static int
2890 check_cmdline(filelist_t flist)
2891 {
2892 line_t *lp;
2893
2894 for (lp = flist.head; lp; lp = lp->next) {
2895 if (strstr(lp->line, "Error:") != NULL ||
2896 strstr(lp->line, "Inode number overflow") != NULL) {
2897 (void) fprintf(stderr, "%s\n", lp->line);
2898 return (BAM_ERROR);
2899 }
2900 }
2901
2902 return (BAM_SUCCESS);
2903 }
2904
2905 static void
2906 dump_errormsg(filelist_t flist)
2907 {
2908 line_t *lp;
2909
2910 for (lp = flist.head; lp; lp = lp->next)
2911 (void) fprintf(stderr, "%s\n", lp->line);
2912 }
2913
2914 static int
2915 check_archive(char *dest)
2916 {
2917 struct stat sb;
2918
2919 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
2920 sb.st_size < 10000) {
2921 bam_error(ARCHIVE_BAD, dest);
2922 (void) unlink(dest);
2923 return (BAM_ERROR);
2924 }
2925
2926 return (BAM_SUCCESS);
2927 }
2928
2929 static boolean_t
2930 is_be(char *root)
2931 {
2932 zfs_handle_t *zhp;
2933 libzfs_handle_t *hdl;
2934 be_node_list_t *be_nodes = NULL;
2935 be_node_list_t *cur_be;
2936 boolean_t be_exist = B_FALSE;
2937 char ds_path[ZFS_MAXNAMELEN];
2938
2939 if (!is_zfs(root))
2940 return (B_FALSE);
2941 /*
2942 * Get dataset for mountpoint
2943 */
2944 if ((hdl = libzfs_init()) == NULL)
2945 return (B_FALSE);
2946
2947 if ((zhp = zfs_path_to_zhandle(hdl, root,
2948 ZFS_TYPE_FILESYSTEM)) == NULL) {
2949 libzfs_fini(hdl);
2950 return (B_FALSE);
2951 }
2952
2953 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
2954
2955 /*
2956 * Check if the current dataset is BE
2957 */
2958 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
2959 for (cur_be = be_nodes; cur_be != NULL;
2960 cur_be = cur_be->be_next_node) {
2961
2962 /*
2963 * Because we guarantee that cur_be->be_root_ds
2964 * is null-terminated by internal data structure,
2965 * we can safely use strcmp()
2966 */
2967 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
2968 be_exist = B_TRUE;
2969 break;
2970 }
2971 }
2972 be_free_list(be_nodes);
2973 }
2974 zfs_close(zhp);
2975 libzfs_fini(hdl);
2976
2977 return (be_exist);
2978 }
2979
2980 /*
2981 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
2982 */
2983 static int
2984 is_mkisofs()
2985 {
2986 if (access(MKISOFS_PATH, X_OK) == 0)
2987 return (1);
2988 return (0);
2989 }
2990
2991 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
2992
2993 static int
2994 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
2995 {
2996 int ret;
2997 char cmdline[3 * PATH_MAX + 64];
2998 filelist_t flist = {0};
2999 const char *func = "create_sparc_archive()";
3000
3001 if (access(bootblk, R_OK) == 1) {
3002 bam_error(BOOTBLK_FAIL, bootblk);
3003 return (BAM_ERROR);
3004 }
3005
3006 /*
3007 * Prepare mkisofs command line and execute it
3008 */
3009 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3010 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3011 tempname, list);
3012
3013 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3014
3015 ret = exec_cmd(cmdline, &flist);
3016 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3017 dump_errormsg(flist);
3018 goto out_err;
3019 }
3020
3021 filelist_free(&flist);
3022
3023 /*
3024 * Prepare dd command line to copy the bootblk on the new archive and
3025 * execute it
3026 */
3027 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3028 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3029 bootblk, tempname);
3030
3031 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3032
3033 ret = exec_cmd(cmdline, &flist);
3034 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3035 goto out_err;
3036
3037 filelist_free(&flist);
3038
3039 /* Did we get a valid archive ? */
3040 if (check_archive(tempname) == BAM_ERROR)
3041 return (BAM_ERROR);
3042
3043 return (do_archive_copy(tempname, archive));
3044
3045 out_err:
3046 filelist_free(&flist);
3047 bam_error(ARCHIVE_FAIL, cmdline);
3048 (void) unlink(tempname);
3049 return (BAM_ERROR);
3050 }
3051
3052 static unsigned int
3053 from_733(unsigned char *s)
3054 {
3055 int i;
3056 unsigned int ret = 0;
3057
3058 for (i = 0; i < 4; i++)
3059 ret |= s[i] << (8 * i);
3060
3061 return (ret);
3062 }
3063
3064 static void
3065 to_733(unsigned char *s, unsigned int val)
3066 {
3067 int i;
3068
3069 for (i = 0; i < 4; i++)
3070 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3071 }
3072
3073 /*
3074 * Extends the current boot archive without recreating it from scratch
3075 */
3076 static int
3077 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3078 {
3079 int fd = -1, newfd = -1, ret, i;
3080 int next_session = 0, new_size = 0;
3081 char cmdline[3 * PATH_MAX + 64];
3082 const char *func = "extend_iso_archive()";
3083 filelist_t flist = {0};
3084 struct iso_pdesc saved_desc[MAX_IVDs];
3085
3086 fd = open(archive, O_RDWR);
3087 if (fd == -1) {
3088 if (bam_verbose)
3089 bam_error(OPEN_FAIL, archive, strerror(errno));
3090 goto out_err;
3091 }
3092
3093 /*
3094 * A partial read is likely due to a corrupted file
3095 */
3096 ret = pread64(fd, saved_desc, sizeof (saved_desc),
3097 VOLDESC_OFF * CD_BLOCK);
3098 if (ret != sizeof (saved_desc)) {
3099 if (bam_verbose)
3100 bam_error(READ_FAIL, archive, strerror(errno));
3101 goto out_err;
3102 }
3103
3104 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3105 if (bam_verbose)
3106 bam_error(SIGN_FAIL, archive);
3107 goto out_err;
3108 }
3109
3110 /*
3111 * Read primary descriptor and locate next_session offset (it should
3112 * point to the end of the archive)
3113 */
3114 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3115
3116 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3117 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3118 MKISO_PARAMS, tempname, update_dir);
3119
3120 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3121
3122 ret = exec_cmd(cmdline, &flist);
3123 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3124 if (bam_verbose) {
3125 bam_error(MULTI_FAIL, cmdline);
3126 dump_errormsg(flist);
3127 }
3128 goto out_flist_err;
3129 }
3130 filelist_free(&flist);
3131
3132 newfd = open(tempname, O_RDONLY);
3133 if (newfd == -1) {
3134 if (bam_verbose)
3135 bam_error(OPEN_FAIL, archive, strerror(errno));
3136 goto out_err;
3137 }
3138
3139 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3140 VOLDESC_OFF * CD_BLOCK);
3141 if (ret != sizeof (saved_desc)) {
3142 if (bam_verbose)
3143 bam_error(READ_FAIL, archive, strerror(errno));
3144 goto out_err;
3145 }
3146
3147 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3148 if (bam_verbose)
3149 bam_error(SIGN_FAIL, archive);
3150 goto out_err;
3151 }
3152
3153 new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3154 to_733(saved_desc[0].volume_space_size, new_size);
3155
3156 for (i = 1; i < MAX_IVDs; i++) {
3157 if (saved_desc[i].type[0] == (unsigned char)255)
3158 break;
3159 if (memcmp(saved_desc[i].id, "CD001", 5))
3160 break;
3161
3162 if (bam_verbose)
3163 bam_print("%s: Updating descriptor entry [%d]\n", func,
3164 i);
3165
3166 to_733(saved_desc[i].volume_space_size, new_size);
3167 }
3168
3169 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3170 if (ret != DVD_BLOCK) {
3171 if (bam_verbose)
3172 bam_error(WRITE_FAIL, archive, strerror(errno));
3173 goto out_err;
3174 }
3175 (void) close(newfd);
3176 newfd = -1;
3177
3178 ret = fsync(fd);
3179 if (ret != 0)
3180 sync();
3181
3182 ret = close(fd);
3183 if (ret != 0) {
3184 if (bam_verbose)
3185 bam_error(CLOSE_FAIL, archive, strerror(errno));
3186 return (BAM_ERROR);
3187 }
3188 fd = -1;
3189
3190 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3191 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3192 (next_session/16));
3193
3194 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3195
3196 ret = exec_cmd(cmdline, &flist);
3197 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3198 if (bam_verbose)
3199 bam_error(MULTI_FAIL, cmdline);
3200 goto out_flist_err;
3201 }
3202 filelist_free(&flist);
3203
3204 (void) unlink(tempname);
3205
3206 if (flushfs(bam_root) != 0)
3207 sync();
3208
3209 if (bam_verbose)
3210 bam_print("boot archive updated successfully\n");
3211
3212 return (BAM_SUCCESS);
3213
3214 out_flist_err:
3215 filelist_free(&flist);
3216 out_err:
3217 if (fd != -1)
3218 (void) close(fd);
3219 if (newfd != -1)
3220 (void) close(newfd);
3221 return (BAM_ERROR);
3222 }
3223
3224 static int
3225 create_x86_archive(char *archive, char *tempname, char *update_dir)
3226 {
3227 int ret;
3228 char cmdline[3 * PATH_MAX + 64];
3229 filelist_t flist = {0};
3230 const char *func = "create_x86_archive()";
3231
3232 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3233 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3234
3235 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3236
3237 ret = exec_cmd(cmdline, &flist);
3238 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3239 bam_error(ARCHIVE_FAIL, cmdline);
3240 dump_errormsg(flist);
3241 filelist_free(&flist);
3242 (void) unlink(tempname);
3243 return (BAM_ERROR);
3244 }
3245
3246 filelist_free(&flist);
3247
3248 if (check_archive(tempname) == BAM_ERROR)
3249 return (BAM_ERROR);
3250
3251 return (do_archive_copy(tempname, archive));
3252 }
3253
3254 static int
3255 mkisofs_archive(char *root, int what)
3256 {
3257 int ret;
3258 char temp[PATH_MAX];
3259 char bootblk[PATH_MAX];
3260 char boot_archive[PATH_MAX];
3261
3262 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3263 ret = snprintf(temp, sizeof (temp),
3264 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3265 get_machine(), getpid());
3266 else
3267 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3268 root, ARCHIVE_PREFIX, get_machine(), getpid());
3269
3270 if (ret >= sizeof (temp))
3271 goto out_path_err;
3272
3273 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3274 ret = snprintf(boot_archive, sizeof (boot_archive),
3275 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3276 ARCHIVE_SUFFIX);
3277 else
3278 ret = snprintf(boot_archive, sizeof (boot_archive),
3279 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3280 ARCHIVE_SUFFIX);
3281
3282 if (ret >= sizeof (boot_archive))
3283 goto out_path_err;
3284
3285 bam_print("updating %s\n", boot_archive);
3286
3287 if (is_flag_on(IS_SPARC_TARGET)) {
3288 ret = snprintf(bootblk, sizeof (bootblk),
3289 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3290 if (ret >= sizeof (bootblk))
3291 goto out_path_err;
3292
3293 ret = create_sparc_archive(boot_archive, temp, bootblk,
3294 get_cachedir(what));
3295 } else {
3296 if (!is_dir_flag_on(what, NO_MULTI)) {
3297 if (bam_verbose)
3298 bam_print("Attempting to extend x86 archive: "
3299 "%s\n", boot_archive);
3300
3301 ret = extend_iso_archive(boot_archive, temp,
3302 get_updatedir(what));
3303 if (ret == BAM_SUCCESS) {
3304 if (bam_verbose)
3305 bam_print("Successfully extended %s\n",
3306 boot_archive);
3307
3308 (void) rmdir_r(get_updatedir(what));
3309 return (BAM_SUCCESS);
3310 }
3311 }
3312 /*
3313 * The boot archive will be recreated from scratch. We get here
3314 * if at least one of these conditions is true:
3315 * - bootadm was called without the -e switch
3316 * - the archive (or the archive cache) doesn't exist
3317 * - archive size is bigger than BA_SIZE_MAX
3318 * - more than COUNT_MAX files need to be updated
3319 * - an error occourred either populating the /updates directory
3320 * or extend_iso_archive() failed
3321 */
3322 if (bam_verbose)
3323 bam_print("Unable to extend %s... rebuilding archive\n",
3324 boot_archive);
3325
3326 if (get_updatedir(what)[0] != '\0')
3327 (void) rmdir_r(get_updatedir(what));
3328
3329
3330 ret = create_x86_archive(boot_archive, temp,
3331 get_cachedir(what));
3332 }
3333
3334 if (ret == BAM_SUCCESS && bam_verbose)
3335 bam_print("Successfully created %s\n", boot_archive);
3336
3337 return (ret);
3338
3339 out_path_err:
3340 bam_error(PATH_TOO_LONG, root);
3341 return (BAM_ERROR);
3342 }
3343
3344 static error_t
3345 create_ramdisk(char *root)
3346 {
3347 char *cmdline, path[PATH_MAX];
3348 size_t len;
3349 struct stat sb;
3350 int ret, what, status = BAM_SUCCESS;
3351
3352 /* If there is mkisofs, use it to create the required archives */
3353 if (is_mkisofs()) {
3354 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3355 if (has_cachedir(what) && is_dir_flag_on(what,
3356 NEED_UPDATE)) {
3357 ret = mkisofs_archive(root, what);
3358 if (ret != 0)
3359 status = BAM_ERROR;
3360 }
3361 }
3362 return (status);
3363 }
3364
3365 /*
3366 * Else setup command args for create_ramdisk.ksh for the UFS archives
3367 */
3368 if (bam_verbose)
3369 bam_print("mkisofs not found, creating UFS archive\n");
3370
3371 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3372 if (stat(path, &sb) != 0) {
3373 bam_error(ARCH_EXEC_MISS, path, strerror(errno));
3374 return (BAM_ERROR);
3375 }
3376
3377 if (is_safe_exec(path) == BAM_ERROR)
3378 return (BAM_ERROR);
3379
3380 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3381 if (bam_alt_platform)
3382 len += strlen(bam_platform) + strlen("-p ");
3383 cmdline = s_calloc(1, len);
3384
3385 if (bam_alt_platform) {
3386 assert(strlen(root) > 1);
3387 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3388 path, bam_platform, root);
3389 /* chop off / at the end */
3390 cmdline[strlen(cmdline) - 1] = '\0';
3391 } else if (strlen(root) > 1) {
3392 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3393 /* chop off / at the end */
3394 cmdline[strlen(cmdline) - 1] = '\0';
3395 } else
3396 (void) snprintf(cmdline, len, "%s", path);
3397
3398 if (exec_cmd(cmdline, NULL) != 0) {
3399 bam_error(ARCHIVE_FAIL, cmdline);
3400 free(cmdline);
3401 return (BAM_ERROR);
3402 }
3403 free(cmdline);
3404 /*
3405 * The existence of the expected archives used to be
3406 * verified here. This check is done in create_ramdisk as
3407 * it needs to be in sync with the altroot operated upon.
3408 */
3409 return (BAM_SUCCESS);
3410 }
3411
3412 /*
3413 * Checks if target filesystem is on a ramdisk
3414 * 1 - is miniroot
3415 * 0 - is not
3416 * When in doubt assume it is not a ramdisk.
3417 */
3418 static int
3419 is_ramdisk(char *root)
3420 {
3421 struct extmnttab mnt;
3422 FILE *fp;
3423 int found;
3424 char mntpt[PATH_MAX];
3425 char *cp;
3426
3427 /*
3428 * There are 3 situations where creating archive is
3429 * of dubious value:
3430 * - create boot_archive on a lofi-mounted boot_archive
3431 * - create it on a ramdisk which is the root filesystem
3432 * - create it on a ramdisk mounted somewhere else
3433 * The first is not easy to detect and checking for it is not
3434 * worth it.
3435 * The other two conditions are handled here
3436 */
3437 fp = fopen(MNTTAB, "r");
3438 if (fp == NULL) {
3439 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3440 return (0);
3441 }
3442
3443 resetmnttab(fp);
3444
3445 /*
3446 * Remove any trailing / from the mount point
3447 */
3448 (void) strlcpy(mntpt, root, sizeof (mntpt));
3449 if (strcmp(root, "/") != 0) {
3450 cp = mntpt + strlen(mntpt) - 1;
3451 if (*cp == '/')
3452 *cp = '\0';
3453 }
3454 found = 0;
3455 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3456 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3457 found = 1;
3458 break;
3459 }
3460 }
3461
3462 if (!found) {
3463 if (bam_verbose)
3464 bam_error(NOT_IN_MNTTAB, mntpt);
3465 (void) fclose(fp);
3466 return (0);
3467 }
3468
3469 if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
3470 if (bam_verbose)
3471 bam_error(IS_RAMDISK, bam_root);
3472 (void) fclose(fp);
3473 return (1);
3474 }
3475
3476 (void) fclose(fp);
3477
3478 return (0);
3479 }
3480
3481 static int
3482 is_boot_archive(char *root)
3483 {
3484 char path[PATH_MAX];
3485 struct stat sb;
3486 int error;
3487 const char *fcn = "is_boot_archive()";
3488
3489 /*
3490 * We can't create an archive without the create_ramdisk script
3491 */
3492 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3493 error = stat(path, &sb);
3494 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3495 if (error == -1) {
3496 if (bam_verbose)
3497 bam_print(FILE_MISS, path);
3498 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
3499 return (0);
3500 }
3501
3502 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
3503 return (1);
3504 }
3505
3506 /*
3507 * Need to call this for anything that operates on the GRUB menu
3508 * In the x86 live upgrade case the directory /boot/grub may be present
3509 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3510 * is to check for the presence of the stage2 binary which is present
3511 * only on GRUB targets (even on x86 boot partitions). Checking for the
3512 * presence of the multiboot binary is not correct as it is not present
3513 * on x86 boot partitions.
3514 */
3515 int
3516 is_grub(const char *root)
3517 {
3518 char path[PATH_MAX];
3519 struct stat sb;
3520 const char *fcn = "is_grub()";
3521
3522 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3523 if (stat(path, &sb) == -1) {
3524 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
3525 return (0);
3526 }
3527
3528 return (1);
3529 }
3530
3531 static int
3532 is_zfs(char *root)
3533 {
3534 struct statvfs vfs;
3535 int ret;
3536 const char *fcn = "is_zfs()";
3537
3538 ret = statvfs(root, &vfs);
3539 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3540 if (ret != 0) {
3541 bam_error(STATVFS_FAIL, root, strerror(errno));
3542 return (0);
3543 }
3544
3545 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3546 BAM_DPRINTF((D_IS_ZFS, fcn, root));
3547 return (1);
3548 } else {
3549 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
3550 return (0);
3551 }
3552 }
3553
3554 static int
3555 is_ufs(char *root)
3556 {
3557 struct statvfs vfs;
3558 int ret;
3559 const char *fcn = "is_ufs()";
3560
3561 ret = statvfs(root, &vfs);
3562 INJECT_ERROR1("STATVFS_UFS", ret = 1);
3563 if (ret != 0) {
3564 bam_error(STATVFS_FAIL, root, strerror(errno));
3565 return (0);
3566 }
3567
3568 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
3569 BAM_DPRINTF((D_IS_UFS, fcn, root));
3570 return (1);
3571 } else {
3572 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
3573 return (0);
3574 }
3575 }
3576
3577 static int
3578 is_pcfs(char *root)
3579 {
3580 struct statvfs vfs;
3581 int ret;
3582 const char *fcn = "is_pcfs()";
3583
3584 ret = statvfs(root, &vfs);
3585 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
3586 if (ret != 0) {
3587 bam_error(STATVFS_FAIL, root, strerror(errno));
3588 return (0);
3589 }
3590
3591 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
3592 BAM_DPRINTF((D_IS_PCFS, fcn, root));
3593 return (1);
3594 } else {
3595 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
3596 return (0);
3597 }
3598 }
3599
3600 static int
3601 is_readonly(char *root)
3602 {
3603 int fd;
3604 int error;
3605 char testfile[PATH_MAX];
3606 const char *fcn = "is_readonly()";
3607
3608 /*
3609 * Using statvfs() to check for a read-only filesystem is not
3610 * reliable. The only way to reliably test is to attempt to
3611 * create a file
3612 */
3613 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
3614 root, BOOTADM_RDONLY_TEST, getpid());
3615
3616 (void) unlink(testfile);
3617
3618 errno = 0;
3619 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
3620 error = errno;
3621 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
3622 if (fd == -1 && error == EROFS) {
3623 BAM_DPRINTF((D_RDONLY_FS, fcn, root));
3624 return (1);
3625 } else if (fd == -1) {
3626 bam_error(RDONLY_TEST_ERROR, root, strerror(error));
3627 }
3628
3629 (void) close(fd);
3630 (void) unlink(testfile);
3631
3632 BAM_DPRINTF((D_RDWR_FS, fcn, root));
3633 return (0);
3634 }
3635
3636 static error_t
3637 update_archive(char *root, char *opt)
3638 {
3639 error_t ret;
3640
3641 assert(root);
3642 assert(opt == NULL);
3643
3644 init_walk_args();
3645 (void) umask(022);
3646
3647 /*
3648 * Never update non-BE root in update_all
3649 */
3650 if (!is_be(root) && bam_update_all)
3651 return (BAM_SUCCESS);
3652 /*
3653 * root must belong to a boot archive based OS,
3654 */
3655 if (!is_boot_archive(root)) {
3656 /*
3657 * Emit message only if not in context of update_all.
3658 * If in update_all, emit only if verbose flag is set.
3659 */
3660 if (!bam_update_all || bam_verbose)
3661 bam_print(NOT_ARCHIVE_BOOT, root);
3662 return (BAM_ERROR);
3663 }
3664
3665 /*
3666 * If smf check is requested when / is writable (can happen
3667 * on first reboot following an upgrade because service
3668 * dependency is messed up), skip the check.
3669 */
3670 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
3671 return (BAM_SUCCESS);
3672
3673 /*
3674 * Don't generate archive on ramdisk.
3675 */
3676 if (is_ramdisk(root))
3677 return (BAM_SUCCESS);
3678
3679 /*
3680 * root must be writable. This check applies to alternate
3681 * root (-R option); bam_root_readonly applies to '/' only.
3682 * The behaviour translates into being the one of a 'check'.
3683 */
3684 if (!bam_smf_check && !bam_check && is_readonly(root)) {
3685 set_flag(RDONLY_FSCHK);
3686 bam_check = 1;
3687 }
3688
3689 /*
3690 * Now check if an update is really needed.
3691 */
3692 ret = update_required(root);
3693
3694 /*
3695 * The check command (-n) is *not* a dry run.
3696 * It only checks if the archive is in sync.
3697 * A readonly filesystem has to be considered an error only if an update
3698 * is required.
3699 */
3700 if (bam_nowrite()) {
3701 if (is_flag_on(RDONLY_FSCHK)) {
3702 bam_check = bam_saved_check;
3703 if (ret > 0)
3704 bam_error(RDONLY_FS, root);
3705 if (bam_update_all)
3706 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
3707 }
3708
3709 bam_exit((ret != 0) ? 1 : 0);
3710 }
3711
3712 if (ret == 1) {
3713 /* create the ramdisk */
3714 ret = create_ramdisk(root);
3715 }
3716
3717 /*
3718 * if the archive is updated, save the new stat data and update the
3719 * timestamp file
3720 */
3721 if (ret == 0 && walk_arg.new_nvlp != NULL) {
3722 savenew(root);
3723 update_timestamp(root);
3724 }
3725
3726 clear_walk_args();
3727
3728 return (ret);
3729 }
3730
3731 static char *
3732 find_root_pool()
3733 {
3734 char *special = get_special("/");
3735 char *p;
3736
3737 if (special == NULL)
3738 return (NULL);
3739
3740 if (*special == '/') {
3741 free(special);
3742 return (NULL);
3743 }
3744
3745 if ((p = strchr(special, '/')) != NULL)
3746 *p = '\0';
3747
3748 return (special);
3749 }
3750
3751 static error_t
3752 synchronize_BE_menu(void)
3753 {
3754 struct stat sb;
3755 char cmdline[PATH_MAX];
3756 char cksum_line[PATH_MAX];
3757 filelist_t flist = {0};
3758 char *old_cksum_str;
3759 char *old_size_str;
3760 char *old_file;
3761 char *curr_cksum_str;
3762 char *curr_size_str;
3763 char *curr_file;
3764 char *pool = NULL;
3765 char *mntpt = NULL;
3766 zfs_mnted_t mnted;
3767 FILE *cfp;
3768 int found;
3769 int ret;
3770 const char *fcn = "synchronize_BE_menu()";
3771
3772 BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
3773
3774 /* Check if findroot enabled LU BE */
3775 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
3776 BAM_DPRINTF((D_NOT_LU_BE, fcn));
3777 return (BAM_SUCCESS);
3778 }
3779
3780 if (stat(LU_MENU_CKSUM, &sb) != 0) {
3781 BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
3782 goto menu_sync;
3783 }
3784
3785 cfp = fopen(LU_MENU_CKSUM, "r");
3786 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
3787 if (cfp == NULL) {
3788 bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
3789 goto menu_sync;
3790 }
3791 BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
3792
3793 found = 0;
3794 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
3795 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
3796 if (found) {
3797 bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
3798 (void) fclose(cfp);
3799 goto menu_sync;
3800 }
3801 found = 1;
3802 }
3803 BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
3804
3805
3806 old_cksum_str = strtok(cksum_line, " \t");
3807 old_size_str = strtok(NULL, " \t");
3808 old_file = strtok(NULL, " \t");
3809
3810 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
3811 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
3812 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
3813 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
3814 bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
3815 goto menu_sync;
3816 }
3817 BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
3818
3819 /* Get checksum of current menu */
3820 pool = find_root_pool();
3821 if (pool) {
3822 mntpt = mount_top_dataset(pool, &mnted);
3823 if (mntpt == NULL) {
3824 bam_error(FAIL_MNT_TOP_DATASET, pool);
3825 free(pool);
3826 return (BAM_ERROR);
3827 }
3828 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
3829 CKSUM, mntpt, GRUB_MENU);
3830 } else {
3831 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
3832 CKSUM, GRUB_MENU);
3833 }
3834 ret = exec_cmd(cmdline, &flist);
3835 if (pool) {
3836 (void) umount_top_dataset(pool, mnted, mntpt);
3837 free(pool);
3838 }
3839 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
3840 if (ret != 0) {
3841 bam_error(MENU_CKSUM_FAIL);
3842 return (BAM_ERROR);
3843 }
3844 BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
3845
3846 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
3847 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3848 bam_error(BAD_CKSUM);
3849 filelist_free(&flist);
3850 return (BAM_ERROR);
3851 }
3852 BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
3853
3854 curr_cksum_str = strtok(flist.head->line, " \t");
3855 curr_size_str = strtok(NULL, " \t");
3856 curr_file = strtok(NULL, " \t");
3857
3858 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
3859 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
3860 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
3861 if (curr_cksum_str == NULL || curr_size_str == NULL ||
3862 curr_file == NULL) {
3863 bam_error(BAD_CKSUM_PARSE);
3864 filelist_free(&flist);
3865 return (BAM_ERROR);
3866 }
3867 BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
3868
3869 if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
3870 strcmp(old_size_str, curr_size_str) == 0 &&
3871 strcmp(old_file, curr_file) == 0) {
3872 filelist_free(&flist);
3873 BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
3874 return (BAM_SUCCESS);
3875 }
3876
3877 filelist_free(&flist);
3878
3879 /* cksum doesn't match - the menu has changed */
3880 BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
3881
3882 menu_sync:
3883 bam_print(PROP_GRUB_MENU);
3884
3885 (void) snprintf(cmdline, sizeof (cmdline),
3886 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
3887 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
3888 ret = exec_cmd(cmdline, NULL);
3889 INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
3890 if (ret != 0) {
3891 bam_error(MENU_PROP_FAIL);
3892 return (BAM_ERROR);
3893 }
3894 BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
3895
3896 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
3897 GRUB_MENU, GRUB_BACKUP_MENU);
3898 ret = exec_cmd(cmdline, NULL);
3899 INJECT_ERROR1("CREATE_BACKUP", ret = 1);
3900 if (ret != 0) {
3901 bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
3902 return (BAM_ERROR);
3903 }
3904 BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
3905
3906 (void) snprintf(cmdline, sizeof (cmdline),
3907 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
3908 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
3909 ret = exec_cmd(cmdline, NULL);
3910 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
3911 if (ret != 0) {
3912 bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
3913 return (BAM_ERROR);
3914 }
3915 BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
3916
3917 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
3918 CKSUM, GRUB_MENU, LU_MENU_CKSUM);
3919 ret = exec_cmd(cmdline, NULL);
3920 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
3921 if (ret != 0) {
3922 bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
3923 return (BAM_ERROR);
3924 }
3925 BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
3926
3927 (void) snprintf(cmdline, sizeof (cmdline),
3928 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
3929 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
3930 ret = exec_cmd(cmdline, NULL);
3931 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
3932 if (ret != 0) {
3933 bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
3934 return (BAM_ERROR);
3935 }
3936 BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
3937
3938 return (BAM_SUCCESS);
3939 }
3940
3941 static error_t
3942 update_all(char *root, char *opt)
3943 {
3944 struct extmnttab mnt;
3945 struct stat sb;
3946 FILE *fp;
3947 char multibt[PATH_MAX];
3948 char creatram[PATH_MAX];
3949 error_t ret = BAM_SUCCESS;
3950
3951 assert(root);
3952 assert(opt == NULL);
3953
3954 if (bam_rootlen != 1 || *root != '/') {
3955 elide_trailing_slash(root, multibt, sizeof (multibt));
3956 bam_error(ALT_ROOT_INVALID, multibt);
3957 return (BAM_ERROR);
3958 }
3959
3960 /*
3961 * First update archive for current root
3962 */
3963 if (update_archive(root, opt) != BAM_SUCCESS)
3964 ret = BAM_ERROR;
3965
3966 if (ret == BAM_ERROR)
3967 goto out;
3968
3969 /*
3970 * Now walk the mount table, performing archive update
3971 * for all mounted Newboot root filesystems
3972 */
3973 fp = fopen(MNTTAB, "r");
3974 if (fp == NULL) {
3975 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3976 ret = BAM_ERROR;
3977 goto out;
3978 }
3979
3980 resetmnttab(fp);
3981
3982 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3983 if (mnt.mnt_special == NULL)
3984 continue;
3985 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
3986 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
3987 continue;
3988 if (strcmp(mnt.mnt_mountp, "/") == 0)
3989 continue;
3990
3991 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
3992 mnt.mnt_mountp, CREATE_RAMDISK);
3993
3994 if (stat(creatram, &sb) == -1)
3995 continue;
3996
3997 /*
3998 * We put a trailing slash to be consistent with root = "/"
3999 * case, such that we don't have to print // in some cases.
4000 */
4001 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4002 mnt.mnt_mountp);
4003 bam_rootlen = strlen(rootbuf);
4004
4005 /*
4006 * It's possible that other mounts may be an alternate boot
4007 * architecture, so check it again.
4008 */
4009 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4010 (update_archive(rootbuf, opt) != BAM_SUCCESS))
4011 ret = BAM_ERROR;
4012 }
4013
4014 (void) fclose(fp);
4015
4016 out:
4017 /*
4018 * We no longer use biosdev for Live Upgrade. Hence
4019 * there is no need to defer (to shutdown time) any fdisk
4020 * updates
4021 */
4022 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4023 bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
4024 }
4025
4026 /*
4027 * If user has updated menu in current BE, propagate the
4028 * updates to all BEs.
4029 */
4030 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4031 ret = BAM_ERROR;
4032
4033 return (ret);
4034 }
4035
4036 static void
4037 append_line(menu_t *mp, line_t *lp)
4038 {
4039 if (mp->start == NULL) {
4040 mp->start = lp;
4041 } else {
4042 mp->end->next = lp;
4043 lp->prev = mp->end;
4044 }
4045 mp->end = lp;
4046 }
4047
4048 void
4049 unlink_line(menu_t *mp, line_t *lp)
4050 {
4051 /* unlink from list */
4052 if (lp->prev)
4053 lp->prev->next = lp->next;
4054 else
4055 mp->start = lp->next;
4056 if (lp->next)
4057 lp->next->prev = lp->prev;
4058 else
4059 mp->end = lp->prev;
4060 }
4061
4062 static entry_t *
4063 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4064 {
4065 entry_t *ent, *prev;
4066 const char *fcn = "boot_entry_new()";
4067
4068 assert(mp);
4069 assert(start);
4070 assert(end);
4071
4072 ent = s_calloc(1, sizeof (entry_t));
4073 BAM_DPRINTF((D_ENTRY_NEW, fcn));
4074 ent->start = start;
4075 ent->end = end;
4076
4077 if (mp->entries == NULL) {
4078 mp->entries = ent;
4079 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
4080 return (ent);
4081 }
4082
4083 prev = mp->entries;
4084 while (prev->next)
4085 prev = prev->next;
4086 prev->next = ent;
4087 ent->prev = prev;
4088 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
4089 return (ent);
4090 }
4091
4092 static void
4093 boot_entry_addline(entry_t *ent, line_t *lp)
4094 {
4095 if (ent)
4096 ent->end = lp;
4097 }
4098
4099 /*
4100 * Check whether cmd matches the one indexed by which, and whether arg matches
4101 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4102 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4103 * strstr(), so it can be a partial match.
4104 */
4105 static int
4106 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4107 {
4108 int ret;
4109 const char *fcn = "check_cmd()";
4110
4111 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
4112
4113 if (cmd != NULL) {
4114 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4115 (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4116 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
4117 fcn, cmd, menu_cmds[which]));
4118 return (0);
4119 }
4120 ret = (strstr(arg, str) != NULL);
4121 } else
4122 ret = 0;
4123
4124 if (ret) {
4125 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
4126 } else {
4127 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
4128 }
4129
4130 return (ret);
4131 }
4132
4133 static error_t
4134 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4135 {
4136 const char *fcn = "kernel_parser()";
4137
4138 assert(entry);
4139 assert(cmd);
4140 assert(arg);
4141
4142 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4143 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4144 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
4145 return (BAM_ERROR);
4146 }
4147
4148 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4149 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
4150 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4151 } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4152 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4153 BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
4154 entry->flags |= BAM_ENTRY_DBOOT;
4155 } else if (strncmp(arg, DIRECT_BOOT_64,
4156 sizeof (DIRECT_BOOT_64) - 1) == 0) {
4157 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
4158 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4159 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4160 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4161 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
4162 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4163 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4164 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4165 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg));
4166 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4167 | BAM_ENTRY_32BIT;
4168 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4169 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4170 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg));
4171 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4172 | BAM_ENTRY_64BIT;
4173 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4174 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
4175 entry->flags |= BAM_ENTRY_MULTIBOOT;
4176 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4177 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4178 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
4179 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4180 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4181 BAM_DPRINTF((D_SET_HV, fcn, arg));
4182 entry->flags |= BAM_ENTRY_HV;
4183 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4184 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
4185 return (BAM_ERROR);
4186 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4187 strstr(arg, UNIX_SPACE)) {
4188 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4189 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4190 strstr(arg, AMD_UNIX_SPACE)) {
4191 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4192 } else {
4193 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
4194 bam_error(UNKNOWN_KERNEL_LINE, linenum);
4195 return (BAM_ERROR);
4196 }
4197
4198 return (BAM_SUCCESS);
4199 }
4200
4201 static error_t
4202 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4203 {
4204 const char *fcn = "module_parser()";
4205
4206 assert(entry);
4207 assert(cmd);
4208 assert(arg);
4209
4210 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4211 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4212 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
4213 return (BAM_ERROR);
4214 }
4215
4216 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4217 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4218 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4219 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4220 strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4221 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4222 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4223 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4224 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4225 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
4226 return (BAM_SUCCESS);
4227 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4228 !(entry->flags & BAM_ENTRY_LU)) {
4229 /* don't emit warning for hand entries */
4230 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
4231 return (BAM_ERROR);
4232 } else {
4233 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
4234 bam_error(UNKNOWN_MODULE_LINE, linenum);
4235 return (BAM_ERROR);
4236 }
4237 }
4238
4239 /*
4240 * A line in menu.lst looks like
4241 * [ ]*<cmd>[ \t=]*<arg>*
4242 */
4243 static void
4244 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4245 {
4246 /*
4247 * save state across calls. This is so that
4248 * header gets the right entry# after title has
4249 * been processed
4250 */
4251 static line_t *prev = NULL;
4252 static entry_t *curr_ent = NULL;
4253 static int in_liveupgrade = 0;
4254 static int is_libbe_ent = 0;
4255
4256 line_t *lp;
4257 char *cmd, *sep, *arg;
4258 char save, *cp, *line;
4259 menu_flag_t flag = BAM_INVALID;
4260 const char *fcn = "line_parser()";
4261
4262 if (str == NULL) {
4263 return;
4264 }
4265
4266 /*
4267 * First save a copy of the entire line.
4268 * We use this later to set the line field.
4269 */
4270 line = s_strdup(str);
4271
4272 /* Eat up leading whitespace */
4273 while (*str == ' ' || *str == '\t')
4274 str++;
4275
4276 if (*str == '#') { /* comment */
4277 cmd = s_strdup("#");
4278 sep = NULL;
4279 arg = s_strdup(str + 1);
4280 flag = BAM_COMMENT;
4281 if (strstr(arg, BAM_LU_HDR) != NULL) {
4282 in_liveupgrade = 1;
4283 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4284 in_liveupgrade = 0;
4285 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4286 is_libbe_ent = 1;
4287 }
4288 } else if (*str == '\0') { /* blank line */
4289 cmd = sep = arg = NULL;
4290 flag = BAM_EMPTY;
4291 } else {
4292 /*
4293 * '=' is not a documented separator in grub syntax.
4294 * However various development bits use '=' as a
4295 * separator. In addition, external users also
4296 * use = as a separator. So we will allow that usage.
4297 */
4298 cp = str;
4299 while (*str != ' ' && *str != '\t' && *str != '=') {
4300 if (*str == '\0') {
4301 cmd = s_strdup(cp);
4302 sep = arg = NULL;
4303 break;
4304 }
4305 str++;
4306 }
4307
4308 if (*str != '\0') {
4309 save = *str;
4310 *str = '\0';
4311 cmd = s_strdup(cp);
4312 *str = save;
4313
4314 str++;
4315 save = *str;
4316 *str = '\0';
4317 sep = s_strdup(str - 1);
4318 *str = save;
4319
4320 while (*str == ' ' || *str == '\t')
4321 str++;
4322 if (*str == '\0')
4323 arg = NULL;
4324 else
4325 arg = s_strdup(str);
4326 }
4327 }
4328
4329 lp = s_calloc(1, sizeof (line_t));
4330
4331 lp->cmd = cmd;
4332 lp->sep = sep;
4333 lp->arg = arg;
4334 lp->line = line;
4335 lp->lineNum = ++(*lineNum);
4336 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4337 lp->entryNum = ++(*entryNum);
4338 lp->flags = BAM_TITLE;
4339 if (prev && prev->flags == BAM_COMMENT &&
4340 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4341 prev->entryNum = lp->entryNum;
4342 curr_ent = boot_entry_new(mp, prev, lp);
4343 curr_ent->flags |= BAM_ENTRY_BOOTADM;
4344 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
4345 } else {
4346 curr_ent = boot_entry_new(mp, lp, lp);
4347 if (in_liveupgrade) {
4348 curr_ent->flags |= BAM_ENTRY_LU;
4349 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
4350 }
4351 }
4352 curr_ent->entryNum = *entryNum;
4353 } else if (flag != BAM_INVALID) {
4354 /*
4355 * For header comments, the entry# is "fixed up"
4356 * by the subsequent title
4357 */
4358 lp->entryNum = *entryNum;
4359 lp->flags = flag;
4360 } else {
4361 lp->entryNum = *entryNum;
4362
4363 if (*entryNum == ENTRY_INIT) {
4364 lp->flags = BAM_GLOBAL;
4365 } else {
4366 lp->flags = BAM_ENTRY;
4367
4368 if (cmd && arg) {
4369 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4370 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
4371 curr_ent->flags |= BAM_ENTRY_ROOT;
4372 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4373 == 0) {
4374 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
4375 arg));
4376 curr_ent->flags |= BAM_ENTRY_FINDROOT;
4377 } else if (strcmp(cmd,
4378 menu_cmds[CHAINLOADER_CMD]) == 0) {
4379 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
4380 arg));
4381 curr_ent->flags |=
4382 BAM_ENTRY_CHAINLOADER;
4383 } else if (kernel_parser(curr_ent, cmd, arg,
4384 lp->lineNum) != BAM_SUCCESS) {
4385 (void) module_parser(curr_ent, cmd,
4386 arg, lp->lineNum);
4387 }
4388 }
4389 }
4390 }
4391
4392 /* record default, old default, and entry line ranges */
4393 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4394 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4395 mp->curdefault = lp;
4396 } else if (lp->flags == BAM_COMMENT &&
4397 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4398 mp->olddefault = lp;
4399 } else if (lp->flags == BAM_COMMENT &&
4400 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4401 mp->old_rc_default = lp;
4402 } else if (lp->flags == BAM_ENTRY ||
4403 (lp->flags == BAM_COMMENT &&
4404 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4405 if (is_libbe_ent) {
4406 curr_ent->flags |= BAM_ENTRY_LIBBE;
4407 is_libbe_ent = 0;
4408 }
4409
4410 boot_entry_addline(curr_ent, lp);
4411 }
4412 append_line(mp, lp);
4413
4414 prev = lp;
4415 }
4416
4417 void
4418 update_numbering(menu_t *mp)
4419 {
4420 int lineNum;
4421 int entryNum;
4422 int old_default_value;
4423 line_t *lp, *prev, *default_lp, *default_entry;
4424 char buf[PATH_MAX];
4425
4426 if (mp->start == NULL) {
4427 return;
4428 }
4429
4430 lineNum = LINE_INIT;
4431 entryNum = ENTRY_INIT;
4432 old_default_value = ENTRY_INIT;
4433 lp = default_lp = default_entry = NULL;
4434
4435 prev = NULL;
4436 for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4437 lp->lineNum = ++lineNum;
4438
4439 /*
4440 * Get the value of the default command
4441 */
4442 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4443 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4444 lp->arg) {
4445 old_default_value = atoi(lp->arg);
4446 default_lp = lp;
4447 }
4448
4449 /*
4450 * If not a booting entry, nothing else to fix for this
4451 * entry
4452 */
4453 if (lp->entryNum == ENTRY_INIT)
4454 continue;
4455
4456 /*
4457 * Record the position of the default entry.
4458 * The following works because global
4459 * commands like default and timeout should precede
4460 * actual boot entries, so old_default_value
4461 * is already known (or default cmd is missing).
4462 */
4463 if (default_entry == NULL &&
4464 old_default_value != ENTRY_INIT &&
4465 lp->entryNum == old_default_value) {
4466 default_entry = lp;
4467 }
4468
4469 /*
4470 * Now fixup the entry number
4471 */
4472 if (lp->cmd != NULL &&
4473 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4474 lp->entryNum = ++entryNum;
4475 /* fixup the bootadm header */
4476 if (prev && prev->flags == BAM_COMMENT &&
4477 prev->arg &&
4478 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4479 prev->entryNum = lp->entryNum;
4480 }
4481 } else {
4482 lp->entryNum = entryNum;
4483 }
4484 }
4485
4486 /*
4487 * No default command in menu, simply return
4488 */
4489 if (default_lp == NULL) {
4490 return;
4491 }
4492
4493 free(default_lp->arg);
4494 free(default_lp->line);
4495
4496 if (default_entry == NULL) {
4497 default_lp->arg = s_strdup("0");
4498 } else {
4499 (void) snprintf(buf, sizeof (buf), "%d",
4500 default_entry->entryNum);
4501 default_lp->arg = s_strdup(buf);
4502 }
4503
4504 /*
4505 * The following is required since only the line field gets
4506 * written back to menu.lst
4507 */
4508 (void) snprintf(buf, sizeof (buf), "%s%s%s",
4509 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4510 default_lp->line = s_strdup(buf);
4511 }
4512
4513
4514 static menu_t *
4515 menu_read(char *menu_path)
4516 {
4517 FILE *fp;
4518 char buf[BAM_MAXLINE], *cp;
4519 menu_t *mp;
4520 int line, entry, len, n;
4521
4522 mp = s_calloc(1, sizeof (menu_t));
4523
4524 fp = fopen(menu_path, "r");
4525 if (fp == NULL) { /* Let the caller handle this error */
4526 free(mp);
4527 return (NULL);
4528 }
4529
4530 /* Note: GRUB boot entry number starts with 0 */
4531 line = LINE_INIT;
4532 entry = ENTRY_INIT;
4533 cp = buf;
4534 len = sizeof (buf);
4535 while (s_fgets(cp, len, fp) != NULL) {
4536 n = strlen(cp);
4537 if (cp[n - 1] == '\\') {
4538 len -= n - 1;
4539 assert(len >= 2);
4540 cp += n - 1;
4541 continue;
4542 }
4543 line_parser(mp, buf, &line, &entry);
4544 cp = buf;
4545 len = sizeof (buf);
4546 }
4547
4548 if (fclose(fp) == EOF) {
4549 bam_error(CLOSE_FAIL, menu_path, strerror(errno));
4550 }
4551
4552 return (mp);
4553 }
4554
4555 static error_t
4556 selector(menu_t *mp, char *opt, int *entry, char **title)
4557 {
4558 char *eq;
4559 char *opt_dup;
4560 int entryNum;
4561
4562 assert(mp);
4563 assert(mp->start);
4564 assert(opt);
4565
4566 opt_dup = s_strdup(opt);
4567
4568 if (entry)
4569 *entry = ENTRY_INIT;
4570 if (title)
4571 *title = NULL;
4572
4573 eq = strchr(opt_dup, '=');
4574 if (eq == NULL) {
4575 bam_error(INVALID_OPT, opt);
4576 free(opt_dup);
4577 return (BAM_ERROR);
4578 }
4579
4580 *eq = '\0';
4581 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
4582 assert(mp->end);
4583 entryNum = s_strtol(eq + 1);
4584 if (entryNum < 0 || entryNum > mp->end->entryNum) {
4585 bam_error(INVALID_ENTRY, eq + 1);
4586 free(opt_dup);
4587 return (BAM_ERROR);
4588 }
4589 *entry = entryNum;
4590 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
4591 *title = opt + (eq - opt_dup) + 1;
4592 } else {
4593 bam_error(INVALID_OPT, opt);
4594 free(opt_dup);
4595 return (BAM_ERROR);
4596 }
4597
4598 free(opt_dup);
4599 return (BAM_SUCCESS);
4600 }
4601
4602 /*
4603 * If invoked with no titles/entries (opt == NULL)
4604 * only title lines in file are printed.
4605 *
4606 * If invoked with a title or entry #, all
4607 * lines in *every* matching entry are listed
4608 */
4609 static error_t
4610 list_entry(menu_t *mp, char *menu_path, char *opt)
4611 {
4612 line_t *lp;
4613 int entry = ENTRY_INIT;
4614 int found;
4615 char *title = NULL;
4616
4617 assert(mp);
4618 assert(menu_path);
4619
4620 /* opt is optional */
4621 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
4622 opt ? opt : "<NULL>"));
4623
4624 if (mp->start == NULL) {
4625 bam_error(NO_MENU, menu_path);
4626 return (BAM_ERROR);
4627 }
4628
4629 if (opt != NULL) {
4630 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
4631 return (BAM_ERROR);
4632 }
4633 assert((entry != ENTRY_INIT) ^ (title != NULL));
4634 } else {
4635 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
4636 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
4637 }
4638
4639 found = 0;
4640 for (lp = mp->start; lp; lp = lp->next) {
4641 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
4642 continue;
4643 if (opt == NULL && lp->flags == BAM_TITLE) {
4644 bam_print(PRINT_TITLE, lp->entryNum,
4645 lp->arg);
4646 found = 1;
4647 continue;
4648 }
4649 if (entry != ENTRY_INIT && lp->entryNum == entry) {
4650 bam_print(PRINT, lp->line);
4651 found = 1;
4652 continue;
4653 }
4654
4655 /*
4656 * We set the entry value here so that all lines
4657 * in entry get printed. If we subsequently match
4658 * title in other entries, all lines in those
4659 * entries get printed as well.
4660 */
4661 if (title && lp->flags == BAM_TITLE && lp->arg &&
4662 strncmp(title, lp->arg, strlen(title)) == 0) {
4663 bam_print(PRINT, lp->line);
4664 entry = lp->entryNum;
4665 found = 1;
4666 continue;
4667 }
4668 }
4669
4670 if (!found) {
4671 bam_error(NO_MATCH_ENTRY);
4672 return (BAM_ERROR);
4673 }
4674
4675 return (BAM_SUCCESS);
4676 }
4677
4678 int
4679 add_boot_entry(menu_t *mp,
4680 char *title,
4681 char *findroot,
4682 char *kernel,
4683 char *mod_kernel,
4684 char *module,
4685 char *bootfs)
4686 {
4687 int lineNum;
4688 int entryNum;
4689 char linebuf[BAM_MAXLINE];
4690 menu_cmd_t k_cmd;
4691 menu_cmd_t m_cmd;
4692 const char *fcn = "add_boot_entry()";
4693
4694 assert(mp);
4695
4696 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
4697 if (findroot == NULL) {
4698 bam_error(NULL_FINDROOT);
4699 return (BAM_ERROR);
4700 }
4701
4702 if (title == NULL) {
4703 title = "Solaris"; /* default to Solaris */
4704 }
4705 if (kernel == NULL) {
4706 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
4707 return (BAM_ERROR);
4708 }
4709 if (module == NULL) {
4710 if (bam_direct != BAM_DIRECT_DBOOT) {
4711 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
4712 return (BAM_ERROR);
4713 }
4714
4715 /* Figure the commands out from the kernel line */
4716 if (strstr(kernel, "$ISADIR") != NULL) {
4717 module = DIRECT_BOOT_ARCHIVE;
4718 } else if (strstr(kernel, "amd64") != NULL) {
4719 module = DIRECT_BOOT_ARCHIVE_64;
4720 } else {
4721 module = DIRECT_BOOT_ARCHIVE_32;
4722 }
4723 }
4724
4725 k_cmd = KERNEL_DOLLAR_CMD;
4726 m_cmd = MODULE_DOLLAR_CMD;
4727
4728 if (mp->start) {
4729 lineNum = mp->end->lineNum;
4730 entryNum = mp->end->entryNum;
4731 } else {
4732 lineNum = LINE_INIT;
4733 entryNum = ENTRY_INIT;
4734 }
4735
4736 /*
4737 * No separator for comment (HDR/FTR) commands
4738 * The syntax for comments is #<comment>
4739 */
4740 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
4741 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
4742 line_parser(mp, linebuf, &lineNum, &entryNum);
4743
4744 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4745 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
4746 line_parser(mp, linebuf, &lineNum, &entryNum);
4747
4748 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4749 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
4750 line_parser(mp, linebuf, &lineNum, &entryNum);
4751 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
4752
4753 if (bootfs != NULL) {
4754 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4755 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
4756 line_parser(mp, linebuf, &lineNum, &entryNum);
4757 }
4758
4759 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4760 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
4761 line_parser(mp, linebuf, &lineNum, &entryNum);
4762
4763 if (mod_kernel != NULL) {
4764 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4765 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
4766 line_parser(mp, linebuf, &lineNum, &entryNum);
4767 }
4768
4769 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4770 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
4771 line_parser(mp, linebuf, &lineNum, &entryNum);
4772
4773 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
4774 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
4775 line_parser(mp, linebuf, &lineNum, &entryNum);
4776
4777 return (entryNum);
4778 }
4779
4780 error_t
4781 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
4782 {
4783 line_t *lp;
4784 line_t *freed;
4785 entry_t *ent;
4786 entry_t *tmp;
4787 int deleted = 0;
4788 const char *fcn = "delete_boot_entry()";
4789
4790 assert(entryNum != ENTRY_INIT);
4791
4792 tmp = NULL;
4793
4794 ent = mp->entries;
4795 while (ent) {
4796 lp = ent->start;
4797
4798 /*
4799 * Check entry number and make sure it's a modifiable entry.
4800 *
4801 * Guidelines:
4802 * + We can modify a bootadm-created entry
4803 * + We can modify a libbe-created entry
4804 */
4805 if ((lp->flags != BAM_COMMENT &&
4806 (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
4807 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
4808 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
4809 ent = ent->next;
4810 continue;
4811 }
4812
4813 /* free the entry content */
4814 do {
4815 freed = lp;
4816 lp = lp->next; /* prev stays the same */
4817 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
4818 unlink_line(mp, freed);
4819 line_free(freed);
4820 } while (freed != ent->end);
4821
4822 /* free the entry_t structure */
4823 assert(tmp == NULL);
4824 tmp = ent;
4825 ent = ent->next;
4826 if (tmp->prev)
4827 tmp->prev->next = ent;
4828 else
4829 mp->entries = ent;
4830 if (ent)
4831 ent->prev = tmp->prev;
4832 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
4833 free(tmp);
4834 tmp = NULL;
4835 deleted = 1;
4836 }
4837
4838 assert(tmp == NULL);
4839
4840 if (!deleted && entryNum != ALL_ENTRIES) {
4841 if (quiet == DBE_PRINTERR)
4842 bam_error(NO_BOOTADM_MATCH);
4843 return (BAM_ERROR);
4844 }
4845
4846 /*
4847 * Now that we have deleted an entry, update
4848 * the entry numbering and the default cmd.
4849 */
4850 update_numbering(mp);
4851
4852 return (BAM_SUCCESS);
4853 }
4854
4855 static error_t
4856 delete_all_entries(menu_t *mp, char *dummy, char *opt)
4857 {
4858 assert(mp);
4859 assert(dummy == NULL);
4860 assert(opt == NULL);
4861
4862 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
4863
4864 if (mp->start == NULL) {
4865 bam_print(EMPTY_MENU);
4866 return (BAM_SUCCESS);
4867 }
4868
4869 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
4870 return (BAM_ERROR);
4871 }
4872
4873 return (BAM_WRITE);
4874 }
4875
4876 static FILE *
4877 create_diskmap(char *osroot)
4878 {
4879 FILE *fp;
4880 char cmd[PATH_MAX + 16];
4881 char path[PATH_MAX];
4882 const char *fcn = "create_diskmap()";
4883
4884 /* make sure we have a map file */
4885 fp = fopen(GRUBDISK_MAP, "r");
4886 if (fp == NULL) {
4887 int ret;
4888
4889 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
4890 CREATE_DISKMAP);
4891 if (ret >= sizeof (path)) {
4892 bam_error(PATH_TOO_LONG, osroot);
4893 return (NULL);
4894 }
4895 if (is_safe_exec(path) == BAM_ERROR)
4896 return (NULL);
4897
4898 (void) snprintf(cmd, sizeof (cmd),
4899 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
4900 if (exec_cmd(cmd, NULL) != 0)
4901 return (NULL);
4902 fp = fopen(GRUBDISK_MAP, "r");
4903 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
4904 if (fp) {
4905 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
4906 } else {
4907 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
4908 }
4909 }
4910 return (fp);
4911 }
4912
4913 #define SECTOR_SIZE 512
4914
4915 static int
4916 get_partition(char *device)
4917 {
4918 int i, fd, is_pcfs, partno = -1;
4919 struct mboot *mboot;
4920 char boot_sect[SECTOR_SIZE];
4921 char *wholedisk, *slice;
4922 #ifdef i386
4923 ext_part_t *epp;
4924 uint32_t secnum, numsec;
4925 int rval, pno, ext_partno = -1;
4926 #endif
4927
4928 /* form whole disk (p0) */
4929 slice = device + strlen(device) - 2;
4930 is_pcfs = (*slice != 's');
4931 if (!is_pcfs)
4932 *slice = '\0';
4933 wholedisk = s_calloc(1, strlen(device) + 3);
4934 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
4935 if (!is_pcfs)
4936 *slice = 's';
4937
4938 /* read boot sector */
4939 fd = open(wholedisk, O_RDONLY);
4940 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
4941 return (partno);
4942 }
4943 (void) close(fd);
4944
4945 #ifdef i386
4946 /* Read/Initialize extended partition information */
4947 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
4948 != FDISK_SUCCESS) {
4949 switch (rval) {
4950 /*
4951 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
4952 * be considered as soft errors and hence
4953 * we do not return
4954 */
4955 case FDISK_EBADLOGDRIVE:
4956 break;
4957 case FDISK_ENOLOGDRIVE:
4958 break;
4959 case FDISK_EBADMAGIC:
4960 /*FALLTHROUGH*/
4961 default:
4962 free(wholedisk);
4963 libfdisk_fini(&epp);
4964 return (partno);
4965 }
4966 }
4967 #endif
4968 free(wholedisk);
4969
4970 /* parse fdisk table */
4971 mboot = (struct mboot *)((void *)boot_sect);
4972 for (i = 0; i < FD_NUMPART; i++) {
4973 struct ipart *part =
4974 (struct ipart *)(uintptr_t)mboot->parts + i;
4975 if (is_pcfs) { /* looking for solaris boot part */
4976 if (part->systid == 0xbe) {
4977 partno = i;
4978 break;
4979 }
4980 } else { /* look for solaris partition, old and new */
4981 #ifdef i386
4982 if ((part->systid == SUNIXOS &&
4983 (fdisk_is_linux_swap(epp, part->relsect,
4984 NULL) != 0)) || part->systid == SUNIXOS2) {
4985 #else
4986 if (part->systid == SUNIXOS ||
4987 part->systid == SUNIXOS2) {
4988 #endif
4989 partno = i;
4990 break;
4991 }
4992
4993 #ifdef i386
4994 if (fdisk_is_dos_extended(part->systid))
4995 ext_partno = i;
4996 #endif
4997 }
4998 }
4999 #ifdef i386
5000 /* If no primary solaris partition, check extended partition */
5001 if ((partno == -1) && (ext_partno != -1)) {
5002 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5003 if (rval == FDISK_SUCCESS) {
5004 partno = pno - 1;
5005 }
5006 }
5007 libfdisk_fini(&epp);
5008 #endif
5009 return (partno);
5010 }
5011
5012 char *
5013 get_grubroot(char *osroot, char *osdev, char *menu_root)
5014 {
5015 char *grubroot; /* (hd#,#,#) */
5016 char *slice;
5017 char *grubhd;
5018 int fdiskpart;
5019 int found = 0;
5020 char *devname;
5021 char *ctdname = strstr(osdev, "dsk/");
5022 char linebuf[PATH_MAX];
5023 FILE *fp;
5024
5025 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5026 if (ctdname == NULL) {
5027 bam_error(INVALID_DEV_DSK, osdev);
5028 return (NULL);
5029 }
5030
5031 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5032 /* menu bears no resemblance to our reality */
5033 bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev);
5034 return (NULL);
5035 }
5036
5037 ctdname += strlen("dsk/");
5038 slice = strrchr(ctdname, 's');
5039 if (slice)
5040 *slice = '\0';
5041
5042 fp = create_diskmap(osroot);
5043 if (fp == NULL) {
5044 bam_error(DISKMAP_FAIL, osroot);
5045 return (NULL);
5046 }
5047
5048 rewind(fp);
5049 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5050 grubhd = strtok(linebuf, " \t\n");
5051 if (grubhd)
5052 devname = strtok(NULL, " \t\n");
5053 else
5054 devname = NULL;
5055 if (devname && strcmp(devname, ctdname) == 0) {
5056 found = 1;
5057 break;
5058 }
5059 }
5060
5061 if (slice)
5062 *slice = 's';
5063
5064 (void) fclose(fp);
5065 fp = NULL;
5066
5067 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5068 if (found == 0) {
5069 bam_error(BIOSDEV_SKIP, osdev);
5070 return (NULL);
5071 }
5072
5073 fdiskpart = get_partition(osdev);
5074 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1);
5075 if (fdiskpart == -1) {
5076 bam_error(FDISKPART_FAIL, osdev);
5077 return (NULL);
5078 }
5079
5080 grubroot = s_calloc(1, 10);
5081 if (slice) {
5082 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5083 grubhd, fdiskpart, slice[1] + 'a' - '0');
5084 } else
5085 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5086 grubhd, fdiskpart);
5087
5088 assert(fp == NULL);
5089 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5090 return (grubroot);
5091 }
5092
5093 static char *
5094 find_primary_common(char *mntpt, char *fstype)
5095 {
5096 char signdir[PATH_MAX];
5097 char tmpsign[MAXNAMELEN + 1];
5098 char *lu;
5099 char *ufs;
5100 char *zfs;
5101 DIR *dirp = NULL;
5102 struct dirent *entp;
5103 struct stat sb;
5104 const char *fcn = "find_primary_common()";
5105
5106 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5107 mntpt, GRUBSIGN_DIR);
5108
5109 if (stat(signdir, &sb) == -1) {
5110 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
5111 return (NULL);
5112 }
5113
5114 dirp = opendir(signdir);
5115 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5116 if (dirp == NULL) {
5117 bam_error(OPENDIR_FAILED, signdir, strerror(errno));
5118 return (NULL);
5119 }
5120
5121 ufs = zfs = lu = NULL;
5122
5123 while (entp = readdir(dirp)) {
5124 if (strcmp(entp->d_name, ".") == 0 ||
5125 strcmp(entp->d_name, "..") == 0)
5126 continue;
5127
5128 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5129
5130 if (lu == NULL &&
5131 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5132 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5133 lu = s_strdup(tmpsign);
5134 }
5135
5136 if (ufs == NULL &&
5137 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5138 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5139 ufs = s_strdup(tmpsign);
5140 }
5141
5142 if (zfs == NULL &&
5143 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5144 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5145 zfs = s_strdup(tmpsign);
5146 }
5147 }
5148
5149 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
5150 zfs ? zfs : "NULL",
5151 ufs ? ufs : "NULL",
5152 lu ? lu : "NULL"));
5153
5154 if (dirp) {
5155 (void) closedir(dirp);
5156 dirp = NULL;
5157 }
5158
5159 if (strcmp(fstype, "ufs") == 0 && zfs) {
5160 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5161 free(zfs);
5162 zfs = NULL;
5163 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5164 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5165 free(ufs);
5166 ufs = NULL;
5167 }
5168
5169 assert(dirp == NULL);
5170
5171 /* For now, we let Live Upgrade take care of its signature itself */
5172 if (lu) {
5173 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5174 free(lu);
5175 lu = NULL;
5176 }
5177
5178 return (zfs ? zfs : ufs);
5179 }
5180
5181 static char *
5182 find_backup_common(char *mntpt, char *fstype)
5183 {
5184 FILE *bfp = NULL;
5185 char tmpsign[MAXNAMELEN + 1];
5186 char backup[PATH_MAX];
5187 char *ufs;
5188 char *zfs;
5189 char *lu;
5190 int error;
5191 const char *fcn = "find_backup_common()";
5192
5193 /*
5194 * We didn't find it in the primary directory.
5195 * Look at the backup
5196 */
5197 (void) snprintf(backup, sizeof (backup), "%s%s",
5198 mntpt, GRUBSIGN_BACKUP);
5199
5200 bfp = fopen(backup, "r");
5201 if (bfp == NULL) {
5202 error = errno;
5203 if (bam_verbose) {
5204 bam_error(OPEN_FAIL, backup, strerror(error));
5205 }
5206 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
5207 return (NULL);
5208 }
5209
5210 ufs = zfs = lu = NULL;
5211
5212 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5213
5214 if (lu == NULL &&
5215 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5216 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5217 lu = s_strdup(tmpsign);
5218 }
5219
5220 if (ufs == NULL &&
5221 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5222 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5223 ufs = s_strdup(tmpsign);
5224 }
5225
5226 if (zfs == NULL &&
5227 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5228 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5229 zfs = s_strdup(tmpsign);
5230 }
5231 }
5232
5233 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
5234 zfs ? zfs : "NULL",
5235 ufs ? ufs : "NULL",
5236 lu ? lu : "NULL"));
5237
5238 if (bfp) {
5239 (void) fclose(bfp);
5240 bfp = NULL;
5241 }
5242
5243 if (strcmp(fstype, "ufs") == 0 && zfs) {
5244 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5245 free(zfs);
5246 zfs = NULL;
5247 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5248 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5249 free(ufs);
5250 ufs = NULL;
5251 }
5252
5253 assert(bfp == NULL);
5254
5255 /* For now, we let Live Upgrade take care of its signature itself */
5256 if (lu) {
5257 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5258 free(lu);
5259 lu = NULL;
5260 }
5261
5262 return (zfs ? zfs : ufs);
5263 }
5264
5265 static char *
5266 find_ufs_existing(char *osroot)
5267 {
5268 char *sign;
5269 const char *fcn = "find_ufs_existing()";
5270
5271 sign = find_primary_common(osroot, "ufs");
5272 if (sign == NULL) {
5273 sign = find_backup_common(osroot, "ufs");
5274 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
5275 } else {
5276 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
5277 }
5278
5279 return (sign);
5280 }
5281
5282 char *
5283 get_mountpoint(char *special, char *fstype)
5284 {
5285 FILE *mntfp;
5286 struct mnttab mp = {0};
5287 struct mnttab mpref = {0};
5288 int error;
5289 int ret;
5290 const char *fcn = "get_mountpoint()";
5291
5292 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
5293
5294 mntfp = fopen(MNTTAB, "r");
5295 error = errno;
5296 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5297 if (mntfp == NULL) {
5298 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
5299 return (NULL);
5300 }
5301
5302 mpref.mnt_special = special;
5303 mpref.mnt_fstype = fstype;
5304
5305 ret = getmntany(mntfp, &mp, &mpref);
5306 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5307 if (ret != 0) {
5308 (void) fclose(mntfp);
5309 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
5310 return (NULL);
5311 }
5312 (void) fclose(mntfp);
5313
5314 assert(mp.mnt_mountp);
5315
5316 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
5317
5318 return (s_strdup(mp.mnt_mountp));
5319 }
5320
5321 /*
5322 * Mounts a "legacy" top dataset (if needed)
5323 * Returns: The mountpoint of the legacy top dataset or NULL on error
5324 * mnted returns one of the above values defined for zfs_mnted_t
5325 */
5326 static char *
5327 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5328 {
5329 char cmd[PATH_MAX];
5330 char tmpmnt[PATH_MAX];
5331 filelist_t flist = {0};
5332 char *is_mounted;
5333 struct stat sb;
5334 int ret;
5335 const char *fcn = "mount_legacy_dataset()";
5336
5337 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5338
5339 *mnted = ZFS_MNT_ERROR;
5340
5341 (void) snprintf(cmd, sizeof (cmd),
5342 "/sbin/zfs get -Ho value mounted %s",
5343 pool);
5344
5345 ret = exec_cmd(cmd, &flist);
5346 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5347 if (ret != 0) {
5348 bam_error(ZFS_MNTED_FAILED, pool);
5349 return (NULL);
5350 }
5351
5352 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5353 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5354 bam_error(BAD_ZFS_MNTED, pool);
5355 filelist_free(&flist);
5356 return (NULL);
5357 }
5358
5359 is_mounted = strtok(flist.head->line, " \t\n");
5360 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5361 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5362 if (strcmp(is_mounted, "no") != 0) {
5363 filelist_free(&flist);
5364 *mnted = LEGACY_ALREADY;
5365 /* get_mountpoint returns a strdup'ed string */
5366 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
5367 return (get_mountpoint(pool, "zfs"));
5368 }
5369
5370 filelist_free(&flist);
5371
5372 /*
5373 * legacy top dataset is not mounted. Mount it now
5374 * First create a mountpoint.
5375 */
5376 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5377 ZFS_LEGACY_MNTPT, getpid());
5378
5379 ret = stat(tmpmnt, &sb);
5380 if (ret == -1) {
5381 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
5382 ret = mkdirp(tmpmnt, DIR_PERMS);
5383 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5384 if (ret == -1) {
5385 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
5386 return (NULL);
5387 }
5388 } else {
5389 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
5390 }
5391
5392 (void) snprintf(cmd, sizeof (cmd),
5393 "/sbin/mount -F zfs %s %s",
5394 pool, tmpmnt);
5395
5396 ret = exec_cmd(cmd, NULL);
5397 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5398 if (ret != 0) {
5399 bam_error(ZFS_MOUNT_FAILED, pool);
5400 (void) rmdir(tmpmnt);
5401 return (NULL);
5402 }
5403
5404 *mnted = LEGACY_MOUNTED;
5405 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
5406 return (s_strdup(tmpmnt));
5407 }
5408
5409 /*
5410 * Mounts the top dataset (if needed)
5411 * Returns: The mountpoint of the top dataset or NULL on error
5412 * mnted returns one of the above values defined for zfs_mnted_t
5413 */
5414 static char *
5415 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5416 {
5417 char cmd[PATH_MAX];
5418 filelist_t flist = {0};
5419 char *is_mounted;
5420 char *mntpt;
5421 char *zmntpt;
5422 int ret;
5423 const char *fcn = "mount_top_dataset()";
5424
5425 *mnted = ZFS_MNT_ERROR;
5426
5427 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5428
5429 /*
5430 * First check if the top dataset is a "legacy" dataset
5431 */
5432 (void) snprintf(cmd, sizeof (cmd),
5433 "/sbin/zfs get -Ho value mountpoint %s",
5434 pool);
5435 ret = exec_cmd(cmd, &flist);
5436 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5437 if (ret != 0) {
5438 bam_error(ZFS_MNTPT_FAILED, pool);
5439 return (NULL);
5440 }
5441
5442 if (flist.head && (flist.head == flist.tail)) {
5443 char *legacy = strtok(flist.head->line, " \t\n");
5444 if (legacy && strcmp(legacy, "legacy") == 0) {
5445 filelist_free(&flist);
5446 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
5447 return (mount_legacy_dataset(pool, mnted));
5448 }
5449 }
5450
5451 filelist_free(&flist);
5452
5453 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
5454
5455 (void) snprintf(cmd, sizeof (cmd),
5456 "/sbin/zfs get -Ho value mounted %s",
5457 pool);
5458
5459 ret = exec_cmd(cmd, &flist);
5460 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5461 if (ret != 0) {
5462 bam_error(ZFS_MNTED_FAILED, pool);
5463 return (NULL);
5464 }
5465
5466 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5467 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5468 bam_error(BAD_ZFS_MNTED, pool);
5469 filelist_free(&flist);
5470 return (NULL);
5471 }
5472
5473 is_mounted = strtok(flist.head->line, " \t\n");
5474 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5475 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5476 if (strcmp(is_mounted, "no") != 0) {
5477 filelist_free(&flist);
5478 *mnted = ZFS_ALREADY;
5479 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
5480 goto mounted;
5481 }
5482
5483 filelist_free(&flist);
5484 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
5485
5486 /* top dataset is not mounted. Mount it now */
5487 (void) snprintf(cmd, sizeof (cmd),
5488 "/sbin/zfs mount %s", pool);
5489 ret = exec_cmd(cmd, NULL);
5490 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5491 if (ret != 0) {
5492 bam_error(ZFS_MOUNT_FAILED, pool);
5493 return (NULL);
5494 }
5495 *mnted = ZFS_MOUNTED;
5496 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
5497 /*FALLTHRU*/
5498 mounted:
5499 /*
5500 * Now get the mountpoint
5501 */
5502 (void) snprintf(cmd, sizeof (cmd),
5503 "/sbin/zfs get -Ho value mountpoint %s",
5504 pool);
5505
5506 ret = exec_cmd(cmd, &flist);
5507 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
5508 if (ret != 0) {
5509 bam_error(ZFS_MNTPT_FAILED, pool);
5510 goto error;
5511 }
5512
5513 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
5514 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5515 bam_error(NULL_ZFS_MNTPT, pool);
5516 goto error;
5517 }
5518
5519 mntpt = strtok(flist.head->line, " \t\n");
5520 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
5521 if (*mntpt != '/') {
5522 bam_error(BAD_ZFS_MNTPT, pool, mntpt);
5523 goto error;
5524 }
5525 zmntpt = s_strdup(mntpt);
5526
5527 filelist_free(&flist);
5528
5529 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
5530
5531 return (zmntpt);
5532
5533 error:
5534 filelist_free(&flist);
5535 (void) umount_top_dataset(pool, *mnted, NULL);
5536 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
5537 return (NULL);
5538 }
5539
5540 static int
5541 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
5542 {
5543 char cmd[PATH_MAX];
5544 int ret;
5545 const char *fcn = "umount_top_dataset()";
5546
5547 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
5548 switch (mnted) {
5549 case LEGACY_ALREADY:
5550 case ZFS_ALREADY:
5551 /* nothing to do */
5552 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
5553 mntpt ? mntpt : "NULL"));
5554 free(mntpt);
5555 return (BAM_SUCCESS);
5556 case LEGACY_MOUNTED:
5557 (void) snprintf(cmd, sizeof (cmd),
5558 "/sbin/umount %s", pool);
5559 ret = exec_cmd(cmd, NULL);
5560 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
5561 if (ret != 0) {
5562 bam_error(UMOUNT_FAILED, pool);
5563 free(mntpt);
5564 return (BAM_ERROR);
5565 }
5566 if (mntpt)
5567 (void) rmdir(mntpt);
5568 free(mntpt);
5569 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
5570 return (BAM_SUCCESS);
5571 case ZFS_MOUNTED:
5572 free(mntpt);
5573 (void) snprintf(cmd, sizeof (cmd),
5574 "/sbin/zfs unmount %s", pool);
5575 ret = exec_cmd(cmd, NULL);
5576 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
5577 if (ret != 0) {
5578 bam_error(UMOUNT_FAILED, pool);
5579 return (BAM_ERROR);
5580 }
5581 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
5582 return (BAM_SUCCESS);
5583 default:
5584 bam_error(INT_BAD_MNTSTATE, pool);
5585 return (BAM_ERROR);
5586 }
5587 /*NOTREACHED*/
5588 }
5589
5590 /*
5591 * For ZFS, osdev can be one of two forms
5592 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
5593 * It can be a /dev/[r]dsk special file. We handle both instances
5594 */
5595 static char *
5596 get_pool(char *osdev)
5597 {
5598 char cmd[PATH_MAX];
5599 char buf[PATH_MAX];
5600 filelist_t flist = {0};
5601 char *pool;
5602 char *cp;
5603 char *slash;
5604 int ret;
5605 const char *fcn = "get_pool()";
5606
5607 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
5608 if (osdev == NULL) {
5609 bam_error(GET_POOL_OSDEV_NULL);
5610 return (NULL);
5611 }
5612
5613 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
5614
5615 if (osdev[0] != '/') {
5616 (void) strlcpy(buf, osdev, sizeof (buf));
5617 slash = strchr(buf, '/');
5618 if (slash)
5619 *slash = '\0';
5620 pool = s_strdup(buf);
5621 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5622 return (pool);
5623 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
5624 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
5625 bam_error(GET_POOL_BAD_OSDEV, osdev);
5626 return (NULL);
5627 }
5628
5629 /*
5630 * Call the zfs fstyp directly since this is a zpool. This avoids
5631 * potential pcfs conflicts if the first block wasn't cleared.
5632 */
5633 (void) snprintf(cmd, sizeof (cmd),
5634 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
5635 osdev);
5636
5637 ret = exec_cmd(cmd, &flist);
5638 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
5639 if (ret != 0) {
5640 bam_error(FSTYP_A_FAILED, osdev);
5641 return (NULL);
5642 }
5643
5644 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
5645 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5646 bam_error(NULL_FSTYP_A, osdev);
5647 filelist_free(&flist);
5648 return (NULL);
5649 }
5650
5651 (void) strtok(flist.head->line, "'");
5652 cp = strtok(NULL, "'");
5653 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
5654 if (cp == NULL) {
5655 bam_error(BAD_FSTYP_A, osdev);
5656 filelist_free(&flist);
5657 return (NULL);
5658 }
5659
5660 pool = s_strdup(cp);
5661
5662 filelist_free(&flist);
5663
5664 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5665
5666 return (pool);
5667 }
5668
5669 static char *
5670 find_zfs_existing(char *osdev)
5671 {
5672 char *pool;
5673 zfs_mnted_t mnted;
5674 char *mntpt;
5675 char *sign;
5676 const char *fcn = "find_zfs_existing()";
5677
5678 pool = get_pool(osdev);
5679 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
5680 if (pool == NULL) {
5681 bam_error(ZFS_GET_POOL_FAILED, osdev);
5682 return (NULL);
5683 }
5684
5685 mntpt = mount_top_dataset(pool, &mnted);
5686 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
5687 if (mntpt == NULL) {
5688 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
5689 free(pool);
5690 return (NULL);
5691 }
5692
5693 sign = find_primary_common(mntpt, "zfs");
5694 if (sign == NULL) {
5695 sign = find_backup_common(mntpt, "zfs");
5696 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
5697 } else {
5698 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
5699 }
5700
5701 (void) umount_top_dataset(pool, mnted, mntpt);
5702
5703 free(pool);
5704
5705 return (sign);
5706 }
5707
5708 static char *
5709 find_existing_sign(char *osroot, char *osdev, char *fstype)
5710 {
5711 const char *fcn = "find_existing_sign()";
5712
5713 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
5714 if (strcmp(fstype, "ufs") == 0) {
5715 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
5716 return (find_ufs_existing(osroot));
5717 } else if (strcmp(fstype, "zfs") == 0) {
5718 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
5719 return (find_zfs_existing(osdev));
5720 } else {
5721 bam_error(GRUBSIGN_NOTSUP, fstype);
5722 return (NULL);
5723 }
5724 }
5725
5726 #define MH_HASH_SZ 16
5727
5728 typedef enum {
5729 MH_ERROR = -1,
5730 MH_NOMATCH,
5731 MH_MATCH
5732 } mh_search_t;
5733
5734 typedef struct mcache {
5735 char *mc_special;
5736 char *mc_mntpt;
5737 char *mc_fstype;
5738 struct mcache *mc_next;
5739 } mcache_t;
5740
5741 typedef struct mhash {
5742 mcache_t *mh_hash[MH_HASH_SZ];
5743 } mhash_t;
5744
5745 static int
5746 mhash_fcn(char *key)
5747 {
5748 int i;
5749 uint64_t sum = 0;
5750
5751 for (i = 0; key[i] != '\0'; i++) {
5752 sum += (uchar_t)key[i];
5753 }
5754
5755 sum %= MH_HASH_SZ;
5756
5757 assert(sum < MH_HASH_SZ);
5758
5759 return (sum);
5760 }
5761
5762 static mhash_t *
5763 cache_mnttab(void)
5764 {
5765 FILE *mfp;
5766 struct extmnttab mnt;
5767 mcache_t *mcp;
5768 mhash_t *mhp;
5769 char *ctds;
5770 int idx;
5771 int error;
5772 char *special_dup;
5773 const char *fcn = "cache_mnttab()";
5774
5775 mfp = fopen(MNTTAB, "r");
5776 error = errno;
5777 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
5778 if (mfp == NULL) {
5779 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
5780 return (NULL);
5781 }
5782
5783 mhp = s_calloc(1, sizeof (mhash_t));
5784
5785 resetmnttab(mfp);
5786
5787 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
5788 /* only cache ufs */
5789 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
5790 continue;
5791
5792 /* basename() modifies its arg, so dup it */
5793 special_dup = s_strdup(mnt.mnt_special);
5794 ctds = basename(special_dup);
5795
5796 mcp = s_calloc(1, sizeof (mcache_t));
5797 mcp->mc_special = s_strdup(ctds);
5798 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
5799 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
5800 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
5801 mnt.mnt_mountp, mnt.mnt_fstype));
5802 idx = mhash_fcn(ctds);
5803 mcp->mc_next = mhp->mh_hash[idx];
5804 mhp->mh_hash[idx] = mcp;
5805 free(special_dup);
5806 }
5807
5808 (void) fclose(mfp);
5809
5810 return (mhp);
5811 }
5812
5813 static void
5814 free_mnttab(mhash_t *mhp)
5815 {
5816 mcache_t *mcp;
5817 int i;
5818
5819 for (i = 0; i < MH_HASH_SZ; i++) {
5820 /*LINTED*/
5821 while (mcp = mhp->mh_hash[i]) {
5822 mhp->mh_hash[i] = mcp->mc_next;
5823 free(mcp->mc_special);
5824 free(mcp->mc_mntpt);
5825 free(mcp->mc_fstype);
5826 free(mcp);
5827 }
5828 }
5829
5830 for (i = 0; i < MH_HASH_SZ; i++) {
5831 assert(mhp->mh_hash[i] == NULL);
5832 }
5833 free(mhp);
5834 }
5835
5836 static mh_search_t
5837 search_hash(mhash_t *mhp, char *special, char **mntpt)
5838 {
5839 int idx;
5840 mcache_t *mcp;
5841 const char *fcn = "search_hash()";
5842
5843 assert(mntpt);
5844
5845 *mntpt = NULL;
5846
5847 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
5848 if (strchr(special, '/')) {
5849 bam_error(INVALID_MHASH_KEY, special);
5850 return (MH_ERROR);
5851 }
5852
5853 idx = mhash_fcn(special);
5854
5855 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
5856 if (strcmp(mcp->mc_special, special) == 0)
5857 break;
5858 }
5859
5860 if (mcp == NULL) {
5861 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
5862 return (MH_NOMATCH);
5863 }
5864
5865 assert(strcmp(mcp->mc_fstype, "ufs") == 0);
5866 *mntpt = mcp->mc_mntpt;
5867 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
5868 return (MH_MATCH);
5869 }
5870
5871 static int
5872 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
5873 {
5874 char *sign;
5875 char *signline;
5876 char signbuf[MAXNAMELEN];
5877 int len;
5878 int error;
5879 const char *fcn = "check_add_ufs_sign_to_list()";
5880
5881 /* safe to specify NULL as "osdev" arg for UFS */
5882 sign = find_existing_sign(mntpt, NULL, "ufs");
5883 if (sign == NULL) {
5884 /* No existing signature, nothing to add to list */
5885 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
5886 return (0);
5887 }
5888
5889 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
5890 signline = signbuf;
5891
5892 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
5893 if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
5894 strlen(GRUBSIGN_UFS_PREFIX))) {
5895 bam_error(INVALID_UFS_SIGNATURE, sign);
5896 free(sign);
5897 /* ignore invalid signatures */
5898 return (0);
5899 }
5900
5901 len = fputs(signline, tfp);
5902 error = errno;
5903 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
5904 if (len != strlen(signline)) {
5905 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
5906 free(sign);
5907 return (-1);
5908 }
5909
5910 free(sign);
5911
5912 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
5913 return (0);
5914 }
5915
5916 /*
5917 * slice is a basename not a full pathname
5918 */
5919 static int
5920 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
5921 {
5922 int ret;
5923 char cmd[PATH_MAX];
5924 char path[PATH_MAX];
5925 struct stat sbuf;
5926 char *mntpt;
5927 filelist_t flist = {0};
5928 char *fstype;
5929 char blkslice[PATH_MAX];
5930 const char *fcn = "process_slice_common()";
5931
5932
5933 ret = search_hash(mhp, slice, &mntpt);
5934 switch (ret) {
5935 case MH_MATCH:
5936 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
5937 return (-1);
5938 else
5939 return (0);
5940 case MH_NOMATCH:
5941 break;
5942 case MH_ERROR:
5943 default:
5944 return (-1);
5945 }
5946
5947 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
5948 if (stat(path, &sbuf) == -1) {
5949 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
5950 return (0);
5951 }
5952
5953 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
5954 (void) snprintf(cmd, sizeof (cmd),
5955 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
5956 slice);
5957
5958 if (exec_cmd(cmd, &flist) != 0) {
5959 if (bam_verbose)
5960 bam_print(FSTYP_FAILED, slice);
5961 return (0);
5962 }
5963
5964 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5965 if (bam_verbose)
5966 bam_print(FSTYP_BAD, slice);
5967 filelist_free(&flist);
5968 return (0);
5969 }
5970
5971 fstype = strtok(flist.head->line, " \t\n");
5972 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
5973 if (bam_verbose)
5974 bam_print(NOT_UFS_SLICE, slice, fstype);
5975 filelist_free(&flist);
5976 return (0);
5977 }
5978
5979 filelist_free(&flist);
5980
5981 /*
5982 * Since we are mounting the filesystem read-only, the
5983 * the last mount field of the superblock is unchanged
5984 * and does not need to be fixed up post-mount;
5985 */
5986
5987 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
5988 slice);
5989
5990 (void) snprintf(cmd, sizeof (cmd),
5991 "/usr/sbin/mount -F ufs -o ro %s %s "
5992 "> /dev/null 2>&1", blkslice, tmpmnt);
5993
5994 if (exec_cmd(cmd, NULL) != 0) {
5995 if (bam_verbose)
5996 bam_print(MOUNT_FAILED, blkslice, "ufs");
5997 return (0);
5998 }
5999
6000 ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6001
6002 (void) snprintf(cmd, sizeof (cmd),
6003 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6004 tmpmnt);
6005
6006 if (exec_cmd(cmd, NULL) != 0) {
6007 bam_print(UMOUNT_FAILED, slice);
6008 return (0);
6009 }
6010
6011 return (ret);
6012 }
6013
6014 static int
6015 process_vtoc_slices(
6016 char *s0,
6017 struct vtoc *vtoc,
6018 FILE *tfp,
6019 mhash_t *mhp,
6020 char *tmpmnt)
6021 {
6022 int idx;
6023 char slice[PATH_MAX];
6024 size_t len;
6025 char *cp;
6026 const char *fcn = "process_vtoc_slices()";
6027
6028 len = strlen(s0);
6029
6030 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6031
6032 s0[len - 1] = '\0';
6033
6034 (void) strlcpy(slice, s0, sizeof (slice));
6035
6036 s0[len - 1] = '0';
6037
6038 cp = slice + len - 1;
6039
6040 for (idx = 0; idx < vtoc->v_nparts; idx++) {
6041
6042 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6043
6044 if (vtoc->v_part[idx].p_size == 0) {
6045 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
6046 continue;
6047 }
6048
6049 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6050 switch (vtoc->v_part[idx].p_tag) {
6051 case V_SWAP:
6052 case V_USR:
6053 case V_BACKUP:
6054 case V_VAR:
6055 case V_HOME:
6056 case V_ALTSCTR:
6057 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
6058 continue;
6059 default:
6060 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
6061 break;
6062 }
6063
6064 /* skip unmountable and readonly slices */
6065 switch (vtoc->v_part[idx].p_flag) {
6066 case V_UNMNT:
6067 case V_RONLY:
6068 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
6069 continue;
6070 default:
6071 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
6072 break;
6073 }
6074
6075 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6076 return (-1);
6077 }
6078 }
6079
6080 return (0);
6081 }
6082
6083 static int
6084 process_efi_slices(
6085 char *s0,
6086 struct dk_gpt *efi,
6087 FILE *tfp,
6088 mhash_t *mhp,
6089 char *tmpmnt)
6090 {
6091 int idx;
6092 char slice[PATH_MAX];
6093 size_t len;
6094 char *cp;
6095 const char *fcn = "process_efi_slices()";
6096
6097 len = strlen(s0);
6098
6099 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6100
6101 s0[len - 1] = '\0';
6102
6103 (void) strlcpy(slice, s0, sizeof (slice));
6104
6105 s0[len - 1] = '0';
6106
6107 cp = slice + len - 1;
6108
6109 for (idx = 0; idx < efi->efi_nparts; idx++) {
6110
6111 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6112
6113 if (efi->efi_parts[idx].p_size == 0) {
6114 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
6115 continue;
6116 }
6117
6118 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6119 switch (efi->efi_parts[idx].p_tag) {
6120 case V_SWAP:
6121 case V_USR:
6122 case V_BACKUP:
6123 case V_VAR:
6124 case V_HOME:
6125 case V_ALTSCTR:
6126 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
6127 continue;
6128 default:
6129 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
6130 break;
6131 }
6132
6133 /* skip unmountable and readonly slices */
6134 switch (efi->efi_parts[idx].p_flag) {
6135 case V_UNMNT:
6136 case V_RONLY:
6137 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
6138 continue;
6139 default:
6140 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
6141 break;
6142 }
6143
6144 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6145 return (-1);
6146 }
6147 }
6148
6149 return (0);
6150 }
6151
6152 /*
6153 * s0 is a basename not a full path
6154 */
6155 static int
6156 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6157 {
6158 struct vtoc vtoc;
6159 struct dk_gpt *efi;
6160 char s0path[PATH_MAX];
6161 struct stat sbuf;
6162 int e_flag;
6163 int v_flag;
6164 int retval;
6165 int err;
6166 int fd;
6167 const char *fcn = "process_slice0()";
6168
6169 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6170
6171 if (stat(s0path, &sbuf) == -1) {
6172 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
6173 return (0);
6174 }
6175
6176 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6177 if (fd == -1) {
6178 bam_error(OPEN_FAIL, s0path, strerror(errno));
6179 return (0);
6180 }
6181
6182 e_flag = v_flag = 0;
6183 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6184 switch (retval) {
6185 case VT_EIO:
6186 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
6187 break;
6188 case VT_EINVAL:
6189 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
6190 break;
6191 case VT_ERROR:
6192 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
6193 break;
6194 case VT_ENOTSUP:
6195 e_flag = 1;
6196 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
6197 break;
6198 case 0:
6199 v_flag = 1;
6200 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
6201 break;
6202 default:
6203 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
6204 break;
6205 }
6206
6207
6208 if (e_flag) {
6209 e_flag = 0;
6210 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6211 switch (retval) {
6212 case VT_EIO:
6213 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
6214 break;
6215 case VT_EINVAL:
6216 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
6217 break;
6218 case VT_ERROR:
6219 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
6220 break;
6221 case VT_ENOTSUP:
6222 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
6223 break;
6224 case 0:
6225 e_flag = 1;
6226 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
6227 break;
6228 default:
6229 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
6230 break;
6231 }
6232 }
6233
6234 (void) close(fd);
6235
6236 if (v_flag) {
6237 retval = process_vtoc_slices(s0,
6238 &vtoc, tfp, mhp, tmpmnt);
6239 } else if (e_flag) {
6240 retval = process_efi_slices(s0,
6241 efi, tfp, mhp, tmpmnt);
6242 } else {
6243 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
6244 return (0);
6245 }
6246
6247 return (retval);
6248 }
6249
6250 /*
6251 * Find and create a list of all existing UFS boot signatures
6252 */
6253 static int
6254 FindAllUfsSignatures(void)
6255 {
6256 mhash_t *mnttab_hash;
6257 DIR *dirp = NULL;
6258 struct dirent *dp;
6259 char tmpmnt[PATH_MAX];
6260 char cmd[PATH_MAX];
6261 struct stat sb;
6262 int fd;
6263 FILE *tfp;
6264 size_t len;
6265 int ret;
6266 int error;
6267 const char *fcn = "FindAllUfsSignatures()";
6268
6269 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) {
6270 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
6271 return (0);
6272 }
6273
6274 fd = open(UFS_SIGNATURE_LIST".tmp",
6275 O_RDWR|O_CREAT|O_TRUNC, 0644);
6276 error = errno;
6277 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6278 if (fd == -1) {
6279 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6280 return (-1);
6281 }
6282
6283 ret = close(fd);
6284 error = errno;
6285 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6286 if (ret == -1) {
6287 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6288 strerror(error));
6289 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6290 return (-1);
6291 }
6292
6293 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6294 error = errno;
6295 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6296 if (tfp == NULL) {
6297 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6298 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6299 return (-1);
6300 }
6301
6302 mnttab_hash = cache_mnttab();
6303 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6304 if (mnttab_hash == NULL) {
6305 (void) fclose(tfp);
6306 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6307 bam_error(CACHE_MNTTAB_FAIL, fcn);
6308 return (-1);
6309 }
6310
6311 (void) snprintf(tmpmnt, sizeof (tmpmnt),
6312 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6313 (void) unlink(tmpmnt);
6314
6315 ret = mkdirp(tmpmnt, DIR_PERMS);
6316 error = errno;
6317 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6318 if (ret == -1) {
6319 bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
6320 free_mnttab(mnttab_hash);
6321 (void) fclose(tfp);
6322 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6323 return (-1);
6324 }
6325
6326 dirp = opendir("/dev/rdsk");
6327 error = errno;
6328 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6329 if (dirp == NULL) {
6330 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
6331 goto fail;
6332 }
6333
6334 while (dp = readdir(dirp)) {
6335 if (strcmp(dp->d_name, ".") == 0 ||
6336 strcmp(dp->d_name, "..") == 0)
6337 continue;
6338
6339 /*
6340 * we only look for the s0 slice. This is guranteed to
6341 * have 's' at len - 2.
6342 */
6343 len = strlen(dp->d_name);
6344 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6345 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
6346 continue;
6347 }
6348
6349 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6350 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6351 if (ret == -1)
6352 goto fail;
6353 }
6354
6355 (void) closedir(dirp);
6356 free_mnttab(mnttab_hash);
6357 (void) rmdir(tmpmnt);
6358
6359 ret = fclose(tfp);
6360 error = errno;
6361 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6362 if (ret == EOF) {
6363 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6364 strerror(error));
6365 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6366 return (-1);
6367 }
6368
6369 /* We have a list of existing GRUB signatures. Sort it first */
6370 (void) snprintf(cmd, sizeof (cmd),
6371 "/usr/bin/sort -u %s.tmp > %s.sorted",
6372 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6373
6374 ret = exec_cmd(cmd, NULL);
6375 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6376 if (ret != 0) {
6377 bam_error(GRUBSIGN_SORT_FAILED);
6378 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6379 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6380 return (-1);
6381 }
6382
6383 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6384
6385 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6386 error = errno;
6387 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6388 if (ret == -1) {
6389 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
6390 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6391 return (-1);
6392 }
6393
6394 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6395 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
6396 }
6397
6398 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6399 return (0);
6400
6401 fail:
6402 if (dirp)
6403 (void) closedir(dirp);
6404 free_mnttab(mnttab_hash);
6405 (void) rmdir(tmpmnt);
6406 (void) fclose(tfp);
6407 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6408 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6409 return (-1);
6410 }
6411
6412 static char *
6413 create_ufs_sign(void)
6414 {
6415 struct stat sb;
6416 int signnum = -1;
6417 char tmpsign[MAXNAMELEN + 1];
6418 char *numstr;
6419 int i;
6420 FILE *tfp;
6421 int ret;
6422 int error;
6423 const char *fcn = "create_ufs_sign()";
6424
6425 bam_print(SEARCHING_UFS_SIGN);
6426
6427 ret = FindAllUfsSignatures();
6428 INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6429 if (ret == -1) {
6430 bam_error(ERR_FIND_UFS_SIGN);
6431 return (NULL);
6432 }
6433
6434 /* Make sure the list exists and is owned by root */
6435 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6436 (void) unlink(UFS_SIGNATURE_LIST));
6437 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6438 (void) unlink(UFS_SIGNATURE_LIST);
6439 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
6440 return (NULL);
6441 }
6442
6443 if (sb.st_size == 0) {
6444 bam_print(GRUBSIGN_UFS_NONE);
6445 i = 0;
6446 goto found;
6447 }
6448
6449 /* The signature list was sorted when it was created */
6450 tfp = fopen(UFS_SIGNATURE_LIST, "r");
6451 error = errno;
6452 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
6453 if (tfp == NULL) {
6454 bam_error(UFS_SIGNATURE_LIST_OPENERR,
6455 UFS_SIGNATURE_LIST, strerror(error));
6456 (void) unlink(UFS_SIGNATURE_LIST);
6457 return (NULL);
6458 }
6459
6460 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
6461
6462 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
6463 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
6464 (void) fclose(tfp);
6465 (void) unlink(UFS_SIGNATURE_LIST);
6466 bam_error(UFS_BADSIGN, tmpsign);
6467 return (NULL);
6468 }
6469 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
6470
6471 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
6472 (void) fclose(tfp);
6473 (void) unlink(UFS_SIGNATURE_LIST);
6474 bam_error(UFS_BADSIGN, tmpsign);
6475 return (NULL);
6476 }
6477
6478 signnum = atoi(numstr);
6479 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
6480 if (signnum < 0) {
6481 (void) fclose(tfp);
6482 (void) unlink(UFS_SIGNATURE_LIST);
6483 bam_error(UFS_BADSIGN, tmpsign);
6484 return (NULL);
6485 }
6486
6487 if (i != signnum) {
6488 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
6489 break;
6490 }
6491 }
6492
6493 (void) fclose(tfp);
6494
6495 found:
6496 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
6497
6498 /* add the ufs signature to the /var/run list of signatures */
6499 ret = ufs_add_to_sign_list(tmpsign);
6500 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
6501 if (ret == -1) {
6502 (void) unlink(UFS_SIGNATURE_LIST);
6503 bam_error(FAILED_ADD_SIGNLIST, tmpsign);
6504 return (NULL);
6505 }
6506
6507 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6508
6509 return (s_strdup(tmpsign));
6510 }
6511
6512 static char *
6513 get_fstype(char *osroot)
6514 {
6515 FILE *mntfp;
6516 struct mnttab mp = {0};
6517 struct mnttab mpref = {0};
6518 int error;
6519 int ret;
6520 const char *fcn = "get_fstype()";
6521
6522 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
6523 if (osroot == NULL) {
6524 bam_error(GET_FSTYPE_ARGS);
6525 return (NULL);
6526 }
6527
6528 mntfp = fopen(MNTTAB, "r");
6529 error = errno;
6530 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
6531 if (mntfp == NULL) {
6532 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6533 return (NULL);
6534 }
6535
6536 if (*osroot == '\0')
6537 mpref.mnt_mountp = "/";
6538 else
6539 mpref.mnt_mountp = osroot;
6540
6541 ret = getmntany(mntfp, &mp, &mpref);
6542 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
6543 if (ret != 0) {
6544 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
6545 (void) fclose(mntfp);
6546 return (NULL);
6547 }
6548 (void) fclose(mntfp);
6549
6550 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
6551 if (mp.mnt_fstype == NULL) {
6552 bam_error(MNTTAB_FSTYPE_NULL, osroot);
6553 return (NULL);
6554 }
6555
6556 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6557
6558 return (s_strdup(mp.mnt_fstype));
6559 }
6560
6561 static char *
6562 create_zfs_sign(char *osdev)
6563 {
6564 char tmpsign[PATH_MAX];
6565 char *pool;
6566 const char *fcn = "create_zfs_sign()";
6567
6568 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
6569
6570 /*
6571 * First find the pool name
6572 */
6573 pool = get_pool(osdev);
6574 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
6575 if (pool == NULL) {
6576 bam_error(GET_POOL_FAILED, osdev);
6577 return (NULL);
6578 }
6579
6580 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
6581
6582 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
6583
6584 free(pool);
6585
6586 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6587
6588 return (s_strdup(tmpsign));
6589 }
6590
6591 static char *
6592 create_new_sign(char *osdev, char *fstype)
6593 {
6594 char *sign;
6595 const char *fcn = "create_new_sign()";
6596
6597 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
6598
6599 if (strcmp(fstype, "zfs") == 0) {
6600 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
6601 sign = create_zfs_sign(osdev);
6602 } else if (strcmp(fstype, "ufs") == 0) {
6603 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
6604 sign = create_ufs_sign();
6605 } else {
6606 bam_error(GRUBSIGN_NOTSUP, fstype);
6607 sign = NULL;
6608 }
6609
6610 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
6611 return (sign);
6612 }
6613
6614 static int
6615 set_backup_common(char *mntpt, char *sign)
6616 {
6617 FILE *bfp;
6618 char backup[PATH_MAX];
6619 char tmpsign[PATH_MAX];
6620 int error;
6621 char *bdir;
6622 char *backup_dup;
6623 struct stat sb;
6624 int ret;
6625 const char *fcn = "set_backup_common()";
6626
6627 (void) snprintf(backup, sizeof (backup), "%s%s",
6628 mntpt, GRUBSIGN_BACKUP);
6629
6630 /* First read the backup */
6631 bfp = fopen(backup, "r");
6632 if (bfp != NULL) {
6633 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
6634 if (strcmp(tmpsign, sign) == 0) {
6635 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
6636 (void) fclose(bfp);
6637 return (0);
6638 }
6639 }
6640 (void) fclose(bfp);
6641 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
6642 } else {
6643 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
6644 }
6645
6646 /*
6647 * Didn't find the correct signature. First create
6648 * the directory if necessary.
6649 */
6650
6651 /* dirname() modifies its argument so dup it */
6652 backup_dup = s_strdup(backup);
6653 bdir = dirname(backup_dup);
6654 assert(bdir);
6655
6656 ret = stat(bdir, &sb);
6657 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
6658 if (ret == -1) {
6659 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
6660 ret = mkdirp(bdir, DIR_PERMS);
6661 error = errno;
6662 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
6663 if (ret == -1) {
6664 bam_error(GRUBSIGN_BACKUP_MKDIRERR,
6665 GRUBSIGN_BACKUP, strerror(error));
6666 free(backup_dup);
6667 return (-1);
6668 }
6669 }
6670 free(backup_dup);
6671
6672 /*
6673 * Open the backup in append mode to add the correct
6674 * signature;
6675 */
6676 bfp = fopen(backup, "a");
6677 error = errno;
6678 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
6679 if (bfp == NULL) {
6680 bam_error(GRUBSIGN_BACKUP_OPENERR,
6681 GRUBSIGN_BACKUP, strerror(error));
6682 return (-1);
6683 }
6684
6685 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
6686
6687 ret = fputs(tmpsign, bfp);
6688 error = errno;
6689 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
6690 if (ret != strlen(tmpsign)) {
6691 bam_error(GRUBSIGN_BACKUP_WRITEERR,
6692 GRUBSIGN_BACKUP, strerror(error));
6693 (void) fclose(bfp);
6694 return (-1);
6695 }
6696
6697 (void) fclose(bfp);
6698
6699 if (bam_verbose)
6700 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
6701
6702 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6703
6704 return (0);
6705 }
6706
6707 static int
6708 set_backup_ufs(char *osroot, char *sign)
6709 {
6710 const char *fcn = "set_backup_ufs()";
6711
6712 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
6713 return (set_backup_common(osroot, sign));
6714 }
6715
6716 static int
6717 set_backup_zfs(char *osdev, char *sign)
6718 {
6719 char *pool;
6720 char *mntpt;
6721 zfs_mnted_t mnted;
6722 int ret;
6723 const char *fcn = "set_backup_zfs()";
6724
6725 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
6726
6727 pool = get_pool(osdev);
6728 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
6729 if (pool == NULL) {
6730 bam_error(GET_POOL_FAILED, osdev);
6731 return (-1);
6732 }
6733
6734 mntpt = mount_top_dataset(pool, &mnted);
6735 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
6736 if (mntpt == NULL) {
6737 bam_error(FAIL_MNT_TOP_DATASET, pool);
6738 free(pool);
6739 return (-1);
6740 }
6741
6742 ret = set_backup_common(mntpt, sign);
6743
6744 (void) umount_top_dataset(pool, mnted, mntpt);
6745
6746 free(pool);
6747
6748 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
6749 if (ret == 0) {
6750 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6751 } else {
6752 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6753 }
6754
6755 return (ret);
6756 }
6757
6758 static int
6759 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
6760 {
6761 const char *fcn = "set_backup()";
6762 int ret;
6763
6764 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
6765
6766 if (strcmp(fstype, "ufs") == 0) {
6767 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
6768 ret = set_backup_ufs(osroot, sign);
6769 } else if (strcmp(fstype, "zfs") == 0) {
6770 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
6771 ret = set_backup_zfs(osdev, sign);
6772 } else {
6773 bam_error(GRUBSIGN_NOTSUP, fstype);
6774 ret = -1;
6775 }
6776
6777 if (ret == 0) {
6778 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6779 } else {
6780 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6781 }
6782
6783 return (ret);
6784 }
6785
6786 static int
6787 set_primary_common(char *mntpt, char *sign)
6788 {
6789 char signfile[PATH_MAX];
6790 char signdir[PATH_MAX];
6791 struct stat sb;
6792 int fd;
6793 int error;
6794 int ret;
6795 const char *fcn = "set_primary_common()";
6796
6797 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
6798 mntpt, GRUBSIGN_DIR, sign);
6799
6800 if (stat(signfile, &sb) != -1) {
6801 if (bam_verbose)
6802 bam_print(PRIMARY_SIGN_EXISTS, sign);
6803 return (0);
6804 } else {
6805 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
6806 }
6807
6808 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
6809 mntpt, GRUBSIGN_DIR);
6810
6811 if (stat(signdir, &sb) == -1) {
6812 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
6813 ret = mkdirp(signdir, DIR_PERMS);
6814 error = errno;
6815 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
6816 if (ret == -1) {
6817 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
6818 return (-1);
6819 }
6820 }
6821
6822 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
6823 error = errno;
6824 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
6825 if (fd == -1) {
6826 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
6827 return (-1);
6828 }
6829
6830 ret = fsync(fd);
6831 error = errno;
6832 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
6833 if (ret != 0) {
6834 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
6835 }
6836
6837 (void) close(fd);
6838
6839 if (bam_verbose)
6840 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
6841
6842 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6843
6844 return (0);
6845 }
6846
6847 static int
6848 set_primary_ufs(char *osroot, char *sign)
6849 {
6850 const char *fcn = "set_primary_ufs()";
6851
6852 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
6853 return (set_primary_common(osroot, sign));
6854 }
6855
6856 static int
6857 set_primary_zfs(char *osdev, char *sign)
6858 {
6859 char *pool;
6860 char *mntpt;
6861 zfs_mnted_t mnted;
6862 int ret;
6863 const char *fcn = "set_primary_zfs()";
6864
6865 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
6866
6867 pool = get_pool(osdev);
6868 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
6869 if (pool == NULL) {
6870 bam_error(GET_POOL_FAILED, osdev);
6871 return (-1);
6872 }
6873
6874 /* Pool name must exist in the sign */
6875 ret = (strstr(sign, pool) != NULL);
6876 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
6877 if (ret == 0) {
6878 bam_error(POOL_SIGN_INCOMPAT, pool, sign);
6879 free(pool);
6880 return (-1);
6881 }
6882
6883 mntpt = mount_top_dataset(pool, &mnted);
6884 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
6885 if (mntpt == NULL) {
6886 bam_error(FAIL_MNT_TOP_DATASET, pool);
6887 free(pool);
6888 return (-1);
6889 }
6890
6891 ret = set_primary_common(mntpt, sign);
6892
6893 (void) umount_top_dataset(pool, mnted, mntpt);
6894
6895 free(pool);
6896
6897 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
6898 if (ret == 0) {
6899 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6900 } else {
6901 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6902 }
6903
6904 return (ret);
6905 }
6906
6907 static int
6908 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
6909 {
6910 const char *fcn = "set_primary()";
6911 int ret;
6912
6913 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
6914 if (strcmp(fstype, "ufs") == 0) {
6915 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
6916 ret = set_primary_ufs(osroot, sign);
6917 } else if (strcmp(fstype, "zfs") == 0) {
6918 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
6919 ret = set_primary_zfs(osdev, sign);
6920 } else {
6921 bam_error(GRUBSIGN_NOTSUP, fstype);
6922 ret = -1;
6923 }
6924
6925 if (ret == 0) {
6926 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6927 } else {
6928 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6929 }
6930
6931 return (ret);
6932 }
6933
6934 static int
6935 ufs_add_to_sign_list(char *sign)
6936 {
6937 FILE *tfp;
6938 char signline[MAXNAMELEN];
6939 char cmd[PATH_MAX];
6940 int ret;
6941 int error;
6942 const char *fcn = "ufs_add_to_sign_list()";
6943
6944 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
6945 if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
6946 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
6947 bam_error(INVALID_UFS_SIGN, sign);
6948 (void) unlink(UFS_SIGNATURE_LIST);
6949 return (-1);
6950 }
6951
6952 /*
6953 * most failures in this routine are not a fatal error
6954 * We simply unlink the /var/run file and continue
6955 */
6956
6957 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
6958 error = errno;
6959 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
6960 if (ret == -1) {
6961 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
6962 strerror(error));
6963 (void) unlink(UFS_SIGNATURE_LIST);
6964 return (0);
6965 }
6966
6967 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6968 error = errno;
6969 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
6970 if (tfp == NULL) {
6971 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6972 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6973 return (0);
6974 }
6975
6976 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
6977
6978 ret = fputs(signline, tfp);
6979 error = errno;
6980 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
6981 if (ret != strlen(signline)) {
6982 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
6983 (void) fclose(tfp);
6984 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6985 return (0);
6986 }
6987
6988 ret = fclose(tfp);
6989 error = errno;
6990 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
6991 if (ret == EOF) {
6992 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6993 strerror(error));
6994 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6995 return (0);
6996 }
6997
6998 /* Sort the list again */
6999 (void) snprintf(cmd, sizeof (cmd),
7000 "/usr/bin/sort -u %s.tmp > %s.sorted",
7001 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7002
7003 ret = exec_cmd(cmd, NULL);
7004 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7005 if (ret != 0) {
7006 bam_error(GRUBSIGN_SORT_FAILED);
7007 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7008 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7009 return (0);
7010 }
7011
7012 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7013
7014 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7015 error = errno;
7016 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7017 if (ret == -1) {
7018 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
7019 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7020 return (0);
7021 }
7022
7023 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7024
7025 return (0);
7026 }
7027
7028 static int
7029 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7030 {
7031 int ret;
7032 const char *fcn = "set_signature()";
7033
7034 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
7035
7036 ret = set_backup(osroot, osdev, sign, fstype);
7037 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7038 if (ret == -1) {
7039 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7040 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
7041 return (-1);
7042 }
7043
7044 ret = set_primary(osroot, osdev, sign, fstype);
7045 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7046
7047 if (ret == 0) {
7048 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7049 } else {
7050 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7051 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
7052
7053 }
7054 return (ret);
7055 }
7056
7057 char *
7058 get_grubsign(char *osroot, char *osdev)
7059 {
7060 char *grubsign; /* (<sign>,#,#) */
7061 char *slice;
7062 int fdiskpart;
7063 char *sign;
7064 char *fstype;
7065 int ret;
7066 const char *fcn = "get_grubsign()";
7067
7068 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
7069 fstype = get_fstype(osroot);
7070 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7071 if (fstype == NULL) {
7072 bam_error(GET_FSTYPE_FAILED, osroot);
7073 return (NULL);
7074 }
7075
7076 sign = find_existing_sign(osroot, osdev, fstype);
7077 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7078 if (sign == NULL) {
7079 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
7080 sign = create_new_sign(osdev, fstype);
7081 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7082 if (sign == NULL) {
7083 bam_error(GRUBSIGN_CREATE_FAIL, osdev);
7084 free(fstype);
7085 return (NULL);
7086 }
7087 }
7088
7089 ret = set_signature(osroot, osdev, sign, fstype);
7090 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7091 if (ret == -1) {
7092 bam_error(GRUBSIGN_WRITE_FAIL, osdev);
7093 free(sign);
7094 free(fstype);
7095 (void) unlink(UFS_SIGNATURE_LIST);
7096 return (NULL);
7097 }
7098
7099 free(fstype);
7100
7101 if (bam_verbose)
7102 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
7103
7104 fdiskpart = get_partition(osdev);
7105 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1);
7106 if (fdiskpart == -1) {
7107 bam_error(FDISKPART_FAIL, osdev);
7108 free(sign);
7109 return (NULL);
7110 }
7111
7112 slice = strrchr(osdev, 's');
7113
7114 grubsign = s_calloc(1, MAXNAMELEN + 10);
7115 if (slice) {
7116 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7117 sign, fdiskpart, slice[1] + 'a' - '0');
7118 } else
7119 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7120 sign, fdiskpart);
7121
7122 free(sign);
7123
7124 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
7125
7126 return (grubsign);
7127 }
7128
7129 static char *
7130 get_title(char *rootdir)
7131 {
7132 static char title[80];
7133 char *cp = NULL;
7134 char release[PATH_MAX];
7135 FILE *fp;
7136 const char *fcn = "get_title()";
7137
7138 /* open the /etc/release file */
7139 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7140
7141 fp = fopen(release, "r");
7142 if (fp == NULL) {
7143 bam_error(OPEN_FAIL, release, strerror(errno));
7144 cp = NULL;
7145 goto out;
7146 }
7147
7148 /* grab first line of /etc/release */
7149 cp = s_fgets(title, sizeof (title), fp);
7150 if (cp) {
7151 while (isspace(*cp)) /* remove leading spaces */
7152 cp++;
7153 }
7154
7155 (void) fclose(fp);
7156
7157 out:
7158 cp = cp ? cp : "Oracle Solaris";
7159
7160 BAM_DPRINTF((D_GET_TITLE, fcn, cp));
7161
7162 return (cp);
7163 }
7164
7165 char *
7166 get_special(char *mountp)
7167 {
7168 FILE *mntfp;
7169 struct mnttab mp = {0};
7170 struct mnttab mpref = {0};
7171 int error;
7172 int ret;
7173 const char *fcn = "get_special()";
7174
7175 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7176 if (mountp == NULL) {
7177 bam_error(GET_SPECIAL_NULL_MNTPT);
7178 return (NULL);
7179 }
7180
7181 mntfp = fopen(MNTTAB, "r");
7182 error = errno;
7183 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7184 if (mntfp == NULL) {
7185 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
7186 return (NULL);
7187 }
7188
7189 if (*mountp == '\0')
7190 mpref.mnt_mountp = "/";
7191 else
7192 mpref.mnt_mountp = mountp;
7193
7194 ret = getmntany(mntfp, &mp, &mpref);
7195 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7196 if (ret != 0) {
7197 (void) fclose(mntfp);
7198 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
7199 return (NULL);
7200 }
7201 (void) fclose(mntfp);
7202
7203 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
7204
7205 return (s_strdup(mp.mnt_special));
7206 }
7207
7208 static void
7209 free_physarray(char **physarray, int n)
7210 {
7211 int i;
7212 const char *fcn = "free_physarray()";
7213
7214 assert(physarray);
7215 assert(n);
7216
7217 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
7218
7219 for (i = 0; i < n; i++) {
7220 free(physarray[i]);
7221 }
7222 free(physarray);
7223
7224 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7225 }
7226
7227 static int
7228 zfs_get_physical(char *special, char ***physarray, int *n)
7229 {
7230 char sdup[PATH_MAX];
7231 char cmd[PATH_MAX];
7232 char dsk[PATH_MAX];
7233 char *pool;
7234 filelist_t flist = {0};
7235 line_t *lp;
7236 line_t *startlp;
7237 char *comp1;
7238 int i;
7239 int ret;
7240 const char *fcn = "zfs_get_physical()";
7241
7242 assert(special);
7243
7244 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7245
7246 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7247 if (special[0] == '/') {
7248 bam_error(INVALID_ZFS_SPECIAL, special);
7249 return (-1);
7250 }
7251
7252 (void) strlcpy(sdup, special, sizeof (sdup));
7253
7254 pool = strtok(sdup, "/");
7255 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7256 if (pool == NULL) {
7257 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
7258 return (-1);
7259 }
7260
7261 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7262
7263 ret = exec_cmd(cmd, &flist);
7264 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7265 if (ret != 0) {
7266 bam_error(ZFS_GET_POOL_STATUS, pool);
7267 return (-1);
7268 }
7269
7270 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7271 if (flist.head == NULL) {
7272 bam_error(BAD_ZPOOL_STATUS, pool);
7273 filelist_free(&flist);
7274 return (-1);
7275 }
7276
7277 for (lp = flist.head; lp; lp = lp->next) {
7278 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
7279 comp1 = strtok(lp->line, " \t");
7280 if (comp1 == NULL) {
7281 free(lp->line);
7282 lp->line = NULL;
7283 } else {
7284 comp1 = s_strdup(comp1);
7285 free(lp->line);
7286 lp->line = comp1;
7287 }
7288 }
7289
7290 for (lp = flist.head; lp; lp = lp->next) {
7291 if (lp->line == NULL)
7292 continue;
7293 if (strcmp(lp->line, pool) == 0) {
7294 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
7295 break;
7296 }
7297 }
7298
7299 if (lp == NULL) {
7300 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
7301 filelist_free(&flist);
7302 return (-1);
7303 }
7304
7305 startlp = lp->next;
7306 for (i = 0, lp = startlp; lp; lp = lp->next) {
7307 if (lp->line == NULL)
7308 continue;
7309 if (strcmp(lp->line, "mirror") == 0)
7310 continue;
7311 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7312 break;
7313 i++;
7314 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
7315 }
7316
7317 if (i == 0) {
7318 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
7319 filelist_free(&flist);
7320 return (-1);
7321 }
7322
7323 *n = i;
7324 *physarray = s_calloc(*n, sizeof (char *));
7325 for (i = 0, lp = startlp; lp; lp = lp->next) {
7326 if (lp->line == NULL)
7327 continue;
7328 if (strcmp(lp->line, "mirror") == 0)
7329 continue;
7330 if (strcmp(lp->line, "errors:") == 0)
7331 break;
7332 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7333 strncmp(lp->line, "/dev/rdsk/",
7334 strlen("/dev/rdsk/")) != 0) {
7335 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7336 lp->line);
7337 } else {
7338 (void) strlcpy(dsk, lp->line, sizeof (dsk));
7339 }
7340 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
7341 (*physarray)[i++] = s_strdup(dsk);
7342 }
7343
7344 assert(i == *n);
7345
7346 filelist_free(&flist);
7347
7348 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7349 return (0);
7350 }
7351
7352 /*
7353 * Certain services needed to run metastat successfully may not
7354 * be enabled. Enable them now.
7355 */
7356 /*
7357 * Checks if the specified service is online
7358 * Returns: 1 if the service is online
7359 * 0 if the service is not online
7360 * -1 on error
7361 */
7362 static int
7363 is_svc_online(char *svc)
7364 {
7365 char *state;
7366 const char *fcn = "is_svc_online()";
7367
7368 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
7369
7370 state = smf_get_state(svc);
7371 INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
7372 if (state == NULL) {
7373 bam_error(GET_SVC_STATE_ERR, svc);
7374 return (-1);
7375 }
7376 BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
7377
7378 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
7379 BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
7380 free(state);
7381 return (1);
7382 }
7383
7384 BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
7385
7386 free(state);
7387
7388 return (0);
7389 }
7390
7391 static int
7392 enable_svc(char *svc)
7393 {
7394 int ret;
7395 int sleeptime;
7396 const char *fcn = "enable_svc()";
7397
7398 ret = is_svc_online(svc);
7399 if (ret == -1) {
7400 bam_error(SVC_IS_ONLINE_FAILED, svc);
7401 return (-1);
7402 } else if (ret == 1) {
7403 BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
7404 return (0);
7405 }
7406
7407 /* Service is not enabled. Enable it now. */
7408 ret = smf_enable_instance(svc, 0);
7409 INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
7410 if (ret != 0) {
7411 bam_error(ENABLE_SVC_FAILED, svc);
7412 return (-1);
7413 }
7414
7415 BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
7416
7417 sleeptime = 0;
7418 do {
7419 ret = is_svc_online(svc);
7420 INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
7421 INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
7422 INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
7423 if (ret == -1) {
7424 bam_error(ERR_SVC_GET_ONLINE, svc);
7425 return (-1);
7426 } else if (ret == 1) {
7427 BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
7428 return (1);
7429 }
7430 (void) sleep(1);
7431 } while (++sleeptime < 60);
7432
7433 bam_error(TIMEOUT_ENABLE_SVC, svc);
7434
7435 return (-1);
7436 }
7437
7438 static int
7439 ufs_get_physical(char *special, char ***physarray, int *n)
7440 {
7441 char cmd[PATH_MAX];
7442 char *shortname;
7443 filelist_t flist = {0};
7444 char *meta;
7445 char *type;
7446 char *comp1;
7447 char *comp2;
7448 char *comp3;
7449 char *comp4;
7450 int i;
7451 line_t *lp;
7452 int ret;
7453 char *svc;
7454 const char *fcn = "ufs_get_physical()";
7455
7456 assert(special);
7457
7458 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7459
7460 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
7461 bam_error(UFS_GET_PHYS_NOT_SVM, special);
7462 return (-1);
7463 }
7464
7465 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
7466 shortname = special + strlen("/dev/md/dsk/");
7467 } else if (strncmp(special, "/dev/md/rdsk/",
7468 strlen("/dev/md/rdsk/")) == 0) {
7469 shortname = special + strlen("/dev/md/rdsk");
7470 } else {
7471 bam_error(UFS_GET_PHYS_INVALID_SVM, special);
7472 return (-1);
7473 }
7474
7475 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
7476
7477 svc = "network/rpc/meta:default";
7478 if (enable_svc(svc) == -1) {
7479 bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
7480 }
7481
7482 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
7483
7484 ret = exec_cmd(cmd, &flist);
7485 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
7486 if (ret != 0) {
7487 bam_error(UFS_SVM_METASTAT_ERR, shortname);
7488 return (-1);
7489 }
7490
7491 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
7492 if (flist.head == NULL) {
7493 bam_error(BAD_UFS_SVM_METASTAT, shortname);
7494 filelist_free(&flist);
7495 return (-1);
7496 }
7497
7498 /*
7499 * Check if not a mirror. We only parse a single metadevice
7500 * if not a mirror
7501 */
7502 meta = strtok(flist.head->line, " \t");
7503 type = strtok(NULL, " \t");
7504 if (meta == NULL || type == NULL) {
7505 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
7506 filelist_free(&flist);
7507 return (-1);
7508 }
7509 if (strcmp(type, "-m") != 0) {
7510 comp1 = strtok(NULL, " \t");
7511 comp2 = strtok(NULL, " \t");
7512 if (comp1 == NULL || comp2 != NULL) {
7513 bam_error(INVALID_UFS_SVM_METASTAT, shortname);
7514 filelist_free(&flist);
7515 return (-1);
7516 }
7517 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
7518 *physarray = s_calloc(1, sizeof (char *));
7519 (*physarray)[0] = s_strdup(comp1);
7520 *n = 1;
7521 filelist_free(&flist);
7522 return (0);
7523 }
7524
7525 /*
7526 * Okay we have a mirror. Everything after the first line
7527 * is a submirror
7528 */
7529 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7530 if (strstr(lp->line, "/dev/dsk/") == NULL &&
7531 strstr(lp->line, "/dev/rdsk/") == NULL) {
7532 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
7533 filelist_free(&flist);
7534 return (-1);
7535 }
7536 i++;
7537 }
7538
7539 *physarray = s_calloc(i, sizeof (char *));
7540 *n = i;
7541
7542 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7543 comp1 = strtok(lp->line, " \t");
7544 comp2 = strtok(NULL, " \t");
7545 comp3 = strtok(NULL, " \t");
7546 comp4 = strtok(NULL, " \t");
7547
7548 if (comp3 == NULL || comp4 == NULL ||
7549 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7550 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
7551 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
7552 filelist_free(&flist);
7553 free_physarray(*physarray, *n);
7554 return (-1);
7555 }
7556
7557 (*physarray)[i++] = s_strdup(comp4);
7558 }
7559
7560 assert(i == *n);
7561
7562 filelist_free(&flist);
7563
7564 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7565 return (0);
7566 }
7567
7568 static int
7569 get_physical(char *menu_root, char ***physarray, int *n)
7570 {
7571 char *special;
7572 int ret;
7573 const char *fcn = "get_physical()";
7574
7575 assert(menu_root);
7576 assert(physarray);
7577 assert(n);
7578
7579 *physarray = NULL;
7580 *n = 0;
7581
7582 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
7583
7584 /* First get the device special file from /etc/mnttab */
7585 special = get_special(menu_root);
7586 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7587 if (special == NULL) {
7588 bam_error(GET_SPECIAL_NULL, menu_root);
7589 return (-1);
7590 }
7591
7592 /* If already a physical device nothing to do */
7593 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7594 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7595 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
7596 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7597 *physarray = s_calloc(1, sizeof (char *));
7598 (*physarray)[0] = special;
7599 *n = 1;
7600 return (0);
7601 }
7602
7603 if (is_zfs(menu_root)) {
7604 ret = zfs_get_physical(special, physarray, n);
7605 } else if (is_ufs(menu_root)) {
7606 ret = ufs_get_physical(special, physarray, n);
7607 } else {
7608 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
7609 ret = -1;
7610 }
7611
7612 free(special);
7613
7614 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7615 if (ret == -1) {
7616 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7617 } else {
7618 int i;
7619 assert (*n > 0);
7620 for (i = 0; i < *n; i++) {
7621 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
7622 }
7623 }
7624
7625 return (ret);
7626 }
7627
7628 static int
7629 is_bootdisk(char *osroot, char *physical)
7630 {
7631 int ret;
7632 char *grubroot;
7633 char *bootp;
7634 const char *fcn = "is_bootdisk()";
7635
7636 assert(osroot);
7637 assert(physical);
7638
7639 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
7640
7641 bootp = strstr(physical, "p0:boot");
7642 if (bootp)
7643 *bootp = '\0';
7644 /*
7645 * We just want the BIOS mapping for menu disk.
7646 * Don't pass menu_root to get_grubroot() as the
7647 * check that it is used for is not relevant here.
7648 * The osroot is immaterial as well - it is only used to
7649 * to find create_diskmap script. Everything hinges on
7650 * "physical"
7651 */
7652 grubroot = get_grubroot(osroot, physical, NULL);
7653
7654 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
7655 if (grubroot == NULL) {
7656 if (bam_verbose)
7657 bam_error(NO_GRUBROOT_FOR_DISK, physical);
7658 return (0);
7659 }
7660 ret = grubroot[3] == '0';
7661 free(grubroot);
7662
7663 BAM_DPRINTF((D_RETURN_RET, fcn, ret));
7664
7665 return (ret);
7666 }
7667
7668 /*
7669 * Check if menu is on the boot device
7670 * Return 0 (false) on error
7671 */
7672 static int
7673 menu_on_bootdisk(char *osroot, char *menu_root)
7674 {
7675 char **physarray;
7676 int ret;
7677 int n;
7678 int i;
7679 int on_bootdisk;
7680 const char *fcn = "menu_on_bootdisk()";
7681
7682 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
7683
7684 ret = get_physical(menu_root, &physarray, &n);
7685 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
7686 if (ret != 0) {
7687 bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
7688 return (0);
7689 }
7690
7691 assert(physarray);
7692 assert(n > 0);
7693
7694 on_bootdisk = 0;
7695 for (i = 0; i < n; i++) {
7696 assert(strncmp(physarray[i], "/dev/dsk/",
7697 strlen("/dev/dsk/")) == 0 ||
7698 strncmp(physarray[i], "/dev/rdsk/",
7699 strlen("/dev/rdsk/")) == 0);
7700
7701 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
7702 if (is_bootdisk(osroot, physarray[i])) {
7703 on_bootdisk = 1;
7704 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
7705 }
7706 }
7707
7708 free_physarray(physarray, n);
7709
7710 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
7711 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
7712 if (on_bootdisk) {
7713 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7714 } else {
7715 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7716 }
7717
7718 return (on_bootdisk);
7719 }
7720
7721 void
7722 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
7723 {
7724 const char *fcn = "bam_add_line()";
7725
7726 assert(mp);
7727 assert(entry);
7728 assert(prev);
7729 assert(lp);
7730
7731 lp->next = prev->next;
7732 if (prev->next) {
7733 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
7734 prev->next->prev = lp;
7735 } else {
7736 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
7737 }
7738 prev->next = lp;
7739 lp->prev = prev;
7740
7741 if (entry->end == prev) {
7742 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
7743 entry->end = lp;
7744 }
7745 if (mp->end == prev) {
7746 assert(lp->next == NULL);
7747 mp->end = lp;
7748 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
7749 }
7750 }
7751
7752 /*
7753 * look for matching bootadm entry with specified parameters
7754 * Here are the rules (based on existing usage):
7755 * - If title is specified, match on title only
7756 * - Else, match on root/findroot, kernel, and module.
7757 * Note that, if root_opt is non-zero, the absence of
7758 * root line is considered a match.
7759 */
7760 static entry_t *
7761 find_boot_entry(
7762 menu_t *mp,
7763 char *title,
7764 char *kernel,
7765 char *findroot,
7766 char *root,
7767 char *module,
7768 int root_opt,
7769 int *entry_num)
7770 {
7771 int i;
7772 line_t *lp;
7773 entry_t *ent;
7774 const char *fcn = "find_boot_entry()";
7775
7776 if (entry_num)
7777 *entry_num = BAM_ERROR;
7778
7779 /* find matching entry */
7780 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
7781 lp = ent->start;
7782
7783 /* first line of entry must be bootadm comment */
7784 lp = ent->start;
7785 if (lp->flags != BAM_COMMENT ||
7786 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
7787 continue;
7788 }
7789
7790 /* advance to title line */
7791 lp = lp->next;
7792 if (title) {
7793 if (lp->flags == BAM_TITLE && lp->arg &&
7794 strcmp(lp->arg, title) == 0) {
7795 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
7796 break;
7797 }
7798 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
7799 continue; /* check title only */
7800 }
7801
7802 lp = lp->next; /* advance to root line */
7803 if (lp == NULL) {
7804 continue;
7805 } else if (lp->cmd != NULL &&
7806 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
7807 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
7808 findroot = NULL);
7809 if (findroot == NULL) {
7810 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
7811 fcn, lp->arg));
7812 continue;
7813 }
7814 /* findroot command found, try match */
7815 if (strcmp(lp->arg, findroot) != 0) {
7816 BAM_DPRINTF((D_NOMATCH_FINDROOT,
7817 fcn, findroot, lp->arg));
7818 continue;
7819 }
7820 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
7821 lp = lp->next; /* advance to kernel line */
7822 } else if (lp->cmd != NULL &&
7823 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
7824 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
7825 if (root == NULL) {
7826 BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
7827 fcn, lp->arg));
7828 continue;
7829 }
7830 /* root cmd found, try match */
7831 if (strcmp(lp->arg, root) != 0) {
7832 BAM_DPRINTF((D_NOMATCH_ROOT,
7833 fcn, root, lp->arg));
7834 continue;
7835 }
7836 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
7837 lp = lp->next; /* advance to kernel line */
7838 } else {
7839 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
7840 root_opt = 0);
7841 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
7842 root_opt = 1);
7843 /* no root command, see if root is optional */
7844 if (root_opt == 0) {
7845 BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
7846 continue;
7847 }
7848 BAM_DPRINTF((D_ROOT_OPT, fcn));
7849 }
7850
7851 if (lp == NULL || lp->next == NULL) {
7852 continue;
7853 }
7854
7855 if (kernel &&
7856 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
7857 if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
7858 !(ent->flags & BAM_ENTRY_DBOOT) ||
7859 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
7860 continue;
7861
7862 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
7863
7864 }
7865 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
7866
7867 /*
7868 * Check for matching module entry (failsafe or normal).
7869 * If it fails to match, we go around the loop again.
7870 * For xpv entries, there are two module lines, so we
7871 * do the check twice.
7872 */
7873 lp = lp->next; /* advance to module line */
7874 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
7875 (((lp = lp->next) != NULL) &&
7876 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
7877 /* match found */
7878 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
7879 break;
7880 }
7881
7882 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
7883 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
7884 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
7885 ent->flags |= BAM_ENTRY_UPGFSMODULE;
7886 break;
7887 }
7888
7889 }
7890
7891 if (ent && entry_num) {
7892 *entry_num = i;
7893 }
7894
7895 if (ent) {
7896 BAM_DPRINTF((D_RETURN_RET, fcn, i));
7897 } else {
7898 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
7899 }
7900 return (ent);
7901 }
7902
7903 static int
7904 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
7905 char *kernel, char *mod_kernel, char *module, int root_opt)
7906 {
7907 int i;
7908 int change_kernel = 0;
7909 entry_t *ent;
7910 line_t *lp;
7911 line_t *tlp;
7912 char linebuf[BAM_MAXLINE];
7913 const char *fcn = "update_boot_entry()";
7914
7915 /* note: don't match on title, it's updated on upgrade */
7916 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
7917 root_opt, &i);
7918 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
7919 /*
7920 * We may be upgrading a kernel from multiboot to
7921 * directboot. Look for a multiboot entry. A multiboot
7922 * entry will not have a findroot line.
7923 */
7924 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
7925 MULTIBOOT_ARCHIVE, root_opt, &i);
7926 if (ent != NULL) {
7927 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
7928 change_kernel = 1;
7929 }
7930 } else if (ent) {
7931 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
7932 }
7933
7934 if (ent == NULL) {
7935 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
7936 return (add_boot_entry(mp, title, findroot,
7937 kernel, mod_kernel, module, NULL));
7938 }
7939
7940 /* replace title of existing entry and update findroot line */
7941 lp = ent->start;
7942 lp = lp->next; /* title line */
7943 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7944 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
7945 free(lp->arg);
7946 free(lp->line);
7947 lp->arg = s_strdup(title);
7948 lp->line = s_strdup(linebuf);
7949 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
7950
7951 tlp = lp; /* title line */
7952 lp = lp->next; /* root line */
7953
7954 /* if no root or findroot command, create a new line_t */
7955 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
7956 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
7957 lp = s_calloc(1, sizeof (line_t));
7958 bam_add_line(mp, ent, tlp, lp);
7959 } else {
7960 if (lp->cmd != NULL)
7961 free(lp->cmd);
7962
7963 free(lp->sep);
7964 free(lp->arg);
7965 free(lp->line);
7966 }
7967
7968 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
7969 lp->sep = s_strdup(menu_cmds[SEP_CMD]);
7970 lp->arg = s_strdup(findroot);
7971 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7972 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
7973 lp->line = s_strdup(linebuf);
7974 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
7975
7976 /* kernel line */
7977 lp = lp->next;
7978
7979 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
7980 char *params = NULL;
7981
7982 params = strstr(lp->line, "-s");
7983 if (params != NULL)
7984 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
7985 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
7986 kernel, params+2);
7987 else
7988 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7989 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
7990 kernel);
7991
7992 if (lp->cmd != NULL)
7993 free(lp->cmd);
7994
7995 free(lp->arg);
7996 free(lp->line);
7997 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
7998 lp->arg = s_strdup(strstr(linebuf, "/"));
7999 lp->line = s_strdup(linebuf);
8000 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8001 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd));
8002 }
8003
8004 if (change_kernel) {
8005 /*
8006 * We're upgrading from multiboot to directboot.
8007 */
8008 if (lp->cmd != NULL &&
8009 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8010 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8011 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8012 kernel);
8013 free(lp->cmd);
8014 free(lp->arg);
8015 free(lp->line);
8016 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8017 lp->arg = s_strdup(kernel);
8018 lp->line = s_strdup(linebuf);
8019 lp = lp->next;
8020 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
8021 }
8022 if (lp->cmd != NULL &&
8023 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8024 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8025 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8026 module);
8027 free(lp->cmd);
8028 free(lp->arg);
8029 free(lp->line);
8030 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8031 lp->arg = s_strdup(module);
8032 lp->line = s_strdup(linebuf);
8033 lp = lp->next;
8034 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8035 }
8036 }
8037
8038 /* module line */
8039 lp = lp->next;
8040
8041 if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8042 if (lp->cmd != NULL &&
8043 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8044 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8045 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8046 module);
8047 free(lp->cmd);
8048 free(lp->arg);
8049 free(lp->line);
8050 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8051 lp->arg = s_strdup(module);
8052 lp->line = s_strdup(linebuf);
8053 lp = lp->next;
8054 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8055 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8056 }
8057 }
8058
8059 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8060 return (i);
8061 }
8062
8063 int
8064 root_optional(char *osroot, char *menu_root)
8065 {
8066 char *ospecial;
8067 char *mspecial;
8068 char *slash;
8069 int root_opt;
8070 int ret1;
8071 int ret2;
8072 const char *fcn = "root_optional()";
8073
8074 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
8075
8076 /*
8077 * For all filesystems except ZFS, a straight compare of osroot
8078 * and menu_root will tell us if root is optional.
8079 * For ZFS, the situation is complicated by the fact that
8080 * menu_root and osroot are always different
8081 */
8082 ret1 = is_zfs(osroot);
8083 ret2 = is_zfs(menu_root);
8084 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8085 if (!ret1 || !ret2) {
8086 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
8087 root_opt = (strcmp(osroot, menu_root) == 0);
8088 goto out;
8089 }
8090
8091 ospecial = get_special(osroot);
8092 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8093 if (ospecial == NULL) {
8094 bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
8095 return (0);
8096 }
8097 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
8098
8099 mspecial = get_special(menu_root);
8100 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8101 if (mspecial == NULL) {
8102 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
8103 free(ospecial);
8104 return (0);
8105 }
8106 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
8107
8108 slash = strchr(ospecial, '/');
8109 if (slash)
8110 *slash = '\0';
8111 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
8112
8113 root_opt = (strcmp(ospecial, mspecial) == 0);
8114
8115 free(ospecial);
8116 free(mspecial);
8117
8118 out:
8119 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8120 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8121 if (root_opt) {
8122 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8123 } else {
8124 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8125 }
8126
8127 return (root_opt);
8128 }
8129
8130 /*ARGSUSED*/
8131 static error_t
8132 update_entry(menu_t *mp, char *menu_root, char *osdev)
8133 {
8134 int entry;
8135 char *grubsign;
8136 char *grubroot;
8137 char *title;
8138 char osroot[PATH_MAX];
8139 char *failsafe_kernel = NULL;
8140 struct stat sbuf;
8141 char failsafe[256];
8142 char failsafe_64[256];
8143 int ret;
8144 const char *fcn = "update_entry()";
8145
8146 assert(mp);
8147 assert(menu_root);
8148 assert(osdev);
8149 assert(bam_root);
8150
8151 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
8152
8153 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8154
8155 title = get_title(osroot);
8156 assert(title);
8157
8158 grubsign = get_grubsign(osroot, osdev);
8159 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8160 if (grubsign == NULL) {
8161 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
8162 return (BAM_ERROR);
8163 }
8164
8165 /*
8166 * It is not a fatal error if get_grubroot() fails
8167 * We no longer rely on biosdev to populate the
8168 * menu
8169 */
8170 grubroot = get_grubroot(osroot, osdev, menu_root);
8171 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8172 if (grubroot) {
8173 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
8174 fcn, osroot, osdev, menu_root));
8175 } else {
8176 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
8177 fcn, osroot, osdev, menu_root));
8178 }
8179
8180 /* add the entry for normal Solaris */
8181 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8182 bam_direct = BAM_DIRECT_MULTIBOOT);
8183 if (bam_direct == BAM_DIRECT_DBOOT) {
8184 entry = update_boot_entry(mp, title, grubsign, grubroot,
8185 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8186 NULL, DIRECT_BOOT_ARCHIVE,
8187 root_optional(osroot, menu_root));
8188 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
8189 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8190 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8191 grubroot, XEN_MENU, bam_zfs ?
8192 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8193 DIRECT_BOOT_ARCHIVE,
8194 root_optional(osroot, menu_root));
8195 BAM_DPRINTF((D_UPDATED_HV_ENTRY,
8196 fcn, bam_zfs, grubsign));
8197 }
8198 } else {
8199 entry = update_boot_entry(mp, title, grubsign, grubroot,
8200 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8201 root_optional(osroot, menu_root));
8202
8203 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
8204 }
8205
8206 /*
8207 * Add the entry for failsafe archive. On a bfu'd system, the
8208 * failsafe may be different than the installed kernel.
8209 */
8210 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8211 osroot, FAILSAFE_ARCHIVE_32);
8212 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8213 osroot, FAILSAFE_ARCHIVE_64);
8214
8215 /*
8216 * Check if at least one of the two archives exists
8217 * Using $ISADIR as the default line, we have an entry which works
8218 * for both the cases.
8219 */
8220
8221 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8222
8223 /* Figure out where the kernel line should point */
8224 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8225 DIRECT_BOOT_FAILSAFE_32);
8226 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8227 osroot, DIRECT_BOOT_FAILSAFE_64);
8228 if (stat(failsafe, &sbuf) == 0 ||
8229 stat(failsafe_64, &sbuf) == 0) {
8230 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8231 } else {
8232 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8233 osroot, MULTI_BOOT_FAILSAFE);
8234 if (stat(failsafe, &sbuf) == 0) {
8235 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8236 }
8237 }
8238 if (failsafe_kernel != NULL) {
8239 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8240 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8241 root_optional(osroot, menu_root));
8242 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
8243 failsafe_kernel));
8244 }
8245 }
8246 free(grubroot);
8247
8248 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8249 if (entry == BAM_ERROR) {
8250 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
8251 free(grubsign);
8252 return (BAM_ERROR);
8253 }
8254 free(grubsign);
8255
8256 update_numbering(mp);
8257 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8258 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8259 if (ret == BAM_ERROR) {
8260 bam_error(SET_DEFAULT_FAILED, entry);
8261 }
8262 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8263 return (BAM_WRITE);
8264 }
8265
8266 static void
8267 save_default_entry(menu_t *mp, const char *which)
8268 {
8269 int lineNum;
8270 int entryNum;
8271 int entry = 0; /* default is 0 */
8272 char linebuf[BAM_MAXLINE];
8273 line_t *lp = mp->curdefault;
8274 const char *fcn = "save_default_entry()";
8275
8276 if (mp->start) {
8277 lineNum = mp->end->lineNum;
8278 entryNum = mp->end->entryNum;
8279 } else {
8280 lineNum = LINE_INIT;
8281 entryNum = ENTRY_INIT;
8282 }
8283
8284 if (lp)
8285 entry = s_strtol(lp->arg);
8286
8287 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8288 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
8289 line_parser(mp, linebuf, &lineNum, &entryNum);
8290 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
8291 }
8292
8293 static void
8294 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8295 {
8296 int entry;
8297 char *str;
8298 const char *fcn = "restore_default_entry()";
8299
8300 if (lp == NULL) {
8301 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
8302 return; /* nothing to restore */
8303 }
8304
8305 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
8306
8307 str = lp->arg + strlen(which);
8308 entry = s_strtol(str);
8309 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8310
8311 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
8312
8313 /* delete saved old default line */
8314 unlink_line(mp, lp);
8315 line_free(lp);
8316 }
8317
8318 /*
8319 * This function is for supporting reboot with args.
8320 * The opt value can be:
8321 * NULL delete temp entry, if present
8322 * entry=<n> switches default entry to <n>
8323 * else treated as boot-args and setup a temperary menu entry
8324 * and make it the default
8325 * Note that we are always rebooting the current OS instance
8326 * so osroot == / always.
8327 */
8328 #define REBOOT_TITLE "Solaris_reboot_transient"
8329
8330 /*ARGSUSED*/
8331 static error_t
8332 update_temp(menu_t *mp, char *dummy, char *opt)
8333 {
8334 int entry;
8335 char *osdev;
8336 char *fstype;
8337 char *sign;
8338 char *opt_ptr;
8339 char *path;
8340 char kernbuf[BUFSIZ];
8341 char args_buf[BUFSIZ];
8342 char signbuf[PATH_MAX];
8343 int ret;
8344 const char *fcn = "update_temp()";
8345
8346 assert(mp);
8347 assert(dummy == NULL);
8348
8349 /* opt can be NULL */
8350 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
8351 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
8352
8353 if (bam_alt_root || bam_rootlen != 1 ||
8354 strcmp(bam_root, "/") != 0 ||
8355 strcmp(rootbuf, "/") != 0) {
8356 bam_error(ALT_ROOT_INVALID, bam_root);
8357 return (BAM_ERROR);
8358 }
8359
8360 /* If no option, delete exiting reboot menu entry */
8361 if (opt == NULL) {
8362 entry_t *ent;
8363 BAM_DPRINTF((D_OPT_NULL, fcn));
8364 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8365 NULL, NULL, 0, &entry);
8366 if (ent == NULL) { /* not found is ok */
8367 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
8368 return (BAM_SUCCESS);
8369 }
8370 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8371 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8372 mp->olddefault = NULL;
8373 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
8374 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8375 return (BAM_WRITE);
8376 }
8377
8378 /* if entry= is specified, set the default entry */
8379 if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8380 int entryNum = s_strtol(opt + strlen("entry="));
8381 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
8382 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8383 /* this is entry=# option */
8384 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8385 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
8386 return (ret);
8387 } else {
8388 bam_error(SET_DEFAULT_FAILED, entryNum);
8389 return (BAM_ERROR);
8390 }
8391 }
8392
8393 /*
8394 * add a new menu entry based on opt and make it the default
8395 */
8396
8397 fstype = get_fstype("/");
8398 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8399 if (fstype == NULL) {
8400 bam_error(REBOOT_FSTYPE_FAILED);
8401 return (BAM_ERROR);
8402 }
8403
8404 osdev = get_special("/");
8405 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8406 if (osdev == NULL) {
8407 free(fstype);
8408 bam_error(REBOOT_SPECIAL_FAILED);
8409 return (BAM_ERROR);
8410 }
8411
8412 sign = find_existing_sign("/", osdev, fstype);
8413 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8414 if (sign == NULL) {
8415 free(fstype);
8416 free(osdev);
8417 bam_error(REBOOT_SIGN_FAILED);
8418 return (BAM_ERROR);
8419 }
8420
8421 free(osdev);
8422 (void) strlcpy(signbuf, sign, sizeof (signbuf));
8423 free(sign);
8424
8425 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8426 strchr(signbuf, ')') == NULL);
8427
8428 /*
8429 * There is no alternate root while doing reboot with args
8430 * This version of bootadm is only delivered with a DBOOT
8431 * version of Solaris.
8432 */
8433 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8434 if (bam_direct != BAM_DIRECT_DBOOT) {
8435 free(fstype);
8436 bam_error(REBOOT_DIRECT_FAILED);
8437 return (BAM_ERROR);
8438 }
8439
8440 /* add an entry for Solaris reboot */
8441 if (opt[0] == '-') {
8442 /* It's an option - first see if boot-file is set */
8443 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8444 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8445 if (ret != BAM_SUCCESS) {
8446 free(fstype);
8447 bam_error(REBOOT_GET_KERNEL_FAILED);
8448 return (BAM_ERROR);
8449 }
8450 if (kernbuf[0] == '\0')
8451 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8452 sizeof (kernbuf));
8453 /*
8454 * If this is a zfs file system and kernbuf does not
8455 * have "-B $ZFS-BOOTFS" string yet, add it.
8456 */
8457 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8458 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8459 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8460 }
8461 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8462 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8463 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
8464 } else if (opt[0] == '/') {
8465 /* It's a full path, so write it out. */
8466 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8467
8468 /*
8469 * If someone runs:
8470 *
8471 * # eeprom boot-args='-kd'
8472 * # reboot /platform/i86pc/kernel/unix
8473 *
8474 * we want to use the boot-args as part of the boot
8475 * line. On the other hand, if someone runs:
8476 *
8477 * # reboot "/platform/i86pc/kernel/unix -kd"
8478 *
8479 * we don't need to mess with boot-args. If there's
8480 * no space in the options string, assume we're in the
8481 * first case.
8482 */
8483 if (strchr(opt, ' ') == NULL) {
8484 ret = get_kernel(mp, ARGS_CMD, args_buf,
8485 sizeof (args_buf));
8486 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8487 if (ret != BAM_SUCCESS) {
8488 free(fstype);
8489 bam_error(REBOOT_GET_ARGS_FAILED);
8490 return (BAM_ERROR);
8491 }
8492
8493 if (args_buf[0] != '\0') {
8494 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8495 (void) strlcat(kernbuf, args_buf,
8496 sizeof (kernbuf));
8497 }
8498 }
8499 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
8500 } else {
8501 /*
8502 * It may be a partial path, or it may be a partial
8503 * path followed by options. Assume that only options
8504 * follow a space. If someone sends us a kernel path
8505 * that includes a space, they deserve to be broken.
8506 */
8507 opt_ptr = strchr(opt, ' ');
8508 if (opt_ptr != NULL) {
8509 *opt_ptr = '\0';
8510 }
8511
8512 path = expand_path(opt);
8513 if (path != NULL) {
8514 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8515 free(path);
8516
8517 /*
8518 * If there were options given, use those.
8519 * Otherwise, copy over the default options.
8520 */
8521 if (opt_ptr != NULL) {
8522 /* Restore the space in opt string */
8523 *opt_ptr = ' ';
8524 (void) strlcat(kernbuf, opt_ptr,
8525 sizeof (kernbuf));
8526 } else {
8527 ret = get_kernel(mp, ARGS_CMD, args_buf,
8528 sizeof (args_buf));
8529 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8530 ret = BAM_ERROR);
8531 if (ret != BAM_SUCCESS) {
8532 free(fstype);
8533 bam_error(REBOOT_GET_ARGS_FAILED);
8534 return (BAM_ERROR);
8535 }
8536
8537 if (args_buf[0] != '\0') {
8538 (void) strlcat(kernbuf, " ",
8539 sizeof (kernbuf));
8540 (void) strlcat(kernbuf,
8541 args_buf, sizeof (kernbuf));
8542 }
8543 }
8544 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
8545 } else {
8546 free(fstype);
8547 bam_error(UNKNOWN_KERNEL, opt);
8548 bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
8549 return (BAM_ERROR);
8550 }
8551 }
8552 free(fstype);
8553 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8554 NULL, NULL, NULL);
8555 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8556 if (entry == BAM_ERROR) {
8557 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
8558 return (BAM_ERROR);
8559 }
8560
8561 save_default_entry(mp, BAM_OLDDEF);
8562 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8563 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8564 if (ret == BAM_ERROR) {
8565 bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
8566 }
8567 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8568 return (BAM_WRITE);
8569 }
8570
8571 error_t
8572 set_global(menu_t *mp, char *globalcmd, int val)
8573 {
8574 line_t *lp;
8575 line_t *found;
8576 line_t *last;
8577 char *cp;
8578 char *str;
8579 char prefix[BAM_MAXLINE];
8580 size_t len;
8581 const char *fcn = "set_global()";
8582
8583 assert(mp);
8584 assert(globalcmd);
8585
8586 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
8587 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
8588 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
8589 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
8590 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
8591 (void) snprintf(prefix, sizeof (prefix), "%d", val);
8592 bam_error(INVALID_ENTRY, prefix);
8593 return (BAM_ERROR);
8594 }
8595 }
8596
8597 found = last = NULL;
8598 for (lp = mp->start; lp; lp = lp->next) {
8599 if (lp->flags != BAM_GLOBAL)
8600 continue;
8601
8602 last = lp; /* track the last global found */
8603
8604 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
8605 if (lp->cmd == NULL) {
8606 bam_error(NO_CMD, lp->lineNum);
8607 continue;
8608 }
8609 if (strcmp(globalcmd, lp->cmd) != 0)
8610 continue;
8611
8612 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
8613
8614 if (found) {
8615 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
8616 }
8617 found = lp;
8618 }
8619
8620 if (found == NULL) {
8621 lp = s_calloc(1, sizeof (line_t));
8622 if (last == NULL) {
8623 lp->next = mp->start;
8624 mp->start = lp;
8625 mp->end = (mp->end) ? mp->end : lp;
8626 } else {
8627 lp->next = last->next;
8628 last->next = lp;
8629 if (lp->next == NULL)
8630 mp->end = lp;
8631 }
8632 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
8633 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
8634 len += 10; /* val < 10 digits */
8635 lp->line = s_calloc(1, len);
8636 (void) snprintf(lp->line, len, "%s%s%d",
8637 globalcmd, menu_cmds[SEP_CMD], val);
8638 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
8639 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8640 return (BAM_WRITE);
8641 }
8642
8643 /*
8644 * We are changing an existing entry. Retain any prefix whitespace,
8645 * but overwrite everything else. This preserves tabs added for
8646 * readability.
8647 */
8648 str = found->line;
8649 cp = prefix;
8650 while (*str == ' ' || *str == '\t')
8651 *(cp++) = *(str++);
8652 *cp = '\0'; /* Terminate prefix */
8653 len = strlen(prefix) + strlen(globalcmd);
8654 len += strlen(menu_cmds[SEP_CMD]) + 10;
8655
8656 free(found->line);
8657 found->line = s_calloc(1, len);
8658 (void) snprintf(found->line, len,
8659 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
8660
8661 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
8662 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8663 return (BAM_WRITE); /* need a write to menu */
8664 }
8665
8666 /*
8667 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
8668 * expand it to a full unix path. The calling function is expected to
8669 * output a message if an error occurs and NULL is returned.
8670 */
8671 static char *
8672 expand_path(const char *partial_path)
8673 {
8674 int new_path_len;
8675 char *new_path;
8676 char new_path2[PATH_MAX];
8677 struct stat sb;
8678 const char *fcn = "expand_path()";
8679
8680 new_path_len = strlen(partial_path) + 64;
8681 new_path = s_calloc(1, new_path_len);
8682
8683 /* First, try the simplest case - something like "kernel/unix" */
8684 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
8685 partial_path);
8686 if (stat(new_path, &sb) == 0) {
8687 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8688 return (new_path);
8689 }
8690
8691 if (strcmp(partial_path, "kmdb") == 0) {
8692 (void) snprintf(new_path, new_path_len, "%s -k",
8693 DIRECT_BOOT_KERNEL);
8694 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8695 return (new_path);
8696 }
8697
8698 /*
8699 * We've quickly reached unsupported usage. Try once more to
8700 * see if we were just given a glom name.
8701 */
8702 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
8703 partial_path);
8704 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
8705 partial_path);
8706 if (stat(new_path, &sb) == 0) {
8707 if (stat(new_path2, &sb) == 0) {
8708 /*
8709 * We matched both, so we actually
8710 * want to write the $ISADIR version.
8711 */
8712 (void) snprintf(new_path, new_path_len,
8713 "/platform/i86pc/kernel/%s/$ISADIR/unix",
8714 partial_path);
8715 }
8716 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8717 return (new_path);
8718 }
8719
8720 free(new_path);
8721 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8722 return (NULL);
8723 }
8724
8725 /*
8726 * The kernel cmd and arg have been changed, so
8727 * check whether the archive line needs to change.
8728 */
8729 static void
8730 set_archive_line(entry_t *entryp, line_t *kernelp)
8731 {
8732 line_t *lp = entryp->start;
8733 char *new_archive;
8734 menu_cmd_t m_cmd;
8735 const char *fcn = "set_archive_line()";
8736
8737 for (; lp != NULL; lp = lp->next) {
8738 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
8739 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
8740 break;
8741 }
8742
8743 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
8744 if (lp == entryp->end) {
8745 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
8746 entryp->entryNum));
8747 return;
8748 }
8749 }
8750 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
8751 if (lp == NULL) {
8752 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
8753 return;
8754 }
8755
8756 if (strstr(kernelp->arg, "$ISADIR") != NULL) {
8757 new_archive = DIRECT_BOOT_ARCHIVE;
8758 m_cmd = MODULE_DOLLAR_CMD;
8759 } else if (strstr(kernelp->arg, "amd64") != NULL) {
8760 new_archive = DIRECT_BOOT_ARCHIVE_64;
8761 m_cmd = MODULE_CMD;
8762 } else {
8763 new_archive = DIRECT_BOOT_ARCHIVE_32;
8764 m_cmd = MODULE_CMD;
8765 }
8766
8767 if (strcmp(lp->arg, new_archive) == 0) {
8768 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
8769 return;
8770 }
8771
8772 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
8773 free(lp->cmd);
8774 lp->cmd = s_strdup(menu_cmds[m_cmd]);
8775 }
8776
8777 free(lp->arg);
8778 lp->arg = s_strdup(new_archive);
8779 update_line(lp);
8780 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
8781 }
8782
8783 /*
8784 * Title for an entry to set properties that once went in bootenv.rc.
8785 */
8786 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
8787
8788 /*
8789 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
8790 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
8791 * string, reset the value to the default. If path is a non-zero-length
8792 * string, set the kernel or arguments.
8793 */
8794 static error_t
8795 get_set_kernel(
8796 menu_t *mp,
8797 menu_cmd_t optnum,
8798 char *path,
8799 char *buf,
8800 size_t bufsize)
8801 {
8802 int entryNum;
8803 int rv = BAM_SUCCESS;
8804 int free_new_path = 0;
8805 entry_t *entryp;
8806 line_t *ptr;
8807 line_t *kernelp;
8808 char *new_arg;
8809 char *old_args;
8810 char *space;
8811 char *new_path;
8812 char old_space;
8813 size_t old_kernel_len;
8814 size_t new_str_len;
8815 char *fstype;
8816 char *osdev;
8817 char *sign;
8818 char signbuf[PATH_MAX];
8819 int ret;
8820 const char *fcn = "get_set_kernel()";
8821
8822 assert(bufsize > 0);
8823
8824 ptr = kernelp = NULL;
8825 new_arg = old_args = space = NULL;
8826 new_path = NULL;
8827 buf[0] = '\0';
8828
8829 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
8830 bam_direct = BAM_DIRECT_MULTIBOOT);
8831 if (bam_direct != BAM_DIRECT_DBOOT) {
8832 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
8833 return (BAM_ERROR);
8834 }
8835
8836 /*
8837 * If a user changed the default entry to a non-bootadm controlled
8838 * one, we don't want to mess with it. Just print an error and
8839 * return.
8840 */
8841 if (mp->curdefault) {
8842 entryNum = s_strtol(mp->curdefault->arg);
8843 for (entryp = mp->entries; entryp; entryp = entryp->next) {
8844 if (entryp->entryNum == entryNum)
8845 break;
8846 }
8847 if ((entryp != NULL) &&
8848 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
8849 bam_error(DEFAULT_NOT_BAM);
8850 return (BAM_ERROR);
8851 }
8852 }
8853
8854 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
8855 0, &entryNum);
8856
8857 if (entryp != NULL) {
8858 for (ptr = entryp->start; ptr && ptr != entryp->end;
8859 ptr = ptr->next) {
8860 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
8861 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
8862 kernelp = ptr;
8863 break;
8864 }
8865 }
8866 if (kernelp == NULL) {
8867 bam_error(NO_KERNEL, entryNum);
8868 return (BAM_ERROR);
8869 }
8870
8871 old_kernel_len = strcspn(kernelp->arg, " \t");
8872 space = old_args = kernelp->arg + old_kernel_len;
8873 while ((*old_args == ' ') || (*old_args == '\t'))
8874 old_args++;
8875 }
8876
8877 if (path == NULL) {
8878 if (entryp == NULL) {
8879 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
8880 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8881 return (BAM_SUCCESS);
8882 }
8883 assert(kernelp);
8884 if (optnum == ARGS_CMD) {
8885 if (old_args[0] != '\0') {
8886 (void) strlcpy(buf, old_args, bufsize);
8887 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
8888 }
8889 } else {
8890 /*
8891 * We need to print the kernel, so we just turn the
8892 * first space into a '\0' and print the beginning.
8893 * We don't print anything if it's the default kernel.
8894 */
8895 old_space = *space;
8896 *space = '\0';
8897 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
8898 (void) strlcpy(buf, kernelp->arg, bufsize);
8899 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
8900 }
8901 *space = old_space;
8902 }
8903 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8904 return (BAM_SUCCESS);
8905 }
8906
8907 /*
8908 * First, check if we're resetting an entry to the default.
8909 */
8910 if ((path[0] == '\0') ||
8911 ((optnum == KERNEL_CMD) &&
8912 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
8913 if ((entryp == NULL) || (kernelp == NULL)) {
8914 /* No previous entry, it's already the default */
8915 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
8916 return (BAM_SUCCESS);
8917 }
8918
8919 /*
8920 * Check if we can delete the entry. If we're resetting the
8921 * kernel command, and the args is already empty, or if we're
8922 * resetting the args command, and the kernel is already the
8923 * default, we can restore the old default and delete the entry.
8924 */
8925 if (((optnum == KERNEL_CMD) &&
8926 ((old_args == NULL) || (old_args[0] == '\0'))) ||
8927 ((optnum == ARGS_CMD) &&
8928 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
8929 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
8930 kernelp = NULL;
8931 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
8932 restore_default_entry(mp, BAM_OLD_RC_DEF,
8933 mp->old_rc_default);
8934 mp->old_rc_default = NULL;
8935 rv = BAM_WRITE;
8936 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
8937 goto done;
8938 }
8939
8940 if (optnum == KERNEL_CMD) {
8941 /*
8942 * At this point, we've already checked that old_args
8943 * and entryp are valid pointers. The "+ 2" is for
8944 * a space a the string termination character.
8945 */
8946 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
8947 strlen(old_args) + 2;
8948 new_arg = s_calloc(1, new_str_len);
8949 (void) snprintf(new_arg, new_str_len, "%s %s",
8950 DIRECT_BOOT_KERNEL, old_args);
8951 free(kernelp->arg);
8952 kernelp->arg = new_arg;
8953
8954 /*
8955 * We have changed the kernel line, so we may need
8956 * to update the archive line as well.
8957 */
8958 set_archive_line(entryp, kernelp);
8959 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
8960 fcn, kernelp->arg));
8961 } else {
8962 /*
8963 * We're resetting the boot args to nothing, so
8964 * we only need to copy the kernel. We've already
8965 * checked that the kernel is not the default.
8966 */
8967 new_arg = s_calloc(1, old_kernel_len + 1);
8968 (void) snprintf(new_arg, old_kernel_len + 1, "%s",
8969 kernelp->arg);
8970 free(kernelp->arg);
8971 kernelp->arg = new_arg;
8972 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
8973 fcn, kernelp->arg));
8974 }
8975 rv = BAM_WRITE;
8976 goto done;
8977 }
8978
8979 /*
8980 * Expand the kernel file to a full path, if necessary
8981 */
8982 if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
8983 new_path = expand_path(path);
8984 if (new_path == NULL) {
8985 bam_error(UNKNOWN_KERNEL, path);
8986 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8987 return (BAM_ERROR);
8988 }
8989 free_new_path = 1;
8990 } else {
8991 new_path = path;
8992 free_new_path = 0;
8993 }
8994
8995 /*
8996 * At this point, we know we're setting a new value. First, take care
8997 * of the case where there was no previous entry.
8998 */
8999 if (entryp == NULL) {
9000
9001 /* Similar to code in update_temp */
9002 fstype = get_fstype("/");
9003 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9004 if (fstype == NULL) {
9005 bam_error(BOOTENV_FSTYPE_FAILED);
9006 rv = BAM_ERROR;
9007 goto done;
9008 }
9009
9010 osdev = get_special("/");
9011 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9012 if (osdev == NULL) {
9013 free(fstype);
9014 bam_error(BOOTENV_SPECIAL_FAILED);
9015 rv = BAM_ERROR;
9016 goto done;
9017 }
9018
9019 sign = find_existing_sign("/", osdev, fstype);
9020 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9021 if (sign == NULL) {
9022 free(fstype);
9023 free(osdev);
9024 bam_error(BOOTENV_SIGN_FAILED);
9025 rv = BAM_ERROR;
9026 goto done;
9027 }
9028
9029 free(osdev);
9030 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9031 free(sign);
9032 assert(strchr(signbuf, '(') == NULL &&
9033 strchr(signbuf, ',') == NULL &&
9034 strchr(signbuf, ')') == NULL);
9035
9036 if (optnum == KERNEL_CMD) {
9037 if (strcmp(fstype, "zfs") == 0) {
9038 new_str_len = strlen(new_path) +
9039 strlen(ZFS_BOOT) + 8;
9040 new_arg = s_calloc(1, new_str_len);
9041 (void) snprintf(new_arg, new_str_len, "%s %s",
9042 new_path, ZFS_BOOT);
9043 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9044 new_arg));
9045 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9046 signbuf, new_arg, NULL, NULL, NULL);
9047 free(new_arg);
9048 } else {
9049 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9050 new_path));
9051 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9052 signbuf, new_path, NULL, NULL, NULL);
9053 }
9054 } else {
9055 new_str_len = strlen(path) + 8;
9056 if (strcmp(fstype, "zfs") == 0) {
9057 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9058 new_arg = s_calloc(1, new_str_len);
9059 (void) snprintf(new_arg, new_str_len, "%s %s",
9060 DIRECT_BOOT_KERNEL_ZFS, path);
9061 } else {
9062 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9063 new_arg = s_calloc(1, new_str_len);
9064 (void) snprintf(new_arg, new_str_len, "%s %s",
9065 DIRECT_BOOT_KERNEL, path);
9066 }
9067
9068 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
9069 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9070 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9071 free(new_arg);
9072 }
9073 free(fstype);
9074 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9075 entryNum = BAM_ERROR);
9076 if (entryNum == BAM_ERROR) {
9077 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
9078 BOOTENV_RC_TITLE);
9079 rv = BAM_ERROR;
9080 goto done;
9081 }
9082 save_default_entry(mp, BAM_OLD_RC_DEF);
9083 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9084 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9085 if (ret == BAM_ERROR) {
9086 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
9087 }
9088 rv = BAM_WRITE;
9089 goto done;
9090 }
9091
9092 /*
9093 * There was already an bootenv entry which we need to edit.
9094 */
9095 if (optnum == KERNEL_CMD) {
9096 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9097 new_arg = s_calloc(1, new_str_len);
9098 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9099 old_args);
9100 free(kernelp->arg);
9101 kernelp->arg = new_arg;
9102
9103 /*
9104 * If we have changed the kernel line, we may need to update
9105 * the archive line as well.
9106 */
9107 set_archive_line(entryp, kernelp);
9108 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
9109 kernelp->arg));
9110 } else {
9111 new_str_len = old_kernel_len + strlen(path) + 8;
9112 new_arg = s_calloc(1, new_str_len);
9113 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9114 (void) strlcat(new_arg, " ", new_str_len);
9115 (void) strlcat(new_arg, path, new_str_len);
9116 free(kernelp->arg);
9117 kernelp->arg = new_arg;
9118 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
9119 kernelp->arg));
9120 }
9121 rv = BAM_WRITE;
9122
9123 done:
9124 if ((rv == BAM_WRITE) && kernelp)
9125 update_line(kernelp);
9126 if (free_new_path)
9127 free(new_path);
9128 if (rv == BAM_WRITE) {
9129 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9130 } else {
9131 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9132 }
9133 return (rv);
9134 }
9135
9136 static error_t
9137 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9138 {
9139 const char *fcn = "get_kernel()";
9140 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
9141 return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9142 }
9143
9144 static error_t
9145 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9146 {
9147 const char *fcn = "set_kernel()";
9148 assert(path != NULL);
9149 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
9150 return (get_set_kernel(mp, optnum, path, buf, bufsize));
9151 }
9152
9153 /*ARGSUSED*/
9154 static error_t
9155 set_option(menu_t *mp, char *dummy, char *opt)
9156 {
9157 int optnum;
9158 int optval;
9159 char *val;
9160 char buf[BUFSIZ] = "";
9161 error_t rv;
9162 const char *fcn = "set_option()";
9163
9164 assert(mp);
9165 assert(opt);
9166 assert(dummy == NULL);
9167
9168 /* opt is set from bam_argv[0] and is always non-NULL */
9169 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
9170
9171 val = strchr(opt, '=');
9172 if (val != NULL) {
9173 *val = '\0';
9174 }
9175
9176 if (strcmp(opt, "default") == 0) {
9177 optnum = DEFAULT_CMD;
9178 } else if (strcmp(opt, "timeout") == 0) {
9179 optnum = TIMEOUT_CMD;
9180 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9181 optnum = KERNEL_CMD;
9182 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9183 optnum = ARGS_CMD;
9184 } else {
9185 bam_error(INVALID_OPTION, opt);
9186 return (BAM_ERROR);
9187 }
9188
9189 /*
9190 * kernel and args are allowed without "=new_value" strings. All
9191 * others cause errors
9192 */
9193 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9194 bam_error(NO_OPTION_ARG, opt);
9195 return (BAM_ERROR);
9196 } else if (val != NULL) {
9197 *val = '=';
9198 }
9199
9200 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9201 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
9202 val ? val + 1 : "NULL"));
9203
9204 if (val)
9205 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9206 else
9207 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9208 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9209 (void) printf("%s\n", buf);
9210 } else {
9211 optval = s_strtol(val + 1);
9212 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
9213 rv = set_global(mp, menu_cmds[optnum], optval);
9214 }
9215
9216 if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9217 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9218 } else {
9219 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9220 }
9221
9222 return (rv);
9223 }
9224
9225 /*
9226 * The quiet argument suppresses messages. This is used
9227 * when invoked in the context of other commands (e.g. list_entry)
9228 */
9229 static error_t
9230 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9231 {
9232 line_t *lp;
9233 char *arg;
9234 int done, ret = BAM_SUCCESS;
9235
9236 assert(mp);
9237 assert(menu_path);
9238 assert(globalcmd);
9239
9240 if (mp->start == NULL) {
9241 if (!quiet)
9242 bam_error(NO_MENU, menu_path);
9243 return (BAM_ERROR);
9244 }
9245
9246 done = 0;
9247 for (lp = mp->start; lp; lp = lp->next) {
9248 if (lp->flags != BAM_GLOBAL)
9249 continue;
9250
9251 if (lp->cmd == NULL) {
9252 if (!quiet)
9253 bam_error(NO_CMD, lp->lineNum);
9254 continue;
9255 }
9256
9257 if (strcmp(globalcmd, lp->cmd) != 0)
9258 continue;
9259
9260 /* Found global. Check for duplicates */
9261 if (done && !quiet) {
9262 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
9263 ret = BAM_ERROR;
9264 }
9265
9266 arg = lp->arg ? lp->arg : "";
9267 bam_print(GLOBAL_CMD, globalcmd, arg);
9268 done = 1;
9269 }
9270
9271 if (!done && bam_verbose)
9272 bam_print(NO_ENTRY, globalcmd);
9273
9274 return (ret);
9275 }
9276
9277 static error_t
9278 menu_write(char *root, menu_t *mp)
9279 {
9280 const char *fcn = "menu_write()";
9281
9282 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
9283 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9284 }
9285
9286 void
9287 line_free(line_t *lp)
9288 {
9289 if (lp == NULL)
9290 return;
9291
9292 if (lp->cmd != NULL)
9293 free(lp->cmd);
9294 if (lp->sep)
9295 free(lp->sep);
9296 if (lp->arg)
9297 free(lp->arg);
9298 if (lp->line)
9299 free(lp->line);
9300 free(lp);
9301 }
9302
9303 static void
9304 linelist_free(line_t *start)
9305 {
9306 line_t *lp;
9307
9308 while (start) {
9309 lp = start;
9310 start = start->next;
9311 line_free(lp);
9312 }
9313 }
9314
9315 static void
9316 filelist_free(filelist_t *flistp)
9317 {
9318 linelist_free(flistp->head);
9319 flistp->head = NULL;
9320 flistp->tail = NULL;
9321 }
9322
9323 static void
9324 menu_free(menu_t *mp)
9325 {
9326 entry_t *ent, *tmp;
9327 assert(mp);
9328
9329 if (mp->start)
9330 linelist_free(mp->start);
9331 ent = mp->entries;
9332 while (ent) {
9333 tmp = ent;
9334 ent = tmp->next;
9335 free(tmp);
9336 }
9337
9338 free(mp);
9339 }
9340
9341 /*
9342 * Utility routines
9343 */
9344
9345
9346 /*
9347 * Returns 0 on success
9348 * Any other value indicates an error
9349 */
9350 static int
9351 exec_cmd(char *cmdline, filelist_t *flistp)
9352 {
9353 char buf[BUFSIZ];
9354 int ret;
9355 FILE *ptr;
9356 sigset_t set;
9357 void (*disp)(int);
9358
9359 /*
9360 * For security
9361 * - only absolute paths are allowed
9362 * - set IFS to space and tab
9363 */
9364 if (*cmdline != '/') {
9365 bam_error(ABS_PATH_REQ, cmdline);
9366 return (-1);
9367 }
9368 (void) putenv("IFS= \t");
9369
9370 /*
9371 * We may have been exec'ed with SIGCHLD blocked
9372 * unblock it here
9373 */
9374 (void) sigemptyset(&set);
9375 (void) sigaddset(&set, SIGCHLD);
9376 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9377 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
9378 return (-1);
9379 }
9380
9381 /*
9382 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9383 */
9384 disp = sigset(SIGCHLD, SIG_DFL);
9385 if (disp == SIG_ERR) {
9386 bam_error(FAILED_SIG, strerror(errno));
9387 return (-1);
9388 }
9389 if (disp == SIG_HOLD) {
9390 bam_error(BLOCKED_SIG, cmdline);
9391 return (-1);
9392 }
9393
9394 ptr = popen(cmdline, "r");
9395 if (ptr == NULL) {
9396 bam_error(POPEN_FAIL, cmdline, strerror(errno));
9397 return (-1);
9398 }
9399
9400 /*
9401 * If we simply do a pclose() following a popen(), pclose()
9402 * will close the reader end of the pipe immediately even
9403 * if the child process has not started/exited. pclose()
9404 * does wait for cmd to terminate before returning though.
9405 * When the executed command writes its output to the pipe
9406 * there is no reader process and the command dies with
9407 * SIGPIPE. To avoid this we read repeatedly until read
9408 * terminates with EOF. This indicates that the command
9409 * (writer) has closed the pipe and we can safely do a
9410 * pclose().
9411 *
9412 * Since pclose() does wait for the command to exit,
9413 * we can safely reap the exit status of the command
9414 * from the value returned by pclose()
9415 */
9416 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9417 if (flistp == NULL) {
9418 /* s_fgets strips newlines, so insert them at the end */
9419 bam_print(PRINT, buf);
9420 } else {
9421 append_to_flist(flistp, buf);
9422 }
9423 }
9424
9425 ret = pclose(ptr);
9426 if (ret == -1) {
9427 bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
9428 return (-1);
9429 }
9430
9431 if (WIFEXITED(ret)) {
9432 return (WEXITSTATUS(ret));
9433 } else {
9434 bam_error(EXEC_FAIL, cmdline, ret);
9435 return (-1);
9436 }
9437 }
9438
9439 /*
9440 * Since this function returns -1 on error
9441 * it cannot be used to convert -1. However,
9442 * that is sufficient for what we need.
9443 */
9444 static long
9445 s_strtol(char *str)
9446 {
9447 long l;
9448 char *res = NULL;
9449
9450 if (str == NULL) {
9451 return (-1);
9452 }
9453
9454 errno = 0;
9455 l = strtol(str, &res, 10);
9456 if (errno || *res != '\0') {
9457 return (-1);
9458 }
9459
9460 return (l);
9461 }
9462
9463 /*
9464 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9465 */
9466 static int
9467 s_fputs(char *str, FILE *fp)
9468 {
9469 char linebuf[BAM_MAXLINE];
9470
9471 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9472 return (fputs(linebuf, fp));
9473 }
9474
9475 /*
9476 * Wrapper around fgets, that strips newlines returned by fgets
9477 */
9478 char *
9479 s_fgets(char *buf, int buflen, FILE *fp)
9480 {
9481 int n;
9482
9483 buf = fgets(buf, buflen, fp);
9484 if (buf) {
9485 n = strlen(buf);
9486 if (n == buflen - 1 && buf[n-1] != '\n')
9487 bam_error(TOO_LONG, buflen - 1, buf);
9488 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9489 }
9490
9491 return (buf);
9492 }
9493
9494 void *
9495 s_calloc(size_t nelem, size_t sz)
9496 {
9497 void *ptr;
9498
9499 ptr = calloc(nelem, sz);
9500 if (ptr == NULL) {
9501 bam_error(NO_MEM, nelem*sz);
9502 bam_exit(1);
9503 }
9504 return (ptr);
9505 }
9506
9507 void *
9508 s_realloc(void *ptr, size_t sz)
9509 {
9510 ptr = realloc(ptr, sz);
9511 if (ptr == NULL) {
9512 bam_error(NO_MEM, sz);
9513 bam_exit(1);
9514 }
9515 return (ptr);
9516 }
9517
9518 char *
9519 s_strdup(char *str)
9520 {
9521 char *ptr;
9522
9523 if (str == NULL)
9524 return (NULL);
9525
9526 ptr = strdup(str);
9527 if (ptr == NULL) {
9528 bam_error(NO_MEM, strlen(str) + 1);
9529 bam_exit(1);
9530 }
9531 return (ptr);
9532 }
9533
9534 /*
9535 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9536 * Returns 0 otherwise
9537 */
9538 static int
9539 is_amd64(void)
9540 {
9541 static int amd64 = -1;
9542 char isabuf[257]; /* from sysinfo(2) manpage */
9543
9544 if (amd64 != -1)
9545 return (amd64);
9546
9547 if (bam_alt_platform) {
9548 if (strcmp(bam_platform, "i86pc") == 0) {
9549 amd64 = 1; /* diskless server */
9550 }
9551 } else {
9552 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
9553 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
9554 amd64 = 1;
9555 } else if (strstr(isabuf, "i386") == NULL) {
9556 amd64 = 1; /* diskless server */
9557 }
9558 }
9559 if (amd64 == -1)
9560 amd64 = 0;
9561
9562 return (amd64);
9563 }
9564
9565 static char *
9566 get_machine(void)
9567 {
9568 static int cached = -1;
9569 static char mbuf[257]; /* from sysinfo(2) manpage */
9570
9571 if (cached == 0)
9572 return (mbuf);
9573
9574 if (bam_alt_platform) {
9575 return (bam_platform);
9576 } else {
9577 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
9578 cached = 1;
9579 }
9580 }
9581 if (cached == -1) {
9582 mbuf[0] = '\0';
9583 cached = 0;
9584 }
9585
9586 return (mbuf);
9587 }
9588
9589 int
9590 is_sparc(void)
9591 {
9592 static int issparc = -1;
9593 char mbuf[257]; /* from sysinfo(2) manpage */
9594
9595 if (issparc != -1)
9596 return (issparc);
9597
9598 if (bam_alt_platform) {
9599 if (strncmp(bam_platform, "sun4", 4) == 0) {
9600 issparc = 1;
9601 }
9602 } else {
9603 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
9604 strcmp(mbuf, "sparc") == 0) {
9605 issparc = 1;
9606 }
9607 }
9608 if (issparc == -1)
9609 issparc = 0;
9610
9611 return (issparc);
9612 }
9613
9614 static void
9615 append_to_flist(filelist_t *flistp, char *s)
9616 {
9617 line_t *lp;
9618
9619 lp = s_calloc(1, sizeof (line_t));
9620 lp->line = s_strdup(s);
9621 if (flistp->head == NULL)
9622 flistp->head = lp;
9623 else
9624 flistp->tail->next = lp;
9625 flistp->tail = lp;
9626 }
9627
9628 #if !defined(_OPB)
9629
9630 UCODE_VENDORS;
9631
9632 /*ARGSUSED*/
9633 static void
9634 ucode_install(char *root)
9635 {
9636 int i;
9637
9638 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
9639 int cmd_len = PATH_MAX + 256;
9640 char cmd[PATH_MAX + 256];
9641 char file[PATH_MAX];
9642 char timestamp[PATH_MAX];
9643 struct stat fstatus, tstatus;
9644 struct utimbuf u_times;
9645
9646 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
9647 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
9648 ucode_vendors[i].extstr);
9649
9650 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
9651 continue;
9652
9653 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
9654
9655 if (stat(timestamp, &tstatus) == 0 &&
9656 fstatus.st_mtime <= tstatus.st_mtime)
9657 continue;
9658
9659 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
9660 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
9661 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
9662 if (system(cmd) != 0)
9663 return;
9664
9665 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
9666 return;
9667
9668 u_times.actime = fstatus.st_atime;
9669 u_times.modtime = fstatus.st_mtime;
9670 (void) utime(timestamp, &u_times);
9671 }
9672 }
9673 #endif