1
2
3 '''
4 Disk And Execution MONitor (Daemon)
5
6 daemonize()
7 Detach the current process from the calling terminal so it can
8 run as a service.
9 startstop()
10 Check the first argument to the program.
11
12 start: start the daemon
13 stop: stop the daemon
14 restart: stop the daemon if running, then start it again
15 status: display running status
16 run: run but not in daemon mode
17
18 Configurable daemon behaviors:
19
20 1.) The current working directory set to the "/" directory.
21 2.) The current file creation mode mask set to 0.
22 3.) Close all open files (1024).
23 4.) Redirect standard I/O streams to "/dev/null".
24
25 Almost none of this is necessary (or advisable) if your daemon
26 is being started by inetd. In that case, stdin, stdout and stderr are
27 all set up for you to refer to the network connection, and the fork()s
28 and session manipulation should not be done (to avoid confusing inetd).
29 Only the chdir() and umask() steps remain as useful.
30
31 References::
32
33 UNIX Programming FAQ 1.7 How do I get my program to act like a daemon?
34 http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
35 Advanced Programming in the Unix Environment
36 W. Richard Stevens, 1992, Addison-Wesley, ISBN 0-201-56317-7.
37
38 History:
39 2001/07/10 by JÃŒrgen Hermann
40 2002/08/28 by Noah Spurrier
41 2003/02/24 by Clark Evans
42 2005/10/03 by Chad J. Schroeder
43 2008/11/05 by Paul Kienzle
44
45 Based on http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012
46 '''
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 import sys, os, time, errno
73 from signal import SIGTERM
74
75 UMASK = 0
76 WORKDIR = "/"
77 MAXFD = 1024
78
79 if hasattr(os, "devnull"):
80 REDIRECT_TO = os.devnull
81 else:
82 REDIRECT_TO = "/dev/null"
83
84
85
86
87
88
89
90
91 exit = os._exit
92
94 """
95 Close all open file descriptors. This prevents the child from keeping
96 open any file descriptors inherited from the parent.
97 """
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 import resource
124 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
125 if maxfd == resource.RLIM_INFINITY:
126 maxfd = MAXFD
127
128
129 for fd in range(0, maxfd):
130 try: os.close(fd)
131 except OSError: pass
132
133
136 """
137 This forks the current process into a daemon.
138
139 The stdin, stdout, and stderr arguments are file names that will
140 be opened and be used to replace the standard file descriptors
141 in sys.stdin, sys.stdout, and sys.stderr.
142 These arguments are optional and default to /dev/null.
143
144 Note that stderr is opened unbuffered, so if it shares a file with
145 stdout then interleaved output may not appear in the order you expect.
146 """
147
148
149
150
151
152
153 try:
154 pid = os.fork()
155 if pid > 0: exit(0)
156 except OSError, e:
157 raise Exception, "[%d] %s" % (e.errno, e.strerror)
158
159
160 os.chdir(WORKDIR)
161 os.umask(UMASK)
162 os.setsid()
163
164
165
166
167
168
169
170
171
172 try:
173 pid = os.fork()
174 if pid > 0: exit(0)
175 except OSError, e:
176 raise Exception, "[%d] %s" % (e.errno, e.strerror)
177
178
179 pid = str(os.getpid())
180 if pidfile:
181 fd = open(pidfile,'w+')
182 fd.write("%s\n" % pid)
183 fd.flush()
184 fd.close()
185
186
187 if startmsg: sys.stderr.write("\n%s\n" % startmsg % pid)
188 sys.stdout.flush()
189 sys.stderr.flush()
190
191
192
193
194
195 if not stderr: stderr = stdout
196 fin = file(stdin, "r")
197 fout = file(stdout, "a+")
198 ferr = file(stderr, "a+")
199 os.dup2(fin.fileno(), 0)
200 os.dup2(fout.fileno(), 1)
201 os.dup2(ferr.fileno(), 2)
202
204 try:
205 pf = file(pidfile,'r')
206 pid = int(pf.read().strip())
207 pf.close()
208 except IOError:
209 pid = None
210 return pid
211
213 """
214 Check if the given process is running.
215
216 Note that this just checks that the pid is in use; since process
217 ids can be reused, this isn't a reliable test.
218 """
219
220
221
222 try:
223 os.kill(pid, 0)
224 return 1
225 except OSError, err:
226 return err.errno == errno.EPERM
227
230 """
231 Process start/stop/restart/status/run commands.
232
233 Start/stop/restart allow the process to be used as an init.d service.
234 Run runs the process without daemonizing, e.g., from inittab.
235 """
236 if len(sys.argv) > 1:
237 action = sys.argv[1]
238 pid = readpid(pidfile)
239 if 'stop' == action or ('restart' == action and pid):
240 if not pid:
241 msg = "Could not stop, pid file '%s' missing."%pidfile
242 sys.stderr.write('%s\n'%msg)
243 sys.exit(1)
244
245 try:
246 while 1:
247 os.kill(pid,SIGTERM)
248 time.sleep(1)
249 except OSError, err:
250 err = str(err)
251 if err.find("No such process") > 0:
252 os.remove(pidfile)
253 if 'stop' == action:
254 sys.exit(0)
255
256 pid = None
257 else:
258 raise
259
260 if action in ['start', 'restart', 'run']:
261 if pid:
262 msg = "Start aborted since pid file '%s' exists."%pidfile
263 sys.stderr.write('%s\n'%msg)
264 sys.exit(1)
265
266
267 if action != 'run':
268 daemonize(stdout=stdout,stderr=stderr,stdin=stdin,
269 pidfile=pidfile,startmsg=startmsg)
270 return
271
272 if action == 'status':
273 if not pid:
274 status='stopped'
275 elif not process_is_running(pid):
276 status="failed, but pid file '%s' still exists."%pidfile
277 else:
278 status='running with pid %d'%pid
279 sys.stderr.write('Status: %s\n'%status)
280 sys.exit(0)
281
282 print "usage: %s start|stop|restart|status|run" % sys.argv[0]
283 sys.exit(2)
284
286 """
287 This is an example main function run by the daemon.
288 This prints a count and timestamp once per second.
289 """
290 sys.stdout.write ('Message to stdout...\n')
291 sys.stderr.write ('Message to stderr...\n')
292 c = 0
293 while 1:
294 sys.stdout.write ('%d: %s\n' % (c, time.ctime(time.time())) )
295 sys.stdout.flush()
296 c = c + 1
297 time.sleep(1)
298
299 if __name__ == "__main__":
300 startstop(stdout='/tmp/daemon.log', pidfile='/tmp/daemon.pid')
301 test()
302