Replacing init with runit on Debian GNU/Linux
This is based mostly on the notes on replacing init with runit written by Gerrit Pape, the main runit developer.
Install the needed Debian runit packages:
# aptitude install runit runit-services runit-run
init
The runit-run package installs the init replacement program, runit-int:
proton:~ 0# ls -al /sbin/init* lrwxrwxrwx 1 root root 10 2007-12-21 18:40 /sbin/init -> runit-init -rwxr-xr-x 1 root root 32652 2007-01-30 17:27 /sbin/init.sysv proton:~ 0#
runit-init's operation is very simple:
- if it is started by root as process number one, then it just replaces itself with the runit program.
- if not called by the kernel, then it is called as "init 0" to halt the system, or "init 6" to restart the system.
runit
The runit program executes three stages in succession:
- /etc/runit/1 - executes one-time system tasks, roughly equivalent to /etc/rcS.d (in fact it actually calls /etc/init.d/rcS, which in fact does this)
- /etc/runit/2 - changes the runlevel to "default" and starts runsvdir on /var/service (which is linked to the current runlevel directory). returns when the system is to be shutdown
- /etc/runit/3 - shuts down the system
runit runlevels
runit has runlevels that are defined in /etc/runit/runsvdir:
proton:~ 0# ls -al /etc/runit/runsvdir/ total 4 drwxr-xr-x 4 root root 1024 2007-12-21 18:40 . drwxr-xr-x 3 root root 1024 2007-12-21 18:41 .. lrwxrwxrwx 1 root root 7 2007-12-21 18:40 current -> default drwxr-xr-x 2 root root 1024 2007-12-21 18:41 default drwxr-xr-x 2 root root 1024 2007-12-21 18:40 single proton:~ 0#
Each of these directories contain the services that are to be managed at the various runlevels. /var/service is a link to /etc/runit/runsvdir/current:
proton:~ 0# ls -al /var/service lrwxrwxrwx 1 root root 27 2007-12-21 18:41 /var/service -> /etc/runit/runsvdir/current proton:~ 0#
services
These are the default services setup for runlevel "default" by default:
proton:~ 0# ls -al /etc/runit/runsvdir/default/ total 2 drwxr-xr-x 2 root root 1024 2007-12-21 18:41 . drwxr-xr-x 4 root root 1024 2007-12-21 18:40 .. lrwxrwxrwx 1 root root 15 2007-12-21 18:40 getty-1 -> /etc/sv/getty-1 lrwxrwxrwx 1 root root 15 2007-12-21 18:40 getty-2 -> /etc/sv/getty-2 lrwxrwxrwx 1 root root 15 2007-12-21 18:40 getty-3 -> /etc/sv/getty-3 lrwxrwxrwx 1 root root 15 2007-12-21 18:40 getty-4 -> /etc/sv/getty-4 lrwxrwxrwx 1 root root 15 2007-12-21 18:40 getty-5 -> /etc/sv/getty-5 lrwxrwxrwx 1 root root 20 2007-12-21 18:41 socklog-klog -> /etc/sv/socklog-klog lrwxrwxrwx 1 root root 20 2007-12-21 18:41 socklog-unix -> /etc/sv/socklog-unix proton:~ 0#
These service directories start the gettys, and the main system log daemons. Once you figure out what services you want managed in a particular runlevel, you have to make their service directories. The runit-services package comes with service directories for a bunch of standard services, all located in /etc/sv:
proton:~ 0# ls /etc/sv/ apache dhcp getty-2 nfs-kernel-server socklog-inet socklog-unix chrony exim getty-3 portmap socklog-klog squid cron gdm getty-4 postfix socklog-notify ssh dhclient getty-1 getty-5 README socklog-ucspi-tcp xdm proton:~ 0#
/etc/sv/README gives a good intro on how to get these services started on a sysv-init managed system. I'm going to start by getting the cron service working:
proton:~ 0# /etc/init.d/cron stop Stopping periodic command scheduler: crond. proton:~ 0# dpkg-divert --add /etc/init.d/cron Adding `local diversion of /etc/init.d/cron to /etc/init.d/cron.distrib' proton:~ 0# mv /etc/init.d/cron /etc/init.d/cron.distrib proton:~ 0# ln -s /usr/bin/sv /etc/init.d/cron proton:~ 0# ln -s /etc/sv/cron /etc/runit/runsvdir/default/ proton:~ 0# sv status cron run: cron: (pid 2033) 2s proton:~ 0#
That was simple. Now ssh:
proton:~ 0# /etc/init.d/ssh stop Stopping OpenBSD Secure Shell server: sshd. proton:~ 0# dpkg-divert --add /etc/init.d/ssh Adding `local diversion of /etc/init.d/ssh to /etc/init.d/ssh.distrib' proton:~ 0# mv /etc/init.d/ssh /etc/init.d/ssh.distrib proton:~ 0# ln -s /usr/bin/sv /etc/init.d/ssh proton:~ 0# ln -s /etc/sv/ssh/ log/ run supervise proton:~ 0# ln -s /etc/sv/ssh /etc/runit/runsvdir/default/ proton:~ 0# sv status ssh run: ssh: (pid 2059) 2s; run: log: (pid 2057) 2s proton:~ 0#
An important one to me, which is not included with runit-services, is a serial console. This is the simple service directory I wrote to put a getty on ttyS0:
proton:~ 0# ls -al /etc/sv/getty-S0/ total 4 drwxr-xr-x 2 root root 1024 2007-12-21 19:32 . drwxr-xr-x 26 root root 1024 2007-12-21 19:26 .. -rwxr-xr-x 1 root root 32 2007-12-21 19:27 finish -rwxr-xr-x 1 root root 43 2007-12-21 19:30 run lrwxrwxrwx 1 root root 20 2007-12-21 19:32 supervise -> /var/run/sv.getty-S0 proton:~ 0# cat /etc/sv/getty-S0/run #!/bin/sh exec getty -L ttyS0 115200 vt102 proton:~ 0# cat /etc/sv/getty-S0/finish #!/bin/sh exec utmpset -w ttyS0 proton:~ 0#
Once the service directory was ready, I commented out the serial getty line ("TO:...") in /etc/inittab, told the current running init to reload the inittab (ie. stop the getty on ttyS0), and then linked in the new service:
proton:~ 0# /sbin/init.sysv q proton:~ 0# ln -s /etc/sv/getty-S0 /etc/runit/runsvdir/default/ proton:~ 0# ln -s /etc/sv/getty-S0 /etc/runit/runsvdir/single/ proton:~ 0#
Notice I want it started in both "single" and "default" runlevels. The serial console started up no problem.
You can have runit supervise dhclient as well. In order to do this, you need to make sure ifupdown doesn't try to control it. If you want to have eth0 use dhcp, in /etc/network/interfaces take out any "auto eth0" lines, and change the "iface" line to read:
iface eth0 inet manual
This is just a place holder. Now all you need to do is kill any dhclient process running and link in the dhclient service dir:
proton:~ 0# ln -s /etc/sv/dhclient /etc/runit/runsvdir/default/ proton:~ 0#
reboot!
Finally, reboot your system:
/sbin/init.sysv 6
Success!
proton:~ 0# pstree runit─┬─events/0 ├─khelper ├─ksoftirqd/0 ├─kthread─┬─aio/0 │ ├─kblockd/0 │ ├─khubd │ ├─4*[kjournald] │ ├─kmirrord │ ├─kseriod │ ├─kswapd0 │ └─2*[pdflush] ├─runsvdir─┬─2*[runsv─┬─socklog] │ │ └─svlogd] │ ├─runsv─┬─dhclient │ │ └─svlogd │ ├─runsv───svlogd │ ├─runsv─┬─sshd─┬─sshd───bash │ │ │ └─sshd───bash───pstree │ │ └─svlogd │ ├─runsv───getty │ └─runsv───cron └─udevd proton:~ 0#
todo
I'm curious about udevd. It's started in /etc/rcS.d, and therefore by /etc/runit/1, but it's not being managed by a runsvdir. I'm not sure what the best way to do that is.