Index: NEWS
===================================================================
RCS file: /cvsroot/mailman/mailman/NEWS,v
retrieving revision 1.25.2.5
retrieving revision 1.25.2.6
diff -u -r1.25.2.5 -r1.25.2.6
--- NEWS 2001/04/18 10:45:54 1.25.2.5
+++ NEWS 2001/05/03 21:06:56 1.25.2.6
@@ -4,6 +4,13 @@
Here is a history of user visible changes to Mailman.
+2.0.5 (04-May-2001)
+
+ Fix a lock stagnation problem that can result when the user hits
+ the `stop' button on their browser during a write operation that
+ can take a long time (e.g. hitting the membership management admin
+ page).
+
2.0.4 (18-Apr-2001)
Python 2.1 compatibility release. There were a few questionable
Index: Mailman/Version.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Version.py,v
retrieving revision 1.20.2.4
retrieving revision 1.20.2.5
diff -u -r1.20.2.4 -r1.20.2.5
--- Mailman/Version.py 2001/04/18 04:43:29 1.20.2.4
+++ Mailman/Version.py 2001/05/03 20:58:19 1.20.2.5
@@ -15,7 +15,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Mailman version
-VERSION = "2.0.4"
+VERSION = "2.0.5"
# And as a hex number in the manner of PY_VERSION_HEX
ALPHA = 0xa
@@ -27,7 +27,7 @@
MAJOR_REV = 2
MINOR_REV = 0
-MICRO_REV = 4
+MICRO_REV = 5
REL_LEVEL = FINAL
# at most 15 beta releases!
REL_SERIAL = 0
Index: Mailman/Cgi/admin.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Cgi/admin.py,v
retrieving revision 1.82.2.2
retrieving revision 1.82.2.3
diff -u -r1.82.2.2 -r1.82.2.3
--- Mailman/Cgi/admin.py 2001/01/03 16:47:47 1.82.2.2
+++ Mailman/Cgi/admin.py 2001/05/03 21:03:48 1.82.2.3
@@ -18,11 +18,13 @@
"""
+import sys
import os
import cgi
import string
import types
import rfc822
+import signal
from Mailman import Utils
from Mailman import MailList
@@ -63,53 +65,86 @@
# get the list object
listname = string.lower(parts[0])
try:
- mlist = MailList.MailList(listname)
+ mlist = MailList.MailList(listname, lock=0)
except Errors.MMListError, e:
FormatAdminOverview('No such list <em>%s</em>' % listname)
syslog('error', 'Someone tried to access the admin interface for a '
'non-existent list: %s' % listname)
return
+
+ if len(parts) == 1:
+ category = 'general'
+ category_suffix = ''
+ else:
+ category = parts[1]
+ category_suffix = category
+
+ # If the user is not authenticated, we're done.
+ cgidata = cgi.FieldStorage(keep_blank_values=1)
try:
- if len(parts) == 1:
- category = 'general'
- category_suffix = ''
- else:
- category = parts[1]
- category_suffix = category
-
- # If the user is not authenticated, we're done.
- cgidata = cgi.FieldStorage(keep_blank_values=1)
- try:
- Auth.authenticate(mlist, cgidata)
- except Auth.NotLoggedInError, e:
- Auth.loginpage(mlist, 'admin', e.message)
- return
-
- # Is this a log-out request?
- if category == 'logout':
- print mlist.ZapCookie('admin')
- Auth.loginpage(mlist, 'admin', frontpage=1)
- return
-
- if category not in map(lambda x: x[0], CATEGORIES):
- category = 'general'
-
- # is the request for variable details?
- varhelp = None
- if cgidata.has_key('VARHELP'):
- varhelp = cgidata['VARHELP'].value
- elif cgidata.has_key('request_login') and \
- os.environ.get('QUERY_STRING'):
- # POST methods, even if their actions have a query string, don't
- # get put into FieldStorage's keys :-(
- qs = cgi.parse_qs(os.environ['QUERY_STRING']).get('VARHELP')
- if qs and type(qs) == types.ListType:
- varhelp = qs[0]
- if varhelp:
- FormatOptionHelp(doc, varhelp, mlist)
- print doc.Format(bgcolor="#ffffff")
- return
+ Auth.authenticate(mlist, cgidata)
+ except Auth.NotLoggedInError, e:
+ Auth.loginpage(mlist, 'admin', e.message)
+ return
+
+ # Is this a log-out request?
+ if category == 'logout':
+ print mlist.ZapCookie('admin')
+ Auth.loginpage(mlist, 'admin', frontpage=1)
+ return
+
+ if category not in map(lambda x: x[0], CATEGORIES):
+ category = 'general'
+ # is the request for variable details?
+ varhelp = None
+ if cgidata.has_key('VARHELP'):
+ varhelp = cgidata['VARHELP'].value
+ elif cgidata.has_key('request_login') and \
+ os.environ.get('QUERY_STRING'):
+ # POST methods, even if their actions have a query string, don't
+ # get put into FieldStorage's keys :-(
+ qs = cgi.parse_qs(os.environ['QUERY_STRING']).get('VARHELP')
+ if qs and type(qs) == types.ListType:
+ varhelp = qs[0]
+ if varhelp:
+ FormatOptionHelp(doc, varhelp, mlist)
+ print doc.Format(bgcolor="#ffffff")
+ return
+
+ # From this point on, the MailList object must be locked. However, we
+ # must release the lock no matter how we exit. try/finally isn't
+ # enough, because of this scenario: user hits the admin page which may
+ # take a long time to render; user gets bored and hits the browser's
+ # STOP button; browser shuts down socket; server tries to write to
+ # broken socket and gets a SIGPIPE. Under Apache 1.3/mod_cgi, Apache
+ # catches this SIGPIPE (I presume it is buffering output from the cgi
+ # script), then turns around and SIGTERMs the cgi process. Apache
+ # waits three seconds and then SIGKILLs the cgi process. We /must/
+ # catch the SIGTERM and do the most reasonable thing we can in as
+ # short a time period as possible. If we get the SIGKILL we're
+ # screwed (because its uncatchable and we'll have no opportunity to
+ # clean up after ourselves).
+ #
+ # This signal handler catches the SIGTERM and unlocks the list. The
+ # effect of this is that the changes made to the MailList object will
+ # be aborted, which seems like the only sensible semantics.
+ #
+ # BAW: This may not be portable to other web servers or cgi execution
+ # models.
+ def sigterm_handler(signum, frame, mlist=mlist):
+ # Make sure the list gets unlocked...
+ mlist.Unlock()
+ # ...and ensure we exit, otherwise race conditions could cause us to
+ # enter MailList.Save() while we're in the unlocked state, and that
+ # could be bad!
+ sys.exit(0)
+
+ mlist.Lock()
+ try:
+ # Install the emergency shutdown signal handler
+ signal.signal(signal.SIGTERM, sigterm_handler)
+
if cgidata.has_key('bounce_matching_headers'):
pairs = mlist.parse_matching_header_opt()
@@ -135,8 +170,12 @@
FormatConfiguration(doc, mlist, 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 unconditional, so it's not an error if we unlock while
+ # we're already unlocked.
mlist.Unlock()
Index: Mailman/Cgi/admindb.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Cgi/admindb.py,v
retrieving revision 1.36.2.1
retrieving revision 1.36.2.4
diff -u -r1.36.2.1 -r1.36.2.4
--- Mailman/Cgi/admindb.py 2001/03/03 06:02:01 1.36.2.1
+++ Mailman/Cgi/admindb.py 2001/05/04 15:54:23 1.36.2.4
@@ -16,10 +16,12 @@
"""Produce and process the pending-approval items for a list."""
+import sys
import os
import string
import types
import cgi
+import signal
from errno import ENOENT
from Mailman import mm_cfg
@@ -62,7 +64,7 @@
return
# now that we have the list name, create the list object
try:
- mlist = MailList.MailList(listname)
+ mlist = MailList.MailList(listname, lock=0)
except Errors.MMListError, e:
handle_no_list(doc, 'No such list <em>%s</em><p>' % listname)
syslog('error', 'No such list "%s": %s\n' % (listname, e))
@@ -71,14 +73,34 @@
# now we must authorize the user to view this page, and if they are, to
# handle both the printing of the current outstanding requests, and the
# selected actions
+ cgidata = cgi.FieldStorage()
try:
- cgidata = cgi.FieldStorage()
- try:
- Auth.authenticate(mlist, cgidata)
- except Auth.NotLoggedInError, e:
- Auth.loginpage(mlist, 'admindb', e.message)
- return
+ Auth.authenticate(mlist, cgidata)
+ except Auth.NotLoggedInError, e:
+ Auth.loginpage(mlist, 'admindb', e.message)
+ return
+
+ # We need a signal handler to catch the SIGTERM that can come from Apache
+ # when the user hits the browser's STOP button. See the comment in
+ # admin.py for details.
+ #
+ # BAW: Strictly speaking, the list should not need to be locked just to
+ # read the request database. However the request database asserts that
+ # the list is locked in order to load it and it's not worth complicating
+ # that logic.
+ def sigterm_handler(signum, frame, mlist=mlist):
+ # Make sure the list gets unlocked...
+ mlist.Unlock()
+ # ...and ensure we exit, otherwise race conditions could cause us to
+ # enter MailList.Save() while we're in the unlocked state, and that
+ # could be bad!
+ sys.exit(0)
+ mlist.Lock()
+ try:
+ # Install the emergency shutdown signal handler
+ signal.signal(signal.SIGTERM, sigterm_handler)
+
# If this is a form submission, then we'll process the requests and
# print the results. otherwise (there are no keys in the form), we'll
# print out the list of pending requests
@@ -91,8 +113,8 @@
PrintRequests(mlist, doc)
text = doc.Format(bgcolor="#ffffff")
print text
- finally:
mlist.Save()
+ finally:
mlist.Unlock()
Index: Mailman/Cgi/handle_opts.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Cgi/handle_opts.py,v
retrieving revision 1.30
retrieving revision 1.30.2.2
diff -u -r1.30 -r1.30.2.2
--- Mailman/Cgi/handle_opts.py 2000/11/09 16:19:03 1.30
+++ Mailman/Cgi/handle_opts.py 2001/05/03 21:05:06 1.30.2.2
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 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
@@ -20,6 +20,7 @@
import os
import string
import cgi
+import signal
from Mailman import mm_cfg
from Mailman import Utils
@@ -61,7 +62,7 @@
user = parts[1]
try:
- mlist = MailList.MailList(listname)
+ mlist = MailList.MailList(listname, lock=0)
except Errors.MMListError, e:
doc.AddItem(Header(2, "Error"))
doc.AddItem(Bold('No such list <em>%s</em>' % listname))
@@ -69,10 +70,30 @@
syslog('error', 'No such list "%s": %s\n' % (listname, e))
return
+ # We need a signal handler to catch the SIGTERM that can come from Apache
+ # when the user hits the browser's STOP button. See the comment in
+ # admin.py for details.
+ #
+ # BAW: Strictly speaking, the list should not need to be locked just to
+ # read the request database. However the request database asserts that
+ # the list is locked in order to load it and it's not worth complicating
+ # that logic.
+ def sigterm_handler(signum, frame, mlist=mlist):
+ # Make sure the list gets unlocked...
+ mlist.Unlock()
+ # ...and ensure we exit, otherwise race conditions could cause us to
+ # enter MailList.Save() while we're in the unlocked state, and that
+ # could be bad!
+ sys.exit(0)
+
+ mlist.Lock()
try:
+ # Install the emergency shutdown signal handler
+ signal.signal(signal.SIGTERM, sigterm_handler)
+
process_form(mlist, user, doc)
- finally:
mlist.Save()
+ finally:
mlist.Unlock()
Index: Mailman/Cgi/subscribe.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Cgi/subscribe.py,v
retrieving revision 1.29
retrieving revision 1.29.2.1
diff -u -r1.29 -r1.29.2.1
--- Mailman/Cgi/subscribe.py 2000/09/29 00:05:05 1.29
+++ Mailman/Cgi/subscribe.py 2001/05/03 21:05:43 1.29.2.1
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 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
@@ -20,6 +20,7 @@
import os
import string
import cgi
+import signal
from Mailman import Utils
from Mailman import MailList
@@ -41,18 +42,38 @@
listname = string.lower(parts[0])
try:
- mlist = MailList.MailList(listname)
- mlist.IsListInitialized()
+ mlist = MailList.MailList(listname, lock=0)
except Errors.MMListError, e:
doc.AddItem(Header(2, "Error"))
doc.AddItem(Bold('No such list <em>%s</em>' % listname))
print doc.Format(bgcolor="#ffffff")
syslog('error', 'No such list "%s": %s\n' % (listname, e))
return
+
+ # We need a signal handler to catch the SIGTERM that can come from Apache
+ # when the user hits the browser's STOP button. See the comment in
+ # admin.py for details.
+ #
+ # BAW: Strictly speaking, the list should not need to be locked just to
+ # read the request database. However the request database asserts that
+ # the list is locked in order to load it and it's not worth complicating
+ # that logic.
+ def sigterm_handler(signum, frame, mlist=mlist):
+ # Make sure the list gets unlocked...
+ mlist.Unlock()
+ # ...and ensure we exit, otherwise race conditions could cause us to
+ # enter MailList.Save() while we're in the unlocked state, and that
+ # could be bad!
+ sys.exit(0)
+
+ mlist.Lock()
try:
+ # Install the emergency shutdown signal handler
+ signal.signal(signal.SIGTERM, sigterm_handler)
+
process_form(mlist, doc)
- finally:
mlist.Save()
+ finally:
mlist.Unlock()
Index: admin/www/download.ht
===================================================================
RCS file: /cvsroot/mailman/mailman/admin/www/download.ht,v
retrieving revision 1.5.2.5
retrieving revision 1.5.2.6
diff -u -r1.5.2.5 -r1.5.2.6
--- admin/www/download.ht 2001/04/18 10:44:14 1.5.2.5
+++ admin/www/download.ht 2001/05/03 21:09:36 1.5.2.6
@@ -65,9 +65,9 @@
<h3>Downloading</h3>
<p>Version
-(<!-VERSION--->2.0.4<!-VERSION--->,
+(<!-VERSION--->2.0.5<!-VERSION--->,
released on
-<!-DATE--->Apr 18 2001<!-DATE--->)
+<!-DATE--->May 4 2001<!-DATE--->)
is the current GNU release. It is available from the following mirror sites:
<ul>
Index: admin/www/download.html
===================================================================
RCS file: /cvsroot/mailman/mailman/admin/www/download.html,v
retrieving revision 1.6.2.7
retrieving revision 1.6.2.8
diff -u -r1.6.2.7 -r1.6.2.8
--- admin/www/download.html 2001/04/18 10:44:14 1.6.2.7
+++ admin/www/download.html 2001/05/03 21:09:36 1.6.2.8
@@ -1,6 +1,6 @@
<HTML>
<!-- THIS PAGE IS AUTOMATICALLY GENERATED. DO NOT EDIT. -->
-<!-- Wed Apr 18 06:43:32 2001 -->
+<!-- Thu May 3 17:09:03 2001 -->
<!-- USING HT2HTML 1.1 -->
<!-- SEE
http://www.wooz.org/barry/software/pyware.html -->
<!-- User-specified headers:
@@ -237,9 +237,9 @@
<h3>Downloading</h3>
<p>Version
-(<!-VERSION--->2.0.4<!-VERSION--->,
+(<!-VERSION--->2.0.5<!-VERSION--->,
released on
-<!-DATE--->Apr 18 2001<!-DATE--->)
+<!-DATE--->May 4 2001<!-DATE--->)
is the current GNU release. It is available from the following mirror sites:
<ul>