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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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:
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
Hi!
I had a similar problem to solve and i made this package here: https://github.com/bithon/python-daemon-set-sunrise/releases
I hope its ok to “advertise” it here, but i hope some people could find it useful.
Best regards,
Oliver
LikeLiked by 1 person