Managing FastCGI Processes with an init script

While there are arguments for managing your FastCGI processes with a services manager like daemontools or perhaps upstart, there's a certain familiarity and simplicity to init scripts.

Wait, did I say simplicity? Maybe not... simple init scripts have a tendency to blow up in lots of interesting ways. Below is a not-so-simple script that I've developed for my own deployments that covers a lot of the bases and seems to work well, together with notes on usage.

Requirements

This script depends on... well not Debian exactly, but start-stop-daemon, which is rarely seen outside of Debian-like systems. It also depends on an LSB-compliant system. If you come up with a port to a system that does things differently, go ahead and post it below in its own section. Most importantly, it depends on FCGI::ProcManager 0.18, as previous versions don't handle being daemonized properly.

Good Stuff

This script supports:

Configuration

Note: If you have a slow server, the script may report that it has failed to start the daemon, even though it does eventually start successfully. This is because the part of the script that checks if the daemon is running gives up too soon. To fix this, just increase the delay in the _start() function from sleep 1 to sleep 2 (or greater).

The Repository

The latest version of this script can be found at https://github.com/arodland/cat-fcgi-init.

The Script

#!/bin/sh
# Start a Catalyst app under FastCGI
# Copyright (c) 2009-2010, Andrew Rodland
# See LICENSE for redistribution conditions.
### BEGIN INIT INFO
# Provides: webapp
# Required-Start: $local_fs $network $named
# Required-Stop: $local_fs $network $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: A Catalyst Application
### END INIT INFO . /lib/lsb/init-functionsAPPNAME=MyApp
APPDIR=/home/myapp/MyApp
UNIXNAME=$(echo $APPNAME | perl -pe 's/::/_/;$_=lc')
PROCS=5
SOCKET=127.0.0.1:3001
# Leave these unset and we won't try to setuid/setgid.
USER=myapp
GROUP=myapp
# Set this if you have more than one instance of the app and you don't want
# them to step on each other's pidfile.
PIDSUFFIX=# local::lib path, if you want to use it.
LOCALLIB=if [ -f "/etc/default/"$UNIXNAME ]; then
    . "/etc/default/"$UNIXNAME
fiif [ $(id -u) -eq 0 ] ; then
    PIDDIR=/var/run/$UNIXNAME
    mkdir $PIDDIR >/dev/null 2>&1
    chown $USER:$GROUP $PIDDIR
    chmod 775 $PIDDIR
else
    PIDDIR=/tmp
fiPIDFILE=$PIDDIR/$UNIXNAME${PIDSUFFIX:+"-$PIDSUFFIX"}.pidif [ -n "$LOCALLIB" ] ; then
    eval `perl -I"$LOCALLIB/lib/perl5" -Mlocal::lib="$LOCALLIB"`
ficheck_running() {
    [ -s $PIDFILE ] && kill -0 $(cat $PIDFILE) >/dev/null 2>&1
}check_compile() {
    if [ -n "$USER" ] ; then
        if su $USER -c "cd $APPDIR ; perl -Ilib -M$APPNAME -ce1" ; then
            return 0
        fi
        return 1
    else
        if ( cd $APPDIR ; perl -Ilib -M$APPNAME -ce1 ) ; then
            return 0
        fi
        return 1
    fi
}_start() {
    start-stop-daemon --start --quiet --pidfile $PIDFILE --chdir $APPDIR \
    ${USER:+"--chuid"} $USER ${GROUP:+"--group"} $GROUP --background \
    --startas $APPDIR/script/${UNIXNAME}_fastcgi.pl -- \ 
    -n $PROCS -l $SOCKET -p $PIDFILE    for i in 1 2 3 4 5 6 7 8 9 10; do
        sleep 1
        if check_running ; then
            return 0
        fi
    done
    return 1
}start() {
    log_daemon_msg "Starting $APPNAME" $UNIXNAME
    if check_running; then
        log_progress_msg "already running"
        log_end_msg 0
        exit 0
    fi    rm -f $PIDFILE 2>/dev/null    _start
    log_end_msg $?
    return $?
}_stop() {
    start-stop-daemon --stop --user $USER --quiet --oknodo --pidfile $PIDFILE \
    --retry TERM/5/TERM/30/KILL/30 \
    || log_failure_message "It won't die!"
}stop() {
    log_daemon_msg "Stopping $APPNAME" $UNIXNAME    _stop
    log_end_msg $?
    return $?
}restart() {
    log_daemon_msg "Restarting $APPNAME" $UNIXNAME    check_compile && _stop && _start
    log_end_msg $?
    return $?
}# See how we were called.
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart|force-reload)
        restart
        ;;
    check|check-compile)
        check_compile
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|check}"
        exit 1
esac
exit $?