off the stack

User services with runit

2012-10-16

Runit is a pretty lightweight process supervisor (tool-set). It can be used alongside your distro's own init scripts to build flexible service structures.

To get a short overview/introduction one could read "Init scripts considered harmful", read a blog post or take a look on at the Arch Linux wiki for some examples.

This post aims to describe the minimal steps to get a user service tree up and running. Furthermore it documents the solution for a small problem when using the runsvdir command this way.

Why

Because it can be quite convenient to have facilities to let users of a system define services on their own - and let them run as their own user - with convenient access to all process related information such as pids or log-files.

And while there is some information out there regarding user-level services using runit, I ran into an issue while trying to take down a whole user service tree. Therefore it seems useful for me to document the problem and the solution.

Installation

The installation of runit should be quite easy on most Linux distributions.

Using Debian (squeeze) it is a matter of running the following command as root:

apt-get install runit

This retrieves an archive of about 130kB in size. Extracted this take less than 400kB disk space. Nice.

The user services service

From here we just need to create a service which runs the runsvdir program as the desired user. This is done by creating a service directory and a corresponding run file (still being root). Suppose we have a user named rico:

mkdir -p /etc/sv/user/rico
cat > /etc/sv/user/rico/run << EOF
#!/bin/sh
exec 2>&1
exec chpst -urico runsvdir -P /home/rico/service 'log:....................'
EOF
chmod u+x /etc/sv/user/rico/run

And in case you are using NIS or other services to provide the system with the user in question you may want to use sudo within your run file:

#!/bin/sh
exec 2>&1
exec sudo -H -u rico runsvdir -P /home/rico/service 'log:....................'

To activate the service tree we then just have to add a symlink:

ln -s /etc/sv/user/rico /etc/service/user-rico

Now the user rico may add services by creating a ~/service directory and adding service directories/symlinks therein.

Take the following (useless) service for example (added as regular user rico):

mkdir -p /home/rico/service/catrandom
cat > /home/rico/service/catrandom/run <<EOF
#!/bin/sh
exec cat /dev/random > /dev/null
EOF
chmod +x /home/rico/service/catrandom/run

Adding this directory and the run file and making it executable should fire up the service. This can be verified using ps or htop for example.

Taking down this (useless) service can be done by simply using sv down ~/service/catrandom. Refer to the documentation for more examples ...

A small gotcha

There is still one thing left to do for our user service tree. We should create a finish script within the same directory where we put the run script for runsvdir. This script will contain logic which takes down all user services in case we decide to stop the user services service.

As root:

cat > /etc/sv/user/rico/finish <<EOF
#!/bin/sh
sv -w600 force-stop /home/rico/service/*
sv exit /home/rico/service/*
EOF
chmod u+x /etc/sv/user/rico/finish

After which we can safely issue sv down user-rico as root to stop all user services for rico.

This has been suggested by the author of runit himself on the mailing-list and because svwaitdown has been merged into sv we use sv there.