]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/config.c
ssh: update to OpenSSH v9.0p1
[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_node_if_unset(nvlist_t *const parent, const char *const name,
140     const char *const value)
141 {
142         if (get_config_value_node(parent, name) != NULL) {
143                 return;
144         }
145
146         set_config_value_node(parent, name, value);
147 }
148
149 void
150 set_config_value(const char *path, const char *value)
151 {
152         const char *name;
153         char *node_name;
154         nvlist_t *nvl;
155
156         /* Look for last separator. */
157         name = strrchr(path, '.');
158         if (name == NULL) {
159                 nvl = config_root;
160                 name = path;
161         } else {
162                 node_name = strndup(path, name - path);
163                 if (node_name == NULL)
164                         errx(4, "Failed to allocate memory");
165                 nvl = create_config_node(node_name);
166                 if (nvl == NULL)
167                         errx(4, "Failed to create configuration node %s",
168                             node_name);
169                 free(node_name);
170
171                 /* Skip over '.'. */
172                 name++;
173         }
174
175         if (nvlist_exists_nvlist(nvl, name))
176                 errx(4, "Attempting to add value %s to existing node %s",
177                     value, path);
178         set_config_value_node(nvl, name, value);
179 }
180
181 void
182 set_config_value_if_unset(const char *const path, const char *const value)
183 {
184         if (get_config_value(path) != NULL) {
185                 return;
186         }
187
188         set_config_value(path, value);
189 }
190
191 static const char *
192 get_raw_config_value(const char *path)
193 {
194         const char *name;
195         char *node_name;
196         nvlist_t *nvl;
197
198         /* Look for last separator. */
199         name = strrchr(path, '.');
200         if (name == NULL) {
201                 nvl = config_root;
202                 name = path;
203         } else {
204                 node_name = strndup(path, name - path);
205                 if (node_name == NULL)
206                         errx(4, "Failed to allocate memory");
207                 nvl = find_config_node(node_name);
208                 free(node_name);
209                 if (nvl == NULL)
210                         return (NULL);
211
212                 /* Skip over '.'. */
213                 name++;
214         }
215
216         if (nvlist_exists_string(nvl, name))
217                 return (nvlist_get_string(nvl, name));
218         if (nvlist_exists_nvlist(nvl, name))
219                 warnx("Attempting to fetch value of node %s", path);
220         return (NULL);
221 }
222
223 static char *
224 _expand_config_value(const char *value, int depth)
225 {
226         FILE *valfp;
227         const char *cp, *vp;
228         char *nestedval, *path, *valbuf;
229         size_t valsize;
230
231         valfp = open_memstream(&valbuf, &valsize);
232         if (valfp == NULL)
233                 errx(4, "Failed to allocate memory");
234
235         vp = value;
236         while (*vp != '\0') {
237                 switch (*vp) {
238                 case '%':
239                         if (depth > 15) {
240                                 warnx(
241                     "Too many recursive references in configuration value");
242                                 fputc('%', valfp);
243                                 vp++;
244                                 break;
245                         }
246                         if (vp[1] != '(' || vp[2] == '\0')
247                                 cp = NULL;
248                         else
249                                 cp = strchr(vp + 2, ')');
250                         if (cp == NULL) {
251                                 warnx(
252                             "Invalid reference in configuration value \"%s\"",
253                                     value);
254                                 fputc('%', valfp);
255                                 vp++;
256                                 break;
257                         }
258                         vp += 2;
259
260                         if (cp == vp) {
261                                 warnx(
262                             "Empty reference in configuration value \"%s\"",
263                                     value);
264                                 vp++;
265                                 break;
266                         }
267
268                         /* Allocate a C string holding the path. */
269                         path = strndup(vp, cp - vp);
270                         if (path == NULL)
271                                 errx(4, "Failed to allocate memory");
272
273                         /* Advance 'vp' past the reference. */
274                         vp = cp + 1;
275
276                         /* Fetch the referenced value. */
277                         cp = get_raw_config_value(path);
278                         if (cp == NULL)
279                                 warnx(
280                     "Failed to fetch referenced configuration variable %s",
281                                     path);
282                         else {
283                                 nestedval = _expand_config_value(cp, depth + 1);
284                                 fputs(nestedval, valfp);
285                                 free(nestedval);
286                         }
287                         free(path);
288                         break;
289                 case '\\':
290                         vp++;
291                         if (*vp == '\0') {
292                                 warnx(
293                             "Trailing \\ in configuration value \"%s\"",
294                                     value);
295                                 break;
296                         }
297                         /* FALLTHROUGH */
298                 default:
299                         fputc(*vp, valfp);
300                         vp++;
301                         break;
302                 }
303         }
304         fclose(valfp);
305         return (valbuf);
306 }
307
308 const char *
309 expand_config_value(const char *value)
310 {
311         static char *valbuf;
312
313         if (strchr(value, '%') == NULL)
314                 return (value);
315
316         free(valbuf);
317         valbuf = _expand_config_value(value, 0);
318         return (valbuf);
319 }
320
321 const char *
322 get_config_value(const char *path)
323 {
324         const char *value;
325
326         value = get_raw_config_value(path);
327         if (value == NULL)
328                 return (NULL);
329         return (expand_config_value(value));
330 }
331
332 const char *
333 get_config_value_node(const nvlist_t *parent, const char *name)
334 {
335
336         if (strchr(name, '.') != NULL)
337                 errx(4, "Invalid config node name %s", name);
338         if (parent == NULL)
339                 parent = config_root;
340         if (nvlist_exists_nvlist(parent, name))
341                 warnx("Attempt to fetch value of node %s of list %p", name,
342                     parent);
343         if (!nvlist_exists_string(parent, name))
344                 return (NULL);
345
346         return (expand_config_value(nvlist_get_string(parent, name)));
347 }
348
349 bool
350 _bool_value(const char *name, const char *value)
351 {
352
353         if (strcasecmp(value, "true") == 0 ||
354             strcasecmp(value, "on") == 0 ||
355             strcasecmp(value, "yes") == 0 ||
356             strcmp(value, "1") == 0)
357                 return (true);
358         if (strcasecmp(value, "false") == 0 ||
359             strcasecmp(value, "off") == 0 ||
360             strcasecmp(value, "no") == 0 ||
361             strcmp(value, "0") == 0)
362                 return (false);
363         err(4, "Invalid value %s for boolean variable %s", value, name);
364 }
365
366 bool
367 get_config_bool(const char *path)
368 {
369         const char *value;
370
371         value = get_config_value(path);
372         if (value == NULL)
373                 err(4, "Failed to fetch boolean variable %s", path);
374         return (_bool_value(path, value));
375 }
376
377 bool
378 get_config_bool_default(const char *path, bool def)
379 {
380         const char *value;
381
382         value = get_config_value(path);
383         if (value == NULL)
384                 return (def);
385         return (_bool_value(path, value));
386 }
387
388 bool
389 get_config_bool_node(const nvlist_t *parent, const char *name)
390 {
391         const char *value;
392
393         value = get_config_value_node(parent, name);
394         if (value == NULL)
395                 err(4, "Failed to fetch boolean variable %s", name);
396         return (_bool_value(name, value));
397 }
398
399 bool
400 get_config_bool_node_default(const nvlist_t *parent, const char *name,
401     bool def)
402 {
403         const char *value;
404
405         value = get_config_value_node(parent, name);
406         if (value == NULL)
407                 return (def);
408         return (_bool_value(name, value));
409 }
410
411 void
412 set_config_bool(const char *path, bool value)
413 {
414
415         set_config_value(path, value ? "true" : "false");
416 }
417
418 void
419 set_config_bool_node(nvlist_t *parent, const char *name, bool value)
420 {
421
422         set_config_value_node(parent, name, value ? "true" : "false");
423 }
424
425 static void
426 dump_tree(const char *prefix, const nvlist_t *nvl)
427 {
428         const char *name;
429         void *cookie;
430         int type;
431
432         cookie = NULL;
433         while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
434                 if (type == NV_TYPE_NVLIST) {
435                         char *new_prefix;
436
437                         asprintf(&new_prefix, "%s%s.", prefix, name);
438                         dump_tree(new_prefix, nvlist_get_nvlist(nvl, name));
439                         free(new_prefix);
440                 } else {
441                         assert(type == NV_TYPE_STRING);
442                         printf("%s%s=%s\n", prefix, name,
443                             nvlist_get_string(nvl, name));
444                 }
445         }
446 }
447
448 void
449 dump_config(void)
450 {
451         dump_tree("", config_root);
452 }