]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/config.c
amd64: stop using top of the thread' kernel stack for FPU user save area
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / config.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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                         nvl = (nvlist_t *)nvlist_get_nvlist(nvl, name);
69                 else if (nvlist_exists(nvl, name)) {
70                         for (copy = tofree; copy < name; copy++)
71                                 if (*copy == '\0')
72                                         *copy = '.';
73                         warnx(
74                     "Configuration node %s is a child of existing variable %s",
75                             path, tofree);
76                         nvl = NULL;
77                         break;
78                 } else if (create) {
79                         new_nvl = nvlist_create(0);
80                         if (new_nvl == NULL)
81                                 errx(4, "Failed to allocate memory");
82                         nvlist_move_nvlist(nvl, name, new_nvl);
83                         nvl = new_nvl;
84                 } else {
85                         nvl = NULL;
86                         break;
87                 }
88         }
89         free(tofree);
90         return (nvl);
91 }
92
93 nvlist_t *
94 create_config_node(const char *path)
95 {
96
97         return (_lookup_config_node(config_root, path, true));
98 }
99
100 nvlist_t *
101 find_config_node(const char *path)
102 {
103
104         return (_lookup_config_node(config_root, path, false));
105 }
106
107 nvlist_t *
108 create_relative_config_node(nvlist_t *parent, const char *path)
109 {
110
111         return (_lookup_config_node(parent, path, true));
112 }
113
114 nvlist_t *
115 find_relative_config_node(nvlist_t *parent, const char *path)
116 {
117
118         return (_lookup_config_node(parent, path, false));
119 }
120
121 void
122 set_config_value_node(nvlist_t *parent, const char *name, const char *value)
123 {
124
125         if (strchr(name, '.') != NULL)
126                 errx(4, "Invalid config node name %s", name);
127         if (parent == NULL)
128                 parent = config_root;
129         if (nvlist_exists_string(parent, name))
130                 nvlist_free_string(parent, name);
131         else if (nvlist_exists(parent, name))
132                 errx(4,
133                     "Attemping to add value %s to existing node %s of list %p",
134                     value, name, parent);
135         nvlist_add_string(parent, name, value);
136 }
137
138 void
139 set_config_value(const char *path, const char *value)
140 {
141         const char *name;
142         char *node_name;
143         nvlist_t *nvl;
144
145         /* Look for last separator. */
146         name = strrchr(path, '.');
147         if (name == NULL) {
148                 nvl = config_root;
149                 name = path;
150         } else {
151                 node_name = strndup(path, name - path);
152                 if (node_name == NULL)
153                         errx(4, "Failed to allocate memory");
154                 nvl = create_config_node(node_name);
155                 if (nvl == NULL)
156                         errx(4, "Failed to create configuration node %s",
157                             node_name);
158                 free(node_name);
159
160                 /* Skip over '.'. */
161                 name++;
162         }
163
164         if (nvlist_exists_nvlist(nvl, name))
165                 errx(4, "Attempting to add value %s to existing node %s",
166                     value, path);
167         set_config_value_node(nvl, name, value);
168 }
169
170 static const char *
171 get_raw_config_value(const char *path)
172 {
173         const char *name;
174         char *node_name;
175         nvlist_t *nvl;
176
177         /* Look for last separator. */
178         name = strrchr(path, '.');
179         if (name == NULL) {
180                 nvl = config_root;
181                 name = path;
182         } else {
183                 node_name = strndup(path, name - path);
184                 if (node_name == NULL)
185                         errx(4, "Failed to allocate memory");
186                 nvl = find_config_node(node_name);
187                 free(node_name);
188                 if (nvl == NULL)
189                         return (NULL);
190
191                 /* Skip over '.'. */
192                 name++;
193         }
194
195         if (nvlist_exists_string(nvl, name))
196                 return (nvlist_get_string(nvl, name));
197         if (nvlist_exists_nvlist(nvl, name))
198                 warnx("Attempting to fetch value of node %s", path);
199         return (NULL);
200 }
201
202 static char *
203 _expand_config_value(const char *value, int depth)
204 {
205         FILE *valfp;
206         const char *cp, *vp;
207         char *nestedval, *path, *valbuf;
208         size_t valsize;
209
210         valfp = open_memstream(&valbuf, &valsize);
211         if (valfp == NULL)
212                 errx(4, "Failed to allocate memory");
213
214         vp = value;
215         while (*vp != '\0') {
216                 switch (*vp) {
217                 case '%':
218                         if (depth > 15) {
219                                 warnx(
220                     "Too many recursive references in configuration value");
221                                 fputc('%', valfp);
222                                 vp++;
223                                 break;
224                         }                               
225                         if (vp[1] != '(' || vp[2] == '\0')
226                                 cp = NULL;
227                         else
228                                 cp = strchr(vp + 2, ')');
229                         if (cp == NULL) {
230                                 warnx(
231                             "Invalid reference in configuration value \"%s\"",
232                                     value);
233                                 fputc('%', valfp);
234                                 vp++;
235                                 break;
236                         }
237                         vp += 2;
238
239                         if (cp == vp) {
240                                 warnx(
241                             "Empty reference in configuration value \"%s\"",
242                                     value);
243                                 vp++;
244                                 break;
245                         }
246
247                         /* Allocate a C string holding the path. */
248                         path = strndup(vp, cp - vp);
249                         if (path == NULL)
250                                 errx(4, "Failed to allocate memory");
251
252                         /* Advance 'vp' past the reference. */
253                         vp = cp + 1;
254
255                         /* Fetch the referenced value. */
256                         cp = get_raw_config_value(path);
257                         if (cp == NULL)
258                                 warnx(
259                     "Failed to fetch referenced configuration variable %s",
260                                     path);
261                         else {
262                                 nestedval = _expand_config_value(cp, depth + 1);
263                                 fputs(nestedval, valfp);
264                                 free(nestedval);
265                         }
266                         free(path);
267                         break;
268                 case '\\':
269                         vp++;
270                         if (*vp == '\0') {
271                                 warnx(
272                             "Trailing \\ in configuration value \"%s\"",
273                                     value);
274                                 break;
275                         }
276                         /* FALLTHROUGH */
277                 default:
278                         fputc(*vp, valfp);
279                         vp++;
280                         break;
281                 }
282         }
283         fclose(valfp);
284         return (valbuf);
285 }
286
287 const char *
288 expand_config_value(const char *value)
289 {
290         static char *valbuf;
291
292         if (strchr(value, '%') == NULL)
293                 return (value);
294
295         free(valbuf);
296         valbuf = _expand_config_value(value, 0);
297         return (valbuf);
298 }
299
300 const char *
301 get_config_value(const char *path)
302 {
303         const char *value;
304
305         value = get_raw_config_value(path);
306         if (value == NULL)
307                 return (NULL);
308         return (expand_config_value(value));
309 }
310
311 const char *
312 get_config_value_node(const nvlist_t *parent, const char *name)
313 {
314
315         if (strchr(name, '.') != NULL)
316                 errx(4, "Invalid config node name %s", name);
317         if (parent == NULL)
318                 parent = config_root;
319         if (nvlist_exists_nvlist(parent, name))
320                 warnx("Attempt to fetch value of node %s of list %p", name,
321                     parent);
322         if (!nvlist_exists_string(parent, name))
323                 return (NULL);
324
325         return (expand_config_value(nvlist_get_string(parent, name)));
326 }
327
328 bool
329 _bool_value(const char *name, const char *value)
330 {
331
332         if (strcasecmp(value, "true") == 0 ||
333             strcasecmp(value, "on") == 0 ||
334             strcasecmp(value, "yes") == 0 ||
335             strcmp(value, "1") == 0)
336                 return (true);
337         if (strcasecmp(value, "false") == 0 ||
338             strcasecmp(value, "off") == 0 ||
339             strcasecmp(value, "no") == 0 ||
340             strcmp(value, "0") == 0)
341                 return (false);
342         err(4, "Invalid value %s for boolean variable %s", value, name);
343 }
344
345 bool
346 get_config_bool(const char *path)
347 {
348         const char *value;
349
350         value = get_config_value(path);
351         if (value == NULL)
352                 err(4, "Failed to fetch boolean variable %s", path);
353         return (_bool_value(path, value));
354 }
355
356 bool
357 get_config_bool_default(const char *path, bool def)
358 {
359         const char *value;
360
361         value = get_config_value(path);
362         if (value == NULL)
363                 return (def);
364         return (_bool_value(path, value));
365 }
366
367 bool
368 get_config_bool_node(const nvlist_t *parent, const char *name)
369 {
370         const char *value;
371
372         value = get_config_value_node(parent, name);
373         if (value == NULL)
374                 err(4, "Failed to fetch boolean variable %s", name);
375         return (_bool_value(name, value));
376 }
377
378 bool
379 get_config_bool_node_default(const nvlist_t *parent, const char *name,
380     bool def)
381 {
382         const char *value;
383
384         value = get_config_value_node(parent, name);
385         if (value == NULL)
386                 return (def);
387         return (_bool_value(name, value));
388 }
389
390 void
391 set_config_bool(const char *path, bool value)
392 {
393
394         set_config_value(path, value ? "true" : "false");
395 }
396
397 void
398 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
399 {
400
401         set_config_value_node(parent, name, value ? "true" : "false");
402 }
403
404 static void
405 dump_tree(const char *prefix, const nvlist_t *nvl)
406 {
407         const char *name;
408         void *cookie;
409         int type;
410
411         cookie = NULL;
412         while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
413                 if (type == NV_TYPE_NVLIST) {
414                         char *new_prefix;
415
416                         asprintf(&new_prefix, "%s%s.", prefix, name);
417                         dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
418                         free(new_prefix);
419                 } else {
420                         assert(type == NV_TYPE_STRING);
421                         printf("%s%s=%s\n", prefix, name,
422                             nvlist_get_string(nvl, name));
423                 }
424         }
425 }
426
427 void
428 dump_config(void)
429 {
430         dump_tree("", config_root);
431 }