1 /*
2 * Copyright (c) 2013-2026 Devin Teske <dteske@FreeBSD.org>
3 * Copyright (c) 2021-2026 Faraz Vahedi <kfv@FreeBSD.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8 #include <sys/stat.h>
9
10 #include <dirent.h>
11 #include <errno.h>
12 #include <libgen.h>
13 #include <limits.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "bsdconf.h"
19 #include "bsdconf_formats.h"
20
21 /*
22 * The format abstraction layer registry.
23 *
24 * Every configuration file format -- built-in or bolted on at run-time -- is
25 * described by the same small descriptor (struct bsdconf_format_def in
26 * bsdconf.h): a target keyword, a default write path, an ordered list of
27 * configuration sources, and the processing/put bitmasks that drive the
28 * shared parsing and writing cores. There is exactly one engine; a format
29 * merely parameterizes it.
30 *
31 * Each built-in format is a self-contained translation unit
32 * (bsdconf_format_<keyword>.c) documenting and defining its own descriptor;
33 * this file only collects the descriptors into the table indexed by enum
34 * bsdconf_format and resolves keywords, basenames, and source lists against
35 * it (see bsdconf_formats.h for the recipe to add a format).
36 *
37 * A format that is "mostly like" an existing one need not be added at all:
38 * an application calls bsdconf_format_derive() to inherit the nearest
39 * descriptor, adjusts only the members that differ, and registers the
40 * result with bsdconf_format_register(). The handle it gets back taps into
41 * the same core engine everywhere a built-in format works, including
42 * keyword, path, and source-list resolution in sysconf(8).
43 */
44
45 static const struct bsdconf_format_def *bsdconf_formats[] = {
46 [BSDCONF_FORMAT_GENERIC] = &bsdconf_format_generic_def,
47 [BSDCONF_FORMAT_LOADER] = &bsdconf_format_loader_def,
48 [BSDCONF_FORMAT_SYSCTL] = &bsdconf_format_sysctl_def,
49 [BSDCONF_FORMAT_MAKE] = &bsdconf_format_make_def,
50 [BSDCONF_FORMAT_SRC] = &bsdconf_format_src_def,
51 };
52 #define BSDCONF_NFORMATS \
53 (sizeof(bsdconf_formats) / sizeof(*bsdconf_formats))
54
55 #define BSDCONF_CONF_SUFFIX ".conf"
56 #define BSDCONF_CONF_SUFFIX_LEN (sizeof(BSDCONF_CONF_SUFFIX) - 1)
57
58 /*
59 * Registered (bolt-on) formats. Descriptors are copied by value; the
60 * keyword and path strings they reference remain owned by the caller and
61 * must stay valid for the life of the registration.
62 */
63 #define BSDCONF_MAXUSERFORMATS 32
64 static struct bsdconf_format_def bsdconf_user_formats[BSDCONF_MAXUSERFORMATS];
65 static unsigned int bsdconf_nuser_formats = 0;
66
67 /*
68 * Return the descriptor for `format' or NULL if the format is neither
69 * built-in nor registered.
70 */
71 const struct bsdconf_format_def *
bsdconf_format_lookup(enum bsdconf_format format)72 bsdconf_format_lookup(enum bsdconf_format format)
73 {
74 unsigned int n;
75
76 if ((unsigned int)format < BSDCONF_NFORMATS)
77 return (bsdconf_formats[format]);
78
79 n = (unsigned int)format - BSDCONF_FORMAT_USER;
80 if (format >= BSDCONF_FORMAT_USER && n < bsdconf_nuser_formats)
81 return (&bsdconf_user_formats[n]);
82
83 return (NULL);
84 }
85
86 /*
87 * Copy the descriptor of `base' into `def' so that a caller can adjust only
88 * the members that differ before registering the result as a new format.
89 * On success, returns zero; otherwise returns -1 (unknown base format) and
90 * errno is set to EINVAL.
91 */
92 int
bsdconf_format_derive(enum bsdconf_format base,struct bsdconf_format_def * def)93 bsdconf_format_derive(enum bsdconf_format base,
94 struct bsdconf_format_def *def)
95 {
96 const struct bsdconf_format_def *found;
97
98 /* Check arguments */
99 if (def == NULL || (found = bsdconf_format_lookup(base)) == NULL) {
100 errno = EINVAL;
101 return (-1);
102 }
103
104 *def = *found;
105 return (0);
106 }
107
108 /*
109 * Register a new configuration file format described by `def', storing its
110 * newly assigned handle through `format'. The descriptor is copied; the
111 * keyword and path strings it references are not (see above). On success,
112 * returns zero; otherwise returns -1 and errno is set (EINVAL for a bad
113 * argument, ENOSPC when the registration table is full).
114 */
115 int
bsdconf_format_register(const struct bsdconf_format_def * def,enum bsdconf_format * format)116 bsdconf_format_register(const struct bsdconf_format_def *def,
117 enum bsdconf_format *format)
118 {
119
120 /* Check arguments */
121 if (def == NULL || format == NULL) {
122 errno = EINVAL;
123 return (-1);
124 }
125 if (bsdconf_nuser_formats >= BSDCONF_MAXUSERFORMATS) {
126 errno = ENOSPC;
127 return (-1);
128 }
129
130 bsdconf_user_formats[bsdconf_nuser_formats] = *def;
131 *format = (enum bsdconf_format)
132 (BSDCONF_FORMAT_USER + bsdconf_nuser_formats);
133 bsdconf_nuser_formats++;
134
135 return (0);
136 }
137
138 /*
139 * Map a target keyword (e.g., "loader") to its format, storing the result
140 * through `format'. Registered formats are searched after the built-ins.
141 * On success, returns zero; otherwise returns -1 (unknown keyword; `format'
142 * is left untouched).
143 */
144 int
bsdconf_format_find(const char * keyword,enum bsdconf_format * format)145 bsdconf_format_find(const char *keyword, enum bsdconf_format *format)
146 {
147 unsigned int n;
148
149 /* Check arguments */
150 if (keyword == NULL || format == NULL)
151 return (-1);
152
153 for (n = 0; n < BSDCONF_NFORMATS; n++) {
154 if (bsdconf_formats[n]->keyword != NULL &&
155 strcmp(bsdconf_formats[n]->keyword, keyword) == 0) {
156 *format = (enum bsdconf_format)n;
157 return (0);
158 }
159 }
160 for (n = 0; n < bsdconf_nuser_formats; n++) {
161 if (bsdconf_user_formats[n].keyword != NULL &&
162 strcmp(bsdconf_user_formats[n].keyword, keyword) == 0) {
163 *format = (enum bsdconf_format)
164 (BSDCONF_FORMAT_USER + n);
165 return (0);
166 }
167 }
168
169 return (-1);
170 }
171
172 /*
173 * Test whether the basename of `path' matches `<keyword>.conf' with an
174 * optional trailing suffix (e.g., "sysctl.conf.local" matches "sysctl").
175 */
176 static int
bsdconf_basename_matches(const char * base,const char * keyword)177 bsdconf_basename_matches(const char *base, const char *keyword)
178 {
179 size_t klen;
180
181 if (keyword == NULL)
182 return (0);
183 klen = strlen(keyword);
184 if (strncmp(base, keyword, klen) != 0)
185 return (0);
186 return (strncmp(base + klen, BSDCONF_CONF_SUFFIX,
187 BSDCONF_CONF_SUFFIX_LEN) == 0);
188 }
189
190 /*
191 * Guess the format of an arbitrary configuration file from the basename of
192 * its `path' (e.g., "/etc/sysctl.conf.local" is BSDCONF_FORMAT_SYSCTL).
193 * Registered formats are searched after the built-ins. Returns
194 * BSDCONF_FORMAT_GENERIC when nothing matches.
195 */
196 enum bsdconf_format
bsdconf_format_guess(const char * path)197 bsdconf_format_guess(const char *path)
198 {
199 unsigned int n;
200 char tmp[PATH_MAX];
201 char *base;
202
203 if (path == NULL)
204 return (BSDCONF_FORMAT_GENERIC);
205
206 if (strlen(path) >= sizeof(tmp))
207 return (BSDCONF_FORMAT_GENERIC);
208 memcpy(tmp, path, strlen(path) + 1);
209 base = basename(tmp);
210
211 for (n = 0; n < BSDCONF_NFORMATS; n++)
212 if (bsdconf_basename_matches(base,
213 bsdconf_formats[n]->keyword))
214 return ((enum bsdconf_format)n);
215 for (n = 0; n < bsdconf_nuser_formats; n++)
216 if (bsdconf_basename_matches(base,
217 bsdconf_user_formats[n].keyword))
218 return ((enum bsdconf_format)
219 (BSDCONF_FORMAT_USER + n));
220
221 return (BSDCONF_FORMAT_GENERIC);
222 }
223
224 /*
225 * Return the default file path for `format' (e.g., "/boot/loader.conf") or
226 * NULL if the format has no fixed path (e.g., BSDCONF_FORMAT_GENERIC).
227 */
228 const char *
bsdconf_format_path(enum bsdconf_format format)229 bsdconf_format_path(enum bsdconf_format format)
230 {
231 const struct bsdconf_format_def *def;
232
233 if ((def = bsdconf_format_lookup(format)) == NULL)
234 return (NULL);
235 return (def->path);
236 }
237
238 /*
239 * Return the processing_options bitmask suited to `format', for passing to
240 * bsdconf_parse(), bsdconf_fparse(), and bsdconf_put(). Unknown formats
241 * fall back to the generic descriptor.
242 */
243 uint16_t
bsdconf_format_processing(enum bsdconf_format format)244 bsdconf_format_processing(enum bsdconf_format format)
245 {
246 const struct bsdconf_format_def *def;
247
248 if ((def = bsdconf_format_lookup(format)) == NULL)
249 def = bsdconf_formats[BSDCONF_FORMAT_GENERIC];
250 return (def->processing);
251 }
252
253 /*
254 * Return the put_options bitmask suited to `format', for passing to
255 * bsdconf_put(). Unknown formats fall back to the generic descriptor.
256 */
257 uint16_t
bsdconf_format_put(enum bsdconf_format format)258 bsdconf_format_put(enum bsdconf_format format)
259 {
260 const struct bsdconf_format_def *def;
261
262 if ((def = bsdconf_format_lookup(format)) == NULL)
263 def = bsdconf_formats[BSDCONF_FORMAT_GENERIC];
264 return (def->put);
265 }
266
267 /*
268 * Append `path' (already allocated; ownership is taken) to the growing
269 * `files' array. On success, returns zero; otherwise returns -1 (errno set
270 * by realloc(3)) and `path' is freed.
271 */
272 static int
bsdconf_files_add(char *** files,size_t * nfiles,size_t * size,char * path)273 bsdconf_files_add(char ***files, size_t *nfiles, size_t *size, char *path)
274 {
275 char **tmp;
276
277 if (path == NULL)
278 return (-1);
279 if (*nfiles >= *size) {
280 *size = (*size == 0) ? 8 : *size << 1;
281 tmp = realloc(*files, *size * sizeof(**files));
282 if (tmp == NULL) {
283 free(path);
284 return (-1);
285 }
286 *files = tmp;
287 }
288 (*files)[(*nfiles)++] = path;
289 return (0);
290 }
291
292 /*
293 * scandir(3) filter accepting non-hidden `*.conf' entries.
294 */
295 static int
bsdconf_files_filter(const struct dirent * entry)296 bsdconf_files_filter(const struct dirent *entry)
297 {
298 size_t len;
299
300 if (entry->d_name[0] == '.')
301 return (0);
302 len = strlen(entry->d_name);
303 return (len > BSDCONF_CONF_SUFFIX_LEN &&
304 strcmp(entry->d_name + len - BSDCONF_CONF_SUFFIX_LEN,
305 BSDCONF_CONF_SUFFIX) == 0);
306 }
307
308 /*
309 * Compose the full path `prefix' + `path' [+ `/' + `name' [+ `.conf']] into
310 * newly allocated storage. Returns NULL on allocation failure (errno set).
311 *
312 * asprintf(3) is not in POSIX.1-2008; size with snprintf(NULL, 0) then fill
313 * so the library still compiles under _POSIX_C_SOURCE=200809L.
314 */
315 static char *
bsdconf_files_path(const char * prefix,const char * path,const char * name,const char * suffix)316 bsdconf_files_path(const char *prefix, const char *path, const char *name,
317 const char *suffix)
318 {
319 char *full;
320 int len;
321
322 len = snprintf(NULL, 0, "%s%s%s%s%s", prefix, path,
323 name != NULL ? "/" : "", name != NULL ? name : "",
324 suffix != NULL ? suffix : "");
325 if (len < 0 || (full = malloc((size_t)len + 1)) == NULL)
326 return (NULL);
327 snprintf(full, (size_t)len + 1, "%s%s%s%s%s", prefix, path,
328 name != NULL ? "/" : "", name != NULL ? name : "",
329 suffix != NULL ? suffix : "");
330 return (full);
331 }
332
333 /*
334 * Append each existing `*.conf' entry of directory `path' (prefixed with
335 * `prefix'), sorted, to the growing `files' array. A missing or unreadable
336 * directory is not an error (a drop-in directory is optional by nature).
337 * On success, returns zero; otherwise returns -1 (errno set).
338 */
339 static int
bsdconf_files_add_dir(char *** files,size_t * nfiles,size_t * size,const char * prefix,const char * path)340 bsdconf_files_add_dir(char ***files, size_t *nfiles, size_t *size,
341 const char *prefix, const char *path)
342 {
343 int n, nentries;
344 char *dir;
345 char *full;
346 struct dirent **entries;
347
348 if ((dir = bsdconf_files_path(prefix, path, NULL, NULL)) == NULL)
349 return (-1);
350 nentries = scandir(dir, &entries, bsdconf_files_filter, alphasort);
351 free(dir);
352 if (nentries < 0)
353 return (0);
354 for (n = 0; n < nentries; n++) {
355 full = bsdconf_files_path(prefix, path, entries[n]->d_name,
356 NULL);
357 if (bsdconf_files_add(files, nfiles, size, full) != 0) {
358 while (n < nentries)
359 free(entries[n++]);
360 free(entries);
361 return (-1);
362 }
363 free(entries[n]);
364 }
365 free(entries);
366 return (0);
367 }
368
369 /*
370 * Parse call-back for the list directives chased by
371 * bsdconf_files_discover() below. The option's `value.data' member points
372 * at the (char *) slot holding the current value of the directive; each
373 * assignment encountered replaces the slot (last assignment wins, exactly
374 * as the consumer of the file behaves).
375 */
376 static int
bsdconf_files_list_cb(struct bsdconf_option * option,uint32_t line,char * directive,char * value)377 bsdconf_files_list_cb(struct bsdconf_option *option, uint32_t line,
378 char *directive, char *value)
379 {
380 char *copy;
381 char **slot = (char **)option->value.data;
382
383 (void)line;
384 (void)directive;
385
386 if ((copy = strdup(bsdconf_unquote(value))) == NULL)
387 return (-1);
388 free(*slot);
389 *slot = copy;
390 return (0);
391 }
392
393 /*
394 * Advance to the next whitespace-separated token of `list' at or beyond
395 * `cursor', storing its length through `lenp'. Returns the token or NULL
396 * when the list is exhausted.
397 */
398 static const char *
bsdconf_files_token(const char * cursor,size_t * lenp)399 bsdconf_files_token(const char *cursor, size_t *lenp)
400 {
401
402 if (cursor == NULL)
403 return (NULL);
404 cursor += strspn(cursor, " \t");
405 if ((*lenp = strcspn(cursor, " \t")) == 0)
406 return (NULL);
407 return (cursor);
408 }
409
410 /*
411 * Ceiling on the number of configuration files discovery will chase,
412 * bounding run-away (or maliciously self-referential) file lists.
413 */
414 #define BSDCONF_DISCOVER_MAX 64
415
416 /*
417 * Resolve the backing files of a format whose descriptor carries a
418 * `defaults' file by performing the same discovery its consumer does: read
419 * the defaults file, then chase the file-list directive (e.g.,
420 * loader_conf_files) -- re-reading it after every file, as the loader does
421 * when a file queues additional names -- and finally append the drop-in
422 * directory entries and local files named by the final values of the other
423 * two list directives (likewise applied only after the file-list walk).
424 * Every file named by the file-list directive is included in
425 * the result whether or not it exists (a listed file is a legitimate
426 * write target before its first directive lands); the defaults file
427 * itself is deliberately excluded. The write candidate stored through
428 * `write_idx' is the last file-list entry, the final word among the
429 * regular configuration files.
430 *
431 * The defaults file is `defaults' verbatim when non-NULL (a caller-level
432 * override; see sysconf(8)'s LOADER_DEFAULTS); otherwise the descriptor
433 * default prefixed with `rootdir'.
434 *
435 * On success, returns zero. Returns 1 when the defaults file is missing
436 * or unreadable, directing the caller to fall back to the static source
437 * list. Otherwise returns -1 (errno set).
438 */
439 static int
bsdconf_files_discover(const struct bsdconf_format_def * def,const char * rootdir,const char * defaults,char *** files,size_t * nfiles,size_t * size,size_t * write_idx)440 bsdconf_files_discover(const struct bsdconf_format_def *def,
441 const char *rootdir, const char *defaults, char ***files,
442 size_t *nfiles, size_t *size, size_t *write_idx)
443 {
444 int error, rv = -1;
445 uint16_t processing;
446 size_t len, n, nvisited = 0;
447 char *dpath = NULL;
448 char *full;
449 char *name;
450 char *conf_dirs = NULL;
451 char *conf_files = NULL;
452 char *local_files = NULL;
453 const char *token;
454 char *visited[BSDCONF_DISCOVER_MAX];
455 struct bsdconf_option options[4];
456
457 /* Wire each list directive to the slot holding its value */
458 memset(options, 0, sizeof(options));
459 n = 0;
460 if (def->files_directive != NULL) {
461 options[n].directive = def->files_directive;
462 options[n].value.data = &conf_files;
463 options[n].parse = bsdconf_files_list_cb;
464 n++;
465 }
466 if (def->dirs_directive != NULL) {
467 options[n].directive = def->dirs_directive;
468 options[n].value.data = &conf_dirs;
469 options[n].parse = bsdconf_files_list_cb;
470 n++;
471 }
472 if (def->local_directive != NULL) {
473 options[n].directive = def->local_directive;
474 options[n].value.data = &local_files;
475 options[n].parse = bsdconf_files_list_cb;
476 n++;
477 }
478
479 /* Resolve and read the defaults file (missing means fall back) */
480 if (defaults != NULL)
481 dpath = strdup(defaults);
482 else
483 dpath = bsdconf_files_path(rootdir, def->defaults, NULL,
484 NULL);
485 if (dpath == NULL)
486 goto cleanup;
487 processing = def->processing & ~BSDCONF_REQUIRE_EQUALS;
488 if (bsdconf_parse(options, dpath, NULL, processing) != 0) {
489 rv = 1;
490 goto cleanup;
491 }
492
493 /*
494 * Chase the file-list directive. Each pass rescans the current
495 * value of the list from the start for the first file not yet
496 * visited (any file read may have revised the list), reads it,
497 * and repeats until every listed file has been visited.
498 */
499 for (;;) {
500 token = conf_files;
501 while ((token = bsdconf_files_token(token, &len)) != NULL) {
502 for (n = 0; n < nvisited; n++)
503 if (strncmp(visited[n], token, len) == 0 &&
504 visited[n][len] == '\0')
505 break;
506 if (n == nvisited)
507 break;
508 token += len;
509 }
510 if (token == NULL || nvisited >= BSDCONF_DISCOVER_MAX)
511 break;
512 if ((visited[nvisited] = strndup(token, len)) == NULL)
513 goto cleanup;
514 full = bsdconf_files_path(rootdir, visited[nvisited], NULL,
515 NULL);
516 nvisited++;
517 if (bsdconf_files_add(files, nfiles, size, full) != 0)
518 goto cleanup;
519 /* A listed file that does not (yet) exist is normal */
520 (void)bsdconf_parse(options, full, NULL, processing);
521 }
522 *write_idx = (*nfiles > 0) ? *nfiles - 1 : 0;
523
524 /* Drop-in directories, then local files, from the final lists */
525 token = conf_dirs;
526 while ((token = bsdconf_files_token(token, &len)) != NULL) {
527 if ((name = strndup(token, len)) == NULL)
528 goto cleanup;
529 token += len;
530 error = bsdconf_files_add_dir(files, nfiles, size, rootdir,
531 name);
532 free(name);
533 if (error != 0)
534 goto cleanup;
535 }
536 token = local_files;
537 while ((token = bsdconf_files_token(token, &len)) != NULL) {
538 if ((name = strndup(token, len)) == NULL)
539 goto cleanup;
540 token += len;
541 full = bsdconf_files_path(rootdir, name, NULL, NULL);
542 free(name);
543 if (bsdconf_files_add(files, nfiles, size, full) != 0)
544 goto cleanup;
545 }
546
547 rv = 0;
548
549 cleanup:
550 n = errno; /* preserve errno across free(3) */
551 while (nvisited > 0)
552 free(visited[--nvisited]);
553 free(conf_files);
554 free(conf_dirs);
555 free(local_files);
556 free(dpath);
557 errno = n;
558 return (rv);
559 }
560
561 /*
562 * Resolve the ordered list of configuration files backing `format' into a
563 * newly allocated array of newly allocated paths, stored through `filesp'
564 * with the count stored through `nfilesp'. Files appear in the order the
565 * system sources them at boot; a directive in a later file overrides the
566 * same directive in an earlier one, making the last file listing a
567 * directive the authoritative source of its value. When `write_idxp' is
568 * non-NULL, the index of the file recommended for directives found in no
569 * file at all is stored through it (the format's default write path, or
570 * the last regular configuration file when discovery is in play).
571 *
572 * A format whose descriptor names a defaults file resolves its list by
573 * discovery (see bsdconf_files_discover() above); `defaults' overrides
574 * the descriptor's defaults file when non-NULL and is used verbatim (it
575 * is not prefixed with `rootdir'). For all other formats `defaults' is
576 * ignored and the static source list governs, as it also does when the
577 * defaults file is missing.
578 *
579 * Each static path is prefixed with `rootdir' unless NULL or empty.
580 * Sources of type BSDCONF_SOURCE_FILE are always listed, whether or not
581 * the file exists; sources of type BSDCONF_SOURCE_DIR contribute each
582 * existing `*.conf' entry (sorted); sources of type BSDCONF_SOURCE_MODDIR
583 * contribute `<module>.conf' only when `module' is non-NULL. A format
584 * with no source list resolves to its default path alone.
585 *
586 * On success, returns zero and the caller frees the result with
587 * bsdconf_format_files_free(). Otherwise returns -1 and errno is set
588 * (EINVAL for an unknown format or a format with no backing files).
589 */
590 int
bsdconf_format_files(enum bsdconf_format format,const char * rootdir,const char * module,const char * defaults,char *** filesp,size_t * nfilesp,size_t * write_idxp)591 bsdconf_format_files(enum bsdconf_format format, const char *rootdir,
592 const char *module, const char *defaults, char ***filesp,
593 size_t *nfilesp, size_t *write_idxp)
594 {
595 int error;
596 size_t nfiles = 0;
597 size_t size = 0;
598 size_t write_idx = 0;
599 char **files = NULL;
600 char *path;
601 const struct bsdconf_format_def *def;
602 const struct bsdconf_source *source;
603
604 /* Check arguments */
605 if (filesp == NULL || nfilesp == NULL ||
606 (def = bsdconf_format_lookup(format)) == NULL) {
607 errno = EINVAL;
608 return (-1);
609 }
610 if (rootdir == NULL)
611 rootdir = "";
612
613 /* Discover the list when the descriptor names a defaults file */
614 if (def->defaults != NULL) {
615 error = bsdconf_files_discover(def, rootdir, defaults,
616 &files, &nfiles, &size, &write_idx);
617 if (error < 0)
618 goto cleanup;
619 if (error == 0 && nfiles > 0)
620 goto done;
621 /* Missing defaults file; fall back to static sources */
622 bsdconf_format_files_free(files, nfiles);
623 files = NULL;
624 nfiles = size = write_idx = 0;
625 }
626
627 /* A format with no source list is backed by its path alone */
628 if (def->sources == NULL) {
629 if (def->path == NULL) {
630 errno = EINVAL;
631 return (-1);
632 }
633 path = bsdconf_files_path(rootdir, def->path, NULL, NULL);
634 if (bsdconf_files_add(&files, &nfiles, &size, path) != 0)
635 goto cleanup;
636 goto done;
637 }
638
639 for (source = def->sources; source->path != NULL; source++) {
640 switch (source->type) {
641 case BSDCONF_SOURCE_FILE:
642 path = bsdconf_files_path(rootdir, source->path,
643 NULL, NULL);
644 if (def->path != NULL &&
645 strcmp(source->path, def->path) == 0)
646 write_idx = nfiles;
647 if (bsdconf_files_add(&files, &nfiles, &size,
648 path) != 0)
649 goto cleanup;
650 break;
651 case BSDCONF_SOURCE_DIR:
652 if (bsdconf_files_add_dir(&files, &nfiles, &size,
653 rootdir, source->path) != 0)
654 goto cleanup;
655 break;
656 case BSDCONF_SOURCE_MODDIR:
657 if (module == NULL)
658 break;
659 path = bsdconf_files_path(rootdir, source->path,
660 module, BSDCONF_CONF_SUFFIX);
661 if (bsdconf_files_add(&files, &nfiles, &size,
662 path) != 0)
663 goto cleanup;
664 break;
665 }
666 }
667
668 done:
669 *filesp = files;
670 *nfilesp = nfiles;
671 if (write_idxp != NULL)
672 *write_idxp = write_idx;
673 return (0);
674
675 cleanup:
676 bsdconf_format_files_free(files, nfiles);
677 return (-1);
678 }
679
680 /*
681 * Release an array of paths obtained from bsdconf_format_files().
682 */
683 void
bsdconf_format_files_free(char ** files,size_t nfiles)684 bsdconf_format_files_free(char **files, size_t nfiles)
685 {
686 size_t n;
687
688 if (files == NULL)
689 return;
690 for (n = 0; n < nfiles; n++)
691 free(files[n]);
692 free(files);
693 }
694