xref: /illumos-gate/usr/src/cmd/bhyve/config.c (revision 32640292339b07090f10ce34d455f98711077343)
12b948146SAndy Fiddaman /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
32b948146SAndy Fiddaman  *
42b948146SAndy Fiddaman  * Copyright (c) 2021 John H. Baldwin <jhb@FreeBSD.org>
52b948146SAndy Fiddaman  *
62b948146SAndy Fiddaman  * Redistribution and use in source and binary forms, with or without
72b948146SAndy Fiddaman  * modification, are permitted provided that the following conditions
82b948146SAndy Fiddaman  * are met:
92b948146SAndy Fiddaman  * 1. Redistributions of source code must retain the above copyright
102b948146SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer.
112b948146SAndy Fiddaman  * 2. Redistributions in binary form must reproduce the above copyright
122b948146SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer in the
132b948146SAndy Fiddaman  *    documentation and/or other materials provided with the distribution.
142b948146SAndy Fiddaman  *
152b948146SAndy Fiddaman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162b948146SAndy Fiddaman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172b948146SAndy Fiddaman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182b948146SAndy Fiddaman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192b948146SAndy Fiddaman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202b948146SAndy Fiddaman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212b948146SAndy Fiddaman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222b948146SAndy Fiddaman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232b948146SAndy Fiddaman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242b948146SAndy Fiddaman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252b948146SAndy Fiddaman  * SUCH DAMAGE.
262b948146SAndy Fiddaman  */
272b948146SAndy Fiddaman 
282b948146SAndy Fiddaman #include <sys/cdefs.h>
292b948146SAndy Fiddaman 
302b948146SAndy Fiddaman #include <assert.h>
312b948146SAndy Fiddaman #include <err.h>
322b948146SAndy Fiddaman #include <stdio.h>
332b948146SAndy Fiddaman #include <stdlib.h>
342b948146SAndy Fiddaman #include <string.h>
3559d65d31SAndy Fiddaman #ifndef	__FreeBSD__
3659d65d31SAndy Fiddaman #include <sys/sysmacros.h>
3759d65d31SAndy Fiddaman #endif
382b948146SAndy Fiddaman 
392b948146SAndy Fiddaman #include "config.h"
402b948146SAndy Fiddaman 
412b948146SAndy Fiddaman static nvlist_t *config_root;
422b948146SAndy Fiddaman 
432b948146SAndy Fiddaman void
init_config(void)442b948146SAndy Fiddaman init_config(void)
452b948146SAndy Fiddaman {
462b948146SAndy Fiddaman 	config_root = nvlist_create(0);
472b948146SAndy Fiddaman 	if (config_root == NULL)
482b948146SAndy Fiddaman 		err(4, "Failed to create configuration root nvlist");
492b948146SAndy Fiddaman }
502b948146SAndy Fiddaman 
512b948146SAndy Fiddaman static nvlist_t *
_lookup_config_node(nvlist_t * parent,const char * path,bool create)522b948146SAndy Fiddaman _lookup_config_node(nvlist_t *parent, const char *path, bool create)
532b948146SAndy Fiddaman {
542b948146SAndy Fiddaman 	char *copy, *name, *tofree;
552b948146SAndy Fiddaman 	nvlist_t *nvl, *new_nvl;
562b948146SAndy Fiddaman 
572b948146SAndy Fiddaman 	copy = strdup(path);
582b948146SAndy Fiddaman 	if (copy == NULL)
592b948146SAndy Fiddaman 		errx(4, "Failed to allocate memory");
602b948146SAndy Fiddaman 	tofree = copy;
612b948146SAndy Fiddaman 	nvl = parent;
622b948146SAndy Fiddaman 	while ((name = strsep(&copy, ".")) != NULL) {
632b948146SAndy Fiddaman 		if (*name == '\0') {
642b948146SAndy Fiddaman 			warnx("Invalid configuration node: %s", path);
652b948146SAndy Fiddaman 			nvl = NULL;
662b948146SAndy Fiddaman 			break;
672b948146SAndy Fiddaman 		}
682b948146SAndy Fiddaman 		if (nvlist_exists_nvlist(nvl, name))
6959d65d31SAndy Fiddaman 			/*
7059d65d31SAndy Fiddaman 			 * XXX-MJ it is incorrect to cast away the const
7159d65d31SAndy Fiddaman 			 * qualifier like this since the contract with nvlist
7259d65d31SAndy Fiddaman 			 * says that values are immutable, and some consumers
7359d65d31SAndy Fiddaman 			 * will indeed add nodes to the returned nvlist.  In
7459d65d31SAndy Fiddaman 			 * practice, however, it appears to be harmless with the
7559d65d31SAndy Fiddaman 			 * current nvlist implementation, so we just live with
7659d65d31SAndy Fiddaman 			 * it until the implementation is reworked.
7759d65d31SAndy Fiddaman 			 */
7859d65d31SAndy Fiddaman 			nvl = __DECONST(nvlist_t *,
7959d65d31SAndy Fiddaman 			    nvlist_get_nvlist(nvl, name));
802b948146SAndy Fiddaman 		else if (nvlist_exists(nvl, name)) {
812b948146SAndy Fiddaman 			for (copy = tofree; copy < name; copy++)
822b948146SAndy Fiddaman 				if (*copy == '\0')
832b948146SAndy Fiddaman 					*copy = '.';
842b948146SAndy Fiddaman 			warnx(
852b948146SAndy Fiddaman 		    "Configuration node %s is a child of existing variable %s",
862b948146SAndy Fiddaman 			    path, tofree);
872b948146SAndy Fiddaman 			nvl = NULL;
882b948146SAndy Fiddaman 			break;
892b948146SAndy Fiddaman 		} else if (create) {
9059d65d31SAndy Fiddaman 			/*
9159d65d31SAndy Fiddaman 			 * XXX-MJ as with the case above, "new_nvl" shouldn't be
9259d65d31SAndy Fiddaman 			 * mutated after its ownership is given to "nvl".
9359d65d31SAndy Fiddaman 			 */
942b948146SAndy Fiddaman 			new_nvl = nvlist_create(0);
952b948146SAndy Fiddaman 			if (new_nvl == NULL)
962b948146SAndy Fiddaman 				errx(4, "Failed to allocate memory");
972b948146SAndy Fiddaman #ifdef __FreeBSD__
982b948146SAndy Fiddaman 			nvlist_move_nvlist(nvl, name, new_nvl);
992b948146SAndy Fiddaman #else
1002b948146SAndy Fiddaman 			if (nvlist_add_nvlist(nvl, name, new_nvl) != 0)
1012b948146SAndy Fiddaman 				errx(4, "Failed to allocate memory");
1022b948146SAndy Fiddaman 			(void) nvlist_free(new_nvl);
1032b948146SAndy Fiddaman 			if (nvlist_lookup_nvlist(nvl, name, &new_nvl) != 0)
1042b948146SAndy Fiddaman 				errx(4, "Failed to retrieve new nvlist");
1052b948146SAndy Fiddaman #endif
1062b948146SAndy Fiddaman 			nvl = new_nvl;
1072b948146SAndy Fiddaman 		} else {
1082b948146SAndy Fiddaman 			nvl = NULL;
1092b948146SAndy Fiddaman 			break;
1102b948146SAndy Fiddaman 		}
1112b948146SAndy Fiddaman 	}
1122b948146SAndy Fiddaman 	free(tofree);
1132b948146SAndy Fiddaman 	return (nvl);
1142b948146SAndy Fiddaman }
1152b948146SAndy Fiddaman 
1162b948146SAndy Fiddaman nvlist_t *
create_config_node(const char * path)1172b948146SAndy Fiddaman create_config_node(const char *path)
1182b948146SAndy Fiddaman {
1192b948146SAndy Fiddaman 
1202b948146SAndy Fiddaman 	return (_lookup_config_node(config_root, path, true));
1212b948146SAndy Fiddaman }
1222b948146SAndy Fiddaman 
1232b948146SAndy Fiddaman nvlist_t *
find_config_node(const char * path)1242b948146SAndy Fiddaman find_config_node(const char *path)
1252b948146SAndy Fiddaman {
1262b948146SAndy Fiddaman 
1272b948146SAndy Fiddaman 	return (_lookup_config_node(config_root, path, false));
1282b948146SAndy Fiddaman }
1292b948146SAndy Fiddaman 
1302b948146SAndy Fiddaman nvlist_t *
create_relative_config_node(nvlist_t * parent,const char * path)1312b948146SAndy Fiddaman create_relative_config_node(nvlist_t *parent, const char *path)
1322b948146SAndy Fiddaman {
1332b948146SAndy Fiddaman 
1342b948146SAndy Fiddaman 	return (_lookup_config_node(parent, path, true));
1352b948146SAndy Fiddaman }
1362b948146SAndy Fiddaman 
1372b948146SAndy Fiddaman nvlist_t *
find_relative_config_node(nvlist_t * parent,const char * path)1382b948146SAndy Fiddaman find_relative_config_node(nvlist_t *parent, const char *path)
1392b948146SAndy Fiddaman {
1402b948146SAndy Fiddaman 
1412b948146SAndy Fiddaman 	return (_lookup_config_node(parent, path, false));
1422b948146SAndy Fiddaman }
1432b948146SAndy Fiddaman 
1442b948146SAndy Fiddaman void
set_config_value_node(nvlist_t * parent,const char * name,const char * value)1452b948146SAndy Fiddaman set_config_value_node(nvlist_t *parent, const char *name, const char *value)
1462b948146SAndy Fiddaman {
1472b948146SAndy Fiddaman 
1482b948146SAndy Fiddaman 	if (strchr(name, '.') != NULL)
1492b948146SAndy Fiddaman 		errx(4, "Invalid config node name %s", name);
1502b948146SAndy Fiddaman 	if (parent == NULL)
1512b948146SAndy Fiddaman 		parent = config_root;
1522b948146SAndy Fiddaman 	if (nvlist_exists_string(parent, name))
1532b948146SAndy Fiddaman 		nvlist_free_string(parent, name);
1542b948146SAndy Fiddaman 	else if (nvlist_exists(parent, name))
1552b948146SAndy Fiddaman 		errx(4,
156*32640292SAndy Fiddaman 		    "Attempting to add value %s to existing node %s of list %p",
1572b948146SAndy Fiddaman 		    value, name, parent);
1582b948146SAndy Fiddaman 	nvlist_add_string(parent, name, value);
1592b948146SAndy Fiddaman }
1602b948146SAndy Fiddaman 
1612b948146SAndy Fiddaman void
set_config_value_node_if_unset(nvlist_t * const parent,const char * const name,const char * const value)162d7b72f7bSAndy Fiddaman set_config_value_node_if_unset(nvlist_t *const parent, const char *const name,
163d7b72f7bSAndy Fiddaman     const char *const value)
164d7b72f7bSAndy Fiddaman {
165d7b72f7bSAndy Fiddaman 	if (get_config_value_node(parent, name) != NULL) {
166d7b72f7bSAndy Fiddaman 		return;
167d7b72f7bSAndy Fiddaman 	}
168d7b72f7bSAndy Fiddaman 
169d7b72f7bSAndy Fiddaman 	set_config_value_node(parent, name, value);
170d7b72f7bSAndy Fiddaman }
171d7b72f7bSAndy Fiddaman 
172d7b72f7bSAndy Fiddaman void
set_config_value(const char * path,const char * value)1732b948146SAndy Fiddaman set_config_value(const char *path, const char *value)
1742b948146SAndy Fiddaman {
1752b948146SAndy Fiddaman 	const char *name;
1762b948146SAndy Fiddaman 	char *node_name;
1772b948146SAndy Fiddaman 	nvlist_t *nvl;
1782b948146SAndy Fiddaman 
1792b948146SAndy Fiddaman 	/* Look for last separator. */
1802b948146SAndy Fiddaman 	name = strrchr(path, '.');
1812b948146SAndy Fiddaman 	if (name == NULL) {
1822b948146SAndy Fiddaman 		nvl = config_root;
1832b948146SAndy Fiddaman 		name = path;
1842b948146SAndy Fiddaman 	} else {
1852b948146SAndy Fiddaman 		node_name = strndup(path, name - path);
1862b948146SAndy Fiddaman 		if (node_name == NULL)
1872b948146SAndy Fiddaman 			errx(4, "Failed to allocate memory");
1882b948146SAndy Fiddaman 		nvl = create_config_node(node_name);
1892b948146SAndy Fiddaman 		if (nvl == NULL)
1902b948146SAndy Fiddaman 			errx(4, "Failed to create configuration node %s",
1912b948146SAndy Fiddaman 			    node_name);
1922b948146SAndy Fiddaman 		free(node_name);
1932b948146SAndy Fiddaman 
1942b948146SAndy Fiddaman 		/* Skip over '.'. */
1952b948146SAndy Fiddaman 		name++;
1962b948146SAndy Fiddaman 	}
1972b948146SAndy Fiddaman 
1982b948146SAndy Fiddaman 	if (nvlist_exists_nvlist(nvl, name))
1992b948146SAndy Fiddaman 		errx(4, "Attempting to add value %s to existing node %s",
2002b948146SAndy Fiddaman 		    value, path);
2012b948146SAndy Fiddaman 	set_config_value_node(nvl, name, value);
2022b948146SAndy Fiddaman }
2032b948146SAndy Fiddaman 
204d7b72f7bSAndy Fiddaman void
set_config_value_if_unset(const char * const path,const char * const value)205d7b72f7bSAndy Fiddaman set_config_value_if_unset(const char *const path, const char *const value)
206d7b72f7bSAndy Fiddaman {
207d7b72f7bSAndy Fiddaman 	if (get_config_value(path) != NULL) {
208d7b72f7bSAndy Fiddaman 		return;
209d7b72f7bSAndy Fiddaman 	}
210d7b72f7bSAndy Fiddaman 
211d7b72f7bSAndy Fiddaman 	set_config_value(path, value);
212d7b72f7bSAndy Fiddaman }
213d7b72f7bSAndy Fiddaman 
2142b948146SAndy Fiddaman static const char *
get_raw_config_value(const char * path)2152b948146SAndy Fiddaman get_raw_config_value(const char *path)
2162b948146SAndy Fiddaman {
2172b948146SAndy Fiddaman 	const char *name;
2182b948146SAndy Fiddaman 	char *node_name;
2192b948146SAndy Fiddaman 	nvlist_t *nvl;
2202b948146SAndy Fiddaman 
2212b948146SAndy Fiddaman 	/* Look for last separator. */
2222b948146SAndy Fiddaman 	name = strrchr(path, '.');
2232b948146SAndy Fiddaman 	if (name == NULL) {
2242b948146SAndy Fiddaman 		nvl = config_root;
2252b948146SAndy Fiddaman 		name = path;
2262b948146SAndy Fiddaman 	} else {
2272b948146SAndy Fiddaman 		node_name = strndup(path, name - path);
2282b948146SAndy Fiddaman 		if (node_name == NULL)
2292b948146SAndy Fiddaman 			errx(4, "Failed to allocate memory");
2302b948146SAndy Fiddaman 		nvl = find_config_node(node_name);
2312b948146SAndy Fiddaman 		free(node_name);
2322b948146SAndy Fiddaman 		if (nvl == NULL)
2332b948146SAndy Fiddaman 			return (NULL);
2342b948146SAndy Fiddaman 
2352b948146SAndy Fiddaman 		/* Skip over '.'. */
2362b948146SAndy Fiddaman 		name++;
2372b948146SAndy Fiddaman 	}
2382b948146SAndy Fiddaman 
2392b948146SAndy Fiddaman 	if (nvlist_exists_string(nvl, name))
2402b948146SAndy Fiddaman 		return (nvlist_get_string(nvl, name));
2412b948146SAndy Fiddaman 	if (nvlist_exists_nvlist(nvl, name))
2422b948146SAndy Fiddaman 		warnx("Attempting to fetch value of node %s", path);
2432b948146SAndy Fiddaman 	return (NULL);
2442b948146SAndy Fiddaman }
2452b948146SAndy Fiddaman 
2462b948146SAndy Fiddaman static char *
_expand_config_value(const char * value,int depth)2472b948146SAndy Fiddaman _expand_config_value(const char *value, int depth)
2482b948146SAndy Fiddaman {
2492b948146SAndy Fiddaman 	FILE *valfp;
2502b948146SAndy Fiddaman 	const char *cp, *vp;
2512b948146SAndy Fiddaman 	char *nestedval, *path, *valbuf;
2522b948146SAndy Fiddaman 	size_t valsize;
2532b948146SAndy Fiddaman 
2542b948146SAndy Fiddaman 	valfp = open_memstream(&valbuf, &valsize);
2552b948146SAndy Fiddaman 	if (valfp == NULL)
2562b948146SAndy Fiddaman 		errx(4, "Failed to allocate memory");
2572b948146SAndy Fiddaman 
2582b948146SAndy Fiddaman 	vp = value;
2592b948146SAndy Fiddaman 	while (*vp != '\0') {
2602b948146SAndy Fiddaman 		switch (*vp) {
2612b948146SAndy Fiddaman 		case '%':
2622b948146SAndy Fiddaman 			if (depth > 15) {
2632b948146SAndy Fiddaman 				warnx(
2642b948146SAndy Fiddaman 		    "Too many recursive references in configuration value");
2652b948146SAndy Fiddaman 				fputc('%', valfp);
2662b948146SAndy Fiddaman 				vp++;
2672b948146SAndy Fiddaman 				break;
2682b948146SAndy Fiddaman 			}
2692b948146SAndy Fiddaman 			if (vp[1] != '(' || vp[2] == '\0')
2702b948146SAndy Fiddaman 				cp = NULL;
2712b948146SAndy Fiddaman 			else
2722b948146SAndy Fiddaman 				cp = strchr(vp + 2, ')');
2732b948146SAndy Fiddaman 			if (cp == NULL) {
2742b948146SAndy Fiddaman 				warnx(
2752b948146SAndy Fiddaman 			    "Invalid reference in configuration value \"%s\"",
2762b948146SAndy Fiddaman 				    value);
2772b948146SAndy Fiddaman 				fputc('%', valfp);
2782b948146SAndy Fiddaman 				vp++;
2792b948146SAndy Fiddaman 				break;
2802b948146SAndy Fiddaman 			}
2812b948146SAndy Fiddaman 			vp += 2;
2822b948146SAndy Fiddaman 
2832b948146SAndy Fiddaman 			if (cp == vp) {
2842b948146SAndy Fiddaman 				warnx(
2852b948146SAndy Fiddaman 			    "Empty reference in configuration value \"%s\"",
2862b948146SAndy Fiddaman 				    value);
2872b948146SAndy Fiddaman 				vp++;
2882b948146SAndy Fiddaman 				break;
2892b948146SAndy Fiddaman 			}
2902b948146SAndy Fiddaman 
2912b948146SAndy Fiddaman 			/* Allocate a C string holding the path. */
2922b948146SAndy Fiddaman 			path = strndup(vp, cp - vp);
2932b948146SAndy Fiddaman 			if (path == NULL)
2942b948146SAndy Fiddaman 				errx(4, "Failed to allocate memory");
2952b948146SAndy Fiddaman 
2962b948146SAndy Fiddaman 			/* Advance 'vp' past the reference. */
2972b948146SAndy Fiddaman 			vp = cp + 1;
2982b948146SAndy Fiddaman 
2992b948146SAndy Fiddaman 			/* Fetch the referenced value. */
3002b948146SAndy Fiddaman 			cp = get_raw_config_value(path);
3012b948146SAndy Fiddaman 			if (cp == NULL)
3022b948146SAndy Fiddaman 				warnx(
3032b948146SAndy Fiddaman 		    "Failed to fetch referenced configuration variable %s",
3042b948146SAndy Fiddaman 				    path);
3052b948146SAndy Fiddaman 			else {
3062b948146SAndy Fiddaman 				nestedval = _expand_config_value(cp, depth + 1);
3072b948146SAndy Fiddaman 				fputs(nestedval, valfp);
3082b948146SAndy Fiddaman 				free(nestedval);
3092b948146SAndy Fiddaman 			}
3102b948146SAndy Fiddaman 			free(path);
3112b948146SAndy Fiddaman 			break;
3122b948146SAndy Fiddaman 		case '\\':
3132b948146SAndy Fiddaman 			vp++;
3142b948146SAndy Fiddaman 			if (*vp == '\0') {
3152b948146SAndy Fiddaman 				warnx(
3162b948146SAndy Fiddaman 			    "Trailing \\ in configuration value \"%s\"",
3172b948146SAndy Fiddaman 				    value);
3182b948146SAndy Fiddaman 				break;
3192b948146SAndy Fiddaman 			}
3202b948146SAndy Fiddaman 			/* FALLTHROUGH */
3212b948146SAndy Fiddaman 		default:
3222b948146SAndy Fiddaman 			fputc(*vp, valfp);
3232b948146SAndy Fiddaman 			vp++;
3242b948146SAndy Fiddaman 			break;
3252b948146SAndy Fiddaman 		}
3262b948146SAndy Fiddaman 	}
3272b948146SAndy Fiddaman 	fclose(valfp);
3282b948146SAndy Fiddaman 	return (valbuf);
3292b948146SAndy Fiddaman }
3302b948146SAndy Fiddaman 
3314f3f3e9aSAndy Fiddaman static const char *
expand_config_value(const char * value)3322b948146SAndy Fiddaman expand_config_value(const char *value)
3332b948146SAndy Fiddaman {
3342b948146SAndy Fiddaman 	static char *valbuf;
3352b948146SAndy Fiddaman 
3362b948146SAndy Fiddaman 	if (strchr(value, '%') == NULL)
3372b948146SAndy Fiddaman 		return (value);
3382b948146SAndy Fiddaman 
3392b948146SAndy Fiddaman 	free(valbuf);
3402b948146SAndy Fiddaman 	valbuf = _expand_config_value(value, 0);
3412b948146SAndy Fiddaman 	return (valbuf);
3422b948146SAndy Fiddaman }
3432b948146SAndy Fiddaman 
3442b948146SAndy Fiddaman const char *
get_config_value(const char * path)3452b948146SAndy Fiddaman get_config_value(const char *path)
3462b948146SAndy Fiddaman {
3472b948146SAndy Fiddaman 	const char *value;
3482b948146SAndy Fiddaman 
3492b948146SAndy Fiddaman 	value = get_raw_config_value(path);
3502b948146SAndy Fiddaman 	if (value == NULL)
3512b948146SAndy Fiddaman 		return (NULL);
3522b948146SAndy Fiddaman 	return (expand_config_value(value));
3532b948146SAndy Fiddaman }
3542b948146SAndy Fiddaman 
3552b948146SAndy Fiddaman const char *
get_config_value_node(const nvlist_t * parent,const char * name)3562b948146SAndy Fiddaman get_config_value_node(const nvlist_t *parent, const char *name)
3572b948146SAndy Fiddaman {
3582b948146SAndy Fiddaman 
3592b948146SAndy Fiddaman 	if (strchr(name, '.') != NULL)
3602b948146SAndy Fiddaman 		errx(4, "Invalid config node name %s", name);
3612b948146SAndy Fiddaman 	if (parent == NULL)
3622b948146SAndy Fiddaman 		parent = config_root;
3632b948146SAndy Fiddaman 
3642b948146SAndy Fiddaman 	if (nvlist_exists_nvlist(parent, name))
3652b948146SAndy Fiddaman 		warnx("Attempt to fetch value of node %s of list %p", name,
3662b948146SAndy Fiddaman 		    parent);
3672b948146SAndy Fiddaman 	if (!nvlist_exists_string(parent, name))
3682b948146SAndy Fiddaman 		return (NULL);
3692b948146SAndy Fiddaman 
3702b948146SAndy Fiddaman 	return (expand_config_value(nvlist_get_string(parent, name)));
3712b948146SAndy Fiddaman }
3722b948146SAndy Fiddaman 
3734f3f3e9aSAndy Fiddaman static bool
_bool_value(const char * name,const char * value)3742b948146SAndy Fiddaman _bool_value(const char *name, const char *value)
3752b948146SAndy Fiddaman {
3762b948146SAndy Fiddaman 
3772b948146SAndy Fiddaman 	if (strcasecmp(value, "true") == 0 ||
3782b948146SAndy Fiddaman 	    strcasecmp(value, "on") == 0 ||
3792b948146SAndy Fiddaman 	    strcasecmp(value, "yes") == 0 ||
3802b948146SAndy Fiddaman 	    strcmp(value, "1") == 0)
3812b948146SAndy Fiddaman 		return (true);
3822b948146SAndy Fiddaman 	if (strcasecmp(value, "false") == 0 ||
3832b948146SAndy Fiddaman 	    strcasecmp(value, "off") == 0 ||
3842b948146SAndy Fiddaman 	    strcasecmp(value, "no") == 0 ||
3852b948146SAndy Fiddaman 	    strcmp(value, "0") == 0)
3862b948146SAndy Fiddaman 		return (false);
3872b948146SAndy Fiddaman 	err(4, "Invalid value %s for boolean variable %s", value, name);
3882b948146SAndy Fiddaman }
3892b948146SAndy Fiddaman 
3902b948146SAndy Fiddaman bool
get_config_bool(const char * path)3912b948146SAndy Fiddaman get_config_bool(const char *path)
3922b948146SAndy Fiddaman {
3932b948146SAndy Fiddaman 	const char *value;
3942b948146SAndy Fiddaman 
3952b948146SAndy Fiddaman 	value = get_config_value(path);
3962b948146SAndy Fiddaman 	if (value == NULL)
3972b948146SAndy Fiddaman 		err(4, "Failed to fetch boolean variable %s", path);
3982b948146SAndy Fiddaman 	return (_bool_value(path, value));
3992b948146SAndy Fiddaman }
4002b948146SAndy Fiddaman 
4012b948146SAndy Fiddaman bool
get_config_bool_default(const char * path,bool def)4022b948146SAndy Fiddaman get_config_bool_default(const char *path, bool def)
4032b948146SAndy Fiddaman {
4042b948146SAndy Fiddaman 	const char *value;
4052b948146SAndy Fiddaman 
4062b948146SAndy Fiddaman 	value = get_config_value(path);
4072b948146SAndy Fiddaman 	if (value == NULL)
4082b948146SAndy Fiddaman 		return (def);
4092b948146SAndy Fiddaman 	return (_bool_value(path, value));
4102b948146SAndy Fiddaman }
4112b948146SAndy Fiddaman 
4122b948146SAndy Fiddaman bool
get_config_bool_node(const nvlist_t * parent,const char * name)4132b948146SAndy Fiddaman get_config_bool_node(const nvlist_t *parent, const char *name)
4142b948146SAndy Fiddaman {
4152b948146SAndy Fiddaman 	const char *value;
4162b948146SAndy Fiddaman 
4172b948146SAndy Fiddaman 	value = get_config_value_node(parent, name);
4182b948146SAndy Fiddaman 	if (value == NULL)
4192b948146SAndy Fiddaman 		err(4, "Failed to fetch boolean variable %s", name);
4202b948146SAndy Fiddaman 	return (_bool_value(name, value));
4212b948146SAndy Fiddaman }
4222b948146SAndy Fiddaman 
4232b948146SAndy Fiddaman bool
get_config_bool_node_default(const nvlist_t * parent,const char * name,bool def)4242b948146SAndy Fiddaman get_config_bool_node_default(const nvlist_t *parent, const char *name,
4252b948146SAndy Fiddaman     bool def)
4262b948146SAndy Fiddaman {
4272b948146SAndy Fiddaman 	const char *value;
4282b948146SAndy Fiddaman 
4292b948146SAndy Fiddaman 	value = get_config_value_node(parent, name);
4302b948146SAndy Fiddaman 	if (value == NULL)
4312b948146SAndy Fiddaman 		return (def);
4322b948146SAndy Fiddaman 	return (_bool_value(name, value));
4332b948146SAndy Fiddaman }
4342b948146SAndy Fiddaman 
4352b948146SAndy Fiddaman void
set_config_bool(const char * path,bool value)4362b948146SAndy Fiddaman set_config_bool(const char *path, bool value)
4372b948146SAndy Fiddaman {
4382b948146SAndy Fiddaman 
4392b948146SAndy Fiddaman 	set_config_value(path, value ? "true" : "false");
4402b948146SAndy Fiddaman }
4412b948146SAndy Fiddaman 
4422b948146SAndy Fiddaman void
set_config_bool_node(nvlist_t * parent,const char * name,bool value)4432b948146SAndy Fiddaman set_config_bool_node(nvlist_t *parent, const char *name, bool value)
4442b948146SAndy Fiddaman {
4452b948146SAndy Fiddaman 
4462b948146SAndy Fiddaman 	set_config_value_node(parent, name, value ? "true" : "false");
4472b948146SAndy Fiddaman }
4482b948146SAndy Fiddaman 
4492b948146SAndy Fiddaman static void
dump_tree(const char * prefix,const nvlist_t * nvl)4502b948146SAndy Fiddaman dump_tree(const char *prefix, const nvlist_t *nvl)
4512b948146SAndy Fiddaman {
4522b948146SAndy Fiddaman 	const char *name;
4532b948146SAndy Fiddaman 	void *cookie;
4542b948146SAndy Fiddaman 	int type;
4552b948146SAndy Fiddaman 
4562b948146SAndy Fiddaman 	cookie = NULL;
4572b948146SAndy Fiddaman 	while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
4582b948146SAndy Fiddaman 		if (type == NV_TYPE_NVLIST) {
4592b948146SAndy Fiddaman 			char *new_prefix;
4602b948146SAndy Fiddaman 
4612b948146SAndy Fiddaman 			asprintf(&new_prefix, "%s%s.", prefix, name);
4622b948146SAndy Fiddaman 			dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
4632b948146SAndy Fiddaman 			free(new_prefix);
4642b948146SAndy Fiddaman 		} else {
4652b948146SAndy Fiddaman 			assert(type == NV_TYPE_STRING);
4662b948146SAndy Fiddaman 			printf("%s%s=%s\n", prefix, name,
4672b948146SAndy Fiddaman 			    nvlist_get_string(nvl, name));
4682b948146SAndy Fiddaman 		}
4692b948146SAndy Fiddaman 	}
4702b948146SAndy Fiddaman }
4712b948146SAndy Fiddaman 
4722b948146SAndy Fiddaman void
dump_config(void)4732b948146SAndy Fiddaman dump_config(void)
4742b948146SAndy Fiddaman {
4752b948146SAndy Fiddaman 	dump_tree("", config_root);
4762b948146SAndy Fiddaman }
477