summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbwarsaw1999-07-01 20:35:54 +0000
committerbwarsaw1999-07-01 20:35:54 +0000
commit80ce1f9259231bceed2bcb352ac82bd735b4285b (patch)
tree06a32d49a01a12e621f0011bdcc7652989ccd7c5
parent3bf91d8e58657ce26c57db0c1099071e36bcd51e (diff)
downloadmailman-80ce1f9259231bceed2bcb352ac82bd735b4285b.tar.gz
mailman-80ce1f9259231bceed2bcb352ac82bd735b4285b.tar.zst
mailman-80ce1f9259231bceed2bcb352ac82bd735b4285b.zip
MailList.Save(): Rewrite to be more robust in maintaining a valid
config.db file in the face of IOErrors during the marshal write. This is critical code so we have to make sure we get it right, but I've been running it on python.org for about a day and have seen no problems under normal conditions. I've tested under failure mode too, but not under real world conditions.
-rw-r--r--Mailman/MailList.py41
1 files changed, 28 insertions, 13 deletions
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 29d2ae70b..65c91a018 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -743,23 +743,38 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
# pretty hosed. That's a good reason to make this a daemon not a
# program.
self.IsListInitialized()
- fname = os.path.join(self._full_path, 'config.db')
- fname_last = fname + ".last"
- file = aside_new(fname, fname_last, reopen=1)
- dict = {}
+ # copy all public attributes to marshalable dictionary
+ dict = {}
for key, value in self.__dict__.items():
if key[0] <> '_':
dict[key] = value
+ # we want to write this dict in a marshal, but be especially paranoid
+ # about how we write the config.db, so we'll be as robust as possible
+ # in the face of, e.g. disk full errors. The idea is that we want to
+ # guarantee that config.db is always valid. The old way had the bad
+ # habit of writing an incomplete file, which happened to be a valid
+ # (but bogus) marshal.
+ fname = os.path.join(self._full_path, 'config.db')
+ fname_tmp = fname + '.tmp.' + `os.getpid()`
+ fname_last = fname + ".last"
+ omask = os.umask(007)
try:
- marshal.dump(dict, file)
- file.close()
- except IOError, status:
- # Darn - try to resurrect the old config.db.
- file = aside_new(fname_last, fname, reopen=0)
- self.LogMsg("error",
- "Failed config file write '%s',"
- " old config resurrected." % `status.args`)
- Utils.reraise()
+ try:
+ fp = open(fname_tmp, 'w')
+ marshal.dump(dict, fp)
+ fp.close()
+ except IOError, status:
+ os.unlink(fname_tmp)
+ self.LogMsg('error',
+ 'Failed config.db file write, retaining old state'
+ '\n %s' % `status.args`)
+ Utils.reraise()
+ # now move config.db -> config.db.last
+ # then move config.db.tmp.xxx -> config.db
+ aside_new(fname, fname_last)
+ aside_new(fname_tmp, fname)
+ finally:
+ os.umask(omask)
self.CheckHTMLArchiveDir()
def Load(self, check_version = 1):