s6-linux-init: the s6-linux-init program
Keywords: s6 linux init administration root init boot pid1 pid 1 /sbin/init

s6-linux-init
Software
skarnet.org

# The `s6-linux-init` program

`s6-linux-init` is a program that is meant to run as pid 1, as a stage 1 init: it performs the necessary early system preparation and execs into s6-svscan.

## Interface

         s6-linux-init [ -c basedir ] [ -p initial_path ] [ -s env_store ] [ -m umask ] [ -d slashdev ] [ -D initdefault ] [ -n | -N ] [ -C ] [ -B ] [ args... ]

- If `s6-linux-init` isn't pid 1, it execs into s6-linux-init-telinit with the same arguments.
- Else, it performs some early preparation, spawns a process that will run the rc.init script, then execs into s6-svscan.

## Options

These options should exactly mirror the options with the same name that have been given to s6-linux-init-maker. If there is a discrepancy, the system might not boot.

- `-c` basedir : read all its initialization data from basedir. If the data has not indeed been copied to basedir, the system will not boot.
- `-p` initial_path : the initial value for the PATH environment variable.
- `-s` env_store : the place where to dump kernel environment variables.
- `-m` initial_umask : the initial file umask.
- `-d` slashdev : mount a devtmpfs on slashdev. By default, no such mount is performed - it is assumed that a devtmpfs is automounted on `/dev` at boot time by the kernel.
- `-D` initdefault : the initial runlevel to boot to, if it isn't overridden by the kernel command line. This is only given as a first argument to rc.init. Default is `default`.
- `-n` : instead of unmounting `/run` and mounting a tmpfs on it, just remount `/run`.
- `-N` : do not touch `/run` at all.
- `-C` : run in a container. This option modifies a few of the operations described below, to accommodate running in a container instead of on real hardware. For instance: it does not scan the command line for a specific runlevel, it does not trap ctrl-alt-del, and before anything else it waits for its descriptor 3, if present, to close. (Docker uses this fd 3 mechanism as synchronization between the Docker daemon and the container's `init`.)
- `-B` : do not run the catch-all logger. This option removes the catch-all-logger-related operations from the list below; `s6-linux-init` will not redirect output descriptors, and will use a different synchronization mechanism to ensure `rc.init` only runs when s6-svscan is ready.

## Early preparation

When booting a system, `s6-linux-init` performs the following operations:

- It prints a banner to `/dev/console`.
- It chdirs to `/`.
- It sets the umask to initial_umask.
- It becomes a session leader.
- It mounts a devtmpfs on slashdev, if requested.
- It uses `/dev/null` as its stdin (instead of `/dev/console`). `/dev/console` is still used, for now, as stdout and stderr.
- It unmounts `/run` (or the directory you have given to the `--tmpfsdir` configure option at package build time), just in case; then it creates a tmpfs on it. Alternatively, it remounts `/run`, or does not touch it at all.
- It copies the whole `basedir/run-image` hierarchy to `/run` (or your chosen tmpfsdir).
- It reads the initial environment from `basedir/env`.
- If required, it stores the kernel environment into env_store.
- It performs "the fifo trick", i.e. it redirects its stdout to the catch-all logger's fifo, without blocking, before the catch-all logger is even up (because it's a service that will be spawned a bit later, when s6-svscan is executed).
- It forks a child.
  - The child scans the kernel command line to find a suitable runlevel (`default`, `2`, `3`, `4`, or `5`). If it doesn't find any kernel command line argument that defines a runlevel, it uses initdefault.
  - The child becomes a session leader.
  - The child blocks until the catch-all logger runs.
- It also makes the catch-all logger's fifo its stderr.
- It traps the ctrl-alt-del keyboard combination.
- It execs into s6-svscan with `/run/service` as its scandir (or tmpfsdir/service).

- s6-svscan spawns the early services that are defined in `basedir/run-image/service`, and have been copied into `/run/service` (or tmpfsdir/service).
- One of those early services is `s6-svscan-log`, which is the catch-all logger. When this service is up, `s6-linux-init`'s child unblocks.
- The child execs into `basedir/scripts/rc.init`. The first argument to rc.init is the chosen runlevel. The kernel command line, as given by the kernel to `s6-linux-init` (i.e. without the `key=value` arguments, which were passed into `s6-linux-init`'s environment and were stored into env_store), makes for the rest of the arguments given to rc.init.

By the time rc.init runs, s6-svscan is running as pid 1 and has spawned its early services - at least the catch-all logger, and the other services, including the early getty if it has been defined, are started in parallel and will be ready instantly. rc.init can then perform stage 2 of the initialization process, i.e. the handoff to the service manager.

## Exit codes

`s6-linux-init` never exits. It spawns the rc.init script and execs into s6-svscan, which runs forever until the machine stops or reboots.

## Notes

- The `s6-linux-init` binary is not meant to be called directly, or be linked to `/sbin/init` directly, because it takes command-line options. Instead, after a s6-linux-init-maker invocation, the `bin/` subdirectory of the target will contain a script called `init`, which execs into `s6-linux-init` with the appropriate command-line options, and is suitable as a `/sbin/init` program. The `bin/` subdirectory should be copied by the administrator into `/sbin` for full interface compatibility with sysvinit.