Dæmon Init Scripts on Linux

Russell Bateman
16 Feburary 2010
last update:

Linux has a peculiar, but highly orthogonal mechanism for installing and maintaining services via dæmon processes as compared to other Unix platforms, what I refer to as the "dæmon init script."

The installation of this script, whether you do it by hand or using a package manager entry (not discussed here), amounts very simply to:

  1. Copy the dæmon itself somewhere it's going to live, say /opt/mycompany/sbin/mylittledaemon.
  2.  
  3. Copy the dæmon script, what this discussion is mostly about, to /etc/init.d. Now, depending on your flavor of Linux, this path can actually vary. Initially, I will not ferret out those differences for you here. My present concern has been Red Hat and SuSE distros. Note that, in my understanding, Ubuntu allows this, but it's no longer their best practice as they've moved on to something called the "Upstart event-based init dæmon."
  4.  
  5. Invoke chkconfig at some point during installation of your software. This "registers" your service with Linux so that it will be relaunched at boot time of each of the appropriate run levels. Here, we're configuring to run mylittledaemon at both user levels (console and GUI).
    # chkconfig --levels 35 mylittledaemon on

    This is very sophisticated on Red Hat: To see how the service is configured, type:

    # chkconfig --list mylittledaemon mylittledaemon 0:off 1:off 2:off 3:on 4:off 5:on 6:off

    The configuration is echoed to the console.

    For SLES, this command works differently. Be certain to be inside the subdirectory where the binary is located (so it will find the binary—not the init script), then issue the command shown below so that it runs whenever the run level reaches 3 (which will keep it running on level 5 also).

    # chkconfig mylittledaemon 3

    To verify on SLES that it's indeed configured to run, type:

    # chkconfig -t mylittledaemon mylittledaemon 3

    The configuration is echoed to the console.

    I have so far found SLES to be a bit squirrelly. Sometimes I issue the command to configure only to get a bunch of errors back. Reissuing it makes it work if the second time no errors come back.


Back to the script...

The first few commented lines in the script are essential to its operation; they must be set, in particular, the "chkconfig:" stuff. This said, and without further ado, I'm going to let the script code and comments speak for the rest of the story here. Assume that DAEMON_HOME is set before this script is run. This could be a stretch, however, given you must run this as root and you wouldn't want to invade root's environment.

# DAEMON_HOME=etretatlogiciels/mylittledaemon
 

Dæmon should be pronounced /DEE mun/.

Say what you will, but I'm here to tell you that back in my 1980s UNIX days, everyone pronounced it that way. Enter Linux and a younger crowd trying too hard and you begin to get some saying /DAY mun/ which is over the top. If you really want to affect a correct Greek or Latin pronunciation, say it /DIE mown/.

In support of my assertion I would point out that had it been pronounced otherwise, then no one would have been tempted to use little devils in their illustrations back in the day. They were sort of like the ubiquitous Linux penguin of our day.

There are two origins of this word (my minor was in Greek, Latin and Linguistics). In English, demon is older and comes from French. The French word comes from Latin, daemon, itself from Greek, daimôn. Romans pronounced the word just as did the Greeks (because, after all, no Roman citizen worth his salt could fail to speak Greek.) The "new" word, dæmon, is an "erudite borrowing" which means that its creators went directly back to Greek (or Latin) and re-transcribed it into English, thus dæmon. It still means "demon" and there is little reason to try a new pronunciation.

Incidentally, this word has roughly the same meaning as Arabic, djin, which gives us genie in English. When the UNIX guys originally chose this term for small, usually ever-present applications and services running in the background, that's the image they had rather than a symbol of evil. Maybe by choosing this alternate spelling they were just trying to lose the Medieval Biblical baggage. Of course, the word has that other meaning and, just as I have done here, lends a lot of fun for illustrations.

 

Here's the script...

