[Mailman-Announce] ANNOUNCE Mailman 2.0.5
Barry A. Warsaw
mailman-developers@python.org
Mon, 7 May 2001 12:24:16 -0400
Folks,
I've just released Mailman 2.0.5 which fixes a problem with stale lock
files that can occur under certain situations when the user hits the
`Stop' button on their browser. These stale lock files can cause
mailing lists to be inaccessible for long periods of time (until the
stale lock file times out or is manually removed).
As usual, I'm releasing this as both a complete tarball and as a patch
against Mailman 2.0.4. You /must/ update your source to 2.0.4 before
applying the 2.0.5 patch. Since the patch is small, I'm including it
in this message. To apply, cd into your 2.0.5 source tree and apply
it like so:
% patch -p0 < mailman-2.0.4-2.0.5.txt
Currently both http://mailman.sourceforge.net and http://www.list.org
are updated, and I expect the gnu.org site to be updated soon as
well. The release information on SF is at
http://sourceforge.net/project/shownotes.php?release_id=31693
See also
http://www.gnu.org/software/mailman
http://www.list.org
http://mailman.sourceforge.net
My thanks to those of you who gave the 2.0.5 pre-release a try!
Enjoy,
-Barry
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>