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