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.
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.
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.