This post is older than a year. Consider some information might not be accurate anymore.
I had to write recently some shell scripts that may be used for LSB like init.d services. It was a quite good experience and fresh up of my bash scripting skills.
One of the major requirements:
- The application can only be started once. A second instance is not allowed.
- To shutdown, we have to kill the process.
Here are my basic steps.
Define colors
I define some colours to illustrate the importance or status of some text.
COLOR_SUCCESS="\\033[1;32m"
COLOR_FAILURE="\\033[1;31m"
COLOR_WARNING="\\033[1;33m"
COLOR_NORMAL="\\033[0;39m"
Remember process id
By starting the application, we need to remember the process id (pid), that has be assigned. This pid will be stored in the file, named pidfile with the extension .pid
. I recommend to use a variable for the location of the pidfile.
# place pid according to FHS in /var/run is not possible,
# since it is not started as root
pidfile="$SCRIPT_HOME/java_app.pid"
If the application is started as root, put the pid under /var/run
. If the application is running as different user and not started by root, you have to use a custom location.
Write pidfile
In the start function, execute the program.
start() {
echo -e "Start $name using classpath $CLASSPATH \n"
${JAVA_HOME} -Djava.library.path=bin \
-classpath $CLASSPATH \
net.cinhtau.Starter logback.xml > /dev/null 2> "${SCRIPT_HOME}/$tan.err" &
# Generate the pidfile from here. If we instead made the forked process
# generate it there will be a race condition between the pidfile writing
# and a process possibly asking for status.
echo $! > $pidfile
}
The special parameter $!
expands to the process ID of the most recently executed background (asynchronous) command. This pid is stored to the pidfile. See Bash Beginners Guide for more information about special parameters.
Check if process is still alive
The status function is essential for checking if the application, or more correctly the process id is still alive.
status() {
if [ -f "$pidfile" ]; then
pid=`cat "$pidfile"`
if [ -e "/proc/$pid" ] ; then
# process by this pid is running.
# It may not be our pid, but that's what you get with just pidfiles.
return 0
else
return 2 # program is dead but pid file exists
fi
else
return 3 # program is not running
fi
}
If the pidfile exists, we look for the pid under /proc
, the process pseudo filesystem.
Stop application based on pidfile
To stop the application, we have to kill the process. If the application is successfully killed, we have to remove the pidfile.
stop() {
# Try a few times to kill TERM the program
if status; then
pid=`cat "$pidfile"`
echo "Killing $name (pid $pid)"
kill -HUP $pid
# Wait for it to exit.
for i in `seq 1 5`; do
echo "Waiting $name (pid $pid) to die..."
status || break
sleep 1
done
if status ; then
echo "$name stop failed; still running."
else
echo "$name stopped."
rm $pidfile
fi
fi
}
Use pidfile for control flow
This pidfile can now be used for the control flow, e.g. don’t start the application a second time, if the application is already running.
echo_success() {
echo -n -e $"[$COLOR_SUCCESS OK $COLOR_NORMAL]"
echo -ne "\r"
return 0
}
case "$1" in
start)
if [ -e $pidfile ] ; then
pid=$(cat $pidfile)
echo -n -e "Application Component is already running under $COLOR_WARNING $pid $COLOR_NORMAL \n"
else
start
fi
;;
stop)
stop ;;
force-stop) force_stop ;;
status)
status
code=$?
if [ $code -eq 0 ]; then
echo_success && echo -e "\t $name is running, process `cat $pidfile`"
else
echo -e " \t $name is $COLOR_WARNING not running $COLOR_NORMAL"
fi
exit $code
;;
restart)
stop && start
;;
*)
echo "Usage: cinhtau.sh {start|stop|force-stop|status|restart}"
exit 3
;;
esac
exit $?