]> kaliko git repositories - python-daemon.git/blob - src/daemon.py
some clean up in error handling.
[python-daemon.git] / src / daemon.py
1 # -*- coding: utf-8 -*-
2
3 # Public Domain
4 #
5 # Copyright 2007, 2009 Sander Marechal <s.marechal@jejik.com>
6 # Copyright 2010, 2011 Jack Kaliko <efrim@azylum.org>
7 #
8 # http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
9
10 import atexit
11 import os
12 import sys
13 import time
14 from signal import signal, SIGTERM
15
16
17 class Daemon(object):
18     """
19     A generic daemon class.
20
21     Usage: subclass the Daemon class and override the run() method
22     """
23     version = "0.4"
24
25     def __init__(self, pidfile,
26             stdin='/dev/null',
27             stdout='/dev/null',
28             stderr='/dev/null'):
29         self.stdin = stdin
30         self.stdout = stdout
31         self.stderr = stderr
32         self.pidfile = pidfile
33         self.umask = 0
34
35     def daemonize(self):
36         """
37         do the UNIX double-fork magic, see Stevens' "Advanced
38         Programming in the UNIX Environment" for details (ISBN 0201563177)
39         http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
40         """
41         try:
42             pid = os.fork()
43             if pid > 0:
44                 # exit first parent
45                 sys.exit(0)
46         except OSError, e:
47             sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
48             sys.exit(1)
49
50         # Decouple from parent environment
51         os.chdir("/")
52         os.setsid()
53         self.umask = os.umask(0)
54
55         # Do second fork
56         try:
57             pid = os.fork()
58             if pid > 0:
59                 # exit from second parent
60                 sys.exit(0)
61         except OSError, e:
62             sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
63             sys.exit(1)
64
65         self.write_pid()
66         # redirect standard file descriptors
67         sys.stdout.flush()
68         sys.stderr.flush()
69         si = file(self.stdin, 'r')
70         so = file(self.stdout, 'a+')
71         se = file(self.stderr, 'a+', 0)
72         os.dup2(si.fileno(), sys.stdin.fileno())
73         os.dup2(so.fileno(), sys.stdout.fileno())
74         os.dup2(se.fileno(), sys.stderr.fileno())
75
76         atexit.register(self.shutdown)
77         self.signal_management()
78
79     def write_pid(self):
80         # write pidfile
81         if not self.pidfile:
82             return
83         pid = str(os.getpid())
84         try:
85             os.umask(self.umask)
86             file(self.pidfile, 'w').write('%s\n' % pid)
87         except Exception, wpid_err:
88             sys.stderr.write(u'Error trying to write pid file: %s\n' % wpid_err)
89             sys.exit(1)
90         os.umask(0)
91         atexit.register(self.delpid)
92
93     def signal_management(self):
94         # Declare signal handlers
95         signal(SIGTERM, self.exit_handler)
96
97     def exit_handler(self, signum, frame):
98         sys.exit(1)
99
100     def delpid(self):
101         try:
102             os.unlink(self.pidfile)
103         except OSError as err:
104             message = 'Error trying to remove PID file: %s\n'
105             sys.stderr.write(message % err)
106
107     def start(self):
108         """
109         Start the daemon
110         """
111         # Check for a pidfile to see if the daemon already runs
112         try:
113             pf = file(self.pidfile, 'r')
114             pid = int(pf.read().strip())
115             pf.close()
116         except IOError:
117             pid = None
118
119         if pid:
120             message = "pidfile %s already exist. Daemon already running?\n"
121             sys.stderr.write(message % self.pidfile)
122             sys.exit(1)
123
124         # Start the daemon
125         self.daemonize()
126         self.run()
127
128     def foreground(self):
129         """
130         Foreground/debug mode
131         """
132         self.write_pid()
133         atexit.register(self.shutdown)
134         self.run()
135
136     def stop(self):
137         """
138         Stop the daemon
139         """
140         # Get the pid from the pidfile
141         try:
142             pf = file(self.pidfile, 'r')
143             pid = int(pf.read().strip())
144             pf.close()
145         except IOError:
146             pid = None
147
148         if not pid:
149             message = "pidfile %s does not exist. Is the Daemon running?\n"
150             sys.stderr.write(message % self.pidfile)
151             return  # not an error in a restart
152
153         # Try killing the daemon process
154         try:
155             os.kill(pid, SIGTERM)
156             time.sleep(0.1)
157         except OSError, err:
158             if err.errno == 3:
159                 if os.path.exists(self.pidfile):
160                     message = "Daemon's not running? removing pid file %s.\n"
161                     sys.stderr.write(message % self.pidfile)
162                     os.remove(self.pidfile)
163             else:
164                 sys.stderr.write(err.strerror)
165                 sys.exit(1)
166
167     def restart(self):
168         """
169         Restart the daemon
170         """
171         self.stop()
172         self.start()
173
174     def shutdown(self):
175         """
176         You should override this method when you subclass Daemon. It will be
177         called when the process is being stopped.
178         Pay attention:
179         Daemon() uses atexit to call Daemon().shutdown(), as a consequence
180         shutdown and any other functions registered via this module are not
181         called when the program is killed by an un-handled/unknown signal.
182         This is the reason of Daemon().signal_management() existence.
183         """
184
185     def run(self):
186         """
187         You should override this method when you subclass Daemon. It will be
188         called after the process has been daemonized by start() or restart().
189         """
190
191
192 def main():
193     pass
194
195 # Script starts here
196 if __name__ == '__main__':
197     main()
198
199 # VIM MODLINE
200 # vim: ai ts=4 sw=4 sts=4 expandtab