forked from dimier/python-daemon
- Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpythonDaemon.py
More file actions
Latest commit
234 lines (197 loc) · 6.71 KB
/
Copy pathpythonDaemon.py
File metadata and controls
234 lines (197 loc) · 6.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
"""
***
Modified generic daemon class
***
Author: http://www.jejik.com/articles/2007/02/
a_simple_unix_linux_daemon_in_python/www.boxedice.com
https://github.com/serverdensity/python-daemon
License: http://creativecommons.org/licenses/by-sa/3.0/
Changes: Various fixes where added in signal handling, pid file handling,
return codes and exception handling
"""
# Core modules
importatexit
importos
importsys
importtime
importsignal
importlogging
importtraceback
classDaemon(object):
"""
A generic daemon class.
Usage: subclass the Daemon class and override the run() method
"""
def__init__(self, pidfile, stdin=os.devnull,
stdout=os.devnull, stderr=os.devnull,
home_dir='.', umask=022, verbose=1):
self.stdin=stdin
self.stdout=stdout
self.stderr=stderr
self.pidfile=pidfile
self.home_dir=home_dir
self.verbose=verbose
self.umask=umask
defdaemonize(self):
"""
Do the UNIX double-fork magic, see Stevens' "Advanced
Programming in the UNIX Environment" for details (ISBN 0201563177)
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
"""
try:
pid=os.fork()
ifpid>0:
# Exit first parent
sys.exit(0)
exceptOSError, e:
sys.stderr.write(
"fork #1 failed: %d (%s)\n"% (e.errno, e.strerror))
os._exit(1)
# Decouple from parent environment
os.chdir(self.home_dir)
os.setsid()
os.umask(self.umask)
# Do second fork
try:
pid=os.fork()
ifpid>0:
# Exit from second parent
sys.exit(0)
exceptOSError, e:
sys.stderr.write(
"fork #2 failed: %d (%s)\n"% (e.errno, e.strerror))
os._exit(1)
ifsys.platform!='darwin': # This block breaks on OS X
# Redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si=file(self.stdin, 'r')
so=file(self.stdout, 'a+')
ifself.stderr:
se=file(self.stderr, 'a+', 0)
else:
se=so
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
signal.signal(signal.SIGTERM, self._shutdown)
signal.signal(signal.SIGINT, self._shutdown)
ifself.verbose>=1:
logging.info("Started")
# Write pidfile
atexit.register(
self.delpid) # Make sure pid file is removed if we quit
pid=str(os.getpid())
file(self.pidfile, 'w+').write("%s\n"%pid)
defdelpid(self):
ifos.path.exists(self.pidfile):
try:
os.remove(self.pidfile)
exceptOSError, e:
sys.stderr.write("Could not delete pidfile: %d (%s)\n"% (e.errno, e.strerror))
defstart(self, return_on_exit=False, overwrite_pid=False, *args, **kwargs):
"""
Start the daemon
"""
ifself.verbose>=1:
logging.info('Start daemon')
# Check for a pidfile to see if the daemon already runs
ifos.path.exists(self.pidfile):
ifoverwrite_pid:
self.delpid()
else:
message="pidfile %s already exists. Daemon already running?\n"
sys.stderr.write(message%self.pidfile)
ifreturn_on_exit:
returnFalse
sys.exit(1)
# Start the daemon
_exitcode=0
self.daemonize()
try:
logging.info('Running process')
self.run(*args, **kwargs)
logging.info('Process finished normally.')
exceptExceptionase:
logging.error('Daemonized process threw an exception [%s].'%e)
tb=traceback.format_exc()
logging.error(tb)
_exitcode=255
finally:
# Make sure the pid file gets deleted even in case of errors
self.delpid()
ifreturn_on_exit:
return_exitcodeif_exitcodeelseFalse
sys.exit(_exitcode)
def_shutdown(self, signum=None, frame=None):
self.shutdown(frame)
defshutdown(self, frame=None):
os.kill(self.pid, signal.SIGTERM)
defstop(self):
"""
Stop the daemon
"""
ifself.verbose>=1:
logging.info("Stopping daemon...")
# Get the pid from the pidfile
pid=self.get_pid()
ifnotpid:
ifos.path.exists(self.pidfile):
message="pidfile %s does not exist. Daemon not running?\n"
else:
message="could not read pid from pidfile %s\n"
sys.stderr.write(message%self.pidfile)
# Just to be sure. A ValueError might occur if the PID file is
# empty but does actually exist
self.delpid()
return# Not an error in a restart
# Try killing the daemon process
_signal=signal.SIGTERM
try:
os.kill(pid, _signal)
_stime=time.time() +10
while1:
os.getpgid(pid)
time.sleep(0.1)
iftime.time() >_stime:
os.kill(pid, _signal)
_signal=signal.SIGKILL
_stime+=10
exceptOSError, err:
err=str(err)
iferr.find("No such process") >0:
self.delpid()
else:
printstr(err)
sys.exit(1)
ifself.verbose>=1:
logging.info("Stopped daemon")
defrestart(self):
"""
Restart the daemon
"""
self.stop()
self.start()
defget_pid(self):
try:
pf=file(self.pidfile, 'r')
pid=int(pf.read().strip())
pf.close()
exceptIOError:
pid=None
exceptSystemExit:
pid=None
returnpid
defis_running(self):
returnself.running
@property
defrunning(self):
pid=self.get_pid()
returnpidandos.path.exists('/proc/%d'%pid)
defrun(self):
"""
You should override this method when you subclass Daemon.
It will be called after the process has been
daemonized by start() or restart().
"""
raiseNotImplemented('You should override this method when you subclass Daemon')