2
Daemon - daemon script and controller
4
This module is based on twisted.scripts.twistd, modified by Nir Soffer
5
to work with non Twisted code.
7
The Daemon class, represents a background process using a pid
8
file. When you create an instance, the process may be running or not.
9
After creating an instance, you can call one of its do_xxx() methods.
11
The DaemonScript class is a script that control a daemon, with a
12
functionality similar to apachectl. To create a daemon script, create an
13
instacne and call its run() method.
20
script = daemon.DaemonScript('myserver', myserver.run, myserver.Config)
24
Copyright (c) 2005 Nir Soffer <nirs@freeshell.org>
26
Twisted, the Framework of Your Internet
27
Copyright (c) 2001-2004 Twisted Matrix Laboratories.
29
Permission is hereby granted, free of charge, to any person obtaining
30
a copy of this software and associated documentation files (the
31
"Software"), to deal in the Software without restriction, including
32
without limitation the rights to use, copy, modify, merge, publish,
33
distribute, sublicense, and/or sell copies of the Software, and to
34
permit persons to whom the Software is furnished to do so, subject to
35
the following conditions:
37
The above copyright notice and this permission notice shall be
38
included in all copies or substantial portions of the Software.
40
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
41
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
43
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
44
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
45
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
46
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
import sys, os, errno, signal, time
52
class Error(Exception):
57
""" A background process
59
Represent a background process, which may be running or not. The
60
process can be started, stopped, restarted or killed.
64
def __init__(self, name, function, *args, **kw):
67
@param name: name of the process (determines pid filename, too)
68
@param function: the server main function, will block until the
70
@param args: arguments to pass to function
71
@param kw: keyword arguments to pass to function
74
self.function = function
77
self.pidFile = os.path.abspath(name + '.pid')
79
# --------------------------------------------------------------------
83
""" Start the daemon process
85
Start will daemonize then block until the server is killed and
86
then cleanup the pid file on the way out.
88
running, pid = self.status()
90
raise Error("another application is running with pid %s "
91
"(try restart)" % pid)
95
self.function(*self.args, **self.kw)
100
""" Stop the daemon process
102
Terminate or raise an error we can't handle here. On success,
103
the pid file will be cleaned by the terminated process.
105
running, pid = self.status()
107
return self.log("%s is not running" % self.name)
108
os.kill(pid, signal.SIGINT)
111
""" Kill the daemon process
113
Kill or raise an error which we can't handle here. Clean the
114
pid file for the killed process.
116
running, pid = self.status()
118
return self.log("%s is not running" % self.name)
119
os.kill(pid, signal.SIGKILL)
122
def do_restart(self):
123
""" stop, wait until pid file gone and start again """
124
running, pid = self.status()
126
self.log("%s is not running, trying to start" % self.name)
131
while time.time() - start < timeoutSeconds:
132
running, pid = self.status()
137
raise Error("could not start after %s seconds" % timeoutSeconds)
140
# -------------------------------------------------------------------
144
""" Return status tuple (running, pid)
146
See http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC18
155
if err.errno == errno.ESRCH:
156
# No such process or security enhancements are causing
157
# the system to deny its existence.
158
self.log("removing stale pid file: %s" % self.pidFile)
165
""" Return the pid from the pid file
167
If there is no pid file, return None. If pid file is corrupted,
168
remove it. If its not readable, raise.
172
pid = int(file(self.pidFile).read())
174
if err.errno != errno.ENOENT:
177
self.warn("removing corrupted pid file: %s" % self.pidFile)
182
""" Make the current process a daemon
184
See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16
186
if os.fork(): # launch child and...
187
os._exit(0) # kill off parent
189
if os.fork(): # launch child and...
190
os._exit(0) # kill off parent again.
192
null = os.open('/dev/null', os.O_RDWR)
197
if e.errno != errno.EBADF:
202
pid = str(os.getpid())
203
open(self.pidFile, 'wb').write(pid)
207
os.remove(self.pidFile)
209
if err.errno != errno.ENOENT:
212
def warn(self, message):
213
self.log('warning: %s' % message)
215
def log(self, message):
216
""" TODO: does it work after daemonize? """
217
sys.stderr.write(message + '\n')
220
class DaemonScript(Daemon):
221
""" A script controlling a daemon
223
TODO: add --pid-dir option?
227
""" Check commandline arguments and run a command """
230
self.usage('nothing to do')
232
self.usage("too many arguments")
234
command = sys.argv[1]
235
func = getattr(self, self.commandPrefix + command)
237
except AttributeError:
238
self.usage('unknown command %r' % command)
239
except Exception, why:
240
sys.exit("error: %s" % str(why))
242
def usage(self, message):
243
sys.stderr.write('error: %s\n' % message)
244
sys.stderr.write(self.help())
249
%(name)s - MoinMoin daemon
251
usage: %(name)s command
254
start start the server
256
restart stop then start the server
259
@copyright: 2004-2005 Thomas Waldmann, Nir Soffer
260
@license: GNU GPL, see COPYING for details.