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