# @GPL_HEADER_START
# Use of the RAVE and related source code is subject to the terms
# of the following licenses:
# 
# GNU Public License (GPL) Rights pursuant to Version 2, June 1991
# Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013
# 
# NO WARRANTY
# 
# ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER
# PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY
# PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN
# "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
# KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT
# LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE,
# MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE
# OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT,
# SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY
# TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF
# WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
# LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF
# CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON
# CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE
# DELIVERABLES UNDER THIS LICENSE.
# 
# Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie
# Mellon University, its trustees, officers, employees, and agents from
# all claims or demands made against them (and any related losses,
# expenses, or attorney's fees) arising out of, or relating to Licensee's
# and/or its sub licensees' negligent use or willful misuse of or
# negligent conduct or willful misconduct regarding the Software,
# facilities, or other rights or assistance granted by Carnegie Mellon
# University under this License, including, but not limited to, any
# claims of product liability, personal injury, death, damage to
# property, or violation of any laws or regulations.
# 
# Carnegie Mellon University Software Engineering Institute authored
# documents are sponsored by the U.S. Department of Defense under
# Contract F19628-00-C-0003. Carnegie Mellon University retains
# copyrights in all material produced under this contract. The U.S.
# Government retains a non-exclusive, royalty-free license to publish or
# reproduce these documents, or allow others to do so, for U.S.
# Government purposes only pursuant to the copyright license under the
# contract clause at 252.227.7013.
# 
# @GPL_HEADER_END
"""Disk And Execution MONitor (Daemon)

Default daemon behaviors (they can be modified):
   1.) Ignore SIGHUP signals.
   2.) Default current working directory to the "/" directory.
   3.) Set the current file creation mode mask to 0.
   4.) Close all open files (0 to [SC_OPEN_MAX or 256]).
   5.) Redirect standard I/O streams to "/dev/null".

Failed fork() calls will return a tuple: (errno, strerror).  This behavior
can be modified to meet your program's needs.

Resources:
   Advanced Programming in the Unix Environment: W. Richard Stevens
   Unix Network Programming (Volume 1): W. Richard Stevens
   http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
"""

#
# Code shamelessly ripped (and modified) from:
# <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731>
#

import os               # Miscellaneous OS interfaces.
import sys              # System-specific parameters and functions.
import signal           # Set handlers for asynchronous events.

def daemonize(wd="/", stdout=None, stderr=None):
    """Detach a process from the controlling terminal and run it in the
    background as a daemon.

    Parameters:
    stdout: str
        name of file to which to write standard output of forked process
    stderr: str
        name of file to which to write standard error of forked process

    """

    if stdout is None:
        stdout = "/dev/null"
    if stderr is None:
        stderr = "/dev/null"

    pid = os.fork()
    if (pid == 0): # Child
        os.setsid()
    #   Ignore SIGHUP so the upcoming child process doesn't barf when
    #   we, the parent, exit mere milliseconds from starting him.
        signal.signal(signal.SIGHUP, signal.SIG_IGN)
        pid = os.fork()
        if (pid == 0): # Grandchild
        #   Set the working directory to wd so we don't stand in the way
        #   of unmounting any filesystems
            os.chdir(wd)
        #   Give daemon process unlimited control on permissions of
        #   files it creates.
            os.umask(0)
        else: # (Still) Child
            os._exit(0)
    else: # Parent
        os._exit(0)
        

    # Still executing? Must be the now-daemonized grandchild process....

    # Close all open files.  Try the system configuration
    # variable, SC_OPEN_MAX, but default if it isn't there.
    try:
        maxfd = os.sysconf("SC_OPEN_MAX")
    except (AttributeError, ValueError):
        # SC_OPEN_MAX didn't exist. Make something up.
        maxfd = 256       # default maximum

    for fd in range(0, maxfd):
        try:
            os.close(fd)
        except OSError:
        #   Ignore
            pass



    # Now that all the file descriptors are closed, any new ones
    # we open will get the lowest fd numbers available -- 0,1, and
    # 2, which correspond to STDIN, STDOUT and STDERR.


    # Redirect the standard file descriptors to specified file, or
    # /dev/null. (This is programming by coincidence -- we just
    # closed these fds, so we should get them when in this order
    # when we open them. This _is not guaranteed_ if you have
    # other threads running in parallel with daemonize().
    os.open("/dev/null", os.O_RDONLY)       # standard input  (0)
    os.open(stdout, os.O_RDWR | os.O_CREAT) # standard output (1)
    os.open(stderr, os.O_RDWR | os.O_CREAT) # standard error  (2)

    # Redirect Python STD* streams
    sys.stdin  = os.fdopen(0, 'rb')
    sys.stdout = os.fdopen(1, 'wb', 1)  # line-buffer STDOUT
    sys.stderr = os.fdopen(2, 'wb', 0)  # unbuffer STDERR
