[Zodb-checkins] CVS: ZODB3/zdaemon - zdaemon.py:1.7
Guido van Rossum
guido@python.org
Mon, 11 Nov 2002 15:17:45 -0500
Update of /cvs-repository/ZODB3/zdaemon
In directory cvs.zope.org:/tmp/cvs-serv15163
Modified Files:
zdaemon.py
Log Message:
Refactoring of command handling -- commands are now defined by methods
whose name begins with 'cmd_'. The signature is always
cmd_foo(self, args) where args is a list of arguments and args[0] is
'foo'.
=== ZODB3/zdaemon/zdaemon.py 1.6 => 1.7 ===
--- ZODB3/zdaemon/zdaemon.py:1.6 Mon Nov 11 12:38:39 2002
+++ ZODB3/zdaemon/zdaemon.py Mon Nov 11 15:17:45 2002
@@ -20,9 +20,10 @@
[command] -- the command to send to the daemon manager (default "status")
Client commands are:
- status -- report application status
- kill [signal] -- send the given signal number to the application
- (default signal is 14, i.e. SIGTERM)
+ help -- return command help
+ status -- report application status (this is the default command)
+ kill [signal] -- send a signal to the application
+ (default signal is SIGTERM)
This daemon manager has two purposes: it restarts the application when
it dies, and (when requested to do so with the -d option) it runs the
@@ -51,18 +52,14 @@
"""
XXX TO DO
-- Refactor the client/server code to be less ugly; avoid blocking
- recv() calls; don't assume send() always sends everything.
+- Refactor the readcommand() to avoid blocking recv() calls.
- Rethink client commands; maybe start/restart/stop make more sense?
(Still need a way to send an arbitrary signal)
-- Should it be possible to change other parameters from the client?
- (e.g. filename, command, backoff etc.)
-
- Do the governor without actual sleeps, using event scheduling etc.
-- Add docstrings
+- Add docstrings.
"""
@@ -287,62 +284,98 @@
self.appid = 0
self.reportstatus(wpid, wsts)
if r:
- try:
- self.readcommand()
- except:
- self.problem("Exception in readcommand()")
+ self.readcommand()
+
+ conn = None
def readcommand(self):
- conn, addr = self.controlsocket.accept()
try:
- data = conn.recv(1000)
- if not data:
- conn.send("No input\n")
- return
- line = data
- while "\n" not in line:
+ conn, addr = self.controlsocket.accept()
+ self.conn = conn
+ try:
data = conn.recv(1000)
if not data:
- conn.send("Incomplete input\n")
+ self.sendreply("No input")
return
- line += data
- lines = line.split("\n")
- words = lines[0].split()
- if not words:
- conn.send("Empty command\n")
- return
- command = words[0]
- if command == "status":
- if not self.appid:
- status = "not running"
- else:
- status = "running"
- conn.send("Application %s\n" % status +
- "pid=%d\n" % self.appid +
- "filename=%s\n" % repr(self.filename) +
- "args=%s\n" % repr(self.args))
- elif command == "kill":
- if words[1:]:
- try:
- sig = int(words[1])
- except:
- conn.send("Bad signal %s\n" % repr(words[1]))
+ line = data
+ while "\n" not in line:
+ data = conn.recv(1000)
+ if not data:
+ self.sendreply("Input not terminated by newline")
return
- else:
- sig = signal.SIGTERM
- if not self.appid:
- conn.send("Application not running\n")
- else:
- try:
- os.kill(self.appid, sig)
- except os.error, msg:
- conn.send("Kill %d failed: %s\n" % (sig, str(msg)))
- else:
- conn.send("Signal %d sent\n" % sig)
+ line += data
+ self.docommand(line)
+ finally:
+ conn.close()
+ self.conn = None
+ except socket.error, msg:
+ self.problem("socket error: %s" % str(msg),
+ error=sys.exc_info())
+
+ def docommand(self, line):
+ lines = line.split("\n")
+ args = lines[0].split()
+ if not args:
+ self.sendreply("Empty command")
+ return
+ command = args[0]
+ methodname = "cmd_" + command
+ method = getattr(self, methodname, None)
+ if method:
+ method(args)
+ else:
+ self.sendreply("Unknown command %s" % (`args[0]`))
+
+ def cmd_kill(self, args):
+ if args[1:]:
+ try:
+ sig = int(args[1])
+ except:
+ self.sendreply("Bad signal %s" % repr(args[1]))
+ return
+ else:
+ sig = signal.SIGTERM
+ if not self.appid:
+ self.sendreply("Application not running")
+ else:
+ try:
+ os.kill(self.appid, sig)
+ except os.error, msg:
+ self.sendreply("Kill %d failed: %s" % (sig, str(msg)))
else:
- conn.send("Unknown command %s\n" % (`words[0]`))
- finally:
- conn.close()
+ self.sendreply("Signal %d sent" % sig)
+
+ def cmd_status(self, args):
+ if not self.appid:
+ status = "stopped"
+ else:
+ status = "running"
+ self.sendreply("status=%s\n" % status +
+ "manager=%d\n" % os.getpid() +
+ "application=%d\n" % self.appid +
+ "filename=%s\n" % repr(self.filename) +
+ "args=%s\n" % repr(self.args))
+
+ def cmd_help(self, args):
+ self.sendreply(
+ "Available commands:\n"
+ " help -- return command help\n"
+ " status -- report application status (default command)\n"
+ " kill [signal] -- send a signal to the application\n"
+ " (default signal is SIGTERM)\n"
+ )
+
+ def sendreply(self, msg):
+ if not msg.endswith("\n"):
+ msg = msg + "\n"
+ conn = self.conn
+ if hasattr(conn, "sendall"):
+ conn.sendall(msg)
+ else:
+ # This is quadratic, but msg is rarely more than 100 bytes :-)
+ while msg:
+ sent = conn.send(msg)
+ msg = msg[sent:]
backoff = 0
lasttime = None
@@ -402,10 +435,8 @@
else:
self.warning(msg)
elif os.WIFSIGNALED(sts):
- signum = os.WTERMSIG(sts)
- signame = self.signame(signum)
- msg = ("pid %d: terminated by signal %s(%s)" %
- (pid, signame, signum))
+ sig = os.WTERMSIG(sts)
+ msg = ("pid %d: terminated by %s" % (pid, self.signame(sig)))
if hasattr(os, "WCOREDUMP"):
iscore = os.WCOREDUMP(sts)
else:
@@ -428,7 +459,7 @@
if self.signames is None:
self.setupsignames()
- return self.signames.get(sig, "unknown")
+ return self.signames.get(sig) or "signal %d" % sig
def setupsignames(self):
self.signames = {}
@@ -470,17 +501,17 @@
def warning(self, msg):
self.log(msg, zLOG.WARNING)
- def problem(self, msg):
- self.log(msg, zLOG.ERROR)
+ def problem(self, msg, error=None):
+ self.log(msg, zLOG.ERROR, error)
- def panic(self, msg):
- self.log(msg, zLOG.PANIC)
+ def panic(self, msg, error=None):
+ self.log(msg, zLOG.PANIC, error)
def getsubsystem(self):
return "ZD:%d" % os.getpid()
- def log(self, msg, severity=zLOG.INFO):
- zLOG.LOG(self.getsubsystem(), severity, msg)
+ def log(self, msg, severity=zLOG.INFO, error=None):
+ zLOG.LOG(self.getsubsystem(), severity, msg, "", error)
def main(args=None):
d = Daemonizer()