Skip to content

Commit da5d4c1

Browse files
author
Alexander Smishlajev
committed
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).
1 parent 7aebff7 commit da5d4c1

File tree

1 file changed

+143
-109
lines changed

1 file changed

+143
-109
lines changed

roundup/scripts/roundup_server.py

Lines changed: 143 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717

1818
"""Command-line script that runs a server over roundup.cgi.client.
1919
20-
$Id: roundup_server.py,v 1.62 2004-09-21 09:29:18 a1s Exp $
20+
$Id: roundup_server.py,v 1.63 2004-10-17 17:54:48 a1s Exp $
2121
"""
2222
__docformat__ = 'restructuredtext'
2323

2424
# python version check
25-
from roundup import version_check
25+
from roundup import configuration, version_check
2626
from roundup import __version__ as roundup_version
2727

2828
import sys, os, urllib, StringIO, traceback, cgi, binascii, getopt, imp
@@ -369,7 +369,7 @@ def usage(message=''):
369369
pairs on the command-line. Make sure the name part doesn't include
370370
any url-unsafe characters like spaces, as these confuse IE.
371371
''')%locals()
372-
sys.exit(0)
372+
#sys.exit(0)
373373

374374

375375
def daemonize(pidfile):
@@ -461,6 +461,76 @@ def setuid(user):
461461
raise ValueError, _("User %(user)s doesn't exist")%locals()
462462
os.setuid(uid)
463463

464+
class TrackerHomeOption(configuration.FilePathOption):
465+
# Tracker homes do not need description strings
466+
# attached to FilePathOption. Description appears once
467+
# before the trackers section.
468+
class_description = ""
469+
470+
class ServerConfig(configuration.Config):
471+
472+
SETTINGS = (
473+
("main", (
474+
(configuration.Option, "host", "",
475+
"Host name of the Roundup web server instance.\n"
476+
"If empty, listen on all network interfaces."),
477+
(configuration.IntegerNumberOption, "port", DEFAULT_PORT,
478+
"Port to listen on."),
479+
(configuration.NullableOption, "user", "",
480+
"User ID as which the server will answer requests.\n"
481+
"In order to use this option, "
482+
"the server must be run initially as root.\n"
483+
"Availability: Unix."),
484+
(configuration.NullableOption, "group", "",
485+
"Group ID as which the server will answer requests.\n"
486+
"In order to use this option, "
487+
"the server must be run initially as root.\n"
488+
"Availability: Unix."),
489+
(configuration.BooleanOption, "log_hostnames", "no",
490+
"Log client machine names instead of IP addresses "
491+
"(much slower)"),
492+
(configuration.NullableFilePathOption, "pidfile", "",
493+
"File to which the server records "
494+
"the process id of the daemon.\n"
495+
"If this option is not set, "
496+
"the server will run in foreground\n"),
497+
(configuration.NullableFilePathOption, "logfile", "",
498+
"Log file path. If unset, log to stderr."),
499+
)),
500+
("trackers", (), "Roundup trackers to serve.\n"
501+
"Each option in this section defines single Roundup tracker.\n"
502+
"Option name identifies the tracker and will appear in the URL.\n"
503+
"Option value is tracker home directory path.\n"
504+
"The path may be either absolute or relative\n"
505+
"to the directory containig this config file."),
506+
)
507+
508+
def __init__(self, config_file=None):
509+
configuration.Config.__init__(self, config_file, self.SETTINGS)
510+
511+
def _adjust_options(self, config):
512+
"""Add options for tracker homes"""
513+
# return early if there are no tracker definitions.
514+
# trackers must be specified on the command line.
515+
if not config.has_section("trackers"):
516+
return
517+
# config defaults appear in all sections.
518+
# filter them out.
519+
defaults = config.defaults().keys()
520+
for name in config.options("trackers"):
521+
if name not in defaults:
522+
self.add_option(TrackerHomeOption(self, "trackers", name))
523+
524+
def _get_name(self):
525+
return "Roundup server"
526+
527+
def trackers(self):
528+
"""Return tracker definitions as a list of (name, home) pairs"""
529+
trackers = []
530+
for option in self._get_section_options("trackers"):
531+
trackers.append((option, self["TRACKERS_" + option.upper()]))
532+
return trackers
533+
464534
undefined = []
465535
def run(port=undefined, success_message=None):
466536
''' Script entry point - handle args and figure out what to to.
@@ -470,134 +540,98 @@ def run(port=undefined, success_message=None):
470540
if hasattr(socket, 'setdefaulttimeout'):
471541
socket.setdefaulttimeout(60)
472542

