diff roundup/scripts/roundup_server.py @ 2771:d385f6c1d4ed

let config handle command line options. support long options (names are same as in config file settings) and config file creation from command line. WARNING! config layout is changed: - main options are in 'main' section (was: 'server' section); - 'log_ip' changed to 'log_hostnames' with reverse meaning to conform to '-N' command line option. - trackers are all defined in section 'trackers', with option name being tracker name (was: separate sections per tracker).
author Alexander Smishlajev <a1s@users.sourceforge.net>
date Sun, 17 Oct 2004 17:54:48 +0000
parents ea18be87d68d
children 52460ff89e10
line wrap: on
line diff
--- a/roundup/scripts/roundup_server.py	Sun Oct 17 17:35:32 2004 +0000
+++ b/roundup/scripts/roundup_server.py	Sun Oct 17 17:54:48 2004 +0000
@@ -17,12 +17,12 @@
 
 """Command-line script that runs a server over roundup.cgi.client.
 
-$Id: roundup_server.py,v 1.62 2004-09-21 09:29:18 a1s Exp $
+$Id: roundup_server.py,v 1.63 2004-10-17 17:54:48 a1s Exp $
 """
 __docformat__ = 'restructuredtext'
 
 # python version check
-from roundup import version_check
+from roundup import configuration, version_check
 from roundup import __version__ as roundup_version
 
 import sys, os, urllib, StringIO, traceback, cgi, binascii, getopt, imp
@@ -369,7 +369,7 @@
    pairs on the command-line. Make sure the name part doesn't include
    any url-unsafe characters like spaces, as these confuse IE.
 ''')%locals()
-    sys.exit(0)
+    #sys.exit(0)
 
 
 def daemonize(pidfile):
@@ -461,6 +461,76 @@
         raise ValueError, _("User %(user)s doesn't exist")%locals()
     os.setuid(uid)
 
+class TrackerHomeOption(configuration.FilePathOption):
+    # Tracker homes do not need description strings
+    # attached to FilePathOption.  Description appears once
+    # before the trackers section.
+    class_description = ""
+
+class ServerConfig(configuration.Config):
+
+    SETTINGS = (
+            ("main", (
+            (configuration.Option, "host", "",
+                "Host name of the Roundup web server instance.\n"
+                "If empty, listen on all network interfaces."),
+            (configuration.IntegerNumberOption, "port", DEFAULT_PORT,
+                "Port to listen on."),
+            (configuration.NullableOption, "user", "",
+                "User ID as which the server will answer requests.\n"
+                "In order to use this option, "
+                "the server must be run initially as root.\n"
+                "Availability: Unix."),
+            (configuration.NullableOption, "group", "",
+                "Group ID as which the server will answer requests.\n"
+                "In order to use this option, "
+                "the server must be run initially as root.\n"
+                "Availability: Unix."),
+            (configuration.BooleanOption, "log_hostnames", "no",
+                "Log client machine names instead of IP addresses "
+                "(much slower)"),
+            (configuration.NullableFilePathOption, "pidfile", "",
+                "File to which the server records "
+                "the process id of the daemon.\n"
+                "If this option is not set, "
+                "the server will run in foreground\n"),
+            (configuration.NullableFilePathOption, "logfile", "",
+                "Log file path.  If unset, log to stderr."),
+        )),
+        ("trackers", (), "Roundup trackers to serve.\n"
+            "Each option in this section defines single Roundup tracker.\n"
+            "Option name identifies the tracker and will appear in the URL.\n"
+            "Option value is tracker home directory path.\n"
+            "The path may be either absolute or relative\n"
+            "to the directory containig this config file."),
+    )
+
+    def __init__(self, config_file=None):
+        configuration.Config.__init__(self, config_file, self.SETTINGS)
+
+    def _adjust_options(self, config):
+        """Add options for tracker homes"""
+        # return early if there are no tracker definitions.
+        # trackers must be specified on the command line.
+        if not config.has_section("trackers"):
+            return
+        # config defaults appear in all sections.
+        # filter them out.
+        defaults = config.defaults().keys()
+        for name in config.options("trackers"):
+            if name not in defaults:
+                self.add_option(TrackerHomeOption(self, "trackers", name))
+
+    def _get_name(self):
+        return "Roundup server"
+
+    def trackers(self):
+        """Return tracker definitions as a list of (name, home) pairs"""
+        trackers = []
+        for option in self._get_section_options("trackers"):
+            trackers.append((option, self["TRACKERS_" + option.upper()]))
+        return trackers
+
 undefined = []
 def run(port=undefined, success_message=None):
     ''' Script entry point - handle args and figure out what to to.
