Daemonize that Python script!

This post builds on a previous experiment where I made a remote control app for my outlets with a Raspberry Pi, Python/Flask and AngularJS. For the backend to be really useful, I want to make it behave as a service in Linux. It should start automatically after a reboot and there should be some control mechanism for manually starting, stopping and viewing the status of the service. There should also be a proper logging in place.

Services in Raspbian

Handling services in Raspbian/Debian requires a few commands: service, update-rc.d and systemctl. To display the status of all registred services, you can use the service command:

service --status-all

It will show a list like:

[ + ] alsa-utils
[ + ] avahi-daemon
[ + ] bluetooth
[ - ] bootlogs
[ - ] bootmisc.sh
[ - ] checkfs.sh
[ - ] checkroot-bootclean.sh
[ - ] checkroot.sh
[ + ] console-setup
...

The prefixes mean:

[+] - the service is running
[-] - the service is not running

And for checking a specific service:

 service [ServiceName] status

A service can be started, stopped or restarted with the appropriate switches:

 sudo service [ServiceName] start
 sudo service [ServiceName] stop
 sudo service [ServiceName] restart

Creating a new service in Raspbian

An application can be started as a daemon process with the start-stop-daemon command. But as this command has a rather intricate argument list, it is better to create a bash script that simplify the usage (this will also allow auto start when booting). For example, to start a daemon process we want to specify the program (with possible arguments) to run, what user that should run the process and create a PID file so that the process can be tracked, the full argument list would look something like this:

start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS

Not something that you would like to type many times.
Instead, you can use a bash file as described by Stephen C Phillips in this excellent tutorial:
http://blog.scphillips.com/posts/2013/07/getting-a-python-script-to-run-in-the-background-as-a-service-on-boot/

The init file for my Flask application would look something like this:


#!/bin/sh
# from http://blog.scphillips.com/posts/2013/07/getting-a-python-script-to-run-in-the-background-as-a-service-on-boot/
### BEGIN INIT INFO
# Provides: remotecontrol
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: A service for a web server that handles remote controlled outles
# Description: The service runs a Flask application that exposes a web api and an AngularJS app
### END INIT INFO
DIR=/home/[USER]/HomeAutomation/RemoteControlApp
DAEMON=$DIR/remotecontrol.py
DAEMON_NAME=remotecontrol
DAEMON_OPTS=""
# Root is needed to use the GPIO pins on the Raspberry
DAEMON_USER=root
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 –chuid $DAEMON_USER –startas $DAEMON — $DAEMON_OPTS
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/$DAEMON_NAME {start|stop|restart|status}"
exit 1
;;
esac
exit 0

Note: to get the Python script started with this bash file, you need to add execution rights on the Python file and also on the bash script file. In my case:

chmod 777 remotecontrol.py
sudo chmod 777 /etc/init.d/remotecontrol.sh

If you put the bash script in the /etc/init.d/ folder you can use the update-rc.d command to add the service for autostart when booting. For example:

sudo update-rc.d remotecontrol.sh defaults

This will add make the application run at the default run level (see https://wiki.debian.org/RunLevel).

To remove the service from autostart:

sudo update-rc.d -f remotecontrol.sh remove

If you change the bash script, you can reload it with the systemctl command:

sudo systemctl daemon-reload

Logging

As the application now runs in the background, we can not see any messages that are printed out. Stephen C Phillips has made a very useful logging example in his blog post:

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

It uses the Python Logging library and is easily adapted for any Python application.

I have updated the code for my Remote Outlet application so that it uses a similar logger. With the Python script now running as a background service, the tracing and any other output are routed to the log file in /tmp/remotecontrol.log.

The GitHub repository has been updated with the new code that includes service handling and logging:

https://github.com/LarsBergqvist/RemoteControlled_Outlets

 

 

 

2 Thoughts

Leave a comment