środa, 11 maja 2016

RaspberryPI Python service - exit nicely with GPIO.cleanup()

I have my python script used as a linux service. It is started by start-stop-daemon from /etc/init.d/myservice script. Because my program uses GPIO, I was thinking how to call GPIO.cleanup() when I stop my service with sudo service myservice stop command.

The solution I've found is to register signals handlers. It looks like:
 
import signal
handle_exit(signal_number,stack):
    GPIO.cleanup()
    exit()

signal.signal(signal.SIGTERM, handle_exit)
signal.signal(signal.SIGINT, handle_exit)

You may find great explanation of signals here, but what you need to know is that SIGTERM is sent for example when you simply kill your program with kill command (without -9) and SIGINT is a result of pressing Ctrl+C. The handler is called with two arguments: the signal number and the current stack frame (but it doesn't matter in this example - you only have to define them as arguments to avoid the error "TypeError: handle_exit() takes no arguments (2 given)")

If you don't know how to create script starting your python app when system starts up, you should read this tutorial: http://blog.scphillips.com/posts/2013/07/getting-a-python-script-to-run-in-the-background-as-a-service-on-boot/

To make this tutorial's code work with your signal handlers you should change the following line:
start-stop-daemon --stop --pidfile $PIDFILE --retry 10  
to
start-stop-daemon --stop --pidfile $PIDFILE --retry TERM/30/KILL/5 --oknodo --user $DAEMON_USER  

This will send TERM signal when you type sudo service myservice stop, and because of registered handlers, your app will exit nicely.


This is a full code of my script placed in /etc/init.d/
#!/bin/sh

### BEGIN INIT INFO
# Provides:          startgate
# 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=/home/pi/bmxstartgate13-02-2016/bmxstartgate
DAEMON=$DIR/startgate.py
DAEMON_NAME=startgate

# Add any command line options for your daemon here
DAEMON_OPTS=""

# 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=pi

# 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 --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
    start-stop-daemon --stop --pidfile $PIDFILE --retry TERM/30/KILL/5 --oknodo --user $DAEMON_USER
    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


I've found also another solution with using atexit (it is mentioned here in comments) but for me it worked only on ctrl+c.
Solution I've described in this post is based on this topic: https://www.raspberrypi.org/forums/viewtopic.php?t=63051


Feel free to ask if you have any questions :)

Brak komentarzy:

Prześlij komentarz