diff options
| author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-09-26 13:27:55 +0000 |
|---|---|---|
| committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-09-26 13:27:55 +0000 |
| commit | 0264fb65b613e5b2c44f273fa48b26bebe491074 (patch) | |
| tree | a375b79dd7543bcf0571578b2189d37168c37c57 /usr/src/cmd/boot/bootadm/bootadm_loader.c | |
| parent | d21e83058c8edeb1becd9202380d088cb056f0c4 (diff) | |
| parent | f76886de6cd6914424d9f6c25bd9d93d87889269 (diff) | |
| download | illumos-joyent-0264fb65b613e5b2c44f273fa48b26bebe491074.tar.gz | |
[illumos-gate merge]
commit f76886de6cd6914424d9f6c25bd9d93d87889269
7402 Create tunable to ignore hole_birth feature
commit 5bdf86e2a288d6c81a0bcc50a98699f52557bab6
7401 loader.4th is missing newline
commit ed4e7a6a5cbc5e8986dc649ad54435210487b102
7340 receive manual origin should override automatic origin
commit b021ac0b78f8df3d9c421783d9a323723df84925
7337 inherit_001_pos occasionally times out
commit c166b69d29138aed7a415fe7cef698e54c6ae945
7254 ztest failed assertion in ztest_dataset_dirobj_verify: dirobjs + 1 == usedobjs
commit 754998c8d410b7b7ddefbfa4de310a030e0c7ce1
7253 ztest failure: dsl_destroy_head(name) == 0 (0x10 == 0x0), file ../ztest.c, line 3235
commit 4220fdc152e5dfec9a1dd51452175295f3684689
7398 zfs test zfs_get_005_neg does not work as expected
commit d5f26ad8122c3762fb16413a17bfb497db86a782
5142 libzfs support raidz root pool (loader project)
commit c8811bd3e2427dddbac6c05a59cfe117d8fea370
5120 zfs should allow large block/gzip/raidz boot pool (loader project)
commit 12e3fba22ec759e9dd8f9564fad79541275b2aa5
6709 manual pages need to be updated for loader (loader project)
commit fa0c327afe484fa5ff164fb81ff93715dd6573f8
6706 disable grub menu management in bootadm (loader project)
6707 disable grub menu management in libbe (loader project)
commit 9abc7a578aecf9064f46563361e8f856b4bdc35e
6705 halt: replace grub_get_boot_args with be_get_boot_args (loader project)
commit a6424c753d6e2f0f04fb65b11e9f9c04445ccbae
6704 svc.startd: replace grub_get_boot_args with be_get_boot_args (loader project)
commit c262cbbc8301f7c884fd4800056ee51ba75d931c
6703 update bootadm to support loader configuration (loader project)
6708 update eeprom for loader (loader project)
commit ce3cb817f67103796b730fd322174dddefd9a9a8
6702 libbe should support x86 installboot command (loader project)
commit 0c946d80993858b7b1314e0b31773e48500e03fb
6701 add installboot to i386 platform (loader project)
commit 1386b601c0c7f5c89a9325b8a1e34037304e8119
6700 remove installboot script from i386 platform (loader project)
commit f5e5a2c4965aa1013184568ca3140cdcba93e44b
6699 be_get_boot_args interface implementation in libbe (loader project)
commit 199767f8919635c4928607450d9e0abb932109ce
5061 freebsd boot loader integration (loader project)
commit 0cc5983c8a077e6396dc7c492ee928b40bf0fed1
6698 freebsd btxld port for illumos (loader project)
commit afc2ba1deb75b323afde536f2dd18bcafdaa308d
6185 want ficl scripting engine in illumos (loader project)
Conflicts:
exception_lists/cstyle
exception_lists/hdrchk
exception_lists/copyright
Diffstat (limited to 'usr/src/cmd/boot/bootadm/bootadm_loader.c')
| -rw-r--r-- | usr/src/cmd/boot/bootadm/bootadm_loader.c | 1329 |
1 files changed, 1329 insertions, 0 deletions
diff --git a/usr/src/cmd/boot/bootadm/bootadm_loader.c b/usr/src/cmd/boot/bootadm/bootadm_loader.c new file mode 100644 index 0000000000..2c7382a11c --- /dev/null +++ b/usr/src/cmd/boot/bootadm/bootadm_loader.c @@ -0,0 +1,1329 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Milan Jurik. All rights reserved. + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2016 Toomas Soome <tsoome@me.com> + */ + +/* + * Loader menu management. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <errno.h> +#include <limits.h> +#include <alloca.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/queue.h> +#include <libbe.h> +#include <ficl.h> +#include <ficlplatform/emu.h> + +#include "bootadm.h" + +extern int bam_rootlen; +extern int bam_alt_root; +extern char *rootbuf; +extern char *bam_root; + +#define BOOT_DIR "/boot" +#define CONF_DIR BOOT_DIR "/conf.d" +#define MENU BOOT_DIR "/menu.lst" +#define TRANSIENT BOOT_DIR "/transient.conf" +#define XEN_CONFIG CONF_DIR "/xen" + +struct menu_entry { + int entry; + char *title; + char *bootfs; + STAILQ_ENTRY(menu_entry) next; +}; +STAILQ_HEAD(menu_lst, menu_entry); + +static error_t set_option(struct menu_lst *, char *, char *); +static error_t list_entry(struct menu_lst *, char *, char *); +static error_t update_entry(struct menu_lst *, char *, char *); +static error_t update_temp(struct menu_lst *, char *, char *); +static error_t list_setting(struct menu_lst *menu, char *, char *); +static error_t disable_hyper(struct menu_lst *, char *, char *); +static error_t enable_hyper(struct menu_lst *, char *, char *); + +/* Menu related sub commands */ +static subcmd_defn_t menu_subcmds[] = { + "set_option", OPT_ABSENT, set_option, 0, /* PUB */ + "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */ + "update_entry", OPT_REQ, update_entry, 0, /* menu */ + "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */ + "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */ + "disable_hypervisor", OPT_ABSENT, disable_hyper, 0, /* menu */ + "enable_hypervisor", OPT_ABSENT, enable_hyper, 0, /* menu */ + NULL, 0, NULL, 0 /* must be last */ +}; + +#define NUM_COLS (4) +struct col_info { + const char *col_name; + size_t width; +}; + +/* + * all columns output format + */ +struct hdr_info { + struct col_info cols[NUM_COLS]; +}; + +static void +print_hdr(struct hdr_info *hdr_info) +{ + boolean_t first = B_TRUE; + size_t i; + + for (i = 0; i < NUM_COLS; i++) { + struct col_info *col_info = &hdr_info->cols[i]; + const char *name = col_info->col_name; + size_t width = col_info->width; + + if (name == NULL) + continue; + + if (first) { + (void) printf("%-*s", width, name); + first = B_FALSE; + } else + (void) printf(" %-*s", width, name); + } + (void) putchar('\n'); +} + +static void +init_hdr_cols(struct hdr_info *hdr) +{ + struct col_info *col = hdr->cols; + size_t i; + + col[0].col_name = _("Index"); + col[1].col_name = _("Default"); + col[2].col_name = _("Dataset"); + col[3].col_name = _("Menu"); + col[4].col_name = NULL; + + for (i = 0; i < NUM_COLS; i++) { + const char *name = col[i].col_name; + col[i].width = 0; + + if (name != NULL) { + wchar_t wname[128]; + size_t sz = mbstowcs(wname, name, sizeof (wname) / + sizeof (wchar_t)); + if (sz > 0) { + int wcsw = wcswidth(wname, sz); + if (wcsw > 0) + col[i].width = wcsw; + else + col[i].width = sz; + } else { + col[i].width = strlen(name); + } + } + } +} + +static void +count_widths(struct hdr_info *hdr, struct menu_lst *menu) +{ + size_t len[NUM_COLS]; + struct menu_entry *entry; + int i; + + for (i = 0; i < NUM_COLS; i++) + len[i] = hdr->cols[i].width + 1; + + STAILQ_FOREACH(entry, menu, next) { + size_t bootfs_len = strlen(entry->bootfs); + if (bootfs_len > len[2]) + len[2] = bootfs_len + 1; + } + + for (i = 0; i < NUM_COLS; i++) + hdr->cols[i].width = len[i]; +} + +static void +print_menu_nodes(boolean_t parsable, struct hdr_info *hdr, + struct menu_lst *menu) +{ + struct menu_entry *entry; + int i = -1; + int rv; + be_node_list_t *be_nodes, *be_node; + + rv = be_list(NULL, &be_nodes); + if (rv != BE_SUCCESS) + return; + + STAILQ_FOREACH(entry, menu, next) { + i++; + for (be_node = be_nodes; be_node; + be_node = be_node->be_next_node) + if (strcmp(be_node->be_root_ds, entry->bootfs) == 0) + break; + + if (parsable) + (void) printf("%d;%s;%s;%s\n", i, + be_node->be_active_on_boot == B_TRUE? "*" : "-", + entry->bootfs, entry->title); + else + (void) printf("%-*d %-*s %-*s %-*s\n", + hdr->cols[0].width, i, + hdr->cols[1].width, + be_node->be_active_on_boot == B_TRUE? "*" : "-", + hdr->cols[2].width, entry->bootfs, + hdr->cols[3].width, entry->title); + } + be_free_list(be_nodes); +} + +static void +print_nodes(boolean_t parsable, struct menu_lst *menu) +{ + struct hdr_info hdr; + + if (!parsable) { + init_hdr_cols(&hdr); + count_widths(&hdr, menu); + print_hdr(&hdr); + } + + print_menu_nodes(parsable, &hdr, menu); +} + +error_t +menu_read(struct menu_lst *menu, char *menu_path) +{ + FILE *fp; + struct menu_entry *mp; + char buf[PATH_MAX]; + char *title; + char *bootfs; + char *ptr; + int i = 0; + int ret = BAM_SUCCESS; + + fp = fopen(menu_path, "r"); + if (fp == NULL) + return (BAM_ERROR); + + /* + * menu.lst entry is on two lines, one for title, one for bootfs + * so we process both lines in succession. + */ + do { + if (fgets(buf, PATH_MAX, fp) == NULL) { + if (!feof(fp)) + ret = BAM_ERROR; + (void) fclose(fp); + return (ret); + } + ptr = strchr(buf, '\n'); + if (ptr != NULL) + *ptr = '\0'; + + ptr = strchr(buf, ' '); + if (ptr == NULL) { + (void) fclose(fp); + return (BAM_ERROR); + } + *ptr++ = '\0'; + if (strcmp(buf, "title") != 0) { + (void) fclose(fp); + return (BAM_ERROR); + } + if ((title = strdup(ptr)) == NULL) { + (void) fclose(fp); + return (BAM_ERROR); + } + + if (fgets(buf, PATH_MAX, fp) == NULL) { + free(title); + (void) fclose(fp); + return (BAM_ERROR); + } + + ptr = strchr(buf, '\n'); + if (ptr != NULL) + *ptr = '\0'; + + ptr = strchr(buf, ' '); + if (ptr == NULL) { + free(title); + (void) fclose(fp); + return (BAM_ERROR); + } + *ptr++ = '\0'; + if (strcmp(buf, "bootfs") != 0) { + free(title); + (void) fclose(fp); + return (BAM_ERROR); + } + if ((bootfs = strdup(ptr)) == NULL) { + free(title); + (void) fclose(fp); + return (BAM_ERROR); + } + if ((mp = malloc(sizeof (struct menu_entry))) == NULL) { + free(title); + free(bootfs); + (void) fclose(fp); + return (BAM_ERROR); + } + mp->entry = i++; + mp->title = title; + mp->bootfs = bootfs; + STAILQ_INSERT_TAIL(menu, mp, next); + } while (feof(fp) == 0); + + (void) fclose(fp); + return (ret); +} + +void +menu_free(struct menu_lst *menu) +{ + struct menu_entry *entry; + STAILQ_FOREACH(entry, menu, next) { + STAILQ_REMOVE_HEAD(menu, next); + free(entry->title); + free(entry->bootfs); + free(entry); + } +} + +error_t +bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[]) +{ + error_t ret; + char menu_path[PATH_MAX]; + char clean_menu_root[PATH_MAX]; + char menu_root[PATH_MAX]; + struct stat sb; + error_t (*f)(struct menu_lst *, char *, char *); + char *special; + char *pool = NULL; + zfs_mnted_t zmnted; + char *zmntpt; + char *osdev; + char *osroot; + const char *fcn = "bam_loader_menu()"; + struct menu_lst menu = {0}; + + STAILQ_INIT(&menu); + + /* + * Check arguments + */ + ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f); + if (ret == BAM_ERROR) { + return (BAM_ERROR); + } + + assert(bam_root); + + (void) strlcpy(menu_root, bam_root, sizeof (menu_root)); + osdev = osroot = NULL; + + if (strcmp(subcmd, "update_entry") == 0) { + assert(opt); + + osdev = strtok(opt, ","); + assert(osdev); + osroot = strtok(NULL, ","); + if (osroot) { + /* fixup bam_root so that it points at osroot */ + if (realpath(osroot, rootbuf) == NULL) { + bam_error(_("cannot resolve path %s: %s\n"), + osroot, strerror(errno)); + return (BAM_ERROR); + } + bam_alt_root = 1; + bam_root = rootbuf; + bam_rootlen = strlen(rootbuf); + } + } + + if (stat(menu_root, &sb) == -1) { + bam_error(_("cannot find menu\n")); + return (BAM_ERROR); + } + + if (!is_zfs(menu_root)) { + bam_error(_("only ZFS root is supported\n")); + return (BAM_ERROR); + } + + assert(strcmp(menu_root, bam_root) == 0); + special = get_special(menu_root); + INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL); + if (special == NULL) { + bam_error(_("cant find special file for mount-point %s\n"), + menu_root); + return (BAM_ERROR); + } + pool = strtok(special, "/"); + INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL); + if (pool == NULL) { + free(special); + bam_error(_("cant find pool for mount-point %s\n"), menu_root); + return (BAM_ERROR); + } + BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool)); + + zmntpt = mount_top_dataset(pool, &zmnted); + INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL); + if (zmntpt == NULL) { + bam_error(_("cannot mount pool dataset for pool: %s\n"), pool); + free(special); + return (BAM_ERROR); + } + BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt)); + + (void) strlcpy(menu_root, zmntpt, sizeof (menu_root)); + BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root)); + + elide_trailing_slash(menu_root, clean_menu_root, + sizeof (clean_menu_root)); + + BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root)); + + (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path)); + (void) strlcat(menu_path, MENU, sizeof (menu_path)); + + BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path)); + + /* + * update_entry is special case, its used by installer + * and needs to create menu.lst file for loader + */ + if (menu_read(&menu, menu_path) == BAM_ERROR && + strcmp(subcmd, "update_entry") != 0) { + bam_error(_("cannot find menu file: %s\n"), menu_path); + if (special != NULL) + free(special); + return (BAM_ERROR); + } + + /* + * If listing the menu, display the menu location + */ + if (strcmp(subcmd, "list_entry") == 0) + bam_print(_("the location for the active menu is: %s\n"), + menu_path); + + /* + * We already checked the following case in + * check_subcmd_and_suboptions() above. Complete the + * final step now. + */ + if (strcmp(subcmd, "set_option") == 0) { + assert(largc == 1 && largv[0] && largv[1] == NULL); + opt = largv[0]; + } else if ((strcmp(subcmd, "enable_hypervisor") != 0) && + (strcmp(subcmd, "list_setting") != 0)) { + assert(largc == 0 && largv == NULL); + } + + /* + * Once the sub-cmd handler has run + * only the line field is guaranteed to have valid values + */ + if (strcmp(subcmd, "update_entry") == 0) { + ret = f(&menu, menu_root, osdev); + } else if (strcmp(subcmd, "upgrade") == 0) { + ret = f(&menu, bam_root, menu_root); + } else if (strcmp(subcmd, "list_entry") == 0) { + ret = f(&menu, menu_path, opt); + } else if (strcmp(subcmd, "list_setting") == 0) { + ret = f(&menu, ((largc > 0) ? largv[0] : ""), + ((largc > 1) ? largv[1] : "")); + } else if (strcmp(subcmd, "disable_hypervisor") == 0) { + if (is_sparc()) { + bam_error(_("%s operation unsupported on SPARC " + "machines\n"), subcmd); + ret = BAM_ERROR; + } else { + ret = f(&menu, bam_root, NULL); + } + } else if (strcmp(subcmd, "enable_hypervisor") == 0) { + if (is_sparc()) { + bam_error(_("%s operation unsupported on SPARC " + "machines\n"), subcmd); + ret = BAM_ERROR; + } else { + char *extra_args = NULL; + + /* + * Compress all arguments passed in the largv[] array + * into one string that can then be appended to the + * end of the kernel$ string the routine to enable the + * hypervisor will build. + * + * This allows the caller to supply arbitrary unparsed + * arguments, such as dom0 memory settings or APIC + * options. + * + * This concatenation will be done without ANY syntax + * checking whatsoever, so it's the responsibility of + * the caller to make sure the arguments are valid and + * do not duplicate arguments the conversion routines + * may create. + */ + if (largc > 0) { + int extra_len, i; + + for (extra_len = 0, i = 0; i < largc; i++) + extra_len += strlen(largv[i]); + + /* + * Allocate space for argument strings, + * intervening spaces and terminating NULL. + */ + extra_args = alloca(extra_len + largc); + + (void) strcpy(extra_args, largv[0]); + + for (i = 1; i < largc; i++) { + (void) strcat(extra_args, " "); + (void) strcat(extra_args, largv[i]); + } + } + + ret = f(&menu, bam_root, extra_args); + } + } else + ret = f(&menu, NULL, opt); + + if (ret == BAM_WRITE) { + BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n", + fcn, clean_menu_root)); + /* ret = menu_write(clean_menu_root, menu); */ + } + + INJECT_ERROR1("POOL_SET", pool = "/pooldata"); + assert((is_zfs(menu_root)) ^ (pool == NULL)); + if (pool) { + (void) umount_top_dataset(pool, zmnted, zmntpt); + free(special); + } + + menu_free(&menu); + return (ret); +} + +/* + * To suppress output from ficl. We do not want to see messages + * from interpreting loader config. + */ + +/*ARGSUSED*/ +static void +ficlTextOutSilent(ficlCallback *cb, char *text) +{ +} + +/*ARGSUSED*/ +static error_t +set_option(struct menu_lst *menu, char *dummy, char *opt) +{ + char path[PATH_MAX]; + char *val; + char *rest; + int optval; + struct menu_entry *entry; + nvlist_t *be_attrs; + FILE *fp; + int rv, ret = BAM_SUCCESS; + + assert(menu); + assert(opt); + assert(dummy == NULL); + + val = strchr(opt, '='); + if (val != NULL) { + *val++ = '\0'; + } + + if (strcmp(opt, "default") == 0) { + errno = 0; + optval = strtol(val, &rest, 10); + if (errno != 0 || *rest != '\0') { + bam_error(_("invalid boot entry number: %s\n"), val); + return (BAM_ERROR); + } + STAILQ_FOREACH(entry, menu, next) { + if (entry->entry == optval) + break; + } + if (entry == NULL) { + bam_error(_("invalid boot entry number: %s\n"), val); + return (BAM_ERROR); + } + if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) { + bam_error(_("out of memory\n")); + return (BAM_ERROR); + } + if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, + entry->title) != 0) { + bam_error(_("out of memory\n")); + nvlist_free(be_attrs); + return (BAM_ERROR); + } + ret = be_activate(be_attrs); + nvlist_free(be_attrs); + if (ret != 0) + ret = BAM_ERROR; + return (ret); + } else if (strcmp(opt, "timeout") == 0) { + errno = 0; + optval = strtol(val, &rest, 10); + if (errno != 0 || *rest != '\0') { + bam_error(_("invalid timeout: %s\n"), val); + return (BAM_ERROR); + } + + (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout", + bam_root); + + fp = fopen(path, "w"); + if (fp == NULL) { + bam_error(_("failed to open file: %s: %s\n"), + path, strerror(errno)); + return (BAM_ERROR); + } + /* + * timeout=-1 is to disable auto boot in illumos, but + * loader needs "NO" to disable auto boot. + */ + if (optval == -1) + rv = fprintf(fp, "autoboot_delay=\"NO\"\n"); + else + rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval); + + if (rv < 0) { + bam_error(_("write to file failed: %s: %s\n"), + path, strerror(errno)); + (void) fclose(fp); + ret = BAM_ERROR; + } else + rv = fclose(fp); + + if (rv < 0) { + bam_error(_("failed to close file: %s: %s\n"), + path, strerror(errno)); + ret = BAM_ERROR; + } + if (ret == BAM_ERROR) + (void) unlink(path); + + return (BAM_SUCCESS); + } + + bam_error(_("invalid option: %s\n"), opt); + return (BAM_ERROR); +} + +static int +bam_mount_be(struct menu_entry *entry, char **dir) +{ + nvlist_t *be_attrs = NULL; + const char *tmpdir = getenv("TMPDIR"); + const char *tmpname = "bam.XXXXXX"; + be_node_list_t *be_node, *be_nodes = NULL; + int ret; + + *dir = NULL; + if (tmpdir == NULL) + tmpdir = "/tmp"; + + ret = asprintf(dir, "%s/%s", tmpdir, tmpname); + if (ret < 0) { + return (BE_ERR_NOMEM); + } + *dir = mkdtemp(*dir); + + if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) { + ret = BE_ERR_NOMEM; + goto out; + } + + ret = be_list(NULL, &be_nodes); + if (ret != BE_SUCCESS) { + goto out; + } + + for (be_node = be_nodes; be_node; + be_node = be_node->be_next_node) + if (strcmp(be_node->be_root_ds, entry->bootfs) == 0) + break; + + if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, + be_node->be_node_name) != 0) { + ret = BE_ERR_NOMEM; + goto out; + } + + if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) { + ret = BE_ERR_NOMEM; + goto out; + } + + ret = be_mount(be_attrs); + if (ret == BE_ERR_MOUNTED) { + /* + * if BE is mounted, dir does not point to correct directory + */ + (void) rmdir(*dir); + free(*dir); + *dir = NULL; + } +out: + if (be_nodes != NULL) + be_free_list(be_nodes); + nvlist_free(be_attrs); + return (ret); +} + +static int +bam_umount_be(char *dir) +{ + nvlist_t *be_attrs; + int ret; + + if (dir == NULL) /* nothing to do */ + return (BE_SUCCESS); + + if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) + return (BE_ERR_NOMEM); + + if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) { + ret = BE_ERR_NOMEM; + goto out; + } + + ret = be_unmount(be_attrs); +out: + nvlist_free(be_attrs); + return (ret); +} + +/* + * display details of menu entry or single property + */ +static error_t +list_menu_entry(struct menu_entry *entry, char *setting) +{ + int ret = BAM_SUCCESS; + char *ptr, *dir; + char buf[MAX_INPUT]; + ficlVm *vm; + int mounted; + + mounted = bam_mount_be(entry, &dir); + if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) { + if (dir != NULL) { + (void) rmdir(dir); + free(dir); + } + bam_error(_("%s is not mounted\n"), entry->title); + return (BAM_ERROR); + } + + vm = bf_init("", ficlTextOutSilent); + if (vm == NULL) { + bam_error(_("error setting up forth interpreter\n")); + ret = BAM_ERROR; + goto done; + } + + /* should only get FICL_VM_STATUS_OUT_OF_TEXT */ + (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", + entry->bootfs); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("error interpreting boot config\n")); + ret = BAM_ERROR; + goto done; + } + (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("error interpreting boot config\n")); + ret = BAM_ERROR; + goto done; + } + (void) snprintf(buf, MAX_INPUT, "start"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("error interpreting boot config\n")); + ret = BAM_ERROR; + goto done; + } + (void) snprintf(buf, MAX_INPUT, "boot"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("error interpreting boot config\n")); + ret = BAM_ERROR; + goto done; + } + + ret = BAM_SUCCESS; + if (*setting == '\0') + (void) printf("\nTitle: %s\n", entry->title); + else if (strcasecmp(setting, "title") == 0) { + (void) printf("%s\n", entry->title); + goto done; + } + + ptr = getenv("autoboot_delay"); + if (ptr != NULL) { + char *timeout = "-1"; + + if (strcasecmp(ptr, "NO") != 0) + timeout = ptr; + + if (*setting == '\0') + (void) printf("Timeout: %s\n", timeout); + else if (strcasecmp(setting, "timeout") == 0) { + (void) printf("%s\n", timeout); + goto done; + } + + } + ptr = getenv("console"); + if (ptr != NULL) { + if (*setting == '\0') + (void) printf("Console: %s\n", ptr); + else if (strcasecmp(setting, "console") == 0) { + (void) printf("%s\n", ptr); + goto done; + } + } + + if (*setting == '\0') + (void) printf("Bootfs: %s\n", entry->bootfs); + else if (strcasecmp(setting, "bootfs") == 0) { + (void) printf("%s\n", entry->bootfs); + goto done; + } + + ptr = getenv("xen_kernel"); + if (ptr != NULL) { + if (*setting == '\0') { + (void) printf("Xen kernel: %s\n", ptr); + } else if (strcasecmp(setting, "xen_kernel") == 0) { + (void) printf("%s\n", ptr); + goto done; + } + + if (*setting == '\0') { + (void) printf("Xen args: \"%s\"\n", + getenv("xen_cmdline")); + } else if (strcasecmp(setting, "xen_cmdline") == 0) { + (void) printf("%s\n", getenv("xen_cmdline")); + goto done; + } + + if (*setting == '\0') { + (void) printf("Kernel: %s\n", + getenv("bootfile")); + } if (strcasecmp(setting, "kernel") == 0) { + (void) printf("%s\n", getenv("bootfile")); + goto done; + } + } else { + ptr = getenv("kernelname"); + if (ptr != NULL) { + if (*setting == '\0') { + (void) printf("Kernel: %s\n", ptr); + } else if (strcasecmp(setting, "kernel") == 0) { + (void) printf("%s\n", ptr); + goto done; + } + } + } + + ptr = getenv("boot-args"); + if (ptr != NULL) { + if (*setting == '\0') { + (void) printf("Boot-args: \"%s\"\n", ptr); + } else if (strcasecmp(setting, "boot-args") == 0) { + (void) printf("%s\n", ptr); + goto done; + } + } + + if (*setting == '\0' || strcasecmp(setting, "modules") == 0) { + (void) printf("\nModules:\n"); + ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut); + (void) snprintf(buf, MAX_INPUT, "show-module-options"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("error interpreting boot config\n")); + ret = BAM_ERROR; + goto done; + } + ret = BAM_SUCCESS; + goto done; + } + + /* if we got here with setting string, its unknown property */ + if (*setting != '\0') { + bam_error(_("unknown property: %s\n"), setting); + ret = BAM_ERROR; + } else + ret = BAM_SUCCESS; +done: + bf_fini(); + if (mounted != BE_ERR_MOUNTED) { + (void) bam_umount_be(dir); + } + + if (dir != NULL) { + (void) rmdir(dir); + free(dir); + } + + return (ret); +} + +/*ARGSUSED*/ +static error_t +list_entry(struct menu_lst *menu, char *menu_root, char *opt) +{ + error_t ret = BAM_SUCCESS; + struct menu_entry *entry; + char *ptr, *title = NULL; + int i, e = -1; + + if (opt == NULL) { + print_nodes(B_FALSE, menu); + return (ret); + } + + if ((ptr = strchr(opt, '=')) == NULL) { + bam_error(_("invalid option: %s\n"), opt); + return (BAM_ERROR); + } + + i = ptr - opt; + if (strncmp(opt, "entry", i) == 0) { + e = atoi(ptr+1); + } else if (strncmp(opt, "title", i) == 0) { + title = ptr+1; + } else { + bam_error(_("invalid option: %s\n"), opt); + return (BAM_ERROR); + } + + STAILQ_FOREACH(entry, menu, next) { + if (title != NULL) { + if (strcmp(title, entry->title) == 0) + break; + } else if (entry->entry == e) + break; + } + + if (entry == NULL) { + bam_error(_("no matching entry found\n")); + return (BAM_ERROR); + } + + return (list_menu_entry(entry, "")); +} + +/* + * For now this is just stub entry to support grub interface, the + * known consumer is installer ict.py code, calling as: + * bootadm update-menu -R /a -Z -o rdisk + * Later this can be converted to do something useful. + */ +/*ARGSUSED*/ +static error_t +update_entry(struct menu_lst *menu, char *menu_root, char *osdev) +{ + char path[PATH_MAX]; + char *pool = menu_root + 1; + be_node_list_t *be_nodes, *be_node; + int rv; + FILE *fp; + + (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU); + rv = be_list(NULL, &be_nodes); + + if (rv != BE_SUCCESS) + return (BAM_ERROR); + + fp = fopen(path, "w"); + if (fp == NULL) { + be_free_list(be_nodes); + return (BAM_ERROR); + } + + for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) { + if (strcmp(be_node->be_rpool, pool) == 0) { + (void) fprintf(fp, "title %s\n", be_node->be_node_name); + (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds); + } + } + + be_free_list(be_nodes); + (void) fclose(fp); + return (BAM_SUCCESS); +} + +/*ARGSUSED*/ +static error_t +update_temp(struct menu_lst *menu, char *dummy, char *opt) +{ + error_t ret = BAM_ERROR; + char path[PATH_MAX]; + char buf[MAX_INPUT]; + struct mnttab mpref = { 0 }; + struct mnttab mp = { 0 }; + ficlVm *vm; + char *env, *o; + FILE *fp; + + (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root); + /* + * if opt == NULL, remove transient config + */ + if (opt == NULL) { + (void) unlink(path); + return (BAM_SUCCESS); + } + + fp = fopen(MNTTAB, "r"); + if (fp == NULL) + return (BAM_ERROR); + + mpref.mnt_mountp = "/"; + if (getmntany(fp, &mp, &mpref) != 0) { + (void) fclose(fp); + return (BAM_ERROR); + } + (void) fclose(fp); + + vm = bf_init("", ficlTextOutSilent); + if (vm == NULL) { + bam_error(_("Error setting up forth interpreter\n")); + return (ret); + } + + /* + * need to check current boot config, so fire up the ficl + * if its xen setup, we add option to boot-args list, not replacing it. + */ + (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + (void) snprintf(buf, MAX_INPUT, "start"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + (void) snprintf(buf, MAX_INPUT, "boot"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + bf_fini(); + + if (opt[0] == '-') { + env = getenv("xen_kernel"); + fp = fopen(path, "w"); + if (fp == NULL) + return (BAM_ERROR); + + if (env != NULL) { + env = getenv("boot-args"); + (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt); + } else + (void) fprintf(fp, "boot-args=\"%s\"\n", opt); + (void) fclose(fp); + return (BAM_SUCCESS); + } + + /* + * it should be the case with "kernel args" + * so, we split the opt at first space + * and store bootfile= and boot-args= + */ + env = getenv("xen_kernel"); + + o = strchr(opt, ' '); + if (o == NULL) { + fp = fopen(path, "w"); + if (fp == NULL) + return (BAM_ERROR); + (void) fprintf(fp, "bootfile=\"%s\"\n", opt); + (void) fclose(fp); + return (BAM_SUCCESS); + } + *o++ = '\0'; + fp = fopen(path, "w"); + if (fp == NULL) + return (BAM_ERROR); + (void) fprintf(fp, "bootfile=\"%s\"\n", opt); + + if (env != NULL) { + env = getenv("boot-args"); + (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt); + } else + (void) fprintf(fp, "boot-args=\"%s\"\n", o); + + (void) fflush(fp); + (void) fclose(fp); + return (ret); +} + +static error_t +list_setting(struct menu_lst *menu, char *which, char *setting) +{ + int entry = -1; + struct menu_entry *m; + be_node_list_t *be_nodes, *be_node = NULL; + int ret; + + assert(which); + assert(setting); + + /* + * which can be: + * "" - list default entry + * number - use for entry number + * property name + */ + if (*which != '\0') { + if (isdigit(*which)) { + char *rest; + errno = 0; + entry = strtol(which, &rest, 10); + if (errno != 0 || *rest != '\0') { + bam_error(_("invalid boot entry number: %s\n"), + which); + return (BAM_ERROR); + } + } else + setting = which; + } + + /* find default entry */ + if (entry == -1) { + ret = be_list(NULL, &be_nodes); + if (ret != BE_SUCCESS) { + bam_error(_("No BE's found\n")); + return (BAM_ERROR); + } + STAILQ_FOREACH(m, menu, next) { + entry++; + for (be_node = be_nodes; be_node; + be_node = be_node->be_next_node) + if (strcmp(be_node->be_root_ds, m->bootfs) == 0) + break; + if (be_node->be_active_on_boot == B_TRUE) + break; /* found active node */ + } + be_free_list(be_nodes); + if (be_node == NULL) { + bam_error(_("None of BE nodes is marked active\n")); + return (BAM_ERROR); + } + } else { + STAILQ_FOREACH(m, menu, next) + if (m->entry == entry) + break; + + if (m == NULL) { + bam_error(_("no matching entry found\n")); + return (BAM_ERROR); + } + } + + return (list_menu_entry(m, setting)); +} + +/*ARGSUSED*/ +static error_t +disable_hyper(struct menu_lst *menu, char *osroot, char *opt) +{ + char path[PATH_MAX]; + + (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root); + (void) unlink(path); + return (BAM_SUCCESS); +} + +/*ARGSUSED*/ +static error_t +enable_hyper(struct menu_lst *menu, char *osroot, char *opt) +{ + ficlVm *vm; + char path[PATH_MAX]; + char buf[MAX_INPUT]; + char *env; + FILE *fp; + struct mnttab mpref = { 0 }; + struct mnttab mp = { 0 }; + int ret; + + fp = fopen(MNTTAB, "r"); + if (fp == NULL) + return (BAM_ERROR); + + mpref.mnt_mountp = "/"; + if (getmntany(fp, &mp, &mpref) != 0) { + (void) fclose(fp); + return (BAM_ERROR); + } + (void) fclose(fp); + + vm = bf_init("", ficlTextOutSilent); + if (vm == NULL) { + bam_error(_("Error setting up forth interpreter\n")); + return (BAM_ERROR); + } + + /* + * need to check current boot config, so fire up the ficl + * if its xen setup, we add option to boot-args list, not replacing it. + */ + (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + (void) snprintf(buf, MAX_INPUT, "start"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + (void) snprintf(buf, MAX_INPUT, "boot"); + ret = ficlVmEvaluate(vm, buf); + if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { + bam_error(_("Error interpreting boot config\n")); + bf_fini(); + return (BAM_ERROR); + } + bf_fini(); + + (void) mkdir(CONF_DIR, 0755); + (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root); + fp = fopen(path, "w"); + if (fp == NULL) { + return (BAM_ERROR); /* error, cant write config */ + } + + errno = 0; + /* + * on write error, remove file to ensure we have bootable config. + * note we dont mind if config exists, it will get updated + */ + (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n"); + if (errno != 0) + goto error; + + /* + * really simple and stupid console conversion. + * it really has to be gone, it belongs to milestone/xvm properties. + */ + env = getenv("console"); + if (env != NULL) { + if (strcmp(env, "ttya") == 0) + (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n", + opt); + else if (strcmp(env, "ttyb") == 0) + (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n", + opt); + else + (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n", + opt); + } else + (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt); + if (errno != 0) + goto error; + + (void) fprintf(fp, + "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n"); + if (errno != 0) + goto error; + + (void) fprintf(fp, + "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n"); + if (errno != 0) + goto error; + + (void) fclose(fp); + if (errno != 0) { + (void) unlink(path); + return (BAM_ERROR); + } + return (BAM_SUCCESS); +error: + (void) fclose(fp); + (void) unlink(path); + return (BAM_ERROR); +} |
