Python を daemon 化してくれる関数が、標準ライブラリで見つからなかった。
探せばいくらでも有るんだろうけれど、面倒なので Ruby の Process.daemon を真似て作ってみた。

といっても、Ruby 1.9 の lib ディレクトリを見ても、Process.rb というファイルは見つからない。
おそらく C で書かれてコンパイルしていると想像される。
(本当かどうかは知らない)

さすがに Ruby インタープリタのソースを読むのは手間がかかるので、
引数の仕様だけを真似てやってみた。

ちなみに、Python ならではの hack として、
「ライブラリの中で exit したい時は、"sys.exit" ではなく "os._exit" を使う」
という事を最近しったので、ついでにメモしておく。

実は、sys.exit は、実際には例外を投げているだけ。
Python のインタープリタがその例外をキャッチして、各種 buffer の処理などを
よろしくやった上で終了してくれる。
要は、面倒な後処理を Python に任せて安全に(?)終了してくれるのだ。

しかし、ライブラリでこの関数を使用する事は危険だ。
何も知らないプログラマが、上流でこの例外をキャッチしてしまうかもしれない。
sys.exit では本当にプログラムが終了する事は保証できないし、
その他どんな副作用が発生するか予想できない。

そのため、上流行程に関係なく問答無用でプログラムを終了したい場合は
os._exit を使用するらしい。

まあ、ライブラリの中から exit する事なんて多く無いと思うが。

そんな訳で、作成した関数が以下。
いい加減な作りだが、一応動作する。

def daemon(nochdir=False, noclose=False):
    r''' Fork twice and become a daemon.
         @param nodir
             The daemon process does not change the root directory to '/' if True.
             The default is False.
         @param noclose
             Does not close the stdin, stdout, stderr if True.
             The default is True.
    '''

    ## 1st fork
    pid = os.fork()
    if pid != 0:
        os.waitpid(pid, os.P_WAIT)
        os._exit(0)

    ## 2nd fork
    os.setsid()
    pid = os.fork()
    if pid != 0:
        os._exit(0)

    ## daemon process
    # ch '/'
    if not nochdir:
        os.chdir('/')

    # close stdin, stdout, stderr
    if not noclose:
        try:
            os.close(0)
        except OSError:
            pass
        else:
            os.open(os.devnull, os.O_WRONLY)

        for fd in [1, 2]:
            try:
                os.close(fd)
            except OSError:
                pass
            else:
                os.open(os.devnull, os.O_RDONLY)

    return



2011/1/2 Typo 発見。直す