Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/config.c
- This file was added.
| /*- | |||||
| * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
| * | |||||
| * Copyright (c) 2019 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"); | |||||
| nvlist_move_nvlist(nvl, name, new_nvl); | |||||
| 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); | |||||
| } | |||||