s6: the s6-usertree-maker program
Keywords: s6 command s6-usertree-maker user supervision tree s6-svscan

s6
Software
skarnet.org

# The s6-usertree-maker program

s6-usertree-maker creates a service directory implementing a service that runs an s6-svscan process owned by a given user, on a scan directory belonging to that user. It is meant to help admins deploy systems where each user has their own supervision subtree, rooted in the main supervision tree owned by root.

Alternatively, s6-usertree-maker can create source definition directories for the s6-rc service manager.

## Interface

         s6-usertree-maker \
           [ -d userscandir ] \
           [ -p path ] \
           [ -E envdir [ -e var -e var ... ] ] \
           [ -r service/logger[/pipeline] ] \
           [ -l loguser ] \
           [ -t stamptype ] \
           [ -n nfiles ] \
           [ -s filesize ] \
           [ -S maxsize ] \
           [ -P prefix ] \
           user logdir dir

s6-usertree-maker creates a service directory in dir, that launches a supervision tree as user user on scan directory userscandir, with a catch-all logger logging the tree's output via s6-log to the logdir directory.

## Exit codes

- 0: success
- 100: wrong usage
- 111: system call failed

## Options

- `-d` userscandir : the supervision tree will be run on the userscandir directory. userscandir is subject to variable substitution (see below). Default is `${HOME}/service`.
- `-p` path : the supervision tree will be run with a PATH environment variable set to path. path is subject to variable substitution. Default is `/usr/bin:/bin`, or whatever has been given to the `--with-default-path` option to skalibs' configure script.
- `-E` envdir : the supervision tree will be run with the environment variables defined in the directory envdir, which will be read via s6-envdir without options. By default, no envdir is defined and the supervision tree will only be run with the basic environment variables listed below.
- `-e` var : Perform variable substitution on var. This option is repeatable, and only makes sense when the `-E` option is also given. For every var listed via a `-e` option, the contents of var will be subjected to variable substitution before the supervision tree is run. This is only useful if var is defined in envdir, as a template, that is then instanced for user when the service is run. By default, only the PATH environment variable, customizable via `-p`, is subjected to variable substitution.
- `-r` service/logger/pipeline : create s6-rc source definition directories. When this option is given, dir is not created as a service directory, but as a directory containing two services: dir/service and dir/logger, and dir is suitable as a source argument to s6-rc-compile. The `/`pipeline part can be omitted, but if it is present, pipeline is used as a name for a bundle containing both service and logger. When this option is not given, dir is a regular service directory for direct inclusion (or linking) in the parent scan directory (and the catch-all logger for the user subtree is declared in dir`/log`).
- `-l` loguser : run the catch-all logger of the user subdirectory as user loguser. Default is `root`.
- `-t` stamptype : how logs are timestamped by the catch-all logger. 0 means no timestamp, 1 means external TAI64N format, 2 means ISO 8601 format, and 3 means both. Default is `1`.
- `-n` nfiles : maximum number of archive files in logdir. Default is `10`.
- `-s` filesize : maximum size of the `current` file (and archive files) in logdir. Default is `1000000`.
- `-S` maxsize : maximum total size of the archives in the logdir. Default is `0`, meaning no limits apart from those enforced by the `-n` and `-s` options.
- `-P` prefix : when logging to logdir, prefix logged lines with the prefix string.

## Operation of the service

When the service is started, its run script will execute the following operations:

- Clear all its environment variables, except PATH. This prevents any data leak from the parent supervision tree into the user subtree.
- Fill its environment with data related to user:
  - USER is set to user
  - HOME is set to user's home directory
  - UID is set to user's uid
  - GID is set to user's primary gid
  - GIDLIST is set to user's supplementary groups list
- If the service has been created with the `-E` option to s6-usertree-maker:
  - Add all the variables defined in envdir to its environment
  - For every variable var given via a `-e` option, subject var to substitution with the USER, HOME, UID, GID and GIDLIST variables (see below).
