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