Menu

Tree [f17bd3] master /
 History

HTTPS access


File Date Author Commit
 _0_ 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 depctl 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 depinit 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 lib 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 scripts 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 spawnshell 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 tests 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 utmp 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 COPYING 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 INSTALL 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 Makefile 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 _0_local 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 make-3.80-eval.patch 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 readme.html 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2
 valid-html40.png 2003-10-06 Richard Lightman Richard Lightman [f17bd3] depinit-0.1.4.tar.bz2

Read Me

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<!--
This is the wrong file to change. In future, the readme will be generated
by "scripts/gen".
 -->
<head><title>depinit</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</head><body>

<h1>Contents</h1>
<ul>
<li><a href="#introduction">Introduction</a>
<li><a href="#license">License</a>
<li><a href="#bugs">Bugs</a>
<li><a href="#downloads">Downloads</a>
<li><a href="#building">Building</a>
<li><a href="#manpages">Man-pages</a>
<li><a href="#setup">Setting it up</a>
</ul>
<p><a href="../index.html">Back</a></p>
<p>
  <a href="http://validator.w3.org/check/referer"><img
      src="valid-html40"
      alt="Valid HTML 4.0!" height="31" width="88"></a>
</p>
<hr>


<h2><a name="introduction">Introduction</a></h2>
<p>
Depinit is an alternative init program that can handle
parallel execution,
dependencies,
true roll-back,
pipelines,
improved signaling
and unmounting filesystems on shutdown.
It incorporates ideas from sysvinit, simpleinit, daemontools
and make. At present, it is a bit experimental, and requires good knowledge
of the initialisation process to set up.
</p><p>
I am keen to hear from people who try it. Here is why you should.
</p>

<h3>Reasons for depinit</h3>
<ul>
<li> Depinit saves time by starting processes in parallel where
     possible.
<li> Depinit saves time by not starting processes until their
     dependencies are ready.
<li> Depinit can start and stop services and groups of services
     by name, so there is no arbitrary classification into
     runlevels or any meaningless runlevel numbers.
<li> Depinit uses a true dependency based roll back system, so
     when it is told to stop a service, it only has to stop the
     minimum number of dependent services first. (Simpleinit
     bases rollback on the time a service started, so rolling back
     a service that started quickly takes down everything, even if
     nothing depends on it.)
<li> Depinit can create pipelines between daemons (for reliable log
     rotators).
<li> Depinit can create started/stopped/failed messages, so there
     is no need for them in init scripts.
<li> During the shutdown stage, most sysvinit/runlevel based scripts
     send SIGTERM to wipe out user processes, wait a fixed amount of
     time, then send SIGKILL and wait again. Depinit sends the
     signals directly, and cuts out the waiting if there is nothing to
     wait for.
<li> Depinit sends the correct signal, taking into account which ones
     are ignored. For example, login shells should be stopped with SIGHUP
     because they ignore SIGTERM.
<li> Depinit stops user processes before services that they may
     rely on. Standard sysvinit/runlevel based scripts send signals to
     server daemons without first giving user clients the chance to detach
     cleanly.
<li> One thing that is impossible to get right with sysvinit/runlevel
     based scripts is the correct order for sendsignals, kill network
     and unmount filesystems. Sendsignals kills daemons required to
     run the network, the network is required to unmount network
     filesystems, and network filesystems may hold the executable or
     data for the daemons. The only complete solution is a more
     intelligent replacement for sendsignals.
<li> Depinit unmounts filesytems and detaches loop back devices
     for shutdown.
<li> Depinit can start and stop services as a result of catching
     arbitrary signals - not just the ones that are currently
     assigned.
</ul>

<h3>Implications of using depinit</h3>
<ul>
<li><p>
     The normal login program does not let depinit work properly.
     Depinit requires separation between the authentication process
     and running the user's shell. But login does the two together.
     Running login as one of depinit's daemons will make depinit
     consider the login shell a service that needs to be kept running
     until all the user processes have been killed. Depinit will
     consider the login shell's children to be part of the service,
     so depinit will not consider any of these processes as user processes
     that must be killed before stopping services. You could make login
     depend on every other service, but then if anything goes wrong,
     you cannot log in.
    </p><p>
     The solution is to use the KeyboardSignal for the purpose it
     was intended for: starting login shells on new virtual terminals.
     KeyboardSignal sends a signal to (dep)init, which then runs a
     script. If no user has been authenticated, the script attempts
     to authenticate a user. If there has been a successful
     authentication, the script starts a login shell as a new session,
     and tells depinit to stop the service started by the KeyboardSignal
     (so this will work next time the user presses KeyboardSignal).
     The login shell is independent of the init scripts, and depinit
     will consider it and its children to be a user process that must
     be killed before any service is stopped for shutdown.
    </p><p>
     The example KeyboardSignal script uses gnupg to authenticate the user.
     You must have this working to use the script.
     You must know which key KeyboardSignal was assigned to.
     If it is not assigned, you must fix it.
    </p>
