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 throughsudo 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.
Pingback: Radio Shows As Podcasts: Part 3 – Daemonisation | confusedpublic
Pingback: Radio Shows As Podcasts: Part 3b – Scheduling the Python Code | confusedpublic