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