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$");
37 #include <sys/sysmacros.h>
42 static nvlist_t
*config_root
;
47 config_root
= nvlist_create(0);
48 if (config_root
== NULL
)
49 err(4, "Failed to create configuration root nvlist");
53 _lookup_config_node(nvlist_t
*parent
, const char *path
, bool create
)
55 char *copy
, *name
, *tofree
;
56 nvlist_t
*nvl
, *new_nvl
;
60 errx(4, "Failed to allocate memory");
63 while ((name
= strsep(©
, ".")) != NULL
) {
65 warnx("Invalid configuration node: %s", path
);
69 if (nvlist_exists_nvlist(nvl
, name
))
71 * XXX-MJ it is incorrect to cast away the const
72 * qualifier like this since the contract with nvlist
73 * says that values are immutable, and some consumers
74 * will indeed add nodes to the returned nvlist. In
75 * practice, however, it appears to be harmless with the
76 * current nvlist implementation, so we just live with
77 * it until the implementation is reworked.
79 nvl
= __DECONST(nvlist_t
*,
80 nvlist_get_nvlist(nvl
, name
));
81 else if (nvlist_exists(nvl
, name
)) {
82 for (copy
= tofree
; copy
< name
; copy
++)
86 "Configuration node %s is a child of existing variable %s",
92 * XXX-MJ as with the case above, "new_nvl" shouldn't be
93 * mutated after its ownership is given to "nvl".
95 new_nvl
= nvlist_create(0);
97 errx(4, "Failed to allocate memory");
99 nvlist_move_nvlist(nvl
, name
, new_nvl
);
101 if (nvlist_add_nvlist(nvl
, name
, new_nvl
) != 0)
102 errx(4, "Failed to allocate memory");
103 (void) nvlist_free(new_nvl
);
104 if (nvlist_lookup_nvlist(nvl
, name
, &new_nvl
) != 0)
105 errx(4, "Failed to retrieve new nvlist");
118 create_config_node(const char *path
)
121 return (_lookup_config_node(config_root
, path
, true));
125 find_config_node(const char *path
)
128 return (_lookup_config_node(config_root
, path
, false));
132 create_relative_config_node(nvlist_t
*parent
, const char *path
)
135 return (_lookup_config_node(parent
, path
, true));
139 find_relative_config_node(nvlist_t
*parent
, const char *path
)
142 return (_lookup_config_node(parent
, path
, false));
146 set_config_value_node(nvlist_t
*parent
, const char *name
, const char *value
)
149 if (strchr(name
, '.') != NULL
)
150 errx(4, "Invalid config node name %s", name
);
152 parent
= config_root
;
153 if (nvlist_exists_string(parent
, name
))
154 nvlist_free_string(parent
, name
);
155 else if (nvlist_exists(parent
, name
))
157 "Attemping to add value %s to existing node %s of list %p",
158 value
, name
, parent
);
159 nvlist_add_string(parent
, name
, value
);
163 set_config_value_node_if_unset(nvlist_t
*const parent
, const char *const name
,
164 const char *const value
)
166 if (get_config_value_node(parent
, name
) != NULL
) {
170 set_config_value_node(parent
, name
, value
);
174 set_config_value(const char *path
, const char *value
)
180 /* Look for last separator. */
181 name
= strrchr(path
, '.');
186 node_name
= strndup(path
, name
- path
);
187 if (node_name
== NULL
)
188 errx(4, "Failed to allocate memory");
189 nvl
= create_config_node(node_name
);
191 errx(4, "Failed to create configuration node %s",
199 if (nvlist_exists_nvlist(nvl
, name
))
200 errx(4, "Attempting to add value %s to existing node %s",
202 set_config_value_node(nvl
, name
, value
);
206 set_config_value_if_unset(const char *const path
, const char *const value
)
208 if (get_config_value(path
) != NULL
) {
212 set_config_value(path
, value
);
216 get_raw_config_value(const char *path
)
222 /* Look for last separator. */
223 name
= strrchr(path
, '.');
228 node_name
= strndup(path
, name
- path
);
229 if (node_name
== NULL
)
230 errx(4, "Failed to allocate memory");
231 nvl
= find_config_node(node_name
);
240 if (nvlist_exists_string(nvl
, name
))
241 return (nvlist_get_string(nvl
, name
));
242 if (nvlist_exists_nvlist(nvl
, name
))
243 warnx("Attempting to fetch value of node %s", path
);
248 _expand_config_value(const char *value
, int depth
)
252 char *nestedval
, *path
, *valbuf
;
255 valfp
= open_memstream(&valbuf
, &valsize
);
257 errx(4, "Failed to allocate memory");
260 while (*vp
!= '\0') {
265 "Too many recursive references in configuration value");
270 if (vp
[1] != '(' || vp
[2] == '\0')
273 cp
= strchr(vp
+ 2, ')');
276 "Invalid reference in configuration value \"%s\"",
286 "Empty reference in configuration value \"%s\"",
292 /* Allocate a C string holding the path. */
293 path
= strndup(vp
, cp
- vp
);
295 errx(4, "Failed to allocate memory");
297 /* Advance 'vp' past the reference. */
300 /* Fetch the referenced value. */
301 cp
= get_raw_config_value(path
);
304 "Failed to fetch referenced configuration variable %s",
307 nestedval
= _expand_config_value(cp
, depth
+ 1);
308 fputs(nestedval
, valfp
);
317 "Trailing \\ in configuration value \"%s\"",
333 expand_config_value(const char *value
)
337 if (strchr(value
, '%') == NULL
)
341 valbuf
= _expand_config_value(value
, 0);
346 get_config_value(const char *path
)
350 value
= get_raw_config_value(path
);
353 return (expand_config_value(value
));
357 get_config_value_node(const nvlist_t
*parent
, const char *name
)
360 if (strchr(name
, '.') != NULL
)
361 errx(4, "Invalid config node name %s", name
);
363 parent
= config_root
;
365 if (nvlist_exists_nvlist(parent
, name
))
366 warnx("Attempt to fetch value of node %s of list %p", name
,
368 if (!nvlist_exists_string(parent
, name
))
371 return (expand_config_value(nvlist_get_string(parent
, name
)));
375 _bool_value(const char *name
, const char *value
)
378 if (strcasecmp(value
, "true") == 0 ||
379 strcasecmp(value
, "on") == 0 ||
380 strcasecmp(value
, "yes") == 0 ||
381 strcmp(value
, "1") == 0)
383 if (strcasecmp(value
, "false") == 0 ||
384 strcasecmp(value
, "off") == 0 ||
385 strcasecmp(value
, "no") == 0 ||
386 strcmp(value
, "0") == 0)
388 err(4, "Invalid value %s for boolean variable %s", value
, name
);
392 get_config_bool(const char *path
)
396 value
= get_config_value(path
);
398 err(4, "Failed to fetch boolean variable %s", path
);
399 return (_bool_value(path
, value
));
403 get_config_bool_default(const char *path
, bool def
)
407 value
= get_config_value(path
);
410 return (_bool_value(path
, value
));
414 get_config_bool_node(const nvlist_t
*parent
, const char *name
)
418 value
= get_config_value_node(parent
, name
);
420 err(4, "Failed to fetch boolean variable %s", name
);
421 return (_bool_value(name
, value
));
425 get_config_bool_node_default(const nvlist_t
*parent
, const char *name
,
430 value
= get_config_value_node(parent
, name
);
433 return (_bool_value(name
, value
));
437 set_config_bool(const char *path
, bool value
)
440 set_config_value(path
, value
? "true" : "false");
444 set_config_bool_node(nvlist_t
*parent
, const char *name
, bool value
)
447 set_config_value_node(parent
, name
, value
? "true" : "false");
451 dump_tree(const char *prefix
, const nvlist_t
*nvl
)
458 while ((name
= nvlist_next(nvl
, &type
, &cookie
)) != NULL
) {
459 if (type
== NV_TYPE_NVLIST
) {
462 asprintf(&new_prefix
, "%s%s.", prefix
, name
);
463 dump_tree(new_prefix
, nvlist_get_nvlist(nvl
, name
));
466 assert(type
== NV_TYPE_STRING
);
467 printf("%s%s=%s\n", prefix
, name
,
468 nvlist_get_string(nvl
, name
));
476 dump_tree("", config_root
);