Reamove grub support.
[unleashed.git] / usr / src / cmd / boot / bootadm / bootadm_loader.c
blob5d114ce4334b3f9a1d5384466908a2a6c3d975d7
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>
50 #include "bootadm.h"
52 extern int bam_rootlen;
53 extern int bam_alt_root;
54 extern char *rootbuf;
55 extern char *bam_root;
57 #define BOOT_DIR "/boot"
58 #define CONF_DIR BOOT_DIR "/conf.d"
59 #define MENU BOOT_DIR "/menu.lst"
60 #define TRANSIENT BOOT_DIR "/transient.conf"
61 #define XEN_CONFIG CONF_DIR "/xen"
63 struct menu_entry {
64 int entry;
65 char *title;
66 char *bootfs;
67 STAILQ_ENTRY(menu_entry) next;
69 STAILQ_HEAD(menu_lst, menu_entry);
71 static error_t set_option(struct menu_lst *, char *, char *);
72 static error_t list_entry(struct menu_lst *, char *, char *);
73 static error_t update_entry(struct menu_lst *, char *, char *);
74 static error_t update_temp(struct menu_lst *, char *, char *);
75 static error_t list_setting(struct menu_lst *menu, char *, char *);
76 static error_t disable_hyper(struct menu_lst *, char *, char *);
77 static error_t enable_hyper(struct menu_lst *, char *, char *);
79 /* Menu related sub commands */
80 static subcmd_defn_t menu_subcmds[] = {
81 { "set_option", OPT_ABSENT, set_option, 0 }, /* PUB */
82 { "list_entry", OPT_OPTIONAL, list_entry, 1 }, /* PUB */
83 { "update_entry", OPT_REQ, update_entry, 0 }, /* menu */
84 { "update_temp", OPT_OPTIONAL, update_temp, 0 }, /* reboot */
85 { "list_setting", OPT_OPTIONAL, list_setting, 1 }, /* menu */
86 { "disable_hypervisor", OPT_ABSENT, disable_hyper, 0 }, /* menu */
87 { "enable_hypervisor", OPT_ABSENT, enable_hyper, 0 }, /* menu */
88 { NULL, 0, NULL, 0 } /* must be last */
91 #define NUM_COLS (4)
92 struct col_info {
93 const char *col_name;
94 size_t width;
98 * all columns output format
100 struct hdr_info {
101 struct col_info cols[NUM_COLS];
104 static void
105 print_hdr(struct hdr_info *hdr_info)
107 boolean_t first = B_TRUE;
108 size_t i;
110 for (i = 0; i < NUM_COLS; i++) {
111 struct col_info *col_info = &hdr_info->cols[i];
112 const char *name = col_info->col_name;
113 size_t width = col_info->width;
115 if (name == NULL)
116 continue;
118 if (first) {
119 (void) printf("%-*s", width, name);
120 first = B_FALSE;
121 } else
122 (void) printf(" %-*s", width, name);
124 (void) putchar('\n');
127 static void
128 init_hdr_cols(struct hdr_info *hdr)
130 struct col_info *col = hdr->cols;
131 size_t i;
133 col[0].col_name = _("Index");
134 col[1].col_name = _("Default");
135 col[2].col_name = _("Dataset");
136 col[3].col_name = _("Menu");
137 col[4].col_name = NULL;
139 for (i = 0; i < NUM_COLS; i++) {
140 const char *name = col[i].col_name;
141 col[i].width = 0;
143 if (name != NULL) {
144 wchar_t wname[128];
145 size_t sz = mbstowcs(wname, name, sizeof (wname) /
146 sizeof (wchar_t));
147 if (sz > 0) {
148 int wcsw = wcswidth(wname, sz);
149 if (wcsw > 0)
150 col[i].width = wcsw;
151 else
152 col[i].width = sz;
153 } else {
154 col[i].width = strlen(name);
160 static void
161 count_widths(struct hdr_info *hdr, struct menu_lst *menu)
163 size_t len[NUM_COLS];
164 struct menu_entry *entry;
165 int i;
167 for (i = 0; i < NUM_COLS; i++)
168 len[i] = hdr->cols[i].width + 1;
170 STAILQ_FOREACH(entry, menu, next) {
171 size_t bootfs_len = strlen(entry->bootfs);
172 if (bootfs_len > len[2])
173 len[2] = bootfs_len + 1;
176 for (i = 0; i < NUM_COLS; i++)
177 hdr->cols[i].width = len[i];
180 static void
181 print_menu_nodes(boolean_t parsable, struct hdr_info *hdr,
182 struct menu_lst *menu)
184 struct menu_entry *entry;
185 int i = -1;
186 int rv;
187 be_node_list_t *be_nodes, *be_node;
189 rv = be_list(NULL, &be_nodes);
190 if (rv != BE_SUCCESS)
191 return;
193 STAILQ_FOREACH(entry, menu, next) {
194 i++;
195 for (be_node = be_nodes; be_node;
196 be_node = be_node->be_next_node)
197 if (strcmp(be_node->be_root_ds, entry->bootfs) == 0)
198 break;
200 if (parsable)
201 (void) printf("%d;%s;%s;%s\n", i,
202 be_node->be_active_on_boot == B_TRUE? "*" : "-",
203 entry->bootfs, entry->title);
204 else
205 (void) printf("%-*d %-*s %-*s %-*s\n",
206 hdr->cols[0].width, i,
207 hdr->cols[1].width,
208 be_node->be_active_on_boot == B_TRUE? "*" : "-",
209 hdr->cols[2].width, entry->bootfs,
210 hdr->cols[3].width, entry->title);
212 be_free_list(be_nodes);
215 static void
216 print_nodes(boolean_t parsable, struct menu_lst *menu)
218 struct hdr_info hdr;
220 if (!parsable) {
221 init_hdr_cols(&hdr);
222 count_widths(&hdr, menu);
223 print_hdr(&hdr);
226 print_menu_nodes(parsable, &hdr, menu);
229 error_t
230 menu_read(struct menu_lst *menu, char *menu_path)
232 FILE *fp;
233 struct menu_entry *mp;
234 char buf[PATH_MAX];
235 char *title;
236 char *bootfs;
237 char *ptr;
238 int i = 0;
239 int ret = BAM_SUCCESS;
241 fp = fopen(menu_path, "r");
242 if (fp == NULL)
243 return (BAM_ERROR);
246 * menu.lst entry is on two lines, one for title, one for bootfs
247 * so we process both lines in succession.
249 do {
250 if (fgets(buf, PATH_MAX, fp) == NULL) {
251 if (!feof(fp))
252 ret = BAM_ERROR;
253 (void) fclose(fp);
254 return (ret);
256 ptr = strchr(buf, '\n');
257 if (ptr != NULL)
258 *ptr = '\0';
260 ptr = strchr(buf, ' ');
261 if (ptr == NULL) {
262 (void) fclose(fp);
263 return (BAM_ERROR);
265 *ptr++ = '\0';
266 if (strcmp(buf, "title") != 0) {
267 (void) fclose(fp);
268 return (BAM_ERROR);
270 if ((title = strdup(ptr)) == NULL) {
271 (void) fclose(fp);
272 return (BAM_ERROR);
275 if (fgets(buf, PATH_MAX, fp) == NULL) {
276 free(title);
277 (void) fclose(fp);
278 return (BAM_ERROR);
281 ptr = strchr(buf, '\n');
282 if (ptr != NULL)
283 *ptr = '\0';
285 ptr = strchr(buf, ' ');
286 if (ptr == NULL) {
287 free(title);
288 (void) fclose(fp);
289 return (BAM_ERROR);
291 *ptr++ = '\0';
292 if (strcmp(buf, "bootfs") != 0) {
293 free(title);
294 (void) fclose(fp);
295 return (BAM_ERROR);
297 if ((bootfs = strdup(ptr)) == NULL) {
298 free(title);
299 (void) fclose(fp);
300 return (BAM_ERROR);
302 if ((mp = malloc(sizeof (struct menu_entry))) == NULL) {
303 free(title);
304 free(bootfs);
305 (void) fclose(fp);
306 return (BAM_ERROR);
308 mp->entry = i++;
309 mp->title = title;
310 mp->bootfs = bootfs;
311 STAILQ_INSERT_TAIL(menu, mp, next);
312 } while (feof(fp) == 0);
314 (void) fclose(fp);
315 return (ret);
318 void
319 menu_free(struct menu_lst *menu)
321 struct menu_entry *entry;
322 STAILQ_FOREACH(entry, menu, next) {
323 STAILQ_REMOVE_HEAD(menu, next);
324 free(entry->title);
325 free(entry->bootfs);
326 free(entry);
330 error_t
331 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[])
333 error_t ret;
334 char menu_path[PATH_MAX];
335 char clean_menu_root[PATH_MAX];
336 char menu_root[PATH_MAX];
337 struct stat sb;
338 error_t (*f)(struct menu_lst *, char *, char *);
339 char *special;
340 char *pool = NULL;
341 zfs_mnted_t zmnted;
342 char *zmntpt;
343 char *osdev;
344 char *osroot;
345 const char *fcn = "bam_loader_menu()";
346 struct menu_lst menu = {0};
348 STAILQ_INIT(&menu);
351 * Check arguments
353 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
354 if (ret == BAM_ERROR) {
355 return (BAM_ERROR);
358 assert(bam_root);
360 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
361 osdev = osroot = NULL;
363 if (strcmp(subcmd, "update_entry") == 0) {
364 assert(opt);
366 osdev = strtok(opt, ",");
367 assert(osdev);
368 osroot = strtok(NULL, ",");
369 if (osroot) {
370 /* fixup bam_root so that it points at osroot */
371 if (realpath(osroot, rootbuf) == NULL) {
372 bam_error(_("cannot resolve path %s: %s\n"),
373 osroot, strerror(errno));
374 return (BAM_ERROR);
376 bam_alt_root = 1;
377 bam_root = rootbuf;
378 bam_rootlen = strlen(rootbuf);
382 if (stat(menu_root, &sb) == -1) {
383 bam_error(_("cannot find menu\n"));
384 return (BAM_ERROR);
387 if (!is_zfs(menu_root)) {
388 bam_error(_("only ZFS root is supported\n"));
389 return (BAM_ERROR);
392 assert(strcmp(menu_root, bam_root) == 0);
393 special = get_special(menu_root);
394 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
395 if (special == NULL) {
396 bam_error(_("cant find special file for mount-point %s\n"),
397 menu_root);
398 return (BAM_ERROR);
400 pool = strtok(special, "/");
401 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
402 if (pool == NULL) {
403 free(special);
404 bam_error(_("cant find pool for mount-point %s\n"), menu_root);
405 return (BAM_ERROR);
407 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
409 zmntpt = mount_top_dataset(pool, &zmnted);
410 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
411 if (zmntpt == NULL) {
412 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool);
413 free(special);
414 return (BAM_ERROR);
416 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
418 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
419 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
421 elide_trailing_slash(menu_root, clean_menu_root,
422 sizeof (clean_menu_root));
424 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
426 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
427 (void) strlcat(menu_path, MENU, sizeof (menu_path));
429 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
432 * update_entry is special case, its used by installer
433 * and needs to create menu.lst file for loader
435 if (menu_read(&menu, menu_path) == BAM_ERROR &&
436 strcmp(subcmd, "update_entry") != 0) {
437 bam_error(_("cannot find menu file: %s\n"), menu_path);
438 if (special != NULL)
439 free(special);
440 return (BAM_ERROR);
444 * If listing the menu, display the menu location
446 if (strcmp(subcmd, "list_entry") == 0)
447 bam_print(_("the location for the active menu is: %s\n"),
448 menu_path);
451 * We already checked the following case in
452 * check_subcmd_and_suboptions() above. Complete the
453 * final step now.
455 if (strcmp(subcmd, "set_option") == 0) {
456 assert(largc == 1 && largv[0] && largv[1] == NULL);
457 opt = largv[0];
458 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
459 (strcmp(subcmd, "list_setting") != 0)) {
460 assert(largc == 0 && largv == NULL);
464 * Once the sub-cmd handler has run
465 * only the line field is guaranteed to have valid values
467 if (strcmp(subcmd, "update_entry") == 0) {
468 ret = f(&menu, menu_root, osdev);
469 } else if (strcmp(subcmd, "upgrade") == 0) {
470 ret = f(&menu, bam_root, menu_root);
471 } else if (strcmp(subcmd, "list_entry") == 0) {
472 ret = f(&menu, menu_path, opt);
473 } else if (strcmp(subcmd, "list_setting") == 0) {
474 ret = f(&menu, ((largc > 0) ? largv[0] : ""),
475 ((largc > 1) ? largv[1] : ""));
476 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
477 if (is_sparc()) {
478 bam_error(_("%s operation unsupported on SPARC "
479 "machines\n"), subcmd);
480 ret = BAM_ERROR;
481 } else {
482 ret = f(&menu, bam_root, NULL);
484 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
485 if (is_sparc()) {
486 bam_error(_("%s operation unsupported on SPARC "
487 "machines\n"), subcmd);
488 ret = BAM_ERROR;
489 } else {
490 char *extra_args = NULL;
493 * Compress all arguments passed in the largv[] array
494 * into one string that can then be appended to the
495 * end of the kernel$ string the routine to enable the
496 * hypervisor will build.
498 * This allows the caller to supply arbitrary unparsed
499 * arguments, such as dom0 memory settings or APIC
500 * options.
502 * This concatenation will be done without ANY syntax
503 * checking whatsoever, so it's the responsibility of
504 * the caller to make sure the arguments are valid and
505 * do not duplicate arguments the conversion routines
506 * may create.
508 if (largc > 0) {
509 int extra_len, i;
511 for (extra_len = 0, i = 0; i < largc; i++)
512 extra_len += strlen(largv[i]);
515 * Allocate space for argument strings,
516 * intervening spaces and terminating NULL.
518 extra_args = alloca(extra_len + largc);
520 (void) strcpy(extra_args, largv[0]);
522 for (i = 1; i < largc; i++) {
523 (void) strcat(extra_args, " ");
524 (void) strcat(extra_args, largv[i]);
528 ret = f(&menu, bam_root, extra_args);
530 } else
531 ret = f(&menu, NULL, opt);
533 if (ret == BAM_WRITE) {
534 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
535 fcn, clean_menu_root));
536 /* ret = menu_write(clean_menu_root, menu); */
539 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
540 assert((is_zfs(menu_root)) ^ (pool == NULL));
541 if (pool) {
542 (void) umount_top_dataset(pool, zmnted, zmntpt);
543 free(special);
546 menu_free(&menu);
547 return (ret);
551 * To suppress output from ficl. We do not want to see messages
552 * from interpreting loader config.
555 /*ARGSUSED*/
556 static void
557 ficlTextOutSilent(ficlCallback *cb, char *text)
561 /*ARGSUSED*/
562 static error_t
563 set_option(struct menu_lst *menu, char *dummy, char *opt)
565 char path[PATH_MAX];
566 char *val;
567 char *rest;
568 int optval;
569 struct menu_entry *entry;
570 nvlist_t *be_attrs;
571 FILE *fp;
572 int rv, ret = BAM_SUCCESS;
574 assert(menu);
575 assert(opt);
576 assert(dummy == NULL);
578 val = strchr(opt, '=');
579 if (val != NULL) {
580 *val++ = '\0';
583 if (strcmp(opt, "default") == 0) {
584 errno = 0;
585 optval = strtol(val, &rest, 10);
586 if (errno != 0 || *rest != '\0') {
587 bam_error(_("invalid boot entry number: %s\n"), val);
588 return (BAM_ERROR);
590 STAILQ_FOREACH(entry, menu, next) {
591 if (entry->entry == optval)
592 break;
594 if (entry == NULL) {
595 bam_error(_("invalid boot entry number: %s\n"), val);
596 return (BAM_ERROR);
598 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
599 bam_error(_("out of memory\n"));
600 return (BAM_ERROR);
602 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
603 entry->title) != 0) {
604 bam_error(_("out of memory\n"));
605 nvlist_free(be_attrs);
606 return (BAM_ERROR);
608 ret = be_activate(be_attrs);
609 nvlist_free(be_attrs);
610 if (ret != 0)
611 ret = BAM_ERROR;
612 return (ret);
613 } else if (strcmp(opt, "timeout") == 0) {
614 errno = 0;
615 optval = strtol(val, &rest, 10);
616 if (errno != 0 || *rest != '\0') {
617 bam_error(_("invalid timeout: %s\n"), val);
618 return (BAM_ERROR);
621 (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout",
622 bam_root);
624 fp = fopen(path, "w");
625 if (fp == NULL) {
626 bam_error(_("failed to open file: %s: %s\n"),
627 path, strerror(errno));
628 return (BAM_ERROR);
631 * timeout=-1 is to disable auto boot in illumos, but
632 * loader needs "NO" to disable auto boot.
634 if (optval == -1)
635 rv = fprintf(fp, "autoboot_delay=\"NO\"\n");
636 else
637 rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval);
639 if (rv < 0) {
640 bam_error(_("write to file failed: %s: %s\n"),
641 path, strerror(errno));
642 (void) fclose(fp);
643 ret = BAM_ERROR;
644 } else
645 rv = fclose(fp);
647 if (rv < 0) {
648 bam_error(_("failed to close file: %s: %s\n"),
649 path, strerror(errno));
650 ret = BAM_ERROR;
652 if (ret == BAM_ERROR)
653 (void) unlink(path);
655 return (BAM_SUCCESS);
658 bam_error(_("invalid option: %s\n"), opt);
659 return (BAM_ERROR);
662 static int
663 bam_mount_be(struct menu_entry *entry, char **dir)
665 nvlist_t *be_attrs = NULL;
666 const char *tmpdir = getenv("TMPDIR");
667 const char *tmpname = "bam.XXXXXX";
668 be_node_list_t *be_node, *be_nodes = NULL;
669 int ret;
671 *dir = NULL;
672 if (tmpdir == NULL)
673 tmpdir = "/tmp";
675 ret = asprintf(dir, "%s/%s", tmpdir, tmpname);
676 if (ret < 0) {
677 return (BE_ERR_NOMEM);
679 *dir = mkdtemp(*dir);
681 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) {
682 ret = BE_ERR_NOMEM;
683 goto out;
686 ret = be_list(NULL, &be_nodes);
687 if (ret != BE_SUCCESS) {
688 goto out;
691 for (be_node = be_nodes; be_node;
692 be_node = be_node->be_next_node)
693 if (strcmp(be_node->be_root_ds, entry->bootfs) == 0)
694 break;
696 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
697 be_node->be_node_name) != 0) {
698 ret = BE_ERR_NOMEM;
699 goto out;
702 if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) {
703 ret = BE_ERR_NOMEM;
704 goto out;
707 ret = be_mount(be_attrs);
708 if (ret == BE_ERR_MOUNTED) {
710 * if BE is mounted, dir does not point to correct directory
712 (void) rmdir(*dir);
713 free(*dir);
714 *dir = NULL;
716 out:
717 if (be_nodes != NULL)
718 be_free_list(be_nodes);
719 nvlist_free(be_attrs);
720 return (ret);
723 static int
724 bam_umount_be(char *dir)
726 nvlist_t *be_attrs;
727 int ret;
729 if (dir == NULL) /* nothing to do */
730 return (BE_SUCCESS);
732 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0)
733 return (BE_ERR_NOMEM);
735 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) {
736 ret = BE_ERR_NOMEM;
737 goto out;
740 ret = be_unmount(be_attrs);
741 out:
742 nvlist_free(be_attrs);
743 return (ret);
747 * display details of menu entry or single property
749 static error_t
750 list_menu_entry(struct menu_entry *entry, char *setting)
752 int ret = BAM_SUCCESS;
753 char *ptr, *dir;
754 char buf[MAX_INPUT];
755 ficlVm *vm;
756 int mounted;
758 mounted = bam_mount_be(entry, &dir);
759 if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) {
760 if (dir != NULL) {
761 (void) rmdir(dir);
762 free(dir);
764 bam_error(_("%s is not mounted\n"), entry->title);
765 return (BAM_ERROR);
768 vm = bf_init("", ficlTextOutSilent);
769 if (vm == NULL) {
770 bam_error(_("error setting up forth interpreter\n"));
771 ret = BAM_ERROR;
772 goto done;
775 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
776 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:",
777 entry->bootfs);
778 ret = ficlVmEvaluate(vm, buf);
779 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
780 bam_error(_("error interpreting boot config\n"));
781 ret = BAM_ERROR;
782 goto done;
784 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
785 ret = ficlVmEvaluate(vm, buf);
786 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
787 bam_error(_("error interpreting boot config\n"));
788 ret = BAM_ERROR;
789 goto done;
791 (void) snprintf(buf, MAX_INPUT, "start");
792 ret = ficlVmEvaluate(vm, buf);
793 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
794 bam_error(_("error interpreting boot config\n"));
795 ret = BAM_ERROR;
796 goto done;
798 (void) snprintf(buf, MAX_INPUT, "boot");
799 ret = ficlVmEvaluate(vm, buf);
800 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
801 bam_error(_("error interpreting boot config\n"));
802 ret = BAM_ERROR;
803 goto done;
806 ret = BAM_SUCCESS;
807 if (*setting == '\0')
808 (void) printf("\nTitle: %s\n", entry->title);
809 else if (strcasecmp(setting, "title") == 0) {
810 (void) printf("%s\n", entry->title);
811 goto done;
814 ptr = getenv("autoboot_delay");
815 if (ptr != NULL) {
816 char *timeout = "-1";
818 if (strcasecmp(ptr, "NO") != 0)
819 timeout = ptr;
821 if (*setting == '\0')
822 (void) printf("Timeout: %s\n", timeout);
823 else if (strcasecmp(setting, "timeout") == 0) {
824 (void) printf("%s\n", timeout);
825 goto done;
829 ptr = getenv("console");
830 if (ptr != NULL) {
831 if (*setting == '\0')
832 (void) printf("Console: %s\n", ptr);
833 else if (strcasecmp(setting, "console") == 0) {
834 (void) printf("%s\n", ptr);
835 goto done;
839 if (*setting == '\0')
840 (void) printf("Bootfs: %s\n", entry->bootfs);
841 else if (strcasecmp(setting, "bootfs") == 0) {
842 (void) printf("%s\n", entry->bootfs);
843 goto done;
846 ptr = getenv("xen_kernel");
847 if (ptr != NULL) {
848 if (*setting == '\0') {
849 (void) printf("Xen kernel: %s\n", ptr);
850 } else if (strcasecmp(setting, "xen_kernel") == 0) {
851 (void) printf("%s\n", ptr);
852 goto done;
855 if (*setting == '\0') {
856 (void) printf("Xen args: \"%s\"\n",
857 getenv("xen_cmdline"));
858 } else if (strcasecmp(setting, "xen_cmdline") == 0) {
859 (void) printf("%s\n", getenv("xen_cmdline"));
860 goto done;
863 if (*setting == '\0') {
864 (void) printf("Kernel: %s\n",
865 getenv("bootfile"));
866 } if (strcasecmp(setting, "kernel") == 0) {
867 (void) printf("%s\n", getenv("bootfile"));
868 goto done;
870 } else {
871 ptr = getenv("kernelname");
872 if (ptr != NULL) {
873 if (*setting == '\0') {
874 (void) printf("Kernel: %s\n", ptr);
875 } else if (strcasecmp(setting, "kernel") == 0) {
876 (void) printf("%s\n", ptr);
877 goto done;
882 ptr = getenv("boot-args");
883 if (ptr != NULL) {
884 if (*setting == '\0') {
885 (void) printf("Boot-args: \"%s\"\n", ptr);
886 } else if (strcasecmp(setting, "boot-args") == 0) {
887 (void) printf("%s\n", ptr);
888 goto done;
892 if (*setting == '\0' || strcasecmp(setting, "modules") == 0) {
893 (void) printf("\nModules:\n");
894 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut);
895 (void) snprintf(buf, MAX_INPUT, "show-module-options");
896 ret = ficlVmEvaluate(vm, buf);
897 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
898 bam_error(_("error interpreting boot config\n"));
899 ret = BAM_ERROR;
900 goto done;
902 ret = BAM_SUCCESS;
903 goto done;
906 /* if we got here with setting string, its unknown property */
907 if (*setting != '\0') {
908 bam_error(_("unknown property: %s\n"), setting);
909 ret = BAM_ERROR;
910 } else
911 ret = BAM_SUCCESS;
912 done:
913 bf_fini();
914 if (mounted != BE_ERR_MOUNTED) {
915 (void) bam_umount_be(dir);
918 if (dir != NULL) {
919 (void) rmdir(dir);
920 free(dir);
923 return (ret);
926 /*ARGSUSED*/
927 static error_t
928 list_entry(struct menu_lst *menu, char *menu_root, char *opt)
930 error_t ret = BAM_SUCCESS;
931 struct menu_entry *entry;
932 char *ptr, *title = NULL;
933 int i, e = -1;
935 if (opt == NULL) {
936 print_nodes(B_FALSE, menu);
937 return (ret);
940 if ((ptr = strchr(opt, '=')) == NULL) {
941 bam_error(_("invalid option: %s\n"), opt);
942 return (BAM_ERROR);
945 i = ptr - opt;
946 if (strncmp(opt, "entry", i) == 0) {
947 e = atoi(ptr+1);
948 } else if (strncmp(opt, "title", i) == 0) {
949 title = ptr+1;
950 } else {
951 bam_error(_("invalid option: %s\n"), opt);
952 return (BAM_ERROR);
955 STAILQ_FOREACH(entry, menu, next) {
956 if (title != NULL) {
957 if (strcmp(title, entry->title) == 0)
958 break;
959 } else if (entry->entry == e)
960 break;
963 if (entry == NULL) {
964 bam_error(_("no matching entry found\n"));
965 return (BAM_ERROR);
968 return (list_menu_entry(entry, ""));
972 * For now this is just stub entry to support grub interface, the
973 * known consumer is installer ict.py code, calling as:
974 * bootadm update-menu -R /a -Z -o rdisk
975 * Later this can be converted to do something useful.
977 /*ARGSUSED*/
978 static error_t
979 update_entry(struct menu_lst *menu, char *menu_root, char *osdev)
981 char path[PATH_MAX];
982 char *pool = menu_root + 1;
983 be_node_list_t *be_nodes, *be_node;
984 int rv;
985 FILE *fp;
987 (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU);
988 rv = be_list(NULL, &be_nodes);
990 if (rv != BE_SUCCESS)
991 return (BAM_ERROR);
993 fp = fopen(path, "w");
994 if (fp == NULL) {
995 be_free_list(be_nodes);
996 return (BAM_ERROR);
999 for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) {
1000 if (strcmp(be_node->be_rpool, pool) == 0) {
1001 (void) fprintf(fp, "title %s\n", be_node->be_node_name);
1002 (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds);
1006 be_free_list(be_nodes);
1007 (void) fclose(fp);
1008 return (BAM_SUCCESS);
1011 /*ARGSUSED*/
1012 static error_t
1013 update_temp(struct menu_lst *menu, char *dummy, char *opt)
1015 error_t ret = BAM_ERROR;
1016 char path[PATH_MAX];
1017 char buf[MAX_INPUT];
1018 struct mnttab mpref = { 0 };
1019 struct mnttab mp = { 0 };
1020 ficlVm *vm;
1021 char *env, *o;
1022 FILE *fp;
1024 (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root);
1026 * if opt == NULL, remove transient config
1028 if (opt == NULL) {
1029 (void) unlink(path);
1030 return (BAM_SUCCESS);
1033 fp = fopen(MNTTAB, "r");
1034 if (fp == NULL)
1035 return (BAM_ERROR);
1037 mpref.mnt_mountp = "/";
1038 if (getmntany(fp, &mp, &mpref) != 0) {
1039 (void) fclose(fp);
1040 return (BAM_ERROR);
1042 (void) fclose(fp);
1044 vm = bf_init("", ficlTextOutSilent);
1045 if (vm == NULL) {
1046 bam_error(_("Error setting up forth interpreter\n"));
1047 return (ret);
1051 * need to check current boot config, so fire up the ficl
1052 * if its xen setup, we add option to boot-args list, not replacing it.
1054 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1055 ret = ficlVmEvaluate(vm, buf);
1056 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1057 bam_error(_("Error interpreting boot config\n"));
1058 bf_fini();
1059 return (BAM_ERROR);
1061 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1062 ret = ficlVmEvaluate(vm, buf);
1063 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1064 bam_error(_("Error interpreting boot config\n"));
1065 bf_fini();
1066 return (BAM_ERROR);
1068 (void) snprintf(buf, MAX_INPUT, "start");
1069 ret = ficlVmEvaluate(vm, buf);
1070 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1071 bam_error(_("Error interpreting boot config\n"));
1072 bf_fini();
1073 return (BAM_ERROR);
1075 (void) snprintf(buf, MAX_INPUT, "boot");
1076 ret = ficlVmEvaluate(vm, buf);
1077 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1078 bam_error(_("Error interpreting boot config\n"));
1079 bf_fini();
1080 return (BAM_ERROR);
1082 bf_fini();
1084 if (opt[0] == '-') {
1085 env = getenv("xen_kernel");
1086 fp = fopen(path, "w");
1087 if (fp == NULL)
1088 return (BAM_ERROR);
1090 if (env != NULL) {
1091 env = getenv("boot-args");
1092 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1093 } else
1094 (void) fprintf(fp, "boot-args=\"%s\"\n", opt);
1095 (void) fclose(fp);
1096 return (BAM_SUCCESS);
1100 * it should be the case with "kernel args"
1101 * so, we split the opt at first space
1102 * and store bootfile= and boot-args=
1104 env = getenv("xen_kernel");
1106 o = strchr(opt, ' ');
1107 if (o == NULL) {
1108 fp = fopen(path, "w");
1109 if (fp == NULL)
1110 return (BAM_ERROR);
1111 (void) fprintf(fp, "bootfile=\"%s\"\n", opt);
1112 (void) fclose(fp);
1113 return (BAM_SUCCESS);
1115 *o++ = '\0';
1116 fp = fopen(path, "w");
1117 if (fp == NULL)
1118 return (BAM_ERROR);
1119 (void) fprintf(fp, "bootfile=\"%s\"\n", opt);
1121 if (env != NULL) {
1122 env = getenv("boot-args");
1123 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt);
1124 } else
1125 (void) fprintf(fp, "boot-args=\"%s\"\n", o);
1127 (void) fflush(fp);
1128 (void) fclose(fp);
1129 return (ret);
1132 static error_t
1133 list_setting(struct menu_lst *menu, char *which, char *setting)
1135 int entry = -1;
1136 struct menu_entry *m;
1137 be_node_list_t *be_nodes, *be_node = NULL;
1138 int ret;
1140 assert(which);
1141 assert(setting);
1144 * which can be:
1145 * "" - list default entry
1146 * number - use for entry number
1147 * property name
1149 if (*which != '\0') {
1150 if (isdigit(*which)) {
1151 char *rest;
1152 errno = 0;
1153 entry = strtol(which, &rest, 10);
1154 if (errno != 0 || *rest != '\0') {
1155 bam_error(_("invalid boot entry number: %s\n"),
1156 which);
1157 return (BAM_ERROR);
1159 } else
1160 setting = which;
1163 /* find default entry */
1164 if (entry == -1) {
1165 ret = be_list(NULL, &be_nodes);
1166 if (ret != BE_SUCCESS) {
1167 bam_error(_("No BE's found\n"));
1168 return (BAM_ERROR);
1170 STAILQ_FOREACH(m, menu, next) {
1171 entry++;
1172 for (be_node = be_nodes; be_node;
1173 be_node = be_node->be_next_node)
1174 if (strcmp(be_node->be_root_ds, m->bootfs) == 0)
1175 break;
1176 if (be_node->be_active_on_boot == B_TRUE)
1177 break; /* found active node */
1179 be_free_list(be_nodes);
1180 if (be_node == NULL) {
1181 bam_error(_("None of BE nodes is marked active\n"));
1182 return (BAM_ERROR);
1184 } else {
1185 STAILQ_FOREACH(m, menu, next)
1186 if (m->entry == entry)
1187 break;
1189 if (m == NULL) {
1190 bam_error(_("no matching entry found\n"));
1191 return (BAM_ERROR);
1195 return (list_menu_entry(m, setting));
1198 /*ARGSUSED*/
1199 static error_t
1200 disable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1202 char path[PATH_MAX];
1204 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1205 (void) unlink(path);
1206 return (BAM_SUCCESS);
1209 /*ARGSUSED*/
1210 static error_t
1211 enable_hyper(struct menu_lst *menu, char *osroot, char *opt)
1213 ficlVm *vm;
1214 char path[PATH_MAX];
1215 char buf[MAX_INPUT];
1216 char *env;
1217 FILE *fp;
1218 struct mnttab mpref = { 0 };
1219 struct mnttab mp = { 0 };
1220 int ret;
1222 fp = fopen(MNTTAB, "r");
1223 if (fp == NULL)
1224 return (BAM_ERROR);
1226 mpref.mnt_mountp = "/";
1227 if (getmntany(fp, &mp, &mpref) != 0) {
1228 (void) fclose(fp);
1229 return (BAM_ERROR);
1231 (void) fclose(fp);
1233 vm = bf_init("", ficlTextOutSilent);
1234 if (vm == NULL) {
1235 bam_error(_("Error setting up forth interpreter\n"));
1236 return (BAM_ERROR);
1240 * need to check current boot config, so fire up the ficl
1241 * if its xen setup, we add option to boot-args list, not replacing it.
1243 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special);
1244 ret = ficlVmEvaluate(vm, buf);
1245 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1246 bam_error(_("Error interpreting boot config\n"));
1247 bf_fini();
1248 return (BAM_ERROR);
1250 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th");
1251 ret = ficlVmEvaluate(vm, buf);
1252 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1253 bam_error(_("Error interpreting boot config\n"));
1254 bf_fini();
1255 return (BAM_ERROR);
1257 (void) snprintf(buf, MAX_INPUT, "start");
1258 ret = ficlVmEvaluate(vm, buf);
1259 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1260 bam_error(_("Error interpreting boot config\n"));
1261 bf_fini();
1262 return (BAM_ERROR);
1264 (void) snprintf(buf, MAX_INPUT, "boot");
1265 ret = ficlVmEvaluate(vm, buf);
1266 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
1267 bam_error(_("Error interpreting boot config\n"));
1268 bf_fini();
1269 return (BAM_ERROR);
1271 bf_fini();
1273 (void) mkdir(CONF_DIR, 0755);
1274 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root);
1275 fp = fopen(path, "w");
1276 if (fp == NULL) {
1277 return (BAM_ERROR); /* error, cant write config */
1280 errno = 0;
1282 * on write error, remove file to ensure we have bootable config.
1283 * note we dont mind if config exists, it will get updated
1285 (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1286 if (errno != 0)
1287 goto error;
1290 * really simple and stupid console conversion.
1291 * it really has to be gone, it belongs to milestone/xvm properties.
1293 env = getenv("console");
1294 if (env != NULL) {
1295 if (strcmp(env, "ttya") == 0)
1296 (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n",
1297 opt);
1298 else if (strcmp(env, "ttyb") == 0)
1299 (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n",
1300 opt);
1301 else
1302 (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n",
1303 opt);
1304 } else
1305 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt);
1306 if (errno != 0)
1307 goto error;
1309 (void) fprintf(fp,
1310 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1311 if (errno != 0)
1312 goto error;
1314 (void) fprintf(fp,
1315 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1316 if (errno != 0)
1317 goto error;
1319 (void) fclose(fp);
1320 if (errno != 0) {
1321 (void) unlink(path);
1322 return (BAM_ERROR);
1324 return (BAM_SUCCESS);
1325 error:
1326 (void) fclose(fp);
1327 (void) unlink(path);
1328 return (BAM_ERROR);