1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
24 */
25
26 /*
27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28 * Copyright 2016 Toomas Soome <tsoome@me.com>
29 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
30 * Copyright 2020 2020 Data Direct Networks.
31 */
32
33 /*
34 * Loader menu management.
35 */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <wchar.h>
41 #include <errno.h>
42 #include <limits.h>
43 #include <alloca.h>
44 #include <unistd.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/queue.h>
48 #include <libbe.h>
49 #include <ficl.h>
50 #include <ficlplatform/emu.h>
51 #include <ofmt.h>
52
53 #include "bootadm.h"
54
55 extern int bam_rootlen;
56 extern int bam_alt_root;
57 extern char *rootbuf;
58 extern char *bam_root;
59
60 #define BOOT_DIR "/boot"
61 #define CONF_DIR BOOT_DIR "/conf.d"
62 #define MENU BOOT_DIR "/menu.lst"
63 #define TRANSIENT BOOT_DIR "/transient.conf"
64 #define XEN_CONFIG CONF_DIR "/xen"
65
66 typedef struct menu_entry {
67 int me_idx;
68 boolean_t me_active;
69 boolean_t me_active_next;
70 char *me_title;
71 char *me_type;
72 char *me_bootfs;
73 STAILQ_ENTRY(menu_entry) me_next;
74 } menu_entry_t;
75 STAILQ_HEAD(menu_lst, menu_entry);
76
77 static error_t set_option(struct menu_lst *, char *, char *);
78 static error_t list_entry(struct menu_lst *, char *, char *);
79 static error_t update_entry(struct menu_lst *, char *, char *);
80 static error_t update_temp(struct menu_lst *, char *, char *);
81 static error_t list_setting(struct menu_lst *menu, char *, char *);
82 static error_t disable_hyper(struct menu_lst *, char *, char *);
83 static error_t enable_hyper(struct menu_lst *, char *, char *);
84
85 /* Menu related sub commands */
86 static subcmd_defn_t menu_subcmds[] = {
87 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
88 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
89 "update_entry", OPT_REQ, update_entry, 0, /* menu */
90 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
91 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
92 "disable_hypervisor", OPT_ABSENT, disable_hyper, 0, /* menu */
93 "enable_hypervisor", OPT_ABSENT, enable_hyper, 0, /* menu */
94 NULL, 0, NULL, 0 /* must be last */
95 };
96
97 #define NUM_COLS (5)
98
99 static boolean_t
print_menu_cb(ofmt_arg_t * ofarg,char * buf,uint_t bufsize)100 print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
101 {
102 menu_entry_t *entry = ofarg->ofmt_cbarg;
103
104 switch (ofarg->ofmt_id) {
105 case 0:
106 (void) snprintf(buf, bufsize, "%d", entry->me_idx);
107 break;
108 case 1:
109 (void) snprintf(buf, bufsize, "%s", entry->me_title);
110 break;
111 case 2:
112 (void) snprintf(buf, bufsize, "%s", entry->me_bootfs);
113 break;
114 case 3:
115 (void) snprintf(buf, bufsize, "%s", entry->me_type);
116 break;
117 case 4:
118 if (entry->me_active_next == B_TRUE) {
119 (void) snprintf(buf, bufsize, " T");
120 break;
121 }
122 if (entry->me_active == B_TRUE)
123 (void) snprintf(buf, bufsize, " *");
124 else
125 (void) snprintf(buf, bufsize, " -");
126 break;
127 default:
128 return (B_FALSE);
129 }
130 return (B_TRUE);
131 }
132
133 static void
init_hdr_cols(ofmt_field_t * hdr)134 init_hdr_cols(ofmt_field_t *hdr)
135 {
136 uint_t i;
137
138 for (i = 0; i < NUM_COLS; i++) {
139 char *name = NULL;
140
141 switch (i) {
142 case 0:
143 name = _("INDEX");
144 break;
145 case 1:
146 name = _("NAME");
147 break;
148 case 2:
149 name = _("DEVICE");
150 break;
151 case 3:
152 name = _("TYPE");
153 break;
154 case 4:
155 name = _("DEFAULT");
156 break;
157 }
158
159 hdr[i].of_name = name;
160 hdr[i].of_id = i;
161 hdr[i].of_cb = print_menu_cb;
162
163 if (name != NULL) {
164 wchar_t wname[128];
165 size_t sz = mbstowcs(wname, name, sizeof (wname) /
166 sizeof (wchar_t));
167 if (sz > 0) {
168 int wcsw = wcswidth(wname, sz);
169 if (wcsw > 0)
170 hdr[i].of_width = wcsw;
171 else
172 hdr[i].of_width = sz;
173 } else {
174 hdr[i].of_width = strlen(name);
175 }
176 }
177 }
178 }
179
180 static void
menu_update_widths(ofmt_field_t * hdr,struct menu_lst * menu)181 menu_update_widths(ofmt_field_t *hdr, struct menu_lst *menu)
182 {
183 size_t len[NUM_COLS];
184 menu_entry_t *entry;
185 int i;
186
187 for (i = 0; i < NUM_COLS; i++)
188 len[i] = hdr[i].of_width + 1;
189
190 STAILQ_FOREACH(entry, menu, me_next) {
191 size_t entry_len;
192
193 entry_len = strlen(entry->me_title) + 1;
194 if (entry_len > len[1])
195 len[1] = entry_len;
196
197 entry_len = strlen(entry->me_bootfs) + 1;
198 if (entry_len > len[2])
199 len[2] = entry_len;
200
201 entry_len = strlen(entry->me_type) + 1;
202 if (entry_len > len[3])
203 len[3] = entry_len;
204 }
205
206 for (i = 0; i < NUM_COLS; i++)
207 hdr[i].of_width = len[i];
208 }
209
210 static ofmt_field_t *
init_menu_template(struct menu_lst * menu)211 init_menu_template(struct menu_lst *menu)
212 {
213 ofmt_field_t *temp;
214
215 if ((temp = calloc(NUM_COLS + 1, sizeof (ofmt_field_t))) == NULL)
216 return (temp);
217
218 init_hdr_cols(temp);
219 menu_update_widths(temp, menu);
220 return (temp);
221 }
222
223 static void
print_nodes(boolean_t parsable,struct menu_lst * menu)224 print_nodes(boolean_t parsable, struct menu_lst *menu)
225 {
226 ofmt_status_t oferr;
227 ofmt_handle_t ofmt;
228 uint_t ofmtflags = 0;
229 ofmt_field_t *menu_template;
230 menu_entry_t *entry;
231
232 if (parsable == B_TRUE)
233 ofmtflags = OFMT_PARSABLE;
234
235 menu_template = init_menu_template(menu);
236 oferr = ofmt_open(NULL, menu_template, ofmtflags, 0, &ofmt);
237
238 if (oferr != OFMT_SUCCESS) {
239 char buf[OFMT_BUFSIZE];
240
241 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
242 (void) printf("bootadm: %s\n", buf);
243 free(menu_template);
244 return;
245 }
246
247 STAILQ_FOREACH(entry, menu, me_next)
248 ofmt_print(ofmt, entry);
249
250 ofmt_close(ofmt);
251 free(menu_template);
252 }
253
254 /*
255 * Get the be_active_on_boot for bootfs.
256 */
257 static boolean_t
menu_active_on_boot(be_node_list_t * be_nodes,const char * bootfs)258 menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs)
259 {
260 be_node_list_t *be_node;
261 boolean_t rv = B_FALSE;
262
263 for (be_node = be_nodes; be_node != NULL;
264 be_node = be_node->be_next_node) {
265 if (strcmp(be_node->be_root_ds, bootfs) == 0) {
266 rv = be_node->be_active_on_boot;
267 break;
268 }
269 }
270
271 return (rv);
272 }
273
274 /*
275 * Get the be_active_next for bootfs.
276 */
277 static boolean_t
menu_active_next(be_node_list_t * be_nodes,const char * bootfs)278 menu_active_next(be_node_list_t *be_nodes, const char *bootfs)
279 {
280 be_node_list_t *be_node;
281 boolean_t rv = B_FALSE;
282
283 for (be_node = be_nodes; be_node != NULL;
284 be_node = be_node->be_next_node) {
285 if (strcmp(be_node->be_root_ds, bootfs) == 0) {
286 rv = be_node->be_active_next;
287 break;
288 }
289 }
290
291 return (rv);
292 }
293
294 error_t
menu_read(struct menu_lst * menu,char * menu_path)295 menu_read(struct menu_lst *menu, char *menu_path)
296 {
297 FILE *fp;
298 be_node_list_t *be_nodes;
299 menu_entry_t *mp;
300 char buf[PATH_MAX];
301 char *title;
302 char *bootfs;
303 char *type;
304 char *key, *value;
305 int i = 0;
306 int ret = BAM_SUCCESS;
307
308 fp = fopen(menu_path, "r");
309 if (fp == NULL)
310 return (BAM_ERROR);
311
312 if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS)
313 be_nodes = NULL;
314
315 /*
316 * menu.lst entry is on two lines, one for title, one for bootfs
317 * so we process both lines in succession.
318 */
319 title = NULL;
320 type = NULL;
321 bootfs = NULL;
322 do {
323 if (fgets(buf, PATH_MAX, fp) == NULL) {
324 if (!feof(fp))
325 ret = BAM_ERROR;
326 goto done;
327 }
328 if (buf[0] == '\n') /* Skip empty lines */
329 continue;
330
331 key = strtok(buf, " \n");
332 if (key == NULL || strcmp(key, "title") != 0) {
333 ret = BAM_ERROR;
334 goto done;
335 }
336 value = strtok(NULL, " \n");
337 if (value == NULL || (title = strdup(value)) == NULL) {
338 ret = BAM_ERROR;
339 goto done;
340 }
341
342 do {
343 if (fgets(buf, PATH_MAX, fp) == NULL) {
344 ret = BAM_ERROR;
345 goto done;
346 }
347 } while (buf[0] == '\n'); /* Skip empty lines */
348
349 key = strtok(buf, " \n");
350 if (key == NULL || (type = strdup(key)) == NULL) {
351 ret = BAM_ERROR;
352 goto done;
353 }
354 value = strtok(NULL, " \n");
355 if (value == NULL || (bootfs = strdup(value)) == NULL) {
356 ret = BAM_ERROR;
357 goto done;
358 }
359 if ((mp = malloc(sizeof (menu_entry_t))) == NULL) {
360 ret = BAM_ERROR;
361 goto done;
362 }
363 mp->me_idx = i++;
364 mp->me_title = title;
365 mp->me_type = type;
366 mp->me_bootfs = bootfs;
367 mp->me_active = menu_active_on_boot(be_nodes, bootfs);
368 mp->me_active_next = menu_active_next(be_nodes, bootfs);
369 STAILQ_INSERT_TAIL(menu, mp, me_next);
370
371 title = NULL;
372 type = NULL;
373 bootfs = NULL;
374 } while (feof(fp) == 0);
375
376 done:
377 free(title);
378 free(type);
379 free(bootfs);
380 (void) fclose(fp);
381 be_free_list(be_nodes);
382 return (ret);
383 }
384
385 void
menu_free(struct menu_lst * menu)386 menu_free(struct menu_lst *menu)
387 {
388 menu_entry_t *entry;
389
390 while (!STAILQ_EMPTY(menu)) {
391 entry = STAILQ_FIRST(menu);
392 STAILQ_REMOVE_HEAD(menu, me_next);
393 free(entry->me_title);
394 free(entry->me_type);
395 free(entry->me_bootfs);
396 free(entry);
397 }
398 }
399
400 error_t
bam_loader_menu(char * subcmd,char * opt,int largc,char * largv[])401 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[])
402 {
403 error_t ret;
404 char menu_path[PATH_MAX];
405 char clean_menu_root[PATH_MAX];
406 char menu_root[PATH_MAX];
407 struct stat sb;
408 error_t (*f)(struct menu_lst *, char *, char *);
409 char *special;
410 char *pool = NULL;
411 zfs_mnted_t zmnted;
412 char *zmntpt;
413 char *osdev;
414 char *osroot;
415 const char *fcn = "bam_loader_menu()";
416 struct menu_lst menu = {0};
417
418 STAILQ_INIT(&menu);
419
420 /*
421 * Check arguments
422 */
423 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
424 if (ret == BAM_ERROR) {
425 return (BAM_ERROR);
426 }
427
428 assert(bam_root);
429
430 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
431 osdev = osroot = NULL;
432
433 if (strcmp(subcmd, "update_entry") == 0) {
434 assert(opt);
435
436 osdev = strtok(opt, ",");
437 assert(osdev);
438 osroot = strtok(NULL, ",");
439 if (osroot) {
440 /* fixup bam_root so that it points at osroot */
441 if (realpath(osroot, rootbuf) == NULL) {
442 bam_error(_("cannot resolve path %s: %s\n"),
443 osroot, strerror(errno));
444 return (BAM_ERROR);
445 }
446 bam_alt_root = 1;
447 bam_root = rootbuf;
448 bam_rootlen = strlen(rootbuf);
449 }
450 }
451
452 if (stat(menu_root, &sb) == -1) {
453 bam_error(_("cannot find menu\n"));
454 return (BAM_ERROR);
455 }
456
457 if (!is_zfs(menu_root)) {
458 bam_error(_("only ZFS root is supported\n"));
459 return (BAM_ERROR);
460 }
461
462 assert(strcmp(menu_root, bam_root) == 0);
463 special = get_special(menu_root);
464 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
465 if (special == NULL) {
466 bam_error(_("cant find special file for mount-point %s\n"),
467 menu_root);
468 return (BAM_ERROR);
469 }
470 pool = strtok(special, "/");
471 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
472 if (pool == NULL) {
473 free(special);
474 bam_error(_("cant find pool for mount-point %s\n"), menu_root);
475 return (BAM_ERROR);
476 }
477 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
478
479 zmntpt = mount_top_dataset(pool, &zmnted);
480 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
481 if (zmntpt == NULL) {
482 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool);
483 free(special);
484 return (BAM_ERROR);
485 }
486 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
487
488 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
489 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
490
491 elide_trailing_slash(menu_root, clean_menu_root,
492 sizeof (clean_menu_root));
493
494 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
495
496 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
497 (void) strlcat(menu_path, MENU, sizeof (menu_path));
498
499 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
500
501 /*
502 * update_entry is special case, its used by installer
503 * and needs to create menu.lst file for loader
504 */
505 if (menu_read(&menu, menu_path) == BAM_ERROR &&
506 strcmp(subcmd, "update_entry") != 0) {
507 bam_error(_("cannot find menu file: %s\n"), menu_path);
508 if (special != NULL)
509 free(special);
510 return (BAM_ERROR);
511 }
512
513 /*
514 * If listing the menu, display the menu location
515 */
516 if (strcmp(subcmd, "list_entry") == 0)
517 bam_print(_("the location for the active menu is: %s\n"),
518 menu_path);
519
520 /*
521 * We already checked the following case in
522 * check_subcmd_and_suboptions() above. Complete the
523 * final step now.
524 */
525 if (strcmp(subcmd, "set_option") == 0) {
526 assert(largc == 1 && largv[0] && largv[1] == NULL);
527 opt = largv[0];
528 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
529 (strcmp(subcmd, "list_setting") != 0)) {
530 assert(largc == 0 && largv == NULL);
531 }
532
533 /*
534 * Once the sub-cmd handler has run
535 * only the line field is guaranteed to have valid values
536 */
537 if (strcmp(subcmd, "update_entry") == 0) {
538 ret = f(&menu, menu_root, osdev);
539 } else if (strcmp(subcmd, "upgrade") == 0) {
540 ret = f(&menu, bam_root, menu_root);
541 } else if (strcmp(subcmd, "list_entry") == 0) {
542 ret = f(&menu, menu_path, opt);
543 } else if (strcmp(subcmd, "list_setting") == 0) {
544 ret = f(&menu, ((largc > 0) ? largv[0] : ""),
545 ((largc > 1) ? largv[1] : ""));
546 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
547 if (is_sparc()) {
548 bam_error(_("%s operation unsupported on SPARC "
549 "machines\n"), subcmd);
550 ret = BAM_ERROR;
551 } else {
552 ret = f(&menu, bam_root, NULL);
553 }
554 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
555 if (is_sparc()) {
556 bam_error(_("%s operation unsupported on SPARC "
557 "machines\n"), subcmd);
558 ret = BAM_ERROR;
559 } else {
560 char *extra_args = NULL;
561
562 /*
563 * Compress all arguments passed in the largv[] array
564 * into one string that can then be appended to the
565 * end of the kernel$ string the routine to enable the
566 * hypervisor will build.
567 *
568 * This allows the caller to supply arbitrary unparsed
569 * arguments, such as dom0 memory settings or APIC
570 * options.
571 *
572 * This concatenation will be done without ANY syntax
573 * checking whatsoever, so it's the responsibility of
574 * the caller to make sure the arguments are valid and
575 * do not duplicate arguments the conversion routines
576 * may create.
577 */
578 if (largc > 0) {
579 int extra_len, i;
580
581 for (extra_len = 0, i = 0; i < largc; i++)
582 extra_len += strlen(largv[i]);
583
584 /*
585 * Allocate space for argument strings,
586 * intervening spaces and terminating NULL.
587 */
588 extra_args = alloca(extra_len + largc);
589
590 (void) strcpy(extra_args, largv[0]);
591
592 for (i = 1; i < largc; i++) {
593 (void) strcat(extra_args, " ");
594 (void) strcat(extra_args, largv[i]);
595 }
596 }
597
598 ret = f(&menu, bam_root, extra_args);
599 }
600 } else
601 ret = f(&menu, NULL, opt);
602
603 if (ret == BAM_WRITE) {
604 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
605 fcn, clean_menu_root));
606 /* ret = menu_write(clean_menu_root, menu); */
607 }
608
609 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
610 assert((is_zfs(menu_root)) ^ (pool == NULL));
611 if (pool) {
612 (void) umount_top_dataset(pool, zmnted, zmntpt);
613 free(special);
614 }
615
616 menu_free(&menu);
617 return (ret);
618 }
619
620 /*
621 * To suppress output from ficl. We do not want to see messages
622 * from interpreting loader config.
623 */
624
625 /*ARGSUSED*/
626 static void
ficlTextOutSilent(ficlCallback * cb,char * text)627 ficlTextOutSilent(ficlCallback *cb, char *text)
628 {
629 }
630
631 /*ARGSUSED*/
632 static error_t
set_option(struct menu_lst * menu,char * dummy,char * opt)633 set_option(struct menu_lst *menu, char *dummy, char *opt)
634 {
635 char path[PATH_MAX];
636 char *val;
637 char *rest;
638 int optval;
639 menu_entry_t *entry;
640 nvlist_t *be_attrs;
641 FILE *fp;
642 int rv, ret = BAM_SUCCESS;
643
644 assert(menu);
645 assert(opt);
646 assert(dummy == NULL);
647
648 val = strchr(opt, '=');
649 if (val != NULL) {
650 *val++ = '\0';
651 } else {
652 bam_error(_("missing value in key=value\n"));
653 return (BAM_ERROR);
654 }
655
656 if (strcmp(opt, "default") == 0) {
657 errno = 0;
658 optval = strtol(val, &rest, 10);
659 if (errno != 0 || *rest != '\0') {
660 bam_error(_("invalid boot entry number: %s\n"), val);
661 return (BAM_ERROR);
662 }
663 STAILQ_FOREACH(entry, menu, me_next) {
664 if (entry->me_idx == optval)
665 break;
666 }
667 if (entry == NULL) {
668 bam_error(_("invalid boot entry number: %s\n"), val);
669 return (BAM_ERROR);
670 }
671 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
672 bam_error(_("out of memory\n"));
673 return (BAM_ERROR);
674 }
675 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
676 entry->me_title) != 0) {
677 bam_error(_("out of memory\n"));
678 nvlist_free(be_attrs);
679 return (BAM_ERROR);
680 }
681 ret = be_activate(be_attrs);
682 nvlist_free(be_attrs);
683 if (ret != 0)
684 ret = BAM_ERROR;
685 return (ret);
686 } else if (strcmp(opt, "timeout") == 0) {
687 errno = 0;
688 optval = strtol(val, &rest, 10);
689 if (errno != 0 || *rest != '\0') {
690 bam_error(_("invalid timeout: %s\n"), val);
691 return (BAM_ERROR);
692 }
693
694 (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout",
695 bam_root);
696
697 fp = fopen(path, "w");
698 if (fp == NULL) {
699 bam_error(_("failed to open file: %s: %s\n"),
700 path, strerror(errno));
701 return (BAM_ERROR);
702 }
703 /*
704 * timeout=-1 is to disable auto boot in illumos, but
705 * loader needs "NO" to disable auto boot.
706 */
707 if (optval == -1)
708 rv = fprintf(fp, "autoboot_delay=\"NO\"\n");
709 else
710 rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval);
711
712 if (rv < 0) {
713 bam_error(_("write to file failed: %s: %s\n"),
714 path, strerror(errno));
715 (void) fclose(fp);
716 ret = BAM_ERROR;
717 } else
718 rv = fclose(fp);
719
720 if (rv < 0) {
721 bam_error(_("failed to close file: %s: %s\n"),
722 path, strerror(errno));
723 ret = BAM_ERROR;
724 }
725 if (ret == BAM_ERROR)
726 (void) unlink(path);
727
728 return (BAM_SUCCESS);
729 }
730
731 bam_error(_("invalid option: %s\n"), opt);
732 return (BAM_ERROR);
733 }
734
735 static int
bam_mount_be(menu_entry_t * entry,char ** dir)736 bam_mount_be(menu_entry_t *entry, char **dir)
737 {
738 nvlist_t *be_attrs = NULL;
739 const char *tmpdir = getenv("TMPDIR");
740 const char *tmpname = "bam.XXXXXX";
741 be_node_list_t *be_node, *be_nodes = NULL;
742 int ret;
743
744 *dir = NULL;
745 if (tmpdir == NULL)
746 tmpdir = "/tmp";
747
748 ret = asprintf(dir, "%s/%s", tmpdir, tmpname);
749 if (ret < 0) {
750 return (BE_ERR_NOMEM);
751 }
752 *dir = mkdtemp(*dir);
753
754 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
755 ret = BE_ERR_NOMEM;
756 goto out;
757 }
758
759 ret = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
760 if (ret != BE_SUCCESS) {
761 goto out;
762 }
763
764 for (be_node = be_nodes; be_node;
765 be_node = be_node->be_next_node)
766 if (strcmp(be_node->be_root_ds, entry->me_bootfs) == 0)
767 break;
768
769 if (be_node == NULL) {
770 ret = BE_ERR_BE_NOENT;
771 goto out;
772 }
773
774 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
775 be_node->be_node_name) != 0) {
776 ret = BE_ERR_NOMEM;
777 goto out;
778 }
779
780 if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) {
781 ret = BE_ERR_NOMEM;
782 goto out;
783 }
784
785 ret = be_mount(be_attrs);
786 if (ret == BE_ERR_MOUNTED) {
787 /*
788 * if BE is mounted, dir does not point to correct directory
789 */
790 (void) rmdir(*dir);
791 free(*dir);
792 *dir = NULL;
793 }
794 out:
795 if (be_nodes != NULL)
796 be_free_list(be_nodes);
797 nvlist_free(be_attrs);
798 return (ret);
799 }
800
801 static int
bam_umount_be(char * dir)802 bam_umount_be(char *dir)
803 {
804 nvlist_t *be_attrs;
805 int ret;
806
807 if (dir == NULL) /* nothing to do */
808 return (BE_SUCCESS);
809
810 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0)
811 return (BE_ERR_NOMEM);
812
813 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) {
814 ret = BE_ERR_NOMEM;
815 goto out;
816 }
817
818 ret = be_unmount(be_attrs);
819 out:
820 nvlist_free(be_attrs);
821 return (ret);
822 }
823
824 /*
825 * display details of menu entry or single property
826 */
827 static error_t
list_menu_entry(menu_entry_t * entry,char * setting)828 list_menu_entry(menu_entry_t *entry, char *setting)
829 {
830 int ret = BAM_SUCCESS;
831 char *ptr, *dir;
832 char buf[MAX_INPUT];
833 ficlVm *vm;
834 int mounted;
835
836 ptr = strrchr(entry->me_bootfs, ':');
837 if (strcmp(entry->me_type, "bootfs") != 0 ||
838 (ptr != NULL && ptr[1] == '\0')) {
839 (void) printf("\nTitle: %s\n", entry->me_title);
840 (void) printf("Type: %s\n", entry->me_type);
841 (void) printf("Device: %s\n", entry->me_bootfs);
842 return (ret);
843 }
844
845 mounted = bam_mount_be(entry, &dir);
846 if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) {
847 if (dir != NULL) {
848 (void) rmdir(dir);
849 free(dir);
850 }
851 bam_error(_("%s is not mounted: %s\n"), entry->me_title,
852 be_err_to_str(mounted));
853 return (BAM_ERROR);
854 }
855
856 vm = bf_init("", ficlTextOutSilent);
857 if (vm == NULL) {
858 bam_error(_("error setting up forth interpreter\n"));
859 ret = BAM_ERROR;
860 goto done;
861 }
862
863 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
864 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:",
865 entry->me_bootfs);
866 ret = ficlVmEvaluate(vm, buf);
867 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
868 bam_error(_("error interpreting boot config\n"));
869 ret = BAM_ERROR;
870 goto done;
871 }
872 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
873 ret = ficlVmEvaluate(vm, buf);
874 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
875 bam_error(_("error interpreting boot config\n"));
876 ret = BAM_ERROR;
877 goto done;
878 }
879 (void) snprintf(buf, MAX_INPUT, "start");
880 ret = ficlVmEvaluate(vm, buf);
881 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
882 bam_error(_("error interpreting boot config\n"));
883 ret = BAM_ERROR;
884 goto done;
885 }
886 (void) snprintf(buf, MAX_INPUT, "boot");
887 ret = ficlVmEvaluate(vm, buf);
888 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
889 bam_error(_("error interpreting boot config\n"));
890 ret = BAM_ERROR;
891 goto done;
892 }
893
894 ret = BAM_SUCCESS;
895 if (*setting == '\0')
896 (void) printf("\nTitle: %s\n", entry->me_title);
897 else if (strcasecmp(setting, "title") == 0) {
898 (void) printf("%s\n", entry->me_title);
899 goto done;
900 }
901
902 ptr = getenv("autoboot_delay");
903 if (ptr != NULL) {
904 char *timeout = "-1";
905
906 if (strcasecmp(ptr, "NO") != 0)
907 timeout = ptr;
908
909 if (*setting == '\0')
910 (void) printf("Timeout: %s\n", timeout);
911 else if (strcasecmp(setting, "timeout") == 0) {
912 (void) printf("%s\n", timeout);
913 goto done;
914 }
915
916 }
917 ptr = getenv("console");
918 if (ptr != NULL) {
919 if (*setting == '\0')
920 (void) printf("Console: %s\n", ptr);
921 else if (strcasecmp(setting, "console") == 0) {
922 (void) printf("%s\n", ptr);
923 goto done;
924 }
925 }
926
927 if (*setting == '\0')
928 (void) printf("Bootfs: %s\n", entry->me_bootfs);
929 else if (strcasecmp(setting, "bootfs") == 0) {
930 (void) printf("%s\n", entry->me_bootfs);
931 goto done;
932 }
933
934 ptr = getenv("xen_kernel");
935 if (ptr != NULL) {
936 if (*setting == '\0') {
937 (void) printf("Xen kernel: %s\n", ptr);
938 } else if (strcasecmp(setting, "xen_kernel") == 0) {
939 (void) printf("%s\n", ptr);
940 goto done;
941 }
942
943 if (*setting == '\0') {
944 (void) printf("Xen args: \"%s\"\n",
945 getenv("xen_cmdline"));
946 } else if (strcasecmp(setting, "xen_cmdline") == 0) {
947 (void) printf("%s\n", getenv("xen_cmdline"));
948 goto done;
949 }
950
951 if (*setting == '\0') {
952 (void) printf("Kernel: %s\n",
953 getenv("bootfile"));
954 } else if (strcasecmp(setting, "kernel") == 0) {
955 (void) printf("%s\n", getenv("bootfile"));
956 goto done;
957 }
958 } else {
959 ptr = getenv("kernelname");
960 if (ptr != NULL) {
961 if (*setting == '\0') {
962 (void) printf("Kernel: %s\n", ptr);
963 } else if (strcasecmp(setting, "kernel") == 0) {
964 (void) printf("%s\n", ptr);
965 goto done;
966 }
967 }
968 }
969
970 ptr = getenv("boot-args");
971 if (ptr != NULL) {
972 if (*setting == '\0') {
973 (void) printf("Boot-args: \"%s\"\n", ptr);
974 } else if (strcasecmp(setting, "boot-args") == 0) {
975 (void) printf("%s\n", ptr);
976 goto done;
977 }
978 }
979
980 if (*setting == '\0' || strcasecmp(setting, "modules") == 0) {
981 (void) printf("\nModules:\n");
982 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut);
983 (void) snprintf(buf, MAX_INPUT, "show-module-options");
984 ret = ficlVmEvaluate(vm, buf);
985 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
986 bam_error(_("error interpreting boot config\n"));
987 ret = BAM_ERROR;
988 goto done;
989 }
990 ret = BAM_SUCCESS;
991 goto done;
992 }
993
994 /* if we got here with setting string, its unknown property */
995 if (*setting != '\0') {
996 bam_error(_("unknown property: %s\n"), setting);
997 ret = BAM_ERROR;
998 } else
999 ret = BAM_SUCCESS;
1000 done:
1001 bf_fini();
1002 if (mounted != BE_ERR_MOUNTED) {
1003 (void) bam_umount_be(dir);
1004 }
1005
1006 if (dir != NULL) {
1007 (void) rmdir(dir);
1008 free(dir);
1009 }
1010
1011 return (ret);
1012 }
1013
1014 /*ARGSUSED*/
1015 static error_t
list_entry(struct menu_lst * menu,char * menu_root,char * opt)1016 list_entry(struct menu_lst *menu, char *menu_root, char *opt)
1017 {
1018 error_t ret = BAM_SUCCESS;
1019 menu_entry_t *entry;
1020 char *ptr, *title = NULL;
1021 int i, e = -1;
1022
1023 if (opt == NULL) {
1024 print_nodes(B_FALSE, menu);
1025 return (ret);
1026 }
1027
1028 if ((ptr = strchr(opt, '=')) == NULL) {
1029 bam_error(_("invalid option: %s\n"), opt);
1030 return (BAM_ERROR);
1031 }
1032
1033 i = ptr - opt;
1034 if (strncmp(opt, "entry", i) == 0) {
1035 e = atoi(ptr+1);
1036 } else if (strncmp(opt, "title", i) == 0) {
1037 title = ptr+1;
1038 } else {
1039 bam_error(_("invalid option: %s\n"), opt);
1040 return (BAM_ERROR);
1041 }
1042
1043 STAILQ_FOREACH(entry, menu, me_next) {
1044 if (title != NULL) {
1045 if (strcmp(title, entry->me_title) == 0)
1046 break;
1047 } else if (entry->me_idx == e)
1048 break;
1049 }
1050
1051 if (entry == NULL) {
1052 bam_error(_("no matching entry found\n"));
1053 return (BAM_ERROR);
1054 }
1055
1056 return (list_menu_entry(entry, ""));
1057 }
1058
1059 /*
1060 * For now this is just stub entry to support grub interface, the
1061 * known consumer is installer ict.py code, calling as:
1062 * bootadm update-menu -R /a -Z -o rdisk
1063 * Later this can be converted to do something useful.
1064 */
1065 /*ARGSUSED*/
1066 static error_t
update_entry(struct menu_lst * menu,char * menu_root,char * osdev)1067 update_entry(struct menu_lst *menu, char *menu_root, char *osdev)
1068 {
1069 char path[PATH_MAX];
1070 char *pool = menu_root + 1;
1071 be_node_list_t *be_nodes, *be_node;
1072 int rv;
1073 FILE *fp;
1074
1075 (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU);
1076 rv = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
1077
1078 if (rv != BE_SUCCESS)
1079 return (BAM_ERROR);
1080
1081 fp = fopen(path, "w");
1082 if (fp == NULL) {
1083 be_free_list(be_nodes);
1084 return (BAM_ERROR);
1085 }
1086
1087 for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) {
1088 if (strcmp(be_node->be_rpool, pool) == 0) {
1089 (void) fprintf(fp, "title %s\n", be_node->be_node_name);
1090 (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds);
1091 }
1092 }
1093
1094 be_free_list(be_nodes);
1095 (void) fclose(fp);
1096 return (BAM_SUCCESS);
1097 }
1098
1099 /*ARGSUSED*/
1100 static error_t
update_temp(struct menu_lst * menu,char * dummy,char * opt)1101 update_temp(struct menu_lst *menu, char *dummy, char *opt)
1102 {
1103 error_t ret = BAM_ERROR;
1104 char path[PATH_MAX];
1105 char buf[MAX_INPUT];
1106 struct mnttab mpref = { 0 };
1107 struct mnttab mp = { 0 };
1108 ficlVm *vm;
1109 char *env, *o;
1110 FILE *fp;
1111
1112 (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root);
1113 /*
1114 * if opt == NULL, remove transient config
1115 */
1116 if (opt == NULL) {
1117 (void) unlink(path);
1118 return (BAM_SUCCESS);
1119 }
1120
1121 fp = fopen(MNTTAB, "r");
1122 if (fp == NULL)
1123 return (BAM_ERROR);
1124
1125 mpref.mnt_mountp = "/";
1126 if (getmntany(fp, &mp, &mpref) != 0) {
1127 (void) fclose(fp);
1128 return (BAM_ERROR);
1129 }
1130 (void) fclose(fp);
1131
1132 vm = bf_init("", ficlTextOutSilent);
1133 if (vm == NULL) {
1134 bam_error(_("Error setting up forth interpreter\n"));
1135 return (ret);
1136 }
1137
1138 /*
1139 * need to check current boot config, so fire up the ficl
1140 * if its xen setup, we add option to boot-args list, not replacing it.
1141 */
1142 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1143 ret = ficlVmEvaluate(vm, buf);
1144 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1145 bam_error(_("Error interpreting boot config\n"));
1146 bf_fini();
1147 return (BAM_ERROR);
1148 }
1149 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1150 ret = ficlVmEvaluate(vm, buf);
1151 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1152 bam_error(_("Error interpreting boot config\n"));
1153 bf_fini();
1154 return (BAM_ERROR);
1155 }
1156 (void) snprintf(buf, MAX_INPUT, "start");
1157 ret = ficlVmEvaluate(vm, buf);
1158 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1159 bam_error(_("Error interpreting boot config\n"));
1160 bf_fini();
1161 return (BAM_ERROR);
1162 }
1163 (void) snprintf(buf, MAX_INPUT, "boot");
1164 ret = ficlVmEvaluate(vm, buf);
1165 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1166 bam_error(_("Error interpreting boot config\n"));
1167 bf_fini();
1168 return (BAM_ERROR);
1169 }
1170 bf_fini();
1171
1172 if (opt[0] == '-') {
1173 env = getenv("xen_kernel");
1174 fp = fopen(path, "w");
1175 if (fp == NULL)
1176 return (BAM_ERROR);
1177
1178 if (env != NULL) {
1179 env = getenv("boot-args");
1180 (void) fprintf(fp, "boot-args='%s %s'\n", env, opt);
1181 } else
1182 (void) fprintf(fp, "boot-args='%s'\n", opt);
1183 (void) fclose(fp);
1184 return (BAM_SUCCESS);
1185 }
1186
1187 /*
1188 * it should be the case with "kernel args"
1189 * so, we split the opt at first space
1190 * and store bootfile= and boot-args=
1191 */
1192 env = getenv("xen_kernel");
1193
1194 o = strchr(opt, ' ');
1195 if (o == NULL) {
1196 fp = fopen(path, "w");
1197 if (fp == NULL)
1198 return (BAM_ERROR);
1199 (void) fprintf(fp, "bootfile='%s;unix'\n", opt);
1200 (void) fclose(fp);
1201 return (BAM_SUCCESS);
1202 }
1203 *o++ = '\0';
1204 fp = fopen(path, "w");
1205 if (fp == NULL)
1206 return (BAM_ERROR);
1207 (void) fprintf(fp, "bootfile='%s;unix'\n", opt);
1208
1209 if (env != NULL) {
1210 env = getenv("boot-args");
1211 (void) fprintf(fp, "boot-args='%s %s'\n", env, o);
1212 } else
1213 (void) fprintf(fp, "boot-args='%s'\n", o);
1214
1215 (void) fflush(fp);
1216 (void) fclose(fp);
1217 return (ret);
1218 }
1219
1220 static error_t
list_setting(struct menu_lst * menu,char * which,char * setting)1221 list_setting(struct menu_lst *menu, char *which, char *setting)
1222 {
1223 int entry = -1;
1224 menu_entry_t *m;
1225 be_node_list_t *be_nodes, *be_node = NULL;
1226 int ret;
1227
1228 assert(which);
1229 assert(setting);
1230
1231 /*
1232 * which can be:
1233 * "" - list default entry
1234 * number - use for entry number
1235 * property name
1236 */
1237 if (*which != '\0') {
1238 if (isdigit(*which)) {
1239 char *rest;
1240 errno = 0;
1241 entry = strtol(which, &rest, 10);
1242 if (errno != 0 || *rest != '\0') {
1243 bam_error(_("invalid boot entry number: %s\n"),
1244 which);
1245 return (BAM_ERROR);
1246 }
1247 } else
1248 setting = which;
1249 }
1250
1251 /* find default entry */
1252 if (entry == -1) {
1253 ret = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
1254 if (ret != BE_SUCCESS) {
1255 bam_error(_("No BE's found\n"));
1256 return (BAM_ERROR);
1257 }
1258 STAILQ_FOREACH(m, menu, me_next) {
1259 entry++;
1260 for (be_node = be_nodes; be_node;
1261 be_node = be_node->be_next_node) {
1262 if (strcmp(be_node->be_root_ds,
1263 m->me_bootfs) == 0)
1264 break;
1265 }
1266 if (be_node != NULL &&
1267 be_node->be_active_on_boot == B_TRUE)
1268 break; /* found active node */
1269 }
1270 be_free_list(be_nodes);
1271 if (be_node == NULL) {
1272 bam_error(_("None of BE nodes is marked active\n"));
1273 return (BAM_ERROR);
1274 }
1275 } else {
1276 STAILQ_FOREACH(m, menu, me_next)
1277 if (m->me_idx == entry)
1278 break;
1279
1280 if (m == NULL) {
1281 bam_error(_("no matching entry found\n"));
1282 return (BAM_ERROR);
1283 }
1284 }
1285
1286 return (list_menu_entry(m, setting));
1287 }
1288
1289 /*ARGSUSED*/
1290 static error_t
disable_hyper(struct menu_lst * menu,char * osroot,char * opt)1291 disable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1292 {
1293 char path[PATH_MAX];
1294
1295 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1296 (void) unlink(path);
1297 return (BAM_SUCCESS);
1298 }
1299
1300 /*ARGSUSED*/
1301 static error_t
enable_hyper(struct menu_lst * menu,char * osroot,char * opt)1302 enable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1303 {
1304 ficlVm *vm;
1305 char path[PATH_MAX];
1306 char buf[MAX_INPUT];
1307 char *env;
1308 FILE *fp;
1309 struct mnttab mpref = { 0 };
1310 struct mnttab mp = { 0 };
1311 int ret;
1312
1313 fp = fopen(MNTTAB, "r");
1314 if (fp == NULL)
1315 return (BAM_ERROR);
1316
1317 mpref.mnt_mountp = "/";
1318 if (getmntany(fp, &mp, &mpref) != 0) {
1319 (void) fclose(fp);
1320 return (BAM_ERROR);
1321 }
1322 (void) fclose(fp);
1323
1324 vm = bf_init("", ficlTextOutSilent);
1325 if (vm == NULL) {
1326 bam_error(_("Error setting up forth interpreter\n"));
1327 return (BAM_ERROR);
1328 }
1329
1330 /*
1331 * need to check current boot config, so fire up the ficl
1332 * if its xen setup, we add option to boot-args list, not replacing it.
1333 */
1334 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1335 ret = ficlVmEvaluate(vm, buf);
1336 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1337 bam_error(_("Error interpreting boot config\n"));
1338 bf_fini();
1339 return (BAM_ERROR);
1340 }
1341 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1342 ret = ficlVmEvaluate(vm, buf);
1343 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1344 bam_error(_("Error interpreting boot config\n"));
1345 bf_fini();
1346 return (BAM_ERROR);
1347 }
1348 (void) snprintf(buf, MAX_INPUT, "start");
1349 ret = ficlVmEvaluate(vm, buf);
1350 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1351 bam_error(_("Error interpreting boot config\n"));
1352 bf_fini();
1353 return (BAM_ERROR);
1354 }
1355 (void) snprintf(buf, MAX_INPUT, "boot");
1356 ret = ficlVmEvaluate(vm, buf);
1357 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1358 bam_error(_("Error interpreting boot config\n"));
1359 bf_fini();
1360 return (BAM_ERROR);
1361 }
1362 bf_fini();
1363
1364 (void) mkdir(CONF_DIR, 0755);
1365 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1366 fp = fopen(path, "w");
1367 if (fp == NULL) {
1368 return (BAM_ERROR); /* error, cant write config */
1369 }
1370
1371 errno = 0;
1372 /*
1373 * on write error, remove file to ensure we have bootable config.
1374 * note we dont mind if config exists, it will get updated
1375 */
1376 (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1377 if (errno != 0)
1378 goto error;
1379
1380 /*
1381 * really simple and stupid console conversion.
1382 * it really has to be gone, it belongs to milestone/xvm properties.
1383 */
1384 env = getenv("console");
1385 if (env != NULL) {
1386 if (strcmp(env, "ttya") == 0)
1387 (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n",
1388 opt);
1389 else if (strcmp(env, "ttyb") == 0)
1390 (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n",
1391 opt);
1392 else
1393 (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n",
1394 opt);
1395 } else
1396 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt);
1397 if (errno != 0)
1398 goto error;
1399
1400 (void) fprintf(fp,
1401 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1402 if (errno != 0)
1403 goto error;
1404
1405 (void) fprintf(fp,
1406 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1407 if (errno != 0)
1408 goto error;
1409
1410 (void) fclose(fp);
1411 if (errno != 0) {
1412 (void) unlink(path);
1413 return (BAM_ERROR);
1414 }
1415 return (BAM_SUCCESS);
1416 error:
1417 (void) fclose(fp);
1418 (void) unlink(path);
1419 return (BAM_ERROR);
1420 }
1421