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