1#!/usr/bin/env python
2
3# This code has been written by Sander Marechal and published at:
4# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
5# where the author has placed it in the public domain (see comment #6 at
6# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/#c6
7# ).
8# Some minor modifications have been made by the V8 authors. The work remains
9# in the public domain.
10
11import atexit
12import os
13from signal import SIGTERM
14from signal import SIGINT
15import sys
16import time
17
18
19class Daemon(object):
20  """
21  A generic daemon class.
22
23  Usage: subclass the Daemon class and override the run() method
24  """
25  def __init__(self, pidfile, stdin='/dev/null',
26               stdout='/dev/null', stderr='/dev/null'):
27    self.stdin = stdin
28    self.stdout = stdout
29    self.stderr = stderr
30    self.pidfile = pidfile
31
32  def daemonize(self):
33    """
34    do the UNIX double-fork magic, see Stevens' "Advanced
35    Programming in the UNIX Environment" for details (ISBN 0201563177)
36    http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
37    """
38    try:
39      pid = os.fork()
40      if pid > 0:
41        # exit first parent
42        sys.exit(0)
43    except OSError, e:
44      sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
45      sys.exit(1)
46
47    # decouple from parent environment
48    os.chdir("/")
49    os.setsid()
50    os.umask(0)
51
52    # do second fork
53    try:
54      pid = os.fork()
55      if pid > 0:
56        # exit from second parent
57        sys.exit(0)
58    except OSError, e:
59      sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
60      sys.exit(1)
61
62    # redirect standard file descriptors
63    sys.stdout.flush()
64    sys.stderr.flush()
65    si = file(self.stdin, 'r')
66    so = file(self.stdout, 'a+')
67    se = file(self.stderr, 'a+', 0)
68    # TODO: (debug) re-enable this!
69    #os.dup2(si.fileno(), sys.stdin.fileno())
70    #os.dup2(so.fileno(), sys.stdout.fileno())
71    #os.dup2(se.fileno(), sys.stderr.fileno())
72
73    # write pidfile
74    atexit.register(self.delpid)
75    pid = str(os.getpid())
76    file(self.pidfile, 'w+').write("%s\n" % pid)
77
78  def delpid(self):
79    os.remove(self.pidfile)
80
81  def start(self):
82    """
83    Start the daemon
84    """
85    # Check for a pidfile to see if the daemon already runs
86    try:
87      pf = file(self.pidfile, 'r')
88      pid = int(pf.read().strip())
89      pf.close()
90    except IOError:
91      pid = None
92
93    if pid:
94      message = "pidfile %s already exist. Daemon already running?\n"
95      sys.stderr.write(message % self.pidfile)
96      sys.exit(1)
97
98    # Start the daemon
99    self.daemonize()
100    self.run()
101
102  def stop(self):
103    """
104    Stop the daemon
105    """
106    # Get the pid from the pidfile
107    try:
108      pf = file(self.pidfile, 'r')
109      pid = int(pf.read().strip())
110      pf.close()
111    except IOError:
112      pid = None
113
114    if not pid:
115      message = "pidfile %s does not exist. Daemon not running?\n"
116      sys.stderr.write(message % self.pidfile)
117      return # not an error in a restart
118
119    # Try killing the daemon process
120    try:
121      # Give the process a one-second chance to exit gracefully.
122      os.kill(pid, SIGINT)
123      time.sleep(1)
124      while 1:
125        os.kill(pid, SIGTERM)
126        time.sleep(0.1)
127    except OSError, err:
128      err = str(err)
129      if err.find("No such process") > 0:
130        if os.path.exists(self.pidfile):
131          os.remove(self.pidfile)
132      else:
133        print str(err)
134        sys.exit(1)
135
136  def restart(self):
137    """
138    Restart the daemon
139    """
140    self.stop()
141    self.start()
142
143  def run(self):
144    """
145    You should override this method when you subclass Daemon. It will be
146    called after the process has been daemonized by start() or restart().
147    """
148