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