Tutorials
goetia
Software
0x53.net

User supervision trees

Preparation

Create the necessary directory structure as shown at the configuration directory page.

Populate the ${USERTREE}/run-image/service/ subdirectory with at least the following service directories:

Populate the ${USERTREE}/src/*/ subdirectories with source directories.

Starting user supervision trees

In practice, it makes sense to start a user supervision tree in one of the two following ways:

As a system service

In this case, the s6-svscan process of the user supervision tree is a service of the system supervision tree that dropped privileges.

For this create a source directory at src/usertree/@USER@-@USERTREE@-init in the system configuration directory. Whats is special here, is the empty file at src/usertree/@USER@-@USERTREE@-init/no-ml prohibiting automatic logger creation, since the user supervision tree brings its own logger.

src/usertree/@USER@-@USERTREE@-init
├── dependencies.d
│   ├── mount          # empty/arbitrary
│   └── @USER@-tmpfs   # empty/arbitrary
├── no-ml              # empty/arbitrary
├── run                # see below
└── type               # longrun
				

run script:

#!/bin/execlineb -P

fdmove -c 2 1

s6-setuidgid @USER@

export USER @USER@
export USERTREE @USERTREE@
export BUNDLE @BUNDLE@ # optional

user-init
				

Additionally a user writable tmpfs to hold the user runtime directory needs to be prepared. This is done through a @USER@-tmpfs service:

src/usertree/@USER@-tmpfs
├── up                 # see below
└── type               # oneshot
				

up script:

#!/bin/execlineb -P

fdmove -c 2 1

export USER @USER@

user-tmpfs
				

Replace @USER@ with the user account the supervision tree is for and @USERTREE@ with a usertree this user has actually prepared a config for.

These system services can now be started either on boot, or once the user logs in. Following are a couple of ideas to get a feel for what user supervision trees can be used for.

Starting on boot

On a server:

  • Users can have their unprivileged jobs (e.g. calculations) run and be restarted if the server reboots.
  • Can be used to group up services, e.g. running multiple minecraft servers as the same user minecraft and having a dedicated, unprivileged admin for those.

On a desktop machine:

  • If the machine is only used by a single user, a user interface can be directly started by the user supervision tree on boot. This interface may then lock itself to provide password protection. This approach bypasses setting up a complex login management.
  • If the machine is used by multiple users with different user accounts, login management can be used. In this case, a specific bundle would be started upon login and stopped upon logout.

Starting at some other point

The system service that initiates the user supervision tree could of course be started anytime later after boot. A practical use of this would be:

  • Having a login management daemon that tells the system supervision tree to start upon login (and stop upon logout) the system service that starts the user supervision tree. Thus the lifetime of the entire user supervision tree (not just a bundle as above!) is bound to the lifetime of the login. While this is similar to the approach below, it has the advantage that the user supervision tree, should it crash, will automatically be restarted by the s6-supervise of the system service that started it.

Initilization without a system service

Technically, the user-init program can be initiated from anywhere. The script to start user supervision trees in this case is the same:

#!/bin/execlineb -P

export USER @USER@
export USERTREE@USERTREE@
export BUNDLE @BUNDLE@ # optional

user-init
				

Some useful points to initiate a user supervision tree from are:

Nested user supervision trees

As explained on the supervision tree page, any user can have any amount of supervison trees. There are situations where it can be useful to nest user supervison trees, that is, start one user supervision tree from another user supervison tree.

An example would be a user interface based on X.Org or Wayland. Here, components like wallpapers and notifications are often provided as daemons separate from the compositor. They do, however, need some information from the compositor, which is ususally passed by environment variables, e.g. DISPLAY and WAYLAND_DISPLAY. These environment variables need to somehow find there way to the daemon. One way to achieve this is starting another user supervision tree from the compositor. This is illustrated below:

As an example, say the user logs in which causes a compositor to be started as user service to a user supervision tree USERTREE=login. The corresponding wallpaper daemon can not really be a service of the same user supervision tree, since the compositor service has no elegant way to update the environment of the wallpaper daemon in this way. Instead one can start a an additional user supervision tree USERTREE=compositor, from inside the compositor. This user supervision tree naturally inherits the environment variables from the compositor and can start wallpaper daemons, notification daemons, statusbar daemons etc.