#!/usr/bin/env python
#-----------------------------------------------------------------------------
# Class: Daemon
# Author: Stephen Bunn 
#
# Notes:
# Adapted from https://github.com/barberj/Saerpent/blob/master/daemon/lib/daemon.py
#-----------------------------------------------------------------------------
"""
Daemonize the current running process
"""
import sys, os, time, atexit, signal, logging
from signal import SIGTERM


class Daemon(object):
  '''
  our daemon class
  '''
  def __init__(self, pidfile, loghandle, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
    self.stdin = stdin
    self.stdout = stdout
    self.stderr = stderr
    self.pidfile = pidfile
    self.log = loghandle

  def daemonize(self):
    '''
    Do the "double" fork process outlined in Richard Stevens UNIX Network
    Programming book and based on the sample code in the O'Reilly book Python
    for UNIX and Linux System Administration (pg 318)
    '''
    self.log.debug('Daemonizing process')
    try:
      self.log.debug('Doing fork #1')
      pid = os.fork()
      if pid > 0:
        sys.exit(0) # exit first parent
    except OSError, e:
      message = "fork #1 failed: ({0}) {1}\n".format(e.errno, e.strerror)
      self.log.error(message)
      sys.stderr.write(message)
      sys.exit(1)
    
    # decouple from parent environment
    os.chdir("/")
    os.umask(0)
    os.setsid()

    # preform the second fork
    try:
      self.log.debug('Doing fork #2')     
      pid = os.fork()
      if pid > 0:
        sys.exit(0) # exit second parent.
    except OSError, e:
      message = "fork #2 failed: ({0}) {1}\n".format(e.errno, e.strerror)
      self.log.error(message)
      sys.stderr.write(message)
      sys.exit(1)

    # redirect standard file descriptors.
    for fd in sys.stdout, sys.stderr: fd.flush()
    si = file(self.stdin, 'r')
    so = file(self.stdout, 'a+')
    se = file(self.stderr, 'a+', 0)
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

    # write pidfile
    pid = str(os.getpid())
    file(self.pidfile, 'w+').write("%s\n" % pid)

    # register a function to do a gracefull shutdown
    atexit.register(self.shutdown)

  def getpid(self):
    '''
    get the pid of the running daemon
    ''' 
    try:
      pf = file(self.pidfile, 'r')
      pid = int(pf.read().strip())
      pf.close()
    except IOError, e:
      pid = None

    return pid

  def shutdown(self):
    '''
    do a gracefull shutdown of the daemon (clean everything up)
    '''
    self.log.debug("stopping the daemon")
    os.remove(self.pidfile)

  def start(self):
    '''
    Start the daemon
    '''
    # make sure we are not already running

    if self.getpid():
      message = "pidfile {0} already exists. Daemon is already running?\n".format(self.pidfile)
      self.log.error(message)
      sys.stderr.write(message)
      sys.exit(1)

    self.daemonize()
    self.run()

  def status(self):
    '''
    Query the status of the daemon
    '''
    pid = self.getpid()
    try:
      procfile = file("/proc/%s/status" % pid, 'r')
      procfile.close()
    except IOError, e:
      message = "There is not a process with the PID specified in {0}\n".format(self.pidfile)
      self.log.error(message)
      sys.stderr.write(message)
      sys.exit(0)
    except TypeError:
      message = "pidfile {0} does not exist\n".format(self.pidfile)
      self.log.error(message)
      sys.stderr.write(message)
      sys.exit(0)

    message = "The process with PID {0} is running\n".format(pid)
    sys.stdout.write(message)
    self.log.info(message)

  def stop(self):
    '''
    Stop the daemon
    '''
    pid = self.getpid()
    if not pid:
      message = "pidfile {0} does not exist.  Daemon is not running?\n".format(self.pidfile)
      return

    self.log.debug('stopping the daemon')
    try:
      os.kill(pid, SIGTERM)
      time.sleep(1)
    except OSError, err:
      err = str(err)
      if err.find("No such process") > 0:
        if os.path.exists(self.pidfile):
          message = "removing pidfile since there is not a process"
          self.log.error(message)
          os.remove(self.pidfile)
          pass
      else:
        message = "error while sending SIGTERM: {0}".format(err)
        self.log.error(message)
        sys.stderr.write(message)
        sys.exit(1)

  def restart(self):
    '''
    restart the daemon
    '''
    self.stop()
    self.start()

  def run(self):
    '''
    overide this in subclass
    '''
    


