13fe5961aSDevin Teske /* 23fe5961aSDevin Teske * Copyright (c) 2002-2026 Devin Teske <dteske@FreeBSD.org> 33fe5961aSDevin Teske * Copyright (c) 2021-2026 Faraz Vahedi <kfv@FreeBSD.org> 43fe5961aSDevin Teske * 53fe5961aSDevin Teske * SPDX-License-Identifier: BSD-2-Clause 63fe5961aSDevin Teske */ 73fe5961aSDevin Teske 83fe5961aSDevin Teske #ifndef _BSDCONF_H_ 93fe5961aSDevin Teske #define _BSDCONF_H_ 103fe5961aSDevin Teske 113fe5961aSDevin Teske #ifdef __FreeBSD__ 123fe5961aSDevin Teske #include <sys/cdefs.h> 133fe5961aSDevin Teske #endif 143fe5961aSDevin Teske 153fe5961aSDevin Teske #include <stdbool.h> 163fe5961aSDevin Teske #include <stddef.h> 173fe5961aSDevin Teske #include <stdint.h> 183fe5961aSDevin Teske 193fe5961aSDevin Teske /* 203fe5961aSDevin Teske * Supplant functionality missing on non-FreeBSD systems (musl libc, for 213fe5961aSDevin Teske * example, provides no <sys/cdefs.h>). On glibc, <stdint.h> pulls in the 223fe5961aSDevin Teske * definitions via <features.h>. 233fe5961aSDevin Teske */ 243fe5961aSDevin Teske #ifndef __BEGIN_DECLS 253fe5961aSDevin Teske #ifdef __cplusplus 263fe5961aSDevin Teske #define __BEGIN_DECLS extern "C" { 273fe5961aSDevin Teske #define __END_DECLS } 283fe5961aSDevin Teske #else 293fe5961aSDevin Teske #define __BEGIN_DECLS 303fe5961aSDevin Teske #define __END_DECLS 313fe5961aSDevin Teske #endif 323fe5961aSDevin Teske #endif 333fe5961aSDevin Teske 343fe5961aSDevin Teske /* 350380d016SDevin Teske * Library version info 360380d016SDevin Teske */ 37*90ad63b5SDevin Teske #define BSDCONF_VERSION "1.1.1 2026-09-16" 380380d016SDevin Teske #define BSDCONF_VERSION_MAJOR 1 390380d016SDevin Teske #define BSDCONF_VERSION_MINOR 1 40*90ad63b5SDevin Teske #define BSDCONF_VERSION_PATCH 1 410380d016SDevin Teske 420380d016SDevin Teske /* 433fe5961aSDevin Teske * Union for storing various types of data in a single common container. 443fe5961aSDevin Teske * 453fe5961aSDevin Teske * NB: When writing a value with bsdconf_put(), the caller supplies the value 463fe5961aSDevin Teske * as a NUL-terminated string via the `str' member regardless of `type'; the 473fe5961aSDevin Teske * type only governs how the value is rendered (see bsdconf_put() below). 483fe5961aSDevin Teske * Signed and unsigned 32- and 64-bit members match sysctl(3) CTLTYPE widths. 493fe5961aSDevin Teske * Until CTLTYPE grows a floating-point kind, the union needs no float member; 503fe5961aSDevin Teske * a value that happens to contain a decimal point is still read and written 513fe5961aSDevin Teske * as a string. 523fe5961aSDevin Teske */ 533fe5961aSDevin Teske union bsdconf_value { 543fe5961aSDevin Teske void *data; /* Opaque pointer (DATA1..DATA3) */ 553fe5961aSDevin Teske char *str; /* Pointer to NUL-terminated string */ 563fe5961aSDevin Teske char **strarray; /* Pointer to an array of strings */ 573fe5961aSDevin Teske int32_t num; /* Signed 32-bit integer value */ 583fe5961aSDevin Teske uint32_t u_num; /* Unsigned 32-bit integer value */ 593fe5961aSDevin Teske int64_t num64; /* Signed 64-bit integer value */ 603fe5961aSDevin Teske uint64_t u_num64; /* Unsigned 64-bit integer value */ 613fe5961aSDevin Teske bool boolean; /* Boolean value */ 623fe5961aSDevin Teske }; 633fe5961aSDevin Teske 643fe5961aSDevin Teske /* 653fe5961aSDevin Teske * Option types (based on above value union) 663fe5961aSDevin Teske */ 673fe5961aSDevin Teske enum bsdconf_type { 683fe5961aSDevin Teske BSDCONF_TYPE_NONE = 0x0000, /* directives with no value */ 693fe5961aSDevin Teske BSDCONF_TYPE_BOOL = 0x0001, /* boolean */ 703fe5961aSDevin Teske BSDCONF_TYPE_INT = 0x0002, /* signed 32-bit integer */ 713fe5961aSDevin Teske BSDCONF_TYPE_UINT = 0x0004, /* unsigned 32-bit integer */ 723fe5961aSDevin Teske BSDCONF_TYPE_STR = 0x0008, /* string pointer */ 733fe5961aSDevin Teske BSDCONF_TYPE_STRARRAY = 0x0010, /* string array pointer */ 743fe5961aSDevin Teske BSDCONF_TYPE_DATA1 = 0x0020, /* void data type-1 (open) */ 753fe5961aSDevin Teske BSDCONF_TYPE_DATA2 = 0x0040, /* void data type-2 (open) */ 763fe5961aSDevin Teske BSDCONF_TYPE_DATA3 = 0x0080, /* void data type-3 (open) */ 773fe5961aSDevin Teske BSDCONF_TYPE_INT64 = 0x0100, /* signed 64-bit integer */ 783fe5961aSDevin Teske BSDCONF_TYPE_UINT64 = 0x0200, /* unsigned 64-bit integer */ 793fe5961aSDevin Teske BSDCONF_TYPE_RESERVED = 0x0400, /* reserved */ 803fe5961aSDevin Teske }; 813fe5961aSDevin Teske 823fe5961aSDevin Teske /* 833fe5961aSDevin Teske * Assignment operators. The default for a given file format is 843fe5961aSDevin Teske * BSDCONF_OP_ASSIGN; the remainder require BSDCONF_OPERATOR_EQUALS (make(1) 853fe5961aSDevin Teske * style configuration files such as make.conf(5)). 863fe5961aSDevin Teske */ 873fe5961aSDevin Teske enum bsdconf_op { 883fe5961aSDevin Teske BSDCONF_OP_DEFAULT = 0, /* format's natural operator */ 893fe5961aSDevin Teske BSDCONF_OP_ASSIGN, /* `=' assign */ 903fe5961aSDevin Teske BSDCONF_OP_APPEND, /* `+=' append (make) */ 913fe5961aSDevin Teske BSDCONF_OP_COND, /* `?=' assign if undefined (make) */ 923fe5961aSDevin Teske BSDCONF_OP_EXPAND, /* `:=' assign with expansion (make) */ 933fe5961aSDevin Teske BSDCONF_OP_SHELL, /* `!=' assign shell output (make) */ 943fe5961aSDevin Teske }; 953fe5961aSDevin Teske 963fe5961aSDevin Teske /* 973fe5961aSDevin Teske * Known configuration file formats (see bsdconf_format(3)). Formats numbered 983fe5961aSDevin Teske * BSDCONF_FORMAT_USER and above are assigned by bsdconf_format_register(). 993fe5961aSDevin Teske */ 1003fe5961aSDevin Teske enum bsdconf_format { 1013fe5961aSDevin Teske BSDCONF_FORMAT_GENERIC = 0, /* quote values only when required */ 1023fe5961aSDevin Teske BSDCONF_FORMAT_LOADER, /* loader.conf(5); always quoted */ 1033fe5961aSDevin Teske BSDCONF_FORMAT_SYSCTL, /* sysctl.conf(5); quote when needed */ 1043fe5961aSDevin Teske BSDCONF_FORMAT_MAKE, /* make.conf(5); `+=' et al. */ 1053fe5961aSDevin Teske BSDCONF_FORMAT_SRC, /* src build triad; make(1) syntax */ 1063fe5961aSDevin Teske BSDCONF_FORMAT_USER = 32, /* first registered format */ 1073fe5961aSDevin Teske }; 1083fe5961aSDevin Teske 1093fe5961aSDevin Teske /* 1103fe5961aSDevin Teske * A single entry in a format's ordered list of configuration sources. Most 1113fe5961aSDevin Teske * formats are backed by more than one file (read in a deterministic order 1123fe5961aSDevin Teske * with directives in later files overriding earlier ones) and some pull 1133fe5961aSDevin Teske * additional files from drop-in directories. 1143fe5961aSDevin Teske */ 1153fe5961aSDevin Teske enum bsdconf_source_type { 1163fe5961aSDevin Teske BSDCONF_SOURCE_FILE = 0, /* a single file */ 1173fe5961aSDevin Teske BSDCONF_SOURCE_DIR, /* each `*.conf' in a directory */ 1183fe5961aSDevin Teske BSDCONF_SOURCE_MODDIR, /* `<module>.conf' in a directory */ 1193fe5961aSDevin Teske }; 1203fe5961aSDevin Teske struct bsdconf_source { 1213fe5961aSDevin Teske enum bsdconf_source_type type; /* how to interpret path */ 1223fe5961aSDevin Teske const char *path; /* file or directory path */ 1233fe5961aSDevin Teske }; 1243fe5961aSDevin Teske 1253fe5961aSDevin Teske /* 1263fe5961aSDevin Teske * The format descriptor: a read-only table of properties characterizing a 1273fe5961aSDevin Teske * configuration file format (no relation to file descriptors). The built-in 1283fe5961aSDevin Teske * formats (above) are described by format descriptors of this same shape; a 1293fe5961aSDevin Teske * new format is "bolted on" by registering a format descriptor of its own -- 1303fe5961aSDevin Teske * typically derived from the built-in it most resembles (see 1313fe5961aSDevin Teske * bsdconf_format_derive() below) with only the differing members adjusted. 1323fe5961aSDevin Teske * 1333fe5961aSDevin Teske * A format whose backing files are themselves configuration data -- the 1343fe5961aSDevin Teske * boot loader reads only /boot/defaults/loader.conf and discovers every 1353fe5961aSDevin Teske * other file from the loader_conf_files, loader_conf_dirs, and 1363fe5961aSDevin Teske * local_loader_conf_files directives it finds along the way -- describes 1373fe5961aSDevin Teske * that machinery with the `defaults' member quintet below, and 1383fe5961aSDevin Teske * bsdconf_format_files() performs the same discovery the consumer does. 1393fe5961aSDevin Teske * The static `sources' list remains as the fallback for systems whose 1403fe5961aSDevin Teske * defaults file is missing. 1413fe5961aSDevin Teske */ 1423fe5961aSDevin Teske struct bsdconf_format_def { 1433fe5961aSDevin Teske const char *keyword; /* target keyword (or NULL) */ 1443fe5961aSDevin Teske const char *path; /* default write path (or NULL) */ 1453fe5961aSDevin Teske const struct bsdconf_source 1463fe5961aSDevin Teske *sources; /* ordered sources; NULL-path 1473fe5961aSDevin Teske terminated (or NULL if `path' 1483fe5961aSDevin Teske is the only source) */ 1493fe5961aSDevin Teske const char *defaults; /* defaults file (or NULL) */ 1503fe5961aSDevin Teske const char *defaults_env; /* environment variable overriding 1513fe5961aSDevin Teske the defaults file (or NULL) */ 1523fe5961aSDevin Teske const char *files_directive; /* directive listing conf files */ 1533fe5961aSDevin Teske const char *dirs_directive; /* directive listing drop-in dirs */ 1543fe5961aSDevin Teske const char *local_directive; /* directive listing local files */ 1553fe5961aSDevin Teske uint16_t processing; /* processing_options bitmask */ 1563fe5961aSDevin Teske uint16_t put; /* put_options bitmask */ 1573fe5961aSDevin Teske }; 1583fe5961aSDevin Teske 1593fe5961aSDevin Teske /* 1603fe5961aSDevin Teske * Options to bsdconf_parse() and bsdconf_put() for processing_options bitmask 1613fe5961aSDevin Teske */ 1623fe5961aSDevin Teske enum bsdconf_processing { 1633fe5961aSDevin Teske BSDCONF_BREAK_ON_EQUALS = 0x0001, /* stop at `=' */ 1643fe5961aSDevin Teske BSDCONF_BREAK_ON_SEMICOLON = 0x0002, /* `;' starts a new line */ 1653fe5961aSDevin Teske BSDCONF_CASE_SENSITIVE = 0x0004, /* applies to directives */ 1663fe5961aSDevin Teske BSDCONF_REQUIRE_EQUALS = 0x0008, /* assignment directives */ 1673fe5961aSDevin Teske BSDCONF_STRICT_EQUALS = 0x0010, /* `=' part of directive */ 1683fe5961aSDevin Teske BSDCONF_OPERATOR_EQUALS = 0x0020, /* `+=' `?=' `:=' `!=' */ 1693fe5961aSDevin Teske }; 1703fe5961aSDevin Teske 1713fe5961aSDevin Teske /* 1723fe5961aSDevin Teske * Options to bsdconf_put() for put_options bitmask 1733fe5961aSDevin Teske */ 1743fe5961aSDevin Teske enum bsdconf_put_flags { 1753fe5961aSDevin Teske BSDCONF_PUT_NO_DUPLICATES = 0x0001, /* error if found twice */ 1763fe5961aSDevin Teske BSDCONF_PUT_ALLOW_EMPTY = 0x0002, /* allow empty SET_VALUE */ 1773fe5961aSDevin Teske BSDCONF_PUT_BACKUP = 0x0004, /* back up file (`.bak') */ 1783fe5961aSDevin Teske BSDCONF_PUT_UNQUOTED = 0x0008, /* value.str as file text */ 1793fe5961aSDevin Teske BSDCONF_PUT_QUOTE_ALWAYS = 0x0010, /* always quote on output */ 1803fe5961aSDevin Teske }; 1813fe5961aSDevin Teske 1823fe5961aSDevin Teske /* 1833fe5961aSDevin Teske * Per-directive actions for bsdconf_put() 1843fe5961aSDevin Teske */ 1853fe5961aSDevin Teske enum bsdconf_action { 1863fe5961aSDevin Teske BSDCONF_ACTION_SET_VALUE = 0x0000, /* set/replace (default) */ 1873fe5961aSDevin Teske BSDCONF_ACTION_CHECK = 0x0001, /* compare against current */ 1883fe5961aSDevin Teske BSDCONF_ACTION_REMOVE = 0x0002, /* remove from config */ 1893fe5961aSDevin Teske }; 1903fe5961aSDevin Teske 1913fe5961aSDevin Teske /* 1923fe5961aSDevin Teske * Per-directive result codes set by bsdconf_put() 1933fe5961aSDevin Teske */ 1943fe5961aSDevin Teske enum bsdconf_result { 1953fe5961aSDevin Teske BSDCONF_DIRECTIVE_FOUND = 0x0001, /* vs not found (see added) */ 1963fe5961aSDevin Teske BSDCONF_VALUE_CHANGED = 0x0002, /* vs no change required */ 1973fe5961aSDevin Teske BSDCONF_DIRECTIVE_ADDED = 0x0004, /* vs already existed */ 1983fe5961aSDevin Teske BSDCONF_DIRECTIVE_REMOVED = 0x0008, /* vs not found */ 1993fe5961aSDevin Teske }; 2003fe5961aSDevin Teske 2013fe5961aSDevin Teske /* 2023fe5961aSDevin Teske * Anatomy of a config file option; used for both reading and writing. 2033fe5961aSDevin Teske * 2043fe5961aSDevin Teske * When parsing with bsdconf_parse(), `directive' is an fnmatch(3) pattern and 2053fe5961aSDevin Teske * `parse' is invoked for each matching statement (with `op' set to the 2063fe5961aSDevin Teske * statement's assignment operator beforehand). 2073fe5961aSDevin Teske * 2083fe5961aSDevin Teske * When writing with bsdconf_put(), `directive' is an exact token (a pattern 2093fe5961aSDevin Teske * is not a writable target), `action' selects the operation, and `result' 2103fe5961aSDevin Teske * and `line' report what was done. A non-zero `match_line' restricts the 2113fe5961aSDevin Teske * put to the statement on that physical line (0 matches any, as before); 2123fe5961aSDevin Teske * make(1) `+=' SET_VALUE still appends a new line when `match_line' is 0, 2133fe5961aSDevin Teske * but rewrites the matched statement when `match_line' selects it. 2143fe5961aSDevin Teske */ 2153fe5961aSDevin Teske struct bsdconf_option { 2163fe5961aSDevin Teske enum bsdconf_type type; /* Option value type */ 2173fe5961aSDevin Teske const char *directive; /* config file keyword */ 2183fe5961aSDevin Teske union bsdconf_value value; /* NB: set by parse action; 2193fe5961aSDevin Teske * value to write for put */ 2203fe5961aSDevin Teske enum bsdconf_op op; /* assignment operator */ 2213fe5961aSDevin Teske uint8_t action; /* bsdconf_put() action */ 2223fe5961aSDevin Teske uint16_t result; /* NB: set by bsdconf_put() */ 2233fe5961aSDevin Teske uint32_t line; /* NB: set by bsdconf_put() */ 2243fe5961aSDevin Teske uint32_t match_line; /* put: 0=any, else only line */ 2253fe5961aSDevin Teske 2263fe5961aSDevin Teske /* 2273fe5961aSDevin Teske * Function pointer; action to be taken when the directive is found 2283fe5961aSDevin Teske * by bsdconf_parse(). Non-zero return aborts the parse (and is 2293fe5961aSDevin Teske * propagated to the bsdconf_parse() caller). 2303fe5961aSDevin Teske */ 2313fe5961aSDevin Teske int (*parse)(struct bsdconf_option *option, uint32_t line, 2323fe5961aSDevin Teske char *directive, char *value); 2333fe5961aSDevin Teske }; 2343fe5961aSDevin Teske 2353fe5961aSDevin Teske /* 2363fe5961aSDevin Teske * Function prototypes 2373fe5961aSDevin Teske * 2383fe5961aSDevin Teske * All functions returning int return zero on success (except 2393fe5961aSDevin Teske * bsdconf_spool(), which returns a new file descriptor); otherwise -1 (or 2403fe5961aSDevin Teske * the non-zero result of a parse call-back) and errno should be consulted. 2413fe5961aSDevin Teske */ 2423fe5961aSDevin Teske __BEGIN_DECLS 2433fe5961aSDevin Teske int bsdconf_parse(struct bsdconf_option _options[], 2443fe5961aSDevin Teske const char *_path, 2453fe5961aSDevin Teske int (*_unknown)(struct bsdconf_option *_option, 2463fe5961aSDevin Teske uint32_t _line, char *_directive, char *_value), 2473fe5961aSDevin Teske uint16_t _processing_options); 2483fe5961aSDevin Teske int bsdconf_fparse(struct bsdconf_option _options[], 2493fe5961aSDevin Teske int _fd, 2503fe5961aSDevin Teske int (*_unknown)(struct bsdconf_option *_option, 2513fe5961aSDevin Teske uint32_t _line, char *_directive, char *_value), 2523fe5961aSDevin Teske uint16_t _processing_options); 2533fe5961aSDevin Teske int bsdconf_spool(int _fd); 2543fe5961aSDevin Teske struct bsdconf_option *bsdconf_get_option(struct bsdconf_option _options[], 2553fe5961aSDevin Teske const char *_directive); 2563fe5961aSDevin Teske char *bsdconf_unquote(char *_value); 2573fe5961aSDevin Teske int bsdconf_set_option(struct bsdconf_option _options[], 2583fe5961aSDevin Teske const char *_directive, 2593fe5961aSDevin Teske union bsdconf_value *_value); 2603fe5961aSDevin Teske int bsdconf_put(struct bsdconf_option _options[], 2613fe5961aSDevin Teske const char *_path, uint16_t _processing_options, 2623fe5961aSDevin Teske uint16_t _put_options); 2633fe5961aSDevin Teske 2643fe5961aSDevin Teske /* 2653fe5961aSDevin Teske * Format abstraction layer (see bsdconf_format(3)) 2663fe5961aSDevin Teske */ 2673fe5961aSDevin Teske int bsdconf_format_derive(enum bsdconf_format _base, 2683fe5961aSDevin Teske struct bsdconf_format_def *_def); 2693fe5961aSDevin Teske int bsdconf_format_files(enum bsdconf_format _format, 2703fe5961aSDevin Teske const char *_rootdir, const char *_module, 2713fe5961aSDevin Teske const char *_defaults, char ***_filesp, 2723fe5961aSDevin Teske size_t *_nfilesp, size_t *_write_idxp); 2733fe5961aSDevin Teske void bsdconf_format_files_free(char **_files, 2743fe5961aSDevin Teske size_t _nfiles); 2753fe5961aSDevin Teske int bsdconf_format_find(const char *_keyword, 2763fe5961aSDevin Teske enum bsdconf_format *_format); 2773fe5961aSDevin Teske enum bsdconf_format bsdconf_format_guess(const char *_path); 2783fe5961aSDevin Teske const struct bsdconf_format_def 2793fe5961aSDevin Teske *bsdconf_format_lookup(enum bsdconf_format _format); 2803fe5961aSDevin Teske const char *bsdconf_format_path(enum bsdconf_format _format); 2813fe5961aSDevin Teske uint16_t bsdconf_format_processing( 2823fe5961aSDevin Teske enum bsdconf_format _format); 2833fe5961aSDevin Teske uint16_t bsdconf_format_put(enum bsdconf_format _format); 2843fe5961aSDevin Teske int bsdconf_format_register( 2853fe5961aSDevin Teske const struct bsdconf_format_def *_def, 2863fe5961aSDevin Teske enum bsdconf_format *_format); 2873fe5961aSDevin Teske __END_DECLS 2883fe5961aSDevin Teske 2893fe5961aSDevin Teske #endif /* !_BSDCONF_H_ */ 290