Mikko Kortelainen

Init Script for Daemonizing Non-Forking Processes

Sometimes you have an executable which does not fork to the background, but you need to control it with init scripts, so that it does indeed run in the background. Here's a pretty generic init script for that. It allows you to configure these:

DAEMON_NAME="My Little Daemon"
DAEMON_EXECUTABLE="/opt/my_daemon/my_daemon"
DAEMON_OPTIONS=""
DAEMON_HOMEDIR="/opt/my_daemon"
DAEMON_PIDFILE="/var/run/my_daemon.pid"
DAEMON_LOGFILE="/var/log/my_daemon.log"
INIT_SLEEPTIME="2"

The script changes directory to the DAEMON_HOMEDIR, runs DAEMON_EXECUTABLE with DAEMON_OPTIONS and saves the new process id to the DAEMON_PIDFILE file. Remember to use different pidfile for every daemon.

The script also checks whether the process is up and running after INIT_SLEEPTIME seconds. If not, it will fail. It will do the check after stopping as well.

All stdout/stderr output is sent to DAEMON_LOGFILE. If you don't want to log the output, set it to "/dev/null".

You can control init scripts with this part in the beginning of the file:

### BEGIN INIT INFO
# Provides:          my_daemon
# Required-Start:    $network
# Required-Stop:     $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Runs the non-forking program in the background
### END INIT INFO

Ubuntu/Debian update-rc.d will use those defaults to install it. So after you drop the script under /etc/init.d with the name my_daemon, you can install it using this:

sudo update-rc.d my_daemon defaults

To disable temporarily:

sudo update-rc.d my_daemon disable

To re-enable:

sudo update-rc.d my_daemon enable

To remove:

sudo update-rc.d -f my_daemon remove

This script does not change running user id from root to something else, so be careful with it.

To start up the daemon, run:

sudo service my_daemon start

To stop:

sudo service my_daemon stop

Restart:

sudo service my_daemon restart

See status:

sudo service my_daemon status

The Script

Enough explanation. Here's the script:

#!/bin/sh

### BEGIN INIT INFO
# Provides:          my_daemon
# Required-Start:    $network
# Required-Stop:     $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Runs the non-forking program in the background
### END INIT INFO

# Defaults
DAEMON_NAME="My Little Daemon"
DAEMON_EXECUTABLE="/opt/my_daemon/my_daemon"
DAEMON_OPTIONS=""
DAEMON_HOMEDIR="/opt/my_daemon"
DAEMON_PIDFILE="/var/run/my_daemon.pid"
DAEMON_LOGFILE="/var/log/my_daemon.log"
INIT_SLEEPTIME="2"

# Defaults can be overridden in this file
DAEMON_DEFAULTS_FILE="/etc/default/my_daemon"

PATH=/sbin:/bin:/usr/sbin:/usr/bin

# Load alternate configuration if exists
test -f $DAEMON_DEFAULTS_FILE && . $DAEMON_DEFAULTS_FILE

. /lib/lsb/init-functions

# Usually no need to edit below this point. Just have these ready:
#
# * DAEMON_EXECUTABLE - full path to the executable
# * DAEMON_OPTIONS    - options to pass to the executable
# * DAEMON_NAME       - a decriptive name
# * DAEMON_HOMEDIR    - place where to cd before running
# * DAEMON_PIDFILE    - pid file name
# * DAEMON_LOGFILE    - log file name
# * INIT_SLEEPTIME    - how long to wait for startup and shutdown
#
# The rest will be taken care of. Executable is run with "nohup", so no
# need to fork.

is_running () {
  # Test whether pid file exists or not
  test -f $DAEMON_PIDFILE || return 1

  # Test whether process is running or not
  read PID < "$DAEMON_PIDFILE"
  ps -p $PID >/dev/null 2>&1 || return 1

  # Is running
  return 0
}

root_only () {
  if [ "$(id -u)" != "0" ]; then
    echo "Only root should run this operation"
    exit 1
  fi
}

run () {
  if is_running; then
    PID="$(cat $DAEMON_PIDFILE)"
    echo "Daemon is already running as PID $PID"
    return 1
  fi

  cd $DAEMON_HOMEDIR

  nohup $DAEMON_EXECUTABLE $DAEMON_OPTIONS >>$DAEMON_LOGFILE 2>&1 &
  echo $! > $DAEMON_PIDFILE
  read PID < "$DAEMON_PIDFILE"

  sleep $INIT_SLEEPTIME
  if ! is_running; then
    echo "Daemon died immediately after starting. Please check your logs and configurations."
    return 1
  fi

  echo "Daemon is running as PID $PID"
  return 0
}

stop () {
  if is_running; then
    read PID < "$DAEMON_PIDFILE"
    kill $PID
  fi
  sleep $INIT_SLEEPTIME
  if is_running; then
    while is_running; do
      echo "waiting for daemon to die (PID $PID)"
      sleep $INIT_SLEEPTIME
    done
  fi
  rm -f "$DAEMON_PIDFILE"
  return 0
}

case "$1" in
  start)
    root_only
    log_daemon_msg "Starting $DAEMON_NAME"
    run
    log_end_msg $?
    ;;
  stop)
    root_only
    log_daemon_msg "Stopping $DAEMON_NAME"
    stop
    log_end_msg $?
    ;;
  restart)
    root_only
    $0 stop && $0 start
    ;;
  status)
    status_of_proc \
      -p "$DAEMON_PIDFILE" \
      "$DAEMON_EXECUTABLE" \
      "$DAEMON_NAME" \
      && exit 0 \
      || exit $?
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|status}"
    exit 1
  ;;
esac