<li>
     You only have to type your password once.
<li>
     If you want to be queried for your username and password again,
     you just need to clear /var/run/console_user.
<li>
     The user accounting databases are not maintained completely.
     Ideally, init marks a login process as an init process, and
     exec's getty. Getty exec's login, which marks the process as
     a login process, until just before it starts the user's shell.
     Then it marks the process as a user process. When the user's
     shell exits, init marks the process as dead. At present
     the only part of this that is implemented is when spawnshell
     creates a user-process entry, and depinit marks it as dead.
<li>
     The only shutdown command available does the shutting down at once.
</ul>

<h3>Future</h3>
<ul>
<li> Make a standard login mechanism. It can be made to work by stopping
     the getty daemons from inside the sig_int script.
<li> Install example scripts if required (The scripts in scripts/example/
     probably need fixing).
<li> Auto-generate script documentation (if python is installed). (The
     scripts/gen python script does not contain corrections made to this
     readme, but does contain corrections made to 0.1.3.)
<li> Make depinit's internal at daemon accessible.
<li> Make a traditional shutdown command using the at daemon.
<li> Run a script whenever a process in /var/run/utmp dies.
</ul>


<h3>Changes since 0.1.3</h3>
<ul>
<li> readme: Patch from Alex Groenewoud included - thanks Alex.
<li> valid-html image should now be on the web-site
<li> utmp: -b option to update utmp and wtmp with the same time stamp.
<li> utmp: Try to modify an existing entry instead of creating a new
     one when it is clear which entry should be changed.
<li> readme: link to the utmp and spawnshell man-pages.
<li> depinit man-page: document special behaviour for process 1
     correctly.
<li> depinit man-page: correctly document choice of signals for killing
     services.
<li> spawnshell: -c option to select a console.
<li> readme: add links to utmp and spawnshell man-pages.
<li> Modified spawnshell for use in authentication.

</ul>


<h2><a name="license">License</a></h2>
<p>
Depinit is copyright 2002 by Richard Lightman, and released under
Version 2 of the
<a href="COPYING">GNU General Public Licence</a>. There is NO WARRANTY.
</p>

<h2><a name="bugs">Bugs</a></h2>
<p>
The big bad bugs have been fixed, but as yet there has not been
sufficient testing to call this anything more than an beta release.
</p><p>
A bit more work needs to go into logging when syslog is not available
or wanted. Anything with a file descriptor open to a terminal can
be killed by anyone at the terminal. All they have to do is press
the SAK key. The (untested) solutions are to have a syslog report
on the console, or to use a separate daemon to log to the console
(it does not matter if anyone kills that daemon because depinit can
restart it without loosing data).
</p>

<h2><a name="downloads">Downloads</a></h2>
<p>
Take a look in the download section of the man-pages for
<a href="man8/depinit.8.html">depinit</a> or 
<a href="man8/depctl.8.html">depctl</a> for the download location
of the source code.
</p>

<h2><a name="building">Building</a></h2>
<p>
This program has only been tested on linux-2.4.*. This could cause
problems with the more unusual system calls such as for shutting
down, detaching loop devices and capturing the keyboard signal.
</p><p>
The build process requires
<a href="ftp://ftp.gnu.org/gnu/make/make-3.80.tar.gz">make-3.80</a>
(with some bugs fixed, a patch by Mr P. Smith is included in the sources),
gcc version 3.* (3.2 works, 2.* probably will not),
<a href="ftp://ftp.gnu.org/gnu/help2man/">help2man</a>,
<a href="ftp://ftp.gnu.org/gnu/gettext/">gettext</a>,
and I suspect a reasonably modern
<a href="ftp://ftp.gnu.org/gnu/gawk/">awk</a> (gawk-3.1.1 is fine).
</p><p>
There are only two settings that are not part of the generic install
process described in the sources. DEFAULT_CONFIG defaults to
$(SYSCONFDIR)/depinit and is the default directory for the configuration
directories. DEFAULT_ERROR_FILE defaults to /dev/console, and is the
default file to use if depinit is process 1 and is told to output to
stderr.
</p>

