changeset 2632:9c55f2bc5961

roundup-server now has a configuration file (-C option)
author Richard Jones <richard@users.sourceforge.net>
date Tue, 27 Jul 2004 00:45:49 +0000
parents 2bbcfc80ba5b
children a9e1fff1e793
files CHANGES.txt doc/admin_guide.txt doc/roundup-server.1 doc/roundup-server.ini.example roundup/scripts/roundup_server.py
diffstat 5 files changed, 182 insertions(+), 102 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Mon Jul 26 09:29:22 2004 +0000
+++ b/CHANGES.txt	Tue Jul 27 00:45:49 2004 +0000
@@ -15,6 +15,7 @@
   from Vickenty Fesunov)
 - hide "(list)" popup links when issue is only viewable
 - roundup-server options -g and -u accept both ids and names (sf bug 983769)
+- roundup-server now has a configuration file (-C option)
 - added mod_python interface (see installation.txt)
 
 
--- a/doc/admin_guide.txt	Mon Jul 26 09:29:22 2004 +0000
+++ b/doc/admin_guide.txt	Tue Jul 27 00:45:49 2004 +0000
@@ -2,7 +2,7 @@
 Administration Guide
 ====================
 
-:Version: $Revision: 1.12 $
+:Version: $Revision: 1.13 $
 
 .. contents::
 
@@ -67,6 +67,55 @@
 In both cases, if no logfile is specified then logging will simply be sent
 to sys.stderr with only logging of ERROR messages.
 
+Configuring roundup-server
+==========================
+
+The basic configuration file layout is as follows (take from the
+``roundup-server.ini.example`` file in the "doc" directory)::
+
+    [server]
+    port = 8080
+    ;hostname = 
+    ;user = 
+    ;group = 
+    ;log_ip = yes
+    ;pidfile = 
+    ;logfile = 
+
+    ; Add one of these per tracker being served
+    [tracker_url_component]
+    home = /path/to/tracker
+
+Values ";commented out" are optional. The meaning of the various options
+are as follows:
+
+**port**
+  Defines the local TCP port to listen for clients on.
+**hostname**
+  Defines the local hostname to listen for clients on. Only required if
+  "localhost" is not sufficient.
+**user** and **group**
+  Defines the Unix user and group to run the server as. Only work if the
+  server is started as root.
+**log_ip**
+  If ``yes`` then we log IP addresses against accesses. If ``no`` then we
+  log the hostname of the client. The latter can be much slower.
+**pidfile**
+  If specified, the server will fork at startup and write its new PID to
+  the file.
+**logfile**
+  Any unhandled exception messages or other output from Roundup will be
+  written to this file. It must be specified if **pidfile** is specified.
+  If per-tracker logging is specified, then very little will be written to
+  this file.
+**example_tracker** sections
+  These are used to specify which Roundup trackers are to be served up by
+  roundup-server. Each tracker has its own ``[section]`` with a single
+  ``home`` value defined pointing to the tracker's home. The section name is
+  used in the URL to select the appropriate reacker. Make sure the name part
+  doesn't include any url-unsafe characters like spaces. Stick to
+  alphanumeric characters and you'll be ok.
+
 
 Users and Security
 ==================
--- a/doc/roundup-server.1	Mon Jul 26 09:29:22 2004 +0000
+++ b/doc/roundup-server.1	Tue Jul 27 00:45:49 2004 +0000
@@ -1,21 +1,25 @@
-.TH ROUNDUP-SERVER 1 "24 January 2003"
+.TH ROUNDUP-SERVER 1 "27 July 2004"
 .SH NAME
-roundup-server \- start roundup server
+roundup-server \- start roundup web server
 .SH SYNOPSIS
 \fBroundup-server\fP [\fIoptions\fP] [\fBname=\fP\fItracker home\fP]*
 .SH OPTIONS
 .TP
+\fB-C\fP \fIfile\fP
+Use options read from the configuration file (see below).
+.TP
 \fB-n\fP \fIhostname\fP
-sets the host name
+Sets the host name.
 .TP
 \fB-p\fP \fIport\fP
-sets the port to listen on
+Sets the port to listen on.
+.TP
+\fB-d\fP \fIfile\fP
+Daemonize, and write the server's PID to the nominated file.
 .TP
 \fB-l\fP \fIfile\fP
-sets a filename to log to (instead of stdout)
-.TP
-\fB-d\fP \fIfile\fP
-daemonize, and write the server's PID to the nominated file
+Sets a filename to log to (instead of stdout). This is required if the -d
+option is used.
 .TP
 \fB-h\fP
 print help
@@ -29,6 +33,8 @@
 TRACKER_HOMES variable in the roundup-server file instead.
 Make sure the name part doesn't include any url-unsafe characters like
 spaces, as these confuse the cookie handling in browsers like IE.
+.SH CONFIGURATION FILE
+See the "admin_guide" in the Roundup "doc" directory.
 .SH AUTHOR
 This manpage was written by Bastian Kleineidam
 <calvin@debian.org> for the Debian distribution of roundup.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/roundup-server.ini.example	Tue Jul 27 00:45:49 2004 +0000
