[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[minion-cvs] Many hacks, checkpointing.
Update of /home/minion/cvsroot/src/minion/lib/mixminion/server
In directory moria.mit.edu:/tmp/cvs-serv24513/lib/mixminion/server
Modified Files:
Modules.py ServerConfig.py ServerMain.py
Added Files:
EventStats.py
Log Message:
Many hacks, checkpointing.
README, Main, ServerMain:
- Rename "mixminion server" to "mixminion server-start" for consistency.
The old command is there, but deprecated.
BuildMessage, ClientMain:
- Die with a useful error message if the path is too long to fit the
address in the last hop. (This can happen even if path_len < 32.)
- Remove dead code for "stateful" reply blocks.
- Use multiple SURB keys to detect which identity was used; resists
George's SURB-swapping attack.
ClientMain:
- s/www.mixminion.net/mixminion.net/
- Beautify list-servers output a little
- Change key storage format to allow multiple keys to be stored with same
password
- Avoid dumping binary messages to ttys unless --force is given.
Main:
- Don't print a stack trace when the user hits ctrl-c.
HashLog:
- Correct comment
ServerMain, EventStats, Modules, ServerConfig, Main, mixminiond.conf:
- Beginnings of code to track server statistics.
--- NEW FILE: EventStats.py ---
# Copyright 2002-2003 Nick Mathewson. See LICENSE for licensing information.
# $Id: EventStats.py,v 1.1 2003/03/26 16:36:46 nickm Exp $
"""mixminion.server.EventStats
Classes to gather time-based server statistics"""
__all__ = [ 'EventLog', 'NilEventLog' ]
import cPickle
import os
from threading import RLock
from time import time
from mixminion.Common import formatTime, LOG
EVENTS = [ 'ReceivedPacket',
'AttemptedRelay',
'SuccessfulRelay', 'FailedRelay', 'UnretriableRelay',
'AttemptedDelivery',
'SuccessfulDelivery', 'FailedDelivery', 'UnretriableDelivery',
]
class NilEventLog:
def __init__(self):
pass
def save(self):
pass
def log(self, event, arg=None):
pass
class EventLog(NilEventLog):
# Fields:
# count (event -> arg -> value)
# lastRotation
# filename, historyFile
# rotateInterval
def __init__(self, filename, historyFile, interval):
NilEventLog.__init__(self)
if os.path.exists(filename):
# XXXX If this doesn't work, then we should
f = open(filename, 'rb')
self.__dict__.update(cPickle.load(f))
f.close()
assert self.count is not None
assert self.lastRotation is not None
else:
self.count = {}
for e in EVENTS:
self.count[e] = {}
self.lastRotation = time()
self.filename = filename
self.historyFilename = historyFile
self.rotateInterval = interval
self._lock = RLock()
self.save()
def save(self):
try:
self._lock.acquire()
if time() > self.lastRotation + self.rotateInterval:
self._rotate()
self._save()
finally:
self._lock.release()
def _save(self):
# Must hold lock
LOG.debug("Syncing statistics to disk")
tmpfile = self.filename + "_tmp"
try:
os.unlink(tmpfile)
except:
pass
f = open(tmpfile, 'wb')
cPickle.dump({ 'count' : self.count, 'filename' : self.filename,
'lastRotation' : self.lastRotation },
f, 1)
f.close()
os.rename(tmpfile, self.filename)
def log(self, event, arg=None):
try:
self._lock.acquire()
if time() > self.lastRotation + self.rotateInterval:
self._rotate()
try:
self.count[event][arg] += 1
except KeyError:
try:
self.count[event][arg] = 1
except KeyError:
raise KeyError("No such event: %r" % event)
finally:
self._lock.release()
def _rotate(self, now=None):
# XXXX Change behavior: rotate indexed on midnight.
# Must hold lock
LOG.debug("Flushing statistics log")
if now is None:
now = time()
f = open(self.historyFilename, 'a')
self.dump(f)
f.close()
self.count = {}
for e in EVENTS:
self.count[e] = {}
self.lastRotation = now
self._save()
def dump(self, f):
try:
self._lock.acquire()
startTime = self.lastRotation
endTime = time()
print >>f, "========== From %s to %s:" % (formatTime(startTime,1),
formatTime(endTime,1))
for event in EVENTS:
count = self.count[event]
if len(count) == 0:
print >>f, " %s: 0" % event
continue
elif len(count) == 1 and count.keys()[0] is None:
print >>f, " %s: %s" % (event, count[None])
continue
print >>f, " %s:" % event
total = 0
args = count.keys()
args.sort()
length = max([ len(str(arg)) for arg in args ])
fmt = " %"+str(length)+"s: %s"
for arg in args:
v = count[arg]
if arg is None: arg = "{Unknown}"
print >>f, fmt % (arg, v)
total += v
print >>f, fmt % ("Total", total)
finally:
self._lock.release()
def setLog(eventLog):
global THE_EVENT_LOG
global log
global save
THE_EVENT_LOG = eventLog
log = THE_EVENT_LOG.log
save = THE_EVENT_LOG.save
def configureLog(config):
if config['Server']['LogStats']:
LOG.info("Enabling statistics logging")
statsfile = config['Server']['StatsFile']
if not statsfile:
homedir = config['Server']['Homedir']
statsfile = os.path.join(homedir, "stats")
workfile = statsfile + ".work"
setLog(EventLog(
workfile, statsfile, config['Server']['StatsInterval'][2]))
else:
LOG.info("Statistics logging disabled")
setLog(NilEventLog())
Index: Modules.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/Modules.py,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -d -r1.33 -r1.34
--- Modules.py 20 Feb 2003 02:19:56 -0000 1.33
+++ Modules.py 26 Mar 2003 16:36:46 -0000 1.34
@@ -28,6 +28,7 @@
import mixminion.BuildMessage
import mixminion.Config
import mixminion.Packet
+import mixminion.server.EventStats as EventStats
import mixminion.server.ServerQueue
import mixminion.server.ServerConfig
from mixminion.Config import ConfigError, _parseBoolean, _parseCommand, \
@@ -134,16 +135,21 @@
"""Instead of queueing our message, pass it directly to the underlying
DeliveryModule."""
try:
+ EventStats.log('AttemptedDelivery') #XXXX
res = self.module.processMessage(packet)
if res == DELIVER_OK:
+ EventStats.log('SuccessfulDelivery') #XXXXq
return
elif res == DELIVER_FAIL_RETRY:
LOG.error("Unable to retry delivery for message")
+ EventStats.log('UnretriableDelivery') #XXXX
else:
LOG.error("Unable to deliver message")
+ EventStats.log('UnretriableDelivery') #XXXX
except:
LOG.error_exc(sys.exc_info(),
"Exception delivering message")
+ EventStats.log('UnretriableDelivery') #XXXX
def sendReadyMessages(self):
# We do nothing here; we already delivered the messages
@@ -166,18 +172,23 @@
def _deliverMessages(self, msgList):
for handle, packet, n_retries in msgList:
try:
+ EventStats.log('AttemptedDelivery') #XXXX
result = self.module.processMessage(packet)
if result == DELIVER_OK:
self.deliverySucceeded(handle)
+ EventStats.log('SuccessfulDelivery') #XXXX
elif result == DELIVER_FAIL_RETRY:
self.deliveryFailed(handle, 1)
+ EventStats.log('FailedDelivery') #XXXX
else:
LOG.error("Unable to deliver message")
self.deliveryFailed(handle, 0)
+ EventStats.log('UnretriableDelivery') #XXXX
except:
LOG.error_exc(sys.exc_info(),
"Exception delivering message")
self.deliveryFailed(handle, 0)
+ EventStats.log('UnretriableDelivery') #XXXX
class DeliveryThread(threading.Thread):
"""A thread object used by ModuleManager to send messages in the
Index: ServerConfig.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerConfig.py,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- ServerConfig.py 9 Feb 2003 22:30:58 -0000 1.18
+++ ServerConfig.py 26 Mar 2003 16:36:46 -0000 1.19
@@ -194,6 +194,10 @@
'Daemon' : ('ALLOW', C._parseBoolean, "no"),
# Deprecated.
'NoDaemon' : ('ALLOW', C._parseBoolean, None),
+ 'LogStats' : ('ALLOW', C._parseBoolean, 'yes'),
+ 'StatsInterval' : ('ALLOW', C._parseInterval,
+ "1 day"),
+ 'StatsFile' : ('ALLOW', None, None),
'EncryptIdentityKey' :('ALLOW', C._parseBoolean, "no"),
'IdentityKeyBits': ('ALLOW', C._parseInt, "2048"),
'PublicKeyLifetime' : ('ALLOW', C._parseInterval,
Index: ServerMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerMain.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -d -r1.44 -r1.45
--- ServerMain.py 20 Feb 2003 16:57:40 -0000 1.44
+++ ServerMain.py 26 Mar 2003 16:36:46 -0000 1.45
@@ -30,6 +30,7 @@
import mixminion.server.ServerQueue
import mixminion.server.ServerConfig
import mixminion.server.ServerKeys
+import mixminion.server.EventStats as EventStats
from bisect import insort
from mixminion.Common import LOG, LogStream, MixError, MixFatalError,\
@@ -258,12 +259,20 @@
def onMessageReceived(self, msg):
self.incomingQueue.queueMessage(msg)
+ EventStats.log("ReceivedPacket", None) # XXXX Replace with server.
def onMessageSent(self, msg, handle):
self.outgoingQueue.deliverySucceeded(handle)
+ EventStats.log("AttemptedRelay", None) # XXXX replace with addr
+ EventStats.log("SuccessfulRelay", None) # XXXX replace with addr
def onMessageUndeliverable(self, msg, handle, retriable):
self.outgoingQueue.deliveryFailed(handle, retriable)
+ EventStats.log("AttemptedRelay", None) # XXXX replace with addr
+ if retriable:
+ EventStats.log("FailedRelay", None) # XXXX replace with addr
+ else:
+ EventStats.log("UnretriableRelay", None) # XXXX replace with addr
#----------------------------------------------------------------------
class CleaningThread(threading.Thread):
@@ -526,6 +535,7 @@
LOG.info("Caught sighup")
LOG.info("Resetting logs")
LOG.reset()
+ EventStats.save()
GOT_HUP = 0
# Make sure that our worker threads are still running.
if not (self.cleaningThread.isAlive() and
@@ -599,8 +609,11 @@
self.cleaningThread.join()
self.processingThread.join()
self.moduleManager.join()
-
+
self.packetHandler.close()
+
+ EventStats.save()
+
try:
self.lockFile.release()
os.unlink(self.pidFile)
@@ -659,6 +672,7 @@
sys.exit(0)
def configFromServerArgs(cmd, args):
+ #XXXX
options, args = getopt.getopt(args, "hf:", ["help", "config="])
if args:
usageAndExit(cmd)
@@ -672,6 +686,7 @@
return readConfigFile(configFile)
def readConfigFile(configFile):
+ #XXXX
if configFile is None:
if os.path.exists(os.path.expanduser("~/.mixminiond.conf")):
configFile = os.path.expanduser("~/.mixminiond.conf")
@@ -698,6 +713,9 @@
#----------------------------------------------------------------------
def runServer(cmd, args):
+ if cmd.endswith(" server"):
+ print "Obsolete command. Use 'mixminion server-start' instead."
+
config = configFromServerArgs(cmd, args)
try:
# Configure the log, but delay disabling stderr until the last
@@ -717,11 +735,17 @@
try:
daemonize()
except:
- info = sys.exc_info()
- LOG.fatal_exc(info,
+ LOG.fatal_exc(sys.exc_info(),
"Exception while starting server in the background")
os._exit(0)
+ # Configure event log
+ try:
+ EventStats.configureLog(config)
+ except:
+ LOG.fatal_exc(sys.exc_info(), "")
+ os._exit(0)
+
installSIGCHLDHandler()
installSignalHandlers()
@@ -752,9 +776,25 @@
server.close()
LOG.info("Server is shut down")
+ LOG.close()
sys.exit(0)
#----------------------------------------------------------------------
+_PRINT_STATS_USAGE = """\
+Usage: mixminion server-stats [options]
+Options:
+ -h, --help: Print this usage message and exit.
+ -f <file>, --config=<file> Use a configuration file other than the default.
+""".strip()
+
+def printServerStats(cmd, args):
+ #XXXX
+ config = configFromServerArgs(cmd, args)
+ _signalServer(config, 1)
+ EventStats.configureLog(config)
+ EventStats.THE_EVENT_LOG.dump(sys.stdout)
+
+#----------------------------------------------------------------------
_SIGNAL_SERVER_USAGE = """\
Usage: %s [options]
Options:
@@ -793,6 +833,10 @@
print _SIGNAL_SERVER_USAGE % { 'cmd' : cmd }
return
+ _signalServer(config, reload)
+
+def _signalServer(config, reload):
+ """DOCDOC"""
homeDir = config['Server']['Homedir']
pidFile = os.path.join(homeDir, "pid")
if not os.path.exists(pidFile):