<h2><a name="manpages">Man-pages</a></h2>
<p>
Depinit uses two programs: <a href="man8/depinit.8.html">depinit</a> often runs as
process 1, and does all the real work. <a href="man8/depctl.8.html">depctl</a>
is used to communicate with depinit, much like telinit communicates with init.
The depinit package also contains some helper programs:
<a href="man8/utmp.8.html">utmp</a>
for maintainting the user accounting databases and
<a href="man8/spawnshell.8.html">spawnshell</a>
for starting new shells with the KeyboardSignal key.
</p>


<h2><a name="setup">Setting it up</a></h2>
<p>
To start with, I recommend using a kernel with the magic SysReq key enabled,
so you can sync and unmount the filesystems if something goes wrong (see
/usr/src/linux/Documentation/sysrq.txt). If you are going to recompile
the kernel, now is a good time to fix the built in keymap. You will need one
with KeyboardSignal defined.
Also, add an entry to your bootloader menu that has the kernel option
'init=/sbin/depinit'. That way, you can still use the old init if you
have problems with depinit. When you are confident that you have depinit
working, you can make /sbin/init a symlink to depinit.
If you did not compile
bash with a sane default path, you will have to set PATH correctly
in all the depinit scripts. The environment depinit provides to the scripts
is exactly the one depinit got from the kernel.
</p>

<h3>/etc/depinit</h3>
<p>
Next you will need some init scripts. By default, these go in '/etc/depinit/'.
</p><pre><code class=bash>

mkdir -p /etc/depinit/ 
cd       /etc/depinit/
</code></pre>

<h3>/etc/depinit/exec</h3>
<p>
If process 1 crashes, your system becomes largely unusable.
If depinit crashes, it will attempt to exec /etc/depinit/exec. This example
script will let you log in and shut things down cleanly.
</p><pre><code class=bash>

echo -e >exec '#! /bin/bash -e\n'
echo   >>exec 'exec &lt;/dev/vc/2 >/dev/vc/2 2>&amp;1'
echo   >>exec 'chvt 2'
echo   >>exec 'exec spawnshell -d -l'
chmod 754 exec
</code></pre><p>

<h3>/etc/depinit/default</h3>
<p>
When depinit starts, it tries to start 'default', so you will need something
there. As there is no start, stop, source, filter, dest or daemon file
in 'default', depinit knows that to start 'default', it only has to start
its dependencies (to be added later). To stop 'default', no action is
required.
</p><pre><code class=bash>

mkdir default
</code></pre>

<h3>/etc/depinit/fixing/</h3>
<p>
As this is your first go with depinit, you will get the scripts wrong,
and probably will not be able to log in. To fix things you could do
with a shell. Remember to remove it when things are working. Any
processes left running from this shell will cause trouble when it is
time to shut down.
</p><pre><code class=bash>

mkdir fixing
echo >>default/depend 'fixing'
echo >fixing/start -e '#! /bin/bash -e\nexec spawnshell -l'

</code></pre>

<h3>/etc/depinit/sig_quit/start</h3>
<p>
It would be handy to be able to log in, so let's fix that next.
This is a slightly modified service - normally services are
started because you asked for the service, or something
depends on it. In this case, the script is started by a signal
caused by pressing the KeyboardSignal key.
</p><p>
Normally, once the start script has run, depinit will not run
it again because the service has already started. In this case
we want depinit to run the script each time the KeyboardSignal
key is pressed. The solution is to tell depinit to stop the
service each time the start script is run. As there is no
stop script, no action is required to stop the service.
</p><pre><code class=bash>

mkdir sig_quit
cat >sig_quit/start &lt;&lt;'EOF'
#! /bin/bash -e

# This script is started when you press the KeyboardSignal key.
# It starts a shell on a new virtual terminal belonging to the same
# user as the owner of /var/run/console_user, if that file exists
# and is not empty. Otherwise the script prompts for a user name,
# and if that is a valid login user, encrypts a token with the user's
# public key. If the user can get gpg to decrypt the token correctly
# (types the password for the corresponding secret key) then this script
# puts the username into /var/run/console_user, changes the owner of
# that file to match the user name, and starts a shell as above.

