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 supervise
s (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.