Creating Services on Ubuntu

It occurred to me earlier that my way of running things that’re designed to be permanent processes on my own boxes what may be generously described as sketchy.

As someone who is primarily a developer and who just kind of picked up anything ops-wise as need be this isn’t something that I’ve actually ever had to bother with.

Up until this point I’ve been fairly lazily just either running screen or tmux and detaching myself from the window or nohup’ing it and piping it to /dev/null… so, not elegant.

Anyway, so, (very basic I’d imagine) adventures in creating a service! Woo!

First things first, Google for creating an Ubuntu service! I’m running Ubuntu on pretty much everything these days as:

  • a. at this point there is so so much more troubleshooting documentation out there
  • b. it’s what I have to deal with at work and most importantly
  • c. i’m sick to death of yum

Anyway, the first article that magically appears is this helpful page

Excellent! Just what I was after! Until you try the first step, wonder why it didn’t work, then re-read the page and realise there’s a small line of the top saying that this is describing something that doesn’t actually exist. Helpful.

Continuing the searching of the interwebs however, there's a very helpful looking entry by "cipherboy" giving a basic example of a script. I added a couple of modifications to it to suit my needs and it can be found below:

#!/bin/bash
## Fill in name of program here.
PROG_NAME="blog"
PROG="node"
PROG_PATH="/usr/bin" ## Not need, but sometimes helpful (if $PROG resides in /opt for example).
PROG_ARGS="/develop/blog/index"
PID_PATH="/var/run"

start() {
    if [ -e "$PID_PATH/$PROG_NAME.pid" ]; then
        ## Program is running, exit with error.
        echo "Error! $PROG_NAME is currently running!" 1>&2
        exit 1
    else
        ## Change from /dev/null to something like /var/log/$PROG if you want to save output.
            $PROG_PATH/$PROG $PROG_ARGS 2>&1 >/dev/null &
        echo "$PROG_NAME started"
        touch "$PID_PATH/$PROG_NAME.pid"
    fi
}

stop() {
    if [ -e "$PID_PATH/$PROG_NAME.pid" ]; then
        ## Program is running, so stop it
        killall $PROG

        rm "$PID_PATH/$PROG_NAME.pid"

        echo "$PROG_NAME stopped"
    else
        ## Program is not running, exit with error.
        echo "Error! $PROG_NAME not started!" 1>&2
        exit 1
    fi
}

## Check to see if we are running as root first.
## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html
if [ "$(id -u)" != "1" ]; then
    echo "This script must be run as root" 1>&2
    #echo "This script must NOT be run as root" 1>&2
    exit 1
fi

case "$1" in
    start)
        start
        exit 0
    ;;
    stop)
        stop
        exit 0
    ;;
    reload|restart|force-reload)
        stop
        start
        exit 0
    ;;
    **)
        echo "Usage: $0 {start|stop|reload}" 1>&2
        exit 1
    ;;
esac

There were a few of things of note, primarily to do with the PID stuff:

  • The purpose of the PID_PATH stuff is to have a path where we can store the process ID so that when stop is called, it can knows where the process lives and can easily kill it. It also means it can easily work out whether a process is still running.
  • The current PID_PATH uses /var/run, which is in fact where you're supposed to store this kind of thing according to the FHS which is useful to know. This however means that you require write privileges to the /var/run directory, which means that you kind of have to be root (and I guess explains why there's that root checking in the code). This could be avoided by writing to /tmp but it doesn't seem too evil to be running this code as root... maybe... #hackedin5...4...3..
  • The original code is based the PID on the name of the file you're running. Which is fine if all you're running some bespoke software, but in this case we're running a node app, and it's conceivable that I may want to run more than 1 node app at the same time in the future. So, the file is now based around the PROG_NAME variable and we just base everything around that.

Altogether, that was more simple than I thought it would be, and the main takeaway is that apparently service scripts are really just basic bash scripts, and that you can pass function names as parameters to bash.

I guess that's the beauty of POSIX...