# This type of login works better with depinit than the standard
# *getty/login because it makes depinit recognise all user processes
# as processes to kill before stopping services for shutdown.

# Users can prevent unauthenticated logins by typing:
# >/var/run/console_user

# @@@ version using /etc/shadow instead of gpg.

# Delete temporary files and directories used in gpg_login:
tidy () {
	if [ "$authdir" ]; then
		rm -r "$authdir"
	fi
	if [ "$userdir" ]; then
		rm -r "$userdir"
	fi
	trap '' EXIT
}

gpg_login () {
	trap tidy EXIT
	umask 077
	authdir="$(mktemp -d -t auth.XXXXXXXXXX)"
	userdir="$(mktemp -d -t auth.XXXXXXXXXX)"

	check="$(dd if=/dev/random of="$authdir/tok" bs=1024 count=1 2>&amp;1)"
	[ "$check" = $'0+1 records in\n0+1 records out' ]

	gpgopts='--no-verbose -q --keyserver-options no-auto-key-retrieve'
	su "$USER" -c &lt;"$authdir/tok" >"$userdir/enc" \
	   "gpg $gpgopts --batch --no-tty -e --default-recipient-self"
	chown -R "$USER" "$userdir"
	su "$USER" -c "gpg $gpgopts -d -o $userdir/dec $userdir/enc"

	cmp "$authdir/tok" "$userdir/dec"
	tidy
	exec &lt;/dev/null >/dev/null 2>&amp;1
}

vc=3
devname=/dev/vc/$vc
if ! [ -e $devname ]; then
	devname=/dev/tty$vc
fi

if [ -s /var/run/console_user ]; then
	USER="$(find /var/run/ -name console_user -printf "%u\n")"
else
	exec &lt;$devname >$devname 2>&amp;1
	TERM=linux
	stty sane
	cd /
	chvt $vc

	echo -en "\n$(hostname) user name: "
	read USER
	id "$USER" &amp;>/dev/null
	shell="$(pwcat | awk -v FS=: '$1=="'"$USER"'" {print $7}')"
	home="$( pwcat | awk -v FS=: '$1=="'"$USER"'" {print $6}')"
	[ "$shell" ]
	[ -x "$shell" ]
	[ /bin/bash == "$shell" ]
	[ "$home" ]
	[ -d "$home" ]
	[ -z "${home##/home/*}" ]

	gpg_login

	echo  "$USER" >/var/run/console_user
	chown "$USER"  /var/run/console_user
fi

umask 022
spawnshell -u "$USER" || true
exec depctl -k sig_quit
EOF
chmod 754 sig_quit/start
</code></pre>

<h3>/etc/depinit/utmp</h3>
<p>
If the 'w' and 'who' commands are needed, /var/run/utmp must exist
before any logins. As I have /var/run on tmpfs, this needs to be fixed.
</p><pre><code class=bash>

mkdir utmp
echo >>sig_quit/depend 'utmp'
echo -e   >utmp/start '#! /bin/bash\n'
echo     >>utmp/start 'exec >>/var/run/utmp chmod 644 /var/run/utmp'
chmod 754 utmp/start
</code></pre>

<h3>/etc/depinit/varrun</h3>
<p>
The above utmp script thinks that /var/run exists. This needs fixing promptly
because I keep /var/run on a tmpfs. /var/run is used to hold pid's of
daemons (superfluous with depinit). Some things get confused
if there are stale files in /var/run, so it is an obvious place to
use a tmpfs (which is always empty when mounted) and prevent such
confusion.
</p><pre><code class=bash>

mkdir varrun
echo >>utmp/depend 'varrun'
echo -e   >varrun/start '#! /bin/bash -e\n'
echo     >>varrun/start 'mounts="$(&lt;/proc/mounts)"'
echo     >>varrun/start '[ -z "${mounts##* /var/run *}" ] ||'
echo     >>varrun/start '       exec mount /var/run'
echo -e   >varrun/stop '#! /bin/bash -e\n'
echo     >>varrun/stop 'mounts="$(&lt;/proc/mounts)"'
echo     >>varrun/stop '[ -n  "${mounts##* /var/run *}" ] ||'
echo     >>varrun/stop '  exec umount -rnd /var/run'
echo -e   >varrun/depend 'proc\nswap\nvar'
chmod 754 varrun/{start,stop}
</code></pre>

