2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2021 John H. Baldwin <jhb@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
39 static nvlist_t *config_root;
45 config_root = nvlist_create(0);
46 if (config_root == NULL)
47 err(4, "Failed to create configuration root nvlist");
51 _lookup_config_node(nvlist_t *parent, const char *path, bool create)
53 char *copy, *name, *tofree;
54 nvlist_t *nvl, *new_nvl;
58 errx(4, "Failed to allocate memory");
61 while ((name = strsep(©, ".")) != NULL) {
63 warnx("Invalid configuration node: %s", path);
67 if (nvlist_exists_nvlist(nvl, name))
68 nvl = (nvlist_t *)nvlist_get_nvlist(nvl, name);
69 else if (nvlist_exists(nvl, name)) {
70 for (copy = tofree; copy < name; copy++)
74 "Configuration node %s is a child of existing variable %s",
79 new_nvl = nvlist_create(0);
81 errx(4, "Failed to allocate memory");
82 nvlist_move_nvlist(nvl, name, new_nvl);
94 create_config_node(const char *path)
97 return (_lookup_config_node(config_root, path, true));
101 find_config_node(const char *path)
104 return (_lookup_config_node(config_root, path, false));
108 create_relative_config_node(nvlist_t *parent, const char *path)
111 return (_lookup_config_node(parent, path, true));
115 find_relative_config_node(nvlist_t *parent, const char *path)
118 return (_lookup_config_node(parent, path, false));
122 set_config_value_node(nvlist_t *parent, const char *name, const char *value)
125 if (strchr(name, '.') != NULL)
126 errx(4, "Invalid config node name %s", name);
128 parent = config_root;
129 if (nvlist_exists_string(parent, name))
130 nvlist_free_string(parent, name);
131 else if (nvlist_exists(parent, name))
133 "Attemping to add value %s to existing node %s of list %p",
134 value, name, parent);
135 nvlist_add_string(parent, name, value);
139 set_config_value_node_if_unset(nvlist_t *const parent, const char *const name,
140 const char *const value)
142 if (get_config_value_node(parent, name) != NULL) {
146 set_config_value_node(parent, name, value);
150 set_config_value(const char *path, const char *value)
156 /* Look for last separator. */
157 name = strrchr(path, '.');
162 node_name = strndup(path, name - path);
163 if (node_name == NULL)
164 errx(4, "Failed to allocate memory");
165 nvl = create_config_node(node_name);
167 errx(4, "Failed to create configuration node %s",
175 if (nvlist_exists_nvlist(nvl, name))
176 errx(4, "Attempting to add value %s to existing node %s",
178 set_config_value_node(nvl, name, value);
182 set_config_value_if_unset(const char *const path, const char *const value)
184 if (get_config_value(path) != NULL) {
188 set_config_value(path, value);
192 get_raw_config_value(const char *path)
198 /* Look for last separator. */
199 name = strrchr(path, '.');
204 node_name = strndup(path, name - path);
205 if (node_name == NULL)
206 errx(4, "Failed to allocate memory");
207 nvl = find_config_node(node_name);
216 if (nvlist_exists_string(nvl, name))
217 return (nvlist_get_string(nvl, name));
218 if (nvlist_exists_nvlist(nvl, name))
219 warnx("Attempting to fetch value of node %s", path);
224 _expand_config_value(const char *value, int depth)
228 char *nestedval, *path, *valbuf;
231 valfp = open_memstream(&valbuf, &valsize);
233 errx(4, "Failed to allocate memory");
236 while (*vp != '\0') {
241 "Too many recursive references in configuration value");
246 if (vp[1] != '(' || vp[2] == '\0')
249 cp = strchr(vp + 2, ')');
252 "Invalid reference in configuration value \"%s\"",
262 "Empty reference in configuration value \"%s\"",
268 /* Allocate a C string holding the path. */
269 path = strndup(vp, cp - vp);
271 errx(4, "Failed to allocate memory");
273 /* Advance 'vp' past the reference. */
276 /* Fetch the referenced value. */
277 cp = get_raw_config_value(path);
280 "Failed to fetch referenced configuration variable %s",
283 nestedval = _expand_config_value(cp, depth + 1);
284 fputs(nestedval, valfp);
293 "Trailing \\ in configuration value \"%s\"",
309 expand_config_value(const char *value)
313 if (strchr(value, '%') == NULL)
317 valbuf = _expand_config_value(value, 0);
322 get_config_value(const char *path)
326 value = get_raw_config_value(path);
329 return (expand_config_value(value));
333 get_config_value_node(const nvlist_t *parent, const char *name)
336 if (strchr(name, '.') != NULL)
337 errx(4, "Invalid config node name %s", name);
339 parent = config_root;
340 if (nvlist_exists_nvlist(parent, name))
341 warnx("Attempt to fetch value of node %s of list %p", name,
343 if (!nvlist_exists_string(parent, name))
346 return (expand_config_value(nvlist_get_string(parent, name)));
350 _bool_value(const char *name, const char *value)
353 if (strcasecmp(value, "true") == 0 ||
354 strcasecmp(value, "on") == 0 ||
355 strcasecmp(value, "yes") == 0 ||
356 strcmp(value, "1") == 0)
358 if (strcasecmp(value, "false") == 0 ||
359 strcasecmp(value, "off") == 0 ||
360 strcasecmp(value, "no") == 0 ||
361 strcmp(value, "0") == 0)
363 err(4, "Invalid value %s for boolean variable %s", value, name);
367 get_config_bool(const char *path)
371 value = get_config_value(path);
373 err(4, "Failed to fetch boolean variable %s", path);
374 return (_bool_value(path, value));
378 get_config_bool_default(const char *path, bool def)
382 value = get_config_value(path);
385 return (_bool_value(path, value));
389 get_config_bool_node(const nvlist_t *parent, const char *name)
393 value = get_config_value_node(parent, name);
395 err(4, "Failed to fetch boolean variable %s", name);
396 return (_bool_value(name, value));
400 get_config_bool_node_default(const nvlist_t *parent, const char *name,
405 value = get_config_value_node(parent, name);
408 return (_bool_value(name, value));
412 set_config_bool(const char *path, bool value)
415 set_config_value(path, value ? "true" : "false");
419 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
422 set_config_value_node(parent, name, value ? "true" : "false");
426 dump_tree(const char *prefix, const nvlist_t *nvl)
433 while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
434 if (type == NV_TYPE_NVLIST) {
437 asprintf(&new_prefix, "%s%s.", prefix, name);
438 dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
441 assert(type == NV_TYPE_STRING);
442 printf("%s%s=%s\n", prefix, name,
443 nvlist_get_string(nvl, name));
451 dump_tree("", config_root);