@@ -0,0 +1,16 @@
+; This is a sample configuration file for roundup-server. See the
+; admin_guide for information about its contents.
+[server]
+port = 8080
+;hostname = 
+;user = 
+;group = 
+;log_ip = yes
+;pidfile = 
+;logfile = 
+
+
+; Add one of these per tracker being served
+[tracker_url_component]
+home = /path/to/tracker
+
--- a/roundup/scripts/roundup_server.py	Mon Jul 26 09:29:22 2004 +0000
+++ b/roundup/scripts/roundup_server.py	Tue Jul 27 00:45:49 2004 +0000
@@ -17,7 +17,7 @@
 
 """Command-line script that runs a server over roundup.cgi.client.
 
-$Id: roundup_server.py,v 1.56 2004-07-20 02:07:58 richard Exp $
+$Id: roundup_server.py,v 1.57 2004-07-27 00:45:49 richard Exp $
 """
 __docformat__ = 'restructuredtext'
 
@@ -26,7 +26,7 @@
 from roundup import __version__ as roundup_version
 
 import sys, os, urllib, StringIO, traceback, cgi, binascii, getopt, imp
-import SocketServer, BaseHTTPServer, socket, errno
+import SocketServer, BaseHTTPServer, socket, errno, ConfigParser
 
 # Roundup modules of use here
 from roundup.cgi import cgitb, client
@@ -38,32 +38,6 @@
 except:
     signal = None
 
-#
-##  Configuration
-#
-
-# This indicates where the Roundup trackers live. They're given as NAME ->
-# TRACKER_HOME, where the NAME part is used in the URL to select the
-# appropriate reacker.
-# Make sure the NAME part doesn't include any url-unsafe characters like
-# spaces, as these confuse the cookie handling in browsers like IE.
-TRACKER_HOMES = {
-#    'example': '/path/to/example',
-}
-
-ROUNDUP_USER = None
-ROUNDUP_GROUP = None
-ROUNDUP_LOG_IP = 1
-HOSTNAME = ''
-PORT = 8080
-PIDFILE = None
-LOGFILE = None
-
-
-#
-##  end configuration
-#
-
 # "default" favicon.ico
 # generate by using "icotool" and tools/base64
 import zlib, base64
@@ -81,8 +55,8 @@
 '''.strip()))
 
 class RoundupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
-    TRACKER_HOMES = TRACKER_HOMES
-    ROUNDUP_USER = ROUNDUP_USER
+    TRACKER_HOMES = {}
+    LOG_IPADDRESS = 1
 
     def run_cgi(self):
         """ Execute the CGI command. Wrap an innner call in an error
@@ -216,7 +190,6 @@
         c = tracker.Client(tracker, self, env)
         c.main()
 
-    LOG_IPADDRESS = ROUNDUP_LOG_IP
     def address_string(self):
         if self.LOG_IPADDRESS:
             return self.client_address[0]
@@ -365,6 +338,7 @@
 
 Options:
  -v            prints the Roundup version number and exits
+ -C <fname>    use configuration file
  -n <name>     sets the host name of the Roundup web server instance
  -p <port>     sets the port to listen on (default: %(port)s)
  -l <fname>    log to the file indicated by fname instead of stderr/stdout
@@ -372,23 +346,27 @@
 %(os_part)s
 
 Examples:
+ roundup-server -C /opt/roundup/etc/roundup-server.ini
+
  roundup-server support=/var/spool/roundup-trackers/support
 
  roundup-server -d /var/run/roundup.pid -l /var/log/roundup.log \\
      support=/var/spool/roundup-trackers/support
 
+Configuration file format:
+   See the "admin_guide" in the Roundup "doc" directory.
+
 How to use "name=tracker home":
    These arguments set the tracker home(s) to use. The name is how the
    tracker is identified in the URL (it's the first part of the URL path).
    The tracker home is the directory that was identified when you did
    "roundup-admin init". You may specify any number of these name=home
-   pairs on the command-line. For convenience, you may edit the
-   TRACKER_HOMES variable in the roundup-server file instead.
-   Make sure the name part doesn't include any url-unsafe characters like
-   spaces, as these confuse the cookie handling in browsers like IE.
+   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)
 
+
 def daemonize(pidfile):
     ''' Turn this process into a daemon.
         - make sure the sys.std(in|out|err) are completely cut off
@@ -424,6 +402,58 @@
     os.dup2(devnull, 1)
     os.dup2(devnull, 2)
 
