I run the
program from Dan Bernstein's
package as process 1. This setup is discussed on
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
init is system initialization. But
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.)
SIGWINCH, and possibly
SIGPWRwould be useful for Linux. (I'm not familiar enough (yet) with other systems to say what would be useful there.)
svscandoesn'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.
utmpaccounting, 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
utmpaccounting, and I'm working on a cooperating program to do
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.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.
/servicewhich adds all the real services to
/serviceand 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
svscandirectly. (Before starting
/serviceis stored on a persistent filesystem, then the script must clean it out in case of a previous system crash; otherwise
/serviceis 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
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 isn't running. But svclean
handles this even better: we can run a logger for
output as a normal service, without involving
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,
supervise keeps a writable descriptor open to a lock
file. We can kill each
svscan will respawn them. We need to prevent that
svscan, trying to unmount the filesystem before
superviseis restarted and reopens the file.
/serviceand restore them at boot time. This would mean that
/servicecan't live on a read-only filesystem, and since
/serviceis 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.
supervisedirectory (and the
/servicedirectory itself) on a RAM filesystem, but that still wouldn't handle the logging issue.
So we give
svscan a specialized
supervise wrapper; during shutdown, if
starts this wrapper to replace a
supervise that has exited, the
wrapper exits immediately instead of running
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
exit, removes itself from
/service, and forks into the
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,
mv). I haven't published (and do not
currently use) an init system based on this technique, but the core of it
load.sh from prjlibs.
Please let me know about other related sites.