- Set the PATH environment variable to path, subjected to variable substitution.
- Execute into s6-svscan, running in userscandir (which is first subjected to variable substitution).

The service is logged: its stderr and stdout are piped to an s6-log process running as loguser and writing to the logdir directory. This logger is the catch-all logger for the supervision tree owned by user; it is recommended to make loguser distinct from user, and to have logdir in a place that is not under the control of user. If user wants to keep control of their logs, they can declare a logger for each of their services.

## Variable substitution

When the service starts, the USER, HOME, UID, GID and GIDLIST environment variables are deduced from user's identity. The value of those variables may be used in a few configuration knobs:

- The value of userscandir: it is likely that the scan directory belonging to user resides under user's home directory. Or under `/run/user/${UID}`, or some similar scheme.
- The PATH environment variable, declared in path: it is often useful to prepend the default system PATH with a user-specific directory that hosts that user's binaries. For instance, you may want the PATH to be set as something like `${HOME}/bin:/usr/bin:/bin`.
- Any variable declared in envdir and given as an argument to a `-e` option to s6-usertree-maker. If envdir is a template valid for all users, it may contain variables that depends on user-specific data: for instance, the XDG_CONFIG_HOME variable may be set to `${HOME}/.config`.

When the strings `${USER}`, `${HOME}`, `${UID}`, `${GID}`, or `${GIDLIST}` appear in the value for userscandir, path, or any of the var variables, they are substituted with the corresponding value of the USER, HOME, UID, GID, or GIDLIST environment variable instead.

For instance, if no `-d` option is provided, the default value for userscandir is `${HOME}/service`. If the provided user is `ska` and ska's home directory is `/home/ska`, then s6-svscan will be run on `/home/ska/service`.

## Examples

         s6-usertree-maker -d '/run/user/${UID}/service' -p '${HOME}/bin:/usr/bin:/bin' -E /etc/user-env -e XDG_CONFIG_HOME -l catchlog ska /var/log/usertree/ska usertree-ska

creates a service directory in `usertree-ska` declaring a service that starts a supervision tree on `/run/user/1000/service` if ska has uid 1000, with `/home/ska/bin:/usr/bin/bin` as its PATH if ska's home directory is `/home/ska`, and with all the environment variables declared in `/etc/user-env`, among which the XDG_CONFIG_HOME variable is processed for variable substitution. The supervision tree has a catch-all logger running as user catchlog, and storing its data in the `/var/log/usertree/ska` directory.

Note that simple quotes are used here to prevent the shell from interpreting `${UID}` and `${HOME}`.

         s6-usertree-maker -d '/run/user/${UID}/service' -p '${HOME}/bin:/usr/bin:/bin' -E /etc/user-env -e XDG_CONFIG_HOME -l catchlog -r usertree-ska/usertree-ska-log/usertree-ska-pipeline ska /var/log/usertree/ska usertree

Same as above, except it does not create a service directory — instead, it creates a `usertree` directory containing two subdirectories: `usertree-ska`, the s6-rc source definition directory for the service, and `usertree-ska-log`, the source definition directory for its logger. It also creates an implicit `usertree-ska-pipeline` bundle containing both the service and the logger.

## Notes

- s6-usertree-maker makes use of the fact that execline scripts are much easier to generate programmatically and to harden than shell scripts, so it is only built if s6 is built with execline support - i.e. the `--disable-execline` switch has not been given to configure.
- For the admin who wants to automate user tree management, s6-usertree-maker is a building block meant to be used in scripts, not a complete turnkey solution. For instance, s6-usertree-maker does not create userscandir for a user: it assumes that that scandir is already in place. It does not create logdir either: logdir, or at least its parent directory, must already exist before the logger is run, else s6-log will fail repeatedly. Make sure that all the data and metadata referenced by the service's and the logger's run scripts are actually present and valid before starting the service.
- If s6-usertree-maker encounters failure (and exits 111), it does not clean up the directories it created. Make sure to always test s6-usertree-maker's return code and clean up after it if needed.