xref: /freebsd/lib/libbsdconf/bsdconf.h (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
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