<h3>Mounting all the filesystems</h3>
<p>
Let's now use the above example as a template
for (almost) all the mounts. Although depinit will unmount
anything that the following scripts miss, it is best to have explicit
stop scripts to avoid confusion, and so that roll-back works
as people would expect.
</p><pre><code class=bash>

mkdir devshm home tmp usb var varlock vartmp
sed &lt;varrun/start >devshm/start  's:/var/run:/dev/shm:g' 
sed &lt;varrun/start >home/start    's:/var/run:/home:g' 
sed &lt;varrun/start >tmp/start     's:/var/run:/tmp:g' 
sed &lt;varrun/start >usb/start     's:/var/run:/proc/bus/usb:g' 
sed &lt;varrun/start >var/start     's:/var/run:/var:g' 
sed &lt;varrun/start >varlock/start 's:/var/run:/var/lock:g' 
sed &lt;varrun/start >vartmp/start  's:/var/run:/var/tmp:g' 
sed &lt;varrun/stop >devshm/stop  's:/var/run:/dev/shm:g' 
sed &lt;varrun/stop >home/stop    's:/var/run:/home:g' 
sed &lt;varrun/stop >tmp/stop     's:/var/run:/tmp:g' 
sed &lt;varrun/stop >usb/stop     's:/var/run:/proc/bus/usb:g' 
sed &lt;varrun/stop >var/stop     's:/var/run:/var:g' 
sed &lt;varrun/stop >varlock/stop 's:/var/run:/var/lock:g' 
sed &lt;varrun/stop >vartmp/stop  's:/var/run:/var/tmp:g' 
echo -e >> devshm/depend 'devfsd\nproc\nswap'
echo -e >> home/depend 'proc'
echo -e >> tmp/depend 'swap\nproc\n'
echo -e >> usb/depend 'proc'
echo -e >> var/depend 'proc'
echo -e >> varlock/depend 'swap\nvar'
echo -e >> vartmp/depend 'swap\nvar'
echo -e >> sig_quit/depend 'home\nvartmp'
</code></pre>

<h3>/etc/depinit/proc</h3>
<p>
If you were awake, you will have noticed that proc was not mounted,
and that the example script would not work with proc. Here is the
right script.
There is no 'stop' script for proc because depinit would only have to
mount it again to check that nothing is mounted before shutdown.

</p><pre><code class=bash>

mkdir proc
echo -e   >proc/start '#! /bin/bash\n'
echo     >>proc/start '[ -f /proc/mounts ] || exec mount /proc'
</code></pre>
<p>
There is no stop script - lots of things do not work if you
unmount proc.
</p>

<h3>/etc/depinit/swap</h3>
<p>
It is about time we had some swapspace so the tmpfs's do not waste
memory:
</p><pre><code class=bash>

mkdir swap
echo -e   >swap/start '#! /bin/bash\nexec swapon -a'
</code></pre>

<h3>Avoiding mount related problems</h3>
<p>
Mount needs /etc/mtab to contain accurate data. This is normally done
by remounting / rw, and creating /etc/mtab. The other way to do it is
to link /etc/mtab to /proc/mounts, and mount /proc. I prefer the second
method, so that is what is in these example script. The advantage of doing
it this way is that /etc/mtab always gives an accurate idea of what file
systems are mounted. The disadvantage is that some information is lost.
There is work in progress on getting the kernel to include this information.
</p><p>
Now for some real confusion. After boot up, imagine that you tell depinit
to stop 'home' (depctl -k home), then you mount it by hand (mount /home),
then you tell depinit to start home (depctl -s home). Without a check,
to see if /home is already mounted, mount would and exit with an error.
This would cause depinit to think that home had not started, so it would
not start anything that depends on home, or run the stop script on
shutdown.
</p><p>
Depinit would still unmount /home on shutdown because it contains code to
unmount everything, but doing proper checks reduce the chance of inaccurate
error messages. Missing out the stop script causes similar problems.
Depinit's unmount on exit code is there to recover from accidents, not to
make up for laziness.
</p><p>
I keep /var/run, /var/lock, /var/tmp, /tmp and /dev/shm on tmpfs's. I
have over 2GB of swap space, but only 128MB of ram. If I put lots of data
in a tmpfs (for example by compiling the kernel in /var/tmp), and turn
off swap, the contents of the tmpfs must be loaded from swap into ram.
It won't fit. The swapoff command does not stop running, and is hard to
kill. The kernel knows it is short of vm, and starts killing things to free
up space. This does not do any good because the vm is being used by tmpfs,
not processes.
</p><p>
To avoid this happening (again ;-), I have proper bomb proof stop scripts
to unmount anything that uses tmpfs, and no stop script for swap.
</p><p>
Finally, /var must be mounted before /var/run can be mounted. This
shows the advantage of using individual mount scripts in depinit over
using 'mount -a'. Depinit has proper dependency checking to allow mounts
to work in parallel. 'mount -a -F' does handle parallel mounts, but only
if there are no dependencies. Also, if any filesystem fails to mount,
'mount -a' exits with error status. Depinit would then not be able to
start anything that depended on mounts - such as login.
</p>

