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]
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.
44 #include <sys/types.h>
46 #include <sys/queue.h>
49 #include <ficlplatform/emu.h>
54 extern int bam_rootlen
;
55 extern int bam_alt_root
;
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
{
71 STAILQ_ENTRY(menu_entry
) me_next
;
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 */
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
) {
104 (void) snprintf(buf
, bufsize
, "%d", entry
->me_idx
);
107 (void) snprintf(buf
, bufsize
, "%s", entry
->me_title
);
110 (void) snprintf(buf
, bufsize
, "%s", entry
->me_bootfs
);
113 (void) snprintf(buf
, bufsize
, "%s", entry
->me_type
);
116 if (entry
->me_active
== B_TRUE
)
117 (void) snprintf(buf
, bufsize
, " *");
119 (void) snprintf(buf
, bufsize
, " -");
128 init_hdr_cols(ofmt_field_t
*hdr
)
132 for (i
= 0; i
< NUM_COLS
; i
++) {
153 hdr
[i
].of_name
= name
;
155 hdr
[i
].of_cb
= print_menu_cb
;
159 size_t sz
= mbstowcs(wname
, name
, sizeof (wname
) /
162 int wcsw
= wcswidth(wname
, sz
);
164 hdr
[i
].of_width
= wcsw
;
166 hdr
[i
].of_width
= sz
;
168 hdr
[i
].of_width
= strlen(name
);
175 menu_update_widths(ofmt_field_t
*hdr
, struct menu_lst
*menu
)
177 size_t len
[NUM_COLS
];
181 for (i
= 0; i
< NUM_COLS
; i
++)
182 len
[i
] = hdr
[i
].of_width
+ 1;
184 STAILQ_FOREACH(entry
, menu
, me_next
) {
187 entry_len
= strlen(entry
->me_title
) + 1;
188 if (entry_len
> len
[1])
191 entry_len
= strlen(entry
->me_bootfs
) + 1;
192 if (entry_len
> len
[2])
195 entry_len
= strlen(entry
->me_type
) + 1;
196 if (entry_len
> len
[3])
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
)
209 if ((temp
= calloc(NUM_COLS
+ 1, sizeof (ofmt_field_t
))) == NULL
)
213 menu_update_widths(temp
, menu
);
218 print_nodes(boolean_t parsable
, struct menu_lst
*menu
)
222 uint_t ofmtflags
= 0;
223 ofmt_field_t
*menu_template
;
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
);
241 STAILQ_FOREACH(entry
, menu
, me_next
)
242 ofmt_print(ofmt
, entry
);
249 * Get the be_active_on_boot for bootfs.
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
;
269 menu_read(struct menu_lst
*menu
, char *menu_path
)
272 be_node_list_t
*be_nodes
;
280 int ret
= BAM_SUCCESS
;
282 fp
= fopen(menu_path
, "r");
286 if (be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
) != BE_SUCCESS
)
290 * menu.lst entry is on two lines, one for title, one for bootfs
291 * so we process both lines in succession.
297 if (fgets(buf
, PATH_MAX
, fp
) == NULL
) {
302 key
= strtok(buf
, " \n");
303 if (strcmp(key
, "title") != 0) {
307 value
= strtok(NULL
, " \n");
308 if ((title
= strdup(value
)) == NULL
) {
313 if (fgets(buf
, PATH_MAX
, fp
) == NULL
) {
318 key
= strtok(buf
, " \n");
319 if ((type
= strdup(key
)) == NULL
) {
323 value
= strtok(NULL
, " \n");
324 if ((bootfs
= strdup(value
)) == NULL
) {
328 if ((mp
= malloc(sizeof (menu_entry_t
))) == NULL
) {
333 mp
->me_title
= title
;
335 mp
->me_bootfs
= bootfs
;
336 mp
->me_active
= menu_active_on_boot(be_nodes
, bootfs
);
337 STAILQ_INSERT_TAIL(menu
, mp
, me_next
);
342 } while (feof(fp
) == 0);
349 be_free_list(be_nodes
);
354 menu_free(struct menu_lst
*menu
)
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
);
369 bam_loader_menu(char *subcmd
, char *opt
, int largc
, char *largv
[])
372 char menu_path
[PATH_MAX
];
373 char clean_menu_root
[PATH_MAX
];
374 char menu_root
[PATH_MAX
];
376 error_t (*f
)(struct menu_lst
*, char *, char *);
383 const char *fcn
= "bam_loader_menu()";
384 struct menu_lst menu
= {0};
391 ret
= check_subcmd_and_options(subcmd
, opt
, menu_subcmds
, &f
);
392 if (ret
== BAM_ERROR
) {
398 (void) strlcpy(menu_root
, bam_root
, sizeof (menu_root
));
399 osdev
= osroot
= NULL
;
401 if (strcmp(subcmd
, "update_entry") == 0) {
404 osdev
= strtok(opt
, ",");
406 osroot
= strtok(NULL
, ",");
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
));
416 bam_rootlen
= strlen(rootbuf
);
420 if (stat(menu_root
, &sb
) == -1) {
421 bam_error(_("cannot find menu\n"));
425 if (!is_zfs(menu_root
)) {
426 bam_error(_("only ZFS root is supported\n"));
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"),
438 pool
= strtok(special
, "/");
439 INJECT_ERROR1("Z_MENU_GET_POOL", pool
= NULL
);
442 bam_error(_("cant find pool for mount-point %s\n"), menu_root
);
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
);
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
);
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"),
489 * We already checked the following case in
490 * check_subcmd_and_suboptions() above. Complete the
493 if (strcmp(subcmd
, "set_option") == 0) {
494 assert(largc
== 1 && largv
[0] && largv
[1] == NULL
);
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
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
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
);
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
));
568 (void) umount_top_dataset(pool
, zmnted
, zmntpt
);
577 * To suppress output from ficl. We do not want to see messages
578 * from interpreting loader config.
583 ficlTextOutSilent(ficlCallback
*cb
, char *text
)
589 set_option(struct menu_lst
*menu
, char *dummy
, char *opt
)
598 int rv
, ret
= BAM_SUCCESS
;
602 assert(dummy
== NULL
);
604 val
= strchr(opt
, '=');
609 if (strcmp(opt
, "default") == 0) {
611 optval
= strtol(val
, &rest
, 10);
612 if (errno
!= 0 || *rest
!= '\0') {
613 bam_error(_("invalid boot entry number: %s\n"), val
);
616 STAILQ_FOREACH(entry
, menu
, me_next
) {
617 if (entry
->me_idx
== optval
)
621 bam_error(_("invalid boot entry number: %s\n"), val
);
624 if (nvlist_alloc(&be_attrs
, NV_UNIQUE_NAME
, 0) != 0) {
625 bam_error(_("out of memory\n"));
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
);
634 ret
= be_activate(be_attrs
);
635 nvlist_free(be_attrs
);
639 } else if (strcmp(opt
, "timeout") == 0) {
641 optval
= strtol(val
, &rest
, 10);
642 if (errno
!= 0 || *rest
!= '\0') {
643 bam_error(_("invalid timeout: %s\n"), val
);
647 (void) snprintf(path
, PATH_MAX
, "%s" CONF_DIR
"/timeout",
650 fp
= fopen(path
, "w");
652 bam_error(_("failed to open file: %s: %s\n"),
653 path
, strerror(errno
));
657 * timeout=-1 is to disable auto boot in illumos, but
658 * loader needs "NO" to disable auto boot.
661 rv
= fprintf(fp
, "autoboot_delay=\"NO\"\n");
663 rv
= fprintf(fp
, "autoboot_delay=\"%d\"\n", optval
);
666 bam_error(_("write to file failed: %s: %s\n"),
667 path
, strerror(errno
));
674 bam_error(_("failed to close file: %s: %s\n"),
675 path
, strerror(errno
));
678 if (ret
== BAM_ERROR
)
681 return (BAM_SUCCESS
);
684 bam_error(_("invalid option: %s\n"), opt
);
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
;
701 ret
= asprintf(dir
, "%s/%s", tmpdir
, tmpname
);
703 return (BE_ERR_NOMEM
);
705 *dir
= mkdtemp(*dir
);
707 if (nvlist_alloc(&be_attrs
, NV_UNIQUE_NAME
, 0) != 0) {
712 ret
= be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
);
713 if (ret
!= BE_SUCCESS
) {
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)
722 if (nvlist_add_string(be_attrs
, BE_ATTR_ORIG_BE_NAME
,
723 be_node
->be_node_name
) != 0) {
728 if (nvlist_add_string(be_attrs
, BE_ATTR_MOUNTPOINT
, *dir
) != 0) {
733 ret
= be_mount(be_attrs
);
734 if (ret
== BE_ERR_MOUNTED
) {
736 * if BE is mounted, dir does not point to correct directory
743 if (be_nodes
!= NULL
)
744 be_free_list(be_nodes
);
745 nvlist_free(be_attrs
);
750 bam_umount_be(char *dir
)
755 if (dir
== NULL
) /* nothing to do */
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) {
766 ret
= be_unmount(be_attrs
);
768 nvlist_free(be_attrs
);
773 * display details of menu entry or single property
776 list_menu_entry(menu_entry_t
*entry
, char *setting
)
778 int ret
= BAM_SUCCESS
;
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
);
792 mounted
= bam_mount_be(entry
, &dir
);
793 if (mounted
!= BE_SUCCESS
&& mounted
!= BE_ERR_MOUNTED
) {
798 bam_error(_("%s is not mounted\n"), entry
->me_title
);
802 vm
= bf_init("", ficlTextOutSilent
);
804 bam_error(_("error setting up forth interpreter\n"));
809 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
810 (void) snprintf(buf
, MAX_INPUT
, "set currdev=zfs:%s:",
812 ret
= ficlVmEvaluate(vm
, buf
);
813 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
814 bam_error(_("error interpreting boot config\n"));
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"));
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"));
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"));
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
);
848 ptr
= getenv("autoboot_delay");
850 char *timeout
= "-1";
852 if (strcasecmp(ptr
, "NO") != 0)
855 if (*setting
== '\0')
856 (void) printf("Timeout: %s\n", timeout
);
857 else if (strcasecmp(setting
, "timeout") == 0) {
858 (void) printf("%s\n", timeout
);
863 ptr
= getenv("console");
865 if (*setting
== '\0')
866 (void) printf("Console: %s\n", ptr
);
867 else if (strcasecmp(setting
, "console") == 0) {
868 (void) printf("%s\n", ptr
);
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
);
880 ptr
= getenv("xen_kernel");
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
);
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"));
897 if (*setting
== '\0') {
898 (void) printf("Kernel: %s\n",
900 } else if (strcasecmp(setting
, "kernel") == 0) {
901 (void) printf("%s\n", getenv("bootfile"));
905 ptr
= getenv("kernelname");
907 if (*setting
== '\0') {
908 (void) printf("Kernel: %s\n", ptr
);
909 } else if (strcasecmp(setting
, "kernel") == 0) {
910 (void) printf("%s\n", ptr
);
916 ptr
= getenv("boot-args");
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
);
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"));
940 /* if we got here with setting string, its unknown property */
941 if (*setting
!= '\0') {
942 bam_error(_("unknown property: %s\n"), setting
);
948 if (mounted
!= BE_ERR_MOUNTED
) {
949 (void) bam_umount_be(dir
);
962 list_entry(struct menu_lst
*menu
, char *menu_root
, char *opt
)
964 error_t ret
= BAM_SUCCESS
;
966 char *ptr
, *title
= NULL
;
970 print_nodes(B_FALSE
, menu
);
974 if ((ptr
= strchr(opt
, '=')) == NULL
) {
975 bam_error(_("invalid option: %s\n"), opt
);
980 if (strncmp(opt
, "entry", i
) == 0) {
982 } else if (strncmp(opt
, "title", i
) == 0) {
985 bam_error(_("invalid option: %s\n"), opt
);
989 STAILQ_FOREACH(entry
, menu
, me_next
) {
991 if (strcmp(title
, entry
->me_title
) == 0)
993 } else if (entry
->me_idx
== e
)
998 bam_error(_("no matching entry found\n"));
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.
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
;
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
)
1027 fp
= fopen(path
, "w");
1029 be_free_list(be_nodes
);
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
);
1042 return (BAM_SUCCESS
);
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 };
1058 (void) snprintf(path
, PATH_MAX
, "%s" TRANSIENT
, bam_root
);
1060 * if opt == NULL, remove transient config
1063 (void) unlink(path
);
1064 return (BAM_SUCCESS
);
1067 fp
= fopen(MNTTAB
, "r");
1071 mpref
.mnt_mountp
= "/";
1072 if (getmntany(fp
, &mp
, &mpref
) != 0) {
1078 vm
= bf_init("", ficlTextOutSilent
);
1080 bam_error(_("Error setting up forth interpreter\n"));
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"));
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"));
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"));
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"));
1118 if (opt
[0] == '-') {
1119 env
= getenv("xen_kernel");
1120 fp
= fopen(path
, "w");
1125 env
= getenv("boot-args");
1126 (void) fprintf(fp
, "boot-args=\"%s %s\"\n", env
, opt
);
1128 (void) fprintf(fp
, "boot-args=\"%s\"\n", opt
);
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
, ' ');
1142 fp
= fopen(path
, "w");
1145 (void) fprintf(fp
, "bootfile=\"%s;unix\"\n", opt
);
1147 return (BAM_SUCCESS
);
1150 fp
= fopen(path
, "w");
1153 (void) fprintf(fp
, "bootfile=\"%s;unix\"\n", opt
);
1156 env
= getenv("boot-args");
1157 (void) fprintf(fp
, "boot-args=\"%s %s\"\n", env
, opt
);
1159 (void) fprintf(fp
, "boot-args=\"%s\"\n", o
);
1167 list_setting(struct menu_lst
*menu
, char *which
, char *setting
)
1171 be_node_list_t
*be_nodes
, *be_node
= NULL
;
1179 * "" - list default entry
1180 * number - use for entry number
1183 if (*which
!= '\0') {
1184 if (isdigit(*which
)) {
1187 entry
= strtol(which
, &rest
, 10);
1188 if (errno
!= 0 || *rest
!= '\0') {
1189 bam_error(_("invalid boot entry number: %s\n"),
1197 /* find default entry */
1199 ret
= be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
);
1200 if (ret
!= BE_SUCCESS
) {
1201 bam_error(_("No BE's found\n"));
1204 STAILQ_FOREACH(m
, menu
, me_next
) {
1206 for (be_node
= be_nodes
; be_node
;
1207 be_node
= be_node
->be_next_node
) {
1208 if (strcmp(be_node
->be_root_ds
,
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"));
1222 STAILQ_FOREACH(m
, menu
, me_next
)
1223 if (m
->me_idx
== entry
)
1227 bam_error(_("no matching entry found\n"));
1232 return (list_menu_entry(m
, setting
));
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
);
1248 enable_hyper(struct menu_lst
*menu
, char *osroot
, char *opt
)
1251 char path
[PATH_MAX
];
1252 char buf
[MAX_INPUT
];
1255 struct mnttab mpref
= { 0 };
1256 struct mnttab mp
= { 0 };
1259 fp
= fopen(MNTTAB
, "r");
1263 mpref
.mnt_mountp
= "/";
1264 if (getmntany(fp
, &mp
, &mpref
) != 0) {
1270 vm
= bf_init("", ficlTextOutSilent
);
1272 bam_error(_("Error setting up forth interpreter\n"));
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"));
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"));
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"));
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"));
1310 (void) mkdir(CONF_DIR
, 0755);
1311 (void) snprintf(path
, PATH_MAX
, "%s" XEN_CONFIG
, bam_root
);
1312 fp
= fopen(path
, "w");
1314 return (BAM_ERROR
); /* error, cant write config */
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");
1327 * really simple and stupid console conversion.
1328 * it really has to be gone, it belongs to milestone/xvm properties.
1330 env
= getenv("console");
1332 if (strcmp(env
, "ttya") == 0)
1333 (void) fprintf(fp
, "xen_cmdline=\"console=com1 %s\"\n",
1335 else if (strcmp(env
, "ttyb") == 0)
1336 (void) fprintf(fp
, "xen_cmdline=\"console=com2 %s\"\n",
1339 (void) fprintf(fp
, "xen_cmdline=\"console=vga %s\"\n",
1342 (void) fprintf(fp
, "xen_cmdline=\"%s\"\n", opt
);
1347 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1352 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1358 (void) unlink(path
);
1361 return (BAM_SUCCESS
);
1364 (void) unlink(path
);