Skip to content

Commit 85b431c

Browse files
author
Richard Jones
committed
fix roundup win service
1 parent 7ed80d3 commit 85b431c

File tree

3 files changed

+144
-16
lines changed

3 files changed

+144
-16
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Fixed:
6464
- hard-coded python2.3-ism (socket.timeout) fixed
6565
- fixed activity displaying as future because of Date arithmetic fix in 0.6.3
6666
(sf bug 842027).
67+
- fix Windows service mode for roundup-server (sf bug 819890)
6768
- fixed #white in cgitb (thanks Henrik Levkowetz)
6869

6970

roundup/instance.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: instance.py,v 1.10 2003-09-04 00:47:01 richard Exp $
18+
# $Id: instance.py,v 1.11 2003-12-06 02:46:34 richard Exp $
1919

2020
__doc__ = '''
2121
Tracker handling (open tracker).
@@ -43,7 +43,7 @@ def open(self):
4343
self._load_config('security.py', db=db)
4444

4545

46-
def __load_python(self, file):
46+
def _load_python(self, file):
4747
file = os.path.join(tracker_home, file)
4848
vars = Vars()
4949
execfile(file, vars.__dict__)

roundup/scripts/roundup_server.py

Lines changed: 141 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#
1717
""" HTTP Server that serves roundup.
1818
19-
$Id: roundup_server.py,v 1.35 2003-12-04 02:43:07 richard Exp $
19+
$Id: roundup_server.py,v 1.36 2003-12-06 02:46:34 richard Exp $
2020
"""
2121

2222
# python version check
@@ -44,6 +44,12 @@
4444
}
4545

4646
ROUNDUP_USER = None
47+
ROUNDUP_GROUP = None
48+
ROUNDUP_LOG_IP = 1
49+
HOSTNAME = ''
50+
PORT = 8080
51+
PIDFILE = None
52+
LOGFILE = None
4753

4854

4955
#
@@ -196,7 +202,7 @@ def inner_run_cgi(self):
196202
c = tracker.Client(tracker, self, env)
197203
c.main()
198204

199-
LOG_IPADDRESS = 1
205+
LOG_IPADDRESS = ROUNDUP_LOG_IP
200206
def address_string(self):
201207
if self.LOG_IPADDRESS:
202208
return self.client_address[0]
@@ -208,23 +214,128 @@ def error():
208214
exc_type, exc_value = sys.exc_info()[:2]
209215
return _('Error: %s: %s' % (exc_type, exc_value))
210216

217+
try:
218+
import win32serviceutil
219+
except:
220+
RoundupService = None
221+
else:
222+
# allow the win32
223+
import win32service
224+
import win32event
225+
from win32event import *
226+
from win32file import *
227+
228+
SvcShutdown = "ServiceShutdown"
229+
230+
class RoundupService(win32serviceutil.ServiceFramework,
231+
BaseHTTPServer.HTTPServer):
232+
''' A Roundup standalone server for Win32 by Ewout Prangsma
233+
'''
234+
_svc_name_ = "Roundup Bug Tracker"
235+
_svc_display_name_ = "Roundup Bug Tracker"
236+
address = (HOSTNAME, PORT)
237+
def __init__(self, args):
238+
# redirect stdout/stderr to our logfile
239+
if LOGFILE:
240+
# appending, unbuffered
241+
sys.stdout = sys.stderr = open(LOGFILE, 'a', 0)
242+
win32serviceutil.ServiceFramework.__init__(self, args)
243+
BaseHTTPServer.HTTPServer.__init__(self, self.address,
244+
RoundupRequestHandler)
245+
246+
# Create the necessary NT Event synchronization objects...
247+
# hevSvcStop is signaled when the SCM sends us a notification
248+
# to shutdown the service.
249+
self.hevSvcStop = win32event.CreateEvent(None, 0, 0, None)
250+
251+
# hevConn is signaled when we have a new incomming connection.
252+
self.hevConn = win32event.CreateEvent(None, 0, 0, None)
253+
254+
# Hang onto this module for other people to use for logging
255+
# purposes.
256+
import servicemanager
257+
self.servicemanager = servicemanager
258+
259+
def SvcStop(self):
260+
# Before we do anything, tell the SCM we are starting the
261+
# stop process.
262+
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
263+
win32event.SetEvent(self.hevSvcStop)
264+
265+
def SvcDoRun(self):
266+
try:
267+
self.serve_forever()
268+
except SvcShutdown:
269+
pass
270+
271+
def get_request(self):
272+
# Call WSAEventSelect to enable self.socket to be waited on.
273+
WSAEventSelect(self.socket, self.hevConn, FD_ACCEPT)
274+
while 1:
275+
try:
276+
rv = self.socket.accept()
277+
except socket.error, why:
278+
if why[0] != WSAEWOULDBLOCK:
279+
raise
280+
# Use WaitForMultipleObjects instead of select() because
281+
# on NT select() is only good for sockets, and not general
282+
# NT synchronization objects.
283+
rc = WaitForMultipleObjects((self.hevSvcStop, self.hevConn),
284+
0, INFINITE)
285+
if rc == WAIT_OBJECT_0:
286+
# self.hevSvcStop was signaled, this means:
287+
# Stop the service!
288+
# So we throw the shutdown exception, which gets
289+
# caught by self.SvcDoRun
290+
raise SvcShutdown
291+
# Otherwise, rc == WAIT_OBJECT_0 + 1 which means
292+
# self.hevConn was signaled, which means when we call
293+
# self.socket.accept(), we'll have our incoming connection
294+
# socket!
295+
# Loop back to the top, and let that accept do its thing...
296+
else:
297+
# yay! we have a connection
298+
# However... the new socket is non-blocking, we need to
299+
# set it back into blocking mode. (The socket that accept()
300+
# returns has the same properties as the listening sockets,
301+
# this includes any properties set by WSAAsyncSelect, or
302+
# WSAEventSelect, and whether its a blocking socket or not.)
303+
#
304+
# So if you yank the following line, the setblocking() call
305+
# will be useless. The socket will still be in non-blocking
306+
# mode.
307+
WSAEventSelect(rv[0], self.hevConn, 0)
308+
rv[0].setblocking(1)
309+
break
310+
return rv
311+
211312
def usage(message=''):
313+
if RoundupService:
314+
win = ''' -c: Windows Service options. If you want to run the server as a Windows
315+
Service, you must configure the rest of the options by changing the
316+
constants of this program. You will at least configure one tracker
317+
in the TRACKER_HOMES variable. This option is mutually exclusive
318+
from the rest. Typing "roundup-server -c help" shows Windows
319+
Services specifics.'''
320+
else:
321+
win = ''
322+
port=PORT
212323
print _('''%(message)s
213-
214324
Usage:
215325
roundup-server [options] [name=tracker home]*
216326
217327
options:
218328
-n: sets the host name
219-
-p: sets the port to listen on
329+
-p: sets the port to listen on (default: %(port)s)
220330
-u: sets the uid to this user after listening on the port
221331
-g: sets the gid to this group after listening on the port
222332
-l: sets a filename to log to (instead of stdout)
223-
-d: sets a filename to write server PID to. This option causes the server
224-
to run in the background. Note: on Windows the PID argument is needed,
225-
but ignored. The -l option *must* be specified if this option is.
333+
-d: run the server in the background and on UN*X write the server's PID
334+
to the nominated file. The -l option *must* be specified if this
335+
option is.
226336
-N: log client machine names in access log instead of IP addresses (much
227337
slower)
338+
%(win)s
228339
229340
name=tracker home:
230341
Sets the tracker home(s) to use. The name is how the tracker is
@@ -273,21 +384,29 @@ def daemonize(pidfile):
273384
os.dup2(devnull, 1)
274385
os.dup2(devnull, 2)
275386