<h3>Daemons and unbreakable pipes</h3>
<p>
Being able to log in is important, but is is not enough, so my next example
shows how to set up unbreakable pipes:
</p><pre><code class=bash>

mkdir daemons devfsd gpm klogd{,l} syslog{c,d,l}
echo >>default/depend -e 'daemons'
echo  >daemons/depend -e 'devfsd\ngpm\nklogd\nsyslogd'

echo  >devfsd/daemon -e '#! /bin/bash\n\nexec devfsd /dev -fg'

echo  >gpm/depend -e 'devfsd\nrw'
echo  >gpm/daemon -e '#! /bin/bash\n'
echo >>gpm/daemon    'exec gpm -D -m /dev/misc/psaux -t imps2 -a 3 -d 4 -3'

echo  >klogd/source -e  '#! /bin/bash\n'
echo >>klogd/source -en 'exec klogd -n -2 -f - 2>&amp;1 -k '
echo >>klogd/source '"/lib/modules/$(&lt;/proc/sys/kernel/osrelease)/System.map"'
echo  >klogd/depend klogdl
echo  >klogdl/depend var
echo  >klogdl/dest -e '#! /bin/bash\n'
echo >>klogdl/dest -e '[ -d /var/log/multi/klog ] || {'
echo >>klogdl/dest -e '  mkdir -p /var/log/multi/klog'
echo >>klogdl/dest -e '  chown klogl:klogl /var/log/multi/klog'
echo >>klogdl/dest -e '  ln -s multi/klog/current /var/log/klog'
echo >>klogdl/dest -e '}'
echo >>klogdl/dest -e 'exec setuidgid klogl multilog t /var/log/multi/klog'

echo  >syslogd/depend -e 'devfsd\nsyslogc\nvar'
echo  >syslogd/daemon -e '#! /bin/bash\n'
echo >>syslogd/daemon -e "exec syslogd -n -m 0 -f $PWD/syslogd/config"
echo  >syslogd/config -e '*.*\t\t||/var/log/multi/syslog/fifo'
echo  >syslogc/depend syslogl
echo  >syslogc/source -e  '#! /bin/cat /var/log/multi/syslog/fifo'
echo  >syslogl/depend var
echo  >syslogl/dest -e '#! /bin/bash\n'
echo >>syslogl/dest -e '[ -d /var/log/multi/syslog ] || {'
echo >>syslogl/dest -e '  mkdir -p /var/log/multi/syslog'
echo >>syslogl/dest -e '  chown syslogl:syslogl /var/log/multi/syslog'
echo >>syslogl/dest -e '  ln -s multi/syslog/current /var/log/syslog'
echo >>syslogl/dest -e '}'
echo >>syslogl/dest -e '[ -p /var/log/multi/syslog/fifo ] ||'
echo >>syslogl/dest -e '    mkfifo --m 600 /var/log/multi/syslog/fifo'
echo >>syslogl/dest -e 'exec setuidgid syslogl multilog /var/log/multi/syslog'

chmod 754 */{start,stop,daemon,dest,source}

groupadd klogdl
useradd -g klogdl -s /bin/false -d / -s /bin/false klogdl
ln -s multi/klogd/current /var/log/klog
groupadd syslogl
useradd -g syslogl -s /bin/false -d / -s /bin/false syslogl
ln -s multi/syslogd/current /var/log/syslog
</code></pre>

