diff options
| author | bwarsaw | 1999-07-01 20:35:54 +0000 |
|---|---|---|
| committer | bwarsaw | 1999-07-01 20:35:54 +0000 |
| commit | 80ce1f9259231bceed2bcb352ac82bd735b4285b (patch) | |
| tree | 06a32d49a01a12e621f0011bdcc7652989ccd7c5 /Mailman/MailList.py | |
| parent | 3bf91d8e58657ce26c57db0c1099071e36bcd51e (diff) | |
| download | mailman-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.
Diffstat (limited to 'Mailman/MailList.py')
| -rw-r--r-- | Mailman/MailList.py | 41 |
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): |
