From the Terminal
Making Any Terminal Command Into a Background Service Daemon
This guide will show you to make a bash script that is self aware about it's location on the file system and therefore able to be used as a global terminal command from any current directory.
It will have the ability to take arguments as commands and have the ability to act on those commands with a set of basic daemon related functions such as start, stop, restart, status, version, and help.
The bash script will also have the ability to save a .pid file, read from the file, and use the file as necessary to send basic signals to the background running process. The script will collect any output from the running process to a log which you can monitor with tail.
First thing's first. Let's create our bash script. Use touch to create a new plaintext file.
user@machine:~# touch servicedaemon
Don't forget to give the file executable permission with this command.
user@machine:~# chmod +x servicedaemon
Now open the file in your favorite text editor and lets start putting our script together. It might be beneficial for you to leave a terminal open so you can play with your script as we go along.
#!/bin/bash
The script must contain this on the first line to tell bash what script interpreter to use. In this case we're using bash of course.
Next we setup a way to handle arguments for our command. In this case we want to show usage information when someone doesn't provide any arguments but also provide basic arguments that are known to most people like -h and -v for help and version information.
#!/bin/bash
# source: https://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within
# this detects the real location of the script even if it's linked
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
cd $DIR
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")";
pidfile="$me.pid"
executable="myservice.sh"
function loadenv() {
if [ -f ".env" ]
then
envtype=$(cat .env)
envfile="conf/$envtype"
if [ -f $envfile ]
then
echo "Loading environment from $envfile"
. $envfile
else
echo "$envfile not found."
fi
else
echo 'Environment type definition not found. Attempting production.'
if [ -f 'conf/production' ]
then
. conf/production
else
echo 'Production environment definition not found.'
fi
fi
}
function status() {
if [ -f $pidfile ]
then
PID=`cat $pidfile`
if ps -p $PID > /dev/null
then
echo "$me is running on $PID"
else
echo "$me is not running"
fi
else
echo 'PID file not found.'
fi
}
function start() {
loadenv
log=$DIR/$me.log
if [ -f $pidfile ]; then
echo "PID file found: " && cat $pidfile && exit 0
fi
daemon() {
echo "Starting $me"
$executable >> $log 2>&1 &
pid="$!"
echo $pid > $pidfile
}
daemon
}
function stop() {
echo 'Stopping $me'
PID=`cat $pidfile`
if ps -p $PID > /dev/null
then
kill `cat $pidfile`
fi
rm $pidfile
}
function version() {
echo '0.0.1'
exit
}
function usage() {
echo "Usage: $me [status|start|reload|stop]" 1>&2;
exit;
}
case $1 in
status)
status
exit
;;
start)
start
exit
;;
reload)
stop
start
exit
;;
stop)
stop
exit
;;
-h|-\?|--help)
usage
exit
;;
-v|--version)
version
exit
;;
*)
usage
exit
;;
esac
shift
At this point you can run ./servicedaemon from terminal and see the usage information pop up.
user@machine:~$ ./servicedaemon
Usage: servicedaemon [status|start|reload|stop]
user@machine:~$ ./servicedaemon status
PID file not found.
user@machine:~$ ./servicedaemon -v
0.0.1
user@machine:~$ ./servicedaemon -h
Usage: servicedaemon [status|start|reload|stop]
Now just replace executable on line 18 with what you want to run. A log of any output will be placed in the same folder with the name of the executable file with a log extension. You can tail it to see output. A PID file will be created in the same folder.