anopa: init system/service manager built around s6 supervision suite
Latest version: 0.5.0
The latest version of anopa can be downloaded here. For Arch Linux users, the AUR has a PKGBUILD.
You can find the source code on this GitHub, where you can also report bugs, send suggestions or other (constructive) feedback.
What is anopa ?
anopa is an collection of tools and scripts aimed to provide an init system and service manager for Linux systems, based around the s6 supervision suite.
It provides some execline scripts that can be used as init for different stage of the boot process, leaving stage 2 to be handled by s6-svscan, as well as tools that can be used to create a runtime repository of servicedirs, start/stop them and other related functions.
What does init (PID 1) do ?
Paraphrasing s6's author, Laurent Bercot, there are really three stages for the init process of a Unix system :
Stage 1: Booting up phase
Stage 2: Supervision phase
Stage 3: Shutting down phase
Stage 1 starts when the kernel launches the first userland process, traditionally called init. The goal is to prepare the system: mounting file systems, setting system clock, configuring the network, and other similar tasks are to be performed, to be ready to start long-running processes, that will be expected to stay running for as long as the system is up.
Stage 2 starts with the launch of all long-running processes (getty-s, ssh server, etc) and continues - as you're using the system - with supervising said processes, making sure they stay up, and restarting them should they not. init's job here also includes reaping any orphaned zombies.
Stage 3 starts when a shutdown (or reboot) is initiated. init then needs to "undo" what was done prior: stop all long-running processes, clean up, unmount file systems, etc
s6-svscan is perfectly suited to be in charge of stage 2, supervising
long-running processes. B
Because the init process launched by the kernel, also known as PID 1, cannot die and has to be the one process to exist from start to finish, lots of init system include all functionalities into a single binary. Instead, since PID 1 is allowed to execute into another binary, this is what anopa makes uses of.
Notion of services
s6 works with services, defined under what's called a service directory, or servicedir. Since it is build around it, anopa uses the same principle; However, a service from anopa's point-of-view can either be one-shot, i.e. a process to start on boot (during stage 1) and that's it (possibly with a counter-part one to be run on shutdown, during stage 3), or a long-run, i.e. a process to be started during phase 2, and to stay up until stage 3 is initiated.
Therefore, you can have two kinds of services, and servicedir: one-shot, and long-run. A servicedir is simply a directory, in which the definition of the service is held.
Long-run services requires a file run
to be present, which will be the
long-running process launched & supervised (or, more likely, is a small script
executing into said process). Therefore, the rule is that if a servicedir
contains a file run
it is of a long-run service, else of a one-shot one.
Long-run services
Long-run services will be launched & supervised by s6-svscan, therefore you should refer to its official documentation when it comes to that, but such servicedirs contain:
- An executable file named
run
It can be any executable file (binary or script), but usually will be a small script containing the command setting up the services, before executing into it.
- An optional executable file named
finish
Like run
, it can be any executable file. If present, it is executed every time
the run
script dies. Generally, its main purpose is to clean up non-volatile
data such as the file system after the supervised process has been killed.
Note that it must do its work and exit in less than 5 seconds, else it'll be killed.
- An optional, empty, regular file named
nosetsid
If such a file exists, s6-supervise will not make the service a process
group and session leader; the service will be run in the same process group as
s6-supervise. If no nosetsid
file exists, the service has its own process
group and is started as a session leader.
- An optional service directory named
log
If it exists then two services will be monitored: the actual service (./run
)
as well as its logger (./log/run
).
A pipe is open and maintained between the two, i.e. everything that ./run
writes to its stdout will appear on ./log/run
's stdin. The service is said to
be logged; the log
service is called the service's logger.
See aa-enable(1) for how you can use a file instead, to automatically use the same logger for all services.
Note that by default, aa-status(1) only list oneshots & longruns, excluding
such loggers. Refer to its option --filter
to include/list them.
- An optional, empty, regular file named
gets-ready
This is anopa-specific, and if present indicates to aa-start(1) that the service supports readiness, so it will wait for event 'U' (instead of 'u') when starting it.
- An optional regular file named
timeout
If such a file exists, it should contain the number of seconds before the service is considered to be in time out; i.e. aa-start(1)/aa-stop(1) will stop waiting for them.
For completeness, the following "internals" are also supported.
- A directory named
supervise
It is automatically created by s6-supervise if it does not exist. This is
where s6-supervise
stores its information. The directory must be writable.
It is automatically created by aa-enable(1) with permissions 0711, to allow
reading the status file as a user.
- A fifodir named
event
It is automatically created by s6-supervise if it does not exist. This is used to send notifications when the service goes up/down.
- An optional, empty, regular file named
down
If such a file exists, the default state of the service is considered down, not
up, and it isn't automatically started by s6-supervise
.
It is automatically created by aa-enable(1), except for the service
specified with --skip-down
.
One-shot services
One-shot services are simply binaries that will be executed by aa-start(1) or aa-stop(1). Such servicedirs contain:
- An optional executable file named
start
It can be any executable file (binary or script), that will be executed by aa-start when starting the service. It if exits with 0 the service will be considered started, else failed. If no such file exists, the service will be considered started instantly.
- An optional executable file named
stop
It can be any executable file (binary or script), that will be executed by aa-stop when stopping the service. It if exits with 0 the service will be considered stopped, else stopped-failed. If no such file exists, the service will be considered stopped instantly.
- An optional, empty, regular file named
essential
If present and the service fails to be started, when it exits aa-start will
return 1. It will only return 0 if no essential services failed to be started.
This can be used by init
, e.g. to launch an emergency shell if aa-start
end successfully (return non-zero).
You would usually use such a file for the service mounting the root filesystem in initramfs, or launching getty.
- An optional regular file
timeout
If such a file exists, it should contain the number of seconds before the service is considered to be in time out; i.e. aa-start(1)/aa-stop(1) will stop waiting for them, killing the process. See aa-start(1) for more.
Note that a one-shot service can have only a start
script, only a stop
script, both, or none. In the later case, it can be used simply to order things.
For example, you could use a service sysinit
, making sure all very early
services (e.g. mounting file systems or setting up console) are started before
it, and starting everything else after it.
(Similarly, it would get everything stopped before those services are stopped, obviously.)
Service dependencies
Common to long-run and one-shot services, therefore supported in both types of servicedirs, are the notion of dependencies and ordering introduced by anopa.
The idea is that on boot, all long-run servicedirs will actually contain an
empty file down
so that they aren't automatically started by s6-svscan.
Instead, aa-start(1) will be used to start everything (during stage 2), both
one-shot and long-run services; It will also be used by aa-stop(1) to order
stopping.
(If you create your runtime service repository using aa-enable(1) this is done automatically.)
Four additional directories can be found inside servicedirs, used by aa-start(1) and aa-stop(1) to determine when to start/stop what.
- An optional directory named
needs
This directory can contain empty regular files, whose name is the name of a service to be marked as dependency of the current service. Such services will be auto-started by aa-start(1) and the current service will automatically be marked to be started after it.
Additionally, should the dependency service fail to start, the current service will not be started, but placed into a failed state (for dependency error).
aa-stop(1) will process those as if they were in directory after
.
- An optional directory named
wants
This directory can contain empty regular files, whose name is the name of a service to be auto-started by aa-start(1). This is not a dependency, and there's no ordering associated either.
This is ignored by aa-stop(1).
- An optional directory named
after
This directory can contain empty regular files. The current service will only be started after those services have been started by aa-start(1); And the other way around for aa-stop(1), i.e. those services will be stopped after the current service has been (or, the current service before them).
It is important to note that this is only an ordering directive, i.e. if the service isn't actually being started, then the directive is simply ignored. And if it is, but fails to start, the current service will still be started regardless.
- An optional directory named
before
This directory can contain empty regular files. If those services are being started, then they'll only be started after the current service by aa-start(1); And the other way around for aa-stop(1), i.e. the current service will be stopped after they have been.
It is important to note that this is only an ordering directive, i.e. if the service isn't being started then it is simply ignored. And if it is, but the current service fails to start, it will still be started regardless.
It should also be noted that a long-run service will be considered started once the event 'u' is received; Should the service actually fail right after will have no consequences on the rest of the aa-start(1) process.
Dependencies and Loggers
Loggers (*/log
services) are special in that they're not in the
repodir/scandir directly, and also not listed by aa-status(1) by default
(See its --filter
for more).
However, they're not ignored by anopa, and every longrun service with a logger
will automatically have a dependency ("needs") on its logger. This means that
e.g. to start foobar
, foobar/log
will first be started, as expected.
They can also contain user-defined dependencies, exactly as with any other services. It is not, however, possible to add a dependency onto such a logger (due to the slash in its name).
Service repository (and scandir)
s6-svscan will supervise all services found in its scandir. Obviously, because it has no notion of one-shot services, only servicedir for long-run services should be found there.
The way this works in anopa is to use aa-enable(1) to create the service repository, as well as the scandir. This is done by using source directories, and optionally merging in configuration directories. The idea is that you will not create your service repository manually, but instead using aa-enable(1) from pre-established definitions.
The service repository will contain all (enabled) servicedirs, both one-shots
and long-runs. For long-run servicedirs, symlinks will be put into directory
.scandir
which will be used by s6-svscan as its scandir.
A typical organization would work like the following :
/usr/lib/services
Source directory containing available servicedirs. This is where servicedirs from packages would be installed. Used by aa-enable(1).
/etc/anopa/services
Source directory containing available servicedirs. This is where the administrator can define its own servicedirs, either because they're not provided by packages, or to be used instead. Used by aa-enable(1).
/etc/anopa/enabled
List directory containing either empty regular files, whose name is the name of a service to enable on boot, or directories, whose name is the name of a service to enable on boot.
In the later case, the content of the folder will also be merged/copied over into the servicedir.
Used by aa-enable(1) from aa-stage1(1).
/etc/anopa/listdirs/onboot
List directory containing empty regular files, whose name is the name of a service to start on boot. Used by aa-start(1) from aa-stage2(1).
/run/services
Runtime repository, the service repository for the current system, containing a
directory .scandir
, with symlinks for all long-run services, used by
s6-svscan as its scandir. Used by aa-enable(1), aa-start(1) and
aa-stop(1).
On boot, aa-enable(1) is used to create the runtime repository. To do so, it reads the content of the listdir, and for each service named there copies the corresponding servicedir from a source directory into the runtime repository. If listdir contained a directory, its content is then merged/copied over into the newlu created servicedir, allowing easy custom service-specific configuration.
Source directories are looked up in order, thus allowing the administrator to provide not only its own servicedirs, but its own version of a given servicedir.
Refer to aa-enable(1) for more on how it works and how the copy operation takes place.
Then, aa-start(1) is used to start all services in order, getting the system up & ready. Refer to aa-start(1) for more on how it works; Refer to aa-stage1(1) and aa-stage2(1) for more on this init process on boot works.
You can also see aa-stage0(1) and aa-stage4(1) for how to use anopa in your initramfs.
Note that this is of course only a possible solution to set up your system, you are of course free to organize things differently, using only the tool(s) you need from anopa however you wish.
Real-life example, filled with servicedirs and all
If you're still unclear what an anopa-powered system would look like, or how to set one up, don't give up just yet: Here's a something that might be of assistance: an example of how to make an anopa-powered bootable ISO with explanations, servicedirs and configuration
Free Software
anopa - Copyright (C) 2015-2017 Olivier Brunel <jjk@jjacky.com>
anopa is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
anopa is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.