]> kaliko git repositories - python-daemon.git/blob - src/daemon.py
Add example script
[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         self.umask = 0
32
33     def daemonize(self):
34         """
35         do the UNIX double-fork magic, see Stevens' "Advanced
36         Programming in the UNIX Environment" for details (ISBN 0201563177)
37         http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
38         """
39         try:
40             pid = os.fork()
41             if pid > 0:
42                 # exit first parent
43                 sys.exit(0)
44         except OSError, e:
45             sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
46             sys.exit(1)
47
48         # Decouple from parent environment
49         os.chdir("/")
50         os.setsid()
51         self.umask = os.umask(0)
52
53         # Do second fork
54         try:
55             pid = os.fork()
56             if pid > 0:
57                 # exit from second parent
58                 sys.exit(0)
59         except OSError, e:
60             sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
61             sys.exit(1)
62
63         self.write_pid()
64         # redirect standard file descriptors
65         sys.stdout.flush()
66         sys.stderr.flush()
67         si = file(self.stdin, 'r')
68         so = file(self.stdout, 'a+')
69         se = file(self.stderr, 'a+', 0)
70         os.dup2(si.fileno(), sys.stdin.fileno())
71         os.dup2(so.fileno(), sys.stdout.fileno())
72         os.dup2(se.fileno(), sys.stderr.fileno())
73
74         atexit.register(self.shutdown)
75         self.signal_management()
76
77     def write_pid(self):
78         # write pidfile
79         if not self.pidfile:
80             return
81         pid = str(os.getpid())
82         try:
83             os.umask(self.umask)
84             file(self.pidfile, 'w').write('%s\n' % pid)
85             #except IOError, wpid_err:
86         except Exception, wpid_err:
87             sys.stderr.write(u'Error trying to write pid file to %s: %s\n' %
88                    (unicode(self.pidfile, 'utf-8'), 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         os.unlink(self.pidfile)
102
103     def start(self):
104         """
105         Start the daemon
106         """
107         # Check for a pidfile to see if the daemon already runs
108         try:
109             pf = file(self.pidfile, 'r')
110             pid = int(pf.read().strip())
111             pf.close()
112         except IOError:
113             pid = None
114
115         if pid:
116             message = "pidfile %s already exist. Daemon already running?\n"
117             sys.stderr.write(message % self.pidfile)
118             sys.exit(1)
119
120         # Start the daemon
121         self.daemonize()
122         self.run()
123
124     def foreground(self):
125         """
126         Foreground/debug mode
127         """
128         self.write_pid()
129         atexit.register(self.shutdown)
130         self.run()
131
132     def stop(self):
133         """
134         Stop the daemon
135         """
136         # Get the pid from the pidfile
137         try:
138             pf = file(self.pidfile, 'r')
139             pid = int(pf.read().strip())
140             pf.close()
141         except IOError:
142             pid = None
143
144         if not pid:
145             message = "pidfile %s does not exist. Daemon not running?\n"
146             sys.stderr.write(message % self.pidfile)
147             return  # not an error in a restart
148
149         # Try killing the daemon process
150         try:
151             os.kill(pid, SIGTERM)
152             time.sleep(0.1)
153         except OSError, err:
154             err = str(err)
155             if err.find("No such process") > 0:
156                 if os.path.exists(self.pidfile):
157                     message = "Daemon not running? removing pid file %s.\n"
158                     sys.stderr.write(message % self.pidfile)
159                     os.remove(self.pidfile)
160             else:
161                 print str(err)
162                 sys.exit(1)
163
164     def restart(self):
165         """
166         Restart the daemon
167         """
168         self.stop()
169         self.start()
170
171     def shutdown(self):
172         """
173         You should override this method when you subclass Daemon. It will be
174         called when the process is being stopped.
175         Pay attention:
176         Daemon() uses atexit to call Daemon().shutdown(), as a consequence
177         shutdown and any other functions registered via this module are not
178         called when the program is killed by an un-handled/unknown signal.
179         This is the reason of Daemon().signal_management() existence.
180         """
181
182     def run(self):
183         """
184         You should override this method when you subclass Daemon. It will be
185         called after the process has been daemonized by start() or restart().
186         """
187
188
189 def main():
190     pass
191
192 # Script starts here
193 if __name__ == '__main__':
194     main()
195
196 # VIM MODLINE
197 # vim: ai ts=4 sw=4 sts=4 expandtab