summaryrefslogtreecommitdiff
path: root/scripts/driver
diff options
context:
space:
mode:
authorbwarsaw1998-07-31 20:12:10 +0000
committerbwarsaw1998-07-31 20:12:10 +0000
commit4ebe7ba208ba42511ae77ce7e6d2dcb9be87137e (patch)
treebb94e335e3714afd70c5ad7a530349d83628fb48 /scripts/driver
parent7aa4450d79231f4a8c14a00de7bb3511e7ff70f6 (diff)
downloadmailman-4ebe7ba208ba42511ae77ce7e6d2dcb9be87137e.tar.gz
mailman-4ebe7ba208ba42511ae77ce7e6d2dcb9be87137e.tar.zst
mailman-4ebe7ba208ba42511ae77ce7e6d2dcb9be87137e.zip
Very substantially rewritten. The old driver script let too many
uncaught exceptions through to the top level. Uncommented out the open() definition. I think this is very helpful during the beta debugging phase. We'll comment this out for 1.0 release (it also won't be necessary in Python 1.5.2, but nobody has that yet!) Exception printing has been changed so that the MultiLogger is no longer necessary. Removes one more potential crash, but also rationalizes print_exception() and print_environment(). There's always exactly two places information goes: 1. To a plain text log file. This could be sys.__stderr__ if no explicit log file is given -- e.g. the Web server's log file. 2. To an HTML sink. This will always be sys.stdout (since we use the `print' statement), and will generally always be connected to the input of the Web browser. Log file printing happens first, just in case (it's better that the server knows what's happening), but we always try to print both. `Main' has been rewritten also. We now do not catch any problems with import sys. If sys can't be imported, then the Python installation is royally screwed -- even `print' won't work! Better to just let the catastrophy percolate up to the Web browser. Even so this should almost never happen. Poke __stderr__ and __stdout__ into the sys module if they aren't already there. GvR says this is a 1.5.1 feature, but we can make it work well enough for older versions of Python, and it's convenient. Give one final shot at printing the traceback and environment if the exception percolates all the way up out of run_main(). This time, just print them to stderr (usually meaning the Web server's error log). Should this fail too, do whatever we can not to generate an end-user visible server error.
Diffstat (limited to 'scripts/driver')
-rw-r--r--scripts/driver160
1 files changed, 100 insertions, 60 deletions
diff --git a/scripts/driver b/scripts/driver
index c85eb68bc..0fc2033e5 100644
--- a/scripts/driver
+++ b/scripts/driver
@@ -16,26 +16,32 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# This better succeed. If this fails, Python is royally screwed so we might
+# as well let the Web server give us a fatal and obtrusive error.
+import sys
+# from here on, we try to be as bullet proof as possible
+
+
+
# Useful for debugging. When an error occurs, this attaches the file name to
# the exception string and re-raises (using the bogus Python 1.5 semantics)
# this may be unnecessary in Python 1.5.2
-## realopen = open
-## def open(filename, mode='r', bufsize=-1, realopen=realopen):
-## try:
-## return realopen(filename, mode, bufsize)
-## except IOError, e:
-## strerror = e.strerror + ': ' + filename
-## e.strerror = strerror
-## e.args = (e.args[0], strerror)
-## # Python 1.5
-## import sys
-## raise e, None, sys.exc_info()[2]
-## # Python 1.5.1
-## #raise
-## import __builtin__
-## __builtin__.__dict__['open'] = open
+realopen = open
+def open(filename, mode='r', bufsize=-1, realopen=realopen):
+ try:
+ return realopen(filename, mode, bufsize)
+ except IOError, e:
+ strerror = e.strerror + ': ' + filename
+ e.strerror = strerror
+ e.args = (e.args[0], strerror)
+ # Python 1.5
+ raise e, None, sys.exc_info()[2]
+ # Python 1.5.1
+ #raise
+import __builtin__
+__builtin__.__dict__['open'] = open
@@ -53,17 +59,12 @@
# get a Web server error since this file wouldn't even compile, and there's
# no way to catch that.
#
-# - The sys module could be royally screwed. Either we couldn't import it, or
-# it didn't have a sys.stderr attribute. Both those would indicate serious
-# problems in the Python installation. These won't generate Web server
-# errors, but neither will they give meaningful tracebacks.
-#
-# - We couldn't import the traceback module, or traceback.print_exc()
-# failed. Same diagnosis and effect as with sys being broken.
+# - The sys module could be royally screwed, probably we couldn't import it.
+# Both those would indicate serious problems in the Python installation.
+# These won't generate Web server errors, but neither will they give
+# meaningful tracebacks.
#
-# I consider all these pretty unlikely. Note that it is also possible that
-# the environment variables wouldn't be printed, perhaps because os couldn't
-# be imported or there was some problem with os.environ. Again, not likely.
+# I consider these pretty unlikely.
@@ -72,8 +73,7 @@ def run_main():
# These will ensure that even if something between now and the
# creation of the real logger below fails, we can still get
# *something* meaningful
- logger = sys.stderr
- multi = sys.stderr
+ logger = None
# insert the relative path to the parent of the Mailman package
# directory, so we can pick up the Utils module
import os
@@ -81,12 +81,11 @@ def run_main():
sys.path.insert(0, os.pardir)
# map stderr to a logger, if possible
from Mailman.Logging.StampedLogger import StampedLogger
- from Mailman.Logging.MultiLogger import MultiLogger
logger = StampedLogger('error',
label='admin',
manual_reprime=1,
- nofail=0)
- multi = MultiLogger(sys.__stdout__, logger)
+ nofail=0,
+ immediate=1)
# The name of the module to run is passed in argv[1]. What we
# actually do is import the module named by argv[1] that lives in the
# Mailman.Cgi package. That module must have a main() function, which
@@ -104,12 +103,33 @@ def run_main():
# this is a valid way for the function to exit
pass
except:
- print_traceback(logger, multi)
+ print_traceback(logger)
print_environment(logger)
-def print_traceback(logger, multi):
+# We are printing error reporting to two places. One will always be stdout
+# and the other will always be the log file. It is assumed that stdout is an
+# HTML sink and the log file is a plain text sink.
+
+def print_traceback(logfp=None):
+ if logfp is None:
+ logfp = sys.__stderr__
+
+ try:
+ import traceback
+ except ImportError:
+ traceback = None
+
+ # write to the log file first
+ logfp.write('[----- Traceback ------]\n')
+ if traceback:
+ traceback.print_exc(file=logfp)
+ else:
+ logfp.write('[failed to import module traceback]\n')
+ logfp.write('[exc: %s, var: %s]\n' % sys.exc_info()[0:2])
+
+ # print to the HTML sink
print """\
Content-type: text/html
@@ -122,22 +142,34 @@ a description of what happened. Thanks!
<h4>Traceback:</h4>
<p><pre>
"""
- logger.write('[----- Traceback ------]\n')
- try:
- import traceback
- # in normal situation, this will get logged to the MultiLogger created
- # above, which will write the data to both the real live stdout, and
- # the StampedLogger
- traceback.print_exc(file=multi)
- except:
- multi.write('[failed to get a traceback]\n')
+ if traceback:
+ traceback.print_exc(file=sys.stdout)
+ else:
+ print '[failed to import module traceback]'
+ print '[exc: %s, var: %s]' % sys.exc_info()[0:2]
print '\n\n</pre>'
-def print_environment(logger):
+def print_environment(logfp=None):
+ if logfp is None:
+ logfp = sys.__stderr__
+
try:
import os
+ except ImportError:
+ os = None
+
+ # write to the log file first
+ logfp.write('[----- Environment Variables -----]\n')
+ if os:
+ for k, v in os.environ.items():
+ logfp.write('\t%s: %s\n' % (k, v))
+ else:
+ logfp.write('[failed to import module os]\n')
+
+ # write to the HTML sink
+ if os:
print '''\
<p><hr><h4>Environment variables:</h4>
@@ -145,32 +177,40 @@ def print_environment(logger):
<tr><td><strong>Variable</strong></td>
<td><strong>Value</strong></td></tr>
'''
- logger.write('[----- Environment Variables -----]\n')
- for varname, value in os.environ.items():
- print '<tr><td>', varname, '</td><td>', value, '</td></tr>'
- logger.write('\t%s: %s\n' % (varname, value))
+ for k, v in os.environ.items():
+ print '<tr><td>', k, '</td><td>', v, '</td></tr>'
print '</table>'
- except:
- print '<p><hr>[environment variables are not available]'
+ else:
+ print '<p><hr>[failed to import module os]'
+
try:
- import sys
- try:
- run_main()
- finally:
- # this is probably not strictly necessary since the script is exiting
- # soon anyway
- sys.stderr = sys.__stderr__
+ # Python 1.5 doesn't have these by default. Let's make our lives easy
+ if not hasattr(sys, '__stderr__'):
+ sys.__stderr__ = sys.stderr
+ if not hasattr(sys, '__stdout__'):
+ sys.__stdout__ = sys.stdout
+
+ run_main()
except:
- # Jeez, we couldn't even import sys, or sys didn't have a stderr
- # attribute!
- print """\
+ # Some exception percolated all the way back up to the top. This
+ # generally shouldn't happen because the run_main() call is similarly
+ # wrapped, but just in case, we'll give it one last ditch effort to report
+ # problems to *somebody*. Most likely this will end up in the Web server
+ # log file.
+ try:
+ print_traceback()
+ print_environment()
+ except:
+ # Nope, we're quite screwed
+ print """\
Content-type: text/html
<p><h3>We're sorry, we hit a bug!</h3>
-Mailman experienced a very low level failure and could not even generate
-a useful traceback. Please report this to the Mailman administrator at
+Mailman experienced a very low level failure and could not even generate a
+useful traceback for you. Please report this to the Mailman administrator at
this site.
"""
+ sys.__stderr__.write('[Mailman: low level unrecoverable exception]\n')