2 * SPDX-License-Identifier: BSD-2-Clause
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))
69 * XXX-MJ it is incorrect to cast away the const
70 * qualifier like this since the contract with nvlist
71 * says that values are immutable, and some consumers
72 * will indeed add nodes to the returned nvlist. In
73 * practice, however, it appears to be harmless with the
74 * current nvlist implementation, so we just live with
75 * it until the implementation is reworked.
77 nvl = __DECONST(nvlist_t *,
78 nvlist_get_nvlist(nvl, name));
79 else if (nvlist_exists(nvl, name)) {
80 for (copy = tofree; copy < name; copy++)
84 "Configuration node %s is a child of existing variable %s",
90 * XXX-MJ as with the case above, "new_nvl" shouldn't be
91 * mutated after its ownership is given to "nvl".
93 new_nvl = nvlist_create(0);
95 errx(4, "Failed to allocate memory");
96 nvlist_move_nvlist(nvl, name, new_nvl);
108 create_config_node(const char *path)
111 return (_lookup_config_node(config_root, path, true));
115 find_config_node(const char *path)
118 return (_lookup_config_node(config_root, path, false));
122 create_relative_config_node(nvlist_t *parent, const char *path)
125 return (_lookup_config_node(parent, path, true));
129 find_relative_config_node(nvlist_t *parent, const char *path)
132 return (_lookup_config_node(parent, path, false));
136 set_config_value_node(nvlist_t *parent, const char *name, const char *value)
139 if (strchr(name, '.') != NULL)
140 errx(4, "Invalid config node name %s", name);
142 parent = config_root;
143 if (nvlist_exists_string(parent, name))
144 nvlist_free_string(parent, name);
145 else if (nvlist_exists(parent, name))
147 "Attempting to add value %s to existing node %s of list %p",
148 value, name, parent);
149 nvlist_add_string(parent, name, value);
153 set_config_value_node_if_unset(nvlist_t *const parent, const char *const name,
154 const char *const value)
156 if (get_config_value_node(parent, name) != NULL) {
160 set_config_value_node(parent, name, value);
164 set_config_value(const char *path, const char *value)
170 /* Look for last separator. */
171 name = strrchr(path, '.');
176 node_name = strndup(path, name - path);
177 if (node_name == NULL)
178 errx(4, "Failed to allocate memory");
179 nvl = create_config_node(node_name);
181 errx(4, "Failed to create configuration node %s",
189 if (nvlist_exists_nvlist(nvl, name))
190 errx(4, "Attempting to add value %s to existing node %s",
192 set_config_value_node(nvl, name, value);
196 set_config_value_if_unset(const char *const path, const char *const value)
198 if (get_config_value(path) != NULL) {
202 set_config_value(path, value);
206 get_raw_config_value(const char *path)
212 /* Look for last separator. */
213 name = strrchr(path, '.');
218 node_name = strndup(path, name - path);
219 if (node_name == NULL)
220 errx(4, "Failed to allocate memory");
221 nvl = find_config_node(node_name);
230 if (nvlist_exists_string(nvl, name))
231 return (nvlist_get_string(nvl, name));
232 if (nvlist_exists_nvlist(nvl, name))
233 warnx("Attempting to fetch value of node %s", path);
238 _expand_config_value(const char *value, int depth)
242 char *nestedval, *path, *valbuf;
245 valfp = open_memstream(&valbuf, &valsize);
247 errx(4, "Failed to allocate memory");
250 while (*vp != '\0') {
255 "Too many recursive references in configuration value");
260 if (vp[1] != '(' || vp[2] == '\0')
263 cp = strchr(vp + 2, ')');
266 "Invalid reference in configuration value \"%s\"",
276 "Empty reference in configuration value \"%s\"",
282 /* Allocate a C string holding the path. */
283 path = strndup(vp, cp - vp);
285 errx(4, "Failed to allocate memory");
287 /* Advance 'vp' past the reference. */
290 /* Fetch the referenced value. */
291 cp = get_raw_config_value(path);
294 "Failed to fetch referenced configuration variable %s",
297 nestedval = _expand_config_value(cp, depth + 1);
298 fputs(nestedval, valfp);
307 "Trailing \\ in configuration value \"%s\"",
323 expand_config_value(const char *value)
327 if (strchr(value, '%') == NULL)
331 valbuf = _expand_config_value(value, 0);
336 get_config_value(const char *path)
340 value = get_raw_config_value(path);
343 return (expand_config_value(value));
347 get_config_value_node(const nvlist_t *parent, const char *name)
350 if (strchr(name, '.') != NULL)
351 errx(4, "Invalid config node name %s", name);
353 parent = config_root;
354 if (nvlist_exists_nvlist(parent, name))
355 warnx("Attempt to fetch value of node %s of list %p", name,
357 if (!nvlist_exists_string(parent, name))
360 return (expand_config_value(nvlist_get_string(parent, name)));
364 _bool_value(const char *name, const char *value)
367 if (strcasecmp(value, "true") == 0 ||
368 strcasecmp(value, "on") == 0 ||
369 strcasecmp(value, "yes") == 0 ||
370 strcmp(value, "1") == 0)
372 if (strcasecmp(value, "false") == 0 ||
373 strcasecmp(value, "off") == 0 ||
374 strcasecmp(value, "no") == 0 ||
375 strcmp(value, "0") == 0)
377 err(4, "Invalid value %s for boolean variable %s", value, name);
381 get_config_bool(const char *path)
385 value = get_config_value(path);
387 err(4, "Failed to fetch boolean variable %s", path);
388 return (_bool_value(path, value));
392 get_config_bool_default(const char *path, bool def)
396 value = get_config_value(path);
399 return (_bool_value(path, value));
403 get_config_bool_node(const nvlist_t *parent, const char *name)
407 value = get_config_value_node(parent, name);
409 err(4, "Failed to fetch boolean variable %s", name);
410 return (_bool_value(name, value));
414 get_config_bool_node_default(const nvlist_t *parent, const char *name,
419 value = get_config_value_node(parent, name);
422 return (_bool_value(name, value));
426 set_config_bool(const char *path, bool value)
429 set_config_value(path, value ? "true" : "false");
433 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
436 set_config_value_node(parent, name, value ? "true" : "false");
440 dump_tree(const char *prefix, const nvlist_t *nvl)
447 while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
448 if (type == NV_TYPE_NVLIST) {
451 asprintf(&new_prefix, "%s%s.", prefix, name);
452 dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
455 assert(type == NV_TYPE_STRING);
456 printf("%s%s=%s\n", prefix, name,
457 nvlist_get_string(nvl, name));
465 dump_tree("", config_root);