1 /* 2 * Copyright (c) 2002-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 #ifndef _BSDCONF_H_ 9 #define _BSDCONF_H_ 10 11 #ifdef __FreeBSD__ 12 #include <sys/cdefs.h> 13 #endif 14 15 #include <stdbool.h> 16 #include <stddef.h> 17 #include <stdint.h> 18 19 /* 20 * Supplant functionality missing on non-FreeBSD systems (musl libc, for 21 * example, provides no <sys/cdefs.h>). On glibc, <stdint.h> pulls in the 22 * definitions via <features.h>. 23 */ 24 #ifndef __BEGIN_DECLS 25 #ifdef __cplusplus 26 #define __BEGIN_DECLS extern "C" { 27 #define __END_DECLS } 28 #else 29 #define __BEGIN_DECLS 30 #define __END_DECLS 31 #endif 32 #endif 33 34 /* 35 * Library version info 36 */ 37 #define BSDCONF_VERSION "1.1.1 2026-09-16" 38 #define BSDCONF_VERSION_MAJOR 1 39 #define BSDCONF_VERSION_MINOR 1 40 #define BSDCONF_VERSION_PATCH 1 41 42 /* 43 * Union for storing various types of data in a single common container. 44 * 45 * NB: When writing a value with bsdconf_put(), the caller supplies the value 46 * as a NUL-terminated string via the `str' member regardless of `type'; the 47 * type only governs how the value is rendered (see bsdconf_put() below). 48 * Signed and unsigned 32- and 64-bit members match sysctl(3) CTLTYPE widths. 49 * Until CTLTYPE grows a floating-point kind, the union needs no float member; 50 * a value that happens to contain a decimal point is still read and written 51 * as a string. 52 */ 53 union bsdconf_value { 54 void *data; /* Opaque pointer (DATA1..DATA3) */ 55 char *str; /* Pointer to NUL-terminated string */ 56 char **strarray; /* Pointer to an array of strings */ 57 int32_t num; /* Signed 32-bit integer value */ 58 uint32_t u_num; /* Unsigned 32-bit integer value */ 59 int64_t num64; /* Signed 64-bit integer value */ 60 uint64_t u_num64; /* Unsigned 64-bit integer value */ 61 bool boolean; /* Boolean value */ 62 }; 63 64 /* 65 * Option types (based on above value union) 66 */ 67 enum bsdconf_type { 68 BSDCONF_TYPE_NONE = 0x0000, /* directives with no value */ 69 BSDCONF_TYPE_BOOL = 0x0001, /* boolean */ 70 BSDCONF_TYPE_INT = 0x0002, /* signed 32-bit integer */ 71 BSDCONF_TYPE_UINT = 0x0004, /* unsigned 32-bit integer */ 72 BSDCONF_TYPE_STR = 0x0008, /* string pointer */ 73 BSDCONF_TYPE_STRARRAY = 0x0010, /* string array pointer */ 74 BSDCONF_TYPE_DATA1 = 0x0020, /* void data type-1 (open) */ 75 BSDCONF_TYPE_DATA2 = 0x0040, /* void data type-2 (open) */ 76 BSDCONF_TYPE_DATA3 = 0x0080, /* void data type-3 (open) */ 77 BSDCONF_TYPE_INT64 = 0x0100, /* signed 64-bit integer */ 78 BSDCONF_TYPE_UINT64 = 0x0200, /* unsigned 64-bit integer */ 79 BSDCONF_TYPE_RESERVED = 0x0400, /* reserved */ 80 }; 81 82 /* 83 * Assignment operators. The default for a given file format is 84 * BSDCONF_OP_ASSIGN; the remainder require BSDCONF_OPERATOR_EQUALS (make(1) 85 * style configuration files such as make.conf(5)). 86 */ 87 enum bsdconf_op { 88 BSDCONF_OP_DEFAULT = 0, /* format's natural operator */ 89 BSDCONF_OP_ASSIGN, /* `=' assign */ 90 BSDCONF_OP_APPEND, /* `+=' append (make) */ 91 BSDCONF_OP_COND, /* `?=' assign if undefined (make) */ 92 BSDCONF_OP_EXPAND, /* `:=' assign with expansion (make) */ 93 BSDCONF_OP_SHELL, /* `!=' assign shell output (make) */ 94 }; 95 96 /* 97 * Known configuration file formats (see bsdconf_format(3)). Formats numbered 98 * BSDCONF_FORMAT_USER and above are assigned by bsdconf_format_register(). 99 */ 100 enum bsdconf_format { 101 BSDCONF_FORMAT_GENERIC = 0, /* quote values only when required */ 102 BSDCONF_FORMAT_LOADER, /* loader.conf(5); always quoted */ 103 BSDCONF_FORMAT_SYSCTL, /* sysctl.conf(5); quote when needed */ 104 BSDCONF_FORMAT_MAKE, /* make.conf(5); `+=' et al. */ 105 BSDCONF_FORMAT_SRC, /* src build triad; make(1) syntax */ 106 BSDCONF_FORMAT_USER = 32, /* first registered format */ 107 }; 108 109 /* 110 * A single entry in a format's ordered list of configuration sources. Most 111 * formats are backed by more than one file (read in a deterministic order 112 * with directives in later files overriding earlier ones) and some pull 113 * additional files from drop-in directories. 114 */ 115 enum bsdconf_source_type { 116 BSDCONF_SOURCE_FILE = 0, /* a single file */ 117 BSDCONF_SOURCE_DIR, /* each `*.conf' in a directory */ 118 BSDCONF_SOURCE_MODDIR, /* `<module>.conf' in a directory */ 119 }; 120 struct bsdconf_source { 121 enum bsdconf_source_type type; /* how to interpret path */ 122 const char *path; /* file or directory path */ 123 }; 124 125 /* 126 * The format descriptor: a read-only table of properties characterizing a 127 * configuration file format (no relation to file descriptors). The built-in 128 * formats (above) are described by format descriptors of this same shape; a 129 * new format is "bolted on" by registering a format descriptor of its own -- 130 * typically derived from the built-in it most resembles (see 131 * bsdconf_format_derive() below) with only the differing members adjusted. 132 * 133 * A format whose backing files are themselves configuration data -- the 134 * boot loader reads only /boot/defaults/loader.conf and discovers every 135 * other file from the loader_conf_files, loader_conf_dirs, and 136 * local_loader_conf_files directives it finds along the way -- describes 137 * that machinery with the `defaults' member quintet below, and 138 * bsdconf_format_files() performs the same discovery the consumer does. 139 * The static `sources' list remains as the fallback for systems whose 140 * defaults file is missing. 141 */ 142 struct bsdconf_format_def { 143 const char *keyword; /* target keyword (or NULL) */ 144 const char *path; /* default write path (or NULL) */ 145 const struct bsdconf_source 146 *sources; /* ordered sources; NULL-path 147 terminated (or NULL if `path' 148 is the only source) */ 149 const char *defaults; /* defaults file (or NULL) */ 150 const char *defaults_env; /* environment variable overriding 151 the defaults file (or NULL) */ 152 const char *files_directive; /* directive listing conf files */ 153 const char *dirs_directive; /* directive listing drop-in dirs */ 154 const char *local_directive; /* directive listing local files */ 155 uint16_t processing; /* processing_options bitmask */ 156 uint16_t put; /* put_options bitmask */ 157 }; 158 159 /* 160 * Options to bsdconf_parse() and bsdconf_put() for processing_options bitmask 161 */ 162 enum bsdconf_processing { 163 BSDCONF_BREAK_ON_EQUALS = 0x0001, /* stop at `=' */ 164 BSDCONF_BREAK_ON_SEMICOLON = 0x0002, /* `;' starts a new line */ 165 BSDCONF_CASE_SENSITIVE = 0x0004, /* applies to directives */ 166 BSDCONF_REQUIRE_EQUALS = 0x0008, /* assignment directives */ 167 BSDCONF_STRICT_EQUALS = 0x0010, /* `=' part of directive */ 168 BSDCONF_OPERATOR_EQUALS = 0x0020, /* `+=' `?=' `:=' `!=' */ 169 }; 170 171 /* 172 * Options to bsdconf_put() for put_options bitmask 173 */ 174 enum bsdconf_put_flags { 175 BSDCONF_PUT_NO_DUPLICATES = 0x0001, /* error if found twice */ 176 BSDCONF_PUT_ALLOW_EMPTY = 0x0002, /* allow empty SET_VALUE */ 177 BSDCONF_PUT_BACKUP = 0x0004, /* back up file (`.bak') */ 178 BSDCONF_PUT_UNQUOTED = 0x0008, /* value.str as file text */ 179 BSDCONF_PUT_QUOTE_ALWAYS = 0x0010, /* always quote on output */ 180 }; 181 182 /* 183 * Per-directive actions for bsdconf_put() 184 */ 185 enum bsdconf_action { 186 BSDCONF_ACTION_SET_VALUE = 0x0000, /* set/replace (default) */ 187 BSDCONF_ACTION_CHECK = 0x0001, /* compare against current */ 188 BSDCONF_ACTION_REMOVE = 0x0002, /* remove from config */ 189 }; 190 191 /* 192 * Per-directive result codes set by bsdconf_put() 193 */ 194 enum bsdconf_result { 195 BSDCONF_DIRECTIVE_FOUND = 0x0001, /* vs not found (see added) */ 196 BSDCONF_VALUE_CHANGED = 0x0002, /* vs no change required */ 197 BSDCONF_DIRECTIVE_ADDED = 0x0004, /* vs already existed */ 198 BSDCONF_DIRECTIVE_REMOVED = 0x0008, /* vs not found */ 199 }; 200 201 /* 202 * Anatomy of a config file option; used for both reading and writing. 203 * 204 * When parsing with bsdconf_parse(), `directive' is an fnmatch(3) pattern and 205 * `parse' is invoked for each matching statement (with `op' set to the 206 * statement's assignment operator beforehand). 207 * 208 * When writing with bsdconf_put(), `directive' is an exact token (a pattern 209 * is not a writable target), `action' selects the operation, and `result' 210 * and `line' report what was done. A non-zero `match_line' restricts the 211 * put to the statement on that physical line (0 matches any, as before); 212 * make(1) `+=' SET_VALUE still appends a new line when `match_line' is 0, 213 * but rewrites the matched statement when `match_line' selects it. 214 */ 215 struct bsdconf_option { 216 enum bsdconf_type type; /* Option value type */ 217 const char *directive; /* config file keyword */ 218 union bsdconf_value value; /* NB: set by parse action; 219 * value to write for put */ 220 enum bsdconf_op op; /* assignment operator */ 221 uint8_t action; /* bsdconf_put() action */ 222 uint16_t result; /* NB: set by bsdconf_put() */ 223 uint32_t line; /* NB: set by bsdconf_put() */ 224 uint32_t match_line; /* put: 0=any, else only line */ 225 226 /* 227 * Function pointer; action to be taken when the directive is found 228 * by bsdconf_parse(). Non-zero return aborts the parse (and is 229 * propagated to the bsdconf_parse() caller). 230 */ 231 int (*parse)(struct bsdconf_option *option, uint32_t line, 232 char *directive, char *value); 233 }; 234 235 /* 236 * Function prototypes 237 * 238 * All functions returning int return zero on success (except 239 * bsdconf_spool(), which returns a new file descriptor); otherwise -1 (or 240 * the non-zero result of a parse call-back) and errno should be consulted. 241 */ 242 __BEGIN_DECLS 243 int bsdconf_parse(struct bsdconf_option _options[], 244 const char *_path, 245 int (*_unknown)(struct bsdconf_option *_option, 246 uint32_t _line, char *_directive, char *_value), 247 uint16_t _processing_options); 248 int bsdconf_fparse(struct bsdconf_option _options[], 249 int _fd, 250 int (*_unknown)(struct bsdconf_option *_option, 251 uint32_t _line, char *_directive, char *_value), 252 uint16_t _processing_options); 253 int bsdconf_spool(int _fd); 254 struct bsdconf_option *bsdconf_get_option(struct bsdconf_option _options[], 255 const char *_directive); 256 char *bsdconf_unquote(char *_value); 257 int bsdconf_set_option(struct bsdconf_option _options[], 258 const char *_directive, 259 union bsdconf_value *_value); 260 int bsdconf_put(struct bsdconf_option _options[], 261 const char *_path, uint16_t _processing_options, 262 uint16_t _put_options); 263 264 /* 265 * Format abstraction layer (see bsdconf_format(3)) 266 */ 267 int bsdconf_format_derive(enum bsdconf_format _base, 268 struct bsdconf_format_def *_def); 269 int bsdconf_format_files(enum bsdconf_format _format, 270 const char *_rootdir, const char *_module, 271 const char *_defaults, char ***_filesp, 272 size_t *_nfilesp, size_t *_write_idxp); 273 void bsdconf_format_files_free(char **_files, 274 size_t _nfiles); 275 int bsdconf_format_find(const char *_keyword, 276 enum bsdconf_format *_format); 277 enum bsdconf_format bsdconf_format_guess(const char *_path); 278 const struct bsdconf_format_def 279 *bsdconf_format_lookup(enum bsdconf_format _format); 280 const char *bsdconf_format_path(enum bsdconf_format _format); 281 uint16_t bsdconf_format_processing( 282 enum bsdconf_format _format); 283 uint16_t bsdconf_format_put(enum bsdconf_format _format); 284 int bsdconf_format_register( 285 const struct bsdconf_format_def *_def, 286 enum bsdconf_format *_format); 287 __END_DECLS 288 289 #endif /* !_BSDCONF_H_ */ 290