The configuration file generation framework is
a build system for configurations files.
It is based on make
and offers a variety of rules
as well as helper scripts.
A Debian package for cfgf
is available in
the apt repository on this server.
By itself, cfgf
does not provide any
configuration file template,
but it has been designed as a pluggable system.
Third-party modules can enhance its functionnality
and provide such templates,
either by dropping files
in its installation directories directly
(naming conventions are defined in order to avoid conflicts),
or by using a separate directory
to be inserted by the administrator
into cfgf
's VPATH
.
Features
- Generation of configuration files from m4 templates, the output of a script, parts in a directory or customized make rules
- Reloads the affected daemons when the configuration is updated
- Built-in tests
- Ready to use on a Debian system
Filesystem usage
cfgf
uses GNU make
's VPATH
feature to superimpose
multiple directories below the working directory.
On an FHS-abiding system these would be:
/usr/share/cfgf
,/usr/lib/cfgf
: installation directories;/etc/cfgf
: additionnal stuff provided by the administrator, such as configuration file templates;/var/lib/cfgf
: the working directory, where generated files end up.
Those directories contain the following files as well as their sources:
etc/
: configuration filesrules.d/
: makefile fragments sourced by the toplevel-makefile;bin/
: helper scripts;tests/
: source files for tests and expected results;stamp/
: stamp files;m4f
,m4h
: the m4 frozen state to use when invoking m4, and its source code, usually generated viam4h.d/
.
make
targets
update
is the default target, it will update the generated configuration files.install
creates symlinks in/etc
pointing to the generated files. Existing files will be backed up (seeetc.orig/%
below).reload
instruct services whose configuration has been updated to reload their configuration files.test
runs the tests, comparing built test files with their expected output.runm4
runsm4
as if it were to interpret a.m4
file.runsh
runs a shell within the same environment asmake
rules.
Configuration file symlinks installation
/etc/%
installs a specific configuration file.etc.orig/%
is created as a backup for/etc/%
, provided it is not a symlink into the working directory.
Stamp files
stamp/%
: the file istouch
ed after its dependencies have been build. Mostly useful as a hook to provide dependencies for or to depend upon.stamp/init.d/%
:touch
ed after runninginvoke-rc.d % force-reload
. It is intended that configuration files are dependencies of this stamp file, and that whoever introduces them register the stamp file as a dependency tostamp/init
.
File generation rules
%.d
->%
: concatenates the files in%.d/
into%
. Within such directories the files are usually named nn_
prefix[_
desc], where:- nn is two-digit sequence number used for ordering the files
(
50
should be used in the absence of particular requirements), - prefix is a unique prefix identifying the module which provided the file,
in order to avoid name conflicts
(
cfgf
itself usescg
, an administrator may uselocal
), - desc is an optional short description of the file's function.
- nn is two-digit sequence number used for ordering the files
(
%.new
->%
: copies its source onto its destination, provided it does not already has the exact same content. This avoid timestamps being updated (and depending rules invoked) when a file has been regenerated in the exact same way.%.static
->%
: simply symlinks its source to its target.%.m4
->%.new
: feeds its input tom4
with definitions fromm4h.d
loaded.%.gen
->%
: runs%.gen
, redirecting its standard output to the target file. The relative pathname of the target is passed as a command-line argument. (unimplemented as of now)%.expected
,%
->%.passed
:touch
es its target if%.expected
and%
are identical, or fails after printing the differences on the standard output.
m4
macros
cg_if(
expr,
trueval,
falseval)
expands to falseval if expr evaluates to zero, or trueval otherwise.cg_resyscmd(
command,
regexp)
matches each line of the command's output against the given extended regexp, and replaces the match with regexp's first subexpression (mostly useful ascg_resyscmd(command, ^.*foo ([^ ]*) bar.*$
).cg_inl
inserts a newline character.cg_quote(*level*, ...)
quotes its arguments as specified by level:- a level of
0
will makecg_quote
expands to its unquoted list of extra arguments; - a level of
1
will make it quote each argument; - levels of
2
and above each add a pair of quotes around the whole list.
- a level of
cg_comment
inserts a fairly standard "automatically generated" comment, and is used at the beginning of configuration file templates.
Lists
cg_list(
list)
"canonicalizes" its list of arguemnts, by expanding to its coma-separated list of quoted arguements, with trailing empty ones removed.cg_len(
list)
expands to the length of its argument list, with trailing empty arguments ignored.cg_head(
n,
list)
expands to the list of the n leading elements of the given list.cg_tail(
n,
list)
expands to the list of the n trailing elements of the given list.cg_index(
n,
list)
expands to the nth element of list, quoted once.cg_lookup(
elem,
list)
looks up elem in list and expands to the position of its first occurence within the given list, or -1 if there is none.cg_reverse(
list)
expands to list in the reversed order.cg_split(
sep,
str)
splits str using sep as a separator and return a list of the pieces obtained.cg_join(
sep,
list...)
joins the elements of list with sep and expands to the resulting string.
List loops
cg_foreach(
var,
n,
exp,
list...)
expands to the concatenation of the expansions of exp with var defined as each successive element of list, quoted n times.cg_map(
var,
n,
subst,
list...)
expands to a list constructed by expanding subst with var defined as each successive element of list, quoted n times.cg_grep(
var,
n,
exp,
val,
list...)
tests each element of list, by expanding exp with var defined as the element quoted n times, and comparing the result to val. It expends to the list of arguments for which they were equal.
Associative lists
Key-value associations can be encoded as lists of lists, where the leading element of each sub-list acts as the key, and the trailing ones as the value. Non-listed keys conceptually have an empty value associated to them.
cg_field(
key,
alist...)
expands to the value associated to key in alist.cg_setfield(
key,
val,
alist...`) expands to alist with the value associated to key replaced by val.cg_setfields(
alist,
k1,
v1,
k2,
v2,
...)
sets multiple values in alist in a single call.
Helper scripts
cg_find
Usage: cg_find
options [--
] findargs
Runs find
in each directory from the union used by cfgf
,
and combines the result.
The precedence between the directories is preserved.
The following output formatting options are available:
-s
: short mode, only the relative pathname of found files will be displayed;-l
: appends the directory the file was found in to each line, after a single space character;-f
: prints the full path of found files.
Note that -l
and -f
will still print .
for the working directory.
The findargs are passed to find
untouched,
and as such can comprise anything it understands,
including a leading list of subdirectories to search.
However, the found files should still be printed by find
in the usual, default -print
format,
or cg_find
won't be able to decipher its output.
cg_m4ify
Useful from m4 templates, this helper script converts its standard input to an m4 list. Its optional depth argument is an integer. The following conversions are made:
- lines containing a comma of a quote character are removed;
- lines are separated by
",\n"
, and the trailing newline is removed; - if depth >= 1, quotes are added around the whole line;
- if depth >= 2, the lines themselves are converted to lists,
with each (space-separted) field quoted and
", "
added between them.