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>
37 static nvlist_t *config_root;
43 config_root = nvlist_create(0);
44 if (config_root == NULL)
45 err(4, "Failed to create configuration root nvlist");
49 _lookup_config_node(nvlist_t *parent, const char *path, bool create)
51 char *copy, *name, *tofree;
52 nvlist_t *nvl, *new_nvl;
56 errx(4, "Failed to allocate memory");
59 while ((name = strsep(©, ".")) != NULL) {
61 warnx("Invalid configuration node: %s", path);
65 if (nvlist_exists_nvlist(nvl, name))
67 * XXX-MJ it is incorrect to cast away the const
68 * qualifier like this since the contract with nvlist
69 * says that values are immutable, and some consumers
70 * will indeed add nodes to the returned nvlist. In
71 * practice, however, it appears to be harmless with the
72 * current nvlist implementation, so we just live with
73 * it until the implementation is reworked.
75 nvl = __DECONST(nvlist_t *,
76 nvlist_get_nvlist(nvl, name));
77 else if (nvlist_exists(nvl, name)) {
78 for (copy = tofree; copy < name; copy++)
82 "Configuration node %s is a child of existing variable %s",
88 * XXX-MJ as with the case above, "new_nvl" shouldn't be
89 * mutated after its ownership is given to "nvl".
91 new_nvl = nvlist_create(0);
93 errx(4, "Failed to allocate memory");
94 nvlist_move_nvlist(nvl, name, new_nvl);
106 create_config_node(const char *path)
109 return (_lookup_config_node(config_root, path, true));
113 find_config_node(const char *path)
116 return (_lookup_config_node(config_root, path, false));
120 create_relative_config_node(nvlist_t *parent, const char *path)
123 return (_lookup_config_node(parent, path, true));
127 find_relative_config_node(nvlist_t *parent, const char *path)
130 return (_lookup_config_node(parent, path, false));
134 set_config_value_node(nvlist_t *parent, const char *name, const char *value)
137 if (strchr(name, '.') != NULL)
138 errx(4, "Invalid config node name %s", name);
140 parent = config_root;
141 if (nvlist_exists_string(parent, name))
142 nvlist_free_string(parent, name);
143 else if (nvlist_exists(parent, name))
145 "Attemping to add value %s to existing node %s of list %p",
146 value, name, parent);
147 nvlist_add_string(parent, name, value);
151 set_config_value_node_if_unset(nvlist_t *const parent, const char *const name,
152 const char *const value)
154 if (get_config_value_node(parent, name) != NULL) {
158 set_config_value_node(parent, name, value);
162 set_config_value(const char *path, const char *value)
168 /* Look for last separator. */
169 name = strrchr(path, '.');
174 node_name = strndup(path, name - path);
175 if (node_name == NULL)
176 errx(4, "Failed to allocate memory");
177 nvl = create_config_node(node_name);
179 errx(4, "Failed to create configuration node %s",
187 if (nvlist_exists_nvlist(nvl, name))
188 errx(4, "Attempting to add value %s to existing node %s",
190 set_config_value_node(nvl, name, value);
194 set_config_value_if_unset(const char *const path, const char *const value)
196 if (get_config_value(path) != NULL) {
200 set_config_value(path, value);
204 get_raw_config_value(const char *path)
210 /* Look for last separator. */
211 name = strrchr(path, '.');
216 node_name = strndup(path, name - path);
217 if (node_name == NULL)
218 errx(4, "Failed to allocate memory");
219 nvl = find_config_node(node_name);
228 if (nvlist_exists_string(nvl, name))
229 return (nvlist_get_string(nvl, name));
230 if (nvlist_exists_nvlist(nvl, name))
231 warnx("Attempting to fetch value of node %s", path);
236 _expand_config_value(const char *value, int depth)
240 char *nestedval, *path, *valbuf;
243 valfp = open_memstream(&valbuf, &valsize);
245 errx(4, "Failed to allocate memory");
248 while (*vp != '\0') {
253 "Too many recursive references in configuration value");
258 if (vp[1] != '(' || vp[2] == '\0')
261 cp = strchr(vp + 2, ')');
264 "Invalid reference in configuration value \"%s\"",
274 "Empty reference in configuration value \"%s\"",
280 /* Allocate a C string holding the path. */
281 path = strndup(vp, cp - vp);
283 errx(4, "Failed to allocate memory");
285 /* Advance 'vp' past the reference. */
288 /* Fetch the referenced value. */
289 cp = get_raw_config_value(path);
292 "Failed to fetch referenced configuration variable %s",
295 nestedval = _expand_config_value(cp, depth + 1);
296 fputs(nestedval, valfp);
305 "Trailing \\ in configuration value \"%s\"",
321 expand_config_value(const char *value)
325 if (strchr(value, '%') == NULL)
329 valbuf = _expand_config_value(value, 0);
334 get_config_value(const char *path)
338 value = get_raw_config_value(path);
341 return (expand_config_value(value));
345 get_config_value_node(const nvlist_t *parent, const char *name)
348 if (strchr(name, '.') != NULL)
349 errx(4, "Invalid config node name %s", name);
351 parent = config_root;
352 if (nvlist_exists_nvlist(parent, name))
353 warnx("Attempt to fetch value of node %s of list %p", name,
355 if (!nvlist_exists_string(parent, name))
358 return (expand_config_value(nvlist_get_string(parent, name)));
362 _bool_value(const char *name, const char *value)
365 if (strcasecmp(value, "true") == 0 ||
366 strcasecmp(value, "on") == 0 ||
367 strcasecmp(value, "yes") == 0 ||
368 strcmp(value, "1") == 0)
370 if (strcasecmp(value, "false") == 0 ||
371 strcasecmp(value, "off") == 0 ||
372 strcasecmp(value, "no") == 0 ||
373 strcmp(value, "0") == 0)
375 err(4, "Invalid value %s for boolean variable %s", value, name);
379 get_config_bool(const char *path)
383 value = get_config_value(path);
385 err(4, "Failed to fetch boolean variable %s", path);
386 return (_bool_value(path, value));
390 get_config_bool_default(const char *path, bool def)
394 value = get_config_value(path);
397 return (_bool_value(path, value));
401 get_config_bool_node(const nvlist_t *parent, const char *name)
405 value = get_config_value_node(parent, name);
407 err(4, "Failed to fetch boolean variable %s", name);
408 return (_bool_value(name, value));
412 get_config_bool_node_default(const nvlist_t *parent, const char *name,
417 value = get_config_value_node(parent, name);
420 return (_bool_value(name, value));
424 set_config_bool(const char *path, bool value)
427 set_config_value(path, value ? "true" : "false");
431 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
434 set_config_value_node(parent, name, value ? "true" : "false");
438 dump_tree(const char *prefix, const nvlist_t *nvl)
445 while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
446 if (type == NV_TYPE_NVLIST) {
449 asprintf(&new_prefix, "%s%s.", prefix, name);
450 dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
453 assert(type == NV_TYPE_STRING);
454 printf("%s%s=%s\n", prefix, name,
455 nvlist_get_string(nvl, name));
463 dump_tree("", config_root);