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
I realize you wrote this almost 2 years ago, but...
ReplyDeleteI'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.
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!
ReplyDeleteNot sure about that, but in STOP part, 'grep mydaemon', should mydaemon be echoHW ?
ReplyDelete