473-
hostname = pidfile = logfile = user = group = svc_args = log_ip = undefined
474-
config = None
543+
config = ServerConfig()
475544

545+
options = "hvS"
546+
if RoundupService:
547+
options += 'c'
476548
try:
477-
# handle the command-line args
478-
options = 'n:p:g:u:d:l:C:hNv'
479-
if RoundupService:
480-
options += 'c'
481-
482-
try:
483-
optlist, args = getopt.getopt(sys.argv[1:], options)
484-
except getopt.GetoptError, e:
485-
usage(str(e))
549+
(optlist, args) = config.getopt(sys.argv[1:],
550+
options, ("help", "version", "save-config",),
551+
host="n:", port="p:", group="g:", user="u:",
552+
logfile="l:", pidfile="d:", log_hostnames="N")
553+
except (getopt.GetoptError), e: #, configuration.ConfigurationError), e:
554+
usage(str(e))
555+
return
486556

557+
# if running in windows service mode, don't do any other stuff
558+
if ("-c", "") in optlist:
559+
RoundupService.address = (config.HOST, config.PORT)
560+
# XXX why the 1st argument to the service is "-c"
561+
# instead of the script name???
562+
return win32serviceutil.HandleCommandLine(RoundupService,
563+
argv=["-c"] + args)
564+
565+
# add tracker names from command line.
566+
# this is done early to let '--save-config' handle the trackers.
567+
if args:
568+
for arg in args:
569+
try:
570+
name, home = arg.split('=')
571+
except ValueError:
572+
raise ValueError, _("Instances must be name=home")
573+
config.add_option(
574+
configuration.FilePathOption(config, "trackers", name))
575+
config["TRACKERS_" + name.upper()] = home
576+
577+
# handle remaining options
578+
if optlist:
487579
for (opt, arg) in optlist:
488-
if opt == '-n': hostname = arg
489-
elif opt == '-v':
490-
print '%s (python %s)'%(roundup_version, sys.version.split()[0])
491-
return
492-
elif opt == '-p': port = int(arg)
493-
elif opt == '-u': user = arg
494-
elif opt == '-g': group = arg
495-
elif opt == '-d': pidfile = os.path.abspath(arg)
496-
elif opt == '-l': logfile = os.path.abspath(arg)
497-
elif opt == '-h': usage()
498-
elif opt == '-N': log_ip = 0
499-
elif opt == '-c': svc_args = [opt] + args; args = None
500-
elif opt == '-C': config = arg
501-
502-
if svc_args and len(optlist) > 1:
503-
raise ValueError, _("windows service option must be the only one")
504-
505-
if pidfile and not logfile:
506-
raise ValueError, _("logfile *must* be specified if pidfile is")
507-
508-
# handle the config file
509-
if config:
510-
cfg = ConfigParser.ConfigParser()
511-
cfg.read(filename)
512-
if port is undefined and cfg.has_option('server', 'port'):
513-
port = cfg.get('server', 'port')
514-
if user is undefined and cfg.has_option('server', 'user'):
515-
user = cfg.get('server', 'user')
516-
if group is undefined and cfg.has_option('server', 'group'):
517-
group = cfg.get('server', 'group')
518-
if log_ip is undefined and cfg.has_option('server', 'log_ip'):
519-
RoundupRequestHandler.LOG_IPADDRESS = cfg.getboolean('server',
520-
'log_ip')
521-
if pidfile is undefined and cfg.has_option('server', 'pidfile'):
522-
pidfile = cfg.get('server', 'pidfile')
523-
if logfile is undefined and cfg.has_option('server', 'logfile'):
524-
logfile = cfg.get('server', 'logfile')
525-
homes = RoundupRequestHandler.TRACKER_HOMES
526-
for section in cfg.sections():
527-
if section == 'server':
528-
continue
529-
homes[section] = cfg.get(section, 'home')
530-
531-
# defaults
532-
if hostname is undefined:
533-
hostname = ''
534-
if port is undefined:
535-
port = DEFAULT_PORT
536-
if group is undefined:
537-
group = None
538-
if user is undefined:
539-
user = None
540-
if svc_args is undefined:
541-
svc_args = None
542-
543-
# obtain server before changing user id - allows to use port <
544-
# 1024 if started as root
545-
address = (hostname, port)
546-
try:
547-
httpd = server_class(address, RoundupRequestHandler)
548-
except socket.error, e:
549-
if e[0] == errno.EADDRINUSE:
550-
raise socket.error, \
551-
_("Unable to bind to port %s, port already in use."%port)
552-
raise
553-
554-
# change user and/or group
555-
setgid(group)
556-
setuid(user)
557-
558-
# handle tracker specs
559-
if args:
560-
for arg in args:
561-
try:
562-
name, home = arg.split('=')
563-
except ValueError:
564-
raise ValueError, _("Instances must be name=home")
565-
home = os.path.abspath(home)
566-
RoundupRequestHandler.TRACKER_HOMES[name] = home
567-
except SystemExit:
580+
if opt in ("-h", "--help"):
581+
usage()
582+
elif opt in ("-v", "--version"):
583+
print '%s (python %s)' % (roundup_version,
584+
sys.version.split()[0])
585+
elif opt in ("-S", "--save-config"):
586+
config.save()
587+
print _("Configuration saved to %s") % config.filepath
588+
# any of the above options prevent server from running
589+
return
590+
591+
RoundupRequestHandler.LOG_IPADDRESS = not config.LOG_HOSTNAMES
592+
593+
# obtain server before changing user id - allows to use port <
594+
# 1024 if started as root
595+
try:
596+
httpd = server_class((config.HOST, config.PORT), RoundupRequestHandler)
597+
except socket.error, e:
598+
if e[0] == errno.EADDRINUSE:
599+
raise socket.error, \
600+
_("Unable to bind to port %s, port already in use.") \
601+
% config.PORT
568602
raise
569-
except ValueError:
570-
usage(error())
571-
# except:
572-
# print error()
573-
# sys.exit(1)
603+
604+
# change user and/or group
605+
setgid(config.GROUP)
606+
setuid(config.USER)
607+
608+
# apply tracker specs
609+
for (name, home) in config.trackers():
610+
home = os.path.abspath(home)
611+
RoundupRequestHandler.TRACKER_HOMES[name] = home
574612

