]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/config.c
Update the Arm Optimized Routine library to v23.01
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / config.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021 John H. Baldwin <jhb@FreeBSD.org>
5  *
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.
14  *
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.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <assert.h>
32 #include <err.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "config.h"
38
39 static nvlist_t *config_root;
40
41 void
42 init_config(void)
43 {
44
45         config_root = nvlist_create(0);
46         if (config_root == NULL)
47                 err(4, "Failed to create configuration root nvlist");
48 }
49
50 static nvlist_t *
51 _lookup_config_node(nvlist_t *parent, const char *path, bool create)
52 {
53         char *copy, *name, *tofree;
54         nvlist_t *nvl, *new_nvl;
55
56         copy = strdup(path);
57         if (copy == NULL)
58                 errx(4, "Failed to allocate memory");
59         tofree = copy;
60         nvl = parent;
61         while ((name = strsep(&copy, ".")) != NULL) {
62                 if (*name == '\0') {
63                         warnx("Invalid configuration node: %s", path);
64                         nvl = NULL;
65                         break;
66                 }
67                 if (nvlist_exists_nvlist(nvl, name))
68                         /*
69                          * XXX-MJ it is incorrect to cast away the const
70                          * qualifier like this since the contract with nvlist
71                          * says that values are immutable, and some consumers
72                          * will indeed add nodes to the returned nvlist.  In
73                          * practice, however, it appears to be harmless with the
74                          * current nvlist implementation, so we just live with
75                          * it until the implementation is reworked.
76                          */
77                         nvl = __DECONST(nvlist_t *,
78                             nvlist_get_nvlist(nvl, name));
79                 else if (nvlist_exists(nvl, name)) {
80                         for (copy = tofree; copy < name; copy++)
81                                 if (*copy == '\0')
82                                         *copy = '.';
83                         warnx(
84                     "Configuration node %s is a child of existing variable %s",
85                             path, tofree);
86                         nvl = NULL;
87                         break;
88                 } else if (create) {
89                         /*
90                          * XXX-MJ as with the case above, "new_nvl" shouldn't be
91                          * mutated after its ownership is given to "nvl".
92                          */
93                         new_nvl = nvlist_create(0);
94                         if (new_nvl == NULL)
95                                 errx(4, "Failed to allocate memory");
96                         nvlist_move_nvlist(nvl, name, new_nvl);
97                         nvl = new_nvl;
98                 } else {
99                         nvl = NULL;
100                         break;
101                 }
102         }
103         free(tofree);
104         return (nvl);
105 }
106
107 nvlist_t *
108 create_config_node(const char *path)
109 {
110
111         return (_lookup_config_node(config_root, path, true));
112 }
113
114 nvlist_t *
115 find_config_node(const char *path)
116 {
117
118         return (_lookup_config_node(config_root, path, false));
119 }
120
121 nvlist_t *
122 create_relative_config_node(nvlist_t *parent, const char *path)
123 {
124
125         return (_lookup_config_node(parent, path, true));
126 }
127
128 nvlist_t *
129 find_relative_config_node(nvlist_t *parent, const char *path)
130 {
131
132         return (_lookup_config_node(parent, path, false));
133 }
134
135 void
136 set_config_value_node(nvlist_t *parent, const char *name, const char *value)
137 {
138
139         if (strchr(name, '.') != NULL)
140                 errx(4, "Invalid config node name %s", name);
141         if (parent == NULL)
142                 parent = config_root;
143         if (nvlist_exists_string(parent, name))
144                 nvlist_free_string(parent, name);
145         else if (nvlist_exists(parent, name))
146                 errx(4,
147                     "Attempting to add value %s to existing node %s of list %p",
148                     value, name, parent);
149         nvlist_add_string(parent, name, value);
150 }
151
152 void
153 set_config_value_node_if_unset(nvlist_t *const parent, const char *const name,
154     const char *const value)
155 {
156         if (get_config_value_node(parent, name) != NULL) {
157                 return;
158         }
159
160         set_config_value_node(parent, name, value);
161 }
162
163 void
164 set_config_value(const char *path, const char *value)
165 {
166         const char *name;
167         char *node_name;
168         nvlist_t *nvl;
169
170         /* Look for last separator. */
171         name = strrchr(path, '.');
172         if (name == NULL) {
173                 nvl = config_root;
174                 name = path;
175         } else {
176                 node_name = strndup(path, name - path);
177                 if (node_name == NULL)
178                         errx(4, "Failed to allocate memory");
179                 nvl = create_config_node(node_name);
180                 if (nvl == NULL)
181                         errx(4, "Failed to create configuration node %s",
182                             node_name);
183                 free(node_name);
184
185                 /* Skip over '.'. */
186                 name++;
187         }
188
189         if (nvlist_exists_nvlist(nvl, name))
190                 errx(4, "Attempting to add value %s to existing node %s",
191                     value, path);
192         set_config_value_node(nvl, name, value);
193 }
194
195 void
196 set_config_value_if_unset(const char *const path, const char *const value)
197 {
198         if (get_config_value(path) != NULL) {
199                 return;
200         }
201
202         set_config_value(path, value);
203 }
204
205 static const char *
206 get_raw_config_value(const char *path)
207 {
208         const char *name;
209         char *node_name;
210         nvlist_t *nvl;
211
212         /* Look for last separator. */
213         name = strrchr(path, '.');
214         if (name == NULL) {
215                 nvl = config_root;
216                 name = path;
217         } else {
218                 node_name = strndup(path, name - path);
219                 if (node_name == NULL)
220                         errx(4, "Failed to allocate memory");
221                 nvl = find_config_node(node_name);
222                 free(node_name);
223                 if (nvl == NULL)
224                         return (NULL);
225
226                 /* Skip over '.'. */
227                 name++;
228         }
229
230         if (nvlist_exists_string(nvl, name))
231                 return (nvlist_get_string(nvl, name));
232         if (nvlist_exists_nvlist(nvl, name))
233                 warnx("Attempting to fetch value of node %s", path);
234         return (NULL);
235 }
236
237 static char *
238 _expand_config_value(const char *value, int depth)
239 {
240         FILE *valfp;
241         const char *cp, *vp;
242         char *nestedval, *path, *valbuf;
243         size_t valsize;
244
245         valfp = open_memstream(&valbuf, &valsize);
246         if (valfp == NULL)
247                 errx(4, "Failed to allocate memory");
248
249         vp = value;
250         while (*vp != '\0') {
251                 switch (*vp) {
252                 case '%':
253                         if (depth > 15) {
254                                 warnx(
255                     "Too many recursive references in configuration value");
256                                 fputc('%', valfp);
257                                 vp++;
258                                 break;
259                         }
260                         if (vp[1] != '(' || vp[2] == '\0')
261                                 cp = NULL;
262                         else
263                                 cp = strchr(vp + 2, ')');
264                         if (cp == NULL) {
265                                 warnx(
266                             "Invalid reference in configuration value \"%s\"",
267                                     value);
268                                 fputc('%', valfp);
269                                 vp++;
270                                 break;
271                         }
272                         vp += 2;
273
274                         if (cp == vp) {
275                                 warnx(
276                             "Empty reference in configuration value \"%s\"",
277                                     value);
278                                 vp++;
279                                 break;
280                         }
281
282                         /* Allocate a C string holding the path. */
283                         path = strndup(vp, cp - vp);
284                         if (path == NULL)
285                                 errx(4, "Failed to allocate memory");
286
287                         /* Advance 'vp' past the reference. */
288                         vp = cp + 1;
289
290                         /* Fetch the referenced value. */
291                         cp = get_raw_config_value(path);
292                         if (cp == NULL)
293                                 warnx(
294                     "Failed to fetch referenced configuration variable %s",
295                                     path);
296                         else {
297                                 nestedval = _expand_config_value(cp, depth + 1);
298                                 fputs(nestedval, valfp);
299                                 free(nestedval);
300                         }
301                         free(path);
302                         break;
303                 case '\\':
304                         vp++;
305                         if (*vp == '\0') {
306                                 warnx(
307                             "Trailing \\ in configuration value \"%s\"",
308                                     value);
309                                 break;
310                         }
311                         /* FALLTHROUGH */
312                 default:
313                         fputc(*vp, valfp);
314                         vp++;
315                         break;
316                 }
317         }
318         fclose(valfp);
319         return (valbuf);
320 }
321
322 static const char *
323 expand_config_value(const char *value)
324 {
325         static char *valbuf;
326
327         if (strchr(value, '%') == NULL)
328                 return (value);
329
330         free(valbuf);
331         valbuf = _expand_config_value(value, 0);
332         return (valbuf);
333 }
334
335 const char *
336 get_config_value(const char *path)
337 {
338         const char *value;
339
340         value = get_raw_config_value(path);
341         if (value == NULL)
342                 return (NULL);
343         return (expand_config_value(value));
344 }
345
346 const char *
347 get_config_value_node(const nvlist_t *parent, const char *name)
348 {
349
350         if (strchr(name, '.') != NULL)
351                 errx(4, "Invalid config node name %s", name);
352         if (parent == NULL)
353                 parent = config_root;
354         if (nvlist_exists_nvlist(parent, name))
355                 warnx("Attempt to fetch value of node %s of list %p", name,
356                     parent);
357         if (!nvlist_exists_string(parent, name))
358                 return (NULL);
359
360         return (expand_config_value(nvlist_get_string(parent, name)));
361 }
362
363 static bool
364 _bool_value(const char *name, const char *value)
365 {
366
367         if (strcasecmp(value, "true") == 0 ||
368             strcasecmp(value, "on") == 0 ||
369             strcasecmp(value, "yes") == 0 ||
370             strcmp(value, "1") == 0)
371                 return (true);
372         if (strcasecmp(value, "false") == 0 ||
373             strcasecmp(value, "off") == 0 ||
374             strcasecmp(value, "no") == 0 ||
375             strcmp(value, "0") == 0)
376                 return (false);
377         err(4, "Invalid value %s for boolean variable %s", value, name);
378 }
379
380 bool
381 get_config_bool(const char *path)
382 {
383         const char *value;
384
385         value = get_config_value(path);
386         if (value == NULL)
387                 err(4, "Failed to fetch boolean variable %s", path);
388         return (_bool_value(path, value));
389 }
390
391 bool
392 get_config_bool_default(const char *path, bool def)
393 {
394         const char *value;
395
396         value = get_config_value(path);
397         if (value == NULL)
398                 return (def);
399         return (_bool_value(path, value));
400 }
401
402 bool
403 get_config_bool_node(const nvlist_t *parent, const char *name)
404 {
405         const char *value;
406
407         value = get_config_value_node(parent, name);
408         if (value == NULL)
409                 err(4, "Failed to fetch boolean variable %s", name);
410         return (_bool_value(name, value));
411 }
412
413 bool
414 get_config_bool_node_default(const nvlist_t *parent, const char *name,
415     bool def)
416 {
417         const char *value;
418
419         value = get_config_value_node(parent, name);
420         if (value == NULL)
421                 return (def);
422         return (_bool_value(name, value));
423 }
424
425 void
426 set_config_bool(const char *path, bool value)
427 {
428
429         set_config_value(path, value ? "true" : "false");
430 }
431
432 void
433 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
434 {
435
436         set_config_value_node(parent, name, value ? "true" : "false");
437 }
438
439 static void
440 dump_tree(const char *prefix, const nvlist_t *nvl)
441 {
442         const char *name;
443         void *cookie;
444         int type;
445
446         cookie = NULL;
447         while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
448                 if (type == NV_TYPE_NVLIST) {
449                         char *new_prefix;
450
451                         asprintf(&new_prefix, "%s%s.", prefix, name);
452                         dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
453                         free(new_prefix);
454                 } else {
455                         assert(type == NV_TYPE_STRING);
456                         printf("%s%s=%s\n", prefix, name,
457                             nvlist_get_string(nvl, name));
458                 }
459         }
460 }
461
462 void
463 dump_config(void)
464 {
465         dump_tree("", config_root);
466 }