4

I wrote a simple python program and started it as a systemd service. I'd like to do something (e.g. write message to logfile) before systemd closes this running python program.

I tried atexit (like mentioned in this post: Doing something before program exit), I also tried to catch SIGTERM (like described here: https://nattster.wordpress.com/2013/06/05/catch-kill-signal-in-python/) but without success .

I am using raspbian-jessie and python2.7.

How can I do something before systemd kills the running python program?

Here is an example code snippet (with atexit):

#!/usr/bin/env python

from pymodbus.server.async import StartTcpServer

from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

import atexit

import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

def exit_handler():
    log.warning('My application is ending!')

atexit.register(exit_handler)

store = ModbusSlaveContext(
    di = ModbusSequentialDataBlock(0, [17]*100),
    co = ModbusSequentialDataBlock(0, [17]*100),
    hr = ModbusSequentialDataBlock(0, [17]*100),
    ir = ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)

identity = ModbusDeviceIdentification()
identity.VendorName  = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl   = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName   = 'Pymodbus Server'
identity.MajorMinorRevision = '1.0'

StartTcpServer(context, identity=identity, address=("localhost", 5020))

This is a snippet with SIGTERM:

#!/usr/bin/env python

from pymodbus.server.async import StartTcpServer

from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

import signal
import sys

import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

def signal_term_handler(signal, frame):
    log.warning('got SIGTERM')
    sys.exit(0)

signal.signal(signal.SIGTERM, signal_term_handler)

store = ModbusSlaveContext(
    di = ModbusSequentialDataBlock(0, [17]*100),
    co = ModbusSequentialDataBlock(0, [17]*100),
    hr = ModbusSequentialDataBlock(0, [17]*100),
    ir = ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)

identity = ModbusDeviceIdentification()
identity.VendorName  = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl   = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName   = 'Pymodbus Server'
identity.MajorMinorRevision = '1.0'

StartTcpServer(context, identity=identity, address=("localhost", 5020))
Community
  • 1
  • 1
wewa
  • 1,628
  • 1
  • 16
  • 35
  • Can you show an example program? It should work like this, but if for some reason your signal handler is not actually executed (for example because you are busy-looping or something like this) the process will die due to the SIGKILL that is sent a bit later ... – filmor Jan 20 '16 at 10:18
  • @filmor examples added, see the question above – wewa Jan 20 '16 at 11:59
  • @wewa, I had no problem with the signal handler. Check out [this little code snippet](https://gist.github.com/Borkason/5649fdb41cd76c79787d785e3e951929), it works beautifully. Are you sure the issue is in your script? – Daniel Nov 22 '16 at 18:48

2 Answers2

1

In service file, use systemd service file option "ExecStopPost=<post execution command>", after "ExecStart=<your python script>".

This "ExecStopPost=" option should only be used for some small or minor task only.

Saturn
  • 966
  • 7
  • 14
1

This worked for me, and I did not have to change my atexit.register() or rest of .py at all:

Add SIGINT to my systemd foo.service file:

KillSignal=SIGINT
ExecStopPost=/bin/echo "my_service stopped"
TimeoutStopSec=3
Donn Lee
  • 2,962
  • 1
  • 24
  • 16
  • 1
    Nice! This is what I've been looking for :) Looks like systemd was sending SIGTERM by default, which after a long time times out to SIGKILL, but atexit is catching SIGINT correctly – smörkex Sep 15 '20 at 05:03