575613
# we don't want the cgi module interpreting the command-line args ;)
576614
sys.argv = sys.argv[:1]
577615

578616
# fork the server from our parent if a pidfile is specified
579-
if pidfile:
617+
if config.PIDFILE:
580618
if not hasattr(os, 'fork'):
581619
print _("Sorry, you can't run the server as a daemon"
582620
" on this Operating System")
583621
sys.exit(0)
584622
else:
585-
daemonize(pidfile)
586-
587-
if svc_args is not None:
588-
# don't do any other stuff
589-
RoundupService.address = address
590-
return win32serviceutil.HandleCommandLine(RoundupService, argv=svc_args)
623+
daemonize(config.PIDFILE)
591624

592625
# redirect stdout/stderr to our logfile
593-
if logfile:
626+
if config.LOGFILE:
594627
# appending, unbuffered
595-
sys.stdout = sys.stderr = open(logfile, 'a', 0)
628+
sys.stdout = sys.stderr = open(config.LOGFILE, 'a', 0)
596629

597630
if success_message:
598631
print success_message
599632
else:
600-
print _('Roundup server started on %(address)s')%locals()
633+
print _('Roundup server started on %(HOST)s:%(PORT)s') \
634+
% config
601635

602636
try:
603637
httpd.serve_forever()

0 commit comments

Comments
 (0)