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