Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bhyve/config.c
105584 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2021 John H. Baldwin <[email protected]>
5
* Copyright 2026 Hans Rosenfeld
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/cdefs.h>
30
#include <assert.h>
31
#include <err.h>
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
36
#include "config.h"
37
38
static nvlist_t *config_root;
39
40
void
41
init_config(void)
42
{
43
44
config_root = nvlist_create(0);
45
if (config_root == NULL)
46
err(4, "Failed to create configuration root nvlist");
47
}
48
49
static nvlist_t *
50
_lookup_config_node(nvlist_t *parent, const char *path, bool create)
51
{
52
char *copy, *name, *tofree;
53
nvlist_t *nvl, *new_nvl;
54
55
copy = strdup(path);
56
if (copy == NULL)
57
errx(4, "Failed to allocate memory");
58
tofree = copy;
59
nvl = parent;
60
while ((name = strsep(&copy, ".")) != NULL) {
61
if (*name == '\0') {
62
warnx("Invalid configuration node: %s", path);
63
nvl = NULL;
64
break;
65
}
66
if (nvlist_exists_nvlist(nvl, name))
67
/*
68
* XXX-MJ it is incorrect to cast away the const
69
* qualifier like this since the contract with nvlist
70
* says that values are immutable, and some consumers
71
* will indeed add nodes to the returned nvlist. In
72
* practice, however, it appears to be harmless with the
73
* current nvlist implementation, so we just live with
74
* it until the implementation is reworked.
75
*/
76
nvl = __DECONST(nvlist_t *,
77
nvlist_get_nvlist(nvl, name));
78
else if (nvlist_exists(nvl, name)) {
79
for (copy = tofree; copy < name; copy++)
80
if (*copy == '\0')
81
*copy = '.';
82
warnx(
83
"Configuration node %s is a child of existing variable %s",
84
path, tofree);
85
nvl = NULL;
86
break;
87
} else if (create) {
88
/*
89
* XXX-MJ as with the case above, "new_nvl" shouldn't be
90
* mutated after its ownership is given to "nvl".
91
*/
92
new_nvl = nvlist_create(0);
93
if (new_nvl == NULL)
94
errx(4, "Failed to allocate memory");
95
nvlist_move_nvlist(nvl, name, new_nvl);
96
nvl = new_nvl;
97
} else {
98
nvl = NULL;
99
break;
100
}
101
}
102
free(tofree);
103
return (nvl);
104
}
105
106
nvlist_t *
107
create_config_node(const char *path)
108
{
109
110
return (_lookup_config_node(config_root, path, true));
111
}
112
113
nvlist_t *
114
find_config_node(const char *path)
115
{
116
117
return (_lookup_config_node(config_root, path, false));
118
}
119
120
nvlist_t *
121
create_relative_config_node(nvlist_t *parent, const char *path)
122
{
123
124
return (_lookup_config_node(parent, path, true));
125
}
126
127
nvlist_t *
128
find_relative_config_node(nvlist_t *parent, const char *path)
129
{
130
131
return (_lookup_config_node(parent, path, false));
132
}
133
134
void
135
set_config_value_node(nvlist_t *parent, const char *name, const char *value)
136
{
137
138
if (strchr(name, '.') != NULL)
139
errx(4, "Invalid config node name %s", name);
140
if (parent == NULL)
141
parent = config_root;
142
if (nvlist_exists_string(parent, name))
143
nvlist_free_string(parent, name);
144
else if (nvlist_exists(parent, name))
145
errx(4,
146
"Attempting to add value %s to existing node %s of list %p",
147
value, name, parent);
148
nvlist_add_string(parent, name, value);
149
}
150
151
void
152
set_config_value_node_if_unset(nvlist_t *const parent, const char *const name,
153
const char *const value)
154
{
155
if (get_config_value_node(parent, name) != NULL) {
156
return;
157
}
158
159
set_config_value_node(parent, name, value);
160
}
161
162
void
163
set_config_value(const char *path, const char *value)
164
{
165
const char *name;
166
char *node_name;
167
nvlist_t *nvl;
168
169
/* Look for last separator. */
170
name = strrchr(path, '.');
171
if (name == NULL) {
172
nvl = config_root;
173
name = path;
174
} else {
175
node_name = strndup(path, name - path);
176
if (node_name == NULL)
177
errx(4, "Failed to allocate memory");
178
nvl = create_config_node(node_name);
179
if (nvl == NULL)
180
errx(4, "Failed to create configuration node %s",
181
node_name);
182
free(node_name);
183
184
/* Skip over '.'. */
185
name++;
186
}
187
188
if (nvlist_exists_nvlist(nvl, name))
189
errx(4, "Attempting to add value %s to existing node %s",
190
value, path);
191
set_config_value_node(nvl, name, value);
192
}
193
194
void
195
set_config_value_if_unset(const char *const path, const char *const value)
196
{
197
if (get_config_value(path) != NULL) {
198
return;
199
}
200
201
set_config_value(path, value);
202
}
203
204
static const char *
205
get_raw_config_value(const char *path)
206
{
207
const char *name;
208
char *node_name;
209
nvlist_t *nvl;
210
211
/* Look for last separator. */
212
name = strrchr(path, '.');
213
if (name == NULL) {
214
nvl = config_root;
215
name = path;
216
} else {
217
node_name = strndup(path, name - path);
218
if (node_name == NULL)
219
errx(4, "Failed to allocate memory");
220
nvl = find_config_node(node_name);
221
free(node_name);
222
if (nvl == NULL)
223
return (NULL);
224
225
/* Skip over '.'. */
226
name++;
227
}
228
229
if (nvlist_exists_string(nvl, name))
230
return (nvlist_get_string(nvl, name));
231
if (nvlist_exists_nvlist(nvl, name))
232
warnx("Attempting to fetch value of node %s", path);
233
return (NULL);
234
}
235
236
static char *
237
_expand_config_value(const char *value, int depth)
238
{
239
FILE *valfp;
240
const char *cp, *vp;
241
char *nestedval, *path, *valbuf;
242
size_t valsize;
243
244
valfp = open_memstream(&valbuf, &valsize);
245
if (valfp == NULL)
246
errx(4, "Failed to allocate memory");
247
248
vp = value;
249
while (*vp != '\0') {
250
switch (*vp) {
251
case '%':
252
if (depth > 15) {
253
warnx(
254
"Too many recursive references in configuration value");
255
fputc('%', valfp);
256
vp++;
257
break;
258
}
259
if (vp[1] != '(' || vp[2] == '\0')
260
cp = NULL;
261
else
262
cp = strchr(vp + 2, ')');
263
if (cp == NULL) {
264
warnx(
265
"Invalid reference in configuration value \"%s\"",
266
value);
267
fputc('%', valfp);
268
vp++;
269
break;
270
}
271
vp += 2;
272
273
if (cp == vp) {
274
warnx(
275
"Empty reference in configuration value \"%s\"",
276
value);
277
vp++;
278
break;
279
}
280
281
/* Allocate a C string holding the path. */
282
path = strndup(vp, cp - vp);
283
if (path == NULL)
284
errx(4, "Failed to allocate memory");
285
286
/* Advance 'vp' past the reference. */
287
vp = cp + 1;
288
289
/* Fetch the referenced value. */
290
cp = get_raw_config_value(path);
291
if (cp == NULL)
292
warnx(
293
"Failed to fetch referenced configuration variable %s",
294
path);
295
else {
296
nestedval = _expand_config_value(cp, depth + 1);
297
fputs(nestedval, valfp);
298
free(nestedval);
299
}
300
free(path);
301
break;
302
case '\\':
303
vp++;
304
if (*vp == '\0') {
305
warnx(
306
"Trailing \\ in configuration value \"%s\"",
307
value);
308
break;
309
}
310
/* FALLTHROUGH */
311
default:
312
fputc(*vp, valfp);
313
vp++;
314
break;
315
}
316
}
317
fclose(valfp);
318
return (valbuf);
319
}
320
321
static const char *
322
expand_config_value(const char *value)
323
{
324
static char *valbuf;
325
326
if (strchr(value, '%') == NULL)
327
return (value);
328
329
free(valbuf);
330
valbuf = _expand_config_value(value, 0);
331
return (valbuf);
332
}
333
334
const char *
335
get_config_value(const char *path)
336
{
337
const char *value;
338
339
value = get_raw_config_value(path);
340
if (value == NULL)
341
return (NULL);
342
return (expand_config_value(value));
343
}
344
345
const char *
346
get_config_value_node(const nvlist_t *parent, const char *name)
347
{
348
349
if (strchr(name, '.') != NULL)
350
errx(4, "Invalid config node name %s", name);
351
if (parent == NULL)
352
parent = config_root;
353
if (nvlist_exists_nvlist(parent, name))
354
warnx("Attempt to fetch value of node %s of list %p", name,
355
parent);
356
if (!nvlist_exists_string(parent, name))
357
return (NULL);
358
359
return (expand_config_value(nvlist_get_string(parent, name)));
360
}
361
362
static bool
363
_bool_value(const char *name, const char *value)
364
{
365
366
if (strcasecmp(value, "true") == 0 ||
367
strcasecmp(value, "on") == 0 ||
368
strcasecmp(value, "yes") == 0 ||
369
strcmp(value, "1") == 0)
370
return (true);
371
if (strcasecmp(value, "false") == 0 ||
372
strcasecmp(value, "off") == 0 ||
373
strcasecmp(value, "no") == 0 ||
374
strcmp(value, "0") == 0)
375
return (false);
376
err(4, "Invalid value %s for boolean variable %s", value, name);
377
}
378
379
bool
380
get_config_bool(const char *path)
381
{
382
const char *value;
383
384
value = get_config_value(path);
385
if (value == NULL)
386
err(4, "Failed to fetch boolean variable %s", path);
387
return (_bool_value(path, value));
388
}
389
390
bool
391
get_config_bool_default(const char *path, bool def)
392
{
393
const char *value;
394
395
value = get_config_value(path);
396
if (value == NULL)
397
return (def);
398
return (_bool_value(path, value));
399
}
400
401
bool
402
get_config_bool_node(const nvlist_t *parent, const char *name)
403
{
404
const char *value;
405
406
value = get_config_value_node(parent, name);
407
if (value == NULL)
408
err(4, "Failed to fetch boolean variable %s", name);
409
return (_bool_value(name, value));
410
}
411
412
bool
413
get_config_bool_node_default(const nvlist_t *parent, const char *name,
414
bool def)
415
{
416
const char *value;
417
418
value = get_config_value_node(parent, name);
419
if (value == NULL)
420
return (def);
421
return (_bool_value(name, value));
422
}
423
424
void
425
set_config_bool(const char *path, bool value)
426
{
427
428
set_config_value(path, value ? "true" : "false");
429
}
430
431
void
432
set_config_bool_node(nvlist_t *parent, const char *name, bool value)
433
{
434
435
set_config_value_node(parent, name, value ? "true" : "false");
436
}
437
438
int
439
walk_config_nodes(const char *prefix, const nvlist_t *parent, void *arg,
440
int (*cb)(const char *, const nvlist_t *, const char *, int, void *))
441
{
442
void *cookie = NULL;
443
const char *name;
444
int type;
445
446
while ((name = nvlist_next(parent, &type, &cookie)) != NULL) {
447
int ret;
448
449
ret = cb(prefix, parent, name, type, arg);
450
if (ret != 0)
451
return (ret);
452
}
453
454
return (0);
455
}
456
457
static int
458
dump_node_cb(const char *prefix, const nvlist_t *parent, const char *name,
459
int type, void *arg)
460
{
461
if (type == NV_TYPE_NVLIST) {
462
char *new_prefix;
463
int ret;
464
465
asprintf(&new_prefix, "%s%s.", prefix, name);
466
ret = walk_config_nodes(new_prefix,
467
nvlist_get_nvlist(parent, name), arg, dump_node_cb);
468
free(new_prefix);
469
return (ret);
470
}
471
472
assert(type == NV_TYPE_STRING);
473
printf("%s%s=%s\n", prefix, name, nvlist_get_string(parent, name));
474
return (0);
475
}
476
477
void
478
dump_config(void)
479
{
480
(void)walk_config_nodes("", config_root, NULL, dump_node_cb);
481
}
482
483