Managing Linux daemons with init scripts

26359

When you install a new Linux server distribution, you can often install all of the daemons you’ll need to run on that machine at install time. Distribution vendors present a “ready to go” distribution by supplying initialization scripts for all of the services you might run. But what happens if you’re building from source, and no init script is supplied? What if you’re writing the source and haven’t ever built an init script? Here are a few ways to cope when you’re faced with this challenge.

You can handle initialization in several different ways. One option is to place the commands used to start up a daemon into /etc/rc.d/rc.local file. This method is very simple: Cut and paste the instructions the README gives to start the daemon, and that’s your initialization. Unfortunately, this method goes against what most systems are set up to do by default, which is to use init scripts.

 

 

Using the rc.local method also forces you to forgo some flexibility afforded by init scripts, or to hack your own functionality into a wrapper startup script that is called in rc.local. I’ve seen this done, but it’s largely a duplication of effort.

Another method is to start your daemon directly from the /etc/inittab file. More than a few administrators use this technique, but I don’t recommend it for an environment of several servers (or more) maintained by several administrators, for a couple of reasons.

Reason one is consistency. If possible, you should manage every daemon on every server using the same procedure. The only thing that should change in the commands run is the name of the daemon.

The second reason, which relates to consistency, is manageability in the event of your demise. If you’re the only administrator, then this is a moot point. However, unless you require every administrator to know the intricacies of every daemon running in the environment, running things out of inittab is not a great idea. Manageability, as a goal of a systems management group, is not something that a single admin can handle. Manageability spans the entire group responsible for the welfare of the systems and services they maintain. Of course, good admins also document procedures.

Init scripts are your friend because they represent an efficient use of the resources given to an administrator with which to manage the startup and shutdown of daemons. Distribution vendors have largely standardized on initialization scripts around which to build their daemon management tools, and they’ve put a great deal of work into make managing daemons as simple as possible. Even if you have an init-scriptless daemon, putting together an init script to be managed by the normal system tools and procedures is easy.

Let’s go over the process of creating an init script. You’ll see how easy this procedure is, and discover functionality that makes you rethink how you manage daemons.

Creating your own init script

I’m going to review this process based on a real-life experience. I’m using the newly released Fedora Directory Server on production Red Hat Advanced Server machines. I’ve downloaded and installed the binary RPM packages and tested that it works. The next step is to make sure it’s manageable in a way that is consistent with everything else on the system. What to do?

I created my own init script. The first thing I did was look for something I could use as a template. If you know that your daemon has similar demands to another daemon, you can use the init script from that daemon as a template. In this case, I simply copied the init script template that comes with my Linux distribution, which is in /usr/share/doc/initscript package/sysvinitfiles. Here’s what I took from that file:

#!/bin/bash
#
#       /etc/rc.d/init.d/
#
#
#
#
#

# Source function library.
. /etc/init.d/functions



start() {
        echo -n "Starting : "

        touch /var/lock/subsys/
        return
}

stop() {
        echo -n "Shutting down : "

        rm -f /var/lock/subsys/
        return
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)

        ;;
    restart)
        stop
        start
        ;;
    reload)

tart)

        [ -f /var/lock/subsys/ ] && restart || :
    probe)

        ;;
    *)
        echo "Usage:  {start|stop|status|reload|restart[|probe]"
        exit 1
        ;;
esac
exit $?

As you can see, all of the structure for an init script is already in place, and the functions and other bits of code you’ll need are stubbed out, just waiting for you to fill in the blanks.

The most important line sits at the top of the file, as unassuming as can be. It’s the one that sources the file /etc/init.d/functions. This file is filled with simple but useful bash shell functions that are ready for use in your init script. If you’ve never noticed this file, read it! Even if you don’t use the stuff, it will give you insight into how your system does things, and it can explain why some boot time or shutdown time errors occur.

Here’s my finished init script, which is currently in use on my almost-production servers:

#!/bin/bash
#
#       /etc/rc.d/init.d/ns-slapd
# ns-slapd      This shell script takes care of starting and stopping
#               ns-slapd (the Fedora Directory Server)
#
# Author: Brian Jones 
 This e-mail address is being protected from spambots. You need JavaScript enabled to view it
 
#
# chkconfig: 2345 13 87
# description: ns-slapd is the Fedora Directory Service daemon. 
# FDS can serve as a repository for (and, subsequently, a source of) 
# data for most of the resources listed in /etc/nsswitch.conf, such as 
# passwd or group entries.

# Source function library.
. /etc/init.d/functions

SLAPD_HOST=`hostname -a`
SLAPD_DIR=/opt/fedora-ds/bin/slapd/server
PIDFILE=$SLAPD_DIR/logs/pid
STARTPIDFILE=$SLAPD_DIR/logs/startpid

if [ -f /etc/sysconfig/ns-slapd ]; then
        . /etc/sysconfig/ns-slapd
fi


start() {
        echo -n "Starting Fedora Directory Server: "
        if [ -f $STARTPIDFILE ]; then
                PID=`cat $STARTPIDFILE`
                echo ns-slapd already running: $PID
                exit 2;
        elif [ -f $PIDFILE ]; then
                PID=`cat $PIDFILE`
                echo ns-slapd already running: $PID
                exit 2;
        else
                cd $SLAPD_DIR
                daemon  ./ns-slapd $OPTIONS
                RETVAL=$?
                echo
                [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ns-slapd
                return $RETVAL
        fi

}

stop() {
        echo -n "Shutting down Fedora Directory Server: "
        echo
        killproc ns-slapd
        echo
        rm -f /var/lock/subsys/ns-slapd
        return 0
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        status ns-slapd
        ;;
    restart)
        stop
        start
        ;;
    *)
        echo "Usage:  {start|stop|status|restart}"
        exit 1
        ;;
esac
exit $?

The three functions I use in my script are killproc, status, and daemon. You have to be careful that you know both what the functions do, and what your application’s default behavior is. For example, the function that attempts to grab the PID of the running process, pidofproc, assumes that the process writes a pid file somewhere under /var/run. For all other daemons on my system, this works, and I’ve requested that the Directory Server folks do this as well. (Right now there are pid files, but they’re managed within the directory hierarchy where the daemon lives, under /opt/fedora-ds.)

Also, notice that this script will work with the chkconfig command, as denoted by the line reading chkconfig: 2345 13 87. After that, just run chkconfig add scriptname. This script also works with the service command on systems that have it (including Fedora and, I think, SUSE), which is a useful tool in the sysadmin’s arsenal. It serves as a shortcut — instead of typing cd /etc/init.d; ./mydaemon status, or /etc/init.d/mydaemon status, you can type service mydaemon status from whatever your working directory happens to be.

One last note about this simple example concerns the line that reads . /etc/sysconfig/ns-slapd. If the file exists, it will be used to pass arguments to the daemon when I start it. I’ve used this file to take the default arguments the server normally starts with and put them in their own file; this keep admins from having to edit init scripts, and lets them easily add items to the file later, just as we add options to other daemons on the system. My file contains a single line, which reads OPTIONS="-D /opt/fedora-ds/slapd-ldap -i /opt/fedora-ds/slapd-ldap/logs/pid -w /opt/fedora-ds/slapd-ldap/logs/startpid". This option line gets tagged right onto the end of the command that starts the daemon.

This whole process seems like it should have been lengthy, but it took only about 30 minutes, and that includes some troubleshooting related to Fedora Directory Server’s unusually self-contained install structure. With a simple daemon that more or less follows the rules, it shouldn’t take you more than 10 minutes to integrate it into the mechanisms used to manage services on your Linux server.