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(const char *path, const char *value)
145 /* Look for last separator. */
146 name = strrchr(path, '.');
151 node_name = strndup(path, name - path);
152 if (node_name == NULL)
153 errx(4, "Failed to allocate memory");
154 nvl = create_config_node(node_name);
156 errx(4, "Failed to create configuration node %s",
164 if (nvlist_exists_nvlist(nvl, name))
165 errx(4, "Attempting to add value %s to existing node %s",
167 set_config_value_node(nvl, name, value);
171 get_raw_config_value(const char *path)
177 /* Look for last separator. */
178 name = strrchr(path, '.');
183 node_name = strndup(path, name - path);
184 if (node_name == NULL)
185 errx(4, "Failed to allocate memory");
186 nvl = find_config_node(node_name);
195 if (nvlist_exists_string(nvl, name))
196 return (nvlist_get_string(nvl, name));
197 if (nvlist_exists_nvlist(nvl, name))
198 warnx("Attempting to fetch value of node %s", path);
203 _expand_config_value(const char *value, int depth)
207 char *nestedval, *path, *valbuf;
210 valfp = open_memstream(&valbuf, &valsize);
212 errx(4, "Failed to allocate memory");
215 while (*vp != '\0') {
220 "Too many recursive references in configuration value");
225 if (vp[1] != '(' || vp[2] == '\0')
228 cp = strchr(vp + 2, ')');
231 "Invalid reference in configuration value \"%s\"",
241 "Empty reference in configuration value \"%s\"",
247 /* Allocate a C string holding the path. */
248 path = strndup(vp, cp - vp);
250 errx(4, "Failed to allocate memory");
252 /* Advance 'vp' past the reference. */
255 /* Fetch the referenced value. */
256 cp = get_raw_config_value(path);
259 "Failed to fetch referenced configuration variable %s",
262 nestedval = _expand_config_value(cp, depth + 1);
263 fputs(nestedval, valfp);
272 "Trailing \\ in configuration value \"%s\"",
288 expand_config_value(const char *value)
292 if (strchr(value, '%') == NULL)
296 valbuf = _expand_config_value(value, 0);
301 get_config_value(const char *path)
305 value = get_raw_config_value(path);
308 return (expand_config_value(value));
312 get_config_value_node(const nvlist_t *parent, const char *name)
315 if (strchr(name, '.') != NULL)
316 errx(4, "Invalid config node name %s", name);
318 parent = config_root;
319 if (nvlist_exists_nvlist(parent, name))
320 warnx("Attempt to fetch value of node %s of list %p", name,
322 if (!nvlist_exists_string(parent, name))
325 return (expand_config_value(nvlist_get_string(parent, name)));
329 _bool_value(const char *name, const char *value)
332 if (strcasecmp(value, "true") == 0 ||
333 strcasecmp(value, "on") == 0 ||
334 strcasecmp(value, "yes") == 0 ||
335 strcmp(value, "1") == 0)
337 if (strcasecmp(value, "false") == 0 ||
338 strcasecmp(value, "off") == 0 ||
339 strcasecmp(value, "no") == 0 ||
340 strcmp(value, "0") == 0)
342 err(4, "Invalid value %s for boolean variable %s", value, name);
346 get_config_bool(const char *path)
350 value = get_config_value(path);
352 err(4, "Failed to fetch boolean variable %s", path);
353 return (_bool_value(path, value));
357 get_config_bool_default(const char *path, bool def)
361 value = get_config_value(path);
364 return (_bool_value(path, value));
368 get_config_bool_node(const nvlist_t *parent, const char *name)
372 value = get_config_value_node(parent, name);
374 err(4, "Failed to fetch boolean variable %s", name);
375 return (_bool_value(name, value));
379 get_config_bool_node_default(const nvlist_t *parent, const char *name,
384 value = get_config_value_node(parent, name);
387 return (_bool_value(name, value));
391 set_config_bool(const char *path, bool value)
394 set_config_value(path, value ? "true" : "false");
398 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
401 set_config_value_node(parent, name, value ? "true" : "false");
405 dump_tree(const char *prefix, const nvlist_t *nvl)
412 while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
413 if (type == NV_TYPE_NVLIST) {
416 asprintf(&new_prefix, "%s%s.", prefix, name);
417 dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
420 assert(type == NV_TYPE_STRING);
421 printf("%s%s=%s\n", prefix, name,
422 nvlist_get_string(nvl, name));
430 dump_tree("", config_root);