]> kaliko git repositories - python-daemon.git/blob - src/daemon.py
787cb65681ae720fbfe48e0d5e8da86239d52c19
[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     def __init__(self, pidfile,
24             stdin='/dev/null',
25             stdout='/dev/null',
26             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         self.write_pid()
63         # redirect standard file descriptors
64         sys.stdout.flush()
65         sys.stderr.flush()
66         si = file(self.stdin, 'r')
67         so = file(self.stdout, 'a+')
68         se = file(self.stderr, 'a+', 0)
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         atexit.register(self.shutdown)
74         self.signal_management()
75
76     def write_pid(self):
77         # write pidfile
78         if not self.pidfile:
79             return
80         pid = str(os.getpid())
81         try:
82             file(self.pidfile, 'w').write('%s\n' % pid)
83             #except IOError, wpid_err:
84         except Exception, wpid_err:
85             sys.stderr.write(u'Error trying to write pid file to %s: %s\n' %
86                    (unicode(self.pidfile, 'utf-8'), wpid_err))
87             sys.exit(1)
88         atexit.register(self.delpid)
89
90     def signal_management(self):
91         # Declare signal handlers
92         signal(SIGTERM, self.exit_handler)
93
94     def exit_handler(self, signum, frame):
95         sys.exit(1)
96
97     def delpid(self):
98         os.remove(self.pidfile)
99
100     def start(self):
101         """
102         Start the daemon
103         """
104         # Check for a pidfile to see if the daemon already runs
105         try:
106             pf = file(self.pidfile, 'r')
107             pid = int(pf.read().strip())
108             pf.close()
109         except IOError:
110             pid = None
111
112         if pid:
113             message = "pidfile %s already exist. Daemon already running?\n"
114             sys.stderr.write(message % self.pidfile)
115             sys.exit(1)
116
117         # Start the daemon
118         self.daemonize()
119         self.run()
120
121     def foreground(self):
122         """
123         Foreground/debug mode
124         """
125         self.write_pid()
126         atexit.register(self.shutdown)
127         self.run()
128
129     def stop(self):
130         """
131         Stop the daemon
132         """
133         # Get the pid from the pidfile
134         try:
135             pf = file(self.pidfile, 'r')
136             pid = int(pf.read().strip())
137             pf.close()
138         except IOError:
139             pid = None
140
141         if not pid:
142             message = "pidfile %s does not exist. Daemon not running?\n"
143             sys.stderr.write(message % self.pidfile)
144             return  # not an error in a restart
145
146         # Try killing the daemon process
147         try:
148             os.kill(pid, SIGTERM)
149             time.sleep(0.1)
150         except OSError, err:
151             err = str(err)
152             if err.find("No such process") > 0:
153                 if os.path.exists(self.pidfile):
154                     message = "Daemon not running? removing pid file %s.\n"
155                     sys.stderr.write(message % self.pidfile)
156                     os.remove(self.pidfile)
157             else:
158                 print str(err)
159                 sys.exit(1)
160
161     def restart(self):
162         """
163         Restart the daemon
164         """
165         self.stop()
166         self.start()
167
168     def shutdown(self):
169         """
170         You should override this method when you subclass Daemon. It will be
171         called when the process is being stopped.
172         Pay attention:
173         Daemon() uses atexit to call Daemon().shutdown(), as a consequence
174         shutdown and any other functions registered via this module are not
175         called when the program is killed by an un-handled/unknown signal.
176         This is the reason of Daemon().signal_management() existence.
177         """
178
179     def run(self):
180         """
181         You should override this method when you subclass Daemon. It will be
182         called after the process has been daemonized by start() or restart().
183         """
184
185
186 def main():
187     pass
188
189 # Script starts here
190 if __name__ == '__main__':
191     main()
192
193 # VIM MODLINE
194 # vim: ai ts=4 sw=4 sts=4 expandtab