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