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