+def setgid(group):
+    if group is None:
+        return
+    if not hasattr(os, 'setgid'):
+        return
+
+    # if root, setgid to the running user
+    if not os.getuid():
+        print _('WARNING: ignoring "-g" argument, not root')
+        return
+
+    try:
+        import grp
+    except ImportError:
+        raise ValueError, _("Can't change groups - no grp module")
+    try:
+        try:
+            gid = int(group)
+        except ValueError:
+            gid = grp.getgrnam(group)[2]
+        else:
+            grp.getgrgid(gid)
+    except KeyError:
+        raise ValueError,_("Group %(group)s doesn't exist")%locals()
+    os.setgid(gid)
+
+def setuid(user):
+    if not hasattr(os, 'getuid'):
+        return
+
+    # People can remove this check if they're really determined
+    if user is None:
+        raise ValueError, _("Can't run as root!")
+
+    if os.getuid():
+        print _('WARNING: ignoring "-u" argument, not root')
+
+    try:
+        import pwd
+    except ImportError:
+        raise ValueError, _("Can't change users - no pwd module")
+    try:
+        try:
+            uid = int(user)
+        except ValueError:
+            uid = pwd.getpwnam(user)[2]
+        else:
+            pwd.getpwuid(uid)
+    except KeyError:
+        raise ValueError, _("User %(user)s doesn't exist")%locals()
+    os.setuid(uid)
+
 def run(port=PORT, success_message=None):
     ''' Script entry point - handle args and figure out what to to.
     '''
@@ -432,16 +462,13 @@
     if hasattr(socket, 'setdefaulttimeout'):
         socket.setdefaulttimeout(60)
 
-    hostname = HOSTNAME
-    pidfile = PIDFILE
-    logfile = LOGFILE
-    user = ROUNDUP_USER
-    group = ROUNDUP_GROUP
-    svc_args = None
+    undefined = []
+    hostname = pidfile = logfile = user = group = svc_args = log_ip = undefined
+    config = None
 
     try:
         # handle the command-line args
-        options = 'n:p:g:u:d:l:hNv'
+        options = 'n:p:g:u:d:l:C:hNv'
         if RoundupService:
             options += 'c'
 
@@ -450,8 +477,6 @@
         except getopt.GetoptError, e:
             usage(str(e))
 
-        user = ROUNDUP_USER
-        group = None
         for (opt, arg) in optlist:
             if opt == '-n': hostname = arg
             elif opt == '-v':
@@ -463,8 +488,9 @@
             elif opt == '-d': pidfile = os.path.abspath(arg)
             elif opt == '-l': logfile = os.path.abspath(arg)
             elif opt == '-h': usage()
-            elif opt == '-N': RoundupRequestHandler.LOG_IPADDRESS = 0
+            elif opt == '-N': log_ip = 0
             elif opt == '-c': svc_args = [opt] + args; args = None
+            elif opt == '-C': config = arg
 
         if svc_args is not None and len(optlist) > 1:
             raise ValueError, _("windows service option must be the only one")
@@ -472,72 +498,53 @@
         if pidfile and not logfile:
             raise ValueError, _("logfile *must* be specified if pidfile is")
 
+        # handle the config file
+        if config:
+            cfg = ConfigParser.ConfigParser()
+            cfg.read(filename)
+            if port is undefined:
+                port = cfg.get('server', 'port', 8080)
+            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')
+
         # 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)
+                      _("Unable to bind to port %s, port already in use."%port)
             raise
 
-        if group is not None and hasattr(os, 'getuid'):
-            # if root, setgid to the running user
-            if not os.getuid():
-                try:
-                    import grp
-                except ImportError:
-                    raise ValueError, _("Can't change groups - no grp module")
-                try:
-                    try:
-                        gid = int(group)
-                    except ValueError:
-                        gid = grp.getgrnam(group)[2]
-                    else:
-                        grp.getgrgid(gid)
-                except KeyError:
-                    raise ValueError,_("Group %(group)s doesn't exist")%locals()
-                os.setgid(gid)
-            elif os.getuid():
-                print _('WARNING: ignoring "-g" argument, not root')
-
-        if hasattr(os, 'getuid'):
-            # if root, setuid to the running user
-            if not os.getuid() and user is not None:
-                try:
-                    import pwd
-                except ImportError:
-                    raise ValueError, _("Can't change users - no pwd module")
-                try:
-                    try:
-                        uid = int(user)
-                    except ValueError:
-                        uid = pwd.getpwnam(user)[2]
-                    else:
-                        pwd.getpwuid(uid)
-                except KeyError:
-                    raise ValueError, _("User %(user)s doesn't exist")%locals()
-                os.setuid(uid)
-            elif os.getuid() and user is not None:
-                print _('WARNING: ignoring "-u" argument, not root')
-
-            # People can remove this check if they're really determined
-            if not os.getuid() and user is None:
-                raise ValueError, _("Can't run as root!")
+        # change user and/or group
+        setgid(group)
+        setuid(user)
 
         # handle tracker specs
         if args:
-            d = {}
             for arg in args:
                 try:
                     name, home = arg.split('=')
                 except ValueError:
                     raise ValueError, _("Instances must be name=home")
-                d[name] = os.path.abspath(home)
-            RoundupRequestHandler.TRACKER_HOMES = d
+                home = os.path.abspath(home)
+                RoundupRequestHandler.TRACKER_HOMES[name] = home
     except SystemExit:
         raise
     except ValueError:
@@ -549,6 +556,7 @@
     # 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 not hasattr(os, 'fork'):
             print _("Sorry, you can't run the server as a daemon"

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