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