aboutsummaryrefslogtreecommitdiffstats
path: root/Mailman/Queue
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/Queue')
-rw-r--r--Mailman/Queue/Runner.py40
-rw-r--r--Mailman/Queue/Switchboard.py23
2 files changed, 46 insertions, 17 deletions
diff --git a/Mailman/Queue/Runner.py b/Mailman/Queue/Runner.py
index e229043e..1724f043 100644
--- a/Mailman/Queue/Runner.py
+++ b/Mailman/Queue/Runner.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -98,16 +98,17 @@ class Runner:
# Ask the switchboard for the message and metadata objects
# associated with this filebase.
msg, msgdata = self._switchboard.dequeue(filebase)
- except email.Errors.MessageParseError, e:
- # It's possible to get here if the message was stored in the
- # pickle in plain text, and the metadata had a _parsemsg key
- # that was true, /and/ if the message had some bogosity in
- # it. It's almost always going to be spam or bounced spam.
- # There's not much we can do (and we didn't even get the
- # metadata, so just log the exception and continue.
+ except Exception, e:
+ # This used to just catch email.Errors.MessageParseError,
+ # but other problems can occur in message parsing, e.g.
+ # ValueError, and exceptions can occur in unpickling too.
+ # We don't want the runner to die, so we just log and skip
+ # this entry, but preserve it for analysis.
self._log(e)
- syslog('error', 'Ignoring unparseable message: %s', filebase)
- self._switchboard.finish(filebase)
+ syslog('error',
+ 'Skipping and preserving unparseable message: %s',
+ filebase)
+ self._switchboard.finish(filebase, preserve=True)
continue
try:
self._onefile(msg, msgdata)
@@ -122,9 +123,22 @@ class Runner:
self._log(e)
# Put a marker in the metadata for unshunting
msgdata['whichq'] = self._switchboard.whichq()
- new_filebase = self._shunt.enqueue(msg, msgdata)
- syslog('error', 'SHUNTING: %s', new_filebase)
- self._switchboard.finish(filebase)
+ # It is possible that shunting can throw an exception, e.g. a
+ # permissions problem or a MemoryError due to a really large
+ # message. Try to be graceful.
+ try:
+ new_filebase = self._shunt.enqueue(msg, msgdata)
+ syslog('error', 'SHUNTING: %s', new_filebase)
+ self._switchboard.finish(filebase)
+ except Exception, e:
+ # The message wasn't successfully shunted. Log the
+ # exception and try to preserve the original queue entry
+ # for possible analysis.
+ self._log(e)
+ syslog('error',
+ 'SHUNTING FAILED, preserving original entry: %s',
+ filebase)
+ self._switchboard.finish(filebase, preserve=True)
# Other work we want to do each time through the loop
Utils.reap(self._kids, once=True)
self._doperiodic()
diff --git a/Mailman/Queue/Switchboard.py b/Mailman/Queue/Switchboard.py
index 9a45280d..84e8a5e3 100644
--- a/Mailman/Queue/Switchboard.py
+++ b/Mailman/Queue/Switchboard.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -164,12 +164,27 @@ class Switchboard:
msg = email.message_from_string(msg, Message.Message)
return msg, data
- def finish(self, filebase):
+ def finish(self, filebase, preserve=False):
bakfile = os.path.join(self.__whichq, filebase + '.bak')
try:
- os.unlink(bakfile)
+ if preserve:
+ psvfile = os.path.join(mm_cfg.SHUNTQUEUE_DIR, filebase + '.psv')
+ # Create the directory if it doesn't yet exist.
+ # Copied from __init__.
+ omask = os.umask(0) # rwxrws---
+ try:
+ try:
+ os.mkdir(mm_cfg.SHUNTQUEUE_DIR, 0770)
+ except OSError, e:
+ if e.errno <> errno.EEXIST: raise
+ finally:
+ os.umask(omask)
+ os.rename(bakfile, psvfile)
+ else:
+ os.unlink(bakfile)
except EnvironmentError, e:
- syslog('error', 'Failed to unlink backup file: %s', bakfile)
+ syslog('error', 'Failed to unlink/preserve backup file: %s',
+ bakfile)
def files(self, extension='.pck'):
times = {}