[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[minion-cvs] Lots of documentation work and code cleanup.
Update of /home/minion/cvsroot/src/minion/lib/mixminion/server
In directory moria.mit.edu:/tmp/cvs-serv32566/lib/mixminion/server
Modified Files:
HashLog.py MMTPServer.py PacketHandler.py ServerKeys.py
ServerMain.py ServerQueue.py
Log Message:
Lots of documentation work and code cleanup.
ClientMain:
- Refactor path selection so it's easier understand.
Main:
- Add a new banner.
ServerInbox:
- Actually use ServerQueuedException as documented.
ServerMain:
- Generate keys when keygen is needed, not when key rotation is needed!
- Flush hashlogs on SIGHUP.
- Simplify USAGE code.
- Remove last vestiges of 'stop-server' and 'reload-server' homonyms.
ServerQueue:
- Put off memory-saving work until 0.0.5; it's too invasive for now.
*.py:
- Comment code, resolve XXXXs and DOCDOCs.
Index: HashLog.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/HashLog.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- HashLog.py 5 Jun 2003 18:41:40 -0000 1.14
+++ HashLog.py 6 Jun 2003 06:04:58 -0000 1.15
@@ -26,12 +26,17 @@
# We flush the log every MAX_JOURNAL hashes.
MAX_JOURNAL = 128
+# Lock to pretect _OPEN_HASHLOGS
_HASHLOG_DICT_LOCK = threading.Lock()
-
-#DOCDOC
+# Map from (filename,keyid) to the open HashLog object with that fname and
+# ID. Needed to implement getHashLog.
_OPEN_HASHLOGS = {}
def getHashLog(filename, keyid):
+ """Given a filename and keyid, return a HashLog object with that fname
+ and ID, opening a new one if necessary. This function is needed to
+ implement key rotation: we want to assemble a list of current
+ hashlogs, but we can't open the same HashLog database twice at once."""
try:
_HASHLOG_DICT_LOCK.acquire()
try:
Index: MMTPServer.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/MMTPServer.py,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -d -r1.36 -r1.37
--- MMTPServer.py 5 Jun 2003 18:41:40 -0000 1.36
+++ MMTPServer.py 6 Jun 2003 06:04:58 -0000 1.37
@@ -235,8 +235,9 @@
# __con: an underlying TLS object
# __state: a callback to use whenever we get a read or a write. May
# throw _ml.TLSWantRead or _ml.TLSWantWrite. See __acceptFn,
- # __connectFn, __shutdownFn, __readFn, __writeFn.
- # __server: an AsyncServer.
+ # __connectFn, __shutdownFn, __readFn, __writeFn. If __state
+ # is None, the connection should close immediately.
+ # __server: an AsyncServer. If None, the connection is closed.
# __inbuf: A list of strings that we've read since the last expectRead.
# __inbuflen: The total length of all the strings in __inbuf
# __expectReadLen: None, or the number of bytes to read before
@@ -246,9 +247,9 @@
# writing.
# __servermode: If true, we're the server side of the connection.
# Else, we're the client side.
- # DOCDOC __connecting
- # DOCDOC __failed
- # DOCDOC possibile None on state, server.
+ # __connection: Are we currently trying to start a connection? (boolean)
+ # __failed: Have we given up on this connection?
+
def __init__(self, sock, tls, serverMode, address=None):
"""Create a new SimpleTLSConnection.
@@ -510,7 +511,8 @@
return "".join(self.__inbuf)
def pullInput(self):
- """DOCDOC"""
+ """Returns the current contents of the input buffer, and clears the
+ input buffer."""
inp = "".join(self.__inbuf)
del self.__inbuf[:]
self.__inbuflen = 0
@@ -524,9 +526,15 @@
pass
def remove(self):
- """DOCDOC"""
+ """Called when this connection is shut down successfully or closed
+ with an error. Removes all state associated with this connection.
+ """
self.__server.unregister(self)
self.__server = None
+
+ # Under heavy loads, having circular references through __state and
+ # __finished can keep the connection object alive for many garbage
+ # collections. Let's nuke those so it is deleted right away.
self.__state = None
self.finished = None
@@ -695,6 +703,8 @@
# finishedCallback, certCache:
# As described in the docstring for __init__ below. We remove entries
# from the front of messageList/handleList as we begin sending them.
+ # _curMessage, _curHandle: Correspond to the message and handle
+ # that we are currently trying to deliver.
# junk: A list of 32KB padding chunks that we're going to send. We
# pregenerate these to avoid timing attacks. They correspond to
# the 'JUNK' entries in messageList.
@@ -705,10 +715,7 @@
# if negotiation hasn't completed.
# PROTOCOL_VERSIONS: (static) a list of protocol versions we allow,
# in the order we offer them.
- # _curMessage, _curHandle: Correspond to the message and handle
- # that we are currently trying to deliver.
- # DOCDOC other callbacks
- # DOCDOC active
+ # active: Are we currently able to send messages to a server? Boolean.
PROTOCOL_VERSIONS = [ '0.3' ]
def __init__(self, context, ip, port, keyID, messageList, handleList,
@@ -772,7 +779,9 @@
debug("Opening client connection to %s", self.address)
def isActive(self):
- """DOCDOC"""
+ """Return true iff messages added to this connection via addMessages
+ will be delivered. isActive() will return false if, for example,
+ the connection is currently shutting down."""
return self.active
def addMessages(self, messages, handles):
@@ -926,13 +935,10 @@
self._curMessage = self._curHandle = None
def shutdown(self, err=0, retriable=0):
- #DOCDOC
-
self.active = 0
SimpleTLSConnection.shutdown(self, err=err, retriable=retriable)
def remove(self):
- """DOCDOC"""
self.active = 0
if self.finishedCallback is not None:
self.finishedCallback()
@@ -941,8 +947,6 @@
self.sentCallback = None
SimpleTLSConnection.remove(self)
-
-
LISTEN_BACKLOG = 128
Index: PacketHandler.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/PacketHandler.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- PacketHandler.py 17 May 2003 00:08:45 -0000 1.16
+++ PacketHandler.py 6 Jun 2003 06:04:58 -0000 1.17
@@ -32,7 +32,7 @@
# privatekeys: a list of 2-tuples of
# (1) a RSA private key that we accept
# (2) a HashLog objects corresponding to the given key
- def __init__(self, privatekey=(), hashlog=()):
+ def __init__(self, privatekeys=(), hashlogs=()):
"""Constructs a new packet handler, given a sequence of
private key object for header encryption, and a sequence of
corresponding hashlog object to prevent replays.
@@ -45,9 +45,10 @@
self.privatekeys = []
self.lock = threading.Lock()
- assert type(privatekey) in (types.ListType, types.TupleType)
+ assert type(privatekeys) in (types.ListType, types.TupleType)
+ assert type(hashlogs) in (types.ListType, types.TupleType)
- self.setKeys(privatekey, hashlog)
+ self.setKeys(privatekeys, hashlogs)
def setKeys(self, keys, hashlogs):
"""Change the keys and hashlogs used by this PacketHandler.
Index: ServerKeys.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerKeys.py,v
retrieving revision 1.39
retrieving revision 1.40
diff -u -d -r1.39 -r1.40
--- ServerKeys.py 5 Jun 2003 18:41:40 -0000 1.39
+++ ServerKeys.py 6 Jun 2003 06:04:58 -0000 1.40
@@ -47,7 +47,7 @@
#FFFF Make this configurable? (Set to 2 weeks).
PREPUBLICATION_INTERVAL = 14*24*60*60
-# DOCDOC
+# URL to which we should post published servers.
#
#FFFF Make this configurable
DIRECTORY_UPLOAD_URL = "http://mixminion.net/minion-cgi/publish"
@@ -55,20 +55,21 @@
#----------------------------------------------------------------------
class ServerKeyring:
"""A ServerKeyring remembers current and future keys, descriptors, and
- hash logs for a mixminion server.
-
- DOCDOC
+ hash logs for a mixminion server. It keeps track of key rotation
+ schedules, and generates new keys as needed.
"""
## Fields:
# homeDir: server home directory
# keyDir: server key directory
# keyOverlap: How long after a new key begins do we accept the old one?
# keySets: sorted list of (start, end, keyset)
- # nextRotation: time_t when this key expires, DOCDOCDOC not so.
+ # nextUpdate: time_t when a new key should be added, or a current key
+ # should be removed, or "None" for uncalculated.
# keyRange: tuple of (firstKey, lastKey) to represent which key names
# have keys on disk.
- #
- #DOCDOC currentKeys
+ # currentKeys: None, if we haven't checked for currently live keys, or
+ # a list of currently live ServerKeyset objects.
+ # _lock: A lock to prevent concurrent key generation or rotation.
def __init__(self, config):
"Create a ServerKeyring from a config object"
@@ -90,7 +91,8 @@
"""Internal method: read information about all this server's
currently-prepared keys from disk.
- DOCDOC raises configerror...
+ May raise ConfigError if any of the server descriptors on disk
+ are invalid.
"""
self.keySets = []
firstKey = sys.maxint
@@ -149,7 +151,9 @@
formatDate(end), formatDate(start))
def checkDescriptorConsistency(self, regen=1):
- """DOCDOC"""
+ """Check whether the server descriptors in this keyring are
+ consistent with the server's configuration. If 'regen' are true,
+ inconsistent descriptors are regenerated."""
identity = None
state = []
for _,_,ks in self.keySets:
@@ -196,7 +200,9 @@
return key
def publishKeys(self, allKeys=0):
- """DOCDOC"""
+ """Publish server descriptors to the directory server. Ordinarily,
+ only unpublished descriptors are sent. If allKeys is true,
+ all descriptors are sent."""
keySets = [ ks for _, _, ks in self.keySets ]
if allKeys:
LOG.info("Republishing all known keys to directory server")
@@ -241,7 +247,8 @@
secureDelete([dhfile], blocking=1)
def createKeysAsNeeded(self,now=None):
- """DOCDOC"""
+ """Generate new keys and descriptors as needed, so that the next
+ PUBLICATION_LATENCY+PREPUBLICATION_INTERVAL seconds are covered."""
if now is None:
now = time.time()
@@ -292,7 +299,8 @@
keyname = "%04d" % keynum
- nextStart = startAt + self.config['Server']['PublicKeyLifetime'].getSeconds()
+ lifetime = self.config['Server']['PublicKeyLifetime'].getSeconds()
+ nextStart = startAt + lifetime
LOG.info("Generating key %s to run from %s through %s (GMT)",
keyname, formatDate(startAt),
@@ -308,16 +316,16 @@
self.checkKeys()
def regenerateDescriptors(self):
- """DOCDOC"""
+ """Regenerate all server descriptors for all keysets in this
+ keyring, but keep all old keys intact."""
LOG.info("Regenerating server descriptors; keeping old keys.")
identityKey = self.getIdentityKey()
for _,_,ks in self.keySets:
ks.regenerateServerDescriptor(self.config, identityKey)
def getNextKeygen(self):
- """DOCDOC
-
- -1 => Right now!
+ """Return the time (in seconds) when we should next generate keys.
+ If -1 is returned, keygen should occur immediately.
"""
if not self.keySets:
return -1
@@ -373,9 +381,8 @@
if va < now and vu > cutoff ]
def getServerKeysets(self, now=None):
- """Return a ServerKeyset object for the currently live key.
-
- DOCDOC"""
+ """Return list of ServerKeyset objects for the currently live keys.
+ """
# FFFF Support passwords on keys
keysets = [ ]
for va, vu, ks in self._getLiveKeys(now):
@@ -414,7 +421,12 @@
self._getDHFile())
def updateKeys(self, packetHandler, mmtpServer, statusFile=None,when=None):
- """DOCDOC: Return next rotation."""
+ """Update the keys and certificates stored in a PacketHandler and an
+ MMTPServer object, so that they contain the currently correct
+ keys. Also removes any dead keys.
+
+ This function is idempotent.
+ """
self.removeDeadKeys()
self.currentKeys = keys = self.getServerKeysets(when)
LOG.info("Updating keys: %s currently valid", len(keys))
@@ -440,7 +452,8 @@
self.getNextKeyRotation(keys)
def getNextKeyRotation(self, curKeys=None):
- """DOCDOC"""
+ """Calculate the next time at which we should change the set of live
+ keys."""
if self.nextUpdate is None:
if curKeys is None:
if self.currentKeys is None:
@@ -449,15 +462,18 @@
curKeys = self.currentKeys
events = []
curNames = {}
- #DOCDOC
+ # For every current keyset, we'll remove it at keyOverlap
+ # seconds after its stated expiry time.
for k in curKeys:
va, vu = k.getLiveness()
events.append((vu+self.keyOverlap, "RM"))
curNames[k.keyname] = 1
+ # For every other keyset, we'll add it when it becomes valid.
for va, vu, k in self.keySets:
if curNames.has_key(k.keyname): continue
events.append((va, "ADD"))
+ # Which even happens first?
events.sort()
if not events:
LOG.info("No future key rotation events.")
@@ -504,13 +520,18 @@
When we create a new ServerKeyset object, the associated keys are not
read from disk unil the object's load method is called."""
## Fields:
+ # keydir: Directory to store this keyset's data.
# hashlogFile: filename of this keyset's hashlog.
# packetKeyFile, mmtpKeyFile: filename of this keyset's short-term keys
# certFile: filename of this keyset's X509 certificate
# descFile: filename of this keyset's server descriptor.
+ # publishedFile: filename to store this server's publication time.
#
# packetKey, mmtpKey: This server's actual short-term keys.
- # DOCDOC serverinfo, validAfter, validUntil,published(File)?, keydir
+ #
+ # serverinfo: None, or a parsed server descriptor.
+ # validAfter, validUntil: This keyset's published lifespan, or None.
+ # published: has this boolean: has this server been published?
def __init__(self, keyroot, keyname, hashroot):
"""Load a set of keys named "keyname" on a server where all keys
are stored under the directory "keyroot" and hashlogs are stored
@@ -534,7 +555,7 @@
createPrivateDir(keydir)
def delete(self):
- """DOCDOC"""
+ """Remove this keyset from disk."""
files = [self.packetKeyFile,
self.mmtpKeyFile,
self.certFile,
@@ -577,31 +598,33 @@
"Return the sha1 hash of the asn1 encoding of the packet public key"
return mixminion.Crypto.sha1(self.packetKey.encode_key(1))
def getServerDescriptor(self):
- """DOCDOC"""
+ """Return a ServerInfo for this keyset, reading it from disk if
+ needed."""
if self.serverinfo is None:
self.serverinfo = ServerInfo(fname=self.descFile)
return self.serverinfo
def getLiveness(self):
- """DOCDOC"""
+ """Return a 2-tuple of validAfter/validUntil for this server."""
if self.validAfter is None or self.validUntil is None:
info = self.getServerDescriptor()
self.validAfter = info['Server']['Valid-After']
self.validUntil = info['Server']['Valid-Until']
return self.validAfter, self.validUntil
def isPublished(self):
- """DOCDOC"""
+ """Return true iff we have published this keyset."""
return self.published
def markAsPublished(self):
- """DOCDOC"""
+ """Mark this keyset as published."""
contents = "%s\n"%formatTime(time.time(),1)
writeFile(self.publishedFile, contents, mode=0600)
self.published = 1
def markAsUnpublished(self):
- """DOCDOC"""
+ """Mark this keyset as unpublished."""
tryUnlink(self.publishedFile)
self.published = 0
def regenerateServerDescriptor(self, config, identityKey):
- """DOCDOC"""
+ """Regenerate the server descriptor for this keyset, keeping the
+ original keys."""
self.load()
self.markAsUnpublished()
validAt,validUntil = self.getLiveness()
@@ -615,14 +638,20 @@
self.serverinfo = self.validAfter = self.validUntil = None
def checkConsistency(self, config, log=1):
- """DOCDOC"""
+ """Check whether this server descriptor is consistent with a
+ given configuration file. Returns are as for
+ 'checkDescriptorConsistency'.
+ """
return checkDescriptorConsistency(self.getServerDescriptor(),
config,
log=log,
isPublished=self.published)
def publish(self, url):
- """DOCDOC Returns 'accept', 'reject', 'error'. """
+ """Try to publish this descriptor to a given directory URL. Returns
+ 'accept' if the publication was successful, 'reject' if the
+ server refused to accept the descriptor, and 'error' if
+ publication failed for some other reason."""
fname = self.getDescriptorFileName()
descriptor = readFile(fname)
fields = urllib.urlencode({"desc" : descriptor})
@@ -656,7 +685,8 @@
LOG.info("Directory accepted descriptor: %r", msg)
self.markAsPublished()
return 'accept'
-
+
+# Matches the reply a directory server gives.
DIRECTORY_RESPONSE_RE = re.compile(r'^Status: (0|1)[ \t]*\nMessage: (.*)$',
re.M)
@@ -681,10 +711,12 @@
def checkDescriptorConsistency(info, config, log=1, isPublished=1):
"""Given a ServerInfo and a ServerConfig, compare them for consistency.
- Return true iff info may have come from 'config'. If 'log' is
- true, warn as well. Does not check keys.
+ Returns 'good' iff info may have come from 'config'.
- DOCDOC returns 'good', 'so-so', 'bad'
+ If the server is inconsistent with the configuration file and should
+ be regenerated, returns 'bad'. Otherwise, returns 'so-so'.
+
+ If 'log' is true, warn as well. Does not check keys.
"""
warn = _WarnWrapper(silence = not log, isPublished=isPublished)
@@ -773,7 +805,6 @@
def generateServerDescriptorAndKeys(config, identityKey, keydir, keyname,
hashdir, validAt=None, now=None,
useServerKeys=0, validUntil=None):
- #XXXX reorder args
"""Generate and sign a new server descriptor, and generate all the keys to
go with it.
@@ -783,10 +814,11 @@
keyname -- The name of this new key set within keydir
hashdir -- The root directory for storing hash logs.
validAt -- The starting time (in seconds) for this key's lifetime.
-
- DOCDOC useServerKeys, validUntil
- """
-
+ useServerKeys -- If true, try to read an existing keyset from
+ (keydir,keyname,hashdir) rather than generating a fresh one.
+ validUntil -- Time at which the generated descriptor should
+ expire.
+ """
if useServerKeys:
serverKeys = ServerKeyset(keydir, keyname, hashdir)
serverKeys.load()
@@ -1036,7 +1068,15 @@
def generateCertChain(filename, mmtpKey, identityKey, nickname,
certStarts, certEnds):
- """Create a two-certificate chain DOCDOC"""
+ """Create a two-certificate chain for use in MMTP.
+
+ filename -- location to store certificate chain.
+ mmtpKey -- a short-term RSA key to use for connection
+ encryption (1024 bits).
+ identityKey -- our long-term signing key (2048-4096 bits).
+ nickname -- nickname to use in our certificates.
+ certStarts, certEnds -- certificate lifetimes.
+ """
fname = filename+"_tmp"
mixminion.Crypto.generate_cert(fname,
mmtpKey, identityKey,
Index: ServerMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerMain.py,v
retrieving revision 1.73
retrieving revision 1.74
diff -u -d -r1.73 -r1.74
--- ServerMain.py 5 Jun 2003 18:41:40 -0000 1.73
+++ ServerMain.py 6 Jun 2003 06:04:58 -0000 1.74
@@ -14,14 +14,12 @@
# MINION_HOME/work/queues/incoming/ [Queue of received,unprocessed pkts]
# mix/ [Mix pool]
# outgoing/ [Messages for mmtp delivery]
-# deliver/mbox/ [DOCDOC]
-# deliver/smtp/
-# deliver/*/
+# deliver/*/ [Messages for delivery via modules]
# tls/dhparam [Diffie-Hellman parameters]
# hashlogs/hash_1* [HashLogs of packet hashes
# hash_2* corresponding to key sets]
# ...
-# stats.tmp [DOCDOC]
+# stats.tmp [Cache of stats from latest period]
# log [Messages from the server]
# keys/identity.key [Long-lived identity PK]
# key_0001/ServerDesc [Server descriptor]
@@ -31,13 +29,12 @@
# published [present if this desc is published]
# key_0002/...
# conf/miniond.conf [configuration file]
-# current-desc
-# stats [DOCDOC]
-# version [DOCDOC]
+# current-desc [Filename of current server descriptor.]
+# stats [Log of server statistics]
+# version [Version of homedir format.]
# FFFF Support to put keys/queues in separate directories.
-
__all__ = [ 'MixminonServer' ]
import errno
@@ -69,11 +66,14 @@
installSIGCHLDHandler, Lockfile, readFile, secureDelete, tryUnlink, \
waitForChildren, writeFile
-#DOCDOC
+# Version number for server home-directory.
+#
# For backward-incompatible changes only.
SERVER_HOMEDIR_VERSION = "1001"
def getHomedirVersion(config):
+ """Return the version of the server's homedir. If no version is found,
+ the version must be '1000'. """
homeDir = config['Server']['Homedir']
versionFile = os.path.join(homeDir, "version")
if not os.path.exists(homeDir):
@@ -95,6 +95,10 @@
return dirVersion
def checkHomedirVersion(config):
+ """Check the version of the server's homedir. If it's too old, tell
+ the user to upgrade and raise UIError. If it's too new, tell the
+ user we're confused and raise UIError. Otherwise, return silently.
+ """
dirVersion = getHomedirVersion(config)
if dirVersion is None:
@@ -530,12 +534,10 @@
def scheduleRecurringComplex(self, first, name, cb):
"""Schedule a callback function 'cb' to be invoked at time 'first,'
- and thereafter at times returned by 'nextFn'.
-
- (nextFn is called immediately after the callback is invoked,
- every time it is invoked, and should return a time at which.)
+ and thereafter at times returned by 'cb'.
- DOCDOC
+ (Every time the callback is invoked, if it returns a non-None value,
+ the event is rescheduled for the time it returns.)
"""
assert type(name) is StringType
assert type(first) in (IntType, LongType, FloatType)
@@ -718,10 +720,12 @@
self.moduleManager.startThreading()
def updateKeys(self, lock=1):
- """DOCDOC"""
- # We don't dare to block here -- we could block the main thread for
- # as long as it takes to generate several new RSA keys, which would
- # stomp responsiveness on slow computers.
+ """Change the keys used by the PacketHandler and MMTPServer objects
+ to reflect the currently keys."""
+ # We don't want to block here -- If key generation is in process, we
+ # could block the main thread for as long as it takes to generate
+ # several new RSA keys, which would stomp responsiveness on slow
+ # computers. Instead, we reschedule for 2 minutes later
# ???? Could there be a more elegant approach to this?
if lock and not self.keyring.lock(0):
LOG.warn("generateKeys in progress:"
@@ -738,8 +742,13 @@
if lock: self.keyring.unlock()
def generateKeys(self):
- """DOCDOC"""
-
+ """Callback used to schedule key-generation"""
+
+ # We generate and publish keys in the processing thread, so we don't
+ # slow down the server. We also reschedule from the processing thread,
+ # so that we can take the new keyset into account when calculating
+ # when keys are next needed.
+
def c(self=self):
try:
self.keyring.lock()
@@ -747,7 +756,7 @@
self.updateKeys(lock=0)
if self.config['DirectoryServers'].get('Publish'):
self.keyring.publishKeys()
- self.scheduleOnce(self.keyring.getNextKeyRotation(),
+ self.scheduleOnce(self.keyring.getNextKeygen(),
"KEY_GEN",
self.generateKeys)
finally:
@@ -835,20 +844,27 @@
self.processEvents()
def doReset(self):
+ """Called when server receives SIGHUP. Flushes logs to disk,
+ regenerates/republishes descriptors as needed.
+ """
LOG.info("Resetting logs")
LOG.reset()
EventStats.log.save()
+ self.packetHandler.syncLogs()
LOG.info("Checking for key rotation")
self.keyring.checkKeys()
self.generateKeys()
def doMix(self):
+ """Called when the server's mix is about to fire. Picks some
+ messages to send, and sends them to the appropriate queues.
+ """
+
now = time.time()
# Before we mix, we need to log the hashes to avoid replays.
try:
- # There's a potential threading problem here... in
- # between this sync and the 'mix' below, nobody should
- # insert into the mix pool.
+ # There's a threading issue here... in between this sync and the
+ # 'mix' below, nobody should insert into the mix pool.
self.mixPool.lock()
self.packetHandler.syncLogs()
@@ -864,6 +880,7 @@
# Send exit messages
self.moduleManager.sendReadyMessages()
+ #XXXX005 use new schedulerecurringcomplex interface
# Choose next mix interval
nextMix = self.mixPool.getNextMixTime(now)
self.scheduleOnce(nextMix, "MIX", self.doMix)
@@ -940,41 +957,32 @@
sys.stdout = sys.__stdout__ = LogStream("STDOUT", "WARN")
sys.stderr = sys.__stderr__ = LogStream("STDERR", "WARN")
-_SERVER_USAGE = """\
-Usage: %s [options]
-Options:
- -h, --help: Print this usage message and exit.
- -f <file>, --config=<file> Use a configuration file other than the default.
-""".strip()
-
-def usageAndExit(cmd):
- print _SERVER_USAGE %cmd
- sys.exit(0)
-
-def configFromServerArgs(cmd, args, usage=None):
- """DOCDOC"""
+def configFromServerArgs(cmd, args, usage):
+ """Given cmd and args as passed to one of the entry commands,
+ parses the standard '-h/--help' and '-f/--config' options.
+ If the user wanted a usage message, print the usage message and exit.
+ Otherwise, find and parse the configuration file.
+ """
options, args = getopt.getopt(args, "hf:", ["help", "config="])
if args:
- if usage:
- print usage
- sys.exit(0)
- else:
- usageAndExit(cmd)
+ print >>sys.stderr, "No arguments expected."
+ print usage
+ sys.exit(0)
configFile = None
for o,v in options:
if o in ('-h', '--help'):
- if usage:
- print usage
- sys.exit(0)
- else:
- usageAndExit(cmd)
+ print usage
+ sys.exit(0)
if o in ('-f', '--config'):
configFile = v
return readConfigFile(configFile)
def readConfigFile(configFile):
- """DOCDOC"""
+ """Given a filename from the command line (or None if the user didn't
+ specify a configuration file), find the configuration file, parse it,
+ and validate it. Return the validated configuration file.
+ """
if configFile is None:
configFile = None
for p in ["~/.mixminiond.conf", "~/etc/mixminiond.conf",
@@ -1000,11 +1008,20 @@
return None #never reached; here to suppress pychecker warning
#----------------------------------------------------------------------
+_SERVER_START_USAGE = """\
+Usage: mixminion server-start [options]
+Start a Mixminion server.
+Options:
+ -h, --help: Print this usage message and exit.
+ -f <file>, --config=<file> Use a configuration file other than the default.
+""".strip()
+
def runServer(cmd, args):
+ """[Entry point] Start a Mixminion server."""
if cmd.endswith(" server"):
print "Obsolete command. Use 'mixminion server-start' instead."
- config = configFromServerArgs(cmd, args)
+ config = configFromServerArgs(cmd, args, _SERVER_START_USAGE)
try:
# Configure the log, but delay disabling stderr until the last
# possible minute; we want to keep echoing to the terminal until
@@ -1085,6 +1102,7 @@
#----------------------------------------------------------------------
_UPGRADE_USAGE = """\
Usage: mixminion server-upgrade [options]
+Upgrade the server's home directory from an earlier version.
Options:
-h, --help: Print this usage message and exit.
-f <file>, --config=<file> Use a configuration file other than
@@ -1092,8 +1110,9 @@
""".strip()
def runUpgrade(cmd, args):
- """Remove all keys server descriptors for old versions of this
- server. If any are found, nuke the keysets, """
+ """[Entry point] Check the version on this server's homedir. If it's
+ old, remove all the old keys and server descriptors, clean out the
+ queues, and mark the directory as up-to-date."""
config = configFromServerArgs(cmd, args, usage=_UPGRADE_USAGE)
assert config
@@ -1171,6 +1190,7 @@
#----------------------------------------------------------------------
_DELKEYS_USAGE = """\
Usage: mixminion server-DELKEYS [options]
+Delete all keys for this server (except the identity key).
Options:
-h, --help: Print this usage message and exit.
-f <file>, --config=<file> Use a configuration file other than
@@ -1178,9 +1198,8 @@
""".strip()
def runDELKEYS(cmd, args):
- """Remove all keys server descriptors for old versions of this
- server. If any are found, nuke the keysets, """
-
+ """[Entry point.] Remove all keys and server descriptors for this
+ server."""
config = configFromServerArgs(cmd, args, usage=_DELKEYS_USAGE)
assert config
@@ -1209,13 +1228,15 @@
#----------------------------------------------------------------------
_PRINT_STATS_USAGE = """\
Usage: mixminion server-stats [options]
+Print server statistics for the current statistics interval.
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):
- """DOCDOC"""
+ """[Entry point] Print server statistics for the current statistics
+ interval."""
config = configFromServerArgs(cmd, args, _PRINT_STATS_USAGE)
checkHomedirVersion(config)
_signalServer(config, 1)
@@ -1224,21 +1245,27 @@
#----------------------------------------------------------------------
_SIGNAL_SERVER_USAGE = """\
-Usage: %s [options]
+Usage: mixminion %s [options]
+Tell a mixminion server to %s.
Options:
-h, --help: Print this usage message and exit.
-f <file>, --config=<file> Use a configuration file other than the default.
""".strip()
def signalServer(cmd, args):
- config = configFromServerArgs(cmd, args, usage=_SIGNAL_SERVER_USAGE%cmd)
- LOG.setMinSeverity("ERROR")
-
- if cmd.endswith("stop-server") or cmd.endswith("server-stop"):
+ """[Entry point] Send a SIGHUP or a SIGTERM to a running mixminion
+ server."""
+ if cmd.endswith("server-stop"):
reload = 0
+ usage = _SIGNAL_SERVER_USAGE % ("server-stop", "shut down")
else:
- assert cmd.endswith("reload-server") or cmd.endswith("server-reload")
+ assert cmd.endswith("server-reload")
reload = 1
+ usage = _SIGNAL_SERVER_USAGE % ("server-reload",
+ "rescan its configuration")
+
+ config = configFromServerArgs(cmd, args, usage=usage)
+ LOG.setMinSeverity("ERROR")
checkHomedirVersion(config)
@@ -1276,6 +1303,7 @@
#----------------------------------------------------------------------
_REPUBLISH_USAGE = """\
Usage: mixminion server-republish [options]
+Force a mixminion server to republish its keys to the directory.
Options:
-h, --help: Print this usage message and exit.
-f <file>, --config=<file> Use a configuration file other than
@@ -1283,6 +1311,8 @@
""".strip()
def runRepublish(cmd, args):
+ """[Entry point] Mark all keys as unpublished, and send a SIGHUP to
+ the server."""
config = configFromServerArgs(cmd, args, usage=_REPUBLISH_USAGE)
checkHomedirVersion(config)
Index: ServerQueue.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerQueue.py,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- ServerQueue.py 5 Jun 2003 18:41:41 -0000 1.20
+++ ServerQueue.py 6 Jun 2003 06:04:58 -0000 1.21
@@ -614,10 +614,10 @@
self._deliverMessages(messages)
self._repOk()
- # FFFF004 This interface is inefficient in space: we don't need to load
- # FFFF004 the messages to tell whether they need to be delivered. We
- # FFFF004 should have _deliverMessages() take a list of handles, not of
- # FFFF004 messages.
+ # FFFF005 This interface is inefficient in space: we don't need to load
+ # FFFF005 the messages to tell whether they need to be delivered. We
+ # FFFF005 should have _deliverMessages() take a list of handles, not of
+ # FFFF005 messages.
def _deliverMessages(self, msgList):
"""Abstract method; Invoked with a list of (handle, message)
tuples every time we have a batch of messages to send.