Managing daemons on Mac OS X 10.4 (Tiger) with rc.local

Introduction
With the release of Mac OS X 10.4 (Tiger), Apple introduced a new way of controlling startup daemons using launchd.

Launchd
is a daemon/agent manager. To do this, it reads property lists located on system-wide and per-user directories. The property lists are XML files. Too bad for me, I'm quite an old school Unix lover that likes rc.local style plain text files; files that you can find or cook for yourself on every Unix-like operating system whether it's a *BSD or SysV style one. I think XML is an overkill for handling a task as simple as starting/stopping daemons. And don't get me started about a change in the DTD.

What's the solution then to revert to ol' style rc.local? It's simple as creating a property list for launchd to launch an rc.local style script. This rc.local style script will then startup the other daemons such as postfix, dovecot etc.

Speaking about postfix and dovecot, let's use them for our examples below.

Step 1: Create per-daemon scripts
First we start by creating a shell script for each daemon we want to control. For postfix, a script like the following one will do the trick:
$ cat /Data/local/etc/rc.d/postfix
#!/bin/ksh
# $DocIsland: postfix,v 1.1 2005/06/02 20:30:12 saad Exp $
# postfix -- Control Postfix
postfix_cmd="/usr/sbin/postfix"
case $1 in
start)
echo -n "starting postfix ..."
$postfix_cmd start >/dev/null 2>&1 && echo done
;;
stop)
echo -n "stopping postfix ..."
$postfix_cmd stop >/dev/null 2>&1 && echo done
;;
restart)
$0 stop;
$0 start;
;;
*)
echo "usage: $0 (start|stop|restart)"
exit 1
;;
esac
exit 0

For dovecot, we use something like:

$ cat /Data/local/etc/rc.d/dovecot
#!/bin/ksh
# $DocIsland: dovecot,v 1.1 2005/06/02 20:30:12 saad Exp $
# dovecot -- Control Dovecot
dovecot_cmd="/Data/local/sbin/dovecot"
killall_cmd="/usr/bin/killall"
case $1 in
start)
echo -n "starting dovecot ..."
$dovecot_cmd >/dev/null 2>&1 && echo done
;;
stop)
echo -n "stopping dovecot ..."
$killall_cmd dovecot >/dev/null 2>&1 && echo done
;;
restart)
$0 stop;
$0 start;
;;
*)
echo "usage: $0 (start|stop|restart)"
exit 1
;;
esac
exit 0

At this point we have our two scripts in /Data/local/etc/rc.d. Make sure they are executables and that they work by running them before carrying on.

Step 2: Create rc.local
Next we create an rc.local file. Here is an example:

$ cat /Data/local/etc/rc.local
#!/bin/ksh
# $DocIsland: rc.local,v 1.2 2005/06/02 19:36:39 saad Exp $
# rc.local -- Emulate rc.local behavior as found in OpenBSD or other
# BSD systems. This file is called a boot time by Mac OS X's launchd via
# an XML property list located in /System/Library/LaunchDaemons/local.rc.plist
locald="/Data/local/etc/rc.d"
# Start Postfix
if [ -x $locald/postfix ]; then
$locald/postfix start
fi
# Start Dovecot
if [ -x $locald/dovecot ]; then
$locald/dovecot start
fi

Before continuing, verify that you can start postfix and dovecot by running this script.

Step 3: Create the XML property list
Now we want to tell launchd to call the rc.local script we just created at boot time. To do this, we need to create an XML property list. This list, which is in fact a file, is /System/Library/LaunchDaemons/local.rc.plist. /System/Library/LaunchDaemons is a system-wide directory.

The XML property list looks like this:

$ cat /System/Library/LaunchDaemons/local.rc.plist




Label
local.rc
ProgramArguments

/Data/local/etc/rc.local

RunAtLoad




Conclusion
As you can see, we used three simple steps to "revert" back to our old school, nonetheless reliable, way of managing daemons. Besides its reliability, it has the advantage of portability to other systems as well.

For more information about launchd, see the manual pages for launchd(8), launchctl(1), and launchd.plist(5).

Should you have any question/comment about this hack, please send them to saad@docisland.org.
|