@@ -470,134 +540,98 @@
     if hasattr(socket, 'setdefaulttimeout'):
         socket.setdefaulttimeout(60)
 
-    hostname = pidfile = logfile = user = group = svc_args = log_ip = undefined
-    config = None
+    config = ServerConfig()
 
+    options = "hvS"
+    if RoundupService:
+        options += 'c'
     try:
-        # handle the command-line args
-        options = 'n:p:g:u:d:l:C:hNv'
-        if RoundupService:
-            options += 'c'
-
-        try:
-            optlist, args = getopt.getopt(sys.argv[1:], options)
-        except getopt.GetoptError, e:
-            usage(str(e))
+        (optlist, args) = config.getopt(sys.argv[1:],
+            options, ("help", "version", "save-config",),
+            host="n:", port="p:", group="g:", user="u:",
+            logfile="l:", pidfile="d:", log_hostnames="N")
+    except (getopt.GetoptError), e: #, configuration.ConfigurationError), e:
+        usage(str(e))
+        return
 
-        for (opt, arg) in optlist:
-            if opt == '-n': hostname = arg
-            elif opt == '-v':
-                print '%s (python %s)'%(roundup_version, sys.version.split()[0])
-                return
-            elif opt == '-p': port = int(arg)
-            elif opt == '-u': user = arg
-            elif opt == '-g': group = arg
-            elif opt == '-d': pidfile = os.path.abspath(arg)
-            elif opt == '-l': logfile = os.path.abspath(arg)
-            elif opt == '-h': usage()
-            elif opt == '-N': log_ip = 0
-            elif opt == '-c': svc_args = [opt] + args; args = None
-            elif opt == '-C': config = arg
+    # if running in windows service mode, don't do any other stuff
+    if ("-c", "") in optlist:
+        RoundupService.address = (config.HOST, config.PORT)
+        # XXX why the 1st argument to the service is "-c"
+        #   instead of the script name???
+        return win32serviceutil.HandleCommandLine(RoundupService,
+            argv=["-c"] + args)
 
-        if svc_args and len(optlist) > 1:
-            raise ValueError, _("windows service option must be the only one")
-
-        if pidfile and not logfile:
-            raise ValueError, _("logfile *must* be specified if pidfile is")
+    # add tracker names from command line.
+    # this is done early to let '--save-config' handle the trackers.
+    if args:
+        for arg in args:
+            try:
+                name, home = arg.split('=')
+            except ValueError:
+                raise ValueError, _("Instances must be name=home")
+            config.add_option(
+                configuration.FilePathOption(config, "trackers", name))
+            config["TRACKERS_" + name.upper()] = home
 
