diff options
Diffstat (limited to 'usr/src/cmd/bhyve/config.c')
| -rw-r--r-- | usr/src/cmd/bhyve/config.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/usr/src/cmd/bhyve/config.c b/usr/src/cmd/bhyve/config.c new file mode 100644 index 0000000000..db28cc5516 --- /dev/null +++ b/usr/src/cmd/bhyve/config.c @@ -0,0 +1,439 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 John H. Baldwin <jhb@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" + +static nvlist_t *config_root; + +void +init_config(void) +{ + config_root = nvlist_create(0); + if (config_root == NULL) + err(4, "Failed to create configuration root nvlist"); +} + +static nvlist_t * +_lookup_config_node(nvlist_t *parent, const char *path, bool create) +{ + char *copy, *name, *tofree; + nvlist_t *nvl, *new_nvl; + + copy = strdup(path); + if (copy == NULL) + errx(4, "Failed to allocate memory"); + tofree = copy; + nvl = parent; + while ((name = strsep(©, ".")) != NULL) { + if (*name == '\0') { + warnx("Invalid configuration node: %s", path); + nvl = NULL; + break; + } + if (nvlist_exists_nvlist(nvl, name)) + nvl = (nvlist_t *)nvlist_get_nvlist(nvl, name); + else if (nvlist_exists(nvl, name)) { + for (copy = tofree; copy < name; copy++) + if (*copy == '\0') + *copy = '.'; + warnx( + "Configuration node %s is a child of existing variable %s", + path, tofree); + nvl = NULL; + break; + } else if (create) { + new_nvl = nvlist_create(0); + if (new_nvl == NULL) + errx(4, "Failed to allocate memory"); +#ifdef __FreeBSD__ + nvlist_move_nvlist(nvl, name, new_nvl); +#else + if (nvlist_add_nvlist(nvl, name, new_nvl) != 0) + errx(4, "Failed to allocate memory"); + (void) nvlist_free(new_nvl); + if (nvlist_lookup_nvlist(nvl, name, &new_nvl) != 0) + errx(4, "Failed to retrieve new nvlist"); +#endif + nvl = new_nvl; + } else { + nvl = NULL; + break; + } + } + free(tofree); + return (nvl); +} + +nvlist_t * +create_config_node(const char *path) +{ + + return (_lookup_config_node(config_root, path, true)); +} + +nvlist_t * +find_config_node(const char *path) +{ + + return (_lookup_config_node(config_root, path, false)); +} + +nvlist_t * +create_relative_config_node(nvlist_t *parent, const char *path) +{ + + return (_lookup_config_node(parent, path, true)); +} + +nvlist_t * +find_relative_config_node(nvlist_t *parent, const char *path) +{ + + return (_lookup_config_node(parent, path, false)); +} + +void +set_config_value_node(nvlist_t *parent, const char *name, const char *value) +{ + + if (strchr(name, '.') != NULL) + errx(4, "Invalid config node name %s", name); + if (parent == NULL) + parent = config_root; + if (nvlist_exists_string(parent, name)) + nvlist_free_string(parent, name); + else if (nvlist_exists(parent, name)) + errx(4, + "Attemping to add value %s to existing node %s of list %p", + value, name, parent); + nvlist_add_string(parent, name, value); +} + +void +set_config_value(const char *path, const char *value) +{ + const char *name; + char *node_name; + nvlist_t *nvl; + + /* Look for last separator. */ + name = strrchr(path, '.'); + if (name == NULL) { + nvl = config_root; + name = path; + } else { + node_name = strndup(path, name - path); + if (node_name == NULL) + errx(4, "Failed to allocate memory"); + nvl = create_config_node(node_name); + if (nvl == NULL) + errx(4, "Failed to create configuration node %s", + node_name); + free(node_name); + + /* Skip over '.'. */ + name++; + } + + if (nvlist_exists_nvlist(nvl, name)) + errx(4, "Attempting to add value %s to existing node %s", + value, path); + set_config_value_node(nvl, name, value); +} + +static const char * +get_raw_config_value(const char *path) +{ + const char *name; + char *node_name; + nvlist_t *nvl; + + /* Look for last separator. */ + name = strrchr(path, '.'); + if (name == NULL) { + nvl = config_root; + name = path; + } else { + node_name = strndup(path, name - path); + if (node_name == NULL) + errx(4, "Failed to allocate memory"); + nvl = find_config_node(node_name); + free(node_name); + if (nvl == NULL) + return (NULL); + + /* Skip over '.'. */ + name++; + } + + if (nvlist_exists_string(nvl, name)) + return (nvlist_get_string(nvl, name)); + if (nvlist_exists_nvlist(nvl, name)) + warnx("Attempting to fetch value of node %s", path); + return (NULL); +} + +static char * +_expand_config_value(const char *value, int depth) +{ + FILE *valfp; + const char *cp, *vp; + char *nestedval, *path, *valbuf; + size_t valsize; + + valfp = open_memstream(&valbuf, &valsize); + if (valfp == NULL) + errx(4, "Failed to allocate memory"); + + vp = value; + while (*vp != '\0') { + switch (*vp) { + case '%': + if (depth > 15) { + warnx( + "Too many recursive references in configuration value"); + fputc('%', valfp); + vp++; + break; + } + if (vp[1] != '(' || vp[2] == '\0') + cp = NULL; + else + cp = strchr(vp + 2, ')'); + if (cp == NULL) { + warnx( + "Invalid reference in configuration value \"%s\"", + value); + fputc('%', valfp); + vp++; + break; + } + vp += 2; + + if (cp == vp) { + warnx( + "Empty reference in configuration value \"%s\"", + value); + vp++; + break; + } + + /* Allocate a C string holding the path. */ + path = strndup(vp, cp - vp); + if (path == NULL) + errx(4, "Failed to allocate memory"); + + /* Advance 'vp' past the reference. */ + vp = cp + 1; + + /* Fetch the referenced value. */ + cp = get_raw_config_value(path); + if (cp == NULL) + warnx( + "Failed to fetch referenced configuration variable %s", + path); + else { + nestedval = _expand_config_value(cp, depth + 1); + fputs(nestedval, valfp); + free(nestedval); + } + free(path); + break; + case '\\': + vp++; + if (*vp == '\0') { + warnx( + "Trailing \\ in configuration value \"%s\"", + value); + break; + } + /* FALLTHROUGH */ + default: + fputc(*vp, valfp); + vp++; + break; + } + } + fclose(valfp); + return (valbuf); +} + +const char * +expand_config_value(const char *value) +{ + static char *valbuf; + + if (strchr(value, '%') == NULL) + return (value); + + free(valbuf); + valbuf = _expand_config_value(value, 0); + return (valbuf); +} + +const char * +get_config_value(const char *path) +{ + const char *value; + + value = get_raw_config_value(path); + if (value == NULL) + return (NULL); + return (expand_config_value(value)); +} + +const char * +get_config_value_node(const nvlist_t *parent, const char *name) +{ + + if (strchr(name, '.') != NULL) + errx(4, "Invalid config node name %s", name); + if (parent == NULL) + parent = config_root; + + if (nvlist_exists_nvlist(parent, name)) + warnx("Attempt to fetch value of node %s of list %p", name, + parent); + if (!nvlist_exists_string(parent, name)) + return (NULL); + + return (expand_config_value(nvlist_get_string(parent, name))); +} + +bool +_bool_value(const char *name, const char *value) +{ + + if (strcasecmp(value, "true") == 0 || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "yes") == 0 || + strcmp(value, "1") == 0) + return (true); + if (strcasecmp(value, "false") == 0 || + strcasecmp(value, "off") == 0 || + strcasecmp(value, "no") == 0 || + strcmp(value, "0") == 0) + return (false); + err(4, "Invalid value %s for boolean variable %s", value, name); +} + +bool +get_config_bool(const char *path) +{ + const char *value; + + value = get_config_value(path); + if (value == NULL) + err(4, "Failed to fetch boolean variable %s", path); + return (_bool_value(path, value)); +} + +bool +get_config_bool_default(const char *path, bool def) +{ + const char *value; + + value = get_config_value(path); + if (value == NULL) + return (def); + return (_bool_value(path, value)); +} + +bool +get_config_bool_node(const nvlist_t *parent, const char *name) +{ + const char *value; + + value = get_config_value_node(parent, name); + if (value == NULL) + err(4, "Failed to fetch boolean variable %s", name); + return (_bool_value(name, value)); +} + +bool +get_config_bool_node_default(const nvlist_t *parent, const char *name, + bool def) +{ + const char *value; + + value = get_config_value_node(parent, name); + if (value == NULL) + return (def); + return (_bool_value(name, value)); +} + +void +set_config_bool(const char *path, bool value) +{ + + set_config_value(path, value ? "true" : "false"); +} + +void +set_config_bool_node(nvlist_t *parent, const char *name, bool value) +{ + + set_config_value_node(parent, name, value ? "true" : "false"); +} + +static void +dump_tree(const char *prefix, const nvlist_t *nvl) +{ + const char *name; + void *cookie; + int type; + + cookie = NULL; + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + char *new_prefix; + + asprintf(&new_prefix, "%s%s.", prefix, name); + dump_tree(new_prefix, nvlist_get_nvlist(nvl, name)); + free(new_prefix); + } else { + assert(type == NV_TYPE_STRING); + printf("%s%s=%s\n", prefix, name, + nvlist_get_string(nvl, name)); + } + } +} + +void +dump_config(void) +{ + dump_tree("", config_root); +} |
