Thursday, May 6, 2010

Example Linux startup service for Python, Java, Ruby, Perl, or Script as a daemon process in init.d

This blog post provides sample Bash code for converting a Python, Java, Ruby, Perl, C++, script, or any other program as a Linux service that will start running at system startup. It is a script that is deployed in /etc/init.d like many other common service programs.

Note that there may be language specific ways of doing what you want. For example, there is an evolving Python package that is supposed to do this for you. At this writing, that package seems to be still in progress.

This solution has three components:

  • The actual program that needs to run as a service. In this example, it is a python (.py) program. But it really can be anything, including Java, Ruby, C++, or whatever
  • A wrapper Bash script that simply invokes the actual program. The only value add is that it writes the process identifier of the actual program to a file. You could possibly folder this functionality into the daemon itself, but I haven’t bothered to do that.
  • The daemon script. It is what is located in /etc/init.d and ultimately is responsible for starting and stopping the service.

Actual Program – echoHW.py

For this example, it does nothing real. It just spins the process:

import time

while 1 < 2:
    time.sleep(10)

Wrapper Script – echoHW.sh

This script simply invokes the actual program. It does also write out the process identifier to the file.

# Execute the actual python script
# The "$@" passes in any parameters into the python exectuable
# The '&' puts the process into background (as a daemon)
# The 'echo $! > mydaemon.pid'  write the process id to a file

python echoHW.py "$@" &
echo $! > mydaemon.pid

Daemon Script – mydaemon

This file must be copied into /etc/init.d.

#!/bin/bash
#
# mydaemon          Start/Stop any shell script
#
# chkconfig: 345 95 65
# description: mydaemon 
# processname: mydaemond
#

# ENVIRONMENT
# Edit these for your configuration

# Name for the service, used in logging
NAME=mydaemon

# Name of the user to be used to execute the service
SCRIPT_USER=plaird

# Example of how to pass paramters into the command
PARAM2=whatever

# In which directory is the shell script that this service will execute
MYDAEMON_SCRIPTS_DIR=/home/plaird/dev/linux

# Construct the command the will cd into the right directory, and invoke the script
MYDAEMON_COMMAND="cd $MYDAEMON_SCRIPTS_DIR; ./echoHW.sh 'param1' $PARAM2"

# How can the script be identified if it appears in a 'ps' command via grep?
#  Examples to use are 'java', 'python' etc.
MYDAEMON_PROCESS_TYPE=python

# Where to write the log file?
MYDAEMON_SVC_LOG_FILE=$MYDAEMON_SCRIPTS_DIR/mydaemon.log

# Where to write the process identifier - this is used to track if the service is already running
# Note: the script noted in the COMMAND must actually write this file
PID_FILE=$MYDAEMON_SCRIPTS_DIR/mydaemon.pid

# Load system specific optional arguments
# Create and populate this file with machine specific settings
if [ -f /etc/sysconfig/mydaemond ]; then
    . /etc/sysconfig/mydaemond
fi

# Is the service already running? If so, capture the process id
if [ -f $PID_FILE ]; then
    PID=`cat $PID_FILE`
else
    PID=""
fi

# SERVICE ENTRY POINTS (START/STOP)

# Start Command
start() {
    if [ "${PID}" != "" ]; then
        # Check to see if the /proc dir for this process exists
        if [ -a /proc/${PID} ]; then
            # check to make sure this is likely the running service
            ps aux | grep ${PID} | grep $MYDAEMON_PROCESS_TYPE >> /dev/null
            # If it is a process of the right type assume that it is mydaemon and just exit
            # otherwise remove the subsys lock file and start mydaemon
            if [ "$?" = "0" ]; then
                exit 1
            else
                echo "mydaemon lock file still exists, removing..."
                rm /var/lock/mydaemond
            fi
        else
            # The process running as pid $PID is not a process of the right type, remove subsys
            # lock and start mydaemon
            echo "mydaemon lock file still exists, removing..."
            rm /var/lock/mydaemond
        fi
    fi
    echo -n "Starting mydaemon: "   
    su - $SCRIPT_USER -c "/bin/sh -c \"$MYDAEMON_COMMAND > $MYDAEMON_SVC_LOG_FILE 2>&1\"" & RETVAL=$?
    sleep 3
    touch /var/lock/mydaemond
    exit 0
}

stop() {

    echo -n $"Stopping mydaemon: "
    if [ "${PID}" != "" ]; then
        echo -n "killing " $PID
        kill ${PID}
        for i in {1..30}
        do
            if [ -n "`ps aux | grep $MYDAEMON_PROCESS_TYPE | grep mydaemon `" ]; then
                sleep 1 # Still running, wait a second.
                echo -n .
            else
                # stopped
                rm -f /var/lock/mydaemond
                rm -f $PID_FILE
                echo
                exit 0
            fi
        done
    else
        echo "$NAME is not running"
        exit 1
    fi
    echo "Failed to stop in 30 seconds."
    kill -QUIT ${PID} # Request a thread dump so we can diagnose a hung shutdown
    exit 1
}

case "$1" in
  start)
      start
    ;;
  stop)
      stop
    ;;
  *)
    echo $"Usage: $0 {start|stop}"
    exit 1
esac

Installation

1. Start by copying the actual program and wrapper script somewhere on your machine in a permanent location.

2. Next, edit the mydaemon script to reflect the location where the program was installed. You may want to rename the file, and update the name of mydaemon inside the file.

3. Copy the mydameon script to /etc/init.d. As in:

sudo cp mydaemon /etc/init.d

4. Install the service. Installation will vary depending on your flavor of Linux. If you have chkconfig installed, it will be:

sudo chkconfig –-add mydaemon

If you are on a distro like Ubuntu, use update-rc.d, as in:

sudo update-rc.d mydaemon defaults

Service Usage

The service should now start on startup the computer. But you can invoke it manually as root:

sudo service mydaemon start

or:

sudo service mydaemon stop

Technorati Tags: ,,

3 comments:

  1. I realize you wrote this almost 2 years ago, but...
    I'm running Ubuntu 10.04, and I followed your instructions, but when I enter "sudo service mydaemon start", I just get "mydaemon: unrecognized service". I have verified that mydaemon is in /etc/init.d/, and also /etc/rc0.d, /etc/rc1.d, and so on.

    Any help would be appreciated. Thank you for your post.

    ReplyDelete
  2. This was an amazing tutorial! Well done, sir. Very well commented so a scripting noob like me was able to accomplish a goal in short order and gain some clarity on scripting in general. Thank you!

    ReplyDelete
  3. Not sure about that, but in STOP part, 'grep mydaemon', should mydaemon be echoHW ?

    ReplyDelete