473 lines
14 KiB
Markdown
473 lines
14 KiB
Markdown
Title: Runit on Arch Linux
|
|
|
|
Last update: 14 August 2014
|
|
|
|
[The easy method, for the impatient](https://aur.archlinux.org/packages/runit-init/)
|
|
|
|
------------------------
|
|
|
|
I like how runit manages things,
|
|
especially the restarting of dead daemons.
|
|
I was growing tired of sysvinit when systemd started making inroads,
|
|
and when Arch moved to systemd,
|
|
I figured it was a good time to make the switch.
|
|
|
|
I know a lot of people feel very passionately about systemd;
|
|
I just like runit better.
|
|
If you want to rage about systemd,
|
|
there are many online venues available for you to do so.
|
|
|
|
What's the advantage?
|
|
---------------------
|
|
|
|
Runit lets—no, forces—you to write your own startup scripts.
|
|
You can write them in any language you want,
|
|
but Bourne shell is pretty convenient.
|
|
I think this is the biggest selling point for me.
|
|
I like doing things with specialized programs,
|
|
and I know Bourne shell pretty well,
|
|
so it's easy for me to figure out what runit is doing,
|
|
and extend it.
|
|
|
|
If a daemon dies, runit restarts it in 2 seconds.
|
|
I find that convenient,
|
|
but some prefer for things with problems to be restarted manually.
|
|
|
|
Runit wants daemons to run in the foreground.
|
|
Having written many daemons,
|
|
I like this philosophy a lot.
|
|
I never understood why the "fork twice" hack needed to be duplicated in every daemon ever;
|
|
Runit takes care of that for you.
|
|
|
|
Runit encourages things to log to stdout (or stderr),
|
|
instead of syslog or custom logging code.
|
|
Writing to stderr is also very convenient from the standpoint of the daemon's author.
|
|
It's a natural way to provide information to the user,
|
|
and all that's needed for "debugging mode" is to launch the daemon at the command line
|
|
instead of from runit.
|
|
In fact, the "log" package in Go (language) works without any modifications
|
|
in this way.
|
|
stdout from a runit service is sent to stdin on a log service,
|
|
which runit also keeps track of.
|
|
That log service can be anything you want:
|
|
`svlogd` does a pretty good job timestamping lines,
|
|
and it also rotates logs automatically without needing to stop and start the daemon.
|
|
|
|
People aren't using sysvinit anymore (alas), but
|
|
[this essay](http://busybox.net/~vda/init_vs_runsv.html)
|
|
by Denys Vlasenko is still a pretty good overview of other ways runit is neat.
|
|
|
|
Have I convinced you?
|
|
---------------------
|
|
|
|
Try out my
|
|
[runit-init AUR](https://aur.archlinux.org/packages/runit-init/)
|
|
which does everything described here,
|
|
and also contains updates for things like mdev (instead of udev)
|
|
patches to X to start without udev,
|
|
and cryptographic filesystem mounting.
|
|
|
|
---------------------------------
|
|
|
|
|
|
The Gory Details
|
|
================
|
|
|
|
Everything below here was written in February 2013.
|
|
Things have changed since then;
|
|
in particular, I have [an AUR](https://aur.archlinux.org/packages/runit-init/)
|
|
which uses busybox runit to boot your system.
|
|
|
|
I'm leaving the rest for people who want discussion about how to boot their system.
|
|
This will all still work, it's just that the AUR works better :)
|
|
|
|
|
|
Peculiarities of my setup
|
|
-------------------------
|
|
|
|
I like the runit that comes with busybox,
|
|
so I'm using that.
|
|
If you prefer Gerrit Pape's runit,
|
|
the procedure will be similar,
|
|
but there are subtle differences you will need to watch out for.
|
|
In particural,
|
|
Gerrit's `runsvdir` does not have the `-s` option:
|
|
that functionality is provided by `runit-init`.
|
|
|
|
I put my runit service directory in `/service`.
|
|
Everything else seems to be making top-level directories these days,
|
|
and, hell, it's my computer.
|
|
You can put yours wherever you want,
|
|
just change `/service` in my examples to your directory.
|
|
|
|
Ctrl-alt-del does an immediate reboot.
|
|
Once I'm more comfortable with this setup,
|
|
I may change that by writing to the approprate file in `/proc`,
|
|
but I actually like this behavior for now.
|
|
|
|
|
|
Warning
|
|
-------
|
|
|
|
If you screw this up,
|
|
you might not be able to boot your computer up anymore.
|
|
If you're using Arch,
|
|
presumably you're already comfortable administering your computer.
|
|
But messing with `init` can break things in exciting new ways.
|
|
|
|
Arch's initrd has a nice `break=postmount` kernel commandline option
|
|
to open a shell after mounting root,
|
|
which I used to recover things several times.
|
|
You might want to play with that and understand what the initrd is,
|
|
before you are put into a position where you *have* to use it and can't start a web browser.
|
|
|
|
|
|
WARNING
|
|
-------
|
|
|
|
This document is now pretty old.
|
|
It's unlikely it will work at all on a modern Arch installation.
|
|
[My AUR](https://aur.archlinux.org/packages/runit-init/)
|
|
is usually only a few days behind the latest change in Arch's packages.
|
|
I've left this here because it might help people trying similar things
|
|
with different distributions.
|
|
But if you're using Arch, I strongly recommend you start with the AUR.
|
|
|
|
|
|
Let's go
|
|
--------
|
|
|
|
The version of `busybox` packaged for Arch comes with many "applets" compiled in:
|
|
enough for us to set it all up.
|
|
Because it's statically linked,
|
|
we don't even need to worry about libc updates.
|
|
To prevent a bad `busybox` update from bringing down the entire works,
|
|
let's make a copy.
|
|
|
|
cp /bin/busybox /usr/local/sbin/busybox.static
|
|
ln -s busybox.static /usr/local/sbin/runsv
|
|
ln -s busybox.static /usr/local/sbin/runsvdir
|
|
ln -s busybox.static /usr/local/sbin/sv
|
|
|
|
|
|
We also need to create a new `/sbin/init` to replace `systemd` (or sysvinit) and launch `runsvdir`.
|
|
Arch actually has a pretty nice init setup,
|
|
almost as though they had this use case in mind when they were designing it.
|
|
The early userspace init sets up /, pivots root, and runs /sbin/init.
|
|
At that point, we can take over, run `/etc/rc.sysinit`,
|
|
and hand off to `runsvdir`.
|
|
Putting the system initialization stuff into a shell script
|
|
was a nice move on the part of the arch folks,
|
|
and makes this almost trivial.
|
|
|
|
The other thing `init` needs to handle is being called by programs like `reboot` and `poweroff`,
|
|
which want to signal `init` by changing runlevel.
|
|
So if our new `init` is not PID 1,
|
|
we'll emulate `telinit` from sysvinit,
|
|
by checking what runlevel is being requested and sending the appropriate
|
|
signal to PID 1.
|
|
|
|
Be sure to move the old `init` to soming like `init.sysv`,
|
|
then create a new `init` similar to this
|
|
(don't forget to `chmod +x`):
|
|
|
|
|
|
#! /bin/sh
|
|
|
|
PATH=/usr/bin; export PATH
|
|
|
|
if [ $$ -ne 1 ]; then
|
|
case $1 in
|
|
6)
|
|
exec kill -15 1
|
|
;;
|
|
0)
|
|
exec kill -12 1
|
|
;;
|
|
esac
|
|
|
|
echo "LOL: runit doesn't have run levels" 1>&2
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
echo 'Arch Linux'
|
|
echo 'http://www.archlinux.org/'
|
|
echo '-----------------------------'
|
|
echo
|
|
|
|
echo ":: Mounting initial filesystems"
|
|
mountpoint -q /proc || mount -t proc proc /proc -o nosuid,noexec,nodev
|
|
mountpoint -q /sys || mount -t sysfs sys /sys -o nosuid,noexec,nodev
|
|
mountpoint -q /run || mount -t tmpfs run /run -o mode=0755,nosuid,nodev
|
|
mountpoint -q /dev || mount -t devtmpfs dev /dev -o mode=0755,nosuid
|
|
|
|
mkdir -p -m0755 /run/runit /run/lock /run/lock/lvm /run/lvm /run/user /dev/pts /dev/shm
|
|
mountpoint -q /dev/pts || mount -n -t devpts devpts /dev/pts -o mode=0620,gid=5,nosuid,noexec
|
|
mountpoint -q /dev/shm || mount -n -t tmpfs shm /dev/shm -o mode=1777,nosuid,nodev
|
|
|
|
mount -o remount,ro /
|
|
|
|
echo ":: Setting up Unicode"
|
|
for i in /dev/tty[0-9]*;do
|
|
unicode_start <$i
|
|
done &
|
|
|
|
echo ":: Setting system clock"
|
|
hwclock --utc --hctosys
|
|
|
|
echo ":: Enabling devices"
|
|
touch /dev/mdev.seq
|
|
/usr/bin/mdev -s &
|
|
|
|
echo ":: Loading drivers"
|
|
for i in $(seq 2); do
|
|
find /sys -name modalias -type f -exec cat {} + | sort -u | xargs modprobe -b -a
|
|
done 2>/dev/null
|
|
|
|
echo ":: Bringing up network"
|
|
ip link set up dev lo
|
|
cat /etc/hostname >/proc/sys/kernel/hostname
|
|
|
|
echo ":: Setting up cryptographic devices"
|
|
grep "^[^#]" /etc/crypttab | while read name device password options; do
|
|
case $options in
|
|
*swap*)
|
|
cryptsetup --key-file /dev/urandom open --type plain $device $name
|
|
mkswap /dev/mapper/$name
|
|
;;
|
|
*)
|
|
cryptsetup luksOpen $device $name < /dev/console
|
|
;;
|
|
esac
|
|
done
|
|
|
|
echo ":: Checking filesystems"
|
|
[ -f /forcefsck ] || grep -q forcefsck /proc/cmdline && FORCEFSCK=-f
|
|
if ! [ -f /fastboot ] && ! grep -q fastboot /proc/cmdline; then
|
|
fsck -A -T -C -a -t noopts=_netdev $FORCEFSCK
|
|
if [ $? -gt 1 ]; then
|
|
sulogin
|
|
fi
|
|
fi
|
|
|
|
echo ":: Mounting filesystems"
|
|
mount -o remount,rw /
|
|
mount -a -t "nosysfs,nonfs,nonfs4,nosmbfs,nocifs" -O no_netdev
|
|
|
|
echo ":: Enabling swap"
|
|
swapon -a
|
|
|
|
echo ":: Tidying up"
|
|
install -m0664 -o root -g utmp /dev/null /run/utmp &
|
|
rm -f /etc/nologin /forcefsck /forcequotacheck /fastboot &
|
|
|
|
if grep -q 'break=init' /proc/cmdline; then
|
|
echo 'Breaking before init, type "exit" to continue booting'
|
|
/bin/sh
|
|
fi
|
|
|
|
if [ -x /etc/rc.local ]; then
|
|
echo ":: Sourcing /etc/rc.local"
|
|
. /etc/rc.local
|
|
fi
|
|
|
|
echo ":: Passing control to runit"
|
|
echo
|
|
exec runsvdir -P -s runit-signal /service
|
|
|
|
This does a couple things:
|
|
|
|
1. Mounts /proc, /sys, /dev, and some other directories.
|
|
2. Turns on Unicode for 9 TTYs
|
|
3. Sets the system clock from the hardware clock
|
|
4. Runs an initial mdev to populate /dev
|
|
5. Loads modules for things in /sys
|
|
6. Bring up the loopback interface
|
|
7. Initialize your cryptfs, if you have any in /etc/crypttab
|
|
8. fsck then mount everything in /etc/fstab
|
|
9. Run whatever's in /etc/rc.local
|
|
10. Start runsvdir
|
|
|
|
You may also want to install the `dash` package,
|
|
and link `/bin/sh` to that,
|
|
if you worry (as I do) about libc breaking things
|
|
(`dash` is statically linked).
|
|
|
|
|
|
runit-signal
|
|
------------
|
|
|
|
Busybox's version of `runsvdir` has a `-s` option.
|
|
When runsvdir gets a signal,
|
|
it runs whatever was provided to `-s` with the signal number as the first argument.
|
|
Here's my `/usr/local/sbin/runit-signal`
|
|
(make sure you `chmod +x`):
|
|
|
|
|
|
#! /bin/sh
|
|
|
|
##
|
|
## Signal handler for runit
|
|
##
|
|
|
|
if [ $PPID != 1 ]; then
|
|
echo "This program should only be invoked by PID 1."
|
|
# The reason is that killall5 won't kill anything in the same
|
|
# process group. That means it won't kill your invoking shell,
|
|
# getty, or svrun. That in turn prevents filesystems from
|
|
# unmounting, or even being remounted ro, since svrun (at least) has
|
|
# a FIFO open for writes. And if we reboot without unmounting
|
|
# filesystems, that's bad.
|
|
|
|
echo "Feel free to read $0 to learn why :)"
|
|
exit 1
|
|
fi
|
|
|
|
waitall () {
|
|
for i in $(seq 50); do
|
|
# If all processes are in group 0, we're done
|
|
awk '($5){exit 1;}' /proc/[0-9]*/stat && return 0
|
|
usleep 200000
|
|
done
|
|
return 1
|
|
}
|
|
|
|
cleanup () {
|
|
echo "Stopping services..."
|
|
sv stop /service/*
|
|
echo "Asking processes to exit..."
|
|
killall5 -1
|
|
killall5 -15
|
|
if waitall; then
|
|
echo "Forcing processes to exit..."
|
|
killall5 -9
|
|
waitall
|
|
fi
|
|
echo "Unmounting file systems..."
|
|
umount -a -r
|
|
|
|
# Sometimes when we reach here we still haven't been able to umount
|
|
# everything. Not much more we can do about that, other than flush
|
|
# write buffers and hope for the best.
|
|
sync
|
|
}
|
|
|
|
case $1 in
|
|
1) # SIGHUP
|
|
;;
|
|
15) # SIGTERM: reboot
|
|
cleanup
|
|
echo "Rebooting..."
|
|
busybox reboot -f
|
|
;;
|
|
10) # SIGUSR1: halt
|
|
cleanup
|
|
echo "Halting..."
|
|
busybox halt -f
|
|
;;
|
|
12) # SIGUSR2: power
|
|
cleanup
|
|
echo "Shutting down..."
|
|
busybox poweroff -f
|
|
;;
|
|
*) # Everything else
|
|
;;
|
|
esac
|
|
|
|
|
|
|
|
Create a getty
|
|
--------------
|
|
|
|
Before we reboot, we need to make sure to create a way to log in.
|
|
The following in `/service/tty2/run` will start a getty on the second virtual console
|
|
(don't forget to `chmod +x`):
|
|
|
|
#! /bin/sh
|
|
|
|
pwd=$(pwd)
|
|
TTY=${pwd##*/}
|
|
exec agetty $TTY
|
|
|
|
You can make more than one getty by copying this to `/service/tty3/run` and so on.
|
|
|
|
|
|
Reboot!
|
|
-------
|
|
|
|
Double check you did it all right:
|
|
|
|
* You understand how to use the initrd shell you get passing the `break=postmount` boot argument
|
|
* `/sbin/init` should be an executable shell script
|
|
* At least a getty service will start from the `service` directory you set up
|
|
|
|
That's not a big checklist.
|
|
Ready to go?
|
|
Tell the currently-running init to reboot:
|
|
|
|
/sbin/init.sysv 6
|
|
|
|
Cross your fingers!
|
|
|
|
|
|
If it doesn't work
|
|
------------------
|
|
|
|
You can always move `/sbin/init.sysv` back to `/sbin/init` and reboot
|
|
into your old setup.
|
|
Nothing in this page will destroy the old bootup process
|
|
(other than renaming `init`, of course).
|
|
|
|
|
|
If it does work
|
|
---------------
|
|
|
|
Congratulations, you're now using runit.
|
|
You now need to write startup scripts for things you like to run,
|
|
like `dhcpcd`, `ntpd`, maybe `xdm`.
|
|
You're an Arch Linux sysadmin,
|
|
you should know what you need,
|
|
and I can't help you past here.
|
|
|
|
Hotplug events won't work, though.
|
|
For that, you need to either run udev or some other hotplug listener.
|
|
|
|
|
|
Setting up `mdev` as a hotplug listener
|
|
---------------------------------------
|
|
|
|
The `mdev` utility of busybox can replace most of what `udev` does.
|
|
You just need to have the kernel run `mdev` as the hotplug userspace thingy.
|
|
|
|
Recent precompiled kernels have removed support for `/proc/sys/kernel/hotplug`,
|
|
so it's necessary to run a userspace program to get netlink events.
|
|
You can [use the one I wrote](http://woozle.org/neale/g.cgi/aur/runit-init/tree/src/hurtplurg.c) if you like.
|
|
|
|
You'll need to configure `mdev` to set up file permissions that work for you.
|
|
A reasonable set of defaults is available
|
|
[as another part of my AUR](http://woozle.org/neale/g.cgi/aur/runit-init/tree/mdev.conf).
|
|
|
|
Since X11 wants `udevd` for something or other,
|
|
you'll also need to tell it to use whatever the old method is.
|
|
I don't quite understand what they do,
|
|
but the files you need are [in my runit-desktop AUR](http://woozle.org/neale/g.cgi/aur/runit-desktop/tree/etc/X11/xorg.conf.d).
|
|
|
|
|
|
Getting rid of `systemd`
|
|
------------------------
|
|
|
|
At this point you are not running anything in `systemd`.
|
|
But you still need it installed,
|
|
because a lot of things depend on libraries it's taken over.
|
|
|
|
Don't panic about this.
|
|
It's going to be okay.
|
|
You have lots of things installed that you don't strictly need.
|
|
I chose Arch over Gentoo because I like precompiled packages,
|
|
even though that means they bring in a couple things I don't need.
|
|
`systemd` is one of those things.
|
|
|
|
|
|
Have fun!
|