diff --git a/usr.sbin/bhyve/config.h b/usr.sbin/bhyve/config.h --- a/usr.sbin/bhyve/config.h +++ b/usr.sbin/bhyve/config.h @@ -58,6 +58,12 @@ */ const char *get_config_value_node(const nvlist_t *parent, const char *name); +/* + * Loop over duplicate values of a configuration variable. + */ +int config_value_node_foreach(const nvlist_t *parent, const char *name, + int (*cb)(const char *, void *), void *arg); + /* * Similar to get_config_value_node but expects a full path to the * leaf node. @@ -93,12 +99,20 @@ * Adds or replaces the value of the specified variable. * * If 'parent' is NULL, 'name' is assumed to be a top-level variable. + * If 'name' is already set, the old value is overwritten. */ void set_config_value_node(nvlist_t *parent, const char *name, const char *value); /* - * Similar to set_config_value_node but only sets value if it's unset yet. + * Similar to set_config_value_node, but an existing value with the same name is + * not overwritten. + */ +void set_config_value_node_dupok(nvlist_t *parent, const char *name, + const char *value); + +/* + * Similar to set_config_value_node but only sets value if it's unset. */ void set_config_value_node_if_unset(nvlist_t *const parent, const char *const name, const char *const value); diff --git a/usr.sbin/bhyve/config.c b/usr.sbin/bhyve/config.c --- a/usr.sbin/bhyve/config.c +++ b/usr.sbin/bhyve/config.c @@ -25,7 +25,8 @@ * SUCH DAMAGE. */ -#include +#include + #include #include #include @@ -36,13 +37,28 @@ static nvlist_t *config_root; +static nvlist_t * +config_node_alloc(void) +{ + nvlist_t *nvl; + + /* + * Config nodes permit duplicate keys, but by default, an existing + * configuration value is overwritten when a new one is set. This + * behavior can be overridden by using set_config_value_node_dupok(), + * in which case, config_value_node_foreach() must be used to retrieve + * values. + */ + nvl = nvlist_create(NV_FLAG_NO_UNIQUE); + if (nvl == NULL) + err(1, "nvlist_create"); + return (nvl); +} + void init_config(void) { - - config_root = nvlist_create(0); - if (config_root == NULL) - err(4, "Failed to create configuration root nvlist"); + config_root = config_node_alloc(); } static nvlist_t * @@ -88,7 +104,7 @@ * XXX-MJ as with the case above, "new_nvl" shouldn't be * mutated after its ownership is given to "nvl". */ - new_nvl = nvlist_create(0); + new_nvl = config_node_alloc(); if (new_nvl == NULL) errx(4, "Failed to allocate memory"); nvlist_move_nvlist(nvl, name, new_nvl); @@ -130,23 +146,39 @@ return (_lookup_config_node(parent, path, false)); } -void -set_config_value_node(nvlist_t *parent, const char *name, const char *value) -{ +#define CONFIG_DUPOK 0x01 +static void +set_config_value_node_flags(nvlist_t *parent, const char *name, + const char *value, int flags) +{ 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)) + if (nvlist_exists_string(parent, name)) { + if ((flags & CONFIG_DUPOK) == 0) + nvlist_free_string(parent, name); + } else if (nvlist_exists(parent, name)) errx(4, "Attempting to add value %s to existing node %s of list %p", value, name, parent); nvlist_add_string(parent, name, value); } +void +set_config_value_node(nvlist_t *parent, const char *name, const char *value) +{ + set_config_value_node_flags(parent, name, value, 0); +} + +void +set_config_value_node_dupok(nvlist_t *parent, const char *name, + const char *value) +{ + set_config_value_node_flags(parent, name, value, CONFIG_DUPOK); +} + void set_config_value_node_if_unset(nvlist_t *const parent, const char *const name, const char *const value) @@ -358,6 +390,31 @@ return (expand_config_value(nvlist_get_string(parent, name))); } +int +config_value_node_foreach(const nvlist_t *parent, const char *name, + int (*cb)(const char *, void *), void *arg) +{ + void *cookie; + const char *nvname; + int error, type; + + if (strchr(name, '.') != NULL) + errx(4, "Invalid config node name %s", name); + if (parent == NULL) + parent = config_root; + + for (cookie = NULL; + (nvname = nvlist_next(parent, &type, &cookie)) != NULL;) { + assert(type == NV_TYPE_STRING); + if (strcmp(nvname, name) == 0) { + error = cb(cnvlist_get_string(cookie), arg); + if (error != 0) + return (error); + } + } + return (0); +} + static bool _bool_value(const char *name, const char *value) {