1"""
2    ***
3    Modified generic daemon class
4    ***
5
6    Author: http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
7            www.serverdensity.com
8
9    License: http://creativecommons.org/licenses/by-sa/3.0/
10
11    Changes: 23rd Jan 2009 (David Mytton <david@serverdensity.com>)
12                 - Replaced hard coded '/dev/null in __init__ with os.devnull
13                 - Added OS check to conditionally remove code that doesn't
14                   work on OS X
15                 - Added output to console on completion
16                 - Tidied up formatting
17             11th Mar 2009 (David Mytton <david@serverdensity.com>)
18                 - Fixed problem with daemon exiting on Python 2.4 (before
19                   SystemExit was part of the Exception base)
20             13th Aug 2010 (David Mytton <david@serverdensity.com>
21                 - Fixed unhandled exception if PID file is empty
22"""
23
24# Core modules
25import atexit
26import os
27import sys
28import time
29
30from signal import SIGTERM
31
32
33class Daemon:
34    """
35    A generic daemon class.
36
37    Usage: subclass the Daemon class and override the run() method
38    """
39    def __init__(self, pidfile, stdin=os.devnull, stdout=os.devnull,
40                 stderr=os.devnull):
41        self.stdin = stdin
42        self.stdout = stdout
43        self.stderr = stderr
44        self.pidfile = pidfile
45
46    def daemonize(self):
47        """
48        Do the UNIX double-fork magic, see Stevens' "Advanced
49        Programming in the UNIX Environment" for details (ISBN 0201563177)
50        http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
51        """
52        try:
53            pid = os.fork()
54            if pid > 0:
55                # Exit first parent
56                sys.exit(0)
57        except OSError, e:
58            sys.stderr.write(
59                "fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
60            sys.exit(1)
61
62        # Decouple from parent environment
63        os.chdir("/")
64        os.setsid()
65        os.umask(0)
66
67        # Do second fork
68        try:
69            pid = os.fork()
70            if pid > 0:
71                # Exit from second parent
72                sys.exit(0)
73        except OSError, e:
74            sys.stderr.write(
75                "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
76            sys.exit(1)
77
78        if sys.platform != 'darwin':  # This block breaks on OS X
79            # Redirect standard file descriptors
80            sys.stdout.flush()
81            sys.stderr.flush()
82            si = open(self.stdin, 'r')
83            so = open(self.stdout, 'a+')
84            se = open(self.stderr, 'a+', 0)
85            os.dup2(si.fileno(), sys.stdin.fileno())
86            os.dup2(so.fileno(), sys.stdout.fileno())
87            os.dup2(se.fileno(), sys.stderr.fileno())
88
89        print "Started"
90
91        # Write pidfile
92        # Make sure pid file is removed if we quit
93        atexit.register(self.delpid)
94        pid = str(os.getpid())
95        pid_handler = open(self.pidfile, 'w+')
96        pid_handler.write("%s\n" % pid)
97        pid_handler.close()
98
99    def delpid(self):
100        os.remove(self.pidfile)
101
102    def start(self):
103        """
104        Start the daemon
105        """
106
107        print "Starting..."
108
109        # Check for a pidfile to see if the daemon already runs
110        pid = None
111        try:
112            pf = open(self.pidfile, 'r')
113            pid = int(pf.read().strip())
114            pf.close()
115        except (IOError, SystemExit):
116            pass
117
118        if pid:
119            message = "pidfile %s already exists. Is it already running?\n"
120            sys.stderr.write(message % self.pidfile)
121            sys.exit(1)
122
123        # Start the daemon
124        self.daemonize()
125        self.run()
126
127    def stop(self):
128        """
129        Stop the daemon
130        """
131
132        print "Stopping..."
133
134        # Get the pid from the pidfile
135        pid = None
136        try:
137            pf = open(self.pidfile, 'r')
138            pid = int(pf.read().strip())
139            pf.close()
140        except (IOError, ValueError):
141            pass
142
143        if not pid:
144            message = "pidfile %s does not exist. Not running?\n"
145            sys.stderr.write(message % self.pidfile)
146
147            # Just to be sure. A ValueError might occur if the PID file is
148            # empty but does actually exist
149            if os.path.exists(self.pidfile):
150                os.remove(self.pidfile)
151
152            return  # Not an error in a restart
153
154        # Try killing the daemon process
155        try:
156            while 1:
157                os.kill(pid, SIGTERM)
158                time.sleep(0.1)
159        except OSError, err:
160            err = str(err)
161            if err.find("No such process") > 0:
162                if os.path.exists(self.pidfile):
163                    os.remove(self.pidfile)
164            else:
165                print str(err)
166                sys.exit(1)
167
168        print "Stopped"
169
170    def restart(self):
171        """
172        Restart the daemon
173        """
174        self.stop()
175        self.start()
176
177    def run(self):
178        """
179        You should override this method when you subclass Daemon. It will be
180        called after the process has been daemonized by start() or restart().
181        """
182        pass
183