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 ] [ -u uids ] [ -g gids ] [ -h fdhuser ] 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.
Exit codes
- 0: success
- 1: error in a source directory
- 100: wrong usage
- 111: system call failed
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.
- -u uids : list users allowed to
use this database with s6-rc to start and
stop services. uids must be a comma-separated list of
numerical UIDs.
- -g gids : list groups allowed to
use this database with s6-rc to start and
stop services. gids must be a comma-separated list of
numerical GIDs.
- -h fdhuser : arrange for the
s6-fdholder-daemon
program, which maintains the pipes for the longrun pipelines, to run
as user fdhuser. By default, it runs as the user owning
the supervision tree, i.e. most likely root.
If the -u or -g option is used, then 0
must be explicitly listed in uids in order to allow root
to operate the database. If neither option is used, then root
(and only root) is implicitly allowed.
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. Names are also forbidden to use the reserved
s6rc- and s6-rc- prefixes.
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.
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, and the definition of A
will still be correct.
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 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 is mandatory, but down is optional;
if no down file is provided in the source definition directory,
then s6-rc will consider that the down transition for this service
does nothing and always succeeds.
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 verbatim, 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 defined as a producer and one defined as a consumer,
making a pipeline of just two services; see below for more information
about pipelines.
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:
they are meant to be used by the
s6-supervise
process that will manage the longrun service.
- Optional directories named data and env. These will
be copied verbatim into the generated service directory.
- An optional file named producer-for. If this file exists, then
it must contain the name of another longrun service servicelog;
service is then declared as a producer for servicelog.
servicelog must also, in its own definition directory,
be declared as a consumer for service.
- An optional file named consumer-for. If this file exists, then
it must contain the name of another longrun service serviceprod;
service is then declared as a consumer for serviceprod.
serviceprod must also, in its own definition directory,
be declared as a producer for service.
- An optional file named pipeline-name. If this file exists
along with a producer-for file, and there is no
consumer-for file, then a bundle will automatically be
created, named with the content of the pipeline-name file, and
containing all the services in the pipeline that starts at service.
See below for more about pipelining. The pipeline-name file
is ignored if service is not a first producer.
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 producer-for, consumer-for and pipeline-name
files are used to set up automatic longrun pipelining.
Longrun pipelining
Users of supervision suites know about logged services: a service acts
as a producer, and is coupled with another service, its logger; the
supervision system automatically maintains an open pipe between the
producer's stdout and the logger's stdin.
s6-rc comes with an extension of this mechanism. Rather than only
allowing two longrun services to be pipelined, it can set up an
indefinite number of longrun services this way.
- The first producer declares its direct consumer in a producer-for file.
- Intermediate services declare both their direct producer in their
consumer-for file, and their direct consumer in their
producer-for file.
- The last consumer only declares its direct producer in a consumer-for file.
- The first producer may declare a name for the whole pipeline, in
its pipeline-name file. If it does so, then a bundle is automatically
created with
the given name, and it contains all the services in the pipeline (plus the
automatically generated supporting services that open and store the
pipes).
s6-rc-compile will detect pipelines, and set up the service directories
so that every producer's stdout is connected to its consumer's stdin, and
that the pipes are not broken whenever one element in the chain dies.
s6-rc-compile checks for pipeline consistency. It must see a
producer-for file in the producer's definition that is consistent
with the consumer-for file in the consumer's definition. It will
detect and reject cycles as well as collisions.
The pipe linking a producer and a consumer is created by an automatically
generated oneshot service that both the producer and consumer depend on,
and stored in a
s6-fdholder-daemon
instance created by an automatically generated longrun service.
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.