8

I'm trying to make a daemon in python and I've come across the python-daemon package. The interesting thing about it is that the most common way I've seen it used isn't even what the documentation, which is very sparse, tells you to do

import os
import grp
import signal
import daemon
import lockfile

from spam import (
    initial_program_setup,
    do_main_program,
    program_cleanup,
    reload_program_config,
    )

context = daemon.DaemonContext(
    working_directory='/var/lib/foo',
    umask=0o002,
    pidfile=lockfile.FileLock('/var/run/spam.pid'),
    )

context.signal_map = {
    signal.SIGTERM: program_cleanup,
    signal.SIGHUP: 'terminate',
    signal.SIGUSR1: reload_program_config,
    }

mail_gid = grp.getgrnam('mail').gr_gid
context.gid = mail_gid

important_file = open('spam.data', 'w')
interesting_file = open('eggs.data', 'w')
context.files_preserve = [important_file, interesting_file]

initial_program_setup()

with context:
    do_main_program()

Instead, people use it like this:

#!/usr/bin/python
import time
from daemon import runner

class App():
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path =  '/tmp/foo.pid'
        self.pidfile_timeout = 5
    def run(self):
        while True:
            print("Howdy!  Gig'em!  Whoop!")
            time.sleep(10)

app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

Examples here and in this thread How do you create a daemon in Python?

So can anyone tell me how the package is supposed to be used as intended? There are 0 examples to be found that use it the way the documentation specifies.

Community
  • 1
  • 1
m0meni
  • 16,006
  • 16
  • 82
  • 141
  • I updated it to the more complex example just for you, but it's the same concept at the end of the day. Do you have an answer as to how to use it properly? – m0meni May 23 '15 at 03:40
  • That's simply not true. I have version 2.0.5 installed and it is located here: `/usr/local/lib/python2.7/dist-packages/daemon/runner.py` – m0meni May 23 '15 at 03:43
  • Exactly, I am looking at a question from 2009, and I am confused, which is why I made this one. The documentation is not clear to me at all. You seem rather knowledgeable about the topic so could you please provide an example of how to actually use the package as it is meant to be used? – m0meni May 23 '15 at 03:48
  • Actually, I was wrong about the `DaemonRunner` entirely; I was mixing it up with the old `Daemon` object… give me a sec and I think I can explain what you're asking about. – abarnert May 23 '15 at 04:03
  • Thanks for this question; I hadn't actually used `python-daemon` since 2011 on any platform but OS X, and I re-learned some things I'd forgotten and learned some things I never knew. :) – abarnert May 23 '15 at 04:14

2 Answers2

12

First, the reason you can't find good documentation is that, to the best of my knowledge, nobody ever wrote it. When Ben Finney proposed the PEP, there was plenty of interest, but then when he asked for someone else to take over the project and champion it, nobody did, so… beyond the PEP, and the sparse documentation in the docs directory of the project, there's really nothing but the source to explain things.

A DaemonContext is the way you're meant to create a daemon. Its API was bikeshedded extensively, and was the only part that was proposed to be part of the public interface in the stdlib. People from the Debian, Ubuntu, and RedHat/Fedora projects were involved in the initial discussion, and changes have been incorporated based on their experiences moving their distros to systemd.

A DaemonRunner wraps up a DaemonContext-based daemon and a control tool (ala apachectl). This implements a “service”, which is only one way of running a daemon out of many other different ways.

Often, you don't want this—if you want to build a "service", you usually want to only implement the daemon using a daemon.DaemonContext, and let systemd or launchd or their older predecessors manage the service by invoking that daemon. So, the PEP, to keep things simple, explicitly said that a service is outside the scope of what the daemon module should attempt.

But there is code for services in the python-daemon distribution. It isn't fully documented, because it is only an example of one way to use a daemon.

It does appear to work, and it's definitely been maintained and updated over the years. So, if you want an apachectl-type tool, I think it makes sense to use a DaemonRunner; just make sure you read the docstrings and write some tests to make sure it's doing what you wanted.

bignose
  • 30,281
  • 14
  • 77
  • 110
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 3
    Unfortunately, the DaemonRunner is going to be deprecated: this is a new development as of about 2 weeks ago. https://pagure.io/python-daemon/c/f25678cacc8cd1fbb8a28c196e8fe2eed291146c?branch=master I have an example which uses the standard logging module, and daemon's included pidfile module, plus an init script. See answer here: http://stackoverflow.com/a/40536099/299952 – phzx_munki Nov 10 '16 at 20:19
4

As abarnert says, the python-daemon documentation is meant to show how to write a daemon process: a program that detaches itself from the controlling terminal and has no parent process and runs in the background.

The python-daemon code doesn't dictate how to use that daemon in a service. To implement the service, you need something to run that daemon: systemd, init, upstart, launchd all can do the job.

As an (undocumented) example, the python-daemon code base includes a simple example runner. It is not part of writing a daemon, and better tools exist.

Write the daemon using the daemon.daemon API as documented, and run it using a runner that comes with your operating system.

bignose
  • 30,281
  • 14
  • 77
  • 110