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