index 05936ef4d10319fd1f0d5f1a04322d6b6f20cf05..2b20fd7b7d2d24a12c75ba416c7d099cb7935070 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-""" HTTP Server that serves roundup.
-$Id: roundup_server.py,v 1.33 2003-11-11 21:51:52 richard Exp $
+"""Command-line script that runs a server over roundup.cgi.client.
+
+$Id: roundup_server.py,v 1.41 2004-04-05 00:51:45 richard Exp $
"""
+__docformat__ = 'restructuredtext'
# python version check
from roundup import version_check
import roundup.instance
from roundup.i18n import _
+try:
+ import signal
+except:
+ signal = None
+
#
## Configuration
#
}
ROUNDUP_USER = None
+ROUNDUP_GROUP = None
+ROUNDUP_LOG_IP = 1
+HOSTNAME = ''
+PORT = 8080
+PIDFILE = None
+LOGFILE = None
#
import zlib, base64
favico = zlib.decompress(base64.decodestring('''
-eJyVUk2IQVEUfn4yaRYjibdQZiVba/ZE2djIUmHWFjaKGVmIlY2iFMVG2ViQhXqFSP6iFFJvw4uF
-LGdWd743mpeMn+a88917Oue7955z3qEoET6FQkHx8iahKDV2A8B7XgERRf/EKMSUzyf8ypbbnnQy
-mWBdr9eVSkVw3tJGoxGNRpvNZigUyufzWPv9Pvwcx0UiERj7/V4g73Y7j8fTarWMRmO73U4kEkKI
-YZhardbr9eLxuOD0+/2ZTMZisYjFYpqmU6kU799uN5tNMBg8HA7ZbPY8GaTh8/mEipRKpclk0ul0
-NpvNarUmk0mWZS/yr9frcrmc+iMOh+NWydPp1Ov1SiSSc344HL7fKKfTiSN2u12tVqOcxWJxn6/V
-ag0GAwxkrlKp5vP5fT7ulMlk6XRar9dLpVIUXi6Xb5Hxa1wul0ajKZVKsVjM7XYXCoVOp3OVPJvN
-AoFAtVo1m825XO7hSODOYrH4kHbxxGAwwODBGI/H6DBs5LNara7yl8slGjIcDsHpdrunU6PRCAP2
-r3fPdUcIYeyEfLSAJ0LeAUZHCAt8Al/8/kLIEWDB5YDj0wm8fAP6fVfo
+eJztjr1PmlEUh59XgVoshdYPWorFIhaRFq0t9pNq37b60lYSTRzcTFw6GAfj5gDYaF0dTB0MxMSE
+gQQd3FzKJiEC0UCIUUN1M41pV2JCXySg/0ITn5tfzvmdc+85FwT56HSc81UJjXJsk1UsNcsSqCk1
+BS64lK+vr7OyssLJyQl2ux2j0cjU1BQajYZIJEIwGMRms+H3+zEYDExOTjI2Nsbm5iZWqxWv18vW
+1hZDQ0Ok02kmJiY4Ojpienqa3d1dxsfHUSqVeDwe5ufnyeVyrK6u4nK5ODs7Y3FxEYfDwdzcHCaT
+icPDQ5LJJIIgMDIyQj6fZ39/n+3tbdbW1pAkiYWFBWZmZtjb2yMejzM8PEwgEMDn85HNZonFYqjV
+asLhMMvLy2QyGfR6PaOjowwODmKxWDg+PkalUhEKhSgUCiwtLWE2m9nZ2UGhULCxscHp6SmpVIpo
+NMrs7CwHBwdotVoSiQRXXPG/IzY7RHtt922xjFRb01H1XhKfPBNbi/7my7rrLXJ88eppvxwEfV3f
+NY3Y6exofVdsV3+2wnPFDdPjB83n7xuVpcFvygPbGwxF31LZIKrQDfR2Xvh7lmrX654L/7bvlnng
+bn3Zuj8M9Hepux6VfZtW1yA6K7cfGqVu8TL325u+fHTb71QKbk+7TZQ+lTc6RcnpqW8qmVQBoj/g
+23eo0sr/NIGvB37K+lOWXMvJ+uWFeKGU/03Cb7n3D4M3wxI=
'''.strip()))
class RoundupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
self.send_error(404, self.path)
except client.Unauthorised:
self.send_error(403, self.path)
- except socket.timeout:
- s = StringIO.StringIO()
- traceback.print_exc(None, s)
- self.log_message(str(s.getvalue()))
except:
- # it'd be nice to be able to detect if these are going to have
- # any effect...
- self.send_response(400)
- self.send_header('Content-Type', 'text/html')
- self.end_headers()
- try:
- reload(cgitb)
- self.wfile.write(cgitb.breaker())
- self.wfile.write(cgitb.html())
- except:
+ exc, val, tb = sys.exc_info()
+ if hasattr(socket, 'timeout') and exc == socket.timeout:
s = StringIO.StringIO()
traceback.print_exc(None, s)
- self.wfile.write("<pre>")
- self.wfile.write(cgi.escape(s.getvalue()))
- self.wfile.write("</pre>\n")
+ self.log_message(str(s.getvalue()))
+ else:
+ # it'd be nice to be able to detect if these are going to have
+ # any effect...
+ self.send_response(400)
+ self.send_header('Content-Type', 'text/html')
+ self.end_headers()
+ try:
+ reload(cgitb)
+ self.wfile.write(cgitb.breaker())
+ self.wfile.write(cgitb.html())
+ except:
+ s = StringIO.StringIO()
+ traceback.print_exc(None, s)
+ self.wfile.write("<pre>")
+ self.wfile.write(cgi.escape(s.getvalue()))
+ self.wfile.write("</pre>\n")
sys.stdin = save_stdin
do_GET = do_POST = run_cgi
rest = self.path
if rest == '/favicon.ico':
- raise client.NotFound
+ self.send_response(200)
+ self.send_header('Content-Type', 'image/x-icon')
+ self.end_headers()
+ self.wfile.write(favico)
+ return
i = rest.rfind('?')
if i >= 0:
c = tracker.Client(tracker, self, env)
c.main()
- LOG_IPADDRESS = 1
+ LOG_IPADDRESS = ROUNDUP_LOG_IP
def address_string(self):
if self.LOG_IPADDRESS:
return self.client_address[0]
host, port = self.client_address
return socket.getfqdn(host)
+ def log_message(self, format, *args):
+ ''' Try to *safely* log to stderr.
+ '''
+ try:
+ BaseHTTPServer.BaseHTTPRequestHandler.log_message(self,
+ format, *args)
+ except IOError:
+ # stderr is no longer viable
+ pass
+
def error():
exc_type, exc_value = sys.exc_info()[:2]
return _('Error: %s: %s' % (exc_type, exc_value))
+try:
+ import win32serviceutil
+except:
+ RoundupService = None
+else:
+ # allow the win32
+ import win32service
+ import win32event
+ from win32event import *
+ from win32file import *
+
+ SvcShutdown = "ServiceShutdown"
+
+ class RoundupService(win32serviceutil.ServiceFramework,
+ BaseHTTPServer.HTTPServer):
+ ''' A Roundup standalone server for Win32 by Ewout Prangsma
+ '''
+ _svc_name_ = "Roundup Bug Tracker"
+ _svc_display_name_ = "Roundup Bug Tracker"
+ address = (HOSTNAME, PORT)
+ def __init__(self, args):
+ # redirect stdout/stderr to our logfile
+ if LOGFILE:
+ # appending, unbuffered
+ sys.stdout = sys.stderr = open(LOGFILE, 'a', 0)
+ win32serviceutil.ServiceFramework.__init__(self, args)
+ BaseHTTPServer.HTTPServer.__init__(self, self.address,
+ RoundupRequestHandler)
+
+ # Create the necessary NT Event synchronization objects...
+ # hevSvcStop is signaled when the SCM sends us a notification
+ # to shutdown the service.
+ self.hevSvcStop = win32event.CreateEvent(None, 0, 0, None)
+
+ # hevConn is signaled when we have a new incomming connection.
+ self.hevConn = win32event.CreateEvent(None, 0, 0, None)
+
+ # Hang onto this module for other people to use for logging
+ # purposes.
+ import servicemanager
+ self.servicemanager = servicemanager
+
+ def SvcStop(self):
+ # Before we do anything, tell the SCM we are starting the
+ # stop process.
+ self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
+ win32event.SetEvent(self.hevSvcStop)
+
+ def SvcDoRun(self):
+ try:
+ self.serve_forever()
+ except SvcShutdown:
+ pass
+
+ def get_request(self):
+ # Call WSAEventSelect to enable self.socket to be waited on.
+ WSAEventSelect(self.socket, self.hevConn, FD_ACCEPT)
+ while 1:
+ try:
+ rv = self.socket.accept()
+ except socket.error, why:
+ if why[0] != WSAEWOULDBLOCK:
+ raise
+ # Use WaitForMultipleObjects instead of select() because
+ # on NT select() is only good for sockets, and not general
+ # NT synchronization objects.
+ rc = WaitForMultipleObjects((self.hevSvcStop, self.hevConn),
+ 0, INFINITE)
+ if rc == WAIT_OBJECT_0:
+ # self.hevSvcStop was signaled, this means:
+ # Stop the service!
+ # So we throw the shutdown exception, which gets
+ # caught by self.SvcDoRun
+ raise SvcShutdown
+ # Otherwise, rc == WAIT_OBJECT_0 + 1 which means
+ # self.hevConn was signaled, which means when we call
+ # self.socket.accept(), we'll have our incoming connection
+ # socket!
+ # Loop back to the top, and let that accept do its thing...
+ else:
+ # yay! we have a connection
+ # However... the new socket is non-blocking, we need to
+ # set it back into blocking mode. (The socket that accept()
+ # returns has the same properties as the listening sockets,
+ # this includes any properties set by WSAAsyncSelect, or
+ # WSAEventSelect, and whether its a blocking socket or not.)
+ #
+ # So if you yank the following line, the setblocking() call
+ # will be useless. The socket will still be in non-blocking
+ # mode.
+ WSAEventSelect(rv[0], self.hevConn, 0)
+ rv[0].setblocking(1)
+ break
+ return rv
+
def usage(message=''):
+ if RoundupService:
+ win = ''' -c: Windows Service options. If you want to run the server as a Windows
+ Service, you must configure the rest of the options by changing the
+ constants of this program. You will at least configure one tracker
+ in the TRACKER_HOMES variable. This option is mutually exclusive
+ from the rest. Typing "roundup-server -c help" shows Windows
+ Services specifics.'''
+ else:
+ win = ''
+ port=PORT
print _('''%(message)s
-
Usage:
roundup-server [options] [name=tracker home]*
options:
-n: sets the host name
- -p: sets the port to listen on
+ -p: sets the port to listen on (default: %(port)s)
-u: sets the uid to this user after listening on the port
-g: sets the gid to this group after listening on the port
- -l: sets a filename to log to (instead of stdout)
- -d: sets a filename to write server PID to. This option causes the server
- to run in the background. Note: on Windows the PID argument is needed,
- but ignored. The -l option *must* be specified if this option is.
+ -l: sets a filename to log to (instead of stderr / stdout)
+ -d: run the server in the background and on UN*X write the server's PID
+ to the nominated file. The -l option *must* be specified if this
+ option is.
-N: log client machine names in access log instead of IP addresses (much
slower)
+%(win)s
name=tracker home:
Sets the tracker home(s) to use. The name is how the tracker is
os.dup2(devnull, 1)
os.dup2(devnull, 2)
-def run(port=8080, success_message=None):
+def run(port=PORT, success_message=None):
''' Script entry point - handle args and figure out what to to.
'''
# time out after a minute if we can
if hasattr(socket, 'setdefaulttimeout'):
socket.setdefaulttimeout(60)
- hostname = ''
- pidfile = None
- logfile = None
+ hostname = HOSTNAME
+ pidfile = PIDFILE
+ logfile = LOGFILE
+ user = ROUNDUP_USER
+ group = ROUNDUP_GROUP
+ svc_args = None
+
try:
# handle the command-line args
+ options = 'n:p:u:d:l:hN'
+ if RoundupService:
+ options += 'c'
+
try:
- optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:hN')
+ optlist, args = getopt.getopt(sys.argv[1:], options)
except getopt.GetoptError, e:
usage(str(e))
elif opt == '-l': logfile = os.path.abspath(arg)
elif opt == '-h': usage()
elif opt == '-N': RoundupRequestHandler.LOG_IPADDRESS = 0
+ elif opt == '-c': svc_args = [opt] + args; args = None
+
+ if svc_args is not None and len(optlist) > 1:
+ raise ValueError, _("windows service option must be the only one")
if pidfile and not logfile:
raise ValueError, _("logfile *must* be specified if pidfile is")
if args:
d = {}
for arg in args:
- try:
+ try:
name, home = arg.split('=')
except ValueError:
raise ValueError, _("Instances must be name=home")
- d[name] = home
+ d[name] = os.path.abspath(home)
RoundupRequestHandler.TRACKER_HOMES = d
except SystemExit:
raise
else:
daemonize(pidfile)
+ if svc_args is not None:
+ # don't do any other stuff
+ return win32serviceutil.HandleCommandLine(RoundupService, argv=svc_args)
+
# redirect stdout/stderr to our logfile
if logfile:
# appending, unbuffered