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