Basj Basj - 26 days ago 6
Python Question

Running code when closing a Python daemon

I'm using the famous code referenced here or here to do a daemon in Python, like this:

import sys, daemon

class test(daemon.Daemon):
def run(self):
self.db = somedb.connect() # connect to a DB
self.blah = 127
with open('blah0.txt', 'w') as f:
f.write(self.blah)
# doing lots of things here, modifying self.blah

def before_stop(self):
self.db.close() # properly close the DB (sync to disk, etc.)
with open('blah1.txt', 'w') as f:
f.write(self.blah)

daemon = test(pidfile='_.pid')

if 'start' == sys.argv[1]:
daemon.start()
elif 'stop' == sys.argv[1]:
daemon.before_stop() # AttributeError: test instance has no attribute 'blah'
daemon.stop()


The problem is that when calling
./myscript.py stop
and thus
daemon.before_stop()
, there is no reference anymore to
self.blah
!


AttributeError: test instance has no attribute 'blah'


Thus with this daemonization method, it's impossible to have access to the daemon's variables before stopping the daemon...

Question: how to have access to the daemon class' variables just before:


  • stopping with
    ./myscript.py stop

  • being stopped with SIGTERM

  • (being killed?)






EDIT: solved, and here is a working daemon code with a
quit()
method.

Answer

The Daemon code sends a SIGTERM signal to the daemon process to ask it to stop. If you want that something is run by the daemon process itself, it must be run from a signal handler or from an atexit.register called method.

The daemonize method already installs such a method, just call beforestop from there:

# this one could be either in a subclass or in a modified base daemeon class
def delpid(self):
        if hasattr(self, 'before_stop'):
                self.before_stop()
        os.remove(self.pidfile)

# this one should be in subclass
def before_stop(self):
    self.db.close()                      # properly close the DB (sync to disk, etc.)
    with open('blah1.txt', 'w') as f:
        f.write(self.blah)

But that is not enough! The Python Standard Library documentation says about atexit:

The functions registered via this module are not called when the program is killed by a signal not handled by Python

As the process is expected to receive a SIGTERM signal, you have to install a handler. As shown in one active state recipe, it is damned simple: just ask the program to stop if it receives the signal:

...
from signal import signal, SIGTERM
...
                atexit.register(self.delpid)
                signal(SIGTERM, lambda signum, stack_frame: exit(1))