comparison roundup/scripts/roundup_server.py @ 1843:d31a25046136

support setgid and running on port < 1024 (patch [SF#777528])
author Richard Jones <richard@users.sourceforge.net>
date Fri, 10 Oct 2003 00:40:16 +0000
parents 921c48ecb3f5
children 9b100d7bcb80
comparison
equal deleted inserted replaced
1842:7bdd9ce360d0 1843:d31a25046136
14 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, 14 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
15 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 15 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
16 # 16 #
17 """ HTTP Server that serves roundup. 17 """ HTTP Server that serves roundup.
18 18
19 $Id: roundup_server.py,v 1.28 2003-10-05 23:29:49 richard Exp $ 19 $Id: roundup_server.py,v 1.29 2003-10-10 00:40:16 richard Exp $
20 """ 20 """
21 21
22 # python version check 22 # python version check
23 from roundup import version_check 23 from roundup import version_check
24 24
120 ''' 120 '''
121 rest = self.path 121 rest = self.path
122 122
123 if rest == '/favicon.ico': 123 if rest == '/favicon.ico':
124 raise client.NotFound 124 raise client.NotFound
125 # self.send_response(200)
126 # self.send_header('Content-Type', 'image/x-ico')
127 # self.end_headers()
128 # self.wfile.write(favicon)
129 # return
130 125
131 i = rest.rfind('?') 126 i = rest.rfind('?')
132 if i >= 0: 127 if i >= 0:
133 rest, query = rest[:i], rest[i+1:] 128 rest, query = rest[:i], rest[i+1:]
134 else: 129 else:
201 return self.client_address[0] 196 return self.client_address[0]
202 else: 197 else:
203 host, port = self.client_address 198 host, port = self.client_address
204 return socket.getfqdn(host) 199 return socket.getfqdn(host)
205 200
206 try:
207 import win32serviceutil
208 except:
209 RoundupService = None
210 else:
211 # allow the win32
212 import win32service
213 import win32event
214 from win32event import *
215 from win32file import *
216
217 SvcShutdown = "ServiceShutdown"
218
219 class RoundupService(win32serviceutil.ServiceFramework,
220 BaseHTTPServer.HTTPServer):
221 ''' A Roundup standalone server for Win32 by Ewout Prangsma
222 '''
223 _svc_name_ = "Roundup Bug Tracker"
224 _svc_display_name_ = "Roundup Bug Tracker"
225 address = ('', 8888)
226 def __init__(self, args):
227 win32serviceutil.ServiceFramework.__init__(self, args)
228 BaseHTTPServer.HTTPServer.__init__(self, self.address,
229 RoundupRequestHandler)
230
231 # Create the necessary NT Event synchronization objects...
232 # hevSvcStop is signaled when the SCM sends us a notification
233 # to shutdown the service.
234 self.hevSvcStop = win32event.CreateEvent(None, 0, 0, None)
235
236 # hevConn is signaled when we have a new incomming connection.
237 self.hevConn = win32event.CreateEvent(None, 0, 0, None)
238
239 # Hang onto this module for other people to use for logging
240 # purposes.
241 import servicemanager
242 self.servicemanager = servicemanager
243
244 def SvcStop(self):
245 # Before we do anything, tell the SCM we are starting the
246 # stop process.
247 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
248 win32event.SetEvent(self.hevSvcStop)
249
250 def SvcDoRun(self):
251 try:
252 self.serve_forever()
253 except SvcShutdown:
254 pass
255
256 def get_request(self):
257 # Call WSAEventSelect to enable self.socket to be waited on.
258 WSAEventSelect(self.socket, self.hevConn, FD_ACCEPT)
259 while 1:
260 try:
261 rv = self.socket.accept()
262 except socket.error, why:
263 if why[0] != WSAEWOULDBLOCK:
264 raise
265 # Use WaitForMultipleObjects instead of select() because
266 # on NT select() is only good for sockets, and not general
267 # NT synchronization objects.
268 rc = WaitForMultipleObjects((self.hevSvcStop, self.hevConn),
269 0, INFINITE)
270 if rc == WAIT_OBJECT_0:
271 # self.hevSvcStop was signaled, this means:
272 # Stop the service!
273 # So we throw the shutdown exception, which gets
274 # caught by self.SvcDoRun
275 raise SvcShutdown
276 # Otherwise, rc == WAIT_OBJECT_0 + 1 which means
277 # self.hevConn was signaled, which means when we call
278 # self.socket.accept(), we'll have our incoming connection
279 # socket!
280 # Loop back to the top, and let that accept do its thing...
281 else:
282 # yay! we have a connection
283 # However... the new socket is non-blocking, we need to
284 # set it back into blocking mode. (The socket that accept()
285 # returns has the same properties as the listening sockets,
286 # this includes any properties set by WSAAsyncSelect, or
287 # WSAEventSelect, and whether its a blocking socket or not.)
288 #
289 # So if you yank the following line, the setblocking() call
290 # will be useless. The socket will still be in non-blocking
291 # mode.
292 WSAEventSelect(rv[0], self.hevConn, 0)
293 rv[0].setblocking(1)
294 break
295 return rv
296
297
298 def usage(message=''): 201 def usage(message=''):
299 if message: 202 if message:
300 message = _('Error: %(error)s\n\n')%{'error': message} 203 message = _('Error: %(error)s\n\n')%{'error': message}
301 print _('''%(message)sUsage: 204 print _('''%(message)sUsage:
302 roundup-server [options] [name=tracker home]* 205 roundup-server [options] [name=tracker home]*
303 206
304 options: 207 options:
305 -n: sets the host name 208 -n: sets the host name
306 -p: sets the port to listen on 209 -p: sets the port to listen on
210 -u: sets the uid to this user after listening on the port
211 -g: sets the gid to this group after listening on the port
307 -l: sets a filename to log to (instead of stdout) 212 -l: sets a filename to log to (instead of stdout)
308 -d: sets a filename to write server PID to. This option causes the server 213 -d: sets a filename to write server PID to. This option causes the server
309 to run in the background. Note: on Windows the PID argument is needed, 214 to run in the background. Note: on Windows the PID argument is needed,
310 but ignored. The -l option *must* be specified if this option is. 215 but ignored. The -l option *must* be specified if this option is.
311 -N: log client machine names in access log instead of IP addresses (much 216 -N: log client machine names in access log instead of IP addresses (much
385 optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:hN') 290 optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:hN')
386 except getopt.GetoptError, e: 291 except getopt.GetoptError, e:
387 usage(str(e)) 292 usage(str(e))
388 293
389 user = ROUNDUP_USER 294 user = ROUNDUP_USER
295 group = None
390 for (opt, arg) in optlist: 296 for (opt, arg) in optlist:
391 if opt == '-n': hostname = arg 297 if opt == '-n': hostname = arg
392 elif opt == '-p': port = int(arg) 298 elif opt == '-p': port = int(arg)
393 elif opt == '-u': user = arg 299 elif opt == '-u': user = arg
300 elif opt == '-g': group = arg
394 elif opt == '-d': pidfile = abspath(arg) 301 elif opt == '-d': pidfile = abspath(arg)
395 elif opt == '-l': logfile = abspath(arg) 302 elif opt == '-l': logfile = abspath(arg)
396 elif opt == '-h': usage() 303 elif opt == '-h': usage()
397 elif opt == '-N': RoundupRequestHandler.LOG_IPADDRESS = 0 304 elif opt == '-N': RoundupRequestHandler.LOG_IPADDRESS = 0
398 305
399 if pidfile and not logfile: 306 if pidfile and not logfile:
400 raise ValueError, _("logfile *must* be specified if pidfile is") 307 raise ValueError, _("logfile *must* be specified if pidfile is")
308
309 # obtain server before changing user id - allows to use port <
310 # 1024 if started as root
311 address = (hostname, port)
312 httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler)
313
314 if group is not None and hasattr(os, 'getgid'):
315 # if root, setgid to the running user
316 if not os.getgid() and user is not None:
317 try:
318 import pwd
319 except ImportError:
320 raise ValueError, _("Can't change groups - no pwd module")
321 try:
322 gid = pwd.getpwnam(user)[3]
323 except KeyError:
324 raise ValueError,_("Group %(group)s doesn't exist")%locals()
325 os.setgid(gid)
326 elif os.getgid() and user is not None:
327 print _('WARNING: ignoring "-g" argument, not root')
401 328
402 if hasattr(os, 'getuid'): 329 if hasattr(os, 'getuid'):
403 # if root, setuid to the running user 330 # if root, setuid to the running user
404 if not os.getuid() and user is not None: 331 if not os.getuid() and user is not None:
405 try: 332 try:
434 exc_type, exc_value = sys.exc_info()[:2] 361 exc_type, exc_value = sys.exc_info()[:2]
435 usage('%s: %s'%(exc_type, exc_value)) 362 usage('%s: %s'%(exc_type, exc_value))
436 363
437 # we don't want the cgi module interpreting the command-line args ;) 364 # we don't want the cgi module interpreting the command-line args ;)
438 sys.argv = sys.argv[:1] 365 sys.argv = sys.argv[:1]
439 address = (hostname, port) 366
440
441 # fork?
442 if pidfile: 367 if pidfile:
443 if RoundupService: 368 if not hasattr(os, 'fork'):
444 # don't do any other stuff
445 RoundupService.address = address
446 return win32serviceutil.HandleCommandLine(RoundupService)
447 elif not hasattr(os, 'fork'):
448 print "Sorry, you can't run the server as a daemon on this" \ 369 print "Sorry, you can't run the server as a daemon on this" \
449 'Operating System' 370 'Operating System'
450 sys.exit(0) 371 sys.exit(0)
451 else: 372 else:
452 daemonize(pidfile) 373 daemonize(pidfile)
454 # redirect stdout/stderr to our logfile 375 # redirect stdout/stderr to our logfile
455 if logfile: 376 if logfile:
456 # appending, unbuffered 377 # appending, unbuffered
457 sys.stdout = sys.stderr = open(logfile, 'a', 0) 378 sys.stdout = sys.stderr = open(logfile, 'a', 0)
458 379
459 httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler)
460 print _('Roundup server started on %(address)s')%locals() 380 print _('Roundup server started on %(address)s')%locals()
461 try: 381 try:
462 httpd.serve_forever() 382 httpd.serve_forever()
463 except KeyboardInterrupt: 383 except KeyboardInterrupt:
464 print 'Keyboard Interrupt: exiting' 384 print 'Keyboard Interrupt: exiting'

Roundup Issue Tracker: http://roundup-tracker.org/