Mercurial > p > roundup > code
comparison roundup/scripts/roundup_server.py @ 6040:d5c51d1ef09c
flake8 whitespace format fixes.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Tue, 07 Jan 2020 21:26:43 -0500 |
| parents | f822a91b3778 |
| children | 5f275158cfa9 |
comparison
equal
deleted
inserted
replaced
| 6039:0dc1e0222353 | 6040:d5c51d1ef09c |
|---|---|
| 93 # Note: the order is important. Preferred multiprocess type | 93 # Note: the order is important. Preferred multiprocess type |
| 94 # is the last element of this list. | 94 # is the last element of this list. |
| 95 # "debug" means "none" + no tracker/template cache | 95 # "debug" means "none" + no tracker/template cache |
| 96 MULTIPROCESS_TYPES = ["debug", "none"] | 96 MULTIPROCESS_TYPES = ["debug", "none"] |
| 97 try: | 97 try: |
| 98 import thread | 98 import thread # nosrc: F401 |
| 99 except ImportError: | 99 except ImportError: |
| 100 pass | 100 pass |
| 101 else: | 101 else: |
| 102 MULTIPROCESS_TYPES.append("thread") | 102 MULTIPROCESS_TYPES.append("thread") |
| 103 if hasattr(os, 'fork'): | 103 if hasattr(os, 'fork'): |
| 104 MULTIPROCESS_TYPES.append("fork") | 104 MULTIPROCESS_TYPES.append("fork") |
| 105 DEFAULT_MULTIPROCESS = MULTIPROCESS_TYPES[-1] | 105 DEFAULT_MULTIPROCESS = MULTIPROCESS_TYPES[-1] |
| 106 | |
| 106 | 107 |
| 107 def auto_ssl(): | 108 def auto_ssl(): |
| 108 print(_('WARNING: generating temporary SSL certificate')) | 109 print(_('WARNING: generating temporary SSL certificate')) |
| 109 import OpenSSL, random | 110 import OpenSSL, random |
| 110 pkey = OpenSSL.crypto.PKey() | 111 pkey = OpenSSL.crypto.PKey() |
| 111 pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 768) | 112 pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 768) |
| 112 cert = OpenSSL.crypto.X509() | 113 cert = OpenSSL.crypto.X509() |
| 113 cert.set_serial_number(random.randint(0, sys.maxsize)) | 114 cert.set_serial_number(random.randint(0, sys.maxsize)) |
| 114 cert.gmtime_adj_notBefore(0) | 115 cert.gmtime_adj_notBefore(0) |
| 115 cert.gmtime_adj_notAfter(60 * 60 * 24 * 365) # one year | 116 cert.gmtime_adj_notAfter(60 * 60 * 24 * 365) # one year |
| 116 cert.get_subject().CN = '*' | 117 cert.get_subject().CN = '*' |
| 117 cert.get_subject().O = 'Roundup Dummy Certificate' | 118 cert.get_subject().O = 'Roundup Dummy Certificate' |
| 118 cert.get_issuer().CN = 'Roundup Dummy Certificate Authority' | 119 cert.get_issuer().CN = 'Roundup Dummy Certificate Authority' |
| 119 cert.get_issuer().O = 'Self-Signed' | 120 cert.get_issuer().O = 'Self-Signed' |
| 120 cert.set_pubkey(pkey) | 121 cert.set_pubkey(pkey) |
| 123 ctx.use_privatekey(pkey) | 124 ctx.use_privatekey(pkey) |
| 124 ctx.use_certificate(cert) | 125 ctx.use_certificate(cert) |
| 125 | 126 |
| 126 return ctx | 127 return ctx |
| 127 | 128 |
| 129 | |
| 128 class SecureHTTPServer(http_.server.HTTPServer): | 130 class SecureHTTPServer(http_.server.HTTPServer): |
| 129 def __init__(self, server_address, HandlerClass, ssl_pem=None): | 131 def __init__(self, server_address, HandlerClass, ssl_pem=None): |
| 130 assert SSL, "pyopenssl not installed" | 132 assert SSL, "pyopenssl not installed" |
| 131 http_.server.HTTPServer.__init__(self, server_address, HandlerClass) | 133 http_.server.HTTPServer.__init__(self, server_address, HandlerClass) |
| 132 self.socket = socket.socket(self.address_family, self.socket_type) | 134 self.socket = socket.socket(self.address_family, self.socket_type) |
| 174 class ConnFixer(object): | 176 class ConnFixer(object): |
| 175 """ wraps an SSL socket so that it implements makefile | 177 """ wraps an SSL socket so that it implements makefile |
| 176 which the HTTP handlers require """ | 178 which the HTTP handlers require """ |
| 177 def __init__(self, conn): | 179 def __init__(self, conn): |
| 178 self.__conn = conn | 180 self.__conn = conn |
| 181 | |
| 179 def makefile(self, mode, bufsize): | 182 def makefile(self, mode, bufsize): |
| 180 fo = socket._fileobject(self.__conn, mode, bufsize) | 183 fo = socket._fileobject(self.__conn, mode, bufsize) |
| 181 return RetryingFile(fo) | 184 return RetryingFile(fo) |
| 182 | 185 |
| 183 def __getattr__(self, attrib): | 186 def __getattr__(self, attrib): |
| 184 return getattr(self.__conn, attrib) | 187 return getattr(self.__conn, attrib) |
| 185 | 188 |
| 186 conn = ConnFixer(conn) | 189 conn = ConnFixer(conn) |
| 187 return (conn, info) | 190 return (conn, info) |
| 191 | |
| 188 | 192 |
| 189 class RoundupRequestHandler(http_.server.BaseHTTPRequestHandler): | 193 class RoundupRequestHandler(http_.server.BaseHTTPRequestHandler): |
| 190 TRACKER_HOMES = {} | 194 TRACKER_HOMES = {} |
| 191 TRACKERS = None | 195 TRACKERS = None |
| 192 LOG_IPADDRESS = 1 | 196 LOG_IPADDRESS = 1 |
| 223 try: | 227 try: |
| 224 self.inner_run_cgi() | 228 self.inner_run_cgi() |
| 225 except client.NotFound: | 229 except client.NotFound: |
| 226 self.send_error(404, self.path) | 230 self.send_error(404, self.path) |
| 227 except client.Unauthorised as message: | 231 except client.Unauthorised as message: |
| 228 self.send_error(403, '%s (%s)'%(self.path, message)) | 232 self.send_error(403, '%s (%s)' % (self.path, message)) |
| 229 except: | 233 except: |
| 230 exc, val, tb = sys.exc_info() | 234 exc, val, tb = sys.exc_info() |
| 231 if hasattr(socket, 'timeout') and isinstance(val, socket.timeout): | 235 if hasattr(socket, 'timeout') and isinstance(val, socket.timeout): |
| 232 self.log_error('timeout') | 236 self.log_error('timeout') |
| 233 else: | 237 else: |
| 250 else: | 254 else: |
| 251 # user feedback | 255 # user feedback |
| 252 self.wfile.write(s2b(cgitb.breaker())) | 256 self.wfile.write(s2b(cgitb.breaker())) |
| 253 ts = time.ctime() | 257 ts = time.ctime() |
| 254 self.wfile.write(s2b('''<p>%s: An error occurred. Please check | 258 self.wfile.write(s2b('''<p>%s: An error occurred. Please check |
| 255 the server log for more information.</p>'''%ts)) | 259 the server log for more information.</p>''' % ts)) |
| 256 # out to the logfile | 260 # out to the logfile |
| 257 print('EXCEPTION AT', ts) | 261 print('EXCEPTION AT', ts) |
| 258 traceback.print_exc() | 262 traceback.print_exc() |
| 259 | 263 |
| 260 do_GET = do_POST = do_HEAD = do_PUT = do_DELETE = do_PATCH = do_OPTIONS = run_cgi | 264 do_GET = do_POST = do_HEAD = do_PUT = do_DELETE = \ |
| 265 do_PATCH = do_OPTIONS = run_cgi | |
| 261 | 266 |
| 262 def index(self): | 267 def index(self): |
| 263 ''' Print up an index of the available trackers | 268 ''' Print up an index of the available trackers |
| 264 ''' | 269 ''' |
| 265 keys = list(self.TRACKER_HOMES.keys()) | 270 keys = list(self.TRACKER_HOMES.keys()) |
| 276 | 281 |
| 277 if self.CONFIG and self.CONFIG['TEMPLATE']: | 282 if self.CONFIG and self.CONFIG['TEMPLATE']: |
| 278 template = open(self.CONFIG['TEMPLATE']).read() | 283 template = open(self.CONFIG['TEMPLATE']).read() |
| 279 pt = PageTemplate() | 284 pt = PageTemplate() |
| 280 pt.write(template) | 285 pt.write(template) |
| 281 extra = { 'trackers': self.TRACKERS, | 286 extra = {'trackers': self.TRACKERS, |
| 282 'nothing' : None, | 287 'nothing': None, |
| 283 'true' : 1, | 288 'true': 1, |
| 284 'false' : 0, | 289 'false': 0, |
| 285 } | 290 } |
| 286 w(s2b(pt.pt_render(extra_context=extra))) | 291 w(s2b(pt.pt_render(extra_context=extra))) |
| 287 else: | 292 else: |
| 288 w(s2b(_('<html><head><title>Roundup trackers index</title></head>\n' | 293 w(s2b(_('<html><head><title>Roundup trackers index</title></head>\n' |
| 289 '<body><h1>Roundup trackers index</h1><ol>\n'))) | 294 '<body><h1>Roundup trackers index</h1><ol>\n'))) |
| 290 keys.sort() | 295 keys.sort() |
| 291 for tracker in keys: | 296 for tracker in keys: |
| 292 w(s2b('<li><a href="%(tracker_url)s/index">%(tracker_name)s</a>\n'%{ | 297 w(s2b('<li><a href="%(tracker_url)s/index">%(tracker_name)s</a>\n' % { |
| 293 'tracker_url': urllib_.quote(tracker), | 298 'tracker_url': urllib_.quote(tracker), |
| 294 'tracker_name': html_escape(tracker)})) | 299 'tracker_name': html_escape(tracker)})) |
| 295 w(b'</ol></body></html>') | 300 w(b'</ol></body></html>') |
| 296 | 301 |
| 297 def inner_run_cgi(self): | 302 def inner_run_cgi(self): |
| 309 favicon_filepath = os.path.abspath(self.CONFIG['FAVICON']) | 314 favicon_filepath = os.path.abspath(self.CONFIG['FAVICON']) |
| 310 | 315 |
| 311 if os.access(favicon_filepath, os.R_OK): | 316 if os.access(favicon_filepath, os.R_OK): |
| 312 favicon_fileobj = open(favicon_filepath, 'rb') | 317 favicon_fileobj = open(favicon_filepath, 'rb') |
| 313 | 318 |
| 314 | |
| 315 if favicon_fileobj is None: | 319 if favicon_fileobj is None: |
| 316 favicon_fileobj = io.BytesIO(favico) | 320 favicon_fileobj = io.BytesIO(favico) |
| 317 | 321 |
| 318 self.send_response(200) | 322 self.send_response(200) |
| 319 self.send_header('Content-Type', 'image/x-icon') | 323 self.send_header('Content-Type', 'image/x-icon') |
| 320 self.end_headers() | 324 self.end_headers() |
| 321 | 325 |
| 322 # this bufsize is completely arbitrary, I picked 4K because it sounded good. | 326 # this bufsize is completely arbitrary, I picked 4K because |
| 323 # if someone knows of a better buffer size, feel free to plug it in. | 327 # it sounded good. if someone knows of a better buffer size, |
| 328 # feel free to plug it in. | |
| 324 bufsize = 4 * 1024 | 329 bufsize = 4 * 1024 |
| 325 Processing = True | 330 Processing = True |
| 326 while Processing: | 331 while Processing: |
| 327 data = favicon_fileobj.read(bufsize) | 332 data = favicon_fileobj.read(bufsize) |
| 328 if len(data) > 0: | 333 if len(data) > 0: |
| 354 # handle missing trailing '/' | 359 # handle missing trailing '/' |
| 355 if len(l_path) == 2: | 360 if len(l_path) == 2: |
| 356 self.send_response(301) | 361 self.send_response(301) |
| 357 # redirect - XXX https?? | 362 # redirect - XXX https?? |
| 358 protocol = 'http' | 363 protocol = 'http' |
| 359 url = '%s://%s%s/'%(protocol, self.headers['host'], rest) | 364 url = '%s://%s%s/' % (protocol, self.headers['host'], rest) |
| 360 if query: | 365 if query: |
| 361 url += '?' + query | 366 url += '?' + query |
| 362 self.send_header('Location', url) | 367 self.send_header('Location', url) |
| 363 self.end_headers() | 368 self.end_headers() |
| 364 self.wfile.write(b'Moved Permanently') | 369 self.wfile.write(b'Moved Permanently') |
| 365 return | 370 return |
| 366 | 371 |
| 383 elif self.headers.typeheader is None: | 388 elif self.headers.typeheader is None: |
| 384 # Python 2. | 389 # Python 2. |
| 385 content_type = self.headers.type | 390 content_type = self.headers.type |
| 386 else: | 391 else: |
| 387 # Python 2. | 392 # Python 2. |
| 388 content_type = self.headers.typeheader | 393 content_type = self.headers.typeheader |
| 389 if content_type: | 394 if content_type: |
| 390 env['CONTENT_TYPE'] = content_type | 395 env['CONTENT_TYPE'] = content_type |
| 391 length = self.headers.get('content-length') | 396 length = self.headers.get('content-length') |
| 392 if length: | 397 if length: |
| 393 env['CONTENT_LENGTH'] = length | 398 env['CONTENT_LENGTH'] = length |
| 403 env['HTTP_AUTHORIZATION'] = self.headers.get('authorization') | 408 env['HTTP_AUTHORIZATION'] = self.headers.get('authorization') |
| 404 env['SCRIPT_NAME'] = '' | 409 env['SCRIPT_NAME'] = '' |
| 405 env['SERVER_NAME'] = self.server.server_name | 410 env['SERVER_NAME'] = self.server.server_name |
| 406 env['SERVER_PORT'] = str(self.server.server_port) | 411 env['SERVER_PORT'] = str(self.server.server_port) |
| 407 try: | 412 try: |
| 408 env['HTTP_HOST'] = self.headers ['host'] | 413 env['HTTP_HOST'] = self.headers['host'] |
| 409 except KeyError: | 414 except KeyError: |
| 410 env['HTTP_HOST'] = '' | 415 env['HTTP_HOST'] = '' |
| 411 # https://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10 | 416 # https://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10 |
| 412 # headers. | 417 # headers. |
| 413 xfh = self.headers.get('X-Forwarded-Host', None) | 418 xfh = self.headers.get('X-Forwarded-Host', None) |
| 474 logger = logging.getLogger('roundup.http') | 479 logger = logging.getLogger('roundup.http') |
| 475 | 480 |
| 476 logger.info("%s - - [%s] %s" % | 481 logger.info("%s - - [%s] %s" % |
| 477 (self.client_address[0], | 482 (self.client_address[0], |
| 478 self.log_date_time_string(), | 483 self.log_date_time_string(), |
| 479 format%args)) | 484 format % args)) |
| 480 else: | 485 else: |
| 481 try: | 486 try: |
| 482 http_.server.BaseHTTPRequestHandler.log_message(self, | 487 http_.server.BaseHTTPRequestHandler.log_message(self, |
| 483 format, *args) | 488 format, *args) |
| 484 except IOError: | 489 except IOError: |
| 489 self.send_response(response) | 494 self.send_response(response) |
| 490 for key, value in headers: | 495 for key, value in headers: |
| 491 self.send_header(key, value) | 496 self.send_header(key, value) |
| 492 self.end_headers() | 497 self.end_headers() |
| 493 | 498 |
| 499 | |
| 494 def error(): | 500 def error(): |
| 495 exc_type, exc_value = sys.exc_info()[:2] | 501 exc_type, exc_value = sys.exc_info()[:2] |
| 496 return _('Error: %s: %s' % (exc_type, exc_value)) | 502 return _('Error: %s: %s' % (exc_type, exc_value)) |
| 503 | |
| 497 | 504 |
| 498 def setgid(group): | 505 def setgid(group): |
| 499 if group is None: | 506 if group is None: |
| 500 return | 507 return |
| 501 if not hasattr(os, 'setgid'): | 508 if not hasattr(os, 'setgid'): |
| 516 except ValueError: | 523 except ValueError: |
| 517 gid = grp.getgrnam(group)[2] | 524 gid = grp.getgrnam(group)[2] |
| 518 else: | 525 else: |
| 519 grp.getgrgid(gid) | 526 grp.getgrgid(gid) |
| 520 except KeyError: | 527 except KeyError: |
| 521 raise ValueError(_("Group %(group)s doesn't exist")%locals()) | 528 raise ValueError(_("Group %(group)s doesn't exist") % locals()) |
| 522 os.setgid(gid) | 529 os.setgid(gid) |
| 530 | |
| 523 | 531 |
| 524 def setuid(user): | 532 def setuid(user): |
| 525 if not hasattr(os, 'getuid'): | 533 if not hasattr(os, 'getuid'): |
| 526 return | 534 return |
| 527 | 535 |
| 545 except ValueError: | 553 except ValueError: |
| 546 uid = pwd.getpwnam(user)[2] | 554 uid = pwd.getpwnam(user)[2] |
| 547 else: | 555 else: |
| 548 pwd.getpwuid(uid) | 556 pwd.getpwuid(uid) |
| 549 except KeyError: | 557 except KeyError: |
| 550 raise ValueError(_("User %(user)s doesn't exist")%locals()) | 558 raise ValueError(_("User %(user)s doesn't exist") % locals()) |
| 551 os.setuid(uid) | 559 os.setuid(uid) |
| 560 | |
| 552 | 561 |
| 553 class TrackerHomeOption(configuration.FilePathOption): | 562 class TrackerHomeOption(configuration.FilePathOption): |
| 554 | 563 |
| 555 # Tracker homes do not need any description strings | 564 # Tracker homes do not need any description strings |
| 556 def format(self): | 565 def format(self): |
| 557 return "%(name)s = %(value)s\n" % { | 566 return "%(name)s = %(value)s\n" % { |
| 558 "name": self.setting, | 567 "name": self.setting, |
| 559 "value": self.value2str(self._value), | 568 "value": self.value2str(self._value), |
| 560 } | 569 } |
| 570 | |
| 561 | 571 |
| 562 class ServerConfig(configuration.Config): | 572 class ServerConfig(configuration.Config): |
| 563 | 573 |
| 564 SETTINGS = ( | 574 SETTINGS = ( |
| 565 ("main", ( | 575 ("main", ( |
| 657 for name in config.options("trackers"): | 667 for name in config.options("trackers"): |
| 658 if name not in defaults: | 668 if name not in defaults: |
| 659 self.add_option(TrackerHomeOption(self, "trackers", name)) | 669 self.add_option(TrackerHomeOption(self, "trackers", name)) |
| 660 | 670 |
| 661 def getopt(self, args, short_options="", long_options=(), | 671 def getopt(self, args, short_options="", long_options=(), |
| 662 config_load_options=("C", "config"), **options | 672 config_load_options=("C", "config"), **options): |
| 663 ): | |
| 664 options.update(self.OPTIONS) | 673 options.update(self.OPTIONS) |
| 665 return configuration.Config.getopt(self, args, | 674 return configuration.Config.getopt(self, args, |
| 666 short_options, long_options, config_load_options, **options) | 675 short_options, long_options, config_load_options, **options) |
| 667 | 676 |
| 668 def _get_name(self): | 677 def _get_name(self): |
| 691 tracker_homes = self.trackers() | 700 tracker_homes = self.trackers() |
| 692 if self["MULTIPROCESS"] == "debug": | 701 if self["MULTIPROCESS"] == "debug": |
| 693 trackers = None | 702 trackers = None |
| 694 else: | 703 else: |
| 695 trackers = dict([(name, roundup.instance.open(home, optimize=1)) | 704 trackers = dict([(name, roundup.instance.open(home, optimize=1)) |
| 696 for (name, home) in tracker_homes]) | 705 for (name, home) in tracker_homes]) |
| 697 | 706 |
| 698 # build customized request handler class | 707 # build customized request handler class |
| 699 class RequestHandler(RoundupRequestHandler): | 708 class RequestHandler(RoundupRequestHandler): |
| 700 LOG_IPADDRESS = not self["LOG_HOSTNAMES"] | 709 LOG_IPADDRESS = not self["LOG_HOSTNAMES"] |
| 701 TRACKER_HOMES = dict(tracker_homes) | 710 TRACKER_HOMES = dict(tracker_homes) |
| 728 base_server = http_.server.HTTPServer | 737 base_server = http_.server.HTTPServer |
| 729 | 738 |
| 730 # obtain request server class | 739 # obtain request server class |
| 731 if self["MULTIPROCESS"] not in MULTIPROCESS_TYPES: | 740 if self["MULTIPROCESS"] not in MULTIPROCESS_TYPES: |
| 732 print(_("Multiprocess mode \"%s\" is not available, " | 741 print(_("Multiprocess mode \"%s\" is not available, " |
| 733 "switching to single-process") % self["MULTIPROCESS"]) | 742 "switching to single-process") % self["MULTIPROCESS"]) |
| 734 self["MULTIPROCESS"] = "none" | 743 self["MULTIPROCESS"] = "none" |
| 735 server_class = base_server | 744 server_class = base_server |
| 736 elif self["MULTIPROCESS"] == "fork": | 745 elif self["MULTIPROCESS"] == "fork": |
| 737 class ForkingServer(socketserver.ForkingMixIn, | 746 class ForkingServer(socketserver.ForkingMixIn, |
| 738 base_server): | 747 base_server): |
| 739 pass | 748 pass |
| 740 server_class = ForkingServer | 749 server_class = ForkingServer |
| 741 elif self["MULTIPROCESS"] == "thread": | 750 elif self["MULTIPROCESS"] == "thread": |
| 742 class ThreadingServer(socketserver.ThreadingMixIn, | 751 class ThreadingServer(socketserver.ThreadingMixIn, |
| 743 base_server): | 752 base_server): |
| 744 pass | 753 pass |
| 745 server_class = ThreadingServer | 754 server_class = ThreadingServer |
| 746 else: | 755 else: |
| 747 server_class = base_server | 756 server_class = base_server |
| 748 | 757 |
| 749 # obtain server before changing user id - allows to | 758 # obtain server before changing user id - allows to |
| 754 if self["SSL"]: | 763 if self["SSL"]: |
| 755 kwargs['ssl_pem'] = self["PEM"] | 764 kwargs['ssl_pem'] = self["PEM"] |
| 756 httpd = server_class(*args, **kwargs) | 765 httpd = server_class(*args, **kwargs) |
| 757 except socket.error as e: | 766 except socket.error as e: |
| 758 if e.args[0] == errno.EADDRINUSE: | 767 if e.args[0] == errno.EADDRINUSE: |
| 759 raise socket.error(_("Unable to bind to port %s, port already in use.") \ | 768 raise socket.error(_("Unable to bind to port %s, " |
| 760 % self["PORT"]) | 769 "port already in use.") % self["PORT"]) |
| 761 raise | 770 raise |
| 762 # change user and/or group | 771 # change user and/or group |
| 763 setgid(self["GROUP"]) | 772 setgid(self["GROUP"]) |
| 764 setuid(self["USER"]) | 773 setuid(self["USER"]) |
| 765 # return the server | 774 # return the server |
| 766 return httpd | 775 return httpd |
| 767 | 776 |
| 777 | |
| 768 try: | 778 try: |
| 769 import win32serviceutil | 779 import win32serviceutil |
| 770 except: | 780 except ImportError: |
| 771 RoundupService = None | 781 RoundupService = None |
| 772 else: | 782 else: |
| 773 | 783 |
| 774 # allow the win32 | 784 # allow the win32 |
| 775 import win32service | 785 import win32service |
| 790 self.ReportServiceStatus(win32service.SERVICE_START_PENDING) | 800 self.ReportServiceStatus(win32service.SERVICE_START_PENDING) |
| 791 config = ServerConfig() | 801 config = ServerConfig() |
| 792 (optlist, args) = config.getopt(sys.argv[1:]) | 802 (optlist, args) = config.getopt(sys.argv[1:]) |
| 793 if not config["LOGFILE"]: | 803 if not config["LOGFILE"]: |
| 794 servicemanager.LogMsg(servicemanager.EVENTLOG_ERROR_TYPE, | 804 servicemanager.LogMsg(servicemanager.EVENTLOG_ERROR_TYPE, |
| 795 servicemanager.PYS_SERVICE_STOPPED, | 805 servicemanager.PYS_SERVICE_STOPPED, |
| 796 (self._svc_display_name_, "\r\nMissing logfile option")) | 806 (self._svc_display_name_, "\r\nMissing logfile option")) |
| 797 self.ReportServiceStatus(win32service.SERVICE_STOPPED) | 807 self.ReportServiceStatus(win32service.SERVICE_STOPPED) |
| 798 return | 808 return |
| 799 config.set_logging() | 809 config.set_logging() |
| 800 self.server = config.get_server() | 810 self.server = config.get_server() |
| 801 self.running = 1 | 811 self.running = 1 |
| 802 self.ReportServiceStatus(win32service.SERVICE_RUNNING) | 812 self.ReportServiceStatus(win32service.SERVICE_RUNNING) |
| 803 servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, | 813 servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, |
| 804 servicemanager.PYS_SERVICE_STARTED, (self._svc_display_name_, | 814 servicemanager.PYS_SERVICE_STARTED, |
| 805 " at %s:%s" % (config["HOST"], config["PORT"]))) | 815 (self._svc_display_name_, |
| 816 " at %s:%s" % (config["HOST"], | |
| 817 config["PORT"]))) | |
| 806 while self.running: | 818 while self.running: |
| 807 self.server.handle_request() | 819 self.server.handle_request() |
| 808 servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, | 820 servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, |
| 809 servicemanager.PYS_SERVICE_STOPPED, | 821 servicemanager.PYS_SERVICE_STOPPED, |
| 810 (self._svc_display_name_, "")) | 822 (self._svc_display_name_, "")) |
| 811 self.ReportServiceStatus(win32service.SERVICE_STOPPED) | 823 self.ReportServiceStatus(win32service.SERVICE_STOPPED) |
| 812 | 824 |
| 813 def SvcStop(self): | 825 def SvcStop(self): |
| 814 self.running = 0 | 826 self.running = 0 |
| 815 # make dummy connection to self to terminate blocking accept() | 827 # make dummy connection to self to terminate blocking accept() |
| 818 addr = ("127.0.0.1", addr[1]) | 830 addr = ("127.0.0.1", addr[1]) |
| 819 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | 831 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 820 sock.connect(addr) | 832 sock.connect(addr) |
| 821 sock.close() | 833 sock.close() |
| 822 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) | 834 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) |
| 835 | |
| 823 | 836 |
| 824 def usage(message=''): | 837 def usage(message=''): |
| 825 if RoundupService: | 838 if RoundupService: |
| 826 os_part = \ | 839 os_part = \ |
| 827 ""''' -c <Command> Windows Service options. | 840 ""''' -c <Command> Windows Service options. |
| 909 if pid: | 922 if pid: |
| 910 pidfile = open(pidfile, 'w') | 923 pidfile = open(pidfile, 'w') |
| 911 pidfile.write(str(pid)) | 924 pidfile.write(str(pid)) |
| 912 pidfile.close() | 925 pidfile.close() |
| 913 | 926 |
| 927 | |
| 914 def daemonize(pidfile): | 928 def daemonize(pidfile): |
| 915 ''' Turn this process into a daemon. | 929 ''' Turn this process into a daemon. |
| 916 - make sure the sys.std(in|out|err) are completely cut off | 930 - make sure the sys.std(in|out|err) are completely cut off |
| 917 - make our parent PID 1 | 931 - make our parent PID 1 |
| 918 | 932 |
| 943 devnull = os.open('/dev/null', 0) | 957 devnull = os.open('/dev/null', 0) |
| 944 os.dup2(devnull, 0) | 958 os.dup2(devnull, 0) |
| 945 os.dup2(devnull, 1) | 959 os.dup2(devnull, 1) |
| 946 os.dup2(devnull, 2) | 960 os.dup2(devnull, 2) |
| 947 | 961 |
| 962 | |
| 948 undefined = [] | 963 undefined = [] |
| 964 | |
| 965 | |
| 949 def run(port=undefined, success_message=None): | 966 def run(port=undefined, success_message=None): |
| 950 ''' Script entry point - handle args and figure out what to to. | 967 ''' Script entry point - handle args and figure out what to to. |
| 951 ''' | 968 ''' |
| 952 config = ServerConfig() | 969 config = ServerConfig() |
| 953 # additional options | 970 # additional options |
| 954 short_options = "hvSc" | 971 short_options = "hvSc" |
| 955 try: | 972 try: |
| 956 (optlist, args) = config.getopt(sys.argv[1:], | 973 (optlist, args) = config.getopt(sys.argv[1:], |
| 957 short_options, ("help", "version", "save-config",)) | 974 short_options, |
| 975 ("help", "version", "save-config",)) | |
| 958 except (getopt.GetoptError, configuration.ConfigurationError) as e: | 976 except (getopt.GetoptError, configuration.ConfigurationError) as e: |
| 959 usage(str(e)) | 977 usage(str(e)) |
| 960 return | 978 return |
| 961 | 979 |
| 962 # if running in windows service mode, don't do any other stuff | 980 # if running in windows service mode, don't do any other stuff |
| 993 elif opt != "-c": | 1011 elif opt != "-c": |
| 994 svc_args.extend(opt) | 1012 svc_args.extend(opt) |
| 995 RoundupService._exe_args_ = " ".join(svc_args) | 1013 RoundupService._exe_args_ = " ".join(svc_args) |
| 996 # pass the control to serviceutil | 1014 # pass the control to serviceutil |
| 997 win32serviceutil.HandleCommandLine(RoundupService, | 1015 win32serviceutil.HandleCommandLine(RoundupService, |
| 998 argv=sys.argv[:1] + args) | 1016 argv=sys.argv[:1] + args) |
| 999 return | 1017 return |
| 1000 | 1018 |
| 1001 # add tracker names from command line. | 1019 # add tracker names from command line. |
| 1002 # this is done early to let '--save-config' handle the trackers. | 1020 # this is done early to let '--save-config' handle the trackers. |
| 1003 if args: | 1021 if args: |
| 1009 config.add_option(TrackerHomeOption(config, "trackers", name)) | 1027 config.add_option(TrackerHomeOption(config, "trackers", name)) |
| 1010 config["TRACKERS_" + name.upper()] = home | 1028 config["TRACKERS_" + name.upper()] = home |
| 1011 | 1029 |
| 1012 # handle remaining options | 1030 # handle remaining options |
| 1013 if optlist: | 1031 if optlist: |
| 1014 for (opt, arg) in optlist: | 1032 for (opt, _arg) in optlist: |
| 1015 if opt in ("-h", "--help"): | 1033 if opt in ("-h", "--help"): |
| 1016 usage() | 1034 usage() |
| 1017 elif opt in ("-v", "--version"): | 1035 elif opt in ("-v", "--version"): |
| 1018 print('%s (python %s)' % (roundup_version, | 1036 print('%s (python %s)' % (roundup_version, |
| 1019 sys.version.split()[0])) | 1037 sys.version.split()[0])) |
| 1020 elif opt in ("-S", "--save-config"): | 1038 elif opt in ("-S", "--save-config"): |
| 1021 config.save() | 1039 config.save() |
| 1022 print(_("Configuration saved to %s") % config.filepath) | 1040 print(_("Configuration saved to %s") % config.filepath) |
| 1023 # any of the above options prevent server from running | 1041 # any of the above options prevent server from running |
| 1024 return | 1042 return |
| 1036 | 1054 |
| 1037 # fork the server from our parent if a pidfile is specified | 1055 # fork the server from our parent if a pidfile is specified |
| 1038 if config["PIDFILE"]: | 1056 if config["PIDFILE"]: |
| 1039 if not hasattr(os, 'fork'): | 1057 if not hasattr(os, 'fork'): |
| 1040 print(_("Sorry, you can't run the server as a daemon" | 1058 print(_("Sorry, you can't run the server as a daemon" |
| 1041 " on this Operating System")) | 1059 " on this Operating System")) |
| 1042 sys.exit(0) | 1060 sys.exit(0) |
| 1043 else: | 1061 else: |
| 1044 if config['NODAEMON']: | 1062 if config['NODAEMON']: |
| 1045 writepidfile(config["PIDFILE"]) | 1063 writepidfile(config["PIDFILE"]) |
| 1046 else: | 1064 else: |
| 1063 try: | 1081 try: |
| 1064 httpd.serve_forever() | 1082 httpd.serve_forever() |
| 1065 except KeyboardInterrupt: | 1083 except KeyboardInterrupt: |
| 1066 print('Keyboard Interrupt: exiting') | 1084 print('Keyboard Interrupt: exiting') |
| 1067 | 1085 |
| 1086 | |
| 1068 if __name__ == '__main__': | 1087 if __name__ == '__main__': |
| 1069 run() | 1088 run() |
| 1070 | 1089 |
| 1071 # vim: sts=4 sw=4 et si | 1090 # vim: sts=4 sw=4 et si |
