While I'm keeping this page available for historical interest, I no longer run this configuration, and I wouldn't recommend it today. These days, I'd use s6 instead.
I run the
svscan
program from Dan Bernstein's
daemontools
package as process 1. This setup is discussed on
the
supervision list, and sometimes on
the log list as well.
I've written a package to take care of some
of the issues described here. If you're interested in how this setup
has changed over time, you can also read
the message I sent to the
log list back around when I first started.
Why bother replacing init at all? The main idea behind
this setup is that one-time system initialization is a completely
separate task from ongoing service management. They should therefore
be handled by separate code. svscan is superior to
init for running services, so the only use I could have
for init is system initialization. But init
is extreme overkill for this task; I don't want to keep all that dead
code (runlevels, login accounting, etc.) on disk and in memory. Once
initialization is complete, the service manager can be
exec'ed by process 1; there's no need to keep the
initializer around in a running process.
Let's consider the usual ongoing duties of process 1. (For now, we ignore all tasks related to system initialization. What is presented here ought to be compatible with any method you might use to handle initialization.)
svscan does this.
svscan does this.
SIGINT,
SIGWINCH, and possibly SIGPWR would be
useful for Linux. (I'm not familiar enough (yet) with other
systems to say what would be useful there.) svscan
doesn't do this; I patched it to run ./sigint, etc.,
on receipt of these signals, but then decided this functionality
isn't really necessary, so I threw away the patch. The tasks
normally done via signals to process 1 would be better done by
other methods, when possible, and handling signals in process 1
(maybe?) involves non-portable programming.
utmp accounting, if you care about
that. However, this is better handled in login services. I
didn't know about this one when I started, and I haven't written
any code to supply this functionality. I have written a
replacement login program that does normal logging instead of
wtmp/utmp accounting, and I'm working on
a cooperating program to do wtmp/utmp
accounting.
Have I missed any? It looks like it ought to be possible to put the
svscan peg in the init hole. So I did, and
it happened to work.
There are (at least) two general ways to handle system initialization:
rc.sysinit/rc.local-type
stuff and then execs svscan. This may fail if there
are boot steps that require the use of supervised services. I
haven't encountered any such on my systems.
/service
which adds all the real services to /service and
removes itself. Doing this in multiple stages may solve the
problem above, but it also increases complexity. There seems to
be little benefit in this method, especially since the kernel
still must invoke a script for initialization instead of
svscan directly. (Before starting
svscan, if /service is stored on a
persistent filesystem, then the script must clean it out in case
of a previous system crash; otherwise /service is
stored on a RAM filesystem, and the script must mount the
filesystem and create the symlink for the initialization service.)
This script could just as easily do the real initialization work
itself.
In any case, the traditional init program does not need to run
at all; all services can run under supervise instead of from
init scripts.
svscan's output
Output from svscan and some supervises (and their
services) will go to the console by default. It could be redirected to a
file or, with some effort, to readproctitle, but multilog (or
some other supervised logger) would obviously be preferable. So I wrote
grabconsole, which is also
useful for grabbing logs meant for syslogd when
syslogd isn't running. But svclean
handles this even better: we can run a logger for svscan's
output as a normal service, without involving /dev/console.
I don't need or use runlevels, but there are ways to have them with
daemontools. E.g., you can have a runlevel directory containing a
subset of the symlinks in /service, and you can run
svc -u dir/* to change to that runlevel. See also the
related links below.
For a clean shutdown, we want to kill each service and ensure that its
logger has written all the logs before killing the logger. We want to
be able to unmount (or remount read-only) all local disk filesystems,
but supervise keeps a writable descriptor open to a lock
file. We can kill each supervise, but
svscan will respawn them. We need to prevent that
somehow.
svscan.
svscan, trying to
unmount the filesystem before supervise is restarted
and reopens the file.
/service and restore them at boot time. This would
mean that /service can't live on a read-only
filesystem, and since /service is process 1's working
directory, its filesystem cannot be unmounted. So either it must
be on the root filesystem (which should not need to be writable),
or its filesystem will be dirty at the next boot.
supervise directory (and the
/service directory itself) on a RAM filesystem, but that
still wouldn't handle the logging issue.
So we give svscan a specialized $PATH
containing a
supervise wrapper; during shutdown, if svscan
starts this wrapper to replace a supervise that has exited, the
wrapper exits immediately instead of running supervise. After
we svc -dx all services, svscan will try to restart
supervise, but it will actually start our wrapper instead.
supervise will not run, and no files will be held open for
writing. The wrapper also allows the logger to read as much data as is in
the log pipe, and exit afterwards.
Some people might want the shutdown script to be spawned from process
1, to ensure a clean process state. This can be done by making a
shutdown service. To shut down, you link the service into
/service; it then tells its own supervise to
exit, removes itself from /service, and forks into the
background so supervise will think it is finished, before
doing the real work.
#!/bin/sh -e svc -ox . rm /service/shutdown do-real-shutdown &
You would also add rm -f /service/shutdown to
your boot scripts, just to be careful.
I also wrote a modular set of boot scripts, with a dependency-ordering system
implemented in pure sh code (i.e., using no external commands,
like ls or mv). I haven't published (and do not
currently use) an init system based on this technique, but the core of it
would be load.sh from prjlibs.
Please let me know about other related sites.