Make maintainer scripts executable
[mspang/vmailman.git] / scripts / driver
1 # -*- python -*-
2
3 # Copyright (C) 1998-2004 by the Free Software Foundation, Inc.
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 # This better succeed.  If this fails, Python is royally screwed so we might
20 # as well let the Web server give us a fatal and obtrusive error.
21 import sys
22
23 # From here on we are as bulletproof as possible!
24
25 # The driver script prints out a lot of information when a Mailman bug is
26 # encountered.  This really helps for development, but it also reveals
27 # information about the host system that some administrators are not
28 # comfortable with.  By setting STEALTH_MODE to 1, you disable the printing of
29 # this information to the web pages.  This information is still, and always,
30 # printed in the error logs.
31 STEALTH_MODE = 1
32
33 # This will be set to the entity escaper.
34 def websafe(s):
35     return s
36
37
38 \f
39 # This standard driver script is used to run CGI programs, wrapped in code
40 # that catches errors, and displays them as HTML.  This guarantees that
41 # (almost) any problem in the Mailman software doesn't result in a Web server
42 # error.  It is much more helpful to generate and show a traceback, which the
43 # user could send to the administrator, than to display a server error and
44 # have to trudge through server logs.
45
46 # Note: this isn't 100% perfect!  Here are some things that can go wrong that
47 # are not caught and reported as traceback-containing HTML:
48 #
49 # - This file could contain a syntax error.  In that case, you would indeed
50 #   get a Web server error since this file wouldn't even compile, and there's
51 #   no way to catch that.  Mailman's install procedure should make this highly
52 #   unlikely.
53 #
54 # - The sys module could be royally screwed, probably we couldn't import it.
55 #   This would indicate a serious problem with the Python installation, so
56 #   it's also highly unlikely to occur.
57
58 \f
59 def run_main():
60     global STEALTH_MODE, websafe
61
62     # These will ensure that even if something between now and the
63     # creation of the real logger below fails, we can still get
64     # *something* meaningful.
65     logger = None
66     try:
67         import paths
68         # When running in non-stealth mode, we need to escape entities,
69         # otherwise we're vulnerable to cross-site scripting attacks.
70         try:
71             if not STEALTH_MODE:
72                 from Mailman.Utils import websafe
73         except:
74             STEALTH_MODE = 1
75             raise
76         # Map stderr to a logger, if possible.
77         from Mailman.Logging.StampedLogger import StampedLogger
78         logger = StampedLogger('error',
79                                label='admin',
80                                manual_reprime=1,
81                                nofail=0,
82                                immediate=1)
83         # Collect stdout in a cStringIO so that if /any/ errors occur during
84         # printing it won't mess up our diagnostics page.
85         from cStringIO import StringIO
86         tempstdout = StringIO()
87         # The name of the module to run is passed in argv[1].  What we
88         # actually do is import the module named by argv[1] that lives in the
89         # Mailman.Cgi package.  That module must have a main() function, which
90         # we dig out and call.
91         scriptname = sys.argv[1]
92         # See the reference manual for why we have to do things this way.
93         # Note that importing should have no side-effects!
94         pkg = __import__('Mailman.Cgi', globals(), locals(), [scriptname])
95         module = getattr(pkg, scriptname)
96         main = getattr(module, 'main')
97         try:
98             import os
99             request_method = os.environ.get('REQUEST_METHOD')
100             if not request_method in ['GET', 'POST', 'HEAD']:
101                 print "Status: 405 Method not allowed"
102                 print "Content-type: text/plain"
103                 print
104                 print "The method is not allowed"
105                 sys.exit()
106                 
107             try:
108                 sys.stderr = logger
109                 sys.stdout = tempstdout
110                 main()
111                 sys.__stdout__.write(tempstdout.getvalue())
112             finally:
113                 sys.stderr = sys.__stderr__
114                 sys.stdout = sys.__stdout__
115         except SystemExit:
116             # This is a valid way for the function to exit.  Be sure any text
117             # produced is still written out to the browser.
118             sys.stdout.write(tempstdout.getvalue())
119     except:
120         print_traceback(logger)
121         print_environment(logger)
122
123
124 \f
125 # We are printing error reporting to two places.  One will always be stdout
126 # and the other will always be the log file.  It is assumed that stdout is an
127 # HTML sink and the log file is a plain text sink.
128
129 def print_traceback(logfp=None):
130     if logfp is None:
131         logfp = sys.__stderr__
132
133     try:
134         import traceback
135     except ImportError:
136         traceback = None
137     try:
138         from Mailman.mm_cfg import VERSION
139     except ImportError:
140         VERSION = '<undetermined>'
141
142     # Write to the log file first.
143     print >> logfp, '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
144     print >> logfp, '[----- Mailman Version: %s -----]' % VERSION
145     print >> logfp, '[----- Traceback ------]'
146     if traceback:
147         traceback.print_exc(file=logfp)
148     else:
149         print >> logfp, '[failed to import module traceback]'
150         print >> logfp, '[exc: %s, var: %s]' % sys.exc_info()[0:2]
151
152     # Write to the HTML sink.
153     print """\
154 Content-type: text/html
155
156 <head><title>Bug in Mailman version %(VERSION)s</title></head>
157 <body bgcolor=#ffffff><h2>Bug in Mailman version %(VERSION)s</h2>
158 <p><h3>We're sorry, we hit a bug!</h3>
159 """ % locals()
160     if not STEALTH_MODE:
161         print '''<p>If you would like to help us identify the problem,
162 please email a copy of this page to the webmaster for this site with
163 a description of what happened.  Thanks!
164
165 <h4>Traceback:</h4><p><pre>'''
166         exc_info = sys.exc_info()
167         if traceback:
168             for line in traceback.format_exception(*exc_info):
169                 print websafe(line),
170         else:
171             print '[failed to import module traceback]'
172             print '[exc: %s, var: %s]' % [websafe(x) for x in exc_info[0:2]]
173         print '\n\n</pre></body>'
174     else:
175         print '''<p>Please inform the webmaster for this site of this
176 problem.  Printing of traceback and other system information has been
177 explicitly inhibited, but the webmaster can find this information in the
178 Mailman error logs.'''
179
180
181 \f
182 def print_environment(logfp=None):
183     if logfp is None:
184         logfp = sys.__stderr__
185
186     try:
187         import os
188     except ImportError:
189         os = None
190
191     # Write some information about our Python executable to the log file.
192     print >> logfp, '[----- Python Information -----]'
193     print >> logfp, 'sys.version     =', sys.version
194     print >> logfp, 'sys.executable  =', sys.executable
195     print >> logfp, 'sys.prefix      =', sys.prefix
196     print >> logfp, 'sys.exec_prefix =', sys.exec_prefix
197     print >> logfp, 'sys.path        =', sys.exec_prefix
198     print >> logfp, 'sys.platform    =', sys.platform
199
200     # Write the same information to the HTML sink.
201     if not STEALTH_MODE:
202         print '''\
203 <p><hr><h4>Python information:</h4>
204
205 <p><table>
206 <tr><th>Variable</th><th>Value</th></tr>
207 '''
208         print '<tr><td><tt>sys.version</tt></td><td>', \
209               sys.version, '</td></tr>'
210         print '<tr><td><tt>sys.executable</tt></td><td>', \
211               sys.executable, '</td></tr>'
212         print '<tr><td><tt>sys.prefix</tt></td><td>', sys.prefix, '</td></tr>'
213         print '<tr><td><tt>sys.exec_prefix</tt></td><td>', \
214               sys.exec_prefix, '</td></tr>'
215         # what else?
216         print '<tr><td><tt>sys.path</tt></td><td>', \
217               sys.exec_prefix, '</td></tr>'
218         print '<tr><td><tt>sys.platform</tt></td><td>', \
219               sys.platform, '</td></tr>'
220         print '</table>'
221
222     # Write environment variables to the log file.
223     print >> logfp, '[----- Environment Variables -----]'
224     if os:
225         for k, v in os.environ.items():
226             print >> logfp, '\t%s: %s' % (k, v)
227     else:
228         print >> logfp, '[failed to import module os]'
229
230     # Write environment variables to the HTML sink.
231     if not STEALTH_MODE:
232         print '''\
233 <p><hr><h4>Environment variables:</h4>
234
235 <p><table>
236 <tr><th>Variable</th><th>Value</th></tr>
237 '''
238         if os:
239             for k, v in os.environ.items():
240                 print '<tr><td><tt>', websafe(k), \
241                       '</tt></td><td>', websafe(v), \
242                       '</td></tr>'
243             print '</table>'
244         else:
245             print '<p><hr>[failed to import module os]'
246
247
248 \f
249 try:
250     run_main()
251 except:
252     # Some exception percolated all the way back up to the top.  This
253     # generally shouldn't happen because the run_main() call is similarly
254     # wrapped, but just in case, we'll give it one last ditch effort to report
255     # problems to *somebody*.  Most likely this will end up in the Web server
256     # log file.
257     try:
258         print_traceback()
259         print_environment()
260     except:
261         # Nope, we're quite screwed
262         print """\
263 Content-type: text/html
264
265 <p><h3>We're sorry, we hit a bug!</h3>
266
267 Mailman experienced a very low level failure and could not even generate a
268 useful traceback for you.  Please report this to the Mailman administrator at
269 this site.
270 """
271         print >> sys.__stderr__, '[Mailman: low level unrecoverable exception]'