s6: the s6-svscan program
Keywords: s6 command s6-svscan scandir supervision supervise svscan monitoring collection

s6
Software
www.skarnet.org

# The s6-svscan program

s6-svscan starts and monitors a collection of s6-supervise processes, each of these processes monitoring a single service. It is designed to be either the root or a branch of a supervision tree.

## Interface

         s6-svscan [ -S | -s ] [ -c max ] [ -t rescan ] [ scandir ]

- If given a scandir argument, s6-svscan switches to it. Else it uses its current directory as scan directory.
- It exits 100 if another s6-svscan process is already monitoring this scan directory.
- If the `./.s6-svscan` control directory does not exist, s6-svscan creates it. However, it is recommended to already have a `.s6-svscan` subdirectory in your scan directory, because s6-svscan may try and execute into the `.s6-svscan/crash` or `.s6-svscan/finish` files at some point - so those files should exist and be executable.
- From this point on, s6-svscan never dies. It tries its best to keep control of what's happening. In case of a major system call failure, which means that the kernel or hardware is broken in some fashion, it executes into the `.s6-svscan/crash` program. (But if that execution fails, s6-svscan exits 111.)
- s6-svscan performs an initial scan of its scan directory.
- s6-svscan then occasionally runs scans or reaps, see below.
- s6-svscan runs until it is told to stop via s6-svscanctl, or a signal. Then it executes into the `.s6-svscan/finish` program. The program is given an argument that depends on the s6-svscanctl options that were used.
- If that execution fails, s6-svscan falls back on a `.s6-svscan/crash` execution.

## Options

- `-S` : do not divert signals. This is the default for now; it may change in a future version of s6.
- `-s` : divert signals - see below.
- `-c max` : maintain services for up to max service directories. Default is 500. Lower limit is 2. There is no upper limit, but:
  - The higher max is, the more stack memory s6-svscan will use, approximately 50 bytes per service.
  - s6-svscan uses 2 file descriptors per logged service.
  It is the admin's responsibility to make sure that s6-svscan has enough available descriptors to function properly and does not exceed its stack limit. The default of 500 is safe and provides enough room for every reasonable system.
- `-t rescan` : perform a scan every rescan milliseconds. If rescan is 0, automatic scans are never performed after the first one and s6-svscan will only detect new services when told to via a s6-svscanctl -a command. The default rescan value is 5000, for compatibility with daemontools' svscan, which performs a scan (and a reap) every 5 seconds. It is strongly discouraged to set rescan to a positive value under 500.

## Signals

s6-svscan always reacts to the following signals:

- SIGCHLD : triggers the reaper.
- SIGALRM : triggers the scanner.
- SIGABRT : acts as if a `s6-svscanctl -b` command had been received.

By default, it also reacts to the following signals:

- SIGTERM : acts as if a `s6-svscanctl -t` command had been received.
- SIGHUP : acts as if a `s6-svscanctl -h` command had been received.
- SIGQUIT : acts as if a `s6-svscanctl -q` command had been received.
- SIGINT : acts as if a `s6-svscanctl -6` command had been received.

But if the `-s` option was given, then instead of those default actions, s6-svscan uses configurable handlers: it forks and executes a program every time it receives one of the following signals.

- SIGTERM : fork and execute `.s6-svscan/SIGTERM`
- SIGHUP : fork and execute `.s6-svscan/SIGHUP`
- SIGQUIT : fork and execute `.s6-svscan/SIGQUIT`
- SIGINT : fork and execute `.s6-svscan/SIGINT`
- SIGUSR1 : fork and execute `.s6-svscan/SIGUSR1`
- SIGUSR2 : fork and execute `.s6-svscan/SIGUSR2`

If an action cannot be taken (the relevant file doesn't exist, or isn't executable, or any kind of error happens), s6-svscan prints a warning message to its standard error but does nothing else with the signal.

The `-s` mechanism is useful, for instance, when s6-svscan is running as process 1 and needs to trap signals such as SIGINT (sent on some systems by a Ctrl-Alt-Del press) in order to perform some specific work instead of executing into `.s6-svscan/finish` on the spot.

s6-svscan will not exit its loop on its own when it receives a signal such as SIGINT and the `-s` option has been given. To make it exit its loop, invoke a s6-svscanctl command from the signal handling script. For instance, a `.s6-svscan/SIGINT` script could look like this:

      #!/command/execlineb -P
      foreground { shutdown-the-services }
      s6-svscanctl -i .

## The reaper

Upon receipt of a SIGCHLD, or a s6-svscanctl -z command, s6-svscan runs a reaper routine.

The reaper acknowledges (via some wait() function), without blocking, every terminated child of s6-svscan, even ones it does not know it has. This is especially important when s6-svscan is run as process 1.

If the dead child is a s6-supervise process watched by s6-svscan, and the last scan flagged that process as active, then it is restarted one second later.

## The scanner

Every rescan milliseconds, or upon receipt of a SIGALRM or a s6-svscanctl -a command, s6-svscan runs a scanner routine.

The scanner scans the current directory for subdirectories (or symbolic links to directories), which must be service directories. It skips names starting with dots. It will not create services for more than max subdirectories.

For every new subdirectory dir it finds, the scanner spawns a s6-supervise process on it. If dir`/log` exists, it spawns a s6-supervise process on both dir and dir`/log`, and maintains a never-closing pipe from the service's stdout to the logger's stdin. This is starting the service, with or without a corresponding logger. Every service the scanner finds is flagged as "active".

The scanner remembers the services it found. If a service has been started in an earlier scan, but the current scan can't find the corresponding directory, the service is then flagged as inactive. No command is sent to stop inactive s6-supervise processes (unless the administrator uses s6-svscanctl -n), but inactive s6-supervise processes will not be restarted if they die.

## Implementation notes

- s6-svscan is designed to run until the machine is shut down. It is also designed as a suitable candidate for process 1. So, it goes out of its way to stay alive, even in dire situations. When it encounters a fatal situation, something it really cannot handle, it executes into `.s6-svscan/crash` instead of dying; when it is told to exit, it executes into `.s6-svscan/finish`. Administrators should make sure to design appropriate `crash` and `finish` routines.
- s6-svscan is a fully asynchronous state machine. It will read and process commands at any time, even when the computer is in trouble.
- s6-svscan does not use malloc(). That means it will never leak memory. However, s6-svscan uses opendir(), and most opendir() implementations internally use heap memory - so unfortunately, it's impossible to guarantee that s6-svscan does not use heap memory at all.
- When run with the `-t0` option, s6-svscan never polls, it only wakes up on notifications, just like s6-supervise. The s6 supervision tree can be used in energy-critical environments.