summaryrefslogtreecommitdiff
path: root/Mailman/Cgi/admin.py
diff options
context:
space:
mode:
authorbwarsaw2001-05-02 03:12:28 +0000
committerbwarsaw2001-05-02 03:12:28 +0000
commita57f266f69c566112e50246ed5dfe973625b387c (patch)
tree480fc332a8941e0d9878f5c367758323d484e93b /Mailman/Cgi/admin.py
parentc7a495d8944a555cfe88d4153c632c1f79e2ba3e (diff)
downloadmailman-a57f266f69c566112e50246ed5dfe973625b387c.tar.gz
mailman-a57f266f69c566112e50246ed5dfe973625b387c.tar.zst
mailman-a57f266f69c566112e50246ed5dfe973625b387c.zip
Fixes to handle User-Hits-Stop-Button problems, specifically,
main(): Set up a signal handler to catch SIGTERM, and unlock the mailing list when this happens. This has the side effect of aborting any changes to the MailList object that this web hit may have made. This is necessary due to semantics of Apache's mod_cgi: when the browser closes the socket, eventually Apache receives a SIGPIPE (on output to the closed socket). This causes Apache to SIGTERM the cgi process, wait three seconds, then SIGKILL it. We want to be able to clean up the locks, so the best we can do is try to unlock the list on the SIGTERM. Once we get SIGKILLed, there's nothing we can do. This change also moves the Save() call into the try: block so that the finally: block /only/ unlocks the list. Thus, the list gets unlocked in most situations. There are still race conditions where 1) the config.db file could be corrupted; 2) list locks could still be unreleased. Given the semantics of signals in Python, the interaction of Apache's mod_cgi, and other factors, this is the best we can do, and it should be better than the old situation. XXX What do other web servers or cgi execution environments do?
Diffstat (limited to 'Mailman/Cgi/admin.py')
-rw-r--r--Mailman/Cgi/admin.py24
1 files changed, 22 insertions, 2 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py
index 1e4b710b7..95d5558af 100644
--- a/Mailman/Cgi/admin.py
+++ b/Mailman/Cgi/admin.py
@@ -24,6 +24,7 @@ import cgi
import types
import sha
import urllib
+import signal
from string import lowercase, digits
from mimelib.address import unquote
@@ -121,7 +122,7 @@ def main():
# The html page document
doc = Document()
doc.set_language(mlist.preferred_language)
- # Now we're ready to do normal form processing. For this, though we must
+ # Now we're ready to do normal form processing. For this though, we must
# lock the mailing list, and everything from here on out must be wrapped
# in a try/except.
#
@@ -130,8 +131,23 @@ def main():
# or stale lock on the list. Maybe we should have a configurable timeout
# setting after which we'll just inform the user that the operation
# couldn't be performed?
+ #
+ # Set things up so that we can clean up the list lock even if the user
+ # hits the browser's stop button. Note that Apache under mod_cgi
+ # apparently can catch the SIGPIPE that results, and calls SIGTERM on the
+ # CGI process. Python doesn't install a signal handler for SIGTERM, so
+ # that would cause us to summarily exit, leaving list locks laying around
+ # (and this behavior has been confirmed). We can't just ignore SIGTERM
+ # because three seconds later Apache will SIGKILL us, giving us no chance
+ # to exit cleanly. By installing this signal handler, we can catch the
+ # SIGTERM and do the right thing. This may not work under other web
+ # servers, or even other Apache/cgi modules (mod_python, etc.).
+ def sigterm_handler(signum, frame, mlist=mlist):
+ mlist.Unlock()
+
mlist.Lock()
try:
+ signal.signal(signal.SIGTERM, sigterm_handler)
if cgidata.keys():
# There are options to change
change_options(mlist, category, cgidata, doc)
@@ -161,8 +177,12 @@ def main():
# Glom up the results page and print it out
show_results(mlist, doc, category, category_suffix, cgidata)
print doc.Format(bgcolor='#ffffff')
- finally:
mlist.Save()
+ finally:
+ # Now be sure to unlock the list. It's okay if we get a signal here
+ # because essentially, the signal handler will do the same thing. And
+ # unlocking is conditional, so it's not an error if we unlock while
+ # we're already unlocked.
mlist.Unlock()