Getting a Python script to run in the background (as a service) on boot

http://blog.scphillips.com/2013/07/getting-a-python-script-to-run-in-the-background-as-a-service-on-boot/

Getting a Python script to run in the background (as a service) on boot

For some of my projects I write a simple service in Python and need it to start running in the background when the Raspberry Pi boots. Different Linux distributions use different ways of starting and stopping services (some now use Upstart, some systemd). I am using the “Wheezy” Debian distribution on my Raspberry Pi, and in this case the proper way to do this is using an “init script”. These are stored in the /etc/init.d folder. In there you can find scripts that for instance, start the networking system or a print server. Debian Wheezy uses the old Sys V init system which means that these scripts are run according to symbolic links in the /etc/rc.x directories. The Debian documentation explains this.

Anyway, the following init script makes getting a Python script (or e.g. a Perl script) to run when the Raspberry Pi boots fairly painless. Services are supposed to run as “daemons” which is quite complicated in Python and involves forking the process twice and other nasty bits. Instead we can make use of the handy start-stop-daemon command to run our script in the background and basically deals with everything we need.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
#!/bin/sh
### BEGIN INIT INFO
# Provides: myservice
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Put a short description of the service here
# Description: Put a long description of the service here
### END INIT INFO
# Change the next 3 lines to suit where you install your script and what you want to call it
DIR=/usr/local/bin/myservice
DAEMON=$DIR/myservice.py
DAEMON_NAME=myservice
# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
DAEMON_USER=root
# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid
. /lib/lsb/init-functions
do_start () {
log_daemon_msg “Starting system $DAEMON_NAME daemon”
start-stop-daemon –start –background –pidfile $PIDFILE –make-pidfile –user $DAEMON_USER –startas $DAEMON
log_end_msg $?
}
do_stop () {
log_daemon_msg “Stopping system $DAEMON_NAME daemon”
start-stop-daemon –stop –pidfile $PIDFILE –retry 10
log_end_msg $?
}
case “$1″ in
start|stop)
do_${1}
;;
restart|reload|force-reload)
do_stop
do_start
;;
status)
status_of_proc “$DAEMON_NAME” “$DAEMON” && exit 0 || exit $?
;;
*)
echo “Usage: /etc/init.d/$DEAMON_NAME {start|stop|restart|status}”
exit 1
;;
esac
exit 0
view rawmyservice.sh hosted with ❤ by GitHub

Changing the init script

Lines 14 and 15 define where to find the Python script. In this case I have said that there is a folder /usr/local/bin/myservice and that the script is called myservice.py inside there. This is so that any additional Python files or other bits that your Python script needs can also be tidily put into that one place (not really how you’re supposed to do it, but is easy).

Line 16 defines what we call the service. You should call this script by the same name.

Line 20 sets what user to run the script as. Using root is generally not a good idea but might be necessary if you need to access the GPIO pins (which I do). You might want to change this to the “pi” user for instance.

Line 25 loads a some useful functions from a standard file. We later use the logging functions for instance. We then define functions do_start and do_stop that will be used to start and stop the script.

start-stop-daemon needs to be able to identify the process belonging to a service so that (1) it can see it is there and does not start it again, and (2) it can find it and kill it when requested. In the case of a Python script then process name is “python” so this is not a very useful identifier as there may well be other Python processes running and things would get confusing. Instead we get start-stop-daemon to store the PID (the or process ID) using the --pidfile $PIDFILE --make-pidfile arguments. When told to start the process it looks for the file $PIDFILE which is defined in line 23 to be /var/run/myservice.pid (which on a Raspberry Pi is actually found at /run/myservice.pid thanks to a symbolic link.

Other than that, we use the --background flag of start-stop-daemon to run our script in the background; the –retry 10 means that first of all a TERM signal is sent to the process and then 10 seconds later it will check if the process is still there and if it is send a KILL signal (which definitely does the job).

Using the init script

To actually use this script, put your Python script where you want and make sure it is executable (e.g. chmod 755 myservice.py) and also starts with the line that tells the computer to use the Python interpreter (e.g. #!/usr/bin/env python). Edit the init script accordingly. Copy the init script into /etc/init.d using e.g. sudo cp myservice.sh /etc/init.d. Make sure the script is executable (chmod again). At this point you should be able to start your Python script using the command sudo /etc/init.d/myservice.sh start, check its status with the/etc/init.d/myservice.sh status argument and stop it with sudo /etc/init.d/myservice.sh stop.

To make the Raspberry Pi use your init script at the right time, one more step is required: running the command sudo update-rc.d myservice.sh defaults. This command adds in symbolic links to the /etc/rc.x directories so that the init script is run at the default times. you can see these links if you do ls -l /etc/rc?.d/*myservice.sh

Updates

  • 2013-09-30: corrected mistake in use of –make-pidfile (thanks to Max Sistemich).