The NT service handling in roundup 0.6.0, apart from not
working properly (was incorrectly doing EventSelect
within "while 1:") also does not allow parameters (port,
etc) to be set for service.
I have hacked out the following code from
roundup_server.py into a file (which I called
roundup_service.py).
Benefits:
- It works.
- It lets you store server/service parameters in
registry.
Perhaps this is of use to you.
Regards,
Giles Brown
"""
import os
import sys
import socket
from BaseHTTPServer import HTTPServer
import win32service
import win32serviceutil
import win32event
import win32file
from roundup_server import RoundupRequestHandler
class SvcShutdown(Exception): pass
class RoundupService
(win32serviceutil.ServiceFramework, HTTPServer):
'''A Roundup standalone server for Win32 by Ewout
Prangsma.
Modified by Giles Brown.
'''
_svc_name_ = "Roundup Bug Tracker"
_svc_display_name_ = "Roundup Bug Tracker"
# Default options.
# Sub-set of roundup_server.py options that make
sense for NT service.
options = {
'hostname' : '',
'port' : 8080,
'logfile' : None,
'log_ipaddress' : 1,
}
def __init__(self, args):
options = dict(RoundupService.options)
# Read options from registry
for name, val in options.items():
options[name] =
win32serviceutil.GetServiceCustomOption(
RoundupService, name, val)
RoundupRequestHandler.LOG_IPADDRESS = options
['log_ipaddress']
numtrackers =
win32serviceutil.GetServiceCustomOption(
RoundupService, 'numtrackers', 0)
RoundupRequestHandler.TRACKER_HOMES.clear()
for i in range(numtrackers):
trackername =
win32serviceutil.GetServiceCustomOption(
RoundupService, 'trackername%d' %
i, '<missing registry value>')
trackerhome =
win32serviceutil.GetServiceCustomOption(
RoundupService, 'trackerhome%d' %
i, '<missing registry value>')
RoundupRequestHandler.TRACKER_HOMES
[trackername] = trackerhome
win32serviceutil.ServiceFramework.__init__(self,
args)
HTTPServer.__init__(self, (options['hostname'],
options['port']),
RoundupRequestHandler)
# redirect stdout/stderr to our logfile
if options['logfile']:
# appending, unbuffered
sys.stdout = sys.stderr = open(options
['logfile'], 'a', 0)
else:
# Re-direct standard output so PythonWin
Trace Collection can read
import win32traceutil
# 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):
'''Return (blocking until) next HTTP request.'''
# Call WSAEventSelect to enable self.socket to be
waited on.
win32file.WSAEventSelect(self.socket,
self.hevConn, win32file.FD_ACCEPT)
# Loop until either:
# - we accept a socket connection, or
# - we raise an exception because we have
been asked to stop
while 1:
try:
rv = self.socket.accept()
break
except socket.error, why:
if why[0] != win32file.WSAEWOULDBLOCK:
raise
# Use WaitForMultipleObjects instead of select
() because
# on NT select() is only good for sockets, and
not general
# NT synchronization objects.
rc = win32event.WaitForMultipleObjects
((self.hevSvcStop,
self.hevConn), 0, win32event.INFINITE)
if rc == win32event.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...
# 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.
win32file.WSAEventSelect(rv[0], self.hevConn, 0)
rv[0].setblocking(1)
return rv
if __name__ == '__main__':
#
# Ugly hack to override win32serviceutil usage()
origusage = win32serviceutil.usage
def usage():
# Catch call to sys.exit(1) so we can tack our
message on the end
try:
origusage()
except SystemExit, e:
pass
print " -n hostname : sets the host name"
print " -p port : sets the port to listen on"
print " -l path : sets path of log file"
print " -N : log names in access log not IP
addresses (much slower)"
print " -t <tracker name>=<tracker home> : add
track to service"
print
print "Cannot change tracker options
using 'update'."
print "Instead use 'remove' then 'install'."
sys.exit(1)
win32serviceutil.usage = usage
def customOptionHandler(optlist):
options = dict(RoundupService.options)
trackers = []
# Override default values
for (opt, arg) in optlist:
if opt == '-n':
options['hostname'] = arg
elif opt == '-p':
options['port'] = int(arg)
elif opt == '-l':
options['logfile'] = os.path.abspath(arg)
elif opt == '-N':
options['log_ipaddress'] = 0
elif opt == '-t':
trackers.append(arg.split('='))
# Save settings
for name, val in options.iteritems():
win32serviceutil.SetServiceCustomOption
(RoundupService, name, val)
# XXX: would use 'enumerate' in python 2.3
onwards
for index, (name, home) in zip(range(len
(trackers)), trackers):
win32serviceutil.SetServiceCustomOption
(RoundupService,
'trackername%d' % index, name)
win32serviceutil.SetServiceCustomOption
(RoundupService,
'trackerhome%d' % index, home)
win32serviceutil.SetServiceCustomOption
(RoundupService,
'numtrackers', len(trackers))
win32serviceutil.HandleCommandLine(RoundupService,
customInstallOptions='t:n:p:l:N',
customOptionHandler=customOptionHandler)
# Uncomment this for debugging first part
of 'RoundupService.__init__'
#else:
# import win32traceutil
""" |