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
)
357 STAILQ_FOREACH(entry
, menu
, me_next
) {
358 STAILQ_REMOVE_HEAD(menu
, me_next
);
359 free(entry
->me_title
);
360 free(entry
->me_type
);
361 free(entry
->me_bootfs
);
367 bam_loader_menu(char *subcmd
, char *opt
, int largc
, char *largv
[])
370 char menu_path
[PATH_MAX
];
371 char clean_menu_root
[PATH_MAX
];
372 char menu_root
[PATH_MAX
];
374 error_t (*f
)(struct menu_lst
*, char *, char *);
381 const char *fcn
= "bam_loader_menu()";
382 struct menu_lst menu
= {0};
389 ret
= check_subcmd_and_options(subcmd
, opt
, menu_subcmds
, &f
);
390 if (ret
== BAM_ERROR
) {
396 (void) strlcpy(menu_root
, bam_root
, sizeof (menu_root
));
397 osdev
= osroot
= NULL
;
399 if (strcmp(subcmd
, "update_entry") == 0) {
402 osdev
= strtok(opt
, ",");
404 osroot
= strtok(NULL
, ",");
406 /* fixup bam_root so that it points at osroot */
407 if (realpath(osroot
, rootbuf
) == NULL
) {
408 bam_error(_("cannot resolve path %s: %s\n"),
409 osroot
, strerror(errno
));
414 bam_rootlen
= strlen(rootbuf
);
418 if (stat(menu_root
, &sb
) == -1) {
419 bam_error(_("cannot find menu\n"));
423 if (!is_zfs(menu_root
)) {
424 bam_error(_("only ZFS root is supported\n"));
428 assert(strcmp(menu_root
, bam_root
) == 0);
429 special
= get_special(menu_root
);
430 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special
= NULL
);
431 if (special
== NULL
) {
432 bam_error(_("cant find special file for mount-point %s\n"),
436 pool
= strtok(special
, "/");
437 INJECT_ERROR1("Z_MENU_GET_POOL", pool
= NULL
);
440 bam_error(_("cant find pool for mount-point %s\n"), menu_root
);
443 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn
, pool
));
445 zmntpt
= mount_top_dataset(pool
, &zmnted
);
446 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt
= NULL
);
447 if (zmntpt
== NULL
) {
448 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool
);
452 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn
, zmntpt
));
454 (void) strlcpy(menu_root
, zmntpt
, sizeof (menu_root
));
455 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn
, menu_root
));
457 elide_trailing_slash(menu_root
, clean_menu_root
,
458 sizeof (clean_menu_root
));
460 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn
, clean_menu_root
));
462 (void) strlcpy(menu_path
, clean_menu_root
, sizeof (menu_path
));
463 (void) strlcat(menu_path
, MENU
, sizeof (menu_path
));
465 BAM_DPRINTF(("%s: menu path is: %s\n", fcn
, menu_path
));
468 * update_entry is special case, its used by installer
469 * and needs to create menu.lst file for loader
471 if (menu_read(&menu
, menu_path
) == BAM_ERROR
&&
472 strcmp(subcmd
, "update_entry") != 0) {
473 bam_error(_("cannot find menu file: %s\n"), menu_path
);
480 * If listing the menu, display the menu location
482 if (strcmp(subcmd
, "list_entry") == 0)
483 bam_print(_("the location for the active menu is: %s\n"),
487 * We already checked the following case in
488 * check_subcmd_and_suboptions() above. Complete the
491 if (strcmp(subcmd
, "set_option") == 0) {
492 assert(largc
== 1 && largv
[0] && largv
[1] == NULL
);
494 } else if ((strcmp(subcmd
, "enable_hypervisor") != 0) &&
495 (strcmp(subcmd
, "list_setting") != 0)) {
496 assert(largc
== 0 && largv
== NULL
);
500 * Once the sub-cmd handler has run
501 * only the line field is guaranteed to have valid values
503 if (strcmp(subcmd
, "update_entry") == 0) {
504 ret
= f(&menu
, menu_root
, osdev
);
505 } else if (strcmp(subcmd
, "upgrade") == 0) {
506 ret
= f(&menu
, bam_root
, menu_root
);
507 } else if (strcmp(subcmd
, "list_entry") == 0) {
508 ret
= f(&menu
, menu_path
, opt
);
509 } else if (strcmp(subcmd
, "list_setting") == 0) {
510 ret
= f(&menu
, ((largc
> 0) ? largv
[0] : ""),
511 ((largc
> 1) ? largv
[1] : ""));
512 } else if (strcmp(subcmd
, "disable_hypervisor") == 0) {
513 ret
= f(&menu
, bam_root
, NULL
);
514 } else if (strcmp(subcmd
, "enable_hypervisor") == 0) {
515 char *extra_args
= NULL
;
518 * Compress all arguments passed in the largv[] array
519 * into one string that can then be appended to the
520 * end of the kernel$ string the routine to enable the
521 * hypervisor will build.
523 * This allows the caller to supply arbitrary unparsed
524 * arguments, such as dom0 memory settings or APIC
527 * This concatenation will be done without ANY syntax
528 * checking whatsoever, so it's the responsibility of
529 * the caller to make sure the arguments are valid and
530 * do not duplicate arguments the conversion routines
536 for (extra_len
= 0, i
= 0; i
< largc
; i
++)
537 extra_len
+= strlen(largv
[i
]);
540 * Allocate space for argument strings,
541 * intervening spaces and terminating NULL.
543 extra_args
= alloca(extra_len
+ largc
);
545 (void) strcpy(extra_args
, largv
[0]);
547 for (i
= 1; i
< largc
; i
++) {
548 (void) strcat(extra_args
, " ");
549 (void) strcat(extra_args
, largv
[i
]);
553 ret
= f(&menu
, bam_root
, extra_args
);
555 ret
= f(&menu
, NULL
, opt
);
557 if (ret
== BAM_WRITE
) {
558 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
559 fcn
, clean_menu_root
));
560 /* ret = menu_write(clean_menu_root, menu); */
563 INJECT_ERROR1("POOL_SET", pool
= "/pooldata");
564 assert((is_zfs(menu_root
)) ^ (pool
== NULL
));
566 (void) umount_top_dataset(pool
, zmnted
, zmntpt
);
575 * To suppress output from ficl. We do not want to see messages
576 * from interpreting loader config.
581 ficlTextOutSilent(ficlCallback
*cb
, char *text
)
587 set_option(struct menu_lst
*menu
, char *dummy
, char *opt
)
596 int rv
, ret
= BAM_SUCCESS
;
600 assert(dummy
== NULL
);
602 val
= strchr(opt
, '=');
607 if (strcmp(opt
, "default") == 0) {
609 optval
= strtol(val
, &rest
, 10);
610 if (errno
!= 0 || *rest
!= '\0') {
611 bam_error(_("invalid boot entry number: %s\n"), val
);
614 STAILQ_FOREACH(entry
, menu
, me_next
) {
615 if (entry
->me_idx
== optval
)
619 bam_error(_("invalid boot entry number: %s\n"), val
);
622 if (nvlist_alloc(&be_attrs
, NV_UNIQUE_NAME
, 0) != 0) {
623 bam_error(_("out of memory\n"));
626 if (nvlist_add_string(be_attrs
, BE_ATTR_ORIG_BE_NAME
,
627 entry
->me_title
) != 0) {
628 bam_error(_("out of memory\n"));
629 nvlist_free(be_attrs
);
632 ret
= be_activate(be_attrs
);
633 nvlist_free(be_attrs
);
637 } else if (strcmp(opt
, "timeout") == 0) {
639 optval
= strtol(val
, &rest
, 10);
640 if (errno
!= 0 || *rest
!= '\0') {
641 bam_error(_("invalid timeout: %s\n"), val
);
645 (void) snprintf(path
, PATH_MAX
, "%s" CONF_DIR
"/timeout",
648 fp
= fopen(path
, "w");
650 bam_error(_("failed to open file: %s: %s\n"),
651 path
, strerror(errno
));
655 * timeout=-1 is to disable auto boot in illumos, but
656 * loader needs "NO" to disable auto boot.
659 rv
= fprintf(fp
, "autoboot_delay=\"NO\"\n");
661 rv
= fprintf(fp
, "autoboot_delay=\"%d\"\n", optval
);
664 bam_error(_("write to file failed: %s: %s\n"),
665 path
, strerror(errno
));
672 bam_error(_("failed to close file: %s: %s\n"),
673 path
, strerror(errno
));
676 if (ret
== BAM_ERROR
)
679 return (BAM_SUCCESS
);
682 bam_error(_("invalid option: %s\n"), opt
);
687 bam_mount_be(menu_entry_t
*entry
, char **dir
)
689 nvlist_t
*be_attrs
= NULL
;
690 const char *tmpdir
= getenv("TMPDIR");
691 const char *tmpname
= "bam.XXXXXX";
692 be_node_list_t
*be_node
, *be_nodes
= NULL
;
699 ret
= asprintf(dir
, "%s/%s", tmpdir
, tmpname
);
701 return (BE_ERR_NOMEM
);
703 *dir
= mkdtemp(*dir
);
705 if (nvlist_alloc(&be_attrs
, NV_UNIQUE_NAME
, 0) != 0) {
710 ret
= be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
);
711 if (ret
!= BE_SUCCESS
) {
715 for (be_node
= be_nodes
; be_node
;
716 be_node
= be_node
->be_next_node
)
717 if (strcmp(be_node
->be_root_ds
, entry
->me_bootfs
) == 0)
720 if (nvlist_add_string(be_attrs
, BE_ATTR_ORIG_BE_NAME
,
721 be_node
->be_node_name
) != 0) {
726 if (nvlist_add_string(be_attrs
, BE_ATTR_MOUNTPOINT
, *dir
) != 0) {
731 ret
= be_mount(be_attrs
);
732 if (ret
== BE_ERR_MOUNTED
) {
734 * if BE is mounted, dir does not point to correct directory
741 if (be_nodes
!= NULL
)
742 be_free_list(be_nodes
);
743 nvlist_free(be_attrs
);
748 bam_umount_be(char *dir
)
753 if (dir
== NULL
) /* nothing to do */
756 if (nvlist_alloc(&be_attrs
, NV_UNIQUE_NAME
, 0) != 0)
757 return (BE_ERR_NOMEM
);
759 if (nvlist_add_string(be_attrs
, BE_ATTR_ORIG_BE_NAME
, dir
) != 0) {
764 ret
= be_unmount(be_attrs
);
766 nvlist_free(be_attrs
);
771 * display details of menu entry or single property
774 list_menu_entry(menu_entry_t
*entry
, char *setting
)
776 int ret
= BAM_SUCCESS
;
782 if (strcmp(entry
->me_type
, "bootfs") != 0 ||
783 strchr(entry
->me_bootfs
, ':') != NULL
) {
784 (void) printf("\nTitle: %s\n", entry
->me_title
);
785 (void) printf("Type: %s\n", entry
->me_type
);
786 (void) printf("Device: %s\n", entry
->me_bootfs
);
790 mounted
= bam_mount_be(entry
, &dir
);
791 if (mounted
!= BE_SUCCESS
&& mounted
!= BE_ERR_MOUNTED
) {
796 bam_error(_("%s is not mounted\n"), entry
->me_title
);
800 vm
= bf_init("", ficlTextOutSilent
);
802 bam_error(_("error setting up forth interpreter\n"));
807 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */
808 (void) snprintf(buf
, MAX_INPUT
, "set currdev=zfs:%s:",
810 ret
= ficlVmEvaluate(vm
, buf
);
811 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
812 bam_error(_("error interpreting boot config\n"));
816 (void) snprintf(buf
, MAX_INPUT
, "include /boot/forth/loader.4th");
817 ret
= ficlVmEvaluate(vm
, buf
);
818 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
819 bam_error(_("error interpreting boot config\n"));
823 (void) snprintf(buf
, MAX_INPUT
, "start");
824 ret
= ficlVmEvaluate(vm
, buf
);
825 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
826 bam_error(_("error interpreting boot config\n"));
830 (void) snprintf(buf
, MAX_INPUT
, "boot");
831 ret
= ficlVmEvaluate(vm
, buf
);
832 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
833 bam_error(_("error interpreting boot config\n"));
839 if (*setting
== '\0')
840 (void) printf("\nTitle: %s\n", entry
->me_title
);
841 else if (strcasecmp(setting
, "title") == 0) {
842 (void) printf("%s\n", entry
->me_title
);
846 ptr
= getenv("autoboot_delay");
848 char *timeout
= "-1";
850 if (strcasecmp(ptr
, "NO") != 0)
853 if (*setting
== '\0')
854 (void) printf("Timeout: %s\n", timeout
);
855 else if (strcasecmp(setting
, "timeout") == 0) {
856 (void) printf("%s\n", timeout
);
861 ptr
= getenv("console");
863 if (*setting
== '\0')
864 (void) printf("Console: %s\n", ptr
);
865 else if (strcasecmp(setting
, "console") == 0) {
866 (void) printf("%s\n", ptr
);
871 if (*setting
== '\0')
872 (void) printf("Bootfs: %s\n", entry
->me_bootfs
);
873 else if (strcasecmp(setting
, "bootfs") == 0) {
874 (void) printf("%s\n", entry
->me_bootfs
);
878 ptr
= getenv("xen_kernel");
880 if (*setting
== '\0') {
881 (void) printf("Xen kernel: %s\n", ptr
);
882 } else if (strcasecmp(setting
, "xen_kernel") == 0) {
883 (void) printf("%s\n", ptr
);
887 if (*setting
== '\0') {
888 (void) printf("Xen args: \"%s\"\n",
889 getenv("xen_cmdline"));
890 } else if (strcasecmp(setting
, "xen_cmdline") == 0) {
891 (void) printf("%s\n", getenv("xen_cmdline"));
895 if (*setting
== '\0') {
896 (void) printf("Kernel: %s\n",
898 } if (strcasecmp(setting
, "kernel") == 0) {
899 (void) printf("%s\n", getenv("bootfile"));
903 ptr
= getenv("kernelname");
905 if (*setting
== '\0') {
906 (void) printf("Kernel: %s\n", ptr
);
907 } else if (strcasecmp(setting
, "kernel") == 0) {
908 (void) printf("%s\n", ptr
);
914 ptr
= getenv("boot-args");
916 if (*setting
== '\0') {
917 (void) printf("Boot-args: \"%s\"\n", ptr
);
918 } else if (strcasecmp(setting
, "boot-args") == 0) {
919 (void) printf("%s\n", ptr
);
924 if (*setting
== '\0' || strcasecmp(setting
, "modules") == 0) {
925 (void) printf("\nModules:\n");
926 ficlVmSetTextOut(vm
, ficlCallbackDefaultTextOut
);
927 (void) snprintf(buf
, MAX_INPUT
, "show-module-options");
928 ret
= ficlVmEvaluate(vm
, buf
);
929 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
930 bam_error(_("error interpreting boot config\n"));
938 /* if we got here with setting string, its unknown property */
939 if (*setting
!= '\0') {
940 bam_error(_("unknown property: %s\n"), setting
);
946 if (mounted
!= BE_ERR_MOUNTED
) {
947 (void) bam_umount_be(dir
);
960 list_entry(struct menu_lst
*menu
, char *menu_root
, char *opt
)
962 error_t ret
= BAM_SUCCESS
;
964 char *ptr
, *title
= NULL
;
968 print_nodes(B_FALSE
, menu
);
972 if ((ptr
= strchr(opt
, '=')) == NULL
) {
973 bam_error(_("invalid option: %s\n"), opt
);
978 if (strncmp(opt
, "entry", i
) == 0) {
980 } else if (strncmp(opt
, "title", i
) == 0) {
983 bam_error(_("invalid option: %s\n"), opt
);
987 STAILQ_FOREACH(entry
, menu
, me_next
) {
989 if (strcmp(title
, entry
->me_title
) == 0)
991 } else if (entry
->me_idx
== e
)
996 bam_error(_("no matching entry found\n"));
1000 return (list_menu_entry(entry
, ""));
1004 * For now this is just stub entry to support grub interface, the
1005 * known consumer is installer ict.py code, calling as:
1006 * bootadm update-menu -R /a -Z -o rdisk
1007 * Later this can be converted to do something useful.
1011 update_entry(struct menu_lst
*menu
, char *menu_root
, char *osdev
)
1013 char path
[PATH_MAX
];
1014 char *pool
= menu_root
+ 1;
1015 be_node_list_t
*be_nodes
, *be_node
;
1019 (void) snprintf(path
, PATH_MAX
, "%s%s", menu_root
, MENU
);
1020 rv
= be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
);
1022 if (rv
!= BE_SUCCESS
)
1025 fp
= fopen(path
, "w");
1027 be_free_list(be_nodes
);
1031 for (be_node
= be_nodes
; be_node
; be_node
= be_node
->be_next_node
) {
1032 if (strcmp(be_node
->be_rpool
, pool
) == 0) {
1033 (void) fprintf(fp
, "title %s\n", be_node
->be_node_name
);
1034 (void) fprintf(fp
, "bootfs %s\n", be_node
->be_root_ds
);
1038 be_free_list(be_nodes
);
1040 return (BAM_SUCCESS
);
1045 update_temp(struct menu_lst
*menu
, char *dummy
, char *opt
)
1047 error_t ret
= BAM_ERROR
;
1048 char path
[PATH_MAX
];
1049 char buf
[MAX_INPUT
];
1050 struct mnttab mpref
= { 0 };
1051 struct mnttab mp
= { 0 };
1056 (void) snprintf(path
, PATH_MAX
, "%s" TRANSIENT
, bam_root
);
1058 * if opt == NULL, remove transient config
1061 (void) unlink(path
);
1062 return (BAM_SUCCESS
);
1065 fp
= fopen(MNTTAB
, "r");
1069 mpref
.mnt_mountp
= "/";
1070 if (getmntany(fp
, &mp
, &mpref
) != 0) {
1076 vm
= bf_init("", ficlTextOutSilent
);
1078 bam_error(_("Error setting up forth interpreter\n"));
1083 * need to check current boot config, so fire up the ficl
1084 * if its xen setup, we add option to boot-args list, not replacing it.
1086 (void) snprintf(buf
, MAX_INPUT
, "set currdev=zfs:%s:", mp
.mnt_special
);
1087 ret
= ficlVmEvaluate(vm
, buf
);
1088 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1089 bam_error(_("Error interpreting boot config\n"));
1093 (void) snprintf(buf
, MAX_INPUT
, "include /boot/forth/loader.4th");
1094 ret
= ficlVmEvaluate(vm
, buf
);
1095 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1096 bam_error(_("Error interpreting boot config\n"));
1100 (void) snprintf(buf
, MAX_INPUT
, "start");
1101 ret
= ficlVmEvaluate(vm
, buf
);
1102 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1103 bam_error(_("Error interpreting boot config\n"));
1107 (void) snprintf(buf
, MAX_INPUT
, "boot");
1108 ret
= ficlVmEvaluate(vm
, buf
);
1109 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1110 bam_error(_("Error interpreting boot config\n"));
1116 if (opt
[0] == '-') {
1117 env
= getenv("xen_kernel");
1118 fp
= fopen(path
, "w");
1123 env
= getenv("boot-args");
1124 (void) fprintf(fp
, "boot-args=\"%s %s\"\n", env
, opt
);
1126 (void) fprintf(fp
, "boot-args=\"%s\"\n", opt
);
1128 return (BAM_SUCCESS
);
1132 * it should be the case with "kernel args"
1133 * so, we split the opt at first space
1134 * and store bootfile= and boot-args=
1136 env
= getenv("xen_kernel");
1138 o
= strchr(opt
, ' ');
1140 fp
= fopen(path
, "w");
1143 (void) fprintf(fp
, "bootfile=\"%s;unix\"\n", opt
);
1145 return (BAM_SUCCESS
);
1148 fp
= fopen(path
, "w");
1151 (void) fprintf(fp
, "bootfile=\"%s;unix\"\n", opt
);
1154 env
= getenv("boot-args");
1155 (void) fprintf(fp
, "boot-args=\"%s %s\"\n", env
, opt
);
1157 (void) fprintf(fp
, "boot-args=\"%s\"\n", o
);
1165 list_setting(struct menu_lst
*menu
, char *which
, char *setting
)
1169 be_node_list_t
*be_nodes
, *be_node
= NULL
;
1177 * "" - list default entry
1178 * number - use for entry number
1181 if (*which
!= '\0') {
1182 if (isdigit(*which
)) {
1185 entry
= strtol(which
, &rest
, 10);
1186 if (errno
!= 0 || *rest
!= '\0') {
1187 bam_error(_("invalid boot entry number: %s\n"),
1195 /* find default entry */
1197 ret
= be_list(NULL
, &be_nodes
, BE_LIST_DEFAULT
);
1198 if (ret
!= BE_SUCCESS
) {
1199 bam_error(_("No BE's found\n"));
1202 STAILQ_FOREACH(m
, menu
, me_next
) {
1204 for (be_node
= be_nodes
; be_node
;
1205 be_node
= be_node
->be_next_node
) {
1206 if (strcmp(be_node
->be_root_ds
,
1210 if (be_node
!= NULL
&&
1211 be_node
->be_active_on_boot
== B_TRUE
)
1212 break; /* found active node */
1214 be_free_list(be_nodes
);
1215 if (be_node
== NULL
) {
1216 bam_error(_("None of BE nodes is marked active\n"));
1220 STAILQ_FOREACH(m
, menu
, me_next
)
1221 if (m
->me_idx
== entry
)
1225 bam_error(_("no matching entry found\n"));
1230 return (list_menu_entry(m
, setting
));
1235 disable_hyper(struct menu_lst
*menu
, char *osroot
, char *opt
)
1237 char path
[PATH_MAX
];
1239 (void) snprintf(path
, PATH_MAX
, "%s" XEN_CONFIG
, bam_root
);
1240 (void) unlink(path
);
1241 return (BAM_SUCCESS
);
1246 enable_hyper(struct menu_lst
*menu
, char *osroot
, char *opt
)
1249 char path
[PATH_MAX
];
1250 char buf
[MAX_INPUT
];
1253 struct mnttab mpref
= { 0 };
1254 struct mnttab mp
= { 0 };
1257 fp
= fopen(MNTTAB
, "r");
1261 mpref
.mnt_mountp
= "/";
1262 if (getmntany(fp
, &mp
, &mpref
) != 0) {
1268 vm
= bf_init("", ficlTextOutSilent
);
1270 bam_error(_("Error setting up forth interpreter\n"));
1275 * need to check current boot config, so fire up the ficl
1276 * if its xen setup, we add option to boot-args list, not replacing it.
1278 (void) snprintf(buf
, MAX_INPUT
, "set currdev=zfs:%s:", mp
.mnt_special
);
1279 ret
= ficlVmEvaluate(vm
, buf
);
1280 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1281 bam_error(_("Error interpreting boot config\n"));
1285 (void) snprintf(buf
, MAX_INPUT
, "include /boot/forth/loader.4th");
1286 ret
= ficlVmEvaluate(vm
, buf
);
1287 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1288 bam_error(_("Error interpreting boot config\n"));
1292 (void) snprintf(buf
, MAX_INPUT
, "start");
1293 ret
= ficlVmEvaluate(vm
, buf
);
1294 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1295 bam_error(_("Error interpreting boot config\n"));
1299 (void) snprintf(buf
, MAX_INPUT
, "boot");
1300 ret
= ficlVmEvaluate(vm
, buf
);
1301 if (ret
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
1302 bam_error(_("Error interpreting boot config\n"));
1308 (void) mkdir(CONF_DIR
, 0755);
1309 (void) snprintf(path
, PATH_MAX
, "%s" XEN_CONFIG
, bam_root
);
1310 fp
= fopen(path
, "w");
1312 return (BAM_ERROR
); /* error, cant write config */
1317 * on write error, remove file to ensure we have bootable config.
1318 * note we dont mind if config exists, it will get updated
1320 (void) fprintf(fp
, "xen_kernel=\"/boot/${ISADIR}/xen\"\n");
1325 * really simple and stupid console conversion.
1326 * it really has to be gone, it belongs to milestone/xvm properties.
1328 env
= getenv("console");
1330 if (strcmp(env
, "ttya") == 0)
1331 (void) fprintf(fp
, "xen_cmdline=\"console=com1 %s\"\n",
1333 else if (strcmp(env
, "ttyb") == 0)
1334 (void) fprintf(fp
, "xen_cmdline=\"console=com2 %s\"\n",
1337 (void) fprintf(fp
, "xen_cmdline=\"console=vga %s\"\n",
1340 (void) fprintf(fp
, "xen_cmdline=\"%s\"\n", opt
);
1345 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1350 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n");
1356 (void) unlink(path
);
1359 return (BAM_SUCCESS
);
1362 (void) unlink(path
);