276-
def run(port=8080, success_message=None):
387+
def run(port=PORT, success_message=None):
277388
''' Script entry point - handle args and figure out what to to.
278389
'''
279390
# time out after a minute if we can
280391
import socket
281392
if hasattr(socket, 'setdefaulttimeout'):
282393
socket.setdefaulttimeout(60)
283394

284-
hostname = ''
285-
pidfile = None
286-
logfile = None
395+
hostname = HOSTNAME
396+
pidfile = PIDFILE
397+
logfile = LOGFILE
398+
user = ROUNDUP_USER
399+
group = ROUNDUP_GROUP
400+
svc_args = None
401+
287402
try:
288403
# handle the command-line args
404+
options = 'n:p:u:d:l:hN'
405+
if RoundupService:
406+
options += 'c'
407+
289408
try:
290-
optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:hN')
409+
optlist, args = getopt.getopt(sys.argv[1:], options)
291410
except getopt.GetoptError, e:
292411
usage(str(e))
293412

@@ -302,6 +421,10 @@ def run(port=8080, success_message=None):
302421
elif opt == '-l': logfile = os.path.abspath(arg)
303422
elif opt == '-h': usage()
304423
elif opt == '-N': RoundupRequestHandler.LOG_IPADDRESS = 0
424+
elif opt == '-c': svc_args = [opt] + args; args = None
425+
426+
if svc_args is not None and len(optlist) > 1:
427+
raise ValueError, _("windows service option must be the only one")
305428

306429
if pidfile and not logfile:
307430
raise ValueError, _("logfile *must* be specified if pidfile is")
@@ -355,11 +478,11 @@ def run(port=8080, success_message=None):
355478
if args:
356479
d = {}
357480
for arg in args:
358-
try:
481+
try:
359482
name, home = arg.split('=')
360483
except ValueError:
361484
raise ValueError, _("Instances must be name=home")
362-
d[name] = home
485+
d[name] = os.path.abspath(home)
363486
RoundupRequestHandler.TRACKER_HOMES = d
364487
except SystemExit:
365488
raise
@@ -380,6 +503,10 @@ def run(port=8080, success_message=None):
380503
else:
381504
daemonize(pidfile)
382505

506+
if svc_args is not None:
507+
# don't do any other stuff
508+
return win32serviceutil.HandleCommandLine(RoundupService, argv=svc_args)
509+
383510
# redirect stdout/stderr to our logfile
384511
if logfile:
385512
# appending, unbuffered

0 commit comments

Comments
 (0)