2

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.

Community
  • 1
  • 1
Basj
  • 41,386
  • 99
  • 383
  • 673
  • I suggest checking the reference of the daemon object and see if you are really accessing the same object between calls. Use your own signature if needed. – dcwhcdhw Nov 03 '16 at 00:50
  • @dcwhcdhw that is precisely the problem, it's not the same object between calls. how to deal with that when closing a daemon? – Basj Nov 03 '16 at 09:36

2 Answers2

5

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))
Måns Thörnvik
  • 1,038
  • 2
  • 10
  • 22
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • 1
    Thanks. I'm not sure to fully understand. Can you post an example showing where to put `self.db.close()` to close a db that has been opened inside the daemon `run` function: `self.db = somedb.connect()` (see my updated question). – Basj Nov 04 '16 at 13:41
  • @Basj: as `delpid` is registered with `atexit.register` in `daemonize` it will be called from the daemon just before it exits. – Serge Ballesta Nov 04 '16 at 14:32
  • Thanks again @SergeBallesta. I spent at least 1 hour on this, but it still doesn't work. Can you see why `quit()` is not executed / `blah1.txt` is not written, when doing `python test.py start` and then `python test.py stop` ? I modified `daemon.py` as you suggested so it should work. Here are the two files: http://gget.it/3tvspqh9/test.py, http://gget.it/y64dirn9/daemon.py – Basj Nov 04 '16 at 18:08
  • Re @SergeBallesta, do you think you could have a quick look at this code. It still doesn't work... It would be useful for many people I think. – Basj Nov 08 '16 at 11:59
  • @Basj: The SIGTERM signal has to be handled by Python for the atexit registered method to be called. See my edit... – Serge Ballesta Nov 08 '16 at 13:19
  • Thanks, your additional `signal(...)` solves the problem indeed! Last mystery: why did `os.remove(self.pidfile)` (from `delpid()`) work, even without this `signal(...)` that you added? it worked, the _.pid file was always properly removed, even without this `signal(...)` code. This is a mystery for me. – Basj Nov 12 '16 at 10:10
  • [Here is a working "daemon" module](https://gist.github.com/josephernest/77fdb0012b72ebdf4c9d19d6256a1119) with a `quit()` method, based on your answer. – Basj Nov 12 '16 at 10:35
0

The Daemon class does not retrieve the former object when you call before_stop(), it just find the process by PID and kill with a SIGTERM. If you need some instance attribute, either initialize them in __init__() or write them down in some temporary file to read them later in your before_stop() implementation.

As a general rule you should always initialize all instance bound attributes inside __init__() anyway.

Oberix
  • 1,131
  • 7
  • 13
  • Thanks. I added 2 lines to the question's code: imagine I open a DB when the daemon starts `self.db = somedb.connect()`. How to access **to the same** `self.db` object in order to do `self.db.close()` when doing `./myscript.py stop`? It seems impossible to access to the same self.db object than before? – Basj Nov 04 '16 at 11:47
  • You are taking it the wrong way in my opinion: the process management system (the `Daemon` class in you case) shouldn't mess with you application logic. Keep them separate and you'll have less problems. Simply your application should react in the correct way when receiving a `SIGTERM`... close any db connection, file, etc. Just don't do this in the `Daemon` subclass. – Oberix Nov 04 '16 at 13:02