lmacken/quantumrandom

Enhancement request: use syslog

kseistrup opened this issue · 3 comments

The quantumrandom-dev script works good. However, it'd be nice if it could use syslog as its output is invisible when run as a dæmon.

Cheers.

Yeah, I'd like to see this as well.

First off, we need to get proper daemonization working with qrandom-dev. Then, adding syslog hooks to our current log() method should be fairly trivial.

Here's a suggestion for a daemonize() function (no error checking):

# -*- coding: utf-8 -*-

import os

from sys import stdin, stdout, stderr


# How do I get my program to act like a dæmon?
# http://www.unixguide.net/unix/programming/1.7.shtml
def daemonize():
    # fork() so the parent can exit, this returns control to the
    # command line or shell invoking your program.  This step is
    # required so that the new process is guaranteed not to be a
    # process group leader.  The next step, setsid(), fails if
    # you're a process group leader.
    pid = os.fork()

    if pid > 0:
        # First parent exits.
        # We use _exit() to bypass normal exit processing.
        os._exit(0)

    # NB: From now on sys.exit() doesn't set a return code!

    os.setsid()

    # fork() again so the parent, the session group leader, can
    # exit.  This means that we, as a non-session group leader, can
    # never regain a controlling terminal.
    pid = os.fork()

    if pid > 0:
        # Second parent exits
        # We use _exit() to bypass normal exit processing.
        os._exit(0)

    # Now let's decouple from parent environment

    # chdir("/") to ensure that our process doesn't keep any directory
    # in use.  Failure to do this could make it so that an administrator
    # couldn't unmount a filesystem, because it was our current directory.
    os.chdir('/')

    # so that we have complete control over the permissions of anything
    # we write. We don't know what umask we may have inherited.
    os.umask(0)

    # close() fds 0, 1, and 2.  This releases the standard in, out, and
    # error we inherited from our parent process.  We have no way of
    # knowing where these fds might have been redirected to.  Note that
    # many daemons use sysconf() to determine the limit _SC_OPEN_MAX.
    # _SC_OPEN_MAX tells you the maximum open files/process.  Then in a
    # loop, the daemon can close all possible file descriptors.  You have
    # to decide if you need to do this or not.  If you think that there
    # might be file-descriptors open you should close them, since there's
    # a limit on number of concurrent file descriptors.
    (fd0, fd1, fd2) = (stdin.fileno(), stdout.fileno(), stderr.fileno())

    for i in range(os.sysconf('SC_OPEN_MAX') - 1, -1, -1):
        # dup2() will close fd0, fd1 and fd2 in a while
        if i not in (fd0, fd1, fd2):
            try:
                os.close(i)
            except:
                pass  # Ignore EBADF

    # Unbuffered resp. line buffered
    # (names taken from, but values unrelated to, stdio.h)
    (IONBUF, IOLBUF) = (0, 1)

    fpi = file('/dev/null', 'r',  IOLBUF)
    fpo = file('/dev/null', 'w',  IOLBUF)
    fpe = file('/dev/null', 'wb', IONBUF)

    os.dup2(fpi.fileno(), fd0)
    os.dup2(fpo.fileno(), fd1)
    os.dup2(fpe.fileno(), fd2)

# eof

Cheers.

PS: When run via upstart, or under dæmontools/runit, there's no need for daemonizing the script, so I suggest it be made optional via a command line switch…