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 2014 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 "/dev/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 (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3470 strlen(RAMDISK_SPECIAL)) == 0) {
3471 if (bam_verbose)
3472 bam_error(IS_RAMDISK, bam_root);
3473 (void) fclose(fp);
3474 return (1);
3475 }
3476
3477 (void) fclose(fp);
3478
3479 return (0);
3480 }
3481
3482 static int
3483 is_boot_archive(char *root)
3484 {
3485 char path[PATH_MAX];
3486 struct stat sb;
3487 int error;
3488 const char *fcn = "is_boot_archive()";
3489
3490 /*
3491 * We can't create an archive without the create_ramdisk script
3492 */
3493 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3494 error = stat(path, &sb);
3495 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3496 if (error == -1) {
3497 if (bam_verbose)
3498 bam_print(FILE_MISS, path);
3499 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
3500 return (0);
3501 }
3502
3503 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
3504 return (1);
3505 }
3506
3507 /*
3508 * Need to call this for anything that operates on the GRUB menu
3509 * In the x86 live upgrade case the directory /boot/grub may be present
3510 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3511 * is to check for the presence of the stage2 binary which is present
3512 * only on GRUB targets (even on x86 boot partitions). Checking for the
3513 * presence of the multiboot binary is not correct as it is not present
3514 * on x86 boot partitions.
3515 */
3516 int
3517 is_grub(const char *root)
3518 {
3519 char path[PATH_MAX];
3520 struct stat sb;
3521 const char *fcn = "is_grub()";
3522
3523 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3524 if (stat(path, &sb) == -1) {
3525 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
3526 return (0);
3527 }
3528
3529 return (1);
3530 }
3531
3532 static int
3533 is_zfs(char *root)
3534 {
3535 struct statvfs vfs;
3536 int ret;
3537 const char *fcn = "is_zfs()";
3538
3539 ret = statvfs(root, &vfs);
3540 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3541 if (ret != 0) {
3542 bam_error(STATVFS_FAIL, root, strerror(errno));
3543 return (0);
3544 }
3545
3546 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3547 BAM_DPRINTF((D_IS_ZFS, fcn, root));
3548 return (1);
3549 } else {
3550 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
3551 return (0);
3552 }
3553 }
3554
3555 static int
3556 is_ufs(char *root)
3557 {
3558 struct statvfs vfs;
3559 int ret;
3560 const char *fcn = "is_ufs()";
3561
3562 ret = statvfs(root, &vfs);
3563 INJECT_ERROR1("STATVFS_UFS", ret = 1);
3564 if (ret != 0) {
3565 bam_error(STATVFS_FAIL, root, strerror(errno));
3566 return (0);
3567 }
3568
3569 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
3570 BAM_DPRINTF((D_IS_UFS, fcn, root));
3571 return (1);
3572 } else {
3573 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
3574 return (0);
3575 }
3576 }
3577
3578 static int
3579 is_pcfs(char *root)
3580 {
3581 struct statvfs vfs;
3582 int ret;
3583 const char *fcn = "is_pcfs()";
3584
3585 ret = statvfs(root, &vfs);
3586 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
3587 if (ret != 0) {
3588 bam_error(STATVFS_FAIL, root, strerror(errno));
3589 return (0);
3590 }
3591
3592 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
3593 BAM_DPRINTF((D_IS_PCFS, fcn, root));
3594 return (1);
3595 } else {
3596 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
3597 return (0);
3598 }
3599 }
3600
3601 static int
3602 is_readonly(char *root)
3603 {
3604 int fd;
3605 int error;
3606 char testfile[PATH_MAX];
3607 const char *fcn = "is_readonly()";
3608
3609 /*
3610 * Using statvfs() to check for a read-only filesystem is not
3611 * reliable. The only way to reliably test is to attempt to
3612 * create a file
3613 */
3614 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
3615 root, BOOTADM_RDONLY_TEST, getpid());
3616
3617 (void) unlink(testfile);
3618
3619 errno = 0;
3620 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
3621 error = errno;
3622 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
3623 if (fd == -1 && error == EROFS) {
3624 BAM_DPRINTF((D_RDONLY_FS, fcn, root));
3625 return (1);
3626 } else if (fd == -1) {
3627 bam_error(RDONLY_TEST_ERROR, root, strerror(error));
3628 }
3629
3630 (void) close(fd);
3631 (void) unlink(testfile);
3632
3633 BAM_DPRINTF((D_RDWR_FS, fcn, root));
3634 return (0);
3635 }
3636
3637 static error_t
3638 update_archive(char *root, char *opt)
3639 {
3640 error_t ret;
3641
3642 assert(root);
3643 assert(opt == NULL);
3644
3645 init_walk_args();
3646 (void) umask(022);
3647
3648 /*
3649 * Never update non-BE root in update_all
3650 */
3651 if (!is_be(root) && bam_update_all)
3652 return (BAM_SUCCESS);
3653 /*
3654 * root must belong to a boot archive based OS,
3655 */
3656 if (!is_boot_archive(root)) {
3657 /*
3658 * Emit message only if not in context of update_all.
3659 * If in update_all, emit only if verbose flag is set.
3660 */
3661 if (!bam_update_all || bam_verbose)
3662 bam_print(NOT_ARCHIVE_BOOT, root);
3663 return (BAM_ERROR);
3664 }
3665
3666 /*
3667 * If smf check is requested when / is writable (can happen
3668 * on first reboot following an upgrade because service
3669 * dependency is messed up), skip the check.
3670 */
3671 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
3672 return (BAM_SUCCESS);
3673
3674 /*
3675 * Don't generate archive on ramdisk.
3676 */
3677 if (is_ramdisk(root))
3678 return (BAM_SUCCESS);
3679
3680 /*
3681 * root must be writable. This check applies to alternate
3682 * root (-R option); bam_root_readonly applies to '/' only.
3683 * The behaviour translates into being the one of a 'check'.
3684 */
3685 if (!bam_smf_check && !bam_check && is_readonly(root)) {
3686 set_flag(RDONLY_FSCHK);
3687 bam_check = 1;
3688 }
3689
3690 /*
3691 * Now check if an update is really needed.
3692 */
3693 ret = update_required(root);
3694
3695 /*
3696 * The check command (-n) is *not* a dry run.
3697 * It only checks if the archive is in sync.
3698 * A readonly filesystem has to be considered an error only if an update
3699 * is required.
3700 */
3701 if (bam_nowrite()) {
3702 if (is_flag_on(RDONLY_FSCHK)) {
3703 bam_check = bam_saved_check;
3704 if (ret > 0)
3705 bam_error(RDONLY_FS, root);
3706 if (bam_update_all)
3707 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
3708 }
3709
3710 bam_exit((ret != 0) ? 1 : 0);
3711 }
3712
3713 if (ret == 1) {
3714 /* create the ramdisk */
3715 ret = create_ramdisk(root);
3716 }
3717
3718 /*
3719 * if the archive is updated, save the new stat data and update the
3720 * timestamp file
3721 */
3722 if (ret == 0 && walk_arg.new_nvlp != NULL) {
3723 savenew(root);
3724 update_timestamp(root);
3725 }
3726
3727 clear_walk_args();
3728
3729 return (ret);
3730 }
3731
3732 static char *
3733 find_root_pool()
3734 {
3735 char *special = get_special("/");
3736 char *p;
3737
3738 if (special == NULL)
3739 return (NULL);
3740
3741 if (*special == '/') {
3742 free(special);
3743 return (NULL);
3744 }
3745
3746 if ((p = strchr(special, '/')) != NULL)
3747 *p = '\0';
3748
3749 return (special);
3750 }
3751
3752 static error_t
3753 synchronize_BE_menu(void)
3754 {
3755 struct stat sb;
3756 char cmdline[PATH_MAX];
3757 char cksum_line[PATH_MAX];
3758 filelist_t flist = {0};
3759 char *old_cksum_str;
3760 char *old_size_str;
3761 char *old_file;
3762 char *curr_cksum_str;
3763 char *curr_size_str;
3764 char *curr_file;
3765 char *pool = NULL;
3766 char *mntpt = NULL;
3767 zfs_mnted_t mnted;
3768 FILE *cfp;
3769 int found;
3770 int ret;
3771 const char *fcn = "synchronize_BE_menu()";
3772
3773 BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
3774
3775 /* Check if findroot enabled LU BE */
3776 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
3777 BAM_DPRINTF((D_NOT_LU_BE, fcn));
3778 return (BAM_SUCCESS);
3779 }
3780
3781 if (stat(LU_MENU_CKSUM, &sb) != 0) {
3782 BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
3783 goto menu_sync;
3784 }
3785
3786 cfp = fopen(LU_MENU_CKSUM, "r");
3787 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
3788 if (cfp == NULL) {
3789 bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
3790 goto menu_sync;
3791 }
3792 BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
3793
3794 found = 0;
3795 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
3796 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
3797 if (found) {
3798 bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
3799 (void) fclose(cfp);
3800 goto menu_sync;
3801 }
3802 found = 1;
3803 }
3804 BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
3805
3806
3807 old_cksum_str = strtok(cksum_line, " \t");
3808 old_size_str = strtok(NULL, " \t");
3809 old_file = strtok(NULL, " \t");
3810
3811 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
3812 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
3813 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
3814 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
3815 bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
3816 goto menu_sync;
3817 }
3818 BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
3819
3820 /* Get checksum of current menu */
3821 pool = find_root_pool();
3822 if (pool) {
3823 mntpt = mount_top_dataset(pool, &mnted);
3824 if (mntpt == NULL) {
3825 bam_error(FAIL_MNT_TOP_DATASET, pool);
3826 free(pool);
3827 return (BAM_ERROR);
3828 }
3829 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
3830 CKSUM, mntpt, GRUB_MENU);
3831 } else {
3832 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
3833 CKSUM, GRUB_MENU);
3834 }
3835 ret = exec_cmd(cmdline, &flist);
3836 if (pool) {
3837 (void) umount_top_dataset(pool, mnted, mntpt);
3838 free(pool);
3839 }
3840 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
3841 if (ret != 0) {
3842 bam_error(MENU_CKSUM_FAIL);
3843 return (BAM_ERROR);
3844 }
3845 BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
3846
3847 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
3848 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3849 bam_error(BAD_CKSUM);
3850 filelist_free(&flist);
3851 return (BAM_ERROR);
3852 }
3853 BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
3854
3855 curr_cksum_str = strtok(flist.head->line, " \t");
3856 curr_size_str = strtok(NULL, " \t");
3857 curr_file = strtok(NULL, " \t");
3858
3859 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
3860 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
3861 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
3862 if (curr_cksum_str == NULL || curr_size_str == NULL ||
3863 curr_file == NULL) {
3864 bam_error(BAD_CKSUM_PARSE);
3865 filelist_free(&flist);
3866 return (BAM_ERROR);
3867 }
3868 BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
3869
3870 if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
3871 strcmp(old_size_str, curr_size_str) == 0 &&
3872 strcmp(old_file, curr_file) == 0) {
3873 filelist_free(&flist);
3874 BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
3875 return (BAM_SUCCESS);
3876 }
3877
3878 filelist_free(&flist);
3879
3880 /* cksum doesn't match - the menu has changed */
3881 BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
3882
3883 menu_sync:
3884 bam_print(PROP_GRUB_MENU);
3885
3886 (void) snprintf(cmdline, sizeof (cmdline),
3887 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
3888 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
3889 ret = exec_cmd(cmdline, NULL);
3890 INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
3891 if (ret != 0) {
3892 bam_error(MENU_PROP_FAIL);
3893 return (BAM_ERROR);
3894 }
3895 BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
3896
3897 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
3898 GRUB_MENU, GRUB_BACKUP_MENU);
3899 ret = exec_cmd(cmdline, NULL);
3900 INJECT_ERROR1("CREATE_BACKUP", ret = 1);
3901 if (ret != 0) {
3902 bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
3903 return (BAM_ERROR);
3904 }
3905 BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
3906
3907 (void) snprintf(cmdline, sizeof (cmdline),
3908 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
3909 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
3910 ret = exec_cmd(cmdline, NULL);
3911 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
3912 if (ret != 0) {
3913 bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
3914 return (BAM_ERROR);
3915 }
3916 BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
3917
3918 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
3919 CKSUM, GRUB_MENU, LU_MENU_CKSUM);
3920 ret = exec_cmd(cmdline, NULL);
3921 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
3922 if (ret != 0) {
3923 bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
3924 return (BAM_ERROR);
3925 }
3926 BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
3927
3928 (void) snprintf(cmdline, sizeof (cmdline),
3929 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
3930 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
3931 ret = exec_cmd(cmdline, NULL);
3932 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
3933 if (ret != 0) {
3934 bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
3935 return (BAM_ERROR);
3936 }
3937 BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
3938
3939 return (BAM_SUCCESS);
3940 }
3941
3942 static error_t
3943 update_all(char *root, char *opt)
3944 {
3945 struct extmnttab mnt;
3946 struct stat sb;
3947 FILE *fp;
3948 char multibt[PATH_MAX];
3949 char creatram[PATH_MAX];
3950 error_t ret = BAM_SUCCESS;
3951
3952 assert(root);
3953 assert(opt == NULL);
3954
3955 if (bam_rootlen != 1 || *root != '/') {
3956 elide_trailing_slash(root, multibt, sizeof (multibt));
3957 bam_error(ALT_ROOT_INVALID, multibt);
3958 return (BAM_ERROR);
3959 }
3960
3961 /*
3962 * First update archive for current root
3963 */
3964 if (update_archive(root, opt) != BAM_SUCCESS)
3965 ret = BAM_ERROR;
3966
3967 if (ret == BAM_ERROR)
3968 goto out;
3969
3970 /*
3971 * Now walk the mount table, performing archive update
3972 * for all mounted Newboot root filesystems
3973 */
3974 fp = fopen(MNTTAB, "r");
3975 if (fp == NULL) {
3976 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3977 ret = BAM_ERROR;
3978 goto out;
3979 }
3980
3981 resetmnttab(fp);
3982
3983 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3984 if (mnt.mnt_special == NULL)
3985 continue;
3986 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
3987 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
3988 continue;
3989 if (strcmp(mnt.mnt_mountp, "/") == 0)
3990 continue;
3991
3992 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
3993 mnt.mnt_mountp, CREATE_RAMDISK);
3994
3995 if (stat(creatram, &sb) == -1)
3996 continue;
3997
3998 /*
3999 * We put a trailing slash to be consistent with root = "/"
4000 * case, such that we don't have to print // in some cases.
4001 */
4002 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4003 mnt.mnt_mountp);
4004 bam_rootlen = strlen(rootbuf);
4005
4006 /*
4007 * It's possible that other mounts may be an alternate boot
4008 * architecture, so check it again.
4009 */
4010 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4011 (update_archive(rootbuf, opt) != BAM_SUCCESS))
4012 ret = BAM_ERROR;
4013 }
4014
4015 (void) fclose(fp);
4016
4017 out:
4018 /*
4019 * We no longer use biosdev for Live Upgrade. Hence
4020 * there is no need to defer (to shutdown time) any fdisk
4021 * updates
4022 */
4023 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4024 bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
4025 }
4026
4027 /*
4028 * If user has updated menu in current BE, propagate the
4029 * updates to all BEs.
4030 */
4031 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4032 ret = BAM_ERROR;
4033
4034 return (ret);
4035 }
4036
4037 static void
4038 append_line(menu_t *mp, line_t *lp)
4039 {
4040 if (mp->start == NULL) {
4041 mp->start = lp;
4042 } else {
4043 mp->end->next = lp;
4044 lp->prev = mp->end;
4045 }
4046 mp->end = lp;
4047 }
4048
4049 void
4050 unlink_line(menu_t *mp, line_t *lp)
4051 {
4052 /* unlink from list */
4053 if (lp->prev)
4054 lp->prev->next = lp->next;
4055 else
4056 mp->start = lp->next;
4057 if (lp->next)
4058 lp->next->prev = lp->prev;
4059 else
4060 mp->end = lp->prev;
4061 }
4062
4063 static entry_t *
4064 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4065 {
4066 entry_t *ent, *prev;
4067 const char *fcn = "boot_entry_new()";
4068
4069 assert(mp);
4070 assert(start);
4071 assert(end);
4072
4073 ent = s_calloc(1, sizeof (entry_t));
4074 BAM_DPRINTF((D_ENTRY_NEW, fcn));
4075 ent->start = start;
4076 ent->end = end;
4077
4078 if (mp->entries == NULL) {
4079 mp->entries = ent;
4080 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
4081 return (ent);
4082 }
4083
4084 prev = mp->entries;
4085 while (prev->next)
4086 prev = prev->next;
4087 prev->next = ent;
4088 ent->prev = prev;
4089 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
4090 return (ent);
4091 }
4092
4093 static void
4094 boot_entry_addline(entry_t *ent, line_t *lp)
4095 {
4096 if (ent)
4097 ent->end = lp;
4098 }
4099
4100 /*
4101 * Check whether cmd matches the one indexed by which, and whether arg matches
4102 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4103 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4104 * strstr(), so it can be a partial match.
4105 */
4106 static int
4107 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4108 {
4109 int ret;
4110 const char *fcn = "check_cmd()";
4111
4112 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
4113
4114 if (cmd != NULL) {
4115 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4116 (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4117 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
4118 fcn, cmd, menu_cmds[which]));
4119 return (0);
4120 }
4121 ret = (strstr(arg, str) != NULL);
4122 } else
4123 ret = 0;
4124
4125 if (ret) {
4126 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
4127 } else {
4128 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
4129 }
4130
4131 return (ret);
4132 }
4133
4134 static error_t
4135 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4136 {
4137 const char *fcn = "kernel_parser()";
4138
4139 assert(entry);
4140 assert(cmd);
4141 assert(arg);
4142
4143 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4144 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4145 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
4146 return (BAM_ERROR);
4147 }
4148
4149 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4150 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
4151 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4152 } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4153 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4154 BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
4155 entry->flags |= BAM_ENTRY_DBOOT;
4156 } else if (strncmp(arg, DIRECT_BOOT_64,
4157 sizeof (DIRECT_BOOT_64) - 1) == 0) {
4158 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
4159 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4160 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4161 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4162 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
4163 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4164 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4165 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4166 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg));
4167 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4168 | BAM_ENTRY_32BIT;
4169 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4170 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4171 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg));
4172 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4173 | BAM_ENTRY_64BIT;
4174 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4175 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
4176 entry->flags |= BAM_ENTRY_MULTIBOOT;
4177 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4178 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4179 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
4180 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4181 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4182 BAM_DPRINTF((D_SET_HV, fcn, arg));
4183 entry->flags |= BAM_ENTRY_HV;
4184 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4185 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
4186 return (BAM_ERROR);
4187 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4188 strstr(arg, UNIX_SPACE)) {
4189 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4190 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4191 strstr(arg, AMD_UNIX_SPACE)) {
4192 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4193 } else {
4194 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
4195 bam_error(UNKNOWN_KERNEL_LINE, linenum);
4196 return (BAM_ERROR);
4197 }
4198
4199 return (BAM_SUCCESS);
4200 }
4201
4202 static error_t
4203 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4204 {
4205 const char *fcn = "module_parser()";
4206
4207 assert(entry);
4208 assert(cmd);
4209 assert(arg);
4210
4211 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4212 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4213 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
4214 return (BAM_ERROR);
4215 }
4216
4217 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4218 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4219 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4220 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4221 strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4222 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4223 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4224 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4225 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4226 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
4227 return (BAM_SUCCESS);
4228 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4229 !(entry->flags & BAM_ENTRY_LU)) {
4230 /* don't emit warning for hand entries */
4231 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
4232 return (BAM_ERROR);
4233 } else {
4234 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
4235 bam_error(UNKNOWN_MODULE_LINE, linenum);
4236 return (BAM_ERROR);
4237 }
4238 }
4239
4240 /*
4241 * A line in menu.lst looks like
4242 * [ ]*<cmd>[ \t=]*<arg>*
4243 */
4244 static void
4245 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4246 {
4247 /*
4248 * save state across calls. This is so that
4249 * header gets the right entry# after title has
4250 * been processed
4251 */
4252 static line_t *prev = NULL;
4253 static entry_t *curr_ent = NULL;
4254 static int in_liveupgrade = 0;
4255 static int is_libbe_ent = 0;
4256
4257 line_t *lp;
4258 char *cmd, *sep, *arg;
4259 char save, *cp, *line;
4260 menu_flag_t flag = BAM_INVALID;
4261 const char *fcn = "line_parser()";
4262
4263 if (str == NULL) {
4264 return;
4265 }
4266
4267 /*
4268 * First save a copy of the entire line.
4269 * We use this later to set the line field.
4270 */
4271 line = s_strdup(str);
4272
4273 /* Eat up leading whitespace */
4274 while (*str == ' ' || *str == '\t')
4275 str++;
4276
4277 if (*str == '#') { /* comment */
4278 cmd = s_strdup("#");
4279 sep = NULL;
4280 arg = s_strdup(str + 1);
4281 flag = BAM_COMMENT;
4282 if (strstr(arg, BAM_LU_HDR) != NULL) {
4283 in_liveupgrade = 1;
4284 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4285 in_liveupgrade = 0;
4286 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4287 is_libbe_ent = 1;
4288 }
4289 } else if (*str == '\0') { /* blank line */
4290 cmd = sep = arg = NULL;
4291 flag = BAM_EMPTY;
4292 } else {
4293 /*
4294 * '=' is not a documented separator in grub syntax.
4295 * However various development bits use '=' as a
4296 * separator. In addition, external users also
4297 * use = as a separator. So we will allow that usage.
4298 */
4299 cp = str;
4300 while (*str != ' ' && *str != '\t' && *str != '=') {
4301 if (*str == '\0') {
4302 cmd = s_strdup(cp);
4303 sep = arg = NULL;
4304 break;
4305 }
4306 str++;
4307 }
4308
4309 if (*str != '\0') {
4310 save = *str;
4311 *str = '\0';
4312 cmd = s_strdup(cp);
4313 *str = save;
4314
4315 str++;
4316 save = *str;
4317 *str = '\0';
4318 sep = s_strdup(str - 1);
4319 *str = save;
4320
4321 while (*str == ' ' || *str == '\t')
4322 str++;
4323 if (*str == '\0')
4324 arg = NULL;
4325 else
4326 arg = s_strdup(str);
4327 }
4328 }
4329
4330 lp = s_calloc(1, sizeof (line_t));
4331
4332 lp->cmd = cmd;
4333 lp->sep = sep;
4334 lp->arg = arg;
4335 lp->line = line;
4336 lp->lineNum = ++(*lineNum);
4337 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4338 lp->entryNum = ++(*entryNum);
4339 lp->flags = BAM_TITLE;
4340 if (prev && prev->flags == BAM_COMMENT &&
4341 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4342 prev->entryNum = lp->entryNum;
4343 curr_ent = boot_entry_new(mp, prev, lp);
4344 curr_ent->flags |= BAM_ENTRY_BOOTADM;
4345 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
4346 } else {
4347 curr_ent = boot_entry_new(mp, lp, lp);
4348 if (in_liveupgrade) {
4349 curr_ent->flags |= BAM_ENTRY_LU;
4350 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
4351 }
4352 }
4353 curr_ent->entryNum = *entryNum;
4354 } else if (flag != BAM_INVALID) {
4355 /*
4356 * For header comments, the entry# is "fixed up"
4357 * by the subsequent title
4358 */
4359 lp->entryNum = *entryNum;
4360 lp->flags = flag;
4361 } else {
4362 lp->entryNum = *entryNum;
4363
4364 if (*entryNum == ENTRY_INIT) {
4365 lp->flags = BAM_GLOBAL;
4366 } else {
4367 lp->flags = BAM_ENTRY;
4368
4369 if (cmd && arg) {
4370 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4371 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
4372 curr_ent->flags |= BAM_ENTRY_ROOT;
4373 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4374 == 0) {
4375 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
4376 arg));
4377 curr_ent->flags |= BAM_ENTRY_FINDROOT;
4378 } else if (strcmp(cmd,
4379 menu_cmds[CHAINLOADER_CMD]) == 0) {
4380 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
4381 arg));
4382 curr_ent->flags |=
4383 BAM_ENTRY_CHAINLOADER;
4384 } else if (kernel_parser(curr_ent, cmd, arg,
4385 lp->lineNum) != BAM_SUCCESS) {
4386 (void) module_parser(curr_ent, cmd,
4387 arg, lp->lineNum);
4388 }
4389 }
4390 }
4391 }
4392
4393 /* record default, old default, and entry line ranges */
4394 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4395 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4396 mp->curdefault = lp;
4397 } else if (lp->flags == BAM_COMMENT &&
4398 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4399 mp->olddefault = lp;
4400 } else if (lp->flags == BAM_COMMENT &&
4401 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4402 mp->old_rc_default = lp;
4403 } else if (lp->flags == BAM_ENTRY ||
4404 (lp->flags == BAM_COMMENT &&
4405 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4406 if (is_libbe_ent) {
4407 curr_ent->flags |= BAM_ENTRY_LIBBE;
4408 is_libbe_ent = 0;
4409 }
4410
4411 boot_entry_addline(curr_ent, lp);
4412 }
4413 append_line(mp, lp);
4414
4415 prev = lp;
4416 }
4417
4418 void
4419 update_numbering(menu_t *mp)
4420 {
4421 int lineNum;
4422 int entryNum;
4423 int old_default_value;
4424 line_t *lp, *prev, *default_lp, *default_entry;
4425 char buf[PATH_MAX];
4426
4427 if (mp->start == NULL) {
4428 return;
4429 }
4430
4431 lineNum = LINE_INIT;
4432 entryNum = ENTRY_INIT;
4433 old_default_value = ENTRY_INIT;
4434 lp = default_lp = default_entry = NULL;
4435
4436 prev = NULL;
4437 for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4438 lp->lineNum = ++lineNum;
4439
4440 /*
4441 * Get the value of the default command
4442 */
4443 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4444 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4445 lp->arg) {
4446 old_default_value = atoi(lp->arg);
4447 default_lp = lp;
4448 }
4449
4450 /*
4451 * If not a booting entry, nothing else to fix for this
4452 * entry
4453 */
4454 if (lp->entryNum == ENTRY_INIT)
4455 continue;
4456
4457 /*
4458 * Record the position of the default entry.
4459 * The following works because global
4460 * commands like default and timeout should precede
4461 * actual boot entries, so old_default_value
4462 * is already known (or default cmd is missing).
4463 */
4464 if (default_entry == NULL &&
4465 old_default_value != ENTRY_INIT &&
4466 lp->entryNum == old_default_value) {
4467 default_entry = lp;
4468 }
4469
4470 /*
4471 * Now fixup the entry number
4472 */
4473 if (lp->cmd != NULL &&
4474 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4475 lp->entryNum = ++entryNum;
4476 /* fixup the bootadm header */
4477 if (prev && prev->flags == BAM_COMMENT &&
4478 prev->arg &&
4479 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4480 prev->entryNum = lp->entryNum;
4481 }
4482 } else {
4483 lp->entryNum = entryNum;
4484 }
4485 }
4486
4487 /*
4488 * No default command in menu, simply return
4489 */
4490 if (default_lp == NULL) {
4491 return;
4492 }
4493
4494 free(default_lp->arg);
4495 free(default_lp->line);
4496
4497 if (default_entry == NULL) {
4498 default_lp->arg = s_strdup("0");
4499 } else {
4500 (void) snprintf(buf, sizeof (buf), "%d",
4501 default_entry->entryNum);
4502 default_lp->arg = s_strdup(buf);
4503 }
4504
4505 /*
4506 * The following is required since only the line field gets
4507 * written back to menu.lst
4508 */
4509 (void) snprintf(buf, sizeof (buf), "%s%s%s",
4510 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4511 default_lp->line = s_strdup(buf);
4512 }
4513
4514
4515 static menu_t *
4516 menu_read(char *menu_path)
4517 {
4518 FILE *fp;
4519 char buf[BAM_MAXLINE], *cp;
4520 menu_t *mp;
4521 int line, entry, len, n;
4522
4523 mp = s_calloc(1, sizeof (menu_t));
4524
4525 fp = fopen(menu_path, "r");
4526 if (fp == NULL) { /* Let the caller handle this error */
4527 free(mp);
4528 return (NULL);
4529 }
4530
4531 /* Note: GRUB boot entry number starts with 0 */
4532 line = LINE_INIT;
4533 entry = ENTRY_INIT;
4534 cp = buf;
4535 len = sizeof (buf);
4536 while (s_fgets(cp, len, fp) != NULL) {
4537 n = strlen(cp);
4538 if (cp[n - 1] == '\\') {
4539 len -= n - 1;
4540 assert(len >= 2);
4541 cp += n - 1;
4542 continue;
4543 }
4544 line_parser(mp, buf, &line, &entry);
4545 cp = buf;
4546 len = sizeof (buf);
4547 }
4548
4549 if (fclose(fp) == EOF) {
4550 bam_error(CLOSE_FAIL, menu_path, strerror(errno));
4551 }
4552
4553 return (mp);
4554 }
4555
4556 static error_t
4557 selector(menu_t *mp, char *opt, int *entry, char **title)
4558 {
4559 char *eq;
4560 char *opt_dup;
4561 int entryNum;
4562
4563 assert(mp);
4564 assert(mp->start);
4565 assert(opt);
4566
4567 opt_dup = s_strdup(opt);
4568
4569 if (entry)
4570 *entry = ENTRY_INIT;
4571 if (title)
4572 *title = NULL;
4573
4574 eq = strchr(opt_dup, '=');
4575 if (eq == NULL) {
4576 bam_error(INVALID_OPT, opt);
4577 free(opt_dup);
4578 return (BAM_ERROR);
4579 }
4580
4581 *eq = '\0';
4582 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
4583 assert(mp->end);
4584 entryNum = s_strtol(eq + 1);
4585 if (entryNum < 0 || entryNum > mp->end->entryNum) {
4586 bam_error(INVALID_ENTRY, eq + 1);
4587 free(opt_dup);
4588 return (BAM_ERROR);
4589 }
4590 *entry = entryNum;
4591 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
4592 *title = opt + (eq - opt_dup) + 1;
4593 } else {
4594 bam_error(INVALID_OPT, opt);
4595 free(opt_dup);
4596 return (BAM_ERROR);
4597 }
4598
4599 free(opt_dup);
4600 return (BAM_SUCCESS);
4601 }
4602
4603 /*
4604 * If invoked with no titles/entries (opt == NULL)
4605 * only title lines in file are printed.
4606 *
4607 * If invoked with a title or entry #, all
4608 * lines in *every* matching entry are listed
4609 */
4610 static error_t
4611 list_entry(menu_t *mp, char *menu_path, char *opt)
4612 {
4613 line_t *lp;
4614 int entry = ENTRY_INIT;
4615 int found;
4616 char *title = NULL;
4617
4618 assert(mp);
4619 assert(menu_path);
4620
4621 /* opt is optional */
4622 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
4623 opt ? opt : "<NULL>"));
4624
4625 if (mp->start == NULL) {
4626 bam_error(NO_MENU, menu_path);
4627 return (BAM_ERROR);
4628 }
4629
4630 if (opt != NULL) {
4631 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
4632 return (BAM_ERROR);
4633 }
4634 assert((entry != ENTRY_INIT) ^ (title != NULL));
4635 } else {
4636 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
4637 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
4638 }
4639
4640 found = 0;
4641 for (lp = mp->start; lp; lp = lp->next) {
4642 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
4643 continue;
4644 if (opt == NULL && lp->flags == BAM_TITLE) {
4645 bam_print(PRINT_TITLE, lp->entryNum,
4646 lp->arg);
4647 found = 1;
4648 continue;
4649 }
4650 if (entry != ENTRY_INIT && lp->entryNum == entry) {
4651 bam_print(PRINT, lp->line);
4652 found = 1;
4653 continue;
4654 }
4655
4656 /*
4657 * We set the entry value here so that all lines
4658 * in entry get printed. If we subsequently match
4659 * title in other entries, all lines in those
4660 * entries get printed as well.
4661 */
4662 if (title && lp->flags == BAM_TITLE && lp->arg &&
4663 strncmp(title, lp->arg, strlen(title)) == 0) {
4664 bam_print(PRINT, lp->line);
4665 entry = lp->entryNum;
4666 found = 1;
4667 continue;
4668 }
4669 }
4670
4671 if (!found) {
4672 bam_error(NO_MATCH_ENTRY);
4673 return (BAM_ERROR);
4674 }
4675
4676 return (BAM_SUCCESS);
4677 }
4678
4679 int
4680 add_boot_entry(menu_t *mp,
4681 char *title,
4682 char *findroot,
4683 char *kernel,
4684 char *mod_kernel,
4685 char *module,
4686 char *bootfs)
4687 {
4688 int lineNum;
4689 int entryNum;
4690 char linebuf[BAM_MAXLINE];
4691 menu_cmd_t k_cmd;
4692 menu_cmd_t m_cmd;
4693 const char *fcn = "add_boot_entry()";
4694
4695 assert(mp);
4696
4697 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
4698 if (findroot == NULL) {
4699 bam_error(NULL_FINDROOT);
4700 return (BAM_ERROR);
4701 }
4702
4703 if (title == NULL) {
4704 title = "Solaris"; /* default to Solaris */
4705 }
4706 if (kernel == NULL) {
4707 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
4708 return (BAM_ERROR);
4709 }
4710 if (module == NULL) {
4711 if (bam_direct != BAM_DIRECT_DBOOT) {
4712 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
4713 return (BAM_ERROR);
4714 }
4715
4716 /* Figure the commands out from the kernel line */
4717 if (strstr(kernel, "$ISADIR") != NULL) {
4718 module = DIRECT_BOOT_ARCHIVE;
4719 } else if (strstr(kernel, "amd64") != NULL) {
4720 module = DIRECT_BOOT_ARCHIVE_64;
4721 } else {
4722 module = DIRECT_BOOT_ARCHIVE_32;
4723 }
4724 }
4725
4726 k_cmd = KERNEL_DOLLAR_CMD;
4727 m_cmd = MODULE_DOLLAR_CMD;
4728
4729 if (mp->start) {
4730 lineNum = mp->end->lineNum;
4731 entryNum = mp->end->entryNum;
4732 } else {
4733 lineNum = LINE_INIT;
4734 entryNum = ENTRY_INIT;
4735 }
4736
4737 /*
4738 * No separator for comment (HDR/FTR) commands
4739 * The syntax for comments is #<comment>
4740 */
4741 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
4742 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
4743 line_parser(mp, linebuf, &lineNum, &entryNum);
4744
4745 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4746 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
4747 line_parser(mp, linebuf, &lineNum, &entryNum);
4748
4749 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4750 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
4751 line_parser(mp, linebuf, &lineNum, &entryNum);
4752 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
4753
4754 if (bootfs != NULL) {
4755 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4756 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
4757 line_parser(mp, linebuf, &lineNum, &entryNum);
4758 }
4759
4760 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4761 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
4762 line_parser(mp, linebuf, &lineNum, &entryNum);
4763
4764 if (mod_kernel != NULL) {
4765 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4766 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
4767 line_parser(mp, linebuf, &lineNum, &entryNum);
4768 }
4769
4770 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
4771 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
4772 line_parser(mp, linebuf, &lineNum, &entryNum);
4773
4774 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
4775 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
4776 line_parser(mp, linebuf, &lineNum, &entryNum);
4777
4778 return (entryNum);
4779 }
4780
4781 error_t
4782 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
4783 {
4784 line_t *lp;
4785 line_t *freed;
4786 entry_t *ent;
4787 entry_t *tmp;
4788 int deleted = 0;
4789 const char *fcn = "delete_boot_entry()";
4790
4791 assert(entryNum != ENTRY_INIT);
4792
4793 tmp = NULL;
4794
4795 ent = mp->entries;
4796 while (ent) {
4797 lp = ent->start;
4798
4799 /*
4800 * Check entry number and make sure it's a modifiable entry.
4801 *
4802 * Guidelines:
4803 * + We can modify a bootadm-created entry
4804 * + We can modify a libbe-created entry
4805 */
4806 if ((lp->flags != BAM_COMMENT &&
4807 (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
4808 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
4809 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
4810 ent = ent->next;
4811 continue;
4812 }
4813
4814 /* free the entry content */
4815 do {
4816 freed = lp;
4817 lp = lp->next; /* prev stays the same */
4818 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
4819 unlink_line(mp, freed);
4820 line_free(freed);
4821 } while (freed != ent->end);
4822
4823 /* free the entry_t structure */
4824 assert(tmp == NULL);
4825 tmp = ent;
4826 ent = ent->next;
4827 if (tmp->prev)
4828 tmp->prev->next = ent;
4829 else
4830 mp->entries = ent;
4831 if (ent)
4832 ent->prev = tmp->prev;
4833 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
4834 free(tmp);
4835 tmp = NULL;
4836 deleted = 1;
4837 }
4838
4839 assert(tmp == NULL);
4840
4841 if (!deleted && entryNum != ALL_ENTRIES) {
4842 if (quiet == DBE_PRINTERR)
4843 bam_error(NO_BOOTADM_MATCH);
4844 return (BAM_ERROR);
4845 }
4846
4847 /*
4848 * Now that we have deleted an entry, update
4849 * the entry numbering and the default cmd.
4850 */
4851 update_numbering(mp);
4852
4853 return (BAM_SUCCESS);
4854 }
4855
4856 static error_t
4857 delete_all_entries(menu_t *mp, char *dummy, char *opt)
4858 {
4859 assert(mp);
4860 assert(dummy == NULL);
4861 assert(opt == NULL);
4862
4863 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
4864
4865 if (mp->start == NULL) {
4866 bam_print(EMPTY_MENU);
4867 return (BAM_SUCCESS);
4868 }
4869
4870 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
4871 return (BAM_ERROR);
4872 }
4873
4874 return (BAM_WRITE);
4875 }
4876
4877 static FILE *
4878 create_diskmap(char *osroot)
4879 {
4880 FILE *fp;
4881 char cmd[PATH_MAX + 16];
4882 char path[PATH_MAX];
4883 const char *fcn = "create_diskmap()";
4884
4885 /* make sure we have a map file */
4886 fp = fopen(GRUBDISK_MAP, "r");
4887 if (fp == NULL) {
4888 int ret;
4889
4890 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
4891 CREATE_DISKMAP);
4892 if (ret >= sizeof (path)) {
4893 bam_error(PATH_TOO_LONG, osroot);
4894 return (NULL);
4895 }
4896 if (is_safe_exec(path) == BAM_ERROR)
4897 return (NULL);
4898
4899 (void) snprintf(cmd, sizeof (cmd),
4900 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
4901 if (exec_cmd(cmd, NULL) != 0)
4902 return (NULL);
4903 fp = fopen(GRUBDISK_MAP, "r");
4904 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
4905 if (fp) {
4906 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
4907 } else {
4908 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
4909 }
4910 }
4911 return (fp);
4912 }
4913
4914 #define SECTOR_SIZE 512
4915
4916 static int
4917 get_partition(char *device)
4918 {
4919 int i, fd, is_pcfs, partno = -1;
4920 struct mboot *mboot;
4921 char boot_sect[SECTOR_SIZE];
4922 char *wholedisk, *slice;
4923 #ifdef i386
4924 ext_part_t *epp;
4925 uint32_t secnum, numsec;
4926 int rval, pno, ext_partno = -1;
4927 #endif
4928
4929 /* form whole disk (p0) */
4930 slice = device + strlen(device) - 2;
4931 is_pcfs = (*slice != 's');
4932 if (!is_pcfs)
4933 *slice = '\0';
4934 wholedisk = s_calloc(1, strlen(device) + 3);
4935 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
4936 if (!is_pcfs)
4937 *slice = 's';
4938
4939 /* read boot sector */
4940 fd = open(wholedisk, O_RDONLY);
4941 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
4942 return (partno);
4943 }
4944 (void) close(fd);
4945
4946 #ifdef i386
4947 /* Read/Initialize extended partition information */
4948 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
4949 != FDISK_SUCCESS) {
4950 switch (rval) {
4951 /*
4952 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
4953 * be considered as soft errors and hence
4954 * we do not return
4955 */
4956 case FDISK_EBADLOGDRIVE:
4957 break;
4958 case FDISK_ENOLOGDRIVE:
4959 break;
4960 case FDISK_EBADMAGIC:
4961 /*FALLTHROUGH*/
4962 default:
4963 free(wholedisk);
4964 libfdisk_fini(&epp);
4965 return (partno);
4966 }
4967 }
4968 #endif
4969 free(wholedisk);
4970
4971 /* parse fdisk table */
4972 mboot = (struct mboot *)((void *)boot_sect);
4973 for (i = 0; i < FD_NUMPART; i++) {
4974 struct ipart *part =
4975 (struct ipart *)(uintptr_t)mboot->parts + i;
4976 if (is_pcfs) { /* looking for solaris boot part */
4977 if (part->systid == 0xbe) {
4978 partno = i;
4979 break;
4980 }
4981 } else { /* look for solaris partition, old and new */
4982 #ifdef i386
4983 if ((part->systid == SUNIXOS &&
4984 (fdisk_is_linux_swap(epp, part->relsect,
4985 NULL) != 0)) || part->systid == SUNIXOS2) {
4986 #else
4987 if (part->systid == SUNIXOS ||
4988 part->systid == SUNIXOS2) {
4989 #endif
4990 partno = i;
4991 break;
4992 }
4993
4994 #ifdef i386
4995 if (fdisk_is_dos_extended(part->systid))
4996 ext_partno = i;
4997 #endif
4998 }
4999 }
5000 #ifdef i386
5001 /* If no primary solaris partition, check extended partition */
5002 if ((partno == -1) && (ext_partno != -1)) {
5003 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5004 if (rval == FDISK_SUCCESS) {
5005 partno = pno - 1;
5006 }
5007 }
5008 libfdisk_fini(&epp);
5009 #endif
5010 return (partno);
5011 }
5012
5013 char *
5014 get_grubroot(char *osroot, char *osdev, char *menu_root)
5015 {
5016 char *grubroot; /* (hd#,#,#) */
5017 char *slice;
5018 char *grubhd;
5019 int fdiskpart;
5020 int found = 0;
5021 char *devname;
5022 char *ctdname = strstr(osdev, "dsk/");
5023 char linebuf[PATH_MAX];
5024 FILE *fp;
5025
5026 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5027 if (ctdname == NULL) {
5028 bam_error(INVALID_DEV_DSK, osdev);
5029 return (NULL);
5030 }
5031
5032 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5033 /* menu bears no resemblance to our reality */
5034 bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev);
5035 return (NULL);
5036 }
5037
5038 ctdname += strlen("dsk/");
5039 slice = strrchr(ctdname, 's');
5040 if (slice)
5041 *slice = '\0';
5042
5043 fp = create_diskmap(osroot);
5044 if (fp == NULL) {
5045 bam_error(DISKMAP_FAIL, osroot);
5046 return (NULL);
5047 }
5048
5049 rewind(fp);
5050 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5051 grubhd = strtok(linebuf, " \t\n");
5052 if (grubhd)
5053 devname = strtok(NULL, " \t\n");
5054 else
5055 devname = NULL;
5056 if (devname && strcmp(devname, ctdname) == 0) {
5057 found = 1;
5058 break;
5059 }
5060 }
5061
5062 if (slice)
5063 *slice = 's';
5064
5065 (void) fclose(fp);
5066 fp = NULL;
5067
5068 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5069 if (found == 0) {
5070 bam_error(BIOSDEV_SKIP, osdev);
5071 return (NULL);
5072 }
5073
5074 fdiskpart = get_partition(osdev);
5075 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1);
5076 if (fdiskpart == -1) {
5077 bam_error(FDISKPART_FAIL, osdev);
5078 return (NULL);
5079 }
5080
5081 grubroot = s_calloc(1, 10);
5082 if (slice) {
5083 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5084 grubhd, fdiskpart, slice[1] + 'a' - '0');
5085 } else
5086 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5087 grubhd, fdiskpart);
5088
5089 assert(fp == NULL);
5090 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5091 return (grubroot);
5092 }
5093
5094 static char *
5095 find_primary_common(char *mntpt, char *fstype)
5096 {
5097 char signdir[PATH_MAX];
5098 char tmpsign[MAXNAMELEN + 1];
5099 char *lu;
5100 char *ufs;
5101 char *zfs;
5102 DIR *dirp = NULL;
5103 struct dirent *entp;
5104 struct stat sb;
5105 const char *fcn = "find_primary_common()";
5106
5107 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5108 mntpt, GRUBSIGN_DIR);
5109
5110 if (stat(signdir, &sb) == -1) {
5111 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
5112 return (NULL);
5113 }
5114
5115 dirp = opendir(signdir);
5116 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5117 if (dirp == NULL) {
5118 bam_error(OPENDIR_FAILED, signdir, strerror(errno));
5119 return (NULL);
5120 }
5121
5122 ufs = zfs = lu = NULL;
5123
5124 while (entp = readdir(dirp)) {
5125 if (strcmp(entp->d_name, ".") == 0 ||
5126 strcmp(entp->d_name, "..") == 0)
5127 continue;
5128
5129 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5130
5131 if (lu == NULL &&
5132 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5133 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5134 lu = s_strdup(tmpsign);
5135 }
5136
5137 if (ufs == NULL &&
5138 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5139 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5140 ufs = s_strdup(tmpsign);
5141 }
5142
5143 if (zfs == NULL &&
5144 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5145 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5146 zfs = s_strdup(tmpsign);
5147 }
5148 }
5149
5150 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
5151 zfs ? zfs : "NULL",
5152 ufs ? ufs : "NULL",
5153 lu ? lu : "NULL"));
5154
5155 if (dirp) {
5156 (void) closedir(dirp);
5157 dirp = NULL;
5158 }
5159
5160 if (strcmp(fstype, "ufs") == 0 && zfs) {
5161 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5162 free(zfs);
5163 zfs = NULL;
5164 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5165 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5166 free(ufs);
5167 ufs = NULL;
5168 }
5169
5170 assert(dirp == NULL);
5171
5172 /* For now, we let Live Upgrade take care of its signature itself */
5173 if (lu) {
5174 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5175 free(lu);
5176 lu = NULL;
5177 }
5178
5179 return (zfs ? zfs : ufs);
5180 }
5181
5182 static char *
5183 find_backup_common(char *mntpt, char *fstype)
5184 {
5185 FILE *bfp = NULL;
5186 char tmpsign[MAXNAMELEN + 1];
5187 char backup[PATH_MAX];
5188 char *ufs;
5189 char *zfs;
5190 char *lu;
5191 int error;
5192 const char *fcn = "find_backup_common()";
5193
5194 /*
5195 * We didn't find it in the primary directory.
5196 * Look at the backup
5197 */
5198 (void) snprintf(backup, sizeof (backup), "%s%s",
5199 mntpt, GRUBSIGN_BACKUP);
5200
5201 bfp = fopen(backup, "r");
5202 if (bfp == NULL) {
5203 error = errno;
5204 if (bam_verbose) {
5205 bam_error(OPEN_FAIL, backup, strerror(error));
5206 }
5207 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
5208 return (NULL);
5209 }
5210
5211 ufs = zfs = lu = NULL;
5212
5213 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5214
5215 if (lu == NULL &&
5216 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5217 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5218 lu = s_strdup(tmpsign);
5219 }
5220
5221 if (ufs == NULL &&
5222 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5223 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5224 ufs = s_strdup(tmpsign);
5225 }
5226
5227 if (zfs == NULL &&
5228 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5229 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5230 zfs = s_strdup(tmpsign);
5231 }
5232 }
5233
5234 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
5235 zfs ? zfs : "NULL",
5236 ufs ? ufs : "NULL",
5237 lu ? lu : "NULL"));
5238
5239 if (bfp) {
5240 (void) fclose(bfp);
5241 bfp = NULL;
5242 }
5243
5244 if (strcmp(fstype, "ufs") == 0 && zfs) {
5245 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5246 free(zfs);
5247 zfs = NULL;
5248 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5249 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5250 free(ufs);
5251 ufs = NULL;
5252 }
5253
5254 assert(bfp == NULL);
5255
5256 /* For now, we let Live Upgrade take care of its signature itself */
5257 if (lu) {
5258 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5259 free(lu);
5260 lu = NULL;
5261 }
5262
5263 return (zfs ? zfs : ufs);
5264 }
5265
5266 static char *
5267 find_ufs_existing(char *osroot)
5268 {
5269 char *sign;
5270 const char *fcn = "find_ufs_existing()";
5271
5272 sign = find_primary_common(osroot, "ufs");
5273 if (sign == NULL) {
5274 sign = find_backup_common(osroot, "ufs");
5275 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
5276 } else {
5277 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
5278 }
5279
5280 return (sign);
5281 }
5282
5283 char *
5284 get_mountpoint(char *special, char *fstype)
5285 {
5286 FILE *mntfp;
5287 struct mnttab mp = {0};
5288 struct mnttab mpref = {0};
5289 int error;
5290 int ret;
5291 const char *fcn = "get_mountpoint()";
5292
5293 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
5294
5295 mntfp = fopen(MNTTAB, "r");
5296 error = errno;
5297 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5298 if (mntfp == NULL) {
5299 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
5300 return (NULL);
5301 }
5302
5303 mpref.mnt_special = special;
5304 mpref.mnt_fstype = fstype;
5305
5306 ret = getmntany(mntfp, &mp, &mpref);
5307 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5308 if (ret != 0) {
5309 (void) fclose(mntfp);
5310 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
5311 return (NULL);
5312 }
5313 (void) fclose(mntfp);
5314
5315 assert(mp.mnt_mountp);
5316
5317 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
5318
5319 return (s_strdup(mp.mnt_mountp));
5320 }
5321
5322 /*
5323 * Mounts a "legacy" top dataset (if needed)
5324 * Returns: The mountpoint of the legacy top dataset or NULL on error
5325 * mnted returns one of the above values defined for zfs_mnted_t
5326 */
5327 static char *
5328 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5329 {
5330 char cmd[PATH_MAX];
5331 char tmpmnt[PATH_MAX];
5332 filelist_t flist = {0};
5333 char *is_mounted;
5334 struct stat sb;
5335 int ret;
5336 const char *fcn = "mount_legacy_dataset()";
5337
5338 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5339
5340 *mnted = ZFS_MNT_ERROR;
5341
5342 (void) snprintf(cmd, sizeof (cmd),
5343 "/sbin/zfs get -Ho value mounted %s",
5344 pool);
5345
5346 ret = exec_cmd(cmd, &flist);
5347 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5348 if (ret != 0) {
5349 bam_error(ZFS_MNTED_FAILED, pool);
5350 return (NULL);
5351 }
5352
5353 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5354 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5355 bam_error(BAD_ZFS_MNTED, pool);
5356 filelist_free(&flist);
5357 return (NULL);
5358 }
5359
5360 is_mounted = strtok(flist.head->line, " \t\n");
5361 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5362 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5363 if (strcmp(is_mounted, "no") != 0) {
5364 filelist_free(&flist);
5365 *mnted = LEGACY_ALREADY;
5366 /* get_mountpoint returns a strdup'ed string */
5367 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
5368 return (get_mountpoint(pool, "zfs"));
5369 }
5370
5371 filelist_free(&flist);
5372
5373 /*
5374 * legacy top dataset is not mounted. Mount it now
5375 * First create a mountpoint.
5376 */
5377 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5378 ZFS_LEGACY_MNTPT, getpid());
5379
5380 ret = stat(tmpmnt, &sb);
5381 if (ret == -1) {
5382 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
5383 ret = mkdirp(tmpmnt, DIR_PERMS);
5384 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5385 if (ret == -1) {
5386 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
5387 return (NULL);
5388 }
5389 } else {
5390 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
5391 }
5392
5393 (void) snprintf(cmd, sizeof (cmd),
5394 "/sbin/mount -F zfs %s %s",
5395 pool, tmpmnt);
5396
5397 ret = exec_cmd(cmd, NULL);
5398 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5399 if (ret != 0) {
5400 bam_error(ZFS_MOUNT_FAILED, pool);
5401 (void) rmdir(tmpmnt);
5402 return (NULL);
5403 }
5404
5405 *mnted = LEGACY_MOUNTED;
5406 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
5407 return (s_strdup(tmpmnt));
5408 }
5409
5410 /*
5411 * Mounts the top dataset (if needed)
5412 * Returns: The mountpoint of the top dataset or NULL on error
5413 * mnted returns one of the above values defined for zfs_mnted_t
5414 */
5415 static char *
5416 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5417 {
5418 char cmd[PATH_MAX];
5419 filelist_t flist = {0};
5420 char *is_mounted;
5421 char *mntpt;
5422 char *zmntpt;
5423 int ret;
5424 const char *fcn = "mount_top_dataset()";
5425
5426 *mnted = ZFS_MNT_ERROR;
5427
5428 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5429
5430 /*
5431 * First check if the top dataset is a "legacy" dataset
5432 */
5433 (void) snprintf(cmd, sizeof (cmd),
5434 "/sbin/zfs get -Ho value mountpoint %s",
5435 pool);
5436 ret = exec_cmd(cmd, &flist);
5437 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5438 if (ret != 0) {
5439 bam_error(ZFS_MNTPT_FAILED, pool);
5440 return (NULL);
5441 }
5442
5443 if (flist.head && (flist.head == flist.tail)) {
5444 char *legacy = strtok(flist.head->line, " \t\n");
5445 if (legacy && strcmp(legacy, "legacy") == 0) {
5446 filelist_free(&flist);
5447 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
5448 return (mount_legacy_dataset(pool, mnted));
5449 }
5450 }
5451
5452 filelist_free(&flist);
5453
5454 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
5455
5456 (void) snprintf(cmd, sizeof (cmd),
5457 "/sbin/zfs get -Ho value mounted %s",
5458 pool);
5459
5460 ret = exec_cmd(cmd, &flist);
5461 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5462 if (ret != 0) {
5463 bam_error(ZFS_MNTED_FAILED, pool);
5464 return (NULL);
5465 }
5466
5467 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5468 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5469 bam_error(BAD_ZFS_MNTED, pool);
5470 filelist_free(&flist);
5471 return (NULL);
5472 }
5473
5474 is_mounted = strtok(flist.head->line, " \t\n");
5475 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5476 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5477 if (strcmp(is_mounted, "no") != 0) {
5478 filelist_free(&flist);
5479 *mnted = ZFS_ALREADY;
5480 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
5481 goto mounted;
5482 }
5483
5484 filelist_free(&flist);
5485 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
5486
5487 /* top dataset is not mounted. Mount it now */
5488 (void) snprintf(cmd, sizeof (cmd),
5489 "/sbin/zfs mount %s", pool);
5490 ret = exec_cmd(cmd, NULL);
5491 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5492 if (ret != 0) {
5493 bam_error(ZFS_MOUNT_FAILED, pool);
5494 return (NULL);
5495 }
5496 *mnted = ZFS_MOUNTED;
5497 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
5498 /*FALLTHRU*/
5499 mounted:
5500 /*
5501 * Now get the mountpoint
5502 */
5503 (void) snprintf(cmd, sizeof (cmd),
5504 "/sbin/zfs get -Ho value mountpoint %s",
5505 pool);
5506
5507 ret = exec_cmd(cmd, &flist);
5508 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
5509 if (ret != 0) {
5510 bam_error(ZFS_MNTPT_FAILED, pool);
5511 goto error;
5512 }
5513
5514 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
5515 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5516 bam_error(NULL_ZFS_MNTPT, pool);
5517 goto error;
5518 }
5519
5520 mntpt = strtok(flist.head->line, " \t\n");
5521 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
5522 if (*mntpt != '/') {
5523 bam_error(BAD_ZFS_MNTPT, pool, mntpt);
5524 goto error;
5525 }
5526 zmntpt = s_strdup(mntpt);
5527
5528 filelist_free(&flist);
5529
5530 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
5531
5532 return (zmntpt);
5533
5534 error:
5535 filelist_free(&flist);
5536 (void) umount_top_dataset(pool, *mnted, NULL);
5537 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
5538 return (NULL);
5539 }
5540
5541 static int
5542 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
5543 {
5544 char cmd[PATH_MAX];
5545 int ret;
5546 const char *fcn = "umount_top_dataset()";
5547
5548 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
5549 switch (mnted) {
5550 case LEGACY_ALREADY:
5551 case ZFS_ALREADY:
5552 /* nothing to do */
5553 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
5554 mntpt ? mntpt : "NULL"));
5555 free(mntpt);
5556 return (BAM_SUCCESS);
5557 case LEGACY_MOUNTED:
5558 (void) snprintf(cmd, sizeof (cmd),
5559 "/sbin/umount %s", pool);
5560 ret = exec_cmd(cmd, NULL);
5561 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
5562 if (ret != 0) {
5563 bam_error(UMOUNT_FAILED, pool);
5564 free(mntpt);
5565 return (BAM_ERROR);
5566 }
5567 if (mntpt)
5568 (void) rmdir(mntpt);
5569 free(mntpt);
5570 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
5571 return (BAM_SUCCESS);
5572 case ZFS_MOUNTED:
5573 free(mntpt);
5574 (void) snprintf(cmd, sizeof (cmd),
5575 "/sbin/zfs unmount %s", pool);
5576 ret = exec_cmd(cmd, NULL);
5577 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
5578 if (ret != 0) {
5579 bam_error(UMOUNT_FAILED, pool);
5580 return (BAM_ERROR);
5581 }
5582 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
5583 return (BAM_SUCCESS);
5584 default:
5585 bam_error(INT_BAD_MNTSTATE, pool);
5586 return (BAM_ERROR);
5587 }
5588 /*NOTREACHED*/
5589 }
5590
5591 /*
5592 * For ZFS, osdev can be one of two forms
5593 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
5594 * It can be a /dev/[r]dsk special file. We handle both instances
5595 */
5596 static char *
5597 get_pool(char *osdev)
5598 {
5599 char cmd[PATH_MAX];
5600 char buf[PATH_MAX];
5601 filelist_t flist = {0};
5602 char *pool;
5603 char *cp;
5604 char *slash;
5605 int ret;
5606 const char *fcn = "get_pool()";
5607
5608 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
5609 if (osdev == NULL) {
5610 bam_error(GET_POOL_OSDEV_NULL);
5611 return (NULL);
5612 }
5613
5614 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
5615
5616 if (osdev[0] != '/') {
5617 (void) strlcpy(buf, osdev, sizeof (buf));
5618 slash = strchr(buf, '/');
5619 if (slash)
5620 *slash = '\0';
5621 pool = s_strdup(buf);
5622 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5623 return (pool);
5624 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
5625 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
5626 bam_error(GET_POOL_BAD_OSDEV, osdev);
5627 return (NULL);
5628 }
5629
5630 /*
5631 * Call the zfs fstyp directly since this is a zpool. This avoids
5632 * potential pcfs conflicts if the first block wasn't cleared.
5633 */
5634 (void) snprintf(cmd, sizeof (cmd),
5635 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
5636 osdev);
5637
5638 ret = exec_cmd(cmd, &flist);
5639 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
5640 if (ret != 0) {
5641 bam_error(FSTYP_A_FAILED, osdev);
5642 return (NULL);
5643 }
5644
5645 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
5646 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5647 bam_error(NULL_FSTYP_A, osdev);
5648 filelist_free(&flist);
5649 return (NULL);
5650 }
5651
5652 (void) strtok(flist.head->line, "'");
5653 cp = strtok(NULL, "'");
5654 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
5655 if (cp == NULL) {
5656 bam_error(BAD_FSTYP_A, osdev);
5657 filelist_free(&flist);
5658 return (NULL);
5659 }
5660
5661 pool = s_strdup(cp);
5662
5663 filelist_free(&flist);
5664
5665 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5666
5667 return (pool);
5668 }
5669
5670 static char *
5671 find_zfs_existing(char *osdev)
5672 {
5673 char *pool;
5674 zfs_mnted_t mnted;
5675 char *mntpt;
5676 char *sign;
5677 const char *fcn = "find_zfs_existing()";
5678
5679 pool = get_pool(osdev);
5680 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
5681 if (pool == NULL) {
5682 bam_error(ZFS_GET_POOL_FAILED, osdev);
5683 return (NULL);
5684 }
5685
5686 mntpt = mount_top_dataset(pool, &mnted);
5687 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
5688 if (mntpt == NULL) {
5689 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
5690 free(pool);
5691 return (NULL);
5692 }
5693
5694 sign = find_primary_common(mntpt, "zfs");
5695 if (sign == NULL) {
5696 sign = find_backup_common(mntpt, "zfs");
5697 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
5698 } else {
5699 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
5700 }
5701
5702 (void) umount_top_dataset(pool, mnted, mntpt);
5703
5704 free(pool);
5705
5706 return (sign);
5707 }
5708
5709 static char *
5710 find_existing_sign(char *osroot, char *osdev, char *fstype)
5711 {
5712 const char *fcn = "find_existing_sign()";
5713
5714 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
5715 if (strcmp(fstype, "ufs") == 0) {
5716 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
5717 return (find_ufs_existing(osroot));
5718 } else if (strcmp(fstype, "zfs") == 0) {
5719 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
5720 return (find_zfs_existing(osdev));
5721 } else {
5722 bam_error(GRUBSIGN_NOTSUP, fstype);
5723 return (NULL);
5724 }
5725 }
5726
5727 #define MH_HASH_SZ 16
5728
5729 typedef enum {
5730 MH_ERROR = -1,
5731 MH_NOMATCH,
5732 MH_MATCH
5733 } mh_search_t;
5734
5735 typedef struct mcache {
5736 char *mc_special;
5737 char *mc_mntpt;
5738 char *mc_fstype;
5739 struct mcache *mc_next;
5740 } mcache_t;
5741
5742 typedef struct mhash {
5743 mcache_t *mh_hash[MH_HASH_SZ];
5744 } mhash_t;
5745
5746 static int
5747 mhash_fcn(char *key)
5748 {
5749 int i;
5750 uint64_t sum = 0;
5751
5752 for (i = 0; key[i] != '\0'; i++) {
5753 sum += (uchar_t)key[i];
5754 }
5755
5756 sum %= MH_HASH_SZ;
5757
5758 assert(sum < MH_HASH_SZ);
5759
5760 return (sum);
5761 }
5762
5763 static mhash_t *
5764 cache_mnttab(void)
5765 {
5766 FILE *mfp;
5767 struct extmnttab mnt;
5768 mcache_t *mcp;
5769 mhash_t *mhp;
5770 char *ctds;
5771 int idx;
5772 int error;
5773 char *special_dup;
5774 const char *fcn = "cache_mnttab()";
5775
5776 mfp = fopen(MNTTAB, "r");
5777 error = errno;
5778 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
5779 if (mfp == NULL) {
5780 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
5781 return (NULL);
5782 }
5783
5784 mhp = s_calloc(1, sizeof (mhash_t));
5785
5786 resetmnttab(mfp);
5787
5788 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
5789 /* only cache ufs */
5790 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
5791 continue;
5792
5793 /* basename() modifies its arg, so dup it */
5794 special_dup = s_strdup(mnt.mnt_special);
5795 ctds = basename(special_dup);
5796
5797 mcp = s_calloc(1, sizeof (mcache_t));
5798 mcp->mc_special = s_strdup(ctds);
5799 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
5800 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
5801 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
5802 mnt.mnt_mountp, mnt.mnt_fstype));
5803 idx = mhash_fcn(ctds);
5804 mcp->mc_next = mhp->mh_hash[idx];
5805 mhp->mh_hash[idx] = mcp;
5806 free(special_dup);
5807 }
5808
5809 (void) fclose(mfp);
5810
5811 return (mhp);
5812 }
5813
5814 static void
5815 free_mnttab(mhash_t *mhp)
5816 {
5817 mcache_t *mcp;
5818 int i;
5819
5820 for (i = 0; i < MH_HASH_SZ; i++) {
5821 /*LINTED*/
5822 while (mcp = mhp->mh_hash[i]) {
5823 mhp->mh_hash[i] = mcp->mc_next;
5824 free(mcp->mc_special);
5825 free(mcp->mc_mntpt);
5826 free(mcp->mc_fstype);
5827 free(mcp);
5828 }
5829 }
5830
5831 for (i = 0; i < MH_HASH_SZ; i++) {
5832 assert(mhp->mh_hash[i] == NULL);
5833 }
5834 free(mhp);
5835 }
5836
5837 static mh_search_t
5838 search_hash(mhash_t *mhp, char *special, char **mntpt)
5839 {
5840 int idx;
5841 mcache_t *mcp;
5842 const char *fcn = "search_hash()";
5843
5844 assert(mntpt);
5845
5846 *mntpt = NULL;
5847
5848 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
5849 if (strchr(special, '/')) {
5850 bam_error(INVALID_MHASH_KEY, special);
5851 return (MH_ERROR);
5852 }
5853
5854 idx = mhash_fcn(special);
5855
5856 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
5857 if (strcmp(mcp->mc_special, special) == 0)
5858 break;
5859 }
5860
5861 if (mcp == NULL) {
5862 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
5863 return (MH_NOMATCH);
5864 }
5865
5866 assert(strcmp(mcp->mc_fstype, "ufs") == 0);
5867 *mntpt = mcp->mc_mntpt;
5868 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
5869 return (MH_MATCH);
5870 }
5871
5872 static int
5873 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
5874 {
5875 char *sign;
5876 char *signline;
5877 char signbuf[MAXNAMELEN];
5878 int len;
5879 int error;
5880 const char *fcn = "check_add_ufs_sign_to_list()";
5881
5882 /* safe to specify NULL as "osdev" arg for UFS */
5883 sign = find_existing_sign(mntpt, NULL, "ufs");
5884 if (sign == NULL) {
5885 /* No existing signature, nothing to add to list */
5886 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
5887 return (0);
5888 }
5889
5890 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
5891 signline = signbuf;
5892
5893 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
5894 if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
5895 strlen(GRUBSIGN_UFS_PREFIX))) {
5896 bam_error(INVALID_UFS_SIGNATURE, sign);
5897 free(sign);
5898 /* ignore invalid signatures */
5899 return (0);
5900 }
5901
5902 len = fputs(signline, tfp);
5903 error = errno;
5904 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
5905 if (len != strlen(signline)) {
5906 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
5907 free(sign);
5908 return (-1);
5909 }
5910
5911 free(sign);
5912
5913 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
5914 return (0);
5915 }
5916
5917 /*
5918 * slice is a basename not a full pathname
5919 */
5920 static int
5921 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
5922 {
5923 int ret;
5924 char cmd[PATH_MAX];
5925 char path[PATH_MAX];
5926 struct stat sbuf;
5927 char *mntpt;
5928 filelist_t flist = {0};
5929 char *fstype;
5930 char blkslice[PATH_MAX];
5931 const char *fcn = "process_slice_common()";
5932
5933
5934 ret = search_hash(mhp, slice, &mntpt);
5935 switch (ret) {
5936 case MH_MATCH:
5937 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
5938 return (-1);
5939 else
5940 return (0);
5941 case MH_NOMATCH:
5942 break;
5943 case MH_ERROR:
5944 default:
5945 return (-1);
5946 }
5947
5948 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
5949 if (stat(path, &sbuf) == -1) {
5950 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
5951 return (0);
5952 }
5953
5954 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
5955 (void) snprintf(cmd, sizeof (cmd),
5956 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
5957 slice);
5958
5959 if (exec_cmd(cmd, &flist) != 0) {
5960 if (bam_verbose)
5961 bam_print(FSTYP_FAILED, slice);
5962 return (0);
5963 }
5964
5965 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5966 if (bam_verbose)
5967 bam_print(FSTYP_BAD, slice);
5968 filelist_free(&flist);
5969 return (0);
5970 }
5971
5972 fstype = strtok(flist.head->line, " \t\n");
5973 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
5974 if (bam_verbose)
5975 bam_print(NOT_UFS_SLICE, slice, fstype);
5976 filelist_free(&flist);
5977 return (0);
5978 }
5979
5980 filelist_free(&flist);
5981
5982 /*
5983 * Since we are mounting the filesystem read-only, the
5984 * the last mount field of the superblock is unchanged
5985 * and does not need to be fixed up post-mount;
5986 */
5987
5988 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
5989 slice);
5990
5991 (void) snprintf(cmd, sizeof (cmd),
5992 "/usr/sbin/mount -F ufs -o ro %s %s "
5993 "> /dev/null 2>&1", blkslice, tmpmnt);
5994
5995 if (exec_cmd(cmd, NULL) != 0) {
5996 if (bam_verbose)
5997 bam_print(MOUNT_FAILED, blkslice, "ufs");
5998 return (0);
5999 }
6000
6001 ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6002
6003 (void) snprintf(cmd, sizeof (cmd),
6004 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6005 tmpmnt);
6006
6007 if (exec_cmd(cmd, NULL) != 0) {
6008 bam_print(UMOUNT_FAILED, slice);
6009 return (0);
6010 }
6011
6012 return (ret);
6013 }
6014
6015 static int
6016 process_vtoc_slices(
6017 char *s0,
6018 struct vtoc *vtoc,
6019 FILE *tfp,
6020 mhash_t *mhp,
6021 char *tmpmnt)
6022 {
6023 int idx;
6024 char slice[PATH_MAX];
6025 size_t len;
6026 char *cp;
6027 const char *fcn = "process_vtoc_slices()";
6028
6029 len = strlen(s0);
6030
6031 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6032
6033 s0[len - 1] = '\0';
6034
6035 (void) strlcpy(slice, s0, sizeof (slice));
6036
6037 s0[len - 1] = '0';
6038
6039 cp = slice + len - 1;
6040
6041 for (idx = 0; idx < vtoc->v_nparts; idx++) {
6042
6043 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6044
6045 if (vtoc->v_part[idx].p_size == 0) {
6046 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
6047 continue;
6048 }
6049
6050 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6051 switch (vtoc->v_part[idx].p_tag) {
6052 case V_SWAP:
6053 case V_USR:
6054 case V_BACKUP:
6055 case V_VAR:
6056 case V_HOME:
6057 case V_ALTSCTR:
6058 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
6059 continue;
6060 default:
6061 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
6062 break;
6063 }
6064
6065 /* skip unmountable and readonly slices */
6066 switch (vtoc->v_part[idx].p_flag) {
6067 case V_UNMNT:
6068 case V_RONLY:
6069 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
6070 continue;
6071 default:
6072 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
6073 break;
6074 }
6075
6076 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6077 return (-1);
6078 }
6079 }
6080
6081 return (0);
6082 }
6083
6084 static int
6085 process_efi_slices(
6086 char *s0,
6087 struct dk_gpt *efi,
6088 FILE *tfp,
6089 mhash_t *mhp,
6090 char *tmpmnt)
6091 {
6092 int idx;
6093 char slice[PATH_MAX];
6094 size_t len;
6095 char *cp;
6096 const char *fcn = "process_efi_slices()";
6097
6098 len = strlen(s0);
6099
6100 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6101
6102 s0[len - 1] = '\0';
6103
6104 (void) strlcpy(slice, s0, sizeof (slice));
6105
6106 s0[len - 1] = '0';
6107
6108 cp = slice + len - 1;
6109
6110 for (idx = 0; idx < efi->efi_nparts; idx++) {
6111
6112 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6113
6114 if (efi->efi_parts[idx].p_size == 0) {
6115 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
6116 continue;
6117 }
6118
6119 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6120 switch (efi->efi_parts[idx].p_tag) {
6121 case V_SWAP:
6122 case V_USR:
6123 case V_BACKUP:
6124 case V_VAR:
6125 case V_HOME:
6126 case V_ALTSCTR:
6127 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
6128 continue;
6129 default:
6130 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
6131 break;
6132 }
6133
6134 /* skip unmountable and readonly slices */
6135 switch (efi->efi_parts[idx].p_flag) {
6136 case V_UNMNT:
6137 case V_RONLY:
6138 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
6139 continue;
6140 default:
6141 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
6142 break;
6143 }
6144
6145 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6146 return (-1);
6147 }
6148 }
6149
6150 return (0);
6151 }
6152
6153 /*
6154 * s0 is a basename not a full path
6155 */
6156 static int
6157 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6158 {
6159 struct vtoc vtoc;
6160 struct dk_gpt *efi;
6161 char s0path[PATH_MAX];
6162 struct stat sbuf;
6163 int e_flag;
6164 int v_flag;
6165 int retval;
6166 int err;
6167 int fd;
6168 const char *fcn = "process_slice0()";
6169
6170 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6171
6172 if (stat(s0path, &sbuf) == -1) {
6173 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
6174 return (0);
6175 }
6176
6177 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6178 if (fd == -1) {
6179 bam_error(OPEN_FAIL, s0path, strerror(errno));
6180 return (0);
6181 }
6182
6183 e_flag = v_flag = 0;
6184 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6185 switch (retval) {
6186 case VT_EIO:
6187 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
6188 break;
6189 case VT_EINVAL:
6190 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
6191 break;
6192 case VT_ERROR:
6193 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
6194 break;
6195 case VT_ENOTSUP:
6196 e_flag = 1;
6197 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
6198 break;
6199 case 0:
6200 v_flag = 1;
6201 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
6202 break;
6203 default:
6204 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
6205 break;
6206 }
6207
6208
6209 if (e_flag) {
6210 e_flag = 0;
6211 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6212 switch (retval) {
6213 case VT_EIO:
6214 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
6215 break;
6216 case VT_EINVAL:
6217 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
6218 break;
6219 case VT_ERROR:
6220 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
6221 break;
6222 case VT_ENOTSUP:
6223 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
6224 break;
6225 case 0:
6226 e_flag = 1;
6227 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
6228 break;
6229 default:
6230 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
6231 break;
6232 }
6233 }
6234
6235 (void) close(fd);
6236
6237 if (v_flag) {
6238 retval = process_vtoc_slices(s0,
6239 &vtoc, tfp, mhp, tmpmnt);
6240 } else if (e_flag) {
6241 retval = process_efi_slices(s0,
6242 efi, tfp, mhp, tmpmnt);
6243 } else {
6244 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
6245 return (0);
6246 }
6247
6248 return (retval);
6249 }
6250
6251 /*
6252 * Find and create a list of all existing UFS boot signatures
6253 */
6254 static int
6255 FindAllUfsSignatures(void)
6256 {
6257 mhash_t *mnttab_hash;
6258 DIR *dirp = NULL;
6259 struct dirent *dp;
6260 char tmpmnt[PATH_MAX];
6261 char cmd[PATH_MAX];
6262 struct stat sb;
6263 int fd;
6264 FILE *tfp;
6265 size_t len;
6266 int ret;
6267 int error;
6268 const char *fcn = "FindAllUfsSignatures()";
6269
6270 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) {
6271 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
6272 return (0);
6273 }
6274
6275 fd = open(UFS_SIGNATURE_LIST".tmp",
6276 O_RDWR|O_CREAT|O_TRUNC, 0644);
6277 error = errno;
6278 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6279 if (fd == -1) {
6280 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6281 return (-1);
6282 }
6283
6284 ret = close(fd);
6285 error = errno;
6286 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6287 if (ret == -1) {
6288 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6289 strerror(error));
6290 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6291 return (-1);
6292 }
6293
6294 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6295 error = errno;
6296 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6297 if (tfp == NULL) {
6298 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6299 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6300 return (-1);
6301 }
6302
6303 mnttab_hash = cache_mnttab();
6304 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6305 if (mnttab_hash == NULL) {
6306 (void) fclose(tfp);
6307 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6308 bam_error(CACHE_MNTTAB_FAIL, fcn);
6309 return (-1);
6310 }
6311
6312 (void) snprintf(tmpmnt, sizeof (tmpmnt),
6313 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6314 (void) unlink(tmpmnt);
6315
6316 ret = mkdirp(tmpmnt, DIR_PERMS);
6317 error = errno;
6318 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6319 if (ret == -1) {
6320 bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
6321 free_mnttab(mnttab_hash);
6322 (void) fclose(tfp);
6323 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6324 return (-1);
6325 }
6326
6327 dirp = opendir("/dev/rdsk");
6328 error = errno;
6329 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6330 if (dirp == NULL) {
6331 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
6332 goto fail;
6333 }
6334
6335 while (dp = readdir(dirp)) {
6336 if (strcmp(dp->d_name, ".") == 0 ||
6337 strcmp(dp->d_name, "..") == 0)
6338 continue;
6339
6340 /*
6341 * we only look for the s0 slice. This is guranteed to
6342 * have 's' at len - 2.
6343 */
6344 len = strlen(dp->d_name);
6345 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6346 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
6347 continue;
6348 }
6349
6350 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6351 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6352 if (ret == -1)
6353 goto fail;
6354 }
6355
6356 (void) closedir(dirp);
6357 free_mnttab(mnttab_hash);
6358 (void) rmdir(tmpmnt);
6359
6360 ret = fclose(tfp);
6361 error = errno;
6362 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6363 if (ret == EOF) {
6364 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6365 strerror(error));
6366 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6367 return (-1);
6368 }
6369
6370 /* We have a list of existing GRUB signatures. Sort it first */
6371 (void) snprintf(cmd, sizeof (cmd),
6372 "/usr/bin/sort -u %s.tmp > %s.sorted",
6373 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6374
6375 ret = exec_cmd(cmd, NULL);
6376 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6377 if (ret != 0) {
6378 bam_error(GRUBSIGN_SORT_FAILED);
6379 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6380 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6381 return (-1);
6382 }
6383
6384 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6385
6386 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6387 error = errno;
6388 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6389 if (ret == -1) {
6390 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
6391 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6392 return (-1);
6393 }
6394
6395 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6396 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
6397 }
6398
6399 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6400 return (0);
6401
6402 fail:
6403 if (dirp)
6404 (void) closedir(dirp);
6405 free_mnttab(mnttab_hash);
6406 (void) rmdir(tmpmnt);
6407 (void) fclose(tfp);
6408 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6409 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6410 return (-1);
6411 }
6412
6413 static char *
6414 create_ufs_sign(void)
6415 {
6416 struct stat sb;
6417 int signnum = -1;
6418 char tmpsign[MAXNAMELEN + 1];
6419 char *numstr;
6420 int i;
6421 FILE *tfp;
6422 int ret;
6423 int error;
6424 const char *fcn = "create_ufs_sign()";
6425
6426 bam_print(SEARCHING_UFS_SIGN);
6427
6428 ret = FindAllUfsSignatures();
6429 INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6430 if (ret == -1) {
6431 bam_error(ERR_FIND_UFS_SIGN);
6432 return (NULL);
6433 }
6434
6435 /* Make sure the list exists and is owned by root */
6436 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6437 (void) unlink(UFS_SIGNATURE_LIST));
6438 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6439 (void) unlink(UFS_SIGNATURE_LIST);
6440 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
6441 return (NULL);
6442 }
6443
6444 if (sb.st_size == 0) {
6445 bam_print(GRUBSIGN_UFS_NONE);
6446 i = 0;
6447 goto found;
6448 }
6449
6450 /* The signature list was sorted when it was created */
6451 tfp = fopen(UFS_SIGNATURE_LIST, "r");
6452 error = errno;
6453 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
6454 if (tfp == NULL) {
6455 bam_error(UFS_SIGNATURE_LIST_OPENERR,
6456 UFS_SIGNATURE_LIST, strerror(error));
6457 (void) unlink(UFS_SIGNATURE_LIST);
6458 return (NULL);
6459 }
6460
6461 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
6462
6463 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
6464 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
6465 (void) fclose(tfp);
6466 (void) unlink(UFS_SIGNATURE_LIST);
6467 bam_error(UFS_BADSIGN, tmpsign);
6468 return (NULL);
6469 }
6470 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
6471
6472 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
6473 (void) fclose(tfp);
6474 (void) unlink(UFS_SIGNATURE_LIST);
6475 bam_error(UFS_BADSIGN, tmpsign);
6476 return (NULL);
6477 }
6478
6479 signnum = atoi(numstr);
6480 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
6481 if (signnum < 0) {
6482 (void) fclose(tfp);
6483 (void) unlink(UFS_SIGNATURE_LIST);
6484 bam_error(UFS_BADSIGN, tmpsign);
6485 return (NULL);
6486 }
6487
6488 if (i != signnum) {
6489 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
6490 break;
6491 }
6492 }
6493
6494 (void) fclose(tfp);
6495
6496 found:
6497 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
6498
6499 /* add the ufs signature to the /var/run list of signatures */
6500 ret = ufs_add_to_sign_list(tmpsign);
6501 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
6502 if (ret == -1) {
6503 (void) unlink(UFS_SIGNATURE_LIST);
6504 bam_error(FAILED_ADD_SIGNLIST, tmpsign);
6505 return (NULL);
6506 }
6507
6508 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6509
6510 return (s_strdup(tmpsign));
6511 }
6512
6513 static char *
6514 get_fstype(char *osroot)
6515 {
6516 FILE *mntfp;
6517 struct mnttab mp = {0};
6518 struct mnttab mpref = {0};
6519 int error;
6520 int ret;
6521 const char *fcn = "get_fstype()";
6522
6523 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
6524 if (osroot == NULL) {
6525 bam_error(GET_FSTYPE_ARGS);
6526 return (NULL);
6527 }
6528
6529 mntfp = fopen(MNTTAB, "r");
6530 error = errno;
6531 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
6532 if (mntfp == NULL) {
6533 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6534 return (NULL);
6535 }
6536
6537 if (*osroot == '\0')
6538 mpref.mnt_mountp = "/";
6539 else
6540 mpref.mnt_mountp = osroot;
6541
6542 ret = getmntany(mntfp, &mp, &mpref);
6543 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
6544 if (ret != 0) {
6545 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
6546 (void) fclose(mntfp);
6547 return (NULL);
6548 }
6549 (void) fclose(mntfp);
6550
6551 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
6552 if (mp.mnt_fstype == NULL) {
6553 bam_error(MNTTAB_FSTYPE_NULL, osroot);
6554 return (NULL);
6555 }
6556
6557 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6558
6559 return (s_strdup(mp.mnt_fstype));
6560 }
6561
6562 static char *
6563 create_zfs_sign(char *osdev)
6564 {
6565 char tmpsign[PATH_MAX];
6566 char *pool;
6567 const char *fcn = "create_zfs_sign()";
6568
6569 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
6570
6571 /*
6572 * First find the pool name
6573 */
6574 pool = get_pool(osdev);
6575 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
6576 if (pool == NULL) {
6577 bam_error(GET_POOL_FAILED, osdev);
6578 return (NULL);
6579 }
6580
6581 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
6582
6583 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
6584
6585 free(pool);
6586
6587 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6588
6589 return (s_strdup(tmpsign));
6590 }
6591
6592 static char *
6593 create_new_sign(char *osdev, char *fstype)
6594 {
6595 char *sign;
6596 const char *fcn = "create_new_sign()";
6597
6598 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
6599
6600 if (strcmp(fstype, "zfs") == 0) {
6601 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
6602 sign = create_zfs_sign(osdev);
6603 } else if (strcmp(fstype, "ufs") == 0) {
6604 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
6605 sign = create_ufs_sign();
6606 } else {
6607 bam_error(GRUBSIGN_NOTSUP, fstype);
6608 sign = NULL;
6609 }
6610
6611 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
6612 return (sign);
6613 }
6614
6615 static int
6616 set_backup_common(char *mntpt, char *sign)
6617 {
6618 FILE *bfp;
6619 char backup[PATH_MAX];
6620 char tmpsign[PATH_MAX];
6621 int error;
6622 char *bdir;
6623 char *backup_dup;
6624 struct stat sb;
6625 int ret;
6626 const char *fcn = "set_backup_common()";
6627
6628 (void) snprintf(backup, sizeof (backup), "%s%s",
6629 mntpt, GRUBSIGN_BACKUP);
6630
6631 /* First read the backup */
6632 bfp = fopen(backup, "r");
6633 if (bfp != NULL) {
6634 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
6635 if (strcmp(tmpsign, sign) == 0) {
6636 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
6637 (void) fclose(bfp);
6638 return (0);
6639 }
6640 }
6641 (void) fclose(bfp);
6642 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
6643 } else {
6644 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
6645 }
6646
6647 /*
6648 * Didn't find the correct signature. First create
6649 * the directory if necessary.
6650 */
6651
6652 /* dirname() modifies its argument so dup it */
6653 backup_dup = s_strdup(backup);
6654 bdir = dirname(backup_dup);
6655 assert(bdir);
6656
6657 ret = stat(bdir, &sb);
6658 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
6659 if (ret == -1) {
6660 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
6661 ret = mkdirp(bdir, DIR_PERMS);
6662 error = errno;
6663 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
6664 if (ret == -1) {
6665 bam_error(GRUBSIGN_BACKUP_MKDIRERR,
6666 GRUBSIGN_BACKUP, strerror(error));
6667 free(backup_dup);
6668 return (-1);
6669 }
6670 }
6671 free(backup_dup);
6672
6673 /*
6674 * Open the backup in append mode to add the correct
6675 * signature;
6676 */
6677 bfp = fopen(backup, "a");
6678 error = errno;
6679 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
6680 if (bfp == NULL) {
6681 bam_error(GRUBSIGN_BACKUP_OPENERR,
6682 GRUBSIGN_BACKUP, strerror(error));
6683 return (-1);
6684 }
6685
6686 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
6687
6688 ret = fputs(tmpsign, bfp);
6689 error = errno;
6690 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
6691 if (ret != strlen(tmpsign)) {
6692 bam_error(GRUBSIGN_BACKUP_WRITEERR,
6693 GRUBSIGN_BACKUP, strerror(error));
6694 (void) fclose(bfp);
6695 return (-1);
6696 }
6697
6698 (void) fclose(bfp);
6699
6700 if (bam_verbose)
6701 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
6702
6703 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6704
6705 return (0);
6706 }
6707
6708 static int
6709 set_backup_ufs(char *osroot, char *sign)
6710 {
6711 const char *fcn = "set_backup_ufs()";
6712
6713 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
6714 return (set_backup_common(osroot, sign));
6715 }
6716
6717 static int
6718 set_backup_zfs(char *osdev, char *sign)
6719 {
6720 char *pool;
6721 char *mntpt;
6722 zfs_mnted_t mnted;
6723 int ret;
6724 const char *fcn = "set_backup_zfs()";
6725
6726 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
6727
6728 pool = get_pool(osdev);
6729 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
6730 if (pool == NULL) {
6731 bam_error(GET_POOL_FAILED, osdev);
6732 return (-1);
6733 }
6734
6735 mntpt = mount_top_dataset(pool, &mnted);
6736 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
6737 if (mntpt == NULL) {
6738 bam_error(FAIL_MNT_TOP_DATASET, pool);
6739 free(pool);
6740 return (-1);
6741 }
6742
6743 ret = set_backup_common(mntpt, sign);
6744
6745 (void) umount_top_dataset(pool, mnted, mntpt);
6746
6747 free(pool);
6748
6749 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
6750 if (ret == 0) {
6751 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6752 } else {
6753 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6754 }
6755
6756 return (ret);
6757 }
6758
6759 static int
6760 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
6761 {
6762 const char *fcn = "set_backup()";
6763 int ret;
6764
6765 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
6766
6767 if (strcmp(fstype, "ufs") == 0) {
6768 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
6769 ret = set_backup_ufs(osroot, sign);
6770 } else if (strcmp(fstype, "zfs") == 0) {
6771 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
6772 ret = set_backup_zfs(osdev, sign);
6773 } else {
6774 bam_error(GRUBSIGN_NOTSUP, fstype);
6775 ret = -1;
6776 }
6777
6778 if (ret == 0) {
6779 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6780 } else {
6781 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6782 }
6783
6784 return (ret);
6785 }
6786
6787 static int
6788 set_primary_common(char *mntpt, char *sign)
6789 {
6790 char signfile[PATH_MAX];
6791 char signdir[PATH_MAX];
6792 struct stat sb;
6793 int fd;
6794 int error;
6795 int ret;
6796 const char *fcn = "set_primary_common()";
6797
6798 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
6799 mntpt, GRUBSIGN_DIR, sign);
6800
6801 if (stat(signfile, &sb) != -1) {
6802 if (bam_verbose)
6803 bam_print(PRIMARY_SIGN_EXISTS, sign);
6804 return (0);
6805 } else {
6806 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
6807 }
6808
6809 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
6810 mntpt, GRUBSIGN_DIR);
6811
6812 if (stat(signdir, &sb) == -1) {
6813 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
6814 ret = mkdirp(signdir, DIR_PERMS);
6815 error = errno;
6816 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
6817 if (ret == -1) {
6818 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
6819 return (-1);
6820 }
6821 }
6822
6823 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
6824 error = errno;
6825 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
6826 if (fd == -1) {
6827 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
6828 return (-1);
6829 }
6830
6831 ret = fsync(fd);
6832 error = errno;
6833 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
6834 if (ret != 0) {
6835 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
6836 }
6837
6838 (void) close(fd);
6839
6840 if (bam_verbose)
6841 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
6842
6843 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6844
6845 return (0);
6846 }
6847
6848 static int
6849 set_primary_ufs(char *osroot, char *sign)
6850 {
6851 const char *fcn = "set_primary_ufs()";
6852
6853 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
6854 return (set_primary_common(osroot, sign));
6855 }
6856
6857 static int
6858 set_primary_zfs(char *osdev, char *sign)
6859 {
6860 char *pool;
6861 char *mntpt;
6862 zfs_mnted_t mnted;
6863 int ret;
6864 const char *fcn = "set_primary_zfs()";
6865
6866 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
6867
6868 pool = get_pool(osdev);
6869 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
6870 if (pool == NULL) {
6871 bam_error(GET_POOL_FAILED, osdev);
6872 return (-1);
6873 }
6874
6875 /* Pool name must exist in the sign */
6876 ret = (strstr(sign, pool) != NULL);
6877 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
6878 if (ret == 0) {
6879 bam_error(POOL_SIGN_INCOMPAT, pool, sign);
6880 free(pool);
6881 return (-1);
6882 }
6883
6884 mntpt = mount_top_dataset(pool, &mnted);
6885 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
6886 if (mntpt == NULL) {
6887 bam_error(FAIL_MNT_TOP_DATASET, pool);
6888 free(pool);
6889 return (-1);
6890 }
6891
6892 ret = set_primary_common(mntpt, sign);
6893
6894 (void) umount_top_dataset(pool, mnted, mntpt);
6895
6896 free(pool);
6897
6898 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
6899 if (ret == 0) {
6900 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6901 } else {
6902 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6903 }
6904
6905 return (ret);
6906 }
6907
6908 static int
6909 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
6910 {
6911 const char *fcn = "set_primary()";
6912 int ret;
6913
6914 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
6915 if (strcmp(fstype, "ufs") == 0) {
6916 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
6917 ret = set_primary_ufs(osroot, sign);
6918 } else if (strcmp(fstype, "zfs") == 0) {
6919 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
6920 ret = set_primary_zfs(osdev, sign);
6921 } else {
6922 bam_error(GRUBSIGN_NOTSUP, fstype);
6923 ret = -1;
6924 }
6925
6926 if (ret == 0) {
6927 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6928 } else {
6929 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6930 }
6931
6932 return (ret);
6933 }
6934
6935 static int
6936 ufs_add_to_sign_list(char *sign)
6937 {
6938 FILE *tfp;
6939 char signline[MAXNAMELEN];
6940 char cmd[PATH_MAX];
6941 int ret;
6942 int error;
6943 const char *fcn = "ufs_add_to_sign_list()";
6944
6945 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
6946 if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
6947 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
6948 bam_error(INVALID_UFS_SIGN, sign);
6949 (void) unlink(UFS_SIGNATURE_LIST);
6950 return (-1);
6951 }
6952
6953 /*
6954 * most failures in this routine are not a fatal error
6955 * We simply unlink the /var/run file and continue
6956 */
6957
6958 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
6959 error = errno;
6960 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
6961 if (ret == -1) {
6962 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
6963 strerror(error));
6964 (void) unlink(UFS_SIGNATURE_LIST);
6965 return (0);
6966 }
6967
6968 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6969 error = errno;
6970 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
6971 if (tfp == NULL) {
6972 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6973 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6974 return (0);
6975 }
6976
6977 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
6978
6979 ret = fputs(signline, tfp);
6980 error = errno;
6981 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
6982 if (ret != strlen(signline)) {
6983 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
6984 (void) fclose(tfp);
6985 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6986 return (0);
6987 }
6988
6989 ret = fclose(tfp);
6990 error = errno;
6991 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
6992 if (ret == EOF) {
6993 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6994 strerror(error));
6995 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6996 return (0);
6997 }
6998
6999 /* Sort the list again */
7000 (void) snprintf(cmd, sizeof (cmd),
7001 "/usr/bin/sort -u %s.tmp > %s.sorted",
7002 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7003
7004 ret = exec_cmd(cmd, NULL);
7005 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7006 if (ret != 0) {
7007 bam_error(GRUBSIGN_SORT_FAILED);
7008 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7009 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7010 return (0);
7011 }
7012
7013 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7014
7015 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7016 error = errno;
7017 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7018 if (ret == -1) {
7019 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
7020 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7021 return (0);
7022 }
7023
7024 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7025
7026 return (0);
7027 }
7028
7029 static int
7030 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7031 {
7032 int ret;
7033 const char *fcn = "set_signature()";
7034
7035 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
7036
7037 ret = set_backup(osroot, osdev, sign, fstype);
7038 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7039 if (ret == -1) {
7040 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7041 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
7042 return (-1);
7043 }
7044
7045 ret = set_primary(osroot, osdev, sign, fstype);
7046 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7047
7048 if (ret == 0) {
7049 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7050 } else {
7051 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7052 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
7053
7054 }
7055 return (ret);
7056 }
7057
7058 char *
7059 get_grubsign(char *osroot, char *osdev)
7060 {
7061 char *grubsign; /* (<sign>,#,#) */
7062 char *slice;
7063 int fdiskpart;
7064 char *sign;
7065 char *fstype;
7066 int ret;
7067 const char *fcn = "get_grubsign()";
7068
7069 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
7070 fstype = get_fstype(osroot);
7071 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7072 if (fstype == NULL) {
7073 bam_error(GET_FSTYPE_FAILED, osroot);
7074 return (NULL);
7075 }
7076
7077 sign = find_existing_sign(osroot, osdev, fstype);
7078 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7079 if (sign == NULL) {
7080 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
7081 sign = create_new_sign(osdev, fstype);
7082 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7083 if (sign == NULL) {
7084 bam_error(GRUBSIGN_CREATE_FAIL, osdev);
7085 free(fstype);
7086 return (NULL);
7087 }
7088 }
7089
7090 ret = set_signature(osroot, osdev, sign, fstype);
7091 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7092 if (ret == -1) {
7093 bam_error(GRUBSIGN_WRITE_FAIL, osdev);
7094 free(sign);
7095 free(fstype);
7096 (void) unlink(UFS_SIGNATURE_LIST);
7097 return (NULL);
7098 }
7099
7100 free(fstype);
7101
7102 if (bam_verbose)
7103 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
7104
7105 fdiskpart = get_partition(osdev);
7106 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1);
7107 if (fdiskpart == -1) {
7108 bam_error(FDISKPART_FAIL, osdev);
7109 free(sign);
7110 return (NULL);
7111 }
7112
7113 slice = strrchr(osdev, 's');
7114
7115 grubsign = s_calloc(1, MAXNAMELEN + 10);
7116 if (slice) {
7117 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7118 sign, fdiskpart, slice[1] + 'a' - '0');
7119 } else
7120 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7121 sign, fdiskpart);
7122
7123 free(sign);
7124
7125 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
7126
7127 return (grubsign);
7128 }
7129
7130 static char *
7131 get_title(char *rootdir)
7132 {
7133 static char title[80];
7134 char *cp = NULL;
7135 char release[PATH_MAX];
7136 FILE *fp;
7137 const char *fcn = "get_title()";
7138
7139 /* open the /etc/release file */
7140 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7141
7142 fp = fopen(release, "r");
7143 if (fp == NULL) {
7144 bam_error(OPEN_FAIL, release, strerror(errno));
7145 cp = NULL;
7146 goto out;
7147 }
7148
7149 /* grab first line of /etc/release */
7150 cp = s_fgets(title, sizeof (title), fp);
7151 if (cp) {
7152 while (isspace(*cp)) /* remove leading spaces */
7153 cp++;
7154 }
7155
7156 (void) fclose(fp);
7157
7158 out:
7159 cp = cp ? cp : "Oracle Solaris";
7160
7161 BAM_DPRINTF((D_GET_TITLE, fcn, cp));
7162
7163 return (cp);
7164 }
7165
7166 char *
7167 get_special(char *mountp)
7168 {
7169 FILE *mntfp;
7170 struct mnttab mp = {0};
7171 struct mnttab mpref = {0};
7172 int error;
7173 int ret;
7174 const char *fcn = "get_special()";
7175
7176 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7177 if (mountp == NULL) {
7178 bam_error(GET_SPECIAL_NULL_MNTPT);
7179 return (NULL);
7180 }
7181
7182 mntfp = fopen(MNTTAB, "r");
7183 error = errno;
7184 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7185 if (mntfp == NULL) {
7186 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
7187 return (NULL);
7188 }
7189
7190 if (*mountp == '\0')
7191 mpref.mnt_mountp = "/";
7192 else
7193 mpref.mnt_mountp = mountp;
7194
7195 ret = getmntany(mntfp, &mp, &mpref);
7196 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7197 if (ret != 0) {
7198 (void) fclose(mntfp);
7199 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
7200 return (NULL);
7201 }
7202 (void) fclose(mntfp);
7203
7204 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
7205
7206 return (s_strdup(mp.mnt_special));
7207 }
7208
7209 static void
7210 free_physarray(char **physarray, int n)
7211 {
7212 int i;
7213 const char *fcn = "free_physarray()";
7214
7215 assert(physarray);
7216 assert(n);
7217
7218 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
7219
7220 for (i = 0; i < n; i++) {
7221 free(physarray[i]);
7222 }
7223 free(physarray);
7224
7225 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7226 }
7227
7228 static int
7229 zfs_get_physical(char *special, char ***physarray, int *n)
7230 {
7231 char sdup[PATH_MAX];
7232 char cmd[PATH_MAX];
7233 char dsk[PATH_MAX];
7234 char *pool;
7235 filelist_t flist = {0};
7236 line_t *lp;
7237 line_t *startlp;
7238 char *comp1;
7239 int i;
7240 int ret;
7241 const char *fcn = "zfs_get_physical()";
7242
7243 assert(special);
7244
7245 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7246
7247 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7248 if (special[0] == '/') {
7249 bam_error(INVALID_ZFS_SPECIAL, special);
7250 return (-1);
7251 }
7252
7253 (void) strlcpy(sdup, special, sizeof (sdup));
7254
7255 pool = strtok(sdup, "/");
7256 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7257 if (pool == NULL) {
7258 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
7259 return (-1);
7260 }
7261
7262 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7263
7264 ret = exec_cmd(cmd, &flist);
7265 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7266 if (ret != 0) {
7267 bam_error(ZFS_GET_POOL_STATUS, pool);
7268 return (-1);
7269 }
7270
7271 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7272 if (flist.head == NULL) {
7273 bam_error(BAD_ZPOOL_STATUS, pool);
7274 filelist_free(&flist);
7275 return (-1);
7276 }
7277
7278 for (lp = flist.head; lp; lp = lp->next) {
7279 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
7280 comp1 = strtok(lp->line, " \t");
7281 if (comp1 == NULL) {
7282 free(lp->line);
7283 lp->line = NULL;
7284 } else {
7285 comp1 = s_strdup(comp1);
7286 free(lp->line);
7287 lp->line = comp1;
7288 }
7289 }
7290
7291 for (lp = flist.head; lp; lp = lp->next) {
7292 if (lp->line == NULL)
7293 continue;
7294 if (strcmp(lp->line, pool) == 0) {
7295 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
7296 break;
7297 }
7298 }
7299
7300 if (lp == NULL) {
7301 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
7302 filelist_free(&flist);
7303 return (-1);
7304 }
7305
7306 startlp = lp->next;
7307 for (i = 0, lp = startlp; lp; lp = lp->next) {
7308 if (lp->line == NULL)
7309 continue;
7310 if (strcmp(lp->line, "mirror") == 0)
7311 continue;
7312 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7313 break;
7314 i++;
7315 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
7316 }
7317
7318 if (i == 0) {
7319 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
7320 filelist_free(&flist);
7321 return (-1);
7322 }
7323
7324 *n = i;
7325 *physarray = s_calloc(*n, sizeof (char *));
7326 for (i = 0, lp = startlp; lp; lp = lp->next) {
7327 if (lp->line == NULL)
7328 continue;
7329 if (strcmp(lp->line, "mirror") == 0)
7330 continue;
7331 if (strcmp(lp->line, "errors:") == 0)
7332 break;
7333 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7334 strncmp(lp->line, "/dev/rdsk/",
7335 strlen("/dev/rdsk/")) != 0) {
7336 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7337 lp->line);
7338 } else {
7339 (void) strlcpy(dsk, lp->line, sizeof (dsk));
7340 }
7341 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
7342 (*physarray)[i++] = s_strdup(dsk);
7343 }
7344
7345 assert(i == *n);
7346
7347 filelist_free(&flist);
7348
7349 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7350 return (0);
7351 }
7352
7353 /*
7354 * Certain services needed to run metastat successfully may not
7355 * be enabled. Enable them now.
7356 */
7357 /*
7358 * Checks if the specified service is online
7359 * Returns: 1 if the service is online
7360 * 0 if the service is not online
7361 * -1 on error
7362 */
7363 static int
7364 is_svc_online(char *svc)
7365 {
7366 char *state;
7367 const char *fcn = "is_svc_online()";
7368
7369 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
7370
7371 state = smf_get_state(svc);
7372 INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
7373 if (state == NULL) {
7374 bam_error(GET_SVC_STATE_ERR, svc);
7375 return (-1);
7376 }
7377 BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
7378
7379 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
7380 BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
7381 free(state);
7382 return (1);
7383 }
7384
7385 BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
7386
7387 free(state);
7388
7389 return (0);
7390 }
7391
7392 static int
7393 enable_svc(char *svc)
7394 {
7395 int ret;
7396 int sleeptime;
7397 const char *fcn = "enable_svc()";
7398
7399 ret = is_svc_online(svc);
7400 if (ret == -1) {
7401 bam_error(SVC_IS_ONLINE_FAILED, svc);
7402 return (-1);
7403 } else if (ret == 1) {
7404 BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
7405 return (0);
7406 }
7407
7408 /* Service is not enabled. Enable it now. */
7409 ret = smf_enable_instance(svc, 0);
7410 INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
7411 if (ret != 0) {
7412 bam_error(ENABLE_SVC_FAILED, svc);
7413 return (-1);
7414 }
7415
7416 BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
7417
7418 sleeptime = 0;
7419 do {
7420 ret = is_svc_online(svc);
7421 INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
7422 INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
7423 INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
7424 if (ret == -1) {
7425 bam_error(ERR_SVC_GET_ONLINE, svc);
7426 return (-1);
7427 } else if (ret == 1) {
7428 BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
7429 return (1);
7430 }
7431 (void) sleep(1);
7432 } while (++sleeptime < 60);
7433
7434 bam_error(TIMEOUT_ENABLE_SVC, svc);
7435
7436 return (-1);
7437 }
7438
7439 static int
7440 ufs_get_physical(char *special, char ***physarray, int *n)
7441 {
7442 char cmd[PATH_MAX];
7443 char *shortname;
7444 filelist_t flist = {0};
7445 char *meta;
7446 char *type;
7447 char *comp1;
7448 char *comp2;
7449 char *comp3;
7450 char *comp4;
7451 int i;
7452 line_t *lp;
7453 int ret;
7454 char *svc;
7455 const char *fcn = "ufs_get_physical()";
7456
7457 assert(special);
7458
7459 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7460
7461 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
7462 bam_error(UFS_GET_PHYS_NOT_SVM, special);
7463 return (-1);
7464 }
7465
7466 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
7467 shortname = special + strlen("/dev/md/dsk/");
7468 } else if (strncmp(special, "/dev/md/rdsk/",
7469 strlen("/dev/md/rdsk/")) == 0) {
7470 shortname = special + strlen("/dev/md/rdsk");
7471 } else {
7472 bam_error(UFS_GET_PHYS_INVALID_SVM, special);
7473 return (-1);
7474 }
7475
7476 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
7477
7478 svc = "network/rpc/meta:default";
7479 if (enable_svc(svc) == -1) {
7480 bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
7481 }
7482
7483 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
7484
7485 ret = exec_cmd(cmd, &flist);
7486 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
7487 if (ret != 0) {
7488 bam_error(UFS_SVM_METASTAT_ERR, shortname);
7489 return (-1);
7490 }
7491
7492 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
7493 if (flist.head == NULL) {
7494 bam_error(BAD_UFS_SVM_METASTAT, shortname);
7495 filelist_free(&flist);
7496 return (-1);
7497 }
7498
7499 /*
7500 * Check if not a mirror. We only parse a single metadevice
7501 * if not a mirror
7502 */
7503 meta = strtok(flist.head->line, " \t");
7504 type = strtok(NULL, " \t");
7505 if (meta == NULL || type == NULL) {
7506 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
7507 filelist_free(&flist);
7508 return (-1);
7509 }
7510 if (strcmp(type, "-m") != 0) {
7511 comp1 = strtok(NULL, " \t");
7512 comp2 = strtok(NULL, " \t");
7513 if (comp1 == NULL || comp2 != NULL) {
7514 bam_error(INVALID_UFS_SVM_METASTAT, shortname);
7515 filelist_free(&flist);
7516 return (-1);
7517 }
7518 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
7519 *physarray = s_calloc(1, sizeof (char *));
7520 (*physarray)[0] = s_strdup(comp1);
7521 *n = 1;
7522 filelist_free(&flist);
7523 return (0);
7524 }
7525
7526 /*
7527 * Okay we have a mirror. Everything after the first line
7528 * is a submirror
7529 */
7530 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7531 if (strstr(lp->line, "/dev/dsk/") == NULL &&
7532 strstr(lp->line, "/dev/rdsk/") == NULL) {
7533 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
7534 filelist_free(&flist);
7535 return (-1);
7536 }
7537 i++;
7538 }
7539
7540 *physarray = s_calloc(i, sizeof (char *));
7541 *n = i;
7542
7543 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7544 comp1 = strtok(lp->line, " \t");
7545 comp2 = strtok(NULL, " \t");
7546 comp3 = strtok(NULL, " \t");
7547 comp4 = strtok(NULL, " \t");
7548
7549 if (comp3 == NULL || comp4 == NULL ||
7550 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7551 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
7552 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
7553 filelist_free(&flist);
7554 free_physarray(*physarray, *n);
7555 return (-1);
7556 }
7557
7558 (*physarray)[i++] = s_strdup(comp4);
7559 }
7560
7561 assert(i == *n);
7562
7563 filelist_free(&flist);
7564
7565 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7566 return (0);
7567 }
7568
7569 static int
7570 get_physical(char *menu_root, char ***physarray, int *n)
7571 {
7572 char *special;
7573 int ret;
7574 const char *fcn = "get_physical()";
7575
7576 assert(menu_root);
7577 assert(physarray);
7578 assert(n);
7579
7580 *physarray = NULL;
7581 *n = 0;
7582
7583 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
7584
7585 /* First get the device special file from /etc/mnttab */
7586 special = get_special(menu_root);
7587 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7588 if (special == NULL) {
7589 bam_error(GET_SPECIAL_NULL, menu_root);
7590 return (-1);
7591 }
7592
7593 /* If already a physical device nothing to do */
7594 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7595 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7596 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
7597 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7598 *physarray = s_calloc(1, sizeof (char *));
7599 (*physarray)[0] = special;
7600 *n = 1;
7601 return (0);
7602 }
7603
7604 if (is_zfs(menu_root)) {
7605 ret = zfs_get_physical(special, physarray, n);
7606 } else if (is_ufs(menu_root)) {
7607 ret = ufs_get_physical(special, physarray, n);
7608 } else {
7609 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
7610 ret = -1;
7611 }
7612
7613 free(special);
7614
7615 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7616 if (ret == -1) {
7617 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7618 } else {
7619 int i;
7620 assert (*n > 0);
7621 for (i = 0; i < *n; i++) {
7622 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
7623 }
7624 }
7625
7626 return (ret);
7627 }
7628
7629 static int
7630 is_bootdisk(char *osroot, char *physical)
7631 {
7632 int ret;
7633 char *grubroot;
7634 char *bootp;
7635 const char *fcn = "is_bootdisk()";
7636
7637 assert(osroot);
7638 assert(physical);
7639
7640 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
7641
7642 bootp = strstr(physical, "p0:boot");
7643 if (bootp)
7644 *bootp = '\0';
7645 /*
7646 * We just want the BIOS mapping for menu disk.
7647 * Don't pass menu_root to get_grubroot() as the
7648 * check that it is used for is not relevant here.
7649 * The osroot is immaterial as well - it is only used to
7650 * to find create_diskmap script. Everything hinges on
7651 * "physical"
7652 */
7653 grubroot = get_grubroot(osroot, physical, NULL);
7654
7655 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
7656 if (grubroot == NULL) {
7657 if (bam_verbose)
7658 bam_error(NO_GRUBROOT_FOR_DISK, physical);
7659 return (0);
7660 }
7661 ret = grubroot[3] == '0';
7662 free(grubroot);
7663
7664 BAM_DPRINTF((D_RETURN_RET, fcn, ret));
7665
7666 return (ret);
7667 }
7668
7669 /*
7670 * Check if menu is on the boot device
7671 * Return 0 (false) on error
7672 */
7673 static int
7674 menu_on_bootdisk(char *osroot, char *menu_root)
7675 {
7676 char **physarray;
7677 int ret;
7678 int n;
7679 int i;
7680 int on_bootdisk;
7681 const char *fcn = "menu_on_bootdisk()";
7682
7683 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
7684
7685 ret = get_physical(menu_root, &physarray, &n);
7686 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
7687 if (ret != 0) {
7688 bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
7689 return (0);
7690 }
7691
7692 assert(physarray);
7693 assert(n > 0);
7694
7695 on_bootdisk = 0;
7696 for (i = 0; i < n; i++) {
7697 assert(strncmp(physarray[i], "/dev/dsk/",
7698 strlen("/dev/dsk/")) == 0 ||
7699 strncmp(physarray[i], "/dev/rdsk/",
7700 strlen("/dev/rdsk/")) == 0);
7701
7702 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
7703 if (is_bootdisk(osroot, physarray[i])) {
7704 on_bootdisk = 1;
7705 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
7706 }
7707 }
7708
7709 free_physarray(physarray, n);
7710
7711 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
7712 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
7713 if (on_bootdisk) {
7714 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7715 } else {
7716 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7717 }
7718
7719 return (on_bootdisk);
7720 }
7721
7722 void
7723 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
7724 {
7725 const char *fcn = "bam_add_line()";
7726
7727 assert(mp);
7728 assert(entry);
7729 assert(prev);
7730 assert(lp);
7731
7732 lp->next = prev->next;
7733 if (prev->next) {
7734 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
7735 prev->next->prev = lp;
7736 } else {
7737 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
7738 }
7739 prev->next = lp;
7740 lp->prev = prev;
7741
7742 if (entry->end == prev) {
7743 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
7744 entry->end = lp;
7745 }
7746 if (mp->end == prev) {
7747 assert(lp->next == NULL);
7748 mp->end = lp;
7749 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
7750 }
7751 }
7752
7753 /*
7754 * look for matching bootadm entry with specified parameters
7755 * Here are the rules (based on existing usage):
7756 * - If title is specified, match on title only
7757 * - Else, match on root/findroot, kernel, and module.
7758 * Note that, if root_opt is non-zero, the absence of
7759 * root line is considered a match.
7760 */
7761 static entry_t *
7762 find_boot_entry(
7763 menu_t *mp,
7764 char *title,
7765 char *kernel,
7766 char *findroot,
7767 char *root,
7768 char *module,
7769 int root_opt,
7770 int *entry_num)
7771 {
7772 int i;
7773 line_t *lp;
7774 entry_t *ent;
7775 const char *fcn = "find_boot_entry()";
7776
7777 if (entry_num)
7778 *entry_num = BAM_ERROR;
7779
7780 /* find matching entry */
7781 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
7782 lp = ent->start;
7783
7784 /* first line of entry must be bootadm comment */
7785 lp = ent->start;
7786 if (lp->flags != BAM_COMMENT ||
7787 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
7788 continue;
7789 }
7790
7791 /* advance to title line */
7792 lp = lp->next;
7793 if (title) {
7794 if (lp->flags == BAM_TITLE && lp->arg &&
7795 strcmp(lp->arg, title) == 0) {
7796 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
7797 break;
7798 }
7799 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
7800 continue; /* check title only */
7801 }
7802
7803 lp = lp->next; /* advance to root line */
7804 if (lp == NULL) {
7805 continue;
7806 } else if (lp->cmd != NULL &&
7807 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
7808 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
7809 findroot = NULL);
7810 if (findroot == NULL) {
7811 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
7812 fcn, lp->arg));
7813 continue;
7814 }
7815 /* findroot command found, try match */
7816 if (strcmp(lp->arg, findroot) != 0) {
7817 BAM_DPRINTF((D_NOMATCH_FINDROOT,
7818 fcn, findroot, lp->arg));
7819 continue;
7820 }
7821 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
7822 lp = lp->next; /* advance to kernel line */
7823 } else if (lp->cmd != NULL &&
7824 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
7825 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
7826 if (root == NULL) {
7827 BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
7828 fcn, lp->arg));
7829 continue;
7830 }
7831 /* root cmd found, try match */
7832 if (strcmp(lp->arg, root) != 0) {
7833 BAM_DPRINTF((D_NOMATCH_ROOT,
7834 fcn, root, lp->arg));
7835 continue;
7836 }
7837 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
7838 lp = lp->next; /* advance to kernel line */
7839 } else {
7840 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
7841 root_opt = 0);
7842 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
7843 root_opt = 1);
7844 /* no root command, see if root is optional */
7845 if (root_opt == 0) {
7846 BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
7847 continue;
7848 }
7849 BAM_DPRINTF((D_ROOT_OPT, fcn));
7850 }
7851
7852 if (lp == NULL || lp->next == NULL) {
7853 continue;
7854 }
7855
7856 if (kernel &&
7857 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
7858 if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
7859 !(ent->flags & BAM_ENTRY_DBOOT) ||
7860 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
7861 continue;
7862
7863 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
7864
7865 }
7866 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
7867
7868 /*
7869 * Check for matching module entry (failsafe or normal).
7870 * If it fails to match, we go around the loop again.
7871 * For xpv entries, there are two module lines, so we
7872 * do the check twice.
7873 */
7874 lp = lp->next; /* advance to module line */
7875 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
7876 (((lp = lp->next) != NULL) &&
7877 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
7878 /* match found */
7879 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
7880 break;
7881 }
7882
7883 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
7884 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
7885 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
7886 ent->flags |= BAM_ENTRY_UPGFSMODULE;
7887 break;
7888 }
7889
7890 }
7891
7892 if (ent && entry_num) {
7893 *entry_num = i;
7894 }
7895
7896 if (ent) {
7897 BAM_DPRINTF((D_RETURN_RET, fcn, i));
7898 } else {
7899 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
7900 }
7901 return (ent);
7902 }
7903
7904 static int
7905 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
7906 char *kernel, char *mod_kernel, char *module, int root_opt)
7907 {
7908 int i;
7909 int change_kernel = 0;
7910 entry_t *ent;
7911 line_t *lp;
7912 line_t *tlp;
7913 char linebuf[BAM_MAXLINE];
7914 const char *fcn = "update_boot_entry()";
7915
7916 /* note: don't match on title, it's updated on upgrade */
7917 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
7918 root_opt, &i);
7919 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
7920 /*
7921 * We may be upgrading a kernel from multiboot to
7922 * directboot. Look for a multiboot entry. A multiboot
7923 * entry will not have a findroot line.
7924 */
7925 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
7926 MULTIBOOT_ARCHIVE, root_opt, &i);
7927 if (ent != NULL) {
7928 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
7929 change_kernel = 1;
7930 }
7931 } else if (ent) {
7932 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
7933 }
7934
7935 if (ent == NULL) {
7936 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
7937 return (add_boot_entry(mp, title, findroot,
7938 kernel, mod_kernel, module, NULL));
7939 }
7940
7941 /* replace title of existing entry and update findroot line */
7942 lp = ent->start;
7943 lp = lp->next; /* title line */
7944 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7945 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
7946 free(lp->arg);
7947 free(lp->line);
7948 lp->arg = s_strdup(title);
7949 lp->line = s_strdup(linebuf);
7950 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
7951
7952 tlp = lp; /* title line */
7953 lp = lp->next; /* root line */
7954
7955 /* if no root or findroot command, create a new line_t */
7956 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
7957 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
7958 lp = s_calloc(1, sizeof (line_t));
7959 bam_add_line(mp, ent, tlp, lp);
7960 } else {
7961 if (lp->cmd != NULL)
7962 free(lp->cmd);
7963
7964 free(lp->sep);
7965 free(lp->arg);
7966 free(lp->line);
7967 }
7968
7969 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
7970 lp->sep = s_strdup(menu_cmds[SEP_CMD]);
7971 lp->arg = s_strdup(findroot);
7972 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7973 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
7974 lp->line = s_strdup(linebuf);
7975 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
7976
7977 /* kernel line */
7978 lp = lp->next;
7979
7980 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
7981 char *params = NULL;
7982
7983 params = strstr(lp->line, "-s");
7984 if (params != NULL)
7985 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
7986 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
7987 kernel, params+2);
7988 else
7989 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7990 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
7991 kernel);
7992
7993 if (lp->cmd != NULL)
7994 free(lp->cmd);
7995
7996 free(lp->arg);
7997 free(lp->line);
7998 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
7999 lp->arg = s_strdup(strstr(linebuf, "/"));
8000 lp->line = s_strdup(linebuf);
8001 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8002 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd));
8003 }
8004
8005 if (change_kernel) {
8006 /*
8007 * We're upgrading from multiboot to directboot.
8008 */
8009 if (lp->cmd != NULL &&
8010 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8011 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8012 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8013 kernel);
8014 free(lp->cmd);
8015 free(lp->arg);
8016 free(lp->line);
8017 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8018 lp->arg = s_strdup(kernel);
8019 lp->line = s_strdup(linebuf);
8020 lp = lp->next;
8021 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
8022 }
8023 if (lp->cmd != NULL &&
8024 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8025 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8026 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8027 module);
8028 free(lp->cmd);
8029 free(lp->arg);
8030 free(lp->line);
8031 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8032 lp->arg = s_strdup(module);
8033 lp->line = s_strdup(linebuf);
8034 lp = lp->next;
8035 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8036 }
8037 }
8038
8039 /* module line */
8040 lp = lp->next;
8041
8042 if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8043 if (lp->cmd != NULL &&
8044 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8045 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8046 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8047 module);
8048 free(lp->cmd);
8049 free(lp->arg);
8050 free(lp->line);
8051 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8052 lp->arg = s_strdup(module);
8053 lp->line = s_strdup(linebuf);
8054 lp = lp->next;
8055 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8056 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8057 }
8058 }
8059
8060 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8061 return (i);
8062 }
8063
8064 int
8065 root_optional(char *osroot, char *menu_root)
8066 {
8067 char *ospecial;
8068 char *mspecial;
8069 char *slash;
8070 int root_opt;
8071 int ret1;
8072 int ret2;
8073 const char *fcn = "root_optional()";
8074
8075 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
8076
8077 /*
8078 * For all filesystems except ZFS, a straight compare of osroot
8079 * and menu_root will tell us if root is optional.
8080 * For ZFS, the situation is complicated by the fact that
8081 * menu_root and osroot are always different
8082 */
8083 ret1 = is_zfs(osroot);
8084 ret2 = is_zfs(menu_root);
8085 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8086 if (!ret1 || !ret2) {
8087 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
8088 root_opt = (strcmp(osroot, menu_root) == 0);
8089 goto out;
8090 }
8091
8092 ospecial = get_special(osroot);
8093 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8094 if (ospecial == NULL) {
8095 bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
8096 return (0);
8097 }
8098 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
8099
8100 mspecial = get_special(menu_root);
8101 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8102 if (mspecial == NULL) {
8103 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
8104 free(ospecial);
8105 return (0);
8106 }
8107 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
8108
8109 slash = strchr(ospecial, '/');
8110 if (slash)
8111 *slash = '\0';
8112 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
8113
8114 root_opt = (strcmp(ospecial, mspecial) == 0);
8115
8116 free(ospecial);
8117 free(mspecial);
8118
8119 out:
8120 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8121 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8122 if (root_opt) {
8123 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8124 } else {
8125 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8126 }
8127
8128 return (root_opt);
8129 }
8130
8131 /*ARGSUSED*/
8132 static error_t
8133 update_entry(menu_t *mp, char *menu_root, char *osdev)
8134 {
8135 int entry;
8136 char *grubsign;
8137 char *grubroot;
8138 char *title;
8139 char osroot[PATH_MAX];
8140 char *failsafe_kernel = NULL;
8141 struct stat sbuf;
8142 char failsafe[256];
8143 char failsafe_64[256];
8144 int ret;
8145 const char *fcn = "update_entry()";
8146
8147 assert(mp);
8148 assert(menu_root);
8149 assert(osdev);
8150 assert(bam_root);
8151
8152 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
8153
8154 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8155
8156 title = get_title(osroot);
8157 assert(title);
8158
8159 grubsign = get_grubsign(osroot, osdev);
8160 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8161 if (grubsign == NULL) {
8162 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
8163 return (BAM_ERROR);
8164 }
8165
8166 /*
8167 * It is not a fatal error if get_grubroot() fails
8168 * We no longer rely on biosdev to populate the
8169 * menu
8170 */
8171 grubroot = get_grubroot(osroot, osdev, menu_root);
8172 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8173 if (grubroot) {
8174 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
8175 fcn, osroot, osdev, menu_root));
8176 } else {
8177 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
8178 fcn, osroot, osdev, menu_root));
8179 }
8180
8181 /* add the entry for normal Solaris */
8182 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8183 bam_direct = BAM_DIRECT_MULTIBOOT);
8184 if (bam_direct == BAM_DIRECT_DBOOT) {
8185 entry = update_boot_entry(mp, title, grubsign, grubroot,
8186 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8187 NULL, DIRECT_BOOT_ARCHIVE,
8188 root_optional(osroot, menu_root));
8189 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
8190 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8191 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8192 grubroot, XEN_MENU, bam_zfs ?
8193 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8194 DIRECT_BOOT_ARCHIVE,
8195 root_optional(osroot, menu_root));
8196 BAM_DPRINTF((D_UPDATED_HV_ENTRY,
8197 fcn, bam_zfs, grubsign));
8198 }
8199 } else {
8200 entry = update_boot_entry(mp, title, grubsign, grubroot,
8201 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8202 root_optional(osroot, menu_root));
8203
8204 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
8205 }
8206
8207 /*
8208 * Add the entry for failsafe archive. On a bfu'd system, the
8209 * failsafe may be different than the installed kernel.
8210 */
8211 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8212 osroot, FAILSAFE_ARCHIVE_32);
8213 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8214 osroot, FAILSAFE_ARCHIVE_64);
8215
8216 /*
8217 * Check if at least one of the two archives exists
8218 * Using $ISADIR as the default line, we have an entry which works
8219 * for both the cases.
8220 */
8221
8222 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8223
8224 /* Figure out where the kernel line should point */
8225 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8226 DIRECT_BOOT_FAILSAFE_32);
8227 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8228 osroot, DIRECT_BOOT_FAILSAFE_64);
8229 if (stat(failsafe, &sbuf) == 0 ||
8230 stat(failsafe_64, &sbuf) == 0) {
8231 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8232 } else {
8233 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8234 osroot, MULTI_BOOT_FAILSAFE);
8235 if (stat(failsafe, &sbuf) == 0) {
8236 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8237 }
8238 }
8239 if (failsafe_kernel != NULL) {
8240 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8241 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8242 root_optional(osroot, menu_root));
8243 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
8244 failsafe_kernel));
8245 }
8246 }
8247 free(grubroot);
8248
8249 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8250 if (entry == BAM_ERROR) {
8251 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
8252 free(grubsign);
8253 return (BAM_ERROR);
8254 }
8255 free(grubsign);
8256
8257 update_numbering(mp);
8258 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8259 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8260 if (ret == BAM_ERROR) {
8261 bam_error(SET_DEFAULT_FAILED, entry);
8262 }
8263 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8264 return (BAM_WRITE);
8265 }
8266
8267 static void
8268 save_default_entry(menu_t *mp, const char *which)
8269 {
8270 int lineNum;
8271 int entryNum;
8272 int entry = 0; /* default is 0 */
8273 char linebuf[BAM_MAXLINE];
8274 line_t *lp = mp->curdefault;
8275 const char *fcn = "save_default_entry()";
8276
8277 if (mp->start) {
8278 lineNum = mp->end->lineNum;
8279 entryNum = mp->end->entryNum;
8280 } else {
8281 lineNum = LINE_INIT;
8282 entryNum = ENTRY_INIT;
8283 }
8284
8285 if (lp)
8286 entry = s_strtol(lp->arg);
8287
8288 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8289 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
8290 line_parser(mp, linebuf, &lineNum, &entryNum);
8291 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
8292 }
8293
8294 static void
8295 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8296 {
8297 int entry;
8298 char *str;
8299 const char *fcn = "restore_default_entry()";
8300
8301 if (lp == NULL) {
8302 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
8303 return; /* nothing to restore */
8304 }
8305
8306 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
8307
8308 str = lp->arg + strlen(which);
8309 entry = s_strtol(str);
8310 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8311
8312 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
8313
8314 /* delete saved old default line */
8315 unlink_line(mp, lp);
8316 line_free(lp);
8317 }
8318
8319 /*
8320 * This function is for supporting reboot with args.
8321 * The opt value can be:
8322 * NULL delete temp entry, if present
8323 * entry=<n> switches default entry to <n>
8324 * else treated as boot-args and setup a temperary menu entry
8325 * and make it the default
8326 * Note that we are always rebooting the current OS instance
8327 * so osroot == / always.
8328 */
8329 #define REBOOT_TITLE "Solaris_reboot_transient"
8330
8331 /*ARGSUSED*/
8332 static error_t
8333 update_temp(menu_t *mp, char *dummy, char *opt)
8334 {
8335 int entry;
8336 char *osdev;
8337 char *fstype;
8338 char *sign;
8339 char *opt_ptr;
8340 char *path;
8341 char kernbuf[BUFSIZ];
8342 char args_buf[BUFSIZ];
8343 char signbuf[PATH_MAX];
8344 int ret;
8345 const char *fcn = "update_temp()";
8346
8347 assert(mp);
8348 assert(dummy == NULL);
8349
8350 /* opt can be NULL */
8351 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
8352 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
8353
8354 if (bam_alt_root || bam_rootlen != 1 ||
8355 strcmp(bam_root, "/") != 0 ||
8356 strcmp(rootbuf, "/") != 0) {
8357 bam_error(ALT_ROOT_INVALID, bam_root);
8358 return (BAM_ERROR);
8359 }
8360
8361 /* If no option, delete exiting reboot menu entry */
8362 if (opt == NULL) {
8363 entry_t *ent;
8364 BAM_DPRINTF((D_OPT_NULL, fcn));
8365 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8366 NULL, NULL, 0, &entry);
8367 if (ent == NULL) { /* not found is ok */
8368 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
8369 return (BAM_SUCCESS);
8370 }
8371 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8372 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8373 mp->olddefault = NULL;
8374 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
8375 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8376 return (BAM_WRITE);
8377 }
8378
8379 /* if entry= is specified, set the default entry */
8380 if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8381 int entryNum = s_strtol(opt + strlen("entry="));
8382 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
8383 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8384 /* this is entry=# option */
8385 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8386 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
8387 return (ret);
8388 } else {
8389 bam_error(SET_DEFAULT_FAILED, entryNum);
8390 return (BAM_ERROR);
8391 }
8392 }
8393
8394 /*
8395 * add a new menu entry based on opt and make it the default
8396 */
8397
8398 fstype = get_fstype("/");
8399 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8400 if (fstype == NULL) {
8401 bam_error(REBOOT_FSTYPE_FAILED);
8402 return (BAM_ERROR);
8403 }
8404
8405 osdev = get_special("/");
8406 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8407 if (osdev == NULL) {
8408 free(fstype);
8409 bam_error(REBOOT_SPECIAL_FAILED);
8410 return (BAM_ERROR);
8411 }
8412
8413 sign = find_existing_sign("/", osdev, fstype);
8414 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8415 if (sign == NULL) {
8416 free(fstype);
8417 free(osdev);
8418 bam_error(REBOOT_SIGN_FAILED);
8419 return (BAM_ERROR);
8420 }
8421
8422 free(osdev);
8423 (void) strlcpy(signbuf, sign, sizeof (signbuf));
8424 free(sign);
8425
8426 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8427 strchr(signbuf, ')') == NULL);
8428
8429 /*
8430 * There is no alternate root while doing reboot with args
8431 * This version of bootadm is only delivered with a DBOOT
8432 * version of Solaris.
8433 */
8434 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8435 if (bam_direct != BAM_DIRECT_DBOOT) {
8436 free(fstype);
8437 bam_error(REBOOT_DIRECT_FAILED);
8438 return (BAM_ERROR);
8439 }
8440
8441 /* add an entry for Solaris reboot */
8442 if (opt[0] == '-') {
8443 /* It's an option - first see if boot-file is set */
8444 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8445 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8446 if (ret != BAM_SUCCESS) {
8447 free(fstype);
8448 bam_error(REBOOT_GET_KERNEL_FAILED);
8449 return (BAM_ERROR);
8450 }
8451 if (kernbuf[0] == '\0')
8452 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8453 sizeof (kernbuf));
8454 /*
8455 * If this is a zfs file system and kernbuf does not
8456 * have "-B $ZFS-BOOTFS" string yet, add it.
8457 */
8458 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8459 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8460 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8461 }
8462 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8463 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8464 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
8465 } else if (opt[0] == '/') {
8466 /* It's a full path, so write it out. */
8467 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8468
8469 /*
8470 * If someone runs:
8471 *
8472 * # eeprom boot-args='-kd'
8473 * # reboot /platform/i86pc/kernel/unix
8474 *
8475 * we want to use the boot-args as part of the boot
8476 * line. On the other hand, if someone runs:
8477 *
8478 * # reboot "/platform/i86pc/kernel/unix -kd"
8479 *
8480 * we don't need to mess with boot-args. If there's
8481 * no space in the options string, assume we're in the
8482 * first case.
8483 */
8484 if (strchr(opt, ' ') == NULL) {
8485 ret = get_kernel(mp, ARGS_CMD, args_buf,
8486 sizeof (args_buf));
8487 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8488 if (ret != BAM_SUCCESS) {
8489 free(fstype);
8490 bam_error(REBOOT_GET_ARGS_FAILED);
8491 return (BAM_ERROR);
8492 }
8493
8494 if (args_buf[0] != '\0') {
8495 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8496 (void) strlcat(kernbuf, args_buf,
8497 sizeof (kernbuf));
8498 }
8499 }
8500 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
8501 } else {
8502 /*
8503 * It may be a partial path, or it may be a partial
8504 * path followed by options. Assume that only options
8505 * follow a space. If someone sends us a kernel path
8506 * that includes a space, they deserve to be broken.
8507 */
8508 opt_ptr = strchr(opt, ' ');
8509 if (opt_ptr != NULL) {
8510 *opt_ptr = '\0';
8511 }
8512
8513 path = expand_path(opt);
8514 if (path != NULL) {
8515 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8516 free(path);
8517
8518 /*
8519 * If there were options given, use those.
8520 * Otherwise, copy over the default options.
8521 */
8522 if (opt_ptr != NULL) {
8523 /* Restore the space in opt string */
8524 *opt_ptr = ' ';
8525 (void) strlcat(kernbuf, opt_ptr,
8526 sizeof (kernbuf));
8527 } else {
8528 ret = get_kernel(mp, ARGS_CMD, args_buf,
8529 sizeof (args_buf));
8530 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8531 ret = BAM_ERROR);
8532 if (ret != BAM_SUCCESS) {
8533 free(fstype);
8534 bam_error(REBOOT_GET_ARGS_FAILED);
8535 return (BAM_ERROR);
8536 }
8537
8538 if (args_buf[0] != '\0') {
8539 (void) strlcat(kernbuf, " ",
8540 sizeof (kernbuf));
8541 (void) strlcat(kernbuf,
8542 args_buf, sizeof (kernbuf));
8543 }
8544 }
8545 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
8546 } else {
8547 free(fstype);
8548 bam_error(UNKNOWN_KERNEL, opt);
8549 bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
8550 return (BAM_ERROR);
8551 }
8552 }
8553 free(fstype);
8554 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8555 NULL, NULL, NULL);
8556 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8557 if (entry == BAM_ERROR) {
8558 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
8559 return (BAM_ERROR);
8560 }
8561
8562 save_default_entry(mp, BAM_OLDDEF);
8563 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8564 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8565 if (ret == BAM_ERROR) {
8566 bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
8567 }
8568 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8569 return (BAM_WRITE);
8570 }
8571
8572 error_t
8573 set_global(menu_t *mp, char *globalcmd, int val)
8574 {
8575 line_t *lp;
8576 line_t *found;
8577 line_t *last;
8578 char *cp;
8579 char *str;
8580 char prefix[BAM_MAXLINE];
8581 size_t len;
8582 const char *fcn = "set_global()";
8583
8584 assert(mp);
8585 assert(globalcmd);
8586
8587 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
8588 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
8589 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
8590 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
8591 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
8592 (void) snprintf(prefix, sizeof (prefix), "%d", val);
8593 bam_error(INVALID_ENTRY, prefix);
8594 return (BAM_ERROR);
8595 }
8596 }
8597
8598 found = last = NULL;
8599 for (lp = mp->start; lp; lp = lp->next) {
8600 if (lp->flags != BAM_GLOBAL)
8601 continue;
8602
8603 last = lp; /* track the last global found */
8604
8605 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
8606 if (lp->cmd == NULL) {
8607 bam_error(NO_CMD, lp->lineNum);
8608 continue;
8609 }
8610 if (strcmp(globalcmd, lp->cmd) != 0)
8611 continue;
8612
8613 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
8614
8615 if (found) {
8616 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
8617 }
8618 found = lp;
8619 }
8620
8621 if (found == NULL) {
8622 lp = s_calloc(1, sizeof (line_t));
8623 if (last == NULL) {
8624 lp->next = mp->start;
8625 mp->start = lp;
8626 mp->end = (mp->end) ? mp->end : lp;
8627 } else {
8628 lp->next = last->next;
8629 last->next = lp;
8630 if (lp->next == NULL)
8631 mp->end = lp;
8632 }
8633 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
8634 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
8635 len += 10; /* val < 10 digits */
8636 lp->line = s_calloc(1, len);
8637 (void) snprintf(lp->line, len, "%s%s%d",
8638 globalcmd, menu_cmds[SEP_CMD], val);
8639 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
8640 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8641 return (BAM_WRITE);
8642 }
8643
8644 /*
8645 * We are changing an existing entry. Retain any prefix whitespace,
8646 * but overwrite everything else. This preserves tabs added for
8647 * readability.
8648 */
8649 str = found->line;
8650 cp = prefix;
8651 while (*str == ' ' || *str == '\t')
8652 *(cp++) = *(str++);
8653 *cp = '\0'; /* Terminate prefix */
8654 len = strlen(prefix) + strlen(globalcmd);
8655 len += strlen(menu_cmds[SEP_CMD]) + 10;
8656
8657 free(found->line);
8658 found->line = s_calloc(1, len);
8659 (void) snprintf(found->line, len,
8660 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
8661
8662 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
8663 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8664 return (BAM_WRITE); /* need a write to menu */
8665 }
8666
8667 /*
8668 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
8669 * expand it to a full unix path. The calling function is expected to
8670 * output a message if an error occurs and NULL is returned.
8671 */
8672 static char *
8673 expand_path(const char *partial_path)
8674 {
8675 int new_path_len;
8676 char *new_path;
8677 char new_path2[PATH_MAX];
8678 struct stat sb;
8679 const char *fcn = "expand_path()";
8680
8681 new_path_len = strlen(partial_path) + 64;
8682 new_path = s_calloc(1, new_path_len);
8683
8684 /* First, try the simplest case - something like "kernel/unix" */
8685 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
8686 partial_path);
8687 if (stat(new_path, &sb) == 0) {
8688 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8689 return (new_path);
8690 }
8691
8692 if (strcmp(partial_path, "kmdb") == 0) {
8693 (void) snprintf(new_path, new_path_len, "%s -k",
8694 DIRECT_BOOT_KERNEL);
8695 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8696 return (new_path);
8697 }
8698
8699 /*
8700 * We've quickly reached unsupported usage. Try once more to
8701 * see if we were just given a glom name.
8702 */
8703 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
8704 partial_path);
8705 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
8706 partial_path);
8707 if (stat(new_path, &sb) == 0) {
8708 if (stat(new_path2, &sb) == 0) {
8709 /*
8710 * We matched both, so we actually
8711 * want to write the $ISADIR version.
8712 */
8713 (void) snprintf(new_path, new_path_len,
8714 "/platform/i86pc/kernel/%s/$ISADIR/unix",
8715 partial_path);
8716 }
8717 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8718 return (new_path);
8719 }
8720
8721 free(new_path);
8722 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8723 return (NULL);
8724 }
8725
8726 /*
8727 * The kernel cmd and arg have been changed, so
8728 * check whether the archive line needs to change.
8729 */
8730 static void
8731 set_archive_line(entry_t *entryp, line_t *kernelp)
8732 {
8733 line_t *lp = entryp->start;
8734 char *new_archive;
8735 menu_cmd_t m_cmd;
8736 const char *fcn = "set_archive_line()";
8737
8738 for (; lp != NULL; lp = lp->next) {
8739 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
8740 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
8741 break;
8742 }
8743
8744 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
8745 if (lp == entryp->end) {
8746 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
8747 entryp->entryNum));
8748 return;
8749 }
8750 }
8751 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
8752 if (lp == NULL) {
8753 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
8754 return;
8755 }
8756
8757 if (strstr(kernelp->arg, "$ISADIR") != NULL) {
8758 new_archive = DIRECT_BOOT_ARCHIVE;
8759 m_cmd = MODULE_DOLLAR_CMD;
8760 } else if (strstr(kernelp->arg, "amd64") != NULL) {
8761 new_archive = DIRECT_BOOT_ARCHIVE_64;
8762 m_cmd = MODULE_CMD;
8763 } else {
8764 new_archive = DIRECT_BOOT_ARCHIVE_32;
8765 m_cmd = MODULE_CMD;
8766 }
8767
8768 if (strcmp(lp->arg, new_archive) == 0) {
8769 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
8770 return;
8771 }
8772
8773 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
8774 free(lp->cmd);
8775 lp->cmd = s_strdup(menu_cmds[m_cmd]);
8776 }
8777
8778 free(lp->arg);
8779 lp->arg = s_strdup(new_archive);
8780 update_line(lp);
8781 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
8782 }
8783
8784 /*
8785 * Title for an entry to set properties that once went in bootenv.rc.
8786 */
8787 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
8788
8789 /*
8790 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
8791 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
8792 * string, reset the value to the default. If path is a non-zero-length
8793 * string, set the kernel or arguments.
8794 */
8795 static error_t
8796 get_set_kernel(
8797 menu_t *mp,
8798 menu_cmd_t optnum,
8799 char *path,
8800 char *buf,
8801 size_t bufsize)
8802 {
8803 int entryNum;
8804 int rv = BAM_SUCCESS;
8805 int free_new_path = 0;
8806 entry_t *entryp;
8807 line_t *ptr;
8808 line_t *kernelp;
8809 char *new_arg;
8810 char *old_args;
8811 char *space;
8812 char *new_path;
8813 char old_space;
8814 size_t old_kernel_len;
8815 size_t new_str_len;
8816 char *fstype;
8817 char *osdev;
8818 char *sign;
8819 char signbuf[PATH_MAX];
8820 int ret;
8821 const char *fcn = "get_set_kernel()";
8822
8823 assert(bufsize > 0);
8824
8825 ptr = kernelp = NULL;
8826 new_arg = old_args = space = NULL;
8827 new_path = NULL;
8828 buf[0] = '\0';
8829
8830 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
8831 bam_direct = BAM_DIRECT_MULTIBOOT);
8832 if (bam_direct != BAM_DIRECT_DBOOT) {
8833 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
8834 return (BAM_ERROR);
8835 }
8836
8837 /*
8838 * If a user changed the default entry to a non-bootadm controlled
8839 * one, we don't want to mess with it. Just print an error and
8840 * return.
8841 */
8842 if (mp->curdefault) {
8843 entryNum = s_strtol(mp->curdefault->arg);
8844 for (entryp = mp->entries; entryp; entryp = entryp->next) {
8845 if (entryp->entryNum == entryNum)
8846 break;
8847 }
8848 if ((entryp != NULL) &&
8849 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
8850 bam_error(DEFAULT_NOT_BAM);
8851 return (BAM_ERROR);
8852 }
8853 }
8854
8855 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
8856 0, &entryNum);
8857
8858 if (entryp != NULL) {
8859 for (ptr = entryp->start; ptr && ptr != entryp->end;
8860 ptr = ptr->next) {
8861 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
8862 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
8863 kernelp = ptr;
8864 break;
8865 }
8866 }
8867 if (kernelp == NULL) {
8868 bam_error(NO_KERNEL, entryNum);
8869 return (BAM_ERROR);
8870 }
8871
8872 old_kernel_len = strcspn(kernelp->arg, " \t");
8873 space = old_args = kernelp->arg + old_kernel_len;
8874 while ((*old_args == ' ') || (*old_args == '\t'))
8875 old_args++;
8876 }
8877
8878 if (path == NULL) {
8879 if (entryp == NULL) {
8880 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
8881 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8882 return (BAM_SUCCESS);
8883 }
8884 assert(kernelp);
8885 if (optnum == ARGS_CMD) {
8886 if (old_args[0] != '\0') {
8887 (void) strlcpy(buf, old_args, bufsize);
8888 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
8889 }
8890 } else {
8891 /*
8892 * We need to print the kernel, so we just turn the
8893 * first space into a '\0' and print the beginning.
8894 * We don't print anything if it's the default kernel.
8895 */
8896 old_space = *space;
8897 *space = '\0';
8898 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
8899 (void) strlcpy(buf, kernelp->arg, bufsize);
8900 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
8901 }
8902 *space = old_space;
8903 }
8904 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8905 return (BAM_SUCCESS);
8906 }
8907
8908 /*
8909 * First, check if we're resetting an entry to the default.
8910 */
8911 if ((path[0] == '\0') ||
8912 ((optnum == KERNEL_CMD) &&
8913 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
8914 if ((entryp == NULL) || (kernelp == NULL)) {
8915 /* No previous entry, it's already the default */
8916 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
8917 return (BAM_SUCCESS);
8918 }
8919
8920 /*
8921 * Check if we can delete the entry. If we're resetting the
8922 * kernel command, and the args is already empty, or if we're
8923 * resetting the args command, and the kernel is already the
8924 * default, we can restore the old default and delete the entry.
8925 */
8926 if (((optnum == KERNEL_CMD) &&
8927 ((old_args == NULL) || (old_args[0] == '\0'))) ||
8928 ((optnum == ARGS_CMD) &&
8929 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
8930 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
8931 kernelp = NULL;
8932 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
8933 restore_default_entry(mp, BAM_OLD_RC_DEF,
8934 mp->old_rc_default);
8935 mp->old_rc_default = NULL;
8936 rv = BAM_WRITE;
8937 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
8938 goto done;
8939 }
8940
8941 if (optnum == KERNEL_CMD) {
8942 /*
8943 * At this point, we've already checked that old_args
8944 * and entryp are valid pointers. The "+ 2" is for
8945 * a space a the string termination character.
8946 */
8947 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
8948 strlen(old_args) + 2;
8949 new_arg = s_calloc(1, new_str_len);
8950 (void) snprintf(new_arg, new_str_len, "%s %s",
8951 DIRECT_BOOT_KERNEL, old_args);
8952 free(kernelp->arg);
8953 kernelp->arg = new_arg;
8954
8955 /*
8956 * We have changed the kernel line, so we may need
8957 * to update the archive line as well.
8958 */
8959 set_archive_line(entryp, kernelp);
8960 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
8961 fcn, kernelp->arg));
8962 } else {
8963 /*
8964 * We're resetting the boot args to nothing, so
8965 * we only need to copy the kernel. We've already
8966 * checked that the kernel is not the default.
8967 */
8968 new_arg = s_calloc(1, old_kernel_len + 1);
8969 (void) snprintf(new_arg, old_kernel_len + 1, "%s",
8970 kernelp->arg);
8971 free(kernelp->arg);
8972 kernelp->arg = new_arg;
8973 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
8974 fcn, kernelp->arg));
8975 }
8976 rv = BAM_WRITE;
8977 goto done;
8978 }
8979
8980 /*
8981 * Expand the kernel file to a full path, if necessary
8982 */
8983 if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
8984 new_path = expand_path(path);
8985 if (new_path == NULL) {
8986 bam_error(UNKNOWN_KERNEL, path);
8987 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8988 return (BAM_ERROR);
8989 }
8990 free_new_path = 1;
8991 } else {
8992 new_path = path;
8993 free_new_path = 0;
8994 }
8995
8996 /*
8997 * At this point, we know we're setting a new value. First, take care
8998 * of the case where there was no previous entry.
8999 */
9000 if (entryp == NULL) {
9001
9002 /* Similar to code in update_temp */
9003 fstype = get_fstype("/");
9004 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9005 if (fstype == NULL) {
9006 bam_error(BOOTENV_FSTYPE_FAILED);
9007 rv = BAM_ERROR;
9008 goto done;
9009 }
9010
9011 osdev = get_special("/");
9012 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9013 if (osdev == NULL) {
9014 free(fstype);
9015 bam_error(BOOTENV_SPECIAL_FAILED);
9016 rv = BAM_ERROR;
9017 goto done;
9018 }
9019
9020 sign = find_existing_sign("/", osdev, fstype);
9021 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9022 if (sign == NULL) {
9023 free(fstype);
9024 free(osdev);
9025 bam_error(BOOTENV_SIGN_FAILED);
9026 rv = BAM_ERROR;
9027 goto done;
9028 }
9029
9030 free(osdev);
9031 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9032 free(sign);
9033 assert(strchr(signbuf, '(') == NULL &&
9034 strchr(signbuf, ',') == NULL &&
9035 strchr(signbuf, ')') == NULL);
9036
9037 if (optnum == KERNEL_CMD) {
9038 if (strcmp(fstype, "zfs") == 0) {
9039 new_str_len = strlen(new_path) +
9040 strlen(ZFS_BOOT) + 8;
9041 new_arg = s_calloc(1, new_str_len);
9042 (void) snprintf(new_arg, new_str_len, "%s %s",
9043 new_path, ZFS_BOOT);
9044 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9045 new_arg));
9046 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9047 signbuf, new_arg, NULL, NULL, NULL);
9048 free(new_arg);
9049 } else {
9050 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9051 new_path));
9052 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9053 signbuf, new_path, NULL, NULL, NULL);
9054 }
9055 } else {
9056 new_str_len = strlen(path) + 8;
9057 if (strcmp(fstype, "zfs") == 0) {
9058 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9059 new_arg = s_calloc(1, new_str_len);
9060 (void) snprintf(new_arg, new_str_len, "%s %s",
9061 DIRECT_BOOT_KERNEL_ZFS, path);
9062 } else {
9063 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9064 new_arg = s_calloc(1, new_str_len);
9065 (void) snprintf(new_arg, new_str_len, "%s %s",
9066 DIRECT_BOOT_KERNEL, path);
9067 }
9068
9069 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
9070 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9071 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9072 free(new_arg);
9073 }
9074 free(fstype);
9075 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9076 entryNum = BAM_ERROR);
9077 if (entryNum == BAM_ERROR) {
9078 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
9079 BOOTENV_RC_TITLE);
9080 rv = BAM_ERROR;
9081 goto done;
9082 }
9083 save_default_entry(mp, BAM_OLD_RC_DEF);
9084 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9085 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9086 if (ret == BAM_ERROR) {
9087 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
9088 }
9089 rv = BAM_WRITE;
9090 goto done;
9091 }
9092
9093 /*
9094 * There was already an bootenv entry which we need to edit.
9095 */
9096 if (optnum == KERNEL_CMD) {
9097 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9098 new_arg = s_calloc(1, new_str_len);
9099 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9100 old_args);
9101 free(kernelp->arg);
9102 kernelp->arg = new_arg;
9103
9104 /*
9105 * If we have changed the kernel line, we may need to update
9106 * the archive line as well.
9107 */
9108 set_archive_line(entryp, kernelp);
9109 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
9110 kernelp->arg));
9111 } else {
9112 new_str_len = old_kernel_len + strlen(path) + 8;
9113 new_arg = s_calloc(1, new_str_len);
9114 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9115 (void) strlcat(new_arg, " ", new_str_len);
9116 (void) strlcat(new_arg, path, new_str_len);
9117 free(kernelp->arg);
9118 kernelp->arg = new_arg;
9119 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
9120 kernelp->arg));
9121 }
9122 rv = BAM_WRITE;
9123
9124 done:
9125 if ((rv == BAM_WRITE) && kernelp)
9126 update_line(kernelp);
9127 if (free_new_path)
9128 free(new_path);
9129 if (rv == BAM_WRITE) {
9130 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9131 } else {
9132 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9133 }
9134 return (rv);
9135 }
9136
9137 static error_t
9138 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9139 {
9140 const char *fcn = "get_kernel()";
9141 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
9142 return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9143 }
9144
9145 static error_t
9146 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9147 {
9148 const char *fcn = "set_kernel()";
9149 assert(path != NULL);
9150 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
9151 return (get_set_kernel(mp, optnum, path, buf, bufsize));
9152 }
9153
9154 /*ARGSUSED*/
9155 static error_t
9156 set_option(menu_t *mp, char *dummy, char *opt)
9157 {
9158 int optnum;
9159 int optval;
9160 char *val;
9161 char buf[BUFSIZ] = "";
9162 error_t rv;
9163 const char *fcn = "set_option()";
9164
9165 assert(mp);
9166 assert(opt);
9167 assert(dummy == NULL);
9168
9169 /* opt is set from bam_argv[0] and is always non-NULL */
9170 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
9171
9172 val = strchr(opt, '=');
9173 if (val != NULL) {
9174 *val = '\0';
9175 }
9176
9177 if (strcmp(opt, "default") == 0) {
9178 optnum = DEFAULT_CMD;
9179 } else if (strcmp(opt, "timeout") == 0) {
9180 optnum = TIMEOUT_CMD;
9181 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9182 optnum = KERNEL_CMD;
9183 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9184 optnum = ARGS_CMD;
9185 } else {
9186 bam_error(INVALID_OPTION, opt);
9187 return (BAM_ERROR);
9188 }
9189
9190 /*
9191 * kernel and args are allowed without "=new_value" strings. All
9192 * others cause errors
9193 */
9194 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9195 bam_error(NO_OPTION_ARG, opt);
9196 return (BAM_ERROR);
9197 } else if (val != NULL) {
9198 *val = '=';
9199 }
9200
9201 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9202 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
9203 val ? val + 1 : "NULL"));
9204
9205 if (val)
9206 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9207 else
9208 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9209 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9210 (void) printf("%s\n", buf);
9211 } else {
9212 optval = s_strtol(val + 1);
9213 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
9214 rv = set_global(mp, menu_cmds[optnum], optval);
9215 }
9216
9217 if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9218 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9219 } else {
9220 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9221 }
9222
9223 return (rv);
9224 }
9225
9226 /*
9227 * The quiet argument suppresses messages. This is used
9228 * when invoked in the context of other commands (e.g. list_entry)
9229 */
9230 static error_t
9231 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9232 {
9233 line_t *lp;
9234 char *arg;
9235 int done, ret = BAM_SUCCESS;
9236
9237 assert(mp);
9238 assert(menu_path);
9239 assert(globalcmd);
9240
9241 if (mp->start == NULL) {
9242 if (!quiet)
9243 bam_error(NO_MENU, menu_path);
9244 return (BAM_ERROR);
9245 }
9246
9247 done = 0;
9248 for (lp = mp->start; lp; lp = lp->next) {
9249 if (lp->flags != BAM_GLOBAL)
9250 continue;
9251
9252 if (lp->cmd == NULL) {
9253 if (!quiet)
9254 bam_error(NO_CMD, lp->lineNum);
9255 continue;
9256 }
9257
9258 if (strcmp(globalcmd, lp->cmd) != 0)
9259 continue;
9260
9261 /* Found global. Check for duplicates */
9262 if (done && !quiet) {
9263 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
9264 ret = BAM_ERROR;
9265 }
9266
9267 arg = lp->arg ? lp->arg : "";
9268 bam_print(GLOBAL_CMD, globalcmd, arg);
9269 done = 1;
9270 }
9271
9272 if (!done && bam_verbose)
9273 bam_print(NO_ENTRY, globalcmd);
9274
9275 return (ret);
9276 }
9277
9278 static error_t
9279 menu_write(char *root, menu_t *mp)
9280 {
9281 const char *fcn = "menu_write()";
9282
9283 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
9284 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9285 }
9286
9287 void
9288 line_free(line_t *lp)
9289 {
9290 if (lp == NULL)
9291 return;
9292
9293 if (lp->cmd != NULL)
9294 free(lp->cmd);
9295 if (lp->sep)
9296 free(lp->sep);
9297 if (lp->arg)
9298 free(lp->arg);
9299 if (lp->line)
9300 free(lp->line);
9301 free(lp);
9302 }
9303
9304 static void
9305 linelist_free(line_t *start)
9306 {
9307 line_t *lp;
9308
9309 while (start) {
9310 lp = start;
9311 start = start->next;
9312 line_free(lp);
9313 }
9314 }
9315
9316 static void
9317 filelist_free(filelist_t *flistp)
9318 {
9319 linelist_free(flistp->head);
9320 flistp->head = NULL;
9321 flistp->tail = NULL;
9322 }
9323
9324 static void
9325 menu_free(menu_t *mp)
9326 {
9327 entry_t *ent, *tmp;
9328 assert(mp);
9329
9330 if (mp->start)
9331 linelist_free(mp->start);
9332 ent = mp->entries;
9333 while (ent) {
9334 tmp = ent;
9335 ent = tmp->next;
9336 free(tmp);
9337 }
9338
9339 free(mp);
9340 }
9341
9342 /*
9343 * Utility routines
9344 */
9345
9346
9347 /*
9348 * Returns 0 on success
9349 * Any other value indicates an error
9350 */
9351 static int
9352 exec_cmd(char *cmdline, filelist_t *flistp)
9353 {
9354 char buf[BUFSIZ];
9355 int ret;
9356 FILE *ptr;
9357 sigset_t set;
9358 void (*disp)(int);
9359
9360 /*
9361 * For security
9362 * - only absolute paths are allowed
9363 * - set IFS to space and tab
9364 */
9365 if (*cmdline != '/') {
9366 bam_error(ABS_PATH_REQ, cmdline);
9367 return (-1);
9368 }
9369 (void) putenv("IFS= \t");
9370
9371 /*
9372 * We may have been exec'ed with SIGCHLD blocked
9373 * unblock it here
9374 */
9375 (void) sigemptyset(&set);
9376 (void) sigaddset(&set, SIGCHLD);
9377 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9378 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
9379 return (-1);
9380 }
9381
9382 /*
9383 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9384 */
9385 disp = sigset(SIGCHLD, SIG_DFL);
9386 if (disp == SIG_ERR) {
9387 bam_error(FAILED_SIG, strerror(errno));
9388 return (-1);
9389 }
9390 if (disp == SIG_HOLD) {
9391 bam_error(BLOCKED_SIG, cmdline);
9392 return (-1);
9393 }
9394
9395 ptr = popen(cmdline, "r");
9396 if (ptr == NULL) {
9397 bam_error(POPEN_FAIL, cmdline, strerror(errno));
9398 return (-1);
9399 }
9400
9401 /*
9402 * If we simply do a pclose() following a popen(), pclose()
9403 * will close the reader end of the pipe immediately even
9404 * if the child process has not started/exited. pclose()
9405 * does wait for cmd to terminate before returning though.
9406 * When the executed command writes its output to the pipe
9407 * there is no reader process and the command dies with
9408 * SIGPIPE. To avoid this we read repeatedly until read
9409 * terminates with EOF. This indicates that the command
9410 * (writer) has closed the pipe and we can safely do a
9411 * pclose().
9412 *
9413 * Since pclose() does wait for the command to exit,
9414 * we can safely reap the exit status of the command
9415 * from the value returned by pclose()
9416 */
9417 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9418 if (flistp == NULL) {
9419 /* s_fgets strips newlines, so insert them at the end */
9420 bam_print(PRINT, buf);
9421 } else {
9422 append_to_flist(flistp, buf);
9423 }
9424 }
9425
9426 ret = pclose(ptr);
9427 if (ret == -1) {
9428 bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
9429 return (-1);
9430 }
9431
9432 if (WIFEXITED(ret)) {
9433 return (WEXITSTATUS(ret));
9434 } else {
9435 bam_error(EXEC_FAIL, cmdline, ret);
9436 return (-1);
9437 }
9438 }
9439
9440 /*
9441 * Since this function returns -1 on error
9442 * it cannot be used to convert -1. However,
9443 * that is sufficient for what we need.
9444 */
9445 static long
9446 s_strtol(char *str)
9447 {
9448 long l;
9449 char *res = NULL;
9450
9451 if (str == NULL) {
9452 return (-1);
9453 }
9454
9455 errno = 0;
9456 l = strtol(str, &res, 10);
9457 if (errno || *res != '\0') {
9458 return (-1);
9459 }
9460
9461 return (l);
9462 }
9463
9464 /*
9465 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9466 */
9467 static int
9468 s_fputs(char *str, FILE *fp)
9469 {
9470 char linebuf[BAM_MAXLINE];
9471
9472 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9473 return (fputs(linebuf, fp));
9474 }
9475
9476 /*
9477 * Wrapper around fgets, that strips newlines returned by fgets
9478 */
9479 char *
9480 s_fgets(char *buf, int buflen, FILE *fp)
9481 {
9482 int n;
9483
9484 buf = fgets(buf, buflen, fp);
9485 if (buf) {
9486 n = strlen(buf);
9487 if (n == buflen - 1 && buf[n-1] != '\n')
9488 bam_error(TOO_LONG, buflen - 1, buf);
9489 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9490 }
9491
9492 return (buf);
9493 }
9494
9495 void *
9496 s_calloc(size_t nelem, size_t sz)
9497 {
9498 void *ptr;
9499
9500 ptr = calloc(nelem, sz);
9501 if (ptr == NULL) {
9502 bam_error(NO_MEM, nelem*sz);
9503 bam_exit(1);
9504 }
9505 return (ptr);
9506 }
9507
9508 void *
9509 s_realloc(void *ptr, size_t sz)
9510 {
9511 ptr = realloc(ptr, sz);
9512 if (ptr == NULL) {
9513 bam_error(NO_MEM, sz);
9514 bam_exit(1);
9515 }
9516 return (ptr);
9517 }
9518
9519 char *
9520 s_strdup(char *str)
9521 {
9522 char *ptr;
9523
9524 if (str == NULL)
9525 return (NULL);
9526
9527 ptr = strdup(str);
9528 if (ptr == NULL) {
9529 bam_error(NO_MEM, strlen(str) + 1);
9530 bam_exit(1);
9531 }
9532 return (ptr);
9533 }
9534
9535 /*
9536 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9537 * Returns 0 otherwise
9538 */
9539 static int
9540 is_amd64(void)
9541 {
9542 static int amd64 = -1;
9543 char isabuf[257]; /* from sysinfo(2) manpage */
9544
9545 if (amd64 != -1)
9546 return (amd64);
9547
9548 if (bam_alt_platform) {
9549 if (strcmp(bam_platform, "i86pc") == 0) {
9550 amd64 = 1; /* diskless server */
9551 }
9552 } else {
9553 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
9554 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
9555 amd64 = 1;
9556 } else if (strstr(isabuf, "i386") == NULL) {
9557 amd64 = 1; /* diskless server */
9558 }
9559 }
9560 if (amd64 == -1)
9561 amd64 = 0;
9562
9563 return (amd64);
9564 }
9565
9566 static char *
9567 get_machine(void)
9568 {
9569 static int cached = -1;
9570 static char mbuf[257]; /* from sysinfo(2) manpage */
9571
9572 if (cached == 0)
9573 return (mbuf);
9574
9575 if (bam_alt_platform) {
9576 return (bam_platform);
9577 } else {
9578 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
9579 cached = 1;
9580 }
9581 }
9582 if (cached == -1) {
9583 mbuf[0] = '\0';
9584 cached = 0;
9585 }
9586
9587 return (mbuf);
9588 }
9589
9590 int
9591 is_sparc(void)
9592 {
9593 static int issparc = -1;
9594 char mbuf[257]; /* from sysinfo(2) manpage */
9595
9596 if (issparc != -1)
9597 return (issparc);
9598
9599 if (bam_alt_platform) {
9600 if (strncmp(bam_platform, "sun4", 4) == 0) {
9601 issparc = 1;
9602 }
9603 } else {
9604 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
9605 strcmp(mbuf, "sparc") == 0) {
9606 issparc = 1;
9607 }
9608 }
9609 if (issparc == -1)
9610 issparc = 0;
9611
9612 return (issparc);
9613 }
9614
9615 static void
9616 append_to_flist(filelist_t *flistp, char *s)
9617 {
9618 line_t *lp;
9619
9620 lp = s_calloc(1, sizeof (line_t));
9621 lp->line = s_strdup(s);
9622 if (flistp->head == NULL)
9623 flistp->head = lp;
9624 else
9625 flistp->tail->next = lp;
9626 flistp->tail = lp;
9627 }
9628
9629 #if !defined(_OPB)
9630
9631 UCODE_VENDORS;
9632
9633 /*ARGSUSED*/
9634 static void
9635 ucode_install(char *root)
9636 {
9637 int i;
9638
9639 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
9640 int cmd_len = PATH_MAX + 256;
9641 char cmd[PATH_MAX + 256];
9642 char file[PATH_MAX];
9643 char timestamp[PATH_MAX];
9644 struct stat fstatus, tstatus;
9645 struct utimbuf u_times;
9646
9647 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
9648 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
9649 ucode_vendors[i].extstr);
9650
9651 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
9652 continue;
9653
9654 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
9655
9656 if (stat(timestamp, &tstatus) == 0 &&
9657 fstatus.st_mtime <= tstatus.st_mtime)
9658 continue;
9659
9660 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
9661 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
9662 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
9663 if (system(cmd) != 0)
9664 return;
9665
9666 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
9667 return;
9668
9669 u_times.actime = fstatus.st_atime;
9670 u_times.modtime = fstatus.st_mtime;
9671 (void) utime(timestamp, &u_times);
9672 }
9673 }
9674 #endif