15325 bhyve upstream sync 2023 January
[illumos-gate.git] / usr / src / cmd / bhyve / config.c
blob214bb0daf608da7615e841148d20af038c6bedd9
1 /*-
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
8 * are met:
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
25 * SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <assert.h>
32 #include <err.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #ifndef __FreeBSD__
37 #include <sys/sysmacros.h>
38 #endif
40 #include "config.h"
42 static nvlist_t *config_root;
44 void
45 init_config(void)
47 config_root = nvlist_create(0);
48 if (config_root == NULL)
49 err(4, "Failed to create configuration root nvlist");
52 static nvlist_t *
53 _lookup_config_node(nvlist_t *parent, const char *path, bool create)
55 char *copy, *name, *tofree;
56 nvlist_t *nvl, *new_nvl;
58 copy = strdup(path);
59 if (copy == NULL)
60 errx(4, "Failed to allocate memory");
61 tofree = copy;
62 nvl = parent;
63 while ((name = strsep(&copy, ".")) != NULL) {
64 if (*name == '\0') {
65 warnx("Invalid configuration node: %s", path);
66 nvl = NULL;
67 break;
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++)
83 if (*copy == '\0')
84 *copy = '.';
85 warnx(
86 "Configuration node %s is a child of existing variable %s",
87 path, tofree);
88 nvl = NULL;
89 break;
90 } else if (create) {
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);
96 if (new_nvl == NULL)
97 errx(4, "Failed to allocate memory");
98 #ifdef __FreeBSD__
99 nvlist_move_nvlist(nvl, name, new_nvl);
100 #else
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");
106 #endif
107 nvl = new_nvl;
108 } else {
109 nvl = NULL;
110 break;
113 free(tofree);
114 return (nvl);
117 nvlist_t *
118 create_config_node(const char *path)
121 return (_lookup_config_node(config_root, path, true));
124 nvlist_t *
125 find_config_node(const char *path)
128 return (_lookup_config_node(config_root, path, false));
131 nvlist_t *
132 create_relative_config_node(nvlist_t *parent, const char *path)
135 return (_lookup_config_node(parent, path, true));
138 nvlist_t *
139 find_relative_config_node(nvlist_t *parent, const char *path)
142 return (_lookup_config_node(parent, path, false));
145 void
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);
151 if (parent == NULL)
152 parent = config_root;
153 if (nvlist_exists_string(parent, name))
154 nvlist_free_string(parent, name);
155 else if (nvlist_exists(parent, name))
156 errx(4,
157 "Attemping to add value %s to existing node %s of list %p",
158 value, name, parent);
159 nvlist_add_string(parent, name, value);
162 void
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) {
167 return;
170 set_config_value_node(parent, name, value);
173 void
174 set_config_value(const char *path, const char *value)
176 const char *name;
177 char *node_name;
178 nvlist_t *nvl;
180 /* Look for last separator. */
181 name = strrchr(path, '.');
182 if (name == NULL) {
183 nvl = config_root;
184 name = path;
185 } else {
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);
190 if (nvl == NULL)
191 errx(4, "Failed to create configuration node %s",
192 node_name);
193 free(node_name);
195 /* Skip over '.'. */
196 name++;
199 if (nvlist_exists_nvlist(nvl, name))
200 errx(4, "Attempting to add value %s to existing node %s",
201 value, path);
202 set_config_value_node(nvl, name, value);
205 void
206 set_config_value_if_unset(const char *const path, const char *const value)
208 if (get_config_value(path) != NULL) {
209 return;
212 set_config_value(path, value);
215 static const char *
216 get_raw_config_value(const char *path)
218 const char *name;
219 char *node_name;
220 nvlist_t *nvl;
222 /* Look for last separator. */
223 name = strrchr(path, '.');
224 if (name == NULL) {
225 nvl = config_root;
226 name = path;
227 } else {
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);
232 free(node_name);
233 if (nvl == NULL)
234 return (NULL);
236 /* Skip over '.'. */
237 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);
244 return (NULL);
247 static char *
248 _expand_config_value(const char *value, int depth)
250 FILE *valfp;
251 const char *cp, *vp;
252 char *nestedval, *path, *valbuf;
253 size_t valsize;
255 valfp = open_memstream(&valbuf, &valsize);
256 if (valfp == NULL)
257 errx(4, "Failed to allocate memory");
259 vp = value;
260 while (*vp != '\0') {
261 switch (*vp) {
262 case '%':
263 if (depth > 15) {
264 warnx(
265 "Too many recursive references in configuration value");
266 fputc('%', valfp);
267 vp++;
268 break;
270 if (vp[1] != '(' || vp[2] == '\0')
271 cp = NULL;
272 else
273 cp = strchr(vp + 2, ')');
274 if (cp == NULL) {
275 warnx(
276 "Invalid reference in configuration value \"%s\"",
277 value);
278 fputc('%', valfp);
279 vp++;
280 break;
282 vp += 2;
284 if (cp == vp) {
285 warnx(
286 "Empty reference in configuration value \"%s\"",
287 value);
288 vp++;
289 break;
292 /* Allocate a C string holding the path. */
293 path = strndup(vp, cp - vp);
294 if (path == NULL)
295 errx(4, "Failed to allocate memory");
297 /* Advance 'vp' past the reference. */
298 vp = cp + 1;
300 /* Fetch the referenced value. */
301 cp = get_raw_config_value(path);
302 if (cp == NULL)
303 warnx(
304 "Failed to fetch referenced configuration variable %s",
305 path);
306 else {
307 nestedval = _expand_config_value(cp, depth + 1);
308 fputs(nestedval, valfp);
309 free(nestedval);
311 free(path);
312 break;
313 case '\\':
314 vp++;
315 if (*vp == '\0') {
316 warnx(
317 "Trailing \\ in configuration value \"%s\"",
318 value);
319 break;
321 /* FALLTHROUGH */
322 default:
323 fputc(*vp, valfp);
324 vp++;
325 break;
328 fclose(valfp);
329 return (valbuf);
332 static const char *
333 expand_config_value(const char *value)
335 static char *valbuf;
337 if (strchr(value, '%') == NULL)
338 return (value);
340 free(valbuf);
341 valbuf = _expand_config_value(value, 0);
342 return (valbuf);
345 const char *
346 get_config_value(const char *path)
348 const char *value;
350 value = get_raw_config_value(path);
351 if (value == NULL)
352 return (NULL);
353 return (expand_config_value(value));
356 const char *
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);
362 if (parent == NULL)
363 parent = config_root;
365 if (nvlist_exists_nvlist(parent, name))
366 warnx("Attempt to fetch value of node %s of list %p", name,
367 parent);
368 if (!nvlist_exists_string(parent, name))
369 return (NULL);
371 return (expand_config_value(nvlist_get_string(parent, name)));
374 static bool
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)
382 return (true);
383 if (strcasecmp(value, "false") == 0 ||
384 strcasecmp(value, "off") == 0 ||
385 strcasecmp(value, "no") == 0 ||
386 strcmp(value, "0") == 0)
387 return (false);
388 err(4, "Invalid value %s for boolean variable %s", value, name);
391 bool
392 get_config_bool(const char *path)
394 const char *value;
396 value = get_config_value(path);
397 if (value == NULL)
398 err(4, "Failed to fetch boolean variable %s", path);
399 return (_bool_value(path, value));
402 bool
403 get_config_bool_default(const char *path, bool def)
405 const char *value;
407 value = get_config_value(path);
408 if (value == NULL)
409 return (def);
410 return (_bool_value(path, value));
413 bool
414 get_config_bool_node(const nvlist_t *parent, const char *name)
416 const char *value;
418 value = get_config_value_node(parent, name);
419 if (value == NULL)
420 err(4, "Failed to fetch boolean variable %s", name);
421 return (_bool_value(name, value));
424 bool
425 get_config_bool_node_default(const nvlist_t *parent, const char *name,
426 bool def)
428 const char *value;
430 value = get_config_value_node(parent, name);
431 if (value == NULL)
432 return (def);
433 return (_bool_value(name, value));
436 void
437 set_config_bool(const char *path, bool value)
440 set_config_value(path, value ? "true" : "false");
443 void
444 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
447 set_config_value_node(parent, name, value ? "true" : "false");
450 static void
451 dump_tree(const char *prefix, const nvlist_t *nvl)
453 const char *name;
454 void *cookie;
455 int type;
457 cookie = NULL;
458 while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
459 if (type == NV_TYPE_NVLIST) {
460 char *new_prefix;
462 asprintf(&new_prefix, "%s%s.", prefix, name);
463 dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
464 free(new_prefix);
465 } else {
466 assert(type == NV_TYPE_STRING);
467 printf("%s%s=%s\n", prefix, name,
468 nvlist_get_string(nvl, name));
473 void
474 dump_config(void)
476 dump_tree("", config_root);