s6-rc
Software
skarnet.org
The s6-rc-compile program
s6-rc-compile is a service database compiler. It takes a series
of service definitions in the source format, and compiles
them into a directory, which is the compiled format.
The administrator can then examine that compiled database via
s6-rc-db, put it into a place where
it will be registered at boot time as the current compiled database
by s6-rc-init, or even live update
the current service database via
s6-rc-update.
Interface
s6-rc-compile [ -v verbosity ] compiled source...
- s6-rc-compile analyzes every directory source in its
arguments. For every subdirectory service in source,
it expects to find a valid service definition in service.
- s6-rc-compile outputs a compiled version of the service database
into compiled. This database contains information for all the
services declared in every source argument.
- s6-rc-compile exits 0.
Options
- -v verbosity : be more or less
verbose. Default is 1: warning and error messages will be printed to
stderr. 0 silences warnings. 2 adds a bit more information about
what s6-rc-compile is doing. 3 or more is heavy debug output.
Source format
s6-rc-compile scans every source directory to find
service definition directories in it. It ignores every
file that is not a directory, or that starts with a dot. For
every service definition directory that it finds, it creates a
service with the same name as the directory. Names cannot be
duplicated and cannot contain a slash or a newline; they can
contain spaces and tabs, but using anything else than alphanumerical
characters, underscores and dashes is discouraged - the s6-rc programs
will handle weird names just fine, but other tools, especially
shell scripts, may not.
Every service
definition directory service is expected to contain the following files:
For every service
- A regular file named type, that contains only the text
oneshot, longrun or bundle, and a terminating
newline. This file declares the type of service defined by the
directory.
For bundles
- A regular file named contents. This file must be a list
of service names, one per line. Whitespace at the beginning of a line
is ignored, but trailing whitespace is not. Lines starting with a #
character are ignored. The file defines the services that will be
represented by the bundle named service.
It is possible to use bundle names in a contents file.
However, if s6-rc-compile detects a cycle in bundle definitions, it will
complain and exit 1.
For atomic services
- An optional regular file named timeout-up. This file, if it exists,
must contain an integer, which is the maximum number of milliseconds
s6-rc will wait for successful completion of the service
start; if starting the service takes longer than this value, s6-rc will declare
the transition a failure. If the file does not exist, or contains 0, no timeout
is defined and s6-rc will wait indefinitely for the service to start.
- An optional regular file named timeout-down. This file, if it exists,
must contain an integer, which is the maximum number of milliseconds
s6-rc will wait for successful completion of the service
stop; if stopping the service takes longer than this value, s6-rc will declare
the transition a failure. If the file does not exist, or contains 0, no timeout
is defined and s6-rc will wait indefinitely for the service to stop.
- An optional regular file named dependencies. This file must be a list
of service names, one per line. Whitespace at the beginning of a line
is ignored, but trailing whitespace is not. Lines starting with a #
character are ignored. The file defines the direct dependencies of
service, i.e. the services that must be up in order for
service to work properly.
It is unnecessary to manually define complete sets of dependencies in the
dependency file, because
s6-rc will properly handle dependency chains.
anyway. If A depends on B, no matter the underlying
implementation of B, and the current implementation of B
depends on C, then you should just put B in
A/dependencies; when starting the set,
s6-rc will start C first, then
B, then A. If the underlying implementation of B
changes and does not depend on C, then you will just have to
modify the dependencies for B, not for A
Of course, if A depends on C anyway, you should add
both B and C to A/dependencies.
If s6-rc-compile detects a cycle in dependencies across services, it will
complain and exit 1.
For oneshots
- Two mandatory regular files named up and down, which
must each contain a single Unix command line. The files will be interpreted by the
execlineb
lexer at compile time and the results will be stored into the
compiled database in an internal form. up will be run when
the service is started, and down will be executed when the service
is stopped.
up and down are interpreted by
execlineb, but
that does not mean they have to be entirely written in the
execline language. The
execlineb
lexer is only used because it can compile a Unix command line from a text file
and store the compiled result, whereas a shell would have to be invoked
everytime the script is run. There are many ways to write up and
down scripts:
- They can be written as standard execline scripts, without the
initial shebang line or positional parameter management.
- They can be written in any scripting language by invoking the interpreter
directly: for instance
/bin/sh -c "script", where script is
a shell script.
- They can also just call a script that will actually be stored somewhere else:
for instance, up can contain /etc/init.d/service start
while down contains /etc/init.d/service stop, and
/etc/init.d/service is a System V-style init script.
Don't think you have to learn all the intricacies of the execline language
just because the up and down scripts get lexed by it.
You don't.
For longruns
The s6-rc service definition directory for a longrun service is similar to
a s6 service
directory, but there are a few differences:
- s6-rc-compile crafts the servicedir itself, based on what it
finds in the service definition directory. It does not copy everything
directly from the definition directory to the servicedir; only two
subdirectories will be copied as is, data and env.
So if you want to store service configuration data, to be used
by the run script, in the service directory, make sure it is in a
data/ or env/ subdirectory.
- Definition directories cannot have a log subdirectory -
or if they do, it will be ignored. From s6-rc-compile's point of view,
logged s6 services must actually be defined as two separate
s6-rc services, one for the producer and one for the logger; there is
specific syntax to link those two services.
The following files must or may appear in a longrun definition directory:
- A mandatory regular file named run, as well as optional files
named finish, notification-fd and nosetsid. These
files will be copied, or recreated, in the generated
service directory.
- Optional directories named data and env. These will
be copied verbatim into the generated service directory.
- An optional file named logger. If this file exists, then
service will be flagged as a producer, and the file must
contain the name of another longrun service servicelog, which will
be declared as a logger for service. service must also
be declared as a producer in servicelog's definition directory.
- An optional file named producer. If this file exists, then
service will be flagged as a logger, and the file must
contain the name of another longrun service serviceprod, which will
be declared as a producer for service. service must also
be declared as a logger in serviceprod's definition directory.
Be aware that service directories will be relocated, and copied at boot time,
so if your run or finish scripts refer to files in the service directory
(probably in the data or env subdirectories!), they should
use relative paths, not absolute ones.
Note that you cannot create a down file in a generated service
directory. Even if such a file exists in the definition directory, it will
be ignored. This is intentional:
s6-rc internally uses down files to mark longrun
services that are down.
The logger and producer files are support for logged services:
A service defined as a logger for producer p will have its s6 service
directory set to p/log. Logged service definitions must be consistent:
- It is impossible to have a both a logger and a producer file
in the same definition directory: loggers cannot have loggers, producers cannot have
producers.
- The logger must be declared in the logger file in its producer's
definition directory, and the producer must be declared in the producer file
in its logger's definition directory. If it sees an inconsistency, s6-rc-compile
will complain and exit 1.
- The producer will automatically have a dependency to its logger (because,
as much as systemd would have you believe the opposite, it is
not safe to start a service while its logger is not up). This means that a
s6-rc change command to start the service will
automatically start the logger beforehand if it's not already up.
A complete example
The examples/source subdirectory of the s6-rc package contains a set
of service definition directories, which is actually a working, valid set for a
Linux system running
busybox and the
skarnet.org packages; of course, only
the service definition set has been kept, and private information has been
removed, so it won't work out-of-the-box without the proper specific files,
notably configuration in /etc/ - but nevertheless, you can browse the
source and understand what it does, and adapt it to your own needs. It will
compile as is with
s6-rc-compile, and you can examine the
resulting compiled database with
s6-rc-db.