Radio Shows As Podcasts: Part 3a – The init.d Script

This is the first part of the post on how I daemonised my Podcast XML auto generating Python programme. Part 3b can be found here.

After some research, I found this blog post to contain most of the information I needed.

I already knew that I could generate a daemon by altering the skeleton init.d file found in /etc/init.d/ to point to the appropriate file. However, I was unsure how to set the flags on the start-stop-daemon command to ensure a Python script behaved properly. The skeleton file is structured as follows:

  • Start daemon function
  • Stop daemon function
  • Restart daemon function
  • A case block to process which function is called through sudo service myservice command

The start, stop and restart functions contain the appropriate flags to achieve the wanted behaviour from the start-stop-daemon command. Details on how I edited the skeleton file can be found below the fold.

The Start Function

The skeleton file provides the following code:

start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
                $DAEMON_ARGS

where $PIDFILE[1] is defined as PIDFILE=/var/run/$NAME.pid, where $NAME is the user defined name for the daemon. This is typically the name of the script being demonised, as $DAEMON is defined as DAEMON=/usr/sbin/$NAME.

The flags are fairly self explanatory; --start obviously starts the daemon, --pidfile points to the location of the pid file, and --exec executes the script the $DAEMON variable points to. --quiet does not allow any information to be printed, only error messages.[2] (I got rid of this flag to ensure I could see what was happening when writing the script for the first time, and used --verbose for extra debugging help when editing the stop function>

There are a few differences between the flags used in the Philips blog post and those outlined above. First, Philips uses the --user and --chuid flags. --chuid sets which user the daemon runs under, while the --user flag can be used to look for scripts with the same name being ran by the user identified by the flag, to check whether the daemon is already running.

The --background flag is explained by Philips as enabling the daemon to run in the background; the manual sheds more information on this. This flag tells start-stop-daemon to fork before the process being daemonised is initiated and then forces it to run this process in the background. This is an important flag for my purposes as I have yet to learn how to fork processes in this way in Python or Linux.

Similarly, the --make-pidfile flag is required as my Python script does not generate its own pidfile file – I need start-stop-daemon to do that for me. Although this would be a rather straightforward thing to do, it would take a few lines of python and require a couple of modules to be imported. Adding this flag is rather quicker!

The other difference is that the skeleton file initiates the script with the --exec flag, while Philips uses is the --start-as flag. The manual warns that --exec might not work as intended with interpreted scripts, as it might point to the interpreter. i.e. it might point to /usr/bin/env python rather than /path/to/myservice. A nice little explanation of this is provided here.

I therefore ended up using the following flags:

start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --startas $DAEMON -- \
                $DAEMON_ARGS

The Stop Function

The stop function provided by the skeleton file:

start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
        RETVAL="$?"

was a little bit more complicated than that provided by Philips:

start-stop-daemon --stop --pidfile $PIDFILE --retry 10

by virtue of the difference in the --retry flag. Philips explains that his choice of flags will send a TERM signal to the process and then, after 10 seconds, check if the process is still running. If it is, it sends a KILL signal, which will definitely kill the process. The --retry flag provided by the skeleton file, therefore, sends a TERM signal, waits 30 seconds before sending a KILL signal if the process is still running, and then waits another 5, after which, if the process is still running, start-stop-daemon exits with an error status 2.

When modifying the skeleton file, I consistently hit problems with start-stop-daemon identifying the process to kill. I would receive errors that indicated, for example, that it could not find processes with the name given in $NAME.[3] This indicated that start-stop-daemon was not finding anything satisfying the “matching” flags (i.e. --name, --pidfile, --exec). The changes in the start function meant that the process should have been identifiable through --name and --pidfile, but this was failing (possibly due to this being an interpreted script, i.e. the “name” path actually having “python” in front of it, while the name variable did not contain this). Removing the –name flag solved this problem, allowing the process to be found through the pid file.

Before reaching this rather simple solution, however, I had been searching for why I was unable to stop the Python script/process through the sudo service myservice stop command. Quite a few of the results, such as this and this, stated that the Python script needed to include some code to handle the TERM signal being sent. Adding this code (specifically that found in the first example result) didn’t have any effect. This was because the signal couldn’t be sent to the process, as the process itself was not being found, rather than the process being unable to handle the TERM signal.

In the end, using the --verbose flag allowed me to identify that start-stop-daemon was not identifying the process; once this problem was solved (as outlined above), I could stop the daemon with the normal sudo service myservice stop command, and did not require any extra code to handle the TERM signal (presumably because the kill command was succeeding).

The case Block

Typically, a service controlled via the sudo service commands would report a nice status message, such as [ ok ] Starting python daemon test: myservice. However, the initial code provided by the skeleton file (given below) only produced these messages for the status command.

start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;

The culprit for the lack of these messages was the if statement checking whether the $VERBOSE variable was set to “yes” or “no”. To check this variable, one appears to be required to include . /lib/init/vars.sh. I did not do much research into what this file is or does, as simply removing the if statement allowed the messages to appear.

The finished init.d script for the Podcast XML generated programme can be found here

[1] A pid file is a file which contains the process id and nothing more. It’s generated when the process starts and allows for an easy way to identify the process id of the daemon, and therefore an easy way to interact with the process, such as issuing the kill command to stop/kill the process.

[2] See the manual for more information.

This entry was posted in Programming, Python. Bookmark the permalink.

2 Responses to Radio Shows As Podcasts: Part 3a – The init.d Script

  1. Pingback: Radio Shows As Podcasts: Part 3 – Daemonisation | confusedpublic

  2. Pingback: Radio Shows As Podcasts: Part 3b – Scheduling the Python Code | confusedpublic

Leave a Reply

Your email address will not be published. Required fields are marked *