web developer & system programmer

coder . cl

ramblings and thoughts on programming...


writing python daemons

published: 12-05-2012 / updated: 15-05-2012
posted in: development, programming, python, tips
by Daniel Molina Wegener

A daemon in Unix™ and similar operating systems, is a background process, which runs without using the terminal or the X11 system as main I/O system. In other operating system, this is called service. A daemon has a very specific task to do, for example the NTP daemon keeps your computer date updated connecting to NTP servers. Many applications that require asynchronous tasks, require this kind of applications to make your life easier. For example a job queue processor for the Gearman job server can be handled with this kind of applications.

A daemon, classically on most Unix™ systems, first closes the three main I/O streams: stdin, stdout and stderr, then the fork(2) system call is used, creating an image of the current process, once the call is made, an exit(1) call is made on the parent process, and the child process keeps working in background. Due to the Python philosophy of being a productive language, this not so complex process is already implemented in the daemon module, so with this module you can create a daemon program.


import os
import sys
import daemon
import atexit


def main():
    """
    Main Program
    """

    install_signal_handler()
    atexit.register(at_exit_handler)

    opts = parse_opts()
    config = parse_config(opts)
    if not opts.cwd:
        print("No Working Directory")
        sys.exit(0)

    with daemon.DaemonContext():
        os.chdir(opts.cwd)
        install_signal_handler()
        start_schedule(config, opts)


""" Executes the main() Function """
if __name__ == '__main__':
    main()

If you observe the code, it changes the working directory using os.chdir() function, because once a daemon is created, its working directory is automatically changed to the root directory or /. Some daemons or servers like the Apache HTTP server, have a precompiled working directory, but it also allows to use chroot directory to make it work outside the precompiled working directory. Also, it is installing signal handlers, to allow signal processing, like SIGHUP and SIGCHLD. The parse_opts() and parse_config() functions, are functions to parse daemon arguments and daemon configuration using the optparse and ConfigParser modules respectively.

The atexit module is used to ensure that most program resources — like files, connections and similar stuff — are released or closed, once the daemon process terminates. So, the following atexit example shows a routine releasing resources.


from django.db import connections
import atexit

def at_exit_handler():
    """
    At Exit Function (close all connections)
    """
    for con in connections.all():
        try:
            con.close()
        except Exception, exc:
            log().error(exc)
    log().close()

The signal handler have very similar behaviour, it register a processing callback, which is executed using each time a signal is received. For example on the SIGHUP signal, I am doing a daemon rehash, rather than doing a daemon termination.


import signal as sig
import traceback as tb


def rehash_daemon():
    """
    Rehash the daemon, reading configurations again.
    """
    global DAEMON_CONFIG
    DAEMON_CONFIG = parse_config()


def install_signal_handler():
    """
    Signal Handler Installer
    """
    try:
        sig.signal(sig.SIGHUP, rehash_daemon)
    except Exception, exc:
        log().error(repr(exc))
        log().error(tb.format_exc(exc))
        return False

This will allow you to create a robust daemons, capable to handle signals, resource releasing on the exit event and similar stuff. Remember that as any language with a garbage collector, Python has the disadvantage of leaking memory if you do not cut the object references properly, due to the reference counting used by the garbage collector inside the GIL cycles. Your daemon design should be optimal.


2 comments to “writing python daemons”

  1. Are you referencing a particular lib off say pypi? From what I can see “daemon” isn’t a standard module/package provided by either 2.7 or 3.x, and there are several third party libs which all use the “daemon” namespace.

  2. I meant this package:

        author = u'Ben Finney <ben+python@benfinney.id.au>'
        author_email = u'ben+python@benfinney.id.au'
        author_name = u'Ben Finney'
        copyright = u'Copyright © 2001—2009 Ben Finney <ben+python@benfinney.id.au>'
        copyright_year = '2009'
    

    Please try pydoc daemon.version.

post a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>