Complete the native configuration trinity: sysctl(8) modifies kernel
state, sysrc(8) manages rc.conf(5), and sysconf(8) reads and modifies
the remaining configuration files of the base system -- loader.conf(5),
sysctl.conf(5), and the make.conf(5) family -- atop a new library,
libbsdconf(3).
The library resurrects the figpar parsing engine, re-engineered into a
unified reader/writer. The parser keeps figpar's ethos intact: raw
directive/value pairs are handed to caller callbacks that own all
semantics, so drastically different formats (space-separated
httpd-style statements, cumulative directives) are read with the stock
engine and bespoke callbacks alone. Statements may span physical
lines via backslash-newline continuation (essential to make.conf and
its siblings), and input that cannot seek -- a pipe or socket, stdin
included -- is detected up front and spooled to an unlinked temporary
through the exported bsdconf_spool(), so streams parse as readily as
files. Three bugs inherited from figpar are fixed in the port:
unquoted inline comment markers leaked into values, the terminating
semicolon was retained in the value under BREAK_ON_SEMICOLON, and
reported line numbers drifted on multi-line directives.
Writes never touch a file in place. Updates stream to a mkstemp(3)
temporary in the target directory, are flushed with fsync(2), and are
atomically rename(2)d over the target with mode and ownership
propagated, so a crash or power loss mid-write cannot truncate or
corrupt a configuration file. The transaction is hardened against
interference as well as interruption: only a regular file is accepted
as a write target (opened O_NONBLOCK so a fifo fails fast instead of
blocking), mode and ownership are taken by fstat(2) from the
descriptor actually read and applied with fchmod(2)/fchown(2), the
temporary never transits a world-writable directory, and the optional
`.bak' backup refuses to follow a planted symbolic link (O_NOFOLLOW).
Symbolic links in the target path are resolved first, so writing
through a link rewrites the file it points at and preserves the link.
The properties (and the deliberate non-goals) are documented in a
SECURITY CONSIDERATIONS section of bsdconf(3).
Above the engine sits a format descriptor layer, one format per source
file plus a central registry: a descriptor contributes a target
keyword, an ordered list of backing files, and tokenizing/quoting
bitmasks -- never private parsing code. Applications bolt on formats
at run time by deriving from the nearest built-in and registering the
result. Built-in formats honor the full syntax of their consumers:
loader.conf(5) values are always written quoted with strict equals, as
the boot loader's reader demands; sysctl.conf(5) values are quoted
only when required to round-trip; and the make(1) family (make.conf,
src.conf, src-env.conf) is written unquoted with assignment modifiers
(+=, ?=, :=, !=) and the empty value (the WITH_/WITHOUT_ knob idiom)
preserved.
Formats backed by multiple files (loader.conf + loader.conf.d/*.conf +
loader.conf.local; sysctl.conf + sysctl.conf.local +
sysctl.kld.d/<module>.conf) are consulted automatically in the order
the system sources them at boot. Reads report the authoritative
(last) value, writes land in the authoritative file so the change
survives a reboot, new directives are appended to the default file,
and removals strike every file listing the directive lest deleting the
authoritative definition merely unmask an earlier one.
The sysconf(8) frontend maps assignment syntax directly onto
sysctl(8): a name alone reads, name=value writes, and options are
accepted on either side of the required target keyword. The format is
always stated explicitly by the target and never guessed from a
filename; -f overrides which file is operated on, never how, so a
loader.conf-formatted file can be staged anywhere and any format can
be deliberately applied to any file (a generic target serves files
matching no known format). A file of `-' means standard input,
permitting reads and checks from a pipeline; fifos and /dev/stdin are
spooled before the sandbox closes, and write requests against
standard input are rejected up front. -a dumps the union across a target's
files, -l/-L list backing files and module drop-in candidates (-E
filters to those on disk), -F names the file holding each directive's
effective value, -k includes a module's sysctl.kld.d drop-in, -c
checks without modifying, -x removes, and -j/-R operate on a jail or
alternate root. Writes to the sysctl target of the running system are
validated against the kernel first: unknown OIDs, read-only OIDs, and
loader-only tunables (CTLFLAG_TUN) are reported and skipped -- the
latter with a pointer to the loader target -- while remaining valid
assignments still apply. Read-only invocations run under a
capsicum(4) sandbox.
The library is strict POSIX C and builds with -std=c99
-D_POSIX_C_SOURCE=200809L with no FreeBSD headers; capsicum, jail,
kld, and sysctl validation layers in the frontend are conditional, and
bsdconf.h supplies a __BEGIN_DECLS fallback for libcs lacking
<sys/cdefs.h>. The bsdconf(3) manual defines the format descriptor at
first use, walks through adding formats (callbacks first, descriptors
second), and closes with a compiled-and-run EXAMPLES section covering
callback parsing, cumulative-directive accumulation, and run-time
format registration. The parser, writer, every format, and every
frontend flag were exercised in a fake-root harness under
AddressSanitizer/UndefinedBehaviorSanitizer, including quoting and
line-continuation round-trips against the real consumers' rules and a
symlink-attack test of the backup path; both manual pages pass
mandoc -Tlint. Not yet exercised: a full buildworld against the new
Makefiles.
libfigpar remains in the tree untouched; its removal is left to a
future commit once downstream consumers are migrated.
Co-authored-by: Faraz Vahedi <kfv@kfv.io>