8987 bootadm: add bootfile fallback to unix
[unleashed.git] / usr / src / cmd / boot / bootadm / bootadm_loader.c
blob56b509b9ed118765040dbe1423891cc9684cdddf
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28 * Copyright 2016 Toomas Soome <tsoome@me.com>
32 * Loader menu management.
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <wchar.h>
39 #include <errno.h>
40 #include <limits.h>
41 #include <alloca.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/queue.h>
46 #include <libbe.h>
47 #include <ficl.h>
48 #include <ficlplatform/emu.h>
49 #include <ofmt.h>
51 #include "bootadm.h"
53 extern int bam_rootlen;
54 extern int bam_alt_root;
55 extern char *rootbuf;
56 extern char *bam_root;
58 #define BOOT_DIR "/boot"
59 #define CONF_DIR BOOT_DIR "/conf.d"
60 #define MENU BOOT_DIR "/menu.lst"
61 #define TRANSIENT BOOT_DIR "/transient.conf"
62 #define XEN_CONFIG CONF_DIR "/xen"
64 typedef struct menu_entry {
65 int me_idx;
66 boolean_t me_active;
67 char *me_title;
68 char *me_type;
69 char *me_bootfs;
70 STAILQ_ENTRY(menu_entry) me_next;
71 } menu_entry_t;
72 STAILQ_HEAD(menu_lst, menu_entry);
74 static error_t set_option(struct menu_lst *, char *, char *);
75 static error_t list_entry(struct menu_lst *, char *, char *);
76 static error_t update_entry(struct menu_lst *, char *, char *);
77 static error_t update_temp(struct menu_lst *, char *, char *);
78 static error_t list_setting(struct menu_lst *menu, char *, char *);
79 static error_t disable_hyper(struct menu_lst *, char *, char *);
80 static error_t enable_hyper(struct menu_lst *, char *, char *);
82 /* Menu related sub commands */
83 static subcmd_defn_t menu_subcmds[] = {
84 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
85 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
86 "update_entry", OPT_REQ, update_entry, 0, /* menu */
87 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
88 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
89 "disable_hypervisor", OPT_ABSENT, disable_hyper, 0, /* menu */
90 "enable_hypervisor", OPT_ABSENT, enable_hyper, 0, /* menu */
91 NULL, 0, NULL, 0 /* must be last */
94 #define NUM_COLS (5)
96 static boolean_t
97 print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
99 menu_entry_t *entry = ofarg->ofmt_cbarg;
101 switch (ofarg->ofmt_id) {
102 case 0:
103 (void) snprintf(buf, bufsize, "%d", entry->me_idx);
104 break;
105 case 1:
106 (void) snprintf(buf, bufsize, "%s", entry->me_title);
107 break;
108 case 2:
109 (void) snprintf(buf, bufsize, "%s", entry->me_bootfs);
110 break;
111 case 3:
112 (void) snprintf(buf, bufsize, "%s", entry->me_type);
113 break;
114 case 4:
115 if (entry->me_active == B_TRUE)
116 (void) snprintf(buf, bufsize, " *");
117 else
118 (void) snprintf(buf, bufsize, " -");
119 break;
120 default:
121 return (B_FALSE);
123 return (B_TRUE);
126 static void
127 init_hdr_cols(ofmt_field_t *hdr)
129 uint_t i;
131 for (i = 0; i < NUM_COLS; i++) {
132 char *name = NULL;
134 switch (i) {
135 case 0:
136 name = _("INDEX");
137 break;
138 case 1:
139 name = _("NAME");
140 break;
141 case 2:
142 name = _("DEVICE");
143 break;
144 case 3:
145 name = _("TYPE");
146 break;
147 case 4:
148 name = _("DEFAULT");
149 break;
152 hdr[i].of_name = name;
153 hdr[i].of_id = i;
154 hdr[i].of_cb = print_menu_cb;
156 if (name != NULL) {
157 wchar_t wname[128];
158 size_t sz = mbstowcs(wname, name, sizeof (wname) /
159 sizeof (wchar_t));
160 if (sz > 0) {
161 int wcsw = wcswidth(wname, sz);
162 if (wcsw > 0)
163 hdr[i].of_width = wcsw;
164 else
165 hdr[i].of_width = sz;
166 } else {
167 hdr[i].of_width = strlen(name);
173 static void
174 menu_update_widths(ofmt_field_t *hdr, struct menu_lst *menu)
176 size_t len[NUM_COLS];
177 menu_entry_t *entry;
178 int i;
180 for (i = 0; i < NUM_COLS; i++)
181 len[i] = hdr[i].of_width + 1;
183 STAILQ_FOREACH(entry, menu, me_next) {
184 size_t entry_len;
186 entry_len = strlen(entry->me_title) + 1;
187 if (entry_len > len[1])
188 len[1] = entry_len;
190 entry_len = strlen(entry->me_bootfs) + 1;
191 if (entry_len > len[2])
192 len[2] = entry_len;
194 entry_len = strlen(entry->me_type) + 1;
195 if (entry_len > len[3])
196 len[3] = entry_len;
199 for (i = 0; i < NUM_COLS; i++)
200 hdr[i].of_width = len[i];
203 static ofmt_field_t *
204 init_menu_template(struct menu_lst *menu)
206 ofmt_field_t *temp;
208 if ((temp = calloc(NUM_COLS + 1, sizeof (ofmt_field_t))) == NULL)
209 return (temp);
211 init_hdr_cols(temp);
212 menu_update_widths(temp, menu);
213 return (temp);
216 static void
217 print_nodes(boolean_t parsable, struct menu_lst *menu)
219 ofmt_status_t oferr;
220 ofmt_handle_t ofmt;
221 uint_t ofmtflags = 0;
222 ofmt_field_t *menu_template;
223 menu_entry_t *entry;
225 if (parsable == B_TRUE)
226 ofmtflags = OFMT_PARSABLE;
228 menu_template = init_menu_template(menu);
229 oferr = ofmt_open(NULL, menu_template, ofmtflags, 0, &ofmt);
231 if (oferr != OFMT_SUCCESS) {
232 char buf[OFMT_BUFSIZE];
234 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
235 (void) printf("bootadm: %s\n", buf);
236 free(menu_template);
237 return;
240 STAILQ_FOREACH(entry, menu, me_next)
241 ofmt_print(ofmt, entry);
243 ofmt_close(ofmt);
244 free(menu_template);
248 * Get the be_active_on_boot for bootfs.
250 static boolean_t
251 menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs)
253 be_node_list_t *be_node;
254 boolean_t rv = B_FALSE;
256 for (be_node = be_nodes; be_node != NULL;
257 be_node = be_node->be_next_node) {
258 if (strcmp(be_node->be_root_ds, bootfs) == 0) {
259 rv = be_node->be_active_on_boot;
260 break;
264 return (rv);
267 error_t
268 menu_read(struct menu_lst *menu, char *menu_path)
270 FILE *fp;
271 be_node_list_t *be_nodes;
272 menu_entry_t *mp;
273 char buf[PATH_MAX];
274 char *title;
275 char *bootfs;
276 char *type;
277 char *key, *value;
278 int i = 0;
279 int ret = BAM_SUCCESS;
281 fp = fopen(menu_path, "r");
282 if (fp == NULL)
283 return (BAM_ERROR);
285 if (be_list(NULL, &be_nodes) != BE_SUCCESS)
286 be_nodes = NULL;
289 * menu.lst entry is on two lines, one for title, one for bootfs
290 * so we process both lines in succession.
292 title = NULL;
293 type = NULL;
294 bootfs = NULL;
295 do {
296 if (fgets(buf, PATH_MAX, fp) == NULL) {
297 if (!feof(fp))
298 ret = BAM_ERROR;
299 goto done;
301 key = strtok(buf, " \n");
302 if (strcmp(key, "title") != 0) {
303 ret = BAM_ERROR;
304 goto done;
306 value = strtok(NULL, " \n");
307 if ((title = strdup(value)) == NULL) {
308 ret = BAM_ERROR;
309 goto done;
312 if (fgets(buf, PATH_MAX, fp) == NULL) {
313 ret = BAM_ERROR;
314 goto done;
317 key = strtok(buf, " \n");
318 if ((type = strdup(key)) == NULL) {
319 ret = BAM_ERROR;
320 goto done;
322 value = strtok(NULL, " \n");
323 if ((bootfs = strdup(value)) == NULL) {
324 ret = BAM_ERROR;
325 goto done;
327 if ((mp = malloc(sizeof (menu_entry_t))) == NULL) {
328 ret = BAM_ERROR;
329 goto done;
331 mp->me_idx = i++;
332 mp->me_title = title;
333 mp->me_type = type;
334 mp->me_bootfs = bootfs;
335 mp->me_active = menu_active_on_boot(be_nodes, bootfs);
336 STAILQ_INSERT_TAIL(menu, mp, me_next);
338 title = NULL;
339 type = NULL;
340 bootfs = NULL;
341 } while (feof(fp) == 0);
343 done:
344 free(title);
345 free(type);
346 free(bootfs);
347 (void) fclose(fp);
348 be_free_list(be_nodes);
349 return (ret);
352 void
353 menu_free(struct menu_lst *menu)
355 menu_entry_t *entry;
356 STAILQ_FOREACH(entry, menu, me_next) {
357 STAILQ_REMOVE_HEAD(menu, me_next);
358 free(entry->me_title);
359 free(entry->me_type);
360 free(entry->me_bootfs);
361 free(entry);
365 error_t
366 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[])
368 error_t ret;
369 char menu_path[PATH_MAX];
370 char clean_menu_root[PATH_MAX];
371 char menu_root[PATH_MAX];
372 struct stat sb;
373 error_t (*f)(struct menu_lst *, char *, char *);
374 char *special;
375 char *pool = NULL;
376 zfs_mnted_t zmnted;
377 char *zmntpt;
378 char *osdev;
379 char *osroot;
380 const char *fcn = "bam_loader_menu()";
381 struct menu_lst menu = {0};
383 STAILQ_INIT(&menu);
386 * Check arguments
388 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
389 if (ret == BAM_ERROR) {
390 return (BAM_ERROR);
393 assert(bam_root);
395 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
396 osdev = osroot = NULL;
398 if (strcmp(subcmd, "update_entry") == 0) {
399 assert(opt);
401 osdev = strtok(opt, ",");
402 assert(osdev);
403 osroot = strtok(NULL, ",");
404 if (osroot) {
405 /* fixup bam_root so that it points at osroot */
406 if (realpath(osroot, rootbuf) == NULL) {
407 bam_error(_("cannot resolve path %s: %s\n"),
408 osroot, strerror(errno));
409 return (BAM_ERROR);
411 bam_alt_root = 1;
412 bam_root = rootbuf;
413 bam_rootlen = strlen(rootbuf);
417 if (stat(menu_root, &sb) == -1) {
418 bam_error(_("cannot find menu\n"));
419 return (BAM_ERROR);
422 if (!is_zfs(menu_root)) {
423 bam_error(_("only ZFS root is supported\n"));
424 return (BAM_ERROR);
427 assert(strcmp(menu_root, bam_root) == 0);
428 special = get_special(menu_root);
429 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
430 if (special == NULL) {
431 bam_error(_("cant find special file for mount-point %s\n"),
432 menu_root);
433 return (BAM_ERROR);
435 pool = strtok(special, "/");
436 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
437 if (pool == NULL) {
438 free(special);
439 bam_error(_("cant find pool for mount-point %s\n"), menu_root);
440 return (BAM_ERROR);
442 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
444 zmntpt = mount_top_dataset(pool, &zmnted);
445 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
446 if (zmntpt == NULL) {
447 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool);
448 free(special);
449 return (BAM_ERROR);
451 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
453 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
454 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
456 elide_trailing_slash(menu_root, clean_menu_root,
457 sizeof (clean_menu_root));
459 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
461 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
462 (void) strlcat(menu_path, MENU, sizeof (menu_path));
464 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
467 * update_entry is special case, its used by installer
468 * and needs to create menu.lst file for loader
470 if (menu_read(&menu, menu_path) == BAM_ERROR &&
471 strcmp(subcmd, "update_entry") != 0) {
472 bam_error(_("cannot find menu file: %s\n"), menu_path);
473 if (special != NULL)
474 free(special);
475 return (BAM_ERROR);
479 * If listing the menu, display the menu location
481 if (strcmp(subcmd, "list_entry") == 0)
482 bam_print(_("the location for the active menu is: %s\n"),
483 menu_path);
486 * We already checked the following case in
487 * check_subcmd_and_suboptions() above. Complete the
488 * final step now.
490 if (strcmp(subcmd, "set_option") == 0) {
491 assert(largc == 1 && largv[0] && largv[1] == NULL);
492 opt = largv[0];
493 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
494 (strcmp(subcmd, "list_setting") != 0)) {
495 assert(largc == 0 && largv == NULL);
499 * Once the sub-cmd handler has run
500 * only the line field is guaranteed to have valid values
502 if (strcmp(subcmd, "update_entry") == 0) {
503 ret = f(&menu, menu_root, osdev);
504 } else if (strcmp(subcmd, "upgrade") == 0) {
505 ret = f(&menu, bam_root, menu_root);
506 } else if (strcmp(subcmd, "list_entry") == 0) {
507 ret = f(&menu, menu_path, opt);
508 } else if (strcmp(subcmd, "list_setting") == 0) {
509 ret = f(&menu, ((largc > 0) ? largv[0] : ""),
510 ((largc > 1) ? largv[1] : ""));
511 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
512 if (is_sparc()) {
513 bam_error(_("%s operation unsupported on SPARC "
514 "machines\n"), subcmd);
515 ret = BAM_ERROR;
516 } else {
517 ret = f(&menu, bam_root, NULL);
519 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
520 if (is_sparc()) {
521 bam_error(_("%s operation unsupported on SPARC "
522 "machines\n"), subcmd);
523 ret = BAM_ERROR;
524 } else {
525 char *extra_args = NULL;
528 * Compress all arguments passed in the largv[] array
529 * into one string that can then be appended to the
530 * end of the kernel$ string the routine to enable the
531 * hypervisor will build.
533 * This allows the caller to supply arbitrary unparsed
534 * arguments, such as dom0 memory settings or APIC
535 * options.
537 * This concatenation will be done without ANY syntax
538 * checking whatsoever, so it's the responsibility of
539 * the caller to make sure the arguments are valid and
540 * do not duplicate arguments the conversion routines
541 * may create.
543 if (largc > 0) {
544 int extra_len, i;
546 for (extra_len = 0, i = 0; i < largc; i++)
547 extra_len += strlen(largv[i]);
550 * Allocate space for argument strings,
551 * intervening spaces and terminating NULL.
553 extra_args = alloca(extra_len + largc);
555 (void) strcpy(extra_args, largv[0]);
557 for (i = 1; i < largc; i++) {
558 (void) strcat(extra_args, " ");
559 (void) strcat(extra_args, largv[i]);
563 ret = f(&menu, bam_root, extra_args);
565 } else
566 ret = f(&menu, NULL, opt);
568 if (ret == BAM_WRITE) {
569 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
570 fcn, clean_menu_root));
571 /* ret = menu_write(clean_menu_root, menu); */
574 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
575 assert((is_zfs(menu_root)) ^ (pool == NULL));
576 if (pool) {
577 (void) umount_top_dataset(pool, zmnted, zmntpt);
578 free(special);
581 menu_free(&menu);
582 return (ret);
586 * To suppress output from ficl. We do not want to see messages
587 * from interpreting loader config.
590 /*ARGSUSED*/
591 static void
592 ficlTextOutSilent(ficlCallback *cb, char *text)
596 /*ARGSUSED*/
597 static error_t
598 set_option(struct menu_lst *menu, char *dummy, char *opt)
600 char path[PATH_MAX];
601 char *val;
602 char *rest;
603 int optval;
604 menu_entry_t *entry;
605 nvlist_t *be_attrs;
606 FILE *fp;
607 int rv, ret = BAM_SUCCESS;
609 assert(menu);
610 assert(opt);
611 assert(dummy == NULL);
613 val = strchr(opt, '=');
614 if (val != NULL) {
615 *val++ = '\0';
618 if (strcmp(opt, "default") == 0) {
619 errno = 0;
620 optval = strtol(val, &rest, 10);
621 if (errno != 0 || *rest != '\0') {
622 bam_error(_("invalid boot entry number: %s\n"), val);
623 return (BAM_ERROR);
625 STAILQ_FOREACH(entry, menu, me_next) {
626 if (entry->me_idx == optval)
627 break;
629 if (entry == NULL) {
630 bam_error(_("invalid boot entry number: %s\n"), val);
631 return (BAM_ERROR);
633 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
634 bam_error(_("out of memory\n"));
635 return (BAM_ERROR);
637 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
638 entry->me_title) != 0) {
639 bam_error(_("out of memory\n"));
640 nvlist_free(be_attrs);
641 return (BAM_ERROR);
643 ret = be_activate(be_attrs);
644 nvlist_free(be_attrs);
645 if (ret != 0)
646 ret = BAM_ERROR;
647 return (ret);
648 } else if (strcmp(opt, "timeout") == 0) {
649 errno = 0;
650 optval = strtol(val, &rest, 10);
651 if (errno != 0 || *rest != '\0') {
652 bam_error(_("invalid timeout: %s\n"), val);
653 return (BAM_ERROR);
656 (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout",
657 bam_root);
659 fp = fopen(path, "w");
660 if (fp == NULL) {
661 bam_error(_("failed to open file: %s: %s\n"),
662 path, strerror(errno));
663 return (BAM_ERROR);
666 * timeout=-1 is to disable auto boot in illumos, but
667 * loader needs "NO" to disable auto boot.
669 if (optval == -1)
670 rv = fprintf(fp, "autoboot_delay=\"NO\"\n");
671 else
672 rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval);
674 if (rv < 0) {
675 bam_error(_("write to file failed: %s: %s\n"),
676 path, strerror(errno));
677 (void) fclose(fp);
678 ret = BAM_ERROR;
679 } else
680 rv = fclose(fp);
682 if (rv < 0) {
683 bam_error(_("failed to close file: %s: %s\n"),
684 path, strerror(errno));
685 ret = BAM_ERROR;
687 if (ret == BAM_ERROR)
688 (void) unlink(path);
690 return (BAM_SUCCESS);
693 bam_error(_("invalid option: %s\n"), opt);
694 return (BAM_ERROR);
697 static int
698 bam_mount_be(menu_entry_t *entry, char **dir)
700 nvlist_t *be_attrs = NULL;
701 const char *tmpdir = getenv("TMPDIR");
702 const char *tmpname = "bam.XXXXXX";
703 be_node_list_t *be_node, *be_nodes = NULL;
704 int ret;
706 *dir = NULL;
707 if (tmpdir == NULL)
708 tmpdir = "/tmp";
710 ret = asprintf(dir, "%s/%s", tmpdir, tmpname);
711 if (ret < 0) {
712 return (BE_ERR_NOMEM);
714 *dir = mkdtemp(*dir);
716 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
717 ret = BE_ERR_NOMEM;
718 goto out;
721 ret = be_list(NULL, &be_nodes);
722 if (ret != BE_SUCCESS) {
723 goto out;
726 for (be_node = be_nodes; be_node;
727 be_node = be_node->be_next_node)
728 if (strcmp(be_node->be_root_ds, entry->me_bootfs) == 0)
729 break;
731 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
732 be_node->be_node_name) != 0) {
733 ret = BE_ERR_NOMEM;
734 goto out;
737 if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) {
738 ret = BE_ERR_NOMEM;
739 goto out;
742 ret = be_mount(be_attrs);
743 if (ret == BE_ERR_MOUNTED) {
745 * if BE is mounted, dir does not point to correct directory
747 (void) rmdir(*dir);
748 free(*dir);
749 *dir = NULL;
751 out:
752 if (be_nodes != NULL)
753 be_free_list(be_nodes);
754 nvlist_free(be_attrs);
755 return (ret);
758 static int
759 bam_umount_be(char *dir)
761 nvlist_t *be_attrs;
762 int ret;
764 if (dir == NULL) /* nothing to do */
765 return (BE_SUCCESS);
767 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0)
768 return (BE_ERR_NOMEM);
770 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) {
771 ret = BE_ERR_NOMEM;
772 goto out;
775 ret = be_unmount(be_attrs);
776 out:
777 nvlist_free(be_attrs);
778 return (ret);
782 * display details of menu entry or single property
784 static error_t
785 list_menu_entry(menu_entry_t *entry, char *setting)
787 int ret = BAM_SUCCESS;
788 char *ptr, *dir;
789 char buf[MAX_INPUT];
790 ficlVm *vm;
791 int mounted;
793 if (strcmp(entry->me_type, "bootfs") != 0 ||
794 strchr(entry->me_bootfs, ':') != NULL) {
795 (void) printf("\nTitle: %s\n", entry->me_title);
796 (void) printf("Type: %s\n", entry->me_type);
797 (void) printf("Device: %s\n", entry->me_bootfs);
798 return (ret);
801 mounted = bam_mount_be(entry, &dir);
802 if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) {
803 if (dir != NULL) {
804 (void) rmdir(dir);
805 free(dir);
807 bam_error(_("%s is not mounted\n"), entry->me_title);
808 return (BAM_ERROR);
811 vm = bf_init("", ficlTextOutSilent);
812 if (vm == NULL) {
813 bam_error(_("error setting up forth interpreter\n"));
814 ret = BAM_ERROR;
815 goto done;
818 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
819 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:",
820 entry->me_bootfs);
821 ret = ficlVmEvaluate(vm, buf);
822 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
823 bam_error(_("error interpreting boot config\n"));
824 ret = BAM_ERROR;
825 goto done;
827 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
828 ret = ficlVmEvaluate(vm, buf);
829 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
830 bam_error(_("error interpreting boot config\n"));
831 ret = BAM_ERROR;
832 goto done;
834 (void) snprintf(buf, MAX_INPUT, "start");
835 ret = ficlVmEvaluate(vm, buf);
836 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
837 bam_error(_("error interpreting boot config\n"));
838 ret = BAM_ERROR;
839 goto done;
841 (void) snprintf(buf, MAX_INPUT, "boot");
842 ret = ficlVmEvaluate(vm, buf);
843 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
844 bam_error(_("error interpreting boot config\n"));
845 ret = BAM_ERROR;
846 goto done;
849 ret = BAM_SUCCESS;
850 if (*setting == '\0')
851 (void) printf("\nTitle: %s\n", entry->me_title);
852 else if (strcasecmp(setting, "title") == 0) {
853 (void) printf("%s\n", entry->me_title);
854 goto done;
857 ptr = getenv("autoboot_delay");
858 if (ptr != NULL) {
859 char *timeout = "-1";
861 if (strcasecmp(ptr, "NO") != 0)
862 timeout = ptr;
864 if (*setting == '\0')
865 (void) printf("Timeout: %s\n", timeout);
866 else if (strcasecmp(setting, "timeout") == 0) {
867 (void) printf("%s\n", timeout);
868 goto done;
872 ptr = getenv("console");
873 if (ptr != NULL) {
874 if (*setting == '\0')
875 (void) printf("Console: %s\n", ptr);
876 else if (strcasecmp(setting, "console") == 0) {
877 (void) printf("%s\n", ptr);
878 goto done;
882 if (*setting == '\0')
883 (void) printf("Bootfs: %s\n", entry->me_bootfs);
884 else if (strcasecmp(setting, "bootfs") == 0) {
885 (void) printf("%s\n", entry->me_bootfs);
886 goto done;
889 ptr = getenv("xen_kernel");
890 if (ptr != NULL) {
891 if (*setting == '\0') {
892 (void) printf("Xen kernel: %s\n", ptr);
893 } else if (strcasecmp(setting, "xen_kernel") == 0) {
894 (void) printf("%s\n", ptr);
895 goto done;
898 if (*setting == '\0') {
899 (void) printf("Xen args: \"%s\"\n",
900 getenv("xen_cmdline"));
901 } else if (strcasecmp(setting, "xen_cmdline") == 0) {
902 (void) printf("%s\n", getenv("xen_cmdline"));
903 goto done;
906 if (*setting == '\0') {
907 (void) printf("Kernel: %s\n",
908 getenv("bootfile"));
909 } if (strcasecmp(setting, "kernel") == 0) {
910 (void) printf("%s\n", getenv("bootfile"));
911 goto done;
913 } else {
914 ptr = getenv("kernelname");
915 if (ptr != NULL) {
916 if (*setting == '\0') {
917 (void) printf("Kernel: %s\n", ptr);
918 } else if (strcasecmp(setting, "kernel") == 0) {
919 (void) printf("%s\n", ptr);
920 goto done;
925 ptr = getenv("boot-args");
926 if (ptr != NULL) {
927 if (*setting == '\0') {
928 (void) printf("Boot-args: \"%s\"\n", ptr);
929 } else if (strcasecmp(setting, "boot-args") == 0) {
930 (void) printf("%s\n", ptr);
931 goto done;
935 if (*setting == '\0' || strcasecmp(setting, "modules") == 0) {
936 (void) printf("\nModules:\n");
937 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut);
938 (void) snprintf(buf, MAX_INPUT, "show-module-options");
939 ret = ficlVmEvaluate(vm, buf);
940 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
941 bam_error(_("error interpreting boot config\n"));
942 ret = BAM_ERROR;
943 goto done;
945 ret = BAM_SUCCESS;
946 goto done;
949 /* if we got here with setting string, its unknown property */
950 if (*setting != '\0') {
951 bam_error(_("unknown property: %s\n"), setting);
952 ret = BAM_ERROR;
953 } else
954 ret = BAM_SUCCESS;
955 done:
956 bf_fini();
957 if (mounted != BE_ERR_MOUNTED) {
958 (void) bam_umount_be(dir);
961 if (dir != NULL) {
962 (void) rmdir(dir);
963 free(dir);
966 return (ret);
969 /*ARGSUSED*/
970 static error_t
971 list_entry(struct menu_lst *menu, char *menu_root, char *opt)
973 error_t ret = BAM_SUCCESS;
974 menu_entry_t *entry;
975 char *ptr, *title = NULL;
976 int i, e = -1;
978 if (opt == NULL) {
979 print_nodes(B_FALSE, menu);
980 return (ret);
983 if ((ptr = strchr(opt, '=')) == NULL) {
984 bam_error(_("invalid option: %s\n"), opt);
985 return (BAM_ERROR);
988 i = ptr - opt;
989 if (strncmp(opt, "entry", i) == 0) {
990 e = atoi(ptr+1);
991 } else if (strncmp(opt, "title", i) == 0) {
992 title = ptr+1;
993 } else {
994 bam_error(_("invalid option: %s\n"), opt);
995 return (BAM_ERROR);
998 STAILQ_FOREACH(entry, menu, me_next) {
999 if (title != NULL) {
1000 if (strcmp(title, entry->me_title) == 0)
1001 break;
1002 } else if (entry->me_idx == e)
1003 break;
1006 if (entry == NULL) {
1007 bam_error(_("no matching entry found\n"));
1008 return (BAM_ERROR);
1011 return (list_menu_entry(entry, ""));
1015 * For now this is just stub entry to support grub interface, the
1016 * known consumer is installer ict.py code, calling as:
1017 * bootadm update-menu -R /a -Z -o rdisk
1018 * Later this can be converted to do something useful.
1020 /*ARGSUSED*/
1021 static error_t
1022 update_entry(struct menu_lst *menu, char *menu_root, char *osdev)
1024 char path[PATH_MAX];
1025 char *pool = menu_root + 1;
1026 be_node_list_t *be_nodes, *be_node;
1027 int rv;
1028 FILE *fp;
1030 (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU);
1031 rv = be_list(NULL, &be_nodes);
1033 if (rv != BE_SUCCESS)
1034 return (BAM_ERROR);
1036 fp = fopen(path, "w");
1037 if (fp == NULL) {
1038 be_free_list(be_nodes);
1039 return (BAM_ERROR);
1042 for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) {
1043 if (strcmp(be_node->be_rpool, pool) == 0) {
1044 (void) fprintf(fp, "title %s\n", be_node->be_node_name);
1045 (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds);
1049 be_free_list(be_nodes);
1050 (void) fclose(fp);
1051 return (BAM_SUCCESS);
1054 /*ARGSUSED*/
1055 static error_t
1056 update_temp(struct menu_lst *menu, char *dummy, char *opt)
1058 error_t ret = BAM_ERROR;
1059 char path[PATH_MAX];
1060 char buf[MAX_INPUT];
1061 struct mnttab mpref = { 0 };
1062 struct mnttab mp = { 0 };
1063 ficlVm *vm;
1064 char *env, *o;
1065 FILE *fp;
1067 (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root);
1069 * if opt == NULL, remove transient config
1071 if (opt == NULL) {
1072 (void) unlink(path);
1073 return (BAM_SUCCESS);
1076 fp = fopen(MNTTAB, "r");
1077 if (fp == NULL)
1078 return (BAM_ERROR);
1080 mpref.mnt_mountp = "/";
1081 if (getmntany(fp, &mp, &mpref) != 0) {
1082 (void) fclose(fp);
1083 return (BAM_ERROR);
1085 (void) fclose(fp);
1087 vm = bf_init("", ficlTextOutSilent);
1088 if (vm == NULL) {
1089 bam_error(_("Error setting up forth interpreter\n"));
1090 return (ret);
1094 * need to check current boot config, so fire up the ficl
1095 * if its xen setup, we add option to boot-args list, not replacing it.
1097 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1098 ret = ficlVmEvaluate(vm, buf);
1099 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1100 bam_error(_("Error interpreting boot config\n"));
1101 bf_fini();
1102 return (BAM_ERROR);
1104 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1105 ret = ficlVmEvaluate(vm, buf);
1106 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1107 bam_error(_("Error interpreting boot config\n"));
1108 bf_fini();
1109 return (BAM_ERROR);
1111 (void) snprintf(buf, MAX_INPUT, "start");
1112 ret = ficlVmEvaluate(vm, buf);
1113 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1114 bam_error(_("Error interpreting boot config\n"));
1115 bf_fini();
1116 return (BAM_ERROR);
1118 (void) snprintf(buf, MAX_INPUT, "boot");
1119 ret = ficlVmEvaluate(vm, buf);
1120 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1121 bam_error(_("Error interpreting boot config\n"));
1122 bf_fini();
1123 return (BAM_ERROR);
1125 bf_fini();
1127 if (opt[0] == '-') {
1128 env = getenv("xen_kernel");
1129 fp = fopen(path, "w");
1130 if (fp == NULL)
1131 return (BAM_ERROR);
1133 if (env != NULL) {
1134 env = getenv("boot-args");
1135 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1136 } else
1137 (void) fprintf(fp, "boot-args=\"%s\"\n", opt);
1138 (void) fclose(fp);
1139 return (BAM_SUCCESS);
1143 * it should be the case with "kernel args"
1144 * so, we split the opt at first space
1145 * and store bootfile= and boot-args=
1147 env = getenv("xen_kernel");
1149 o = strchr(opt, ' ');
1150 if (o == NULL) {
1151 fp = fopen(path, "w");
1152 if (fp == NULL)
1153 return (BAM_ERROR);
1154 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt);
1155 (void) fclose(fp);
1156 return (BAM_SUCCESS);
1158 *o++ = '\0';
1159 fp = fopen(path, "w");
1160 if (fp == NULL)
1161 return (BAM_ERROR);
1162 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt);
1164 if (env != NULL) {
1165 env = getenv("boot-args");
1166 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1167 } else
1168 (void) fprintf(fp, "boot-args=\"%s\"\n", o);
1170 (void) fflush(fp);
1171 (void) fclose(fp);
1172 return (ret);
1175 static error_t
1176 list_setting(struct menu_lst *menu, char *which, char *setting)
1178 int entry = -1;
1179 menu_entry_t *m;
1180 be_node_list_t *be_nodes, *be_node = NULL;
1181 int ret;
1183 assert(which);
1184 assert(setting);
1187 * which can be:
1188 * "" - list default entry
1189 * number - use for entry number
1190 * property name
1192 if (*which != '\0') {
1193 if (isdigit(*which)) {
1194 char *rest;
1195 errno = 0;
1196 entry = strtol(which, &rest, 10);
1197 if (errno != 0 || *rest != '\0') {
1198 bam_error(_("invalid boot entry number: %s\n"),
1199 which);
1200 return (BAM_ERROR);
1202 } else
1203 setting = which;
1206 /* find default entry */
1207 if (entry == -1) {
1208 ret = be_list(NULL, &be_nodes);
1209 if (ret != BE_SUCCESS) {
1210 bam_error(_("No BE's found\n"));
1211 return (BAM_ERROR);
1213 STAILQ_FOREACH(m, menu, me_next) {
1214 entry++;
1215 for (be_node = be_nodes; be_node;
1216 be_node = be_node->be_next_node) {
1217 if (strcmp(be_node->be_root_ds,
1218 m->me_bootfs) == 0)
1219 break;
1221 if (be_node != NULL &&
1222 be_node->be_active_on_boot == B_TRUE)
1223 break; /* found active node */
1225 be_free_list(be_nodes);
1226 if (be_node == NULL) {
1227 bam_error(_("None of BE nodes is marked active\n"));
1228 return (BAM_ERROR);
1230 } else {
1231 STAILQ_FOREACH(m, menu, me_next)
1232 if (m->me_idx == entry)
1233 break;
1235 if (m == NULL) {
1236 bam_error(_("no matching entry found\n"));
1237 return (BAM_ERROR);
1241 return (list_menu_entry(m, setting));
1244 /*ARGSUSED*/
1245 static error_t
1246 disable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1248 char path[PATH_MAX];
1250 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1251 (void) unlink(path);
1252 return (BAM_SUCCESS);
1255 /*ARGSUSED*/
1256 static error_t
1257 enable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1259 ficlVm *vm;
1260 char path[PATH_MAX];
1261 char buf[MAX_INPUT];
1262 char *env;
1263 FILE *fp;
1264 struct mnttab mpref = { 0 };
1265 struct mnttab mp = { 0 };
1266 int ret;
1268 fp = fopen(MNTTAB, "r");
1269 if (fp == NULL)
1270 return (BAM_ERROR);
1272 mpref.mnt_mountp = "/";
1273 if (getmntany(fp, &mp, &mpref) != 0) {
1274 (void) fclose(fp);
1275 return (BAM_ERROR);
1277 (void) fclose(fp);
1279 vm = bf_init("", ficlTextOutSilent);
1280 if (vm == NULL) {
1281 bam_error(_("Error setting up forth interpreter\n"));
1282 return (BAM_ERROR);
1286 * need to check current boot config, so fire up the ficl
1287 * if its xen setup, we add option to boot-args list, not replacing it.
1289 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1290 ret = ficlVmEvaluate(vm, buf);
1291 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1292 bam_error(_("Error interpreting boot config\n"));
1293 bf_fini();
1294 return (BAM_ERROR);
1296 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1297 ret = ficlVmEvaluate(vm, buf);
1298 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1299 bam_error(_("Error interpreting boot config\n"));
1300 bf_fini();
1301 return (BAM_ERROR);
1303 (void) snprintf(buf, MAX_INPUT, "start");
1304 ret = ficlVmEvaluate(vm, buf);
1305 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1306 bam_error(_("Error interpreting boot config\n"));
1307 bf_fini();
1308 return (BAM_ERROR);
1310 (void) snprintf(buf, MAX_INPUT, "boot");
1311 ret = ficlVmEvaluate(vm, buf);
1312 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1313 bam_error(_("Error interpreting boot config\n"));
1314 bf_fini();
1315 return (BAM_ERROR);
1317 bf_fini();
1319 (void) mkdir(CONF_DIR, 0755);
1320 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1321 fp = fopen(path, "w");
1322 if (fp == NULL) {
1323 return (BAM_ERROR); /* error, cant write config */
1326 errno = 0;
1328 * on write error, remove file to ensure we have bootable config.
1329 * note we dont mind if config exists, it will get updated
1331 (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1332 if (errno != 0)
1333 goto error;
1336 * really simple and stupid console conversion.
1337 * it really has to be gone, it belongs to milestone/xvm properties.
1339 env = getenv("console");
1340 if (env != NULL) {
1341 if (strcmp(env, "ttya") == 0)
1342 (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n",
1343 opt);
1344 else if (strcmp(env, "ttyb") == 0)
1345 (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n",
1346 opt);
1347 else
1348 (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n",
1349 opt);
1350 } else
1351 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt);
1352 if (errno != 0)
1353 goto error;
1355 (void) fprintf(fp,
1356 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1357 if (errno != 0)
1358 goto error;
1360 (void) fprintf(fp,
1361 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1362 if (errno != 0)
1363 goto error;
1365 (void) fclose(fp);
1366 if (errno != 0) {
1367 (void) unlink(path);
1368 return (BAM_ERROR);
1370 return (BAM_SUCCESS);
1371 error:
1372 (void) fclose(fp);
1373 (void) unlink(path);
1374 return (BAM_ERROR);