Mercurial > p > roundup > code
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' |
