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