#!/bin/bash # chkconfig: 35 20 80 # description: My little daemon manager # processname: mylittledaemon # ============================================================================= DAEMON_COPYRIGHT="Copyright (c) 2010 by Etretat Logiciels, LLC. All rights reserved." DAEMON_VERSION=1.0.0 # # NAME=mylittledaemon # Start/stop/restart/etc. for my little daemon daemon. # # During my little daemon manager installation, the following command # is issued: # chkconfig --levels 35 mylittledaemon on # # Ordinarily, the license and log files are these and aren't changed. # Whatever the case, $DAEMON_HOME must be set and accurate before this # script is executed. LICENSE_FILE=/opt/$DAEMON_HOME/licenses/daemon_ts.lic LOGFILE_PATH=/var/log/$DAEMON_HOME/mylittledaemon.log # # These are the possible options, ordinarily never passed, to this # script. These are pretty much the only, possible options. OPTIONS="-c $LICENSE_FILE -l $LOGFILE_PATH" # # Platform support # Deal with what platform we're running on: we claim to support Red # Hat Enterprise Linux and SuSE Linux Enterprise Server. We'll be # doing Solaris later, but there will be no sign of that in here since # the daemon init script is a purely Linux phenomenon. if [ -f "/etc/rc.d/init.d/functions" ]; then PLATFORM="RHEL" . /etc/rc.d/init.d/functions elif [ -f /etc/rc.status ] ; then PLATFORM="SLES" . /etc/rc.status else echo "Platform is unsupported." exit 0 fi # # (Note: this document is entabbed at 2.) # ============================================================================= TRUE=1 FALSE=0 EXIT_STATUS=0 daemon_running=$FALSE DoUsage() { echo "Usage: /etc/init.d/mylittledaemon [-c] [-l] {start|stop|restart|status}" echo echo "Options" echo " -c license-file-path New path to license file. Default is" echo " $LICENSE_FILE" echo " -l logfile-path New location of log file. Default is" echo " $LOGFILE_PATH" echo echo "Note: it is unusual to provide any command-line options." echo "See also manpage for mylittledaemon." echo DoVersion } DoVersion() { echo "mylittledaemon: version $DAEMON_VERSION" echo $DAEMON_COPYRIGHT } SeeIfDaemonIsRunning() { # Determine whether the daemon already happens to be running since it's # important depending on what operation this script is to undertake. Note: # we don't have to test this ps command because we know both Red Hat and SuSE # support it. If other Linux platform support is added, we might have to use # a different one and parse it differently too. PS_CMD="ps -e -o pid,args" # Get the list of processes whose status contains "mylittledaemon" and filter # out the ones due to grep, [g]vi[m], this script executing, etc. (mostly # happening during initial bring up and testing) because they don't count as # instances of this daemon running. # find daemon no greps no sh -x no editing proc_list=`$PS_CMD | grep $NAME | grep -v grep | grep -v sh | grep -v vi` # Some presumably real instances to sort through (almost certainly never more # than one single process). Here's the business end of this function: # producing a process id or a list of process ids. procs=`echo $proc_list | awk '{print $1}'` # If we're left with anything, then it means the daemon is running! if [ -n "$procs" ]; then daemon_running=$TRUE else daemon_running=$FALSE fi } start() { echo -n "Starting $NAME: " case "$PLATFORM" in "RHEL") daemon ./$NAME $OPTIONS EXIT_STATUS=$? ;; "SLES") /sbin/startproc ./$NAME $OPTIONS rc_status -v ;; esac echo [ $EXIT_STATUS -eq 0 ] && touch /var/lock/subsys/$NAME; } stop() { echo -n "Stopping $NAME: " case "$PLATFORM" in "RHEL") killproc $NAME ;; "SLES") /sbin/killproc $NAME ;; esac EXIT_STATUS=$? # at very least, Red Hat wants lock file removed, so we'll do it for all rm -f /var/lock/subsys/$NAME echo if [ "$PLATFORM" = "SLES" ]; then rc_status -v fi } getstatus() { echo -n "Checking $NAME: " case "$PLATFORM" in "RHEL") status $NAME EXIT_STATUS=$? ;; "SLES") /sbin/checkproc ./$NAME rc_status -v EXIT_STATUS=$? ;; esac } # ----------------------------------------------------------------------------- # Script entry point... # ----------------------------------------------------------------------------- # Switch to the daemon's home directory to do all of this... cd /opt/$DAEMON_HOME/sbin if [ $? -ne 0 ]; then echo "Unable to find $NAME's directory." exit 1 fi # Ensure the daemon exists... if [ ! -x "$NAME" ]; then echo "$NAME could not be found." exit 1 fi # Enforce that only root can do this... if [ "`id | sed 's/uid=\([0-9]*\).*/\1/'`" -ne 0 ] ; then echo "This script can only be run by root." exit fi # Cover the modern options: either way, we don't continue on. We assume a # minimal level of competence here: we're not idiot-proofing this script. If # they want help or the version, it will be the first and probably only thing # passed. arg=$1 if [ "$arg" = "--help" ]; then DoUsage exit 0 elif [ "$arg" = "--version" ]; then DoVersion exit 0 fi # Parse command-line arguments if any. There would not usually be any. Skip # gracefully over any invalid arguments if not too goopy. while getopts "hc:l:v" opt; do case $opt in h) DoUsage ; exit 0 ;; v) DoVersion ; exit 0 ;; c) shift ; LICENSE_FILE=$1 ; shift ;; l) shift ; LOGFILE_PATH=$1 ; shift ;; *) echo "WARNING: Ignoring invalid option ($1)" ;; esac done operation=$1 EXIT_STATUS=0 # Determine what platform (OS) is underneath us and check to see if the daemon # is already running. SeeIfDaemonIsRunning # Here's what we really came to do... case "$operation" in start) # ----------------------------------------------------------------- if [ $daemon_running -eq $FALSE ]; then start else echo "$NAME appears already to be running." if [ "$PLATFORM" = "SLES" ]; then rc_status -s fi fi ;; stop) # ----------------------------------------------------------------- if [ $daemon_running -eq $TRUE ]; then stop else echo "$NAME does not appear to be running." if [ "$PLATFORM" = "SLES" ]; then rc_status -s fi fi ;; restart) # ----------------------------------------------------------------- if [ $daemon_running -eq $TRUE ]; then stop sleep 1 fi start EXIT_STATUS=$? ;; status) # ----------------------------------------------------------------- getstatus ;; *) # ----------------------------------------------------------------- echo "Usage: $NAME {start|stop|restart|status}" exit 1 esac case "$PLATFORM" in "RHEL") exit $EXIT_STATUS ;; "SLES") rc_exit ;; esac # vim: set tabstop=2 shiftwidth=2 noexpandtab: