Unleashed v1.4
[unleashed.git] / usr / src / cmd / boot / bootadm / bootadm_loader.c
blobd2a023044d8e8a0db94451f5b524d103bb13b54b
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>
29 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
33 * Loader menu management.
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <wchar.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <alloca.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/queue.h>
47 #include <libbe.h>
48 #include <ficl.h>
49 #include <ficlplatform/emu.h>
50 #include <ofmt.h>
52 #include "bootadm.h"
54 extern int bam_rootlen;
55 extern int bam_alt_root;
56 extern char *rootbuf;
57 extern char *bam_root;
59 #define BOOT_DIR "/boot"
60 #define CONF_DIR BOOT_DIR "/conf.d"
61 #define MENU BOOT_DIR "/menu.lst"
62 #define TRANSIENT BOOT_DIR "/transient.conf"
63 #define XEN_CONFIG CONF_DIR "/xen"
65 typedef struct menu_entry {
66 int me_idx;
67 boolean_t me_active;
68 char *me_title;
69 char *me_type;
70 char *me_bootfs;
71 STAILQ_ENTRY(menu_entry) me_next;
72 } menu_entry_t;
73 STAILQ_HEAD(menu_lst, menu_entry);
75 static error_t set_option(struct menu_lst *, char *, char *);
76 static error_t list_entry(struct menu_lst *, char *, char *);
77 static error_t update_entry(struct menu_lst *, char *, char *);
78 static error_t update_temp(struct menu_lst *, char *, char *);
79 static error_t list_setting(struct menu_lst *menu, char *, char *);
80 static error_t disable_hyper(struct menu_lst *, char *, char *);
81 static error_t enable_hyper(struct menu_lst *, char *, char *);
83 /* Menu related sub commands */
84 static subcmd_defn_t menu_subcmds[] = {
85 { "set_option", OPT_ABSENT, set_option, 0 }, /* PUB */
86 { "list_entry", OPT_OPTIONAL, list_entry, 1 }, /* PUB */
87 { "update_entry", OPT_REQ, update_entry, 0 }, /* menu */
88 { "update_temp", OPT_OPTIONAL, update_temp, 0 }, /* reboot */
89 { "list_setting", OPT_OPTIONAL, list_setting, 1 }, /* menu */
90 { "disable_hypervisor", OPT_ABSENT, disable_hyper, 0 }, /* menu */
91 { "enable_hypervisor", OPT_ABSENT, enable_hyper, 0 }, /* menu */
92 { NULL, 0, NULL, 0 } /* must be last */
95 #define NUM_COLS (5)
97 static boolean_t
98 print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
100 menu_entry_t *entry = ofarg->ofmt_cbarg;
102 switch (ofarg->ofmt_id) {
103 case 0:
104 (void) snprintf(buf, bufsize, "%d", entry->me_idx);
105 break;
106 case 1:
107 (void) snprintf(buf, bufsize, "%s", entry->me_title);
108 break;
109 case 2:
110 (void) snprintf(buf, bufsize, "%s", entry->me_bootfs);
111 break;
112 case 3:
113 (void) snprintf(buf, bufsize, "%s", entry->me_type);
114 break;
115 case 4:
116 if (entry->me_active == B_TRUE)
117 (void) snprintf(buf, bufsize, " *");
118 else
119 (void) snprintf(buf, bufsize, " -");
120 break;
121 default:
122 return (B_FALSE);
124 return (B_TRUE);
127 static void
128 init_hdr_cols(ofmt_field_t *hdr)
130 uint_t i;
132 for (i = 0; i < NUM_COLS; i++) {
133 char *name = NULL;
135 switch (i) {
136 case 0:
137 name = _("INDEX");
138 break;
139 case 1:
140 name = _("NAME");
141 break;
142 case 2:
143 name = _("DEVICE");
144 break;
145 case 3:
146 name = _("TYPE");
147 break;
148 case 4:
149 name = _("DEFAULT");
150 break;
153 hdr[i].of_name = name;
154 hdr[i].of_id = i;
155 hdr[i].of_cb = print_menu_cb;
157 if (name != NULL) {
158 wchar_t wname[128];
159 size_t sz = mbstowcs(wname, name, sizeof (wname) /
160 sizeof (wchar_t));
161 if (sz > 0) {
162 int wcsw = wcswidth(wname, sz);
163 if (wcsw > 0)
164 hdr[i].of_width = wcsw;
165 else
166 hdr[i].of_width = sz;
167 } else {
168 hdr[i].of_width = strlen(name);
174 static void
175 menu_update_widths(ofmt_field_t *hdr, struct menu_lst *menu)
177 size_t len[NUM_COLS];
178 menu_entry_t *entry;
179 int i;
181 for (i = 0; i < NUM_COLS; i++)
182 len[i] = hdr[i].of_width + 1;
184 STAILQ_FOREACH(entry, menu, me_next) {
185 size_t entry_len;
187 entry_len = strlen(entry->me_title) + 1;
188 if (entry_len > len[1])
189 len[1] = entry_len;
191 entry_len = strlen(entry->me_bootfs) + 1;
192 if (entry_len > len[2])
193 len[2] = entry_len;
195 entry_len = strlen(entry->me_type) + 1;
196 if (entry_len > len[3])
197 len[3] = entry_len;
200 for (i = 0; i < NUM_COLS; i++)
201 hdr[i].of_width = len[i];
204 static ofmt_field_t *
205 init_menu_template(struct menu_lst *menu)
207 ofmt_field_t *temp;
209 if ((temp = calloc(NUM_COLS + 1, sizeof (ofmt_field_t))) == NULL)
210 return (temp);
212 init_hdr_cols(temp);
213 menu_update_widths(temp, menu);
214 return (temp);
217 static void
218 print_nodes(boolean_t parsable, struct menu_lst *menu)
220 ofmt_status_t oferr;
221 ofmt_handle_t ofmt;
222 uint_t ofmtflags = 0;
223 ofmt_field_t *menu_template;
224 menu_entry_t *entry;
226 if (parsable == B_TRUE)
227 ofmtflags = OFMT_PARSABLE;
229 menu_template = init_menu_template(menu);
230 oferr = ofmt_open(NULL, menu_template, ofmtflags, 0, &ofmt);
232 if (oferr != OFMT_SUCCESS) {
233 char buf[OFMT_BUFSIZE];
235 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
236 (void) printf("bootadm: %s\n", buf);
237 free(menu_template);
238 return;
241 STAILQ_FOREACH(entry, menu, me_next)
242 ofmt_print(ofmt, entry);
244 ofmt_close(ofmt);
245 free(menu_template);
249 * Get the be_active_on_boot for bootfs.
251 static boolean_t
252 menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs)
254 be_node_list_t *be_node;
255 boolean_t rv = B_FALSE;
257 for (be_node = be_nodes; be_node != NULL;
258 be_node = be_node->be_next_node) {
259 if (strcmp(be_node->be_root_ds, bootfs) == 0) {
260 rv = be_node->be_active_on_boot;
261 break;
265 return (rv);
268 error_t
269 menu_read(struct menu_lst *menu, char *menu_path)
271 FILE *fp;
272 be_node_list_t *be_nodes;
273 menu_entry_t *mp;
274 char buf[PATH_MAX];
275 char *title;
276 char *bootfs;
277 char *type;
278 char *key, *value;
279 int i = 0;
280 int ret = BAM_SUCCESS;
282 fp = fopen(menu_path, "r");
283 if (fp == NULL)
284 return (BAM_ERROR);
286 if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS)
287 be_nodes = NULL;
290 * menu.lst entry is on two lines, one for title, one for bootfs
291 * so we process both lines in succession.
293 title = NULL;
294 type = NULL;
295 bootfs = NULL;
296 do {
297 if (fgets(buf, PATH_MAX, fp) == NULL) {
298 if (!feof(fp))
299 ret = BAM_ERROR;
300 goto done;
302 key = strtok(buf, " \n");
303 if (strcmp(key, "title") != 0) {
304 ret = BAM_ERROR;
305 goto done;
307 value = strtok(NULL, " \n");
308 if ((title = strdup(value)) == NULL) {
309 ret = BAM_ERROR;
310 goto done;
313 if (fgets(buf, PATH_MAX, fp) == NULL) {
314 ret = BAM_ERROR;
315 goto done;
318 key = strtok(buf, " \n");
319 if ((type = strdup(key)) == NULL) {
320 ret = BAM_ERROR;
321 goto done;
323 value = strtok(NULL, " \n");
324 if ((bootfs = strdup(value)) == NULL) {
325 ret = BAM_ERROR;
326 goto done;
328 if ((mp = malloc(sizeof (menu_entry_t))) == NULL) {
329 ret = BAM_ERROR;
330 goto done;
332 mp->me_idx = i++;
333 mp->me_title = title;
334 mp->me_type = type;
335 mp->me_bootfs = bootfs;
336 mp->me_active = menu_active_on_boot(be_nodes, bootfs);
337 STAILQ_INSERT_TAIL(menu, mp, me_next);
339 title = NULL;
340 type = NULL;
341 bootfs = NULL;
342 } while (feof(fp) == 0);
344 done:
345 free(title);
346 free(type);
347 free(bootfs);
348 (void) fclose(fp);
349 be_free_list(be_nodes);
350 return (ret);
353 void
354 menu_free(struct menu_lst *menu)
356 menu_entry_t *entry;
358 while (!STAILQ_EMPTY(menu)) {
359 entry = STAILQ_FIRST(menu);
360 STAILQ_REMOVE_HEAD(menu, me_next);
361 free(entry->me_title);
362 free(entry->me_type);
363 free(entry->me_bootfs);
364 free(entry);
368 error_t
369 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[])
371 error_t ret;
372 char menu_path[PATH_MAX];
373 char clean_menu_root[PATH_MAX];
374 char menu_root[PATH_MAX];
375 struct stat sb;
376 error_t (*f)(struct menu_lst *, char *, char *);
377 char *special;
378 char *pool = NULL;
379 zfs_mnted_t zmnted;
380 char *zmntpt;
381 char *osdev;
382 char *osroot;
383 const char *fcn = "bam_loader_menu()";
384 struct menu_lst menu = {0};
386 STAILQ_INIT(&menu);
389 * Check arguments
391 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
392 if (ret == BAM_ERROR) {
393 return (BAM_ERROR);
396 assert(bam_root);
398 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
399 osdev = osroot = NULL;
401 if (strcmp(subcmd, "update_entry") == 0) {
402 assert(opt);
404 osdev = strtok(opt, ",");
405 assert(osdev);
406 osroot = strtok(NULL, ",");
407 if (osroot) {
408 /* fixup bam_root so that it points at osroot */
409 if (realpath(osroot, rootbuf) == NULL) {
410 bam_error(_("cannot resolve path %s: %s\n"),
411 osroot, strerror(errno));
412 return (BAM_ERROR);
414 bam_alt_root = 1;
415 bam_root = rootbuf;
416 bam_rootlen = strlen(rootbuf);
420 if (stat(menu_root, &sb) == -1) {
421 bam_error(_("cannot find menu\n"));
422 return (BAM_ERROR);
425 if (!is_zfs(menu_root)) {
426 bam_error(_("only ZFS root is supported\n"));
427 return (BAM_ERROR);
430 assert(strcmp(menu_root, bam_root) == 0);
431 special = get_special(menu_root);
432 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
433 if (special == NULL) {
434 bam_error(_("cant find special file for mount-point %s\n"),
435 menu_root);
436 return (BAM_ERROR);
438 pool = strtok(special, "/");
439 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
440 if (pool == NULL) {
441 free(special);
442 bam_error(_("cant find pool for mount-point %s\n"), menu_root);
443 return (BAM_ERROR);
445 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
447 zmntpt = mount_top_dataset(pool, &zmnted);
448 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
449 if (zmntpt == NULL) {
450 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool);
451 free(special);
452 return (BAM_ERROR);
454 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
456 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
457 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
459 elide_trailing_slash(menu_root, clean_menu_root,
460 sizeof (clean_menu_root));
462 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
464 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
465 (void) strlcat(menu_path, MENU, sizeof (menu_path));
467 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
470 * update_entry is special case, its used by installer
471 * and needs to create menu.lst file for loader
473 if (menu_read(&menu, menu_path) == BAM_ERROR &&
474 strcmp(subcmd, "update_entry") != 0) {
475 bam_error(_("cannot find menu file: %s\n"), menu_path);
476 if (special != NULL)
477 free(special);
478 return (BAM_ERROR);
482 * If listing the menu, display the menu location
484 if (strcmp(subcmd, "list_entry") == 0)
485 bam_print(_("the location for the active menu is: %s\n"),
486 menu_path);
489 * We already checked the following case in
490 * check_subcmd_and_suboptions() above. Complete the
491 * final step now.
493 if (strcmp(subcmd, "set_option") == 0) {
494 assert(largc == 1 && largv[0] && largv[1] == NULL);
495 opt = largv[0];
496 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
497 (strcmp(subcmd, "list_setting") != 0)) {
498 assert(largc == 0 && largv == NULL);
502 * Once the sub-cmd handler has run
503 * only the line field is guaranteed to have valid values
505 if (strcmp(subcmd, "update_entry") == 0) {
506 ret = f(&menu, menu_root, osdev);
507 } else if (strcmp(subcmd, "upgrade") == 0) {
508 ret = f(&menu, bam_root, menu_root);
509 } else if (strcmp(subcmd, "list_entry") == 0) {
510 ret = f(&menu, menu_path, opt);
511 } else if (strcmp(subcmd, "list_setting") == 0) {
512 ret = f(&menu, ((largc > 0) ? largv[0] : ""),
513 ((largc > 1) ? largv[1] : ""));
514 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
515 ret = f(&menu, bam_root, NULL);
516 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
517 char *extra_args = NULL;
520 * Compress all arguments passed in the largv[] array
521 * into one string that can then be appended to the
522 * end of the kernel$ string the routine to enable the
523 * hypervisor will build.
525 * This allows the caller to supply arbitrary unparsed
526 * arguments, such as dom0 memory settings or APIC
527 * options.
529 * This concatenation will be done without ANY syntax
530 * checking whatsoever, so it's the responsibility of
531 * the caller to make sure the arguments are valid and
532 * do not duplicate arguments the conversion routines
533 * may create.
535 if (largc > 0) {
536 int extra_len, i;
538 for (extra_len = 0, i = 0; i < largc; i++)
539 extra_len += strlen(largv[i]);
542 * Allocate space for argument strings,
543 * intervening spaces and terminating NULL.
545 extra_args = alloca(extra_len + largc);
547 (void) strcpy(extra_args, largv[0]);
549 for (i = 1; i < largc; i++) {
550 (void) strcat(extra_args, " ");
551 (void) strcat(extra_args, largv[i]);
555 ret = f(&menu, bam_root, extra_args);
556 } else
557 ret = f(&menu, NULL, opt);
559 if (ret == BAM_WRITE) {
560 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
561 fcn, clean_menu_root));
562 /* ret = menu_write(clean_menu_root, menu); */
565 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
566 assert((is_zfs(menu_root)) ^ (pool == NULL));
567 if (pool) {
568 (void) umount_top_dataset(pool, zmnted, zmntpt);
569 free(special);
572 menu_free(&menu);
573 return (ret);
577 * To suppress output from ficl. We do not want to see messages
578 * from interpreting loader config.
581 /*ARGSUSED*/
582 static void
583 ficlTextOutSilent(ficlCallback *cb, char *text)
587 /*ARGSUSED*/
588 static error_t
589 set_option(struct menu_lst *menu, char *dummy, char *opt)
591 char path[PATH_MAX];
592 char *val;
593 char *rest;
594 int optval;
595 menu_entry_t *entry;
596 nvlist_t *be_attrs;
597 FILE *fp;
598 int rv, ret = BAM_SUCCESS;
600 assert(menu);
601 assert(opt);
602 assert(dummy == NULL);
604 val = strchr(opt, '=');
605 if (val != NULL) {
606 *val++ = '\0';
609 if (strcmp(opt, "default") == 0) {
610 errno = 0;
611 optval = strtol(val, &rest, 10);
612 if (errno != 0 || *rest != '\0') {
613 bam_error(_("invalid boot entry number: %s\n"), val);
614 return (BAM_ERROR);
616 STAILQ_FOREACH(entry, menu, me_next) {
617 if (entry->me_idx == optval)
618 break;
620 if (entry == NULL) {
621 bam_error(_("invalid boot entry number: %s\n"), val);
622 return (BAM_ERROR);
624 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
625 bam_error(_("out of memory\n"));
626 return (BAM_ERROR);
628 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
629 entry->me_title) != 0) {
630 bam_error(_("out of memory\n"));
631 nvlist_free(be_attrs);
632 return (BAM_ERROR);
634 ret = be_activate(be_attrs);
635 nvlist_free(be_attrs);
636 if (ret != 0)
637 ret = BAM_ERROR;
638 return (ret);
639 } else if (strcmp(opt, "timeout") == 0) {
640 errno = 0;
641 optval = strtol(val, &rest, 10);
642 if (errno != 0 || *rest != '\0') {
643 bam_error(_("invalid timeout: %s\n"), val);
644 return (BAM_ERROR);
647 (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout",
648 bam_root);
650 fp = fopen(path, "w");
651 if (fp == NULL) {
652 bam_error(_("failed to open file: %s: %s\n"),
653 path, strerror(errno));
654 return (BAM_ERROR);
657 * timeout=-1 is to disable auto boot in illumos, but
658 * loader needs "NO" to disable auto boot.
660 if (optval == -1)
661 rv = fprintf(fp, "autoboot_delay=\"NO\"\n");
662 else
663 rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval);
665 if (rv < 0) {
666 bam_error(_("write to file failed: %s: %s\n"),
667 path, strerror(errno));
668 (void) fclose(fp);
669 ret = BAM_ERROR;
670 } else
671 rv = fclose(fp);
673 if (rv < 0) {
674 bam_error(_("failed to close file: %s: %s\n"),
675 path, strerror(errno));
676 ret = BAM_ERROR;
678 if (ret == BAM_ERROR)
679 (void) unlink(path);
681 return (BAM_SUCCESS);
684 bam_error(_("invalid option: %s\n"), opt);
685 return (BAM_ERROR);
688 static int
689 bam_mount_be(menu_entry_t *entry, char **dir)
691 nvlist_t *be_attrs = NULL;
692 const char *tmpdir = getenv("TMPDIR");
693 const char *tmpname = "bam.XXXXXX";
694 be_node_list_t *be_node, *be_nodes = NULL;
695 int ret;
697 *dir = NULL;
698 if (tmpdir == NULL)
699 tmpdir = "/tmp";
701 ret = asprintf(dir, "%s/%s", tmpdir, tmpname);
702 if (ret < 0) {
703 return (BE_ERR_NOMEM);
705 *dir = mkdtemp(*dir);
707 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
708 ret = BE_ERR_NOMEM;
709 goto out;
712 ret = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
713 if (ret != BE_SUCCESS) {
714 goto out;
717 for (be_node = be_nodes; be_node;
718 be_node = be_node->be_next_node)
719 if (strcmp(be_node->be_root_ds, entry->me_bootfs) == 0)
720 break;
722 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
723 be_node->be_node_name) != 0) {
724 ret = BE_ERR_NOMEM;
725 goto out;
728 if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) {
729 ret = BE_ERR_NOMEM;
730 goto out;
733 ret = be_mount(be_attrs);
734 if (ret == BE_ERR_MOUNTED) {
736 * if BE is mounted, dir does not point to correct directory
738 (void) rmdir(*dir);
739 free(*dir);
740 *dir = NULL;
742 out:
743 if (be_nodes != NULL)
744 be_free_list(be_nodes);
745 nvlist_free(be_attrs);
746 return (ret);
749 static int
750 bam_umount_be(char *dir)
752 nvlist_t *be_attrs;
753 int ret;
755 if (dir == NULL) /* nothing to do */
756 return (BE_SUCCESS);
758 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0)
759 return (BE_ERR_NOMEM);
761 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) {
762 ret = BE_ERR_NOMEM;
763 goto out;
766 ret = be_unmount(be_attrs);
767 out:
768 nvlist_free(be_attrs);
769 return (ret);
773 * display details of menu entry or single property
775 static error_t
776 list_menu_entry(menu_entry_t *entry, char *setting)
778 int ret = BAM_SUCCESS;
779 char *ptr, *dir;
780 char buf[MAX_INPUT];
781 ficlVm *vm;
782 int mounted;
784 if (strcmp(entry->me_type, "bootfs") != 0 ||
785 strchr(entry->me_bootfs, ':') != NULL) {
786 (void) printf("\nTitle: %s\n", entry->me_title);
787 (void) printf("Type: %s\n", entry->me_type);
788 (void) printf("Device: %s\n", entry->me_bootfs);
789 return (ret);
792 mounted = bam_mount_be(entry, &dir);
793 if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) {
794 if (dir != NULL) {
795 (void) rmdir(dir);
796 free(dir);
798 bam_error(_("%s is not mounted\n"), entry->me_title);
799 return (BAM_ERROR);
802 vm = bf_init("", ficlTextOutSilent);
803 if (vm == NULL) {
804 bam_error(_("error setting up forth interpreter\n"));
805 ret = BAM_ERROR;
806 goto done;
809 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
810 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:",
811 entry->me_bootfs);
812 ret = ficlVmEvaluate(vm, buf);
813 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
814 bam_error(_("error interpreting boot config\n"));
815 ret = BAM_ERROR;
816 goto done;
818 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
819 ret = ficlVmEvaluate(vm, buf);
820 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
821 bam_error(_("error interpreting boot config\n"));
822 ret = BAM_ERROR;
823 goto done;
825 (void) snprintf(buf, MAX_INPUT, "start");
826 ret = ficlVmEvaluate(vm, buf);
827 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
828 bam_error(_("error interpreting boot config\n"));
829 ret = BAM_ERROR;
830 goto done;
832 (void) snprintf(buf, MAX_INPUT, "boot");
833 ret = ficlVmEvaluate(vm, buf);
834 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
835 bam_error(_("error interpreting boot config\n"));
836 ret = BAM_ERROR;
837 goto done;
840 ret = BAM_SUCCESS;
841 if (*setting == '\0')
842 (void) printf("\nTitle: %s\n", entry->me_title);
843 else if (strcasecmp(setting, "title") == 0) {
844 (void) printf("%s\n", entry->me_title);
845 goto done;
848 ptr = getenv("autoboot_delay");
849 if (ptr != NULL) {
850 char *timeout = "-1";
852 if (strcasecmp(ptr, "NO") != 0)
853 timeout = ptr;
855 if (*setting == '\0')
856 (void) printf("Timeout: %s\n", timeout);
857 else if (strcasecmp(setting, "timeout") == 0) {
858 (void) printf("%s\n", timeout);
859 goto done;
863 ptr = getenv("console");
864 if (ptr != NULL) {
865 if (*setting == '\0')
866 (void) printf("Console: %s\n", ptr);
867 else if (strcasecmp(setting, "console") == 0) {
868 (void) printf("%s\n", ptr);
869 goto done;
873 if (*setting == '\0')
874 (void) printf("Bootfs: %s\n", entry->me_bootfs);
875 else if (strcasecmp(setting, "bootfs") == 0) {
876 (void) printf("%s\n", entry->me_bootfs);
877 goto done;
880 ptr = getenv("xen_kernel");
881 if (ptr != NULL) {
882 if (*setting == '\0') {
883 (void) printf("Xen kernel: %s\n", ptr);
884 } else if (strcasecmp(setting, "xen_kernel") == 0) {
885 (void) printf("%s\n", ptr);
886 goto done;
889 if (*setting == '\0') {
890 (void) printf("Xen args: \"%s\"\n",
891 getenv("xen_cmdline"));
892 } else if (strcasecmp(setting, "xen_cmdline") == 0) {
893 (void) printf("%s\n", getenv("xen_cmdline"));
894 goto done;
897 if (*setting == '\0') {
898 (void) printf("Kernel: %s\n",
899 getenv("bootfile"));
900 } else if (strcasecmp(setting, "kernel") == 0) {
901 (void) printf("%s\n", getenv("bootfile"));
902 goto done;
904 } else {
905 ptr = getenv("kernelname");
906 if (ptr != NULL) {
907 if (*setting == '\0') {
908 (void) printf("Kernel: %s\n", ptr);
909 } else if (strcasecmp(setting, "kernel") == 0) {
910 (void) printf("%s\n", ptr);
911 goto done;
916 ptr = getenv("boot-args");
917 if (ptr != NULL) {
918 if (*setting == '\0') {
919 (void) printf("Boot-args: \"%s\"\n", ptr);
920 } else if (strcasecmp(setting, "boot-args") == 0) {
921 (void) printf("%s\n", ptr);
922 goto done;
926 if (*setting == '\0' || strcasecmp(setting, "modules") == 0) {
927 (void) printf("\nModules:\n");
928 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut);
929 (void) snprintf(buf, MAX_INPUT, "show-module-options");
930 ret = ficlVmEvaluate(vm, buf);
931 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
932 bam_error(_("error interpreting boot config\n"));
933 ret = BAM_ERROR;
934 goto done;
936 ret = BAM_SUCCESS;
937 goto done;
940 /* if we got here with setting string, its unknown property */
941 if (*setting != '\0') {
942 bam_error(_("unknown property: %s\n"), setting);
943 ret = BAM_ERROR;
944 } else
945 ret = BAM_SUCCESS;
946 done:
947 bf_fini();
948 if (mounted != BE_ERR_MOUNTED) {
949 (void) bam_umount_be(dir);
952 if (dir != NULL) {
953 (void) rmdir(dir);
954 free(dir);
957 return (ret);
960 /*ARGSUSED*/
961 static error_t
962 list_entry(struct menu_lst *menu, char *menu_root, char *opt)
964 error_t ret = BAM_SUCCESS;
965 menu_entry_t *entry;
966 char *ptr, *title = NULL;
967 int i, e = -1;
969 if (opt == NULL) {
970 print_nodes(B_FALSE, menu);
971 return (ret);
974 if ((ptr = strchr(opt, '=')) == NULL) {
975 bam_error(_("invalid option: %s\n"), opt);
976 return (BAM_ERROR);
979 i = ptr - opt;
980 if (strncmp(opt, "entry", i) == 0) {
981 e = atoi(ptr+1);
982 } else if (strncmp(opt, "title", i) == 0) {
983 title = ptr+1;
984 } else {
985 bam_error(_("invalid option: %s\n"), opt);
986 return (BAM_ERROR);
989 STAILQ_FOREACH(entry, menu, me_next) {
990 if (title != NULL) {
991 if (strcmp(title, entry->me_title) == 0)
992 break;
993 } else if (entry->me_idx == e)
994 break;
997 if (entry == NULL) {
998 bam_error(_("no matching entry found\n"));
999 return (BAM_ERROR);
1002 return (list_menu_entry(entry, ""));
1006 * For now this is just stub entry to support grub interface, the
1007 * known consumer is installer ict.py code, calling as:
1008 * bootadm update-menu -R /a -Z -o rdisk
1009 * Later this can be converted to do something useful.
1011 /*ARGSUSED*/
1012 static error_t
1013 update_entry(struct menu_lst *menu, char *menu_root, char *osdev)
1015 char path[PATH_MAX];
1016 char *pool = menu_root + 1;
1017 be_node_list_t *be_nodes, *be_node;
1018 int rv;
1019 FILE *fp;
1021 (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU);
1022 rv = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
1024 if (rv != BE_SUCCESS)
1025 return (BAM_ERROR);
1027 fp = fopen(path, "w");
1028 if (fp == NULL) {
1029 be_free_list(be_nodes);
1030 return (BAM_ERROR);
1033 for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) {
1034 if (strcmp(be_node->be_rpool, pool) == 0) {
1035 (void) fprintf(fp, "title %s\n", be_node->be_node_name);
1036 (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds);
1040 be_free_list(be_nodes);
1041 (void) fclose(fp);
1042 return (BAM_SUCCESS);
1045 /*ARGSUSED*/
1046 static error_t
1047 update_temp(struct menu_lst *menu, char *dummy, char *opt)
1049 error_t ret = BAM_ERROR;
1050 char path[PATH_MAX];
1051 char buf[MAX_INPUT];
1052 struct mnttab mpref = { 0 };
1053 struct mnttab mp = { 0 };
1054 ficlVm *vm;
1055 char *env, *o;
1056 FILE *fp;
1058 (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root);
1060 * if opt == NULL, remove transient config
1062 if (opt == NULL) {
1063 (void) unlink(path);
1064 return (BAM_SUCCESS);
1067 fp = fopen(MNTTAB, "r");
1068 if (fp == NULL)
1069 return (BAM_ERROR);
1071 mpref.mnt_mountp = "/";
1072 if (getmntany(fp, &mp, &mpref) != 0) {
1073 (void) fclose(fp);
1074 return (BAM_ERROR);
1076 (void) fclose(fp);
1078 vm = bf_init("", ficlTextOutSilent);
1079 if (vm == NULL) {
1080 bam_error(_("Error setting up forth interpreter\n"));
1081 return (ret);
1085 * need to check current boot config, so fire up the ficl
1086 * if its xen setup, we add option to boot-args list, not replacing it.
1088 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1089 ret = ficlVmEvaluate(vm, buf);
1090 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1091 bam_error(_("Error interpreting boot config\n"));
1092 bf_fini();
1093 return (BAM_ERROR);
1095 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1096 ret = ficlVmEvaluate(vm, buf);
1097 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1098 bam_error(_("Error interpreting boot config\n"));
1099 bf_fini();
1100 return (BAM_ERROR);
1102 (void) snprintf(buf, MAX_INPUT, "start");
1103 ret = ficlVmEvaluate(vm, buf);
1104 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1105 bam_error(_("Error interpreting boot config\n"));
1106 bf_fini();
1107 return (BAM_ERROR);
1109 (void) snprintf(buf, MAX_INPUT, "boot");
1110 ret = ficlVmEvaluate(vm, buf);
1111 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1112 bam_error(_("Error interpreting boot config\n"));
1113 bf_fini();
1114 return (BAM_ERROR);
1116 bf_fini();
1118 if (opt[0] == '-') {
1119 env = getenv("xen_kernel");
1120 fp = fopen(path, "w");
1121 if (fp == NULL)
1122 return (BAM_ERROR);
1124 if (env != NULL) {
1125 env = getenv("boot-args");
1126 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1127 } else
1128 (void) fprintf(fp, "boot-args=\"%s\"\n", opt);
1129 (void) fclose(fp);
1130 return (BAM_SUCCESS);
1134 * it should be the case with "kernel args"
1135 * so, we split the opt at first space
1136 * and store bootfile= and boot-args=
1138 env = getenv("xen_kernel");
1140 o = strchr(opt, ' ');
1141 if (o == NULL) {
1142 fp = fopen(path, "w");
1143 if (fp == NULL)
1144 return (BAM_ERROR);
1145 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt);
1146 (void) fclose(fp);
1147 return (BAM_SUCCESS);
1149 *o++ = '\0';
1150 fp = fopen(path, "w");
1151 if (fp == NULL)
1152 return (BAM_ERROR);
1153 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt);
1155 if (env != NULL) {
1156 env = getenv("boot-args");
1157 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1158 } else
1159 (void) fprintf(fp, "boot-args=\"%s\"\n", o);
1161 (void) fflush(fp);
1162 (void) fclose(fp);
1163 return (ret);
1166 static error_t
1167 list_setting(struct menu_lst *menu, char *which, char *setting)
1169 int entry = -1;
1170 menu_entry_t *m;
1171 be_node_list_t *be_nodes, *be_node = NULL;
1172 int ret;
1174 assert(which);
1175 assert(setting);
1178 * which can be:
1179 * "" - list default entry
1180 * number - use for entry number
1181 * property name
1183 if (*which != '\0') {
1184 if (isdigit(*which)) {
1185 char *rest;
1186 errno = 0;
1187 entry = strtol(which, &rest, 10);
1188 if (errno != 0 || *rest != '\0') {
1189 bam_error(_("invalid boot entry number: %s\n"),
1190 which);
1191 return (BAM_ERROR);
1193 } else
1194 setting = which;
1197 /* find default entry */
1198 if (entry == -1) {
1199 ret = be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
1200 if (ret != BE_SUCCESS) {
1201 bam_error(_("No BE's found\n"));
1202 return (BAM_ERROR);
1204 STAILQ_FOREACH(m, menu, me_next) {
1205 entry++;
1206 for (be_node = be_nodes; be_node;
1207 be_node = be_node->be_next_node) {
1208 if (strcmp(be_node->be_root_ds,
1209 m->me_bootfs) == 0)
1210 break;
1212 if (be_node != NULL &&
1213 be_node->be_active_on_boot == B_TRUE)
1214 break; /* found active node */
1216 be_free_list(be_nodes);
1217 if (be_node == NULL) {
1218 bam_error(_("None of BE nodes is marked active\n"));
1219 return (BAM_ERROR);
1221 } else {
1222 STAILQ_FOREACH(m, menu, me_next)
1223 if (m->me_idx == entry)
1224 break;
1226 if (m == NULL) {
1227 bam_error(_("no matching entry found\n"));
1228 return (BAM_ERROR);
1232 return (list_menu_entry(m, setting));
1235 /*ARGSUSED*/
1236 static error_t
1237 disable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1239 char path[PATH_MAX];
1241 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1242 (void) unlink(path);
1243 return (BAM_SUCCESS);
1246 /*ARGSUSED*/
1247 static error_t
1248 enable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1250 ficlVm *vm;
1251 char path[PATH_MAX];
1252 char buf[MAX_INPUT];
1253 char *env;
1254 FILE *fp;
1255 struct mnttab mpref = { 0 };
1256 struct mnttab mp = { 0 };
1257 int ret;
1259 fp = fopen(MNTTAB, "r");
1260 if (fp == NULL)
1261 return (BAM_ERROR);
1263 mpref.mnt_mountp = "/";
1264 if (getmntany(fp, &mp, &mpref) != 0) {
1265 (void) fclose(fp);
1266 return (BAM_ERROR);
1268 (void) fclose(fp);
1270 vm = bf_init("", ficlTextOutSilent);
1271 if (vm == NULL) {
1272 bam_error(_("Error setting up forth interpreter\n"));
1273 return (BAM_ERROR);
1277 * need to check current boot config, so fire up the ficl
1278 * if its xen setup, we add option to boot-args list, not replacing it.
1280 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1281 ret = ficlVmEvaluate(vm, buf);
1282 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1283 bam_error(_("Error interpreting boot config\n"));
1284 bf_fini();
1285 return (BAM_ERROR);
1287 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1288 ret = ficlVmEvaluate(vm, buf);
1289 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1290 bam_error(_("Error interpreting boot config\n"));
1291 bf_fini();
1292 return (BAM_ERROR);
1294 (void) snprintf(buf, MAX_INPUT, "start");
1295 ret = ficlVmEvaluate(vm, buf);
1296 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1297 bam_error(_("Error interpreting boot config\n"));
1298 bf_fini();
1299 return (BAM_ERROR);
1301 (void) snprintf(buf, MAX_INPUT, "boot");
1302 ret = ficlVmEvaluate(vm, buf);
1303 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1304 bam_error(_("Error interpreting boot config\n"));
1305 bf_fini();
1306 return (BAM_ERROR);
1308 bf_fini();
1310 (void) mkdir(CONF_DIR, 0755);
1311 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1312 fp = fopen(path, "w");
1313 if (fp == NULL) {
1314 return (BAM_ERROR); /* error, cant write config */
1317 errno = 0;
1319 * on write error, remove file to ensure we have bootable config.
1320 * note we dont mind if config exists, it will get updated
1322 (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1323 if (errno != 0)
1324 goto error;
1327 * really simple and stupid console conversion.
1328 * it really has to be gone, it belongs to milestone/xvm properties.
1330 env = getenv("console");
1331 if (env != NULL) {
1332 if (strcmp(env, "ttya") == 0)
1333 (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n",
1334 opt);
1335 else if (strcmp(env, "ttyb") == 0)
1336 (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n",
1337 opt);
1338 else
1339 (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n",
1340 opt);
1341 } else
1342 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt);
1343 if (errno != 0)
1344 goto error;
1346 (void) fprintf(fp,
1347 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1348 if (errno != 0)
1349 goto error;
1351 (void) fprintf(fp,
1352 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1353 if (errno != 0)
1354 goto error;
1356 (void) fclose(fp);
1357 if (errno != 0) {
1358 (void) unlink(path);
1359 return (BAM_ERROR);
1361 return (BAM_SUCCESS);
1362 error:
1363 (void) fclose(fp);
1364 (void) unlink(path);
1365 return (BAM_ERROR);