-        # handle the config file
-        if config:
-            cfg = ConfigParser.ConfigParser()
-            cfg.read(filename)
-            if port is undefined and cfg.has_option('server', 'port'):
-                port = cfg.get('server', 'port')
-            if user is undefined and cfg.has_option('server', 'user'):
-                user = cfg.get('server', 'user')
-            if group is undefined and cfg.has_option('server', 'group'):
-                group = cfg.get('server', 'group')
-            if log_ip is undefined and cfg.has_option('server', 'log_ip'):
-                RoundupRequestHandler.LOG_IPADDRESS = cfg.getboolean('server',
-                    'log_ip')
-            if pidfile is undefined and cfg.has_option('server', 'pidfile'):
-                pidfile = cfg.get('server', 'pidfile')
-            if logfile is undefined and cfg.has_option('server', 'logfile'):
-                logfile = cfg.get('server', 'logfile')
-            homes = RoundupRequestHandler.TRACKER_HOMES
-            for section in cfg.sections():
-                if section == 'server':
-                    continue
-                homes[section] = cfg.get(section, 'home')
+    # handle remaining options
+    if optlist:
+        for (opt, arg) in optlist:
+            if opt in ("-h", "--help"):
+                usage()
+            elif opt in ("-v", "--version"):
+                print '%s (python %s)' % (roundup_version,
+                    sys.version.split()[0])
+            elif opt in ("-S", "--save-config"):
+                config.save()
+                print _("Configuration saved to %s") % config.filepath
+        # any of the above options prevent server from running
+        return
+
+    RoundupRequestHandler.LOG_IPADDRESS = not config.LOG_HOSTNAMES
 
-        # defaults
-        if hostname is undefined:
-            hostname = ''
-        if port is undefined:
-            port = DEFAULT_PORT
-        if group is undefined:
-            group = None
-        if user is undefined:
-            user = None
-        if svc_args is undefined:
-            svc_args = None
+    # obtain server before changing user id - allows to use port <
+    # 1024 if started as root
+    try:
+        httpd = server_class((config.HOST, config.PORT), RoundupRequestHandler)
+    except socket.error, e:
+        if e[0] == errno.EADDRINUSE:
+            raise socket.error, \
+                _("Unable to bind to port %s, port already in use.") \
+                % config.PORT
+        raise
 
-        # obtain server before changing user id - allows to use port <
-        # 1024 if started as root
-        address = (hostname, port)
-        try:
-            httpd = server_class(address, RoundupRequestHandler)
-        except socket.error, e:
-            if e[0] == errno.EADDRINUSE:
-                raise socket.error, \
-                      _("Unable to bind to port %s, port already in use."%port)
-            raise
-
-        # change user and/or group
-        setgid(group)
-        setuid(user)
+    # change user and/or group
+    setgid(config.GROUP)
+    setuid(config.USER)
 
-        # handle tracker specs
-        if args:
-            for arg in args:
-                try:
-                    name, home = arg.split('=')
-                except ValueError:
-                    raise ValueError, _("Instances must be name=home")
-                home = os.path.abspath(home)
-                RoundupRequestHandler.TRACKER_HOMES[name] = home
-    except SystemExit:
-        raise
-    except ValueError:
-        usage(error())
-#    except:
-#        print error()
-#        sys.exit(1)
+    # apply tracker specs
+    for (name, home) in config.trackers():
+        home = os.path.abspath(home)
+        RoundupRequestHandler.TRACKER_HOMES[name] = home
 
     # we don't want the cgi module interpreting the command-line args ;)
     sys.argv = sys.argv[:1]
 
     # fork the server from our parent if a pidfile is specified
-    if pidfile:
+    if config.PIDFILE:
         if not hasattr(os, 'fork'):
             print _("Sorry, you can't run the server as a daemon"
                 " on this Operating System")
             sys.exit(0)
         else:
-            daemonize(pidfile)
-
-    if svc_args is not None:
-        # don't do any other stuff
-        RoundupService.address = address
-        return win32serviceutil.HandleCommandLine(RoundupService, argv=svc_args)
+            daemonize(config.PIDFILE)
 
     # redirect stdout/stderr to our logfile
-    if logfile:
+    if config.LOGFILE:
         # appending, unbuffered
-        sys.stdout = sys.stderr = open(logfile, 'a', 0)
+        sys.stdout = sys.stderr = open(config.LOGFILE, 'a', 0)
 
     if success_message:
         print success_message
     else:
-        print _('Roundup server started on %(address)s')%locals()
+        print _('Roundup server started on %(HOST)s:%(PORT)s') \
+            % config
 
     try:
         httpd.serve_forever()

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