2 releases
Uses new Rust 2024
| 0.1.2 | May 3, 2026 |
|---|---|
| 0.1.1 | May 2, 2026 |
#35 in Configuration
65KB
1.5K
SLoC
Tomloader
Tomloader is an utility designed to facilitate the creation of multiple systemd unit files through grouping and reuse of systemd fields.
Installation
Tomloader can be installed through cargo:
cargo install tomloader
To generate info/plaintext/html/epub3/docbook documentation you can use the texi2any command on texinfo/tomloader.texi. For example, to generate info pages which can be then installed with install-info
texi2any texinfo/tomloader.texi
To generate PDF documentation you can use the texi2pdf command:
texi2pdf --clean texinfo/tomloader.texi
or
texi2pdf --tidy texinfo/tomloader.texi
Overview
The primary purpose of Tomloader is to simplify the managment of multiple systemd unit files that share configuration fields.
Systemd already provides native support for configuration reuse through .conf files inside drop-in directories (.d/) which can be hierarchically organized (for example, unit A-B-C.unit inherits all drop-in configurations listed in A-B-.unit.d/ and A-.unit.d/). While useful, it present some limitations:
- inheritance follows a single linear hierarchy, preventing composition from multiple unrelated sources;
.conffiles cannot be templated;- the final result heavily depends on file ordering inside the drop-in directories, making outcomes sensitive to file naming;
- dependencies between
.conffiles cannot be explicitly expressed.
Although symlinks may be used the first issue, they do no address the lack of parametrization. Additionally, ordering constraints remain a source of potential misconfiguration even with naming conventions like numeric prefixes.
Tomloader addresses these limitations with the introduction of groups. A group is a collection of systemd configuration fields which may declare dependencies on other groups. Despite .conf files in drop-in directories:
- multiple groups are not allowed to modify the same fiel; such conflicts are detected and will prevent unit generation unless resolved;
- groups may define parameters that may change the values set by configuration fields.
Groups may declare dependencies of two distinct types:
- pull dependencies: automatically included whenever the parent group is used;
- replace dependencies: explicitly excluded whenever the parent group is used, even if references elsewhere.
Group configuration
Groups are defined in the file ${XDG_CONFIG_HOME}/tomloader/groups.kdl formatted as a KDL configuration file. Eachgroup is declared by using a def-group node:
def-group Group1 {
sd_section Unit {
add After "dbus.service" "pipewire.service"
add Requisite "dbus.service" "pipewire.service"
reset JoinsNamespaceOf
set Description "Group1"
}
}
def-group Group2 {
sd_section Unit {
set Documentation "man:group(2)"
}
sd_section Service {
set Type "exec"
}
}
Each group may contain zero or more sd_section nodes representing systemd sections (e.g. Unit, Service, Slice). Within each section, the following operations are supported:
set: assigns one or more values to a field;reset: clear the field;add: adds one or more values to the field.
Field values are internally represented as a list of strings. Just before generating the systemd unit file each list of strings is merged into a single:
- whitespace-separated string (default behaviour);
- colon-separated string for
ExecSearchPath; - comma-separated string for
RootImageOptions.
Note: The order of values is not preserved. If ordering is significant, a single set operation with a full-formed string value is sufficient.
Unit configuration
To generate a systemd unit named unitname.ext, a corresponding KDL-formatted unit configuration file named unitname.ext.kdl must be provided. Example:
pull {
group Group1
group Group2
}
sd_section Unit {
set Description "Unit"
}
sd_section Service {
set ExecStart "/usr/bin/bash"
}
Notice that:
- the
pullnode specifies groups to include; - the
replacenode specifies groups to exclude, even if they are included directly.
A group may appear in both pull and replace. In this case, the group itself is excluded while its dependencies remain included.
Dependencies
Just like unit configuration files, dependencies in groups are defined inside pull and replace nodes. For example:
def-group Group3 {}
def-group Group4 {
pull {
group Group3
}
}
def-group Group5 {
pull {
group Group3
group Group4
}
}
def-group Group6 {
pull {
group Group4
}
replace {
group Group3
}
}
Dependencies are transitive by default:
- pull dependencies propagate both pull and replace dependencies;
- replace dependencies propagate only replace dependencies.
Transitive behaviour can be disabled with the inherit=#false property in group nodes, which can be used in unit configuration files too:
def-group Group7 {
pull {
// Group4 is loaded as pull dependency
// Group3 is loaded as a replace dependency
group Group6
}
}
def-group Group8 {
pull {
// No further groups are loaded as dependency
group Group6 inherit=#false
}
}
Parameters and Arguments
A group may define one or more parameters:
def-group GroupA 2 {
sd_section Service {
set PIDFile "/run/${0}-pid"
set ExecStartPre "/usr/bin/pre-${1}"
}
}
Parameters can be accessed in string values or dependency arguments through ${0}, ${1}, ..., ${N-1} specifiers. Additionally, the special specifier ${$} expands into a literal $.
Arguments are provided through a child args node:
def-group GroupB {
pulls {
group GroupA {
args "pidname" "exec-sh"
}
}
}
def-group GroupC 1 {
pulls {
group GroupA {
args "${0}-A" "${0}-B"
}
}
}
Command-line interface
The tomloader executable generates systemd unit files from .kdl configuration files.
Current interface:
tomloader sd-v0.1 -t <output directory> [unit configuration file 1] [unit configuration file 2]...
Additional options are documented in tomloader --help or tomloader sd-v0.1 --help.
Backward-incompatible changes to the command-line interface are introduced through new subcommands. Existing subcommands remain backward-compatible, except where changes are required for security or deprecation reasons.
Dependencies
~5MB
~98K SLoC