<p>
devfsd needs to be told not to fork, so that depinit
can keep restarting it if required. The same is required for klogd, but I have
told it to output to stdout, and told depinit to pipe the output to klogdl.
klogdl runs multilog - a log rotator - as an ordinary user. If either klogd
or klogdl dies, depinit can restart it with the old pipe, so no data is lost.
</p><p>
syslogd cannot output to stdout, but it will output to a fifo, and that can
be copied to stdout by cat. depinit can do more complicated stuff with pipes
such as one daemon with pipes to several others, and daemons acting as filters
for other daemons. These features are there because they were simple to do.
I have not yet found a use for them.
</p><p>
gpm is badly screwed up. The only way to stop it from forking is to use
the debug option, and then you have to send all its trace output somewhere.
Depinit starts daemons with stdout and stderr connected to /dev/null, so
that is dealt with. If gpm was allowed to fork, execution of the daemon
script would continue and exit. Depinit would spot that the process id
assigned to gpm had terminated, so depinit would try to restart gpm.
On the second attempt gpm would spot that it is already running, and
terminate. Depinit would start it again, and again...
</p>

<h3>Loose ends</h3>
<p>
I think it is time to tidy up some loose ends, and sort out shutdown. The only
new depinit concept is a stop script without a start script. This is useful
for doing things just before shutdown.
</p><pre><code class=bash>

mkdir apm c_a_d dmesg extras hwclock log rw sig_int
echo >>default/depend -e 'extras'
echo  >extras/depend -e 'apm\nc_a_d\ndmesg\nhwclock'
echo  >apm/stop -e '#! /bin/bash\n\nexec modprobe apm'
echo  >c_a_d/depend proc
echo  >c_a_d/start -e '#! /bin/bash\n\necho 0 >/proc/sys/kernel/ctrl-alt-del'
echo  >dmesg/start -e '#! /bin/bash\n\nexec dmesg -n 4'
echo  >hwclock/depend rw
echo  >hwclock/stop -e '#! /bin/bash\n\nhwclock --systohc --utc'
mkdir -p /var/lib/misc/hwclock
mv /etc/adjtime /var/lib/misc/hwclock
ln -s /var/lib/misc/hwclock/adjtime /etc/adjtime
echo  >log/depend 'syslogd'
echo  >log/start -e '#! /bin/bash -e\n'
echo >>log/start 'depctl --syslog notice'
echo >>log/start 'exec depctl --no-stderr'
echo  >log/stop -e '#! /bin/bash -e\n'
echo >>log/stop -e 'depctl -F /dev/console'
echo >>log/stop -e 'depctl --stderr debug'
echo >>log/stop -e 'exec depctl --no-syslog'
echo >>sig_quit/depend 'log'
echo  >rw/depend -e 'devshm\ntmp\nvartmp\nutmp\nvarlock'
echo  >sig_int/start -e '#! /bin/bash\n\nexec depctl -Q'
chmod 754 */{start,stop}
</code></pre>

<p>
depinit needs the apm module loaded to reset, halt or shut down the computer.
By default, the &lt;ctrl&gt;&lt;alt&gt;&lt;delete&gt; button does not do
anything. c_a_d tells the kernel that it should send SIGINT to init when
&lt;ctrl&gt;&lt;alt&gt;&lt;delete&gt; is pressed. dmesg prevents trivial
kernel messages from being output to the console. I update the system clock
with ntp. hwclock can be used to correct systematic errors in the hardware
clock. The commands above make this work even when / is mounted read only.
</p><p>
hwclock probably does not need everything in rw, but I wanted to introduce
the concept because I think it is a good idea. rw depends on all the
standard read/write filesystems. The idea is to have a few standard service
names for portability. I should probably use ro to mean that /usr and /opt
are mounted, but I have been extending / with LVM rather than allocating
new partitions.
</p><p>
The log scripts switch logging to syslog after syslog has started, but
before sig_quit starts, so messages to the console do not hinder typing
the user name and password. Also, on shutdown, logging is diverted back
to the console when syslog dies. The debug setting is probably a bit
verbose for every day use, and can be trimmed back to something reasonable
like 'notice' or 'info'.
</p><p>
The final piece of magic is to do something with SIGINT. When depinit
receives SIGINT, it starts 'sig_int'. This uses depctl to tell depinit
to kill any children it did not create, stop everything, unmount everything
and turn the power off.
</p>

<h3>Network scripts</h3>
<p>
I have been trying to decide what to do about the network. A service
called network that starts all the interfaces looks useful as a standard
thing for network daemons to depend on. Traditionally, the data to set
up network interfaces goes in files in /etc/sysconfig/, and these files are
detected, sourced and interpreted by complex init scripts.
</p><p>
IMarrogantO init scripts have become excessively complex so they can handle
many interface setups from configuration files set up by GUI system
administration program. X is one of the things I like to delete from
secure servers, so I want the network configuration files to be easy to
use without an administration program. The simplest answer is to just
put the data in the depinit scripts, and call those scripts configuration data.
If you want to separate data from code, you could always put the
appropriate scripts in /etc/sysconfig/, and access them via symlinks.
</p><pre><code class=bash>

