python daemon

I would like to create a python daemon, completely detaching from the terminal or parent process, and yet retaining any log handlers through the python logging module. There is a wonderful example at cookbook-278731 of a well-behaved daemon, and see PEP 3143.

Borrowing from these examples, here is a simple daemonize function that will retain thread-safe logging handlers that were setup through the logging module. This is also available via github.

# vim: set tabstop=4 shiftwidth=4 autoindent smartindent:
'''
Daemon (python daemonize function)

Detach process through double-fork (to avoid zombie process), close all
file descriptors, and set working directory to root (to avoid umount problems)
'''

import os, resource
import logging

# target environment
UMASK = 0 
WORKINGDIR = '/' 
MAXFD = 1024
if (hasattr(os, "devnull")):
    REDIRECT_TO = os.devnull
else:
    REDIRECT_TO = "/dev/null"

def daemonize():
    '''Detach this process and run it as a daemon'''

    try:
        pid = os.fork() #first fork
    except OSError, e:
        raise Exception, "%s [%d]" % (e.strerror, e.errno)

    if (pid == 0): #first child
        os.setsid()
        try:
            pid = os.fork() #second fork
        except OSError, e:
            raise Exception, "%s [%d]" % (e.strerror, e.errno)

        if (pid == 0): #second child
            os.chdir(WORKINGDIR)
            os.umask(UMASK)
        else:
            os._exit(0)
    else:
        os._exit(0)

    #close all file descriptors except from non-console logging handlers
    maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
    if (maxfd == resource.RLIM_INFINITY):
        maxfd = MAXFD
    filenos = []
    for handler in logging.root.handlers:
        if hasattr(handler, 'stream') and hasattr(handler.stream, 'fileno') and handler.stream.fileno() > 2:
            filenos.append( handler.stream.fileno() )
    for fd in range(0, maxfd):
        try:
            if fd not in filenos:
                os.close(fd)
        except OSError:
            pass

    #redirect stdin, stdout, stderr to null
    os.open(REDIRECT_TO, os.O_RDWR)
    os.dup2(0, 1)
    os.dup2(0, 2)

    return(0)

Simply call the daemonize() function within your application, e.g.,

import logging
import Daemon

logging.info('daemonize?')
Daemon.daemonize():
logging.info('daemonized! we are now safely detached')

From a shell, console logging will only appear before daemonize() was called, e.g.,

# python test/daemon.py 
INFO:daemon.py:4 -- daemonize?
#

And the logfile output:

# cat test.log
2011-09-27 13:26:02,712 INFO:daemon.py:4 -- daemonize?
2011-09-27 13:26:02,717 INFO:daemon.py:6 -- daemonized! we are now safely detached
#
This entry was posted in python, software arch.. Bookmark the permalink.

Comments are closed.