mkdir eth0 lo name network wall
echo >>default/depend network
echo  >eth0/depend wall
echo  >eth0/start -e  '#! /bin/bash -e\n'
echo >>eth0/start -n 'ifconfig eth0 192.168.189.1'
echo >>eth0/start -n     ' broadcast 192.168.189.255'
echo >>eth0/start -e     ' netmask 255.255.255.0'
echo >>eth0/start 'echo 0 >/proc/sys/net/ipv4/conf/eth0/accept_redirects'
echo >>eth0/start 'echo 0 >/proc/sys/net/ipv4/conf/eth0/accept_source_route'
echo >>eth0/start 'echo 2 >/proc/sys/net/ipv4/conf/eth0/rp_filter'
echo  >eth0/stop -e '#! /bin/bash -e\n\nexec ifconfig eth0 down'
echo  >lo/depend proc
echo  >lo/start -e  '#! /bin/bash -e\n\nexec ifconfig lo 127.0.0.1'
echo  >lo/stop  -e  '#! /bin/bash -e\n\nexec ifconfig lo down'
echo  >name/depend proc
echo  >name/start '#! /bin/hostname urusai.no.nezumi.plus.com'
echo  >network/depend -e 'eth0\nlo\nname\nwall'
echo  >wall/depend proc
cat   >wall/start &lt;&lt;'EOF'
#! /bin/bash -e

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
echo 1 > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
echo 0 > /proc/sys/net/ipv4/ip_forward
#    0 > /proc/sys/net/ipv4/tcp_ecn
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 0 > /proc/sys/net/ipv4/tcp_timestamps
echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects
echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route
echo 1 > /proc/sys/net/ipv4/conf/all/log_martians
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter

modprobe ip_tables
modprobe ip_nat_ftp
modprobe ip_conntrack_ftp
iptables-restore &lt;/etc/firewall
EOF
chmod 754 */{start,stop}
</code></pre>

<p>
Now that we have a network, I think it is time to put it to some good use.
</p><pre><code class=bash>

mkdir sshd{,l}
echo  >sshd/depend -e sshdl
echo  >sshd/source -e '#! /bin/bash\n'
echo >>sshd/source 'exec tcpserver -v 192.168.189.1 ssh /usr/sbin/sshd -i -e'
echo  >sshdl/depend -e 'rw\nnetwork'
echo  >sshdl/dest -e '#! /bin/bash\n'
echo >>sshdl/dest -e '[ -d /var/log/multi/sshd ] || {'
echo >>sshdl/dest -e '  mkdir -p /var/log/multi/sshd'
echo >>sshdl/dest -e '  chown sshdl:sshdl /var/log/multi/sshd'
echo >>sshdl/dest -e '  ln -s multi/sshd/current /var/log/sshd'
echo >>sshdl/dest -e '}'
echo >>sshdl/dest -e 'exec setuidgid sshdl multilog t /var/log/multi/sshd'
chmod 754 */{dest,source}
groupadd sshdl
useradd -g sshdl -s /bin/false sshdl
ln -s multi/sshd/current /var/log/sshd
</code></pre>

<p>
I have not started sshd by default. It would be simple to make
'daemons' depend on sshd, but I wanted to an example for an
a service started on the command line. sshd can be started
with 'depctl -s sshd', and stopped with 'depctl -k sshd'.
The stop command is a bit unsatisfactory because it leaves
sshdl running. A better stop command is 'depctl -r sshd',
because that takes out everything sshd explicitly depends on.
</p>

<hr>

<h1>Contents</h1>
<ul>
<li><a href="#introduction">Introduction</a>
<li><a href="#license">License</a>
<li><a href="#bugs">Bugs</a>
<li><a href="#downloads">Downloads</a>
<li><a href="#building">Building</a>
<li><a href="#manpages">Man-pages</a>
<li><a href="#setup">Setting it up</a>
</ul>
<p><a href="../index.html">Back</a></p>
<p>
  <a href="http://validator.w3.org/check/referer"><img
      src="valid-html40"
      alt="Valid HTML 4.0!" height="31" width="88"></a>
</p>

</body></html>