[Zope-Checkins] CVS: Zope/lib/python/Zope/Startup - ZctlLib.py:1.2 __init__.py:1.2 cmdline.py:1.2 datatypes.py:1.2 handlers.py:1.2 zopeschema.xml:1.2

Fred L. Drake, Jr. fred@zope.com
Wed, 29 Jan 2003 15:25:49 -0500


Update of /cvs-repository/Zope/lib/python/Zope/Startup
In directory cvs.zope.org:/tmp/cvs-serv21529

Added Files:
	ZctlLib.py __init__.py cmdline.py datatypes.py handlers.py 
	zopeschema.xml 
Log Message:
Update from chrism-install-branch.

=== Zope/lib/python/Zope/Startup/ZctlLib.py 1.1 => 1.2 === (648/748 lines abridged)
--- /dev/null	Wed Jan 29 15:25:49 2003
+++ Zope/lib/python/Zope/Startup/ZctlLib.py	Wed Jan 29 15:25:15 2003
@@ -0,0 +1,745 @@
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+    Zope appserver controller.  This is akin to apache's apachectl,
+    except with an interactive interpreter if no commands are specified
+    on the command-line.
+"""
+
+__version__ = '$Revision$'[11:-2]
+
+import cmd
+import getopt
+import os
+import signal
+import sys
+import time
+
+try:
+    import readline
+except:
+    readline = None
+
+from Zope.Startup import getOptions, getOptionDescriptions, configure
+from Zope.Startup.misc import TextBlockFormatter
+from Zope.Startup.misc.lock_file import lock_file
+
+USAGE = """\
+
+zopectl:  Zope appserver controller
+
+Usage:
+    zopectl [-h | --help] [--config=filepath or url] [additional options]
+
+Options:
+    -h or --help       Print this message.  Not compatible with the 'start'
+                       or 'restart' command.
+

[-=- -=- -=- 648 lines omitted -=- -=- -=-]

+            self.cmdqueue.append(' '.join(args))
+            self.cmdqueue.append('EOF')
+
+        self.cmdloop()
+
+def get_pids(filename):
+    for line in open(filename).readlines():
+        pids = line.split()
+        if pids:
+            return [ int(x.strip()) for x in pids ]
+
+def win32kill(pid, sig):
+    # we ignore the signal on win32
+    try:
+        import win32api
+        import pywintypes
+    except:
+        print ("Could not open win32api module, have you installed the "
+               "'win32all' package?")
+        return 1
+    try:
+        handle = win32api.OpenProcess(1, 0, pid)
+    except pywintypes.error, why:
+        # process named by pid not running
+        return 1
+    try:
+        status = win32api.TerminateProcess(handle, 0)
+    except:
+        return 1
+    if status is None:
+        return 0
+    return 1
+
+def kill(pid, sig):
+    try:
+        os.kill(pid, sig)
+    except OSError, why:
+        return 1
+    else:
+        return 0
+
+def cmdquote(cmd):
+    if sys.platform == 'win32':
+        # ugh.  win32 requires the command to be quoted.  unix requires
+        # that the command *not* be quoted.
+        cmd = '"%s"' % cmd
+    return cmd
+
+if sys.platform == 'win32':
+    kill = win32kill


=== Zope/lib/python/Zope/Startup/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Jan 29 15:25:49 2003
+++ Zope/lib/python/Zope/Startup/__init__.py	Wed Jan 29 15:25:15 2003
@@ -0,0 +1,314 @@
+""" Startup package.  Responsible for startup configuration of Zope """
+
+import os
+import sys
+import socket
+import re
+
+import ZConfig
+
+from misc.lock_file import lock_file
+from cmdline import getOptions, getOptionDescriptions # exported
+
+# global to hold config structures
+_schema = None
+_configuration = None
+
+def getConfiguration():
+    return _configuration
+
+def getSchema():
+    global _schema
+    if _schema is None:
+        here = os.path.dirname(__file__)
+        path = os.path.join(here, 'zopeschema.xml')
+        _schema = ZConfig.loadSchema(path)
+    return _schema
+
+def getSchemaKeys():
+    schema = getSchema()
+    return schema.getchildnames()
+
+def configure(config_location, options):
+    global _configuration
+    import handlers
+    schema = getSchema()
+    _configuration, handler = ZConfig.loadConfig(schema, config_location)
+    handlers.handleConfig(_configuration, handler, options)
+    return _configuration
+
+def start_zope(config_location, options):
+    check_python_version()
+    cfg = configure(config_location, options)
+
+    if cfg.zope_home not in sys.path:
+        sys.path.insert(0, cfg.zope_home)
+
+    if cfg.software_home not in sys.path:
+        sys.path.insert(0, cfg.software_home)
+
+    sys.path = filter(None, sys.path) # strip empties out of sys.path
+
+    # set up our initial logging environment (log everything to stderr
+    # if we're not in debug mode).
+    import zLOG
+
+    # don't initialize the event logger from the environment
+    zLOG._call_initialize = 0
+    import logging
+
+    from zLOG.LogHandlers import StartupHandler
+
+    # we log events to the root logger, which is backed by a
+    # "StartupHandler" log handler.  The "StartupHandler" outputs to
+    # stderr but also buffers log messages.  When the "real" loggers
+    # are set up, we flush accumulated messages in StartupHandler's
+    # buffers to the real logger.
+    startup_handler = StartupHandler(sys.stderr)
+    formatter = zLOG.EventLogger.formatters['file']
+    startup_handler.setFormatter(formatter)
+    if not cfg.debug_mode:
+        # prevent startup messages from going to stderr if we're not
+        # in debug mode
+        if os.path.exists('/dev/null'): # unix
+            devnull = '/dev/null'
+        else: # win32
+            devnull = 'nul:'
+        startup_handler = StartupHandler(open(devnull, 'w'))
+
+    # set up our event logger temporarily with a startup handler
+    event_logger = logging.getLogger('event')
+    event_logger.addHandler(startup_handler)
+
+    # set a locale if one has been specified in the config
+    cfg.locale and do_locale(cfg.locale)
+
+    # goofy source_port clients business
+    dav_clients = cfg.webdav_source_user_agents
+    if dav_clients:
+        sys.WEBDAV_SOURCE_PORT_CLIENTS = re.compile(dav_clients).search
+
+    # make sure to import zdaemon before zserver or weird things
+    # begin to happen
+    import zdaemon
+
+    # Import ZServer before we open the database or get at interesting
+    # application code so that ZServer's asyncore gets to be the
+    # official one. Also gets SOFTWARE_HOME, INSTANCE_HOME, and CLIENT_HOME
+    import ZServer
+
+    # Increase the number of threads
+    from ZServer import setNumberOfThreads
+    setNumberOfThreads(cfg.zserver_threads)
+
+    # if we're not using ZDaemon or if we're the child of a Zdaemon process,
+    # start ZServer servers before we setuid so we can bind to low ports
+    if not cfg.use_daemon_process or (
+        cfg.use_daemon_process and os.environ.get('ZDAEMON_MANAGED')
+        ):
+        socket_err = (
+            'There was a problem starting a server of type "%s". '
+            'This may mean that your user does not have permission to '
+            'bind to the port which the server is trying to use or the '
+            'port may already be in use by another application.'
+            )
+        for server_type, server in cfg.servers:
+            # create the server from the server factory
+            # set up in the config
+            try:
+                server()
+            except socket.error:
+                raise ZConfig.ConfigurationError(socket_err % server_type)
+
+    # do stuff that only applies to posix platforms (setuid, daemonizing)
+    if os.name == 'posix':
+        do_posix_stuff(cfg)
+
+    # Import Zope
+    import Zope
+    Zope.startup()
+
+    if not cfg.zserver_read_only_mode:
+        # lock_file is used for the benefit of zctl, so it can tell whether
+        # Zope is already running before attempting to fire it off again.
+        # We aren't concerned about locking the file to protect against
+        # other Zope instances running from our CLIENT_HOME, we just
+        # try to lock the file to signal that zctl should not try to
+        # start Zope if *it* can't lock the file; we don't panic
+        # if we can't lock it.
+        # we need a separate lock file because on win32, locks are not
+        # advisory, otherwise we would just use the pid file
+        lock_filename = (cfg.lock_filename or
+                         os.path.join(cfg.instance_home, 'var', 'Z2.lock'))
+
+        try:
+            if os.path.exists(lock_filename):
+                os.unlink(lock_filename)
+            LOCK_FILE = open(lock_filename, 'w')
+            lock_file(LOCK_FILE)
+        except IOError:
+            pass
+
+        # write pid file if zdaemon didn't do it already
+        if not cfg.use_daemon_process:
+            pf = open(cfg.pid_filename, 'w')
+            pid='%s\n' % os.getpid()
+            pf.write(pid)
+            pf.close()
+
+        # now that we've successfully setuid'd, we can log to
+        # somewhere other than stderr.  We rely on config
+        # to set up our logging properly.
+        for logger_name in ('access', 'trace'):
+            factory = getattr(cfg, logger_name)
+            if factory:
+                logger = factory() # activate the logger
+
+        # flush buffered startup messages to event logger
+        if cfg.eventlog:
+            logger = cfg.eventlog()
+            startup_handler.flushBufferTo(logger)
+
+        event_logger.removeHandler(startup_handler)
+
+    zLOG.LOG('Zope', zLOG.INFO, 'Ready to handle requests')
+
+    # Start Medusa, Ye Hass!
+    sys.ZServerExitCode=0
+    try:
+        import Lifetime
+        Lifetime.loop()
+        sys.exit(sys.ZServerExitCode)
+    finally:
+        if not cfg.zserver_read_only_mode:
+            try:
+                os.unlink(cfg.pid_filename)
+            except OSError:
+                pass
+            try:
+                LOCK_FILE.close()
+                os.unlink(lock_filename)
+            except OSError:
+                pass
+
+def _warn_nobody():
+    import zLOG
+    zLOG.LOG("Zope", zLOG.INFO, ("Running Zope as 'nobody' can compromise "
+                                 "your Zope files; consider using a "
+                                 "dedicated user account for Zope"))
+
+def check_python_version():
+    # check for Python version
+    python_version = sys.version.split()[0]
+    optimum_version = '2.2.2'
+    if python_version < '2.2':
+        raise ZConfig.ConfigurationError(
+            'Invalid python version ' + python_version)
+    if python_version[:3] == '2.2':
+        if python_version[4:5] < '2':
+            err = ('You are running Python version %s.  This Python version '
+                   'has known bugs that may cause Zope to run improperly. '
+                   'Consider upgrading to Python %s\n' %
+                   (python_version, optimum_version))
+            sys.stderr.write(err)
+
+def do_posix_stuff(cfg):
+    import zLOG
+    import zdaemon
+    import pwd
+    from Signals import Signals
+    Signals.registerZopeSignals()
+
+    # Warn if we were started as nobody.
+    if os.getuid():
+        if pwd.getpwuid(os.getuid())[0] == 'nobody':
+            _warn_nobody()
+
+    # Drop root privileges if we have them, and do some sanity checking
+    # to make sure we're not starting with an obviously insecure setup.
+    if os.getuid() == 0:
+        UID  = cfg.effective_user
+        if UID == None:
+            msg = ('A user was not specified to setuid to; fix this to '
+                   'start as root (change the effective_user directive '
+                   'in zope.conf)')
+            zLOG.LOG('Zope', zLOG.PANIC, msg)
+            raise ZConfig.ConfigurationError(msg)
+        # stuff about client home faults removed (real effective user
+        # support now)
+        try:
+            UID = int(UID)
+        except (TypeError, ValueError):
+            pass
+        gid = None
+        if isinstance(UID, str):
+            uid = pwd.getpwnam(UID)[2]
+            gid = pwd.getpwnam(UID)[3]
+        elif isinstance(UID, int):
+            uid = pwd.getpwuid(UID)[2]
+            gid = pwd.getpwuid(UID)[3]
+            UID = pwd.getpwuid(UID)[0]
+        else:
+            zLOG.LOG("Zope", zLOG.ERROR, ("Can't find UID %s" % UID))
+            raise ZConfig.ConfigurationError('Cant find UID %s' % UID)
+        if UID == 'nobody':
+            _warn_nobody()
+        if gid is not None:
+            try:
+                import initgroups
+                initgroups.initgroups(UID, gid)
+                os.setgid(gid)
+            except OSError:
+                zLOG.LOG("Zope", zLOG.INFO,
+                         'Could not set group id of effective user',
+                         error=sys.exc_info())
+        os.setuid(uid)
+        zLOG.LOG("Zope", zLOG.INFO,
+                 'Set effective user to "%s"' % UID)
+
+    if not cfg.debug_mode:
+        # umask is silly, blame POSIX.  We have to set it to get its value.
+        current_umask = os.umask(0)
+        os.umask(current_umask)
+        if current_umask != 077:
+            current_umask = '%03o' % current_umask
+            zLOG.LOG("Zope", zLOG.INFO, (
+                'Your umask of %s may be too permissive; for the security of '
+                'your Zope data, it is recommended you use 077' % current_umask
+                ))
+
+    # try to use a management daemon process.  We do this after we setuid so
+    # we don't write our pidfile out as root.
+    if cfg.use_daemon_process and not cfg.zserver_read_only_mode:
+        import App.FindHomes
+        sys.ZMANAGED=1
+        # zdaemon.run creates a process which "manages" the actual Zope
+        # process (restarts it if it dies).  The management process passes
+        # along signals that it receives to its child.
+        zdaemon.run(sys.argv, cfg.pid_filename)
+
+def do_locale(locale_id):
+    # workaround to allow unicode encoding conversions in DTML
+    import codecs
+    dummy = codecs.lookup('iso-8859-1')
+
+    if locale_id is not None:
+        try:
+            import locale
+        except:
+            raise ZConfig.ConfigurationError(
+                'The locale module could not be imported.\n'
+                'To use localization options, you must ensure\n'
+                'that the locale module is compiled into your\n'
+                'Python installation.'
+                )
+        try:
+            locale.setlocale(locale.LC_ALL, locale_id)
+        except:
+            raise ZConfig.ConfigurationError(
+                'The specified locale "%s" is not supported by your system.\n'
+                'See your operating system documentation for more\n'
+                'information on locale support.' % locale_id
+                )
+
+


=== Zope/lib/python/Zope/Startup/cmdline.py 1.1 => 1.2 ===
--- /dev/null	Wed Jan 29 15:25:49 2003
+++ Zope/lib/python/Zope/Startup/cmdline.py	Wed Jan 29 15:25:15 2003
@@ -0,0 +1,179 @@
+import getopt
+
+def getOptionDescriptions():
+    """ Temporary implementation """
+
+    short, long = getOptions()
+    n = 0
+    short_d = {}
+    long_d = {}
+
+    last = 0
+    n = 0
+
+    print short
+
+    if short:
+        while 1:
+            try:
+                opt = short[n]
+            except IndexError:
+                next = None
+            try:
+                next = short[n+1]
+            except IndexError:
+                next = None
+            if next == ':':
+                short_d[opt] = 1
+                n = n + 2
+            else:
+                if next is None and short.endswith(':'):
+                    short_d[opt] = 1
+                else:
+                    short_d[opt] = 0
+                n = n + 1
+            if next is None:
+                break
+
+    for opt in long:
+        if opt.endswith('='):
+            long_d[opt[:-1]] = 1
+        else:
+            long_d[opt] = 0
+
+    opts = []
+
+    short_l = short_d.items()
+    short_l.sort()
+    for k, v in short_l:
+        opts.append('    -%s%s' % (k, (v and ' <value>' or '')))
+
+    long_l = long_d.items()
+    long_l.sort()
+    for k, v in long_l:
+        opts.append('    --%s%s' % (k, (v and ' <value>' or '')))
+    return '\n'.join(opts)
+
+def getOptions():
+    short = 'Z:t:i:D:a:d:u:L:l:M:E:Xw:W:f:p:F:m:'
+    long  = [
+             'use-daemon-process=',
+             'zserver-threads=',
+             'python-check-interval=',
+             'debug-mode=',
+             'ip-address=',
+             'dns-ip-address=',
+             'effective-user=',
+             'locale=',
+             'access-log=',
+             'trace-log=',
+             'event-log=',
+             'disable-servers',
+             'http-server=',
+             'webdav-source-server=',
+# XXX need to finish these
+#             'ftp-server=',
+#             'pcgi-server=',
+#             'fcgi-server=',
+#             'monitor-server=',
+#             'icp-server=',
+             ]
+    return short, long
+
+class CommandLineOptions:
+
+    def __call__(self, cfg, options):
+        import Zope.Startup.datatypes
+        import Zope.Startup.handlers
+        import ZConfig.datatypes
+
+        short, long = getOptions()
+        opts, args = getopt.getopt(options, short, long)
+
+        for k, v in opts:
+            # set up data that servers may rely on
+            if k in ('-Z', '--use-daemon-process'):
+                datatype = Zope.Startup.datatypes.use_daemon_process
+                handler  = Zope.Startup.handlers.use_daemon_process
+                v = datatype(v)
+                handler(v)
+                cfg.use_daemon_process = v
+            elif k in ('-t', '--zserver-threads'):
+                cfg.zserver_threads = int(v)
+            elif k in ('-i', '--python-check-interval'):
+                cfg.python_check_interval = int(v)
+            elif k in ('-D', '--debug-mode'):
+                datatype = ZConfig.datatypes.asBoolean
+                handler = Zope.Startup.handlers.debug_mode
+                v = datatype(v)
+                handler(v)
+                cfg.debug_mode = v
+            elif k in ('-i', '--ip-address'):
+                datatype = ZConfig.datatypes.IpaddrOrHostname()
+                cfg.ip_address = datatype(v)
+            elif k in ('-d', '--dns-ip-address'):
+                datatype = ZConfig.datatypes.IpaddrOrHostname()
+                cfg.dns_ip_address = datatype(v)
+            elif k in ('-u', '--effective-user'):
+                cfg.effective_user = v
+            elif k in ('-L', '--locale'):
+                datatype = ZConfig.datatypes.check_locale
+                cfg.locale = datatype(v)
+            elif k in ('-l', '--access-log'):
+                cfg.access = default_logger('access', v,
+                                            '%(message)s',
+                                            '%Y-%m-%dT%H:%M:%S')
+            elif k in ('-M', '--trace-log'):
+                cfg.trace = default_logger('trace', v,
+                                           '%(message)s',
+                                           '%Y-%m-%dT%H:%M:%S')
+            elif k in ('-E', '--event-log'):
+                cfg.trace = default_logger('event', v,
+                                           '------\n%(asctime)s %(message)s',
+                                           '%Y-%m-%dT%H:%M:%S')
+            elif k in ('-X', '--disable-servers'):
+                cfg.servers = []
+            else:
+                # continue if we've not matched, otherwise
+                # fall through to the pop statement below
+                continue
+
+            opts.pop(0) # pop non-server data from opts
+
+        factory = Zope.Startup.handlers.ServerFactoryFactory(cfg)
+
+        for k, v in opts:
+            # set up server data from what's left in opts,
+            # using repopulated cfg
+            if k in ('-w', '--http-server'):
+                datatype = ZConfig.datatypes.inet_address
+                host, port = datatype(v)
+                section = dummy()
+                section.ports = [(host, port)]
+                section.force_connection_close = 0
+                cfg.servers.append(['http_server',
+                                    factory.http_server(section)[0]])
+            if k in ('-W', '--webdav-source-server'):
+                datatype = ZConfig.datatypes.inet_address
+                host, port = datatype(v)
+                section = dummy()
+                section.ports = [(host, port)]
+                section.force_connection_close = 0
+                cfg.servers.append(['webdav_source_server',
+                                    factory.webdav_source_server(section)[0]])
+
+class dummy:
+    # used as a namespace generator
+    pass
+            
+def default_logger(name, file, format, dateformat):
+    import Zope.Startup.datatypes
+    logger = dummy()
+    logger.level = 20
+    handler = dummy()
+    handler.file = file
+    handler.format = format
+    handler.dateformat = dateformat
+    handler.level = 20
+    handlers = [Zope.Startup.datatypes.file_handler(handler)]
+    return Zope.Startup.datatypes.LoggerWrapper(name, 20, handlers)


=== Zope/lib/python/Zope/Startup/datatypes.py 1.1 => 1.2 ===
--- /dev/null	Wed Jan 29 15:25:49 2003
+++ Zope/lib/python/Zope/Startup/datatypes.py	Wed Jan 29 15:25:15 2003
@@ -0,0 +1,137 @@
+import os
+
+from misc.factory import Factory
+
+# generic datatypes
+
+def security_policy_implementation(value):
+    value = value.upper()
+    ok = ('PYTHON', 'C')
+    if value not in ok:
+        raise ValueError, (
+            "security_policy_implementation must be one of %s" % ok
+            )
+    return value
+
+def use_daemon_process(value):
+    from ZConfig.datatypes import asBoolean
+    daemonize = asBoolean(value)
+    # cannot use daemon process on non-POSIX platforms
+    if os.name != 'posix':
+        return False
+    return daemonize
+
+# log-related datatypes
+# (the loghandler datatypes come from the zLOG package)
+
+def logger(section):
+    return LoggerWrapper(section.getSectionName(),
+                         section.level,
+                         section.handlers)
+
+# database-related datatypes
+
+def mount_point(value):
+    if value.startswith('/'):
+        return value
+    raise ValueError, (
+        'Invalid mount_point "%s" (must start with a slash)' % value
+        )
+
+def database(section):
+    if len(section.storages) > 1:
+        raise ValueError, ('Current database support limits database '
+                           'instances to a single storage')
+    if len(section.storages) < 1:
+        raise ValueError, 'Must name one storage in a database section'
+    klass = section.db_class
+    mounts = section.mount_points
+    dbfactory = Factory(
+        klass, None,
+        pool_size=section.pool_size,
+        cache_size=section.cache_size,
+        cache_deactivate_after=section.cache_deactivate_after,
+        version_pool_size=section.version_pool_size,
+        version_cache_size=section.version_cache_size,
+        version_cache_deactivate_after=section.version_cache_deactivate_after)
+    storagefactory = section.storages[0]
+    return mounts, DBWrapper(dbfactory, storagefactory)
+
+def filestorage(section):
+    return Factory('ZODB.FileStorage.FileStorage', None, section.path,
+                   create=section.create,
+                   read_only=section.read_only,
+                   stop=section.stop,
+                   quota=section.quota)
+
+def mappingstorage(section):
+    name = section.name
+    return Factory('ZODB.MappingStorage.MappingStorage', None, name)
+
+def clientstorage(section):
+    return Factory('ZEO.ClientStorage.ClientStorage', None, section.addr,
+                   storage=section.storage,
+                   cache_size=section.cache_size,
+                   name=section.name,
+                   client=section.client,
+                   debug=section.debug,
+                   var=section.var,
+                   min_disconnect_poll=section.min_disconnect_poll,
+                   max_disconnect_poll=section.max_disconnect_poll,
+                   wait=section.wait,
+                   read_only=section.read_only,
+                   read_only_fallback=section.read_only_fallback)
+
+_marker = object()
+
+class LoggerWrapper:
+    """
+    A wrapper used to create loggers while delaying actual logger
+    instance construction.  We need to do this because we may
+    want to reference a logger before actually instantiating it (for example,
+    to allow the app time to set an effective user).
+    An instance of this wrapper is a callable which, when called, returns a
+    logger object.
+    """
+    def __init__(self, name, level, handler_factories):
+        self.name = name
+        self.level = level
+        self.handler_factories = handler_factories
+        self.resolved = _marker
+
+    def __call__(self):
+        if self.resolved is _marker:
+            # set the logger up
+            import logging
+            logger = logging.getLogger(self.name)
+            logger.handlers = []
+            logger.propagate = 0
+            logger.setLevel(self.level)
+            for handler_factory in self.handler_factories:
+                handler = handler_factory()
+                logger.addHandler(handler)
+            self.resolved = logger
+        return self.resolved
+
+class DBWrapper:
+    """
+    A wrapper used to create ZODB databases while delaying the underlying
+    storage instance construction.  We need to do this because we may
+    want to reference a database before actually instantiating it (for
+    example, in order to delay database construction until after an
+    effective user is set or until all configuration parsing is done).
+    An instance of this wrapper is a callable which, when called, returns a
+    database object.
+    """
+    def __init__(self, dbfactory, storagefactory):
+        self.dbfactory = dbfactory
+        self.storagefactory = storagefactory
+        self.resolved = _marker
+
+    def __call__(self):
+        if self.resolved is _marker:
+            args, kw = self.dbfactory.getArgs()
+            args = [self.storagefactory()] + list(args)
+            self.dbfactory.setArgs(args, kw)
+            self.resolved = self.dbfactory()
+        return self.resolved


=== Zope/lib/python/Zope/Startup/handlers.py 1.1 => 1.2 ===
--- /dev/null	Wed Jan 29 15:25:49 2003
+++ Zope/lib/python/Zope/Startup/handlers.py	Wed Jan 29 15:25:15 2003
@@ -0,0 +1,334 @@
+import os
+import types
+
+from misc.factory import Factory
+
+# top-level key handlers
+
+def _setenv(name, value):
+    if isinstance(value, types.StringTypes):
+        os.environ[name] = value
+    else:
+        os.environ[name] = `value`
+
+def _append_slash(value):
+    if value is None:
+        return None
+    if not value.endswith(os.sep):
+        return value + os.sep
+    return value
+
+def software_home(value):
+    value = _append_slash(value)
+    value and _setenv('SOFTWARE_HOME', value)
+    return value
+
+def zope_home(value):
+    value = _append_slash(value)
+    value and _setenv('ZOPE_HOME', value)
+    return value
+
+def instance_home(value):
+    value = _append_slash(value)
+    value and _setenv('INSTANCE_HOME', value)
+    return value
+
+def client_home(value):
+    value = _append_slash(value)
+    value and _setenv('CLIENT_HOME', value)
+    return value
+
+def debug_mode(value):
+    value and _setenv('Z_DEBUG_MODE', '1')
+    return value
+
+def enable_product_installation(value):
+    value and _setenv('FORCE_PRODUCT_LOAD', '1')
+    return value
+
+def locale(value):
+    import locale
+    locale.setlocale(locale.LC_ALL, value)
+    return value
+
+def use_daemon_process(value):
+    value and _setenv('Z_DEBUG_MODE', '1')
+    return value
+
+def zserver_read_only_mode(value):
+    value and _setenv('ZOPE_READ_ONLY', '1')
+    return value
+
+def automatically_quote_dtml_request_data(value):
+    not value and _setenv('ZOPE_DTML_REQUEST_AUTOQUOTE', '0')
+    return value
+
+def skip_authentication_checking(value):
+    value and _setenv('ZSP_AUTHENTICATED_SKIP', '1')
+    return value
+
+def skip_ownership_checking(value):
+    value and _setenv('ZSP_OWNEROUS_SKIP', '1')
+    return value
+
+def maximum_number_of_session_objects(value):
+    default = 1000
+    value not in (None, default) and _setenv('ZSESSION_OBJECT_LIMIT', value)
+    return value
+
+def session_add_notify_script_path(value):
+    value is not None and _setenv('ZSESSION_ADD_NOTIFY', value)
+    return value
+
+def session_delete_notify_script_path(value):
+    value is not None and _setenv('ZSESSION_DEL_NOTIFY', value)
+    return value
+
+def session_timeout_minutes(value):
+    default = 20
+    value not in (None, default) and _setenv('ZSESSION_TIMEOUT_MINS', value)
+    return value
+
+def suppress_all_access_rules(value):
+    value and _setenv('SUPPRESS_ACCESSRULE', value)
+    return value
+
+def suppress_all_site_roots(value):
+    value and _setenv('SUPPRESS_SITEROOT', value)
+    return value
+
+def database_quota_size(value):
+    value and _setenv('ZOPE_DATABASE_QUOTA', value)
+    return value
+
+def read_only_database(value):
+    value and _setenv('ZOPE_READ_ONLY', '1')
+    return value
+
+def zeo_client_name(value):
+    value and _setenv('ZEO_CLIENT', value)
+    return value
+
+def structured_text_header_level(value):
+    value is not None and _setenv('STX_DEFAULT_LEVEL', value)
+    return value
+
+def maximum_security_manager_stack_size(value):
+    value is not None and _setenv('Z_MAX_STACK_SIZE', value)
+    return value
+
+def publisher_profile_file(value):
+    value is not None and _setenv('PROFILE_PUBLISHER', value)
+    return value
+
+def http_realm(value):
+    value is not None and _setenv('Z_REALM', value)
+    return value
+
+def security_policy_implementation(value):
+    value not in ('C', None) and _setenv('ZOPE_SECURITY_POLICY', value)
+
+# server handlers
+
+class _RootHandler:
+    def __init__(self, options):
+        self.options = options
+
+    def __call__(self, config):
+        """ Mutate the configuration with defaults and perform
+        fixups of values that require knowledge about configuration
+        values outside of their context. """
+
+        # set up cgi overrides
+        env = {}
+        for pair in config.cgi_environment_variables:
+            key, value = pair
+            self._env[key] = value
+        config.cgi_environment_variables = env
+
+        # set up server factories
+        factory = ServerFactoryFactory(config)
+        l = []
+
+        for section in config.servers:
+            # XXX ugly; need to clean this up soon
+            server_type = section.getSectionType().replace("-", "_")
+            for server_factory in getattr(factory, server_type)(section):
+                l.append((server_type, server_factory))
+
+        # if no servers are defined, create default http server and ftp server
+        if not l:
+            class dummy:
+                pass
+            http, ftp = dummy(), dummy()
+
+            http.ports = [('', 8080)]
+            http.force_connection_close = 0
+            ftp.ports = [('', 8021)]
+
+            http = factory.http_server(http)[0]
+            ftp = factory.ftp_server(ftp)[0]
+            l.extend([('http_server', http), ('ftp_server', ftp)])
+
+        config.servers = l
+
+        # set up defaults for zope_home and client_home if they're
+        # not in the config
+        if config.zope_home is None:
+            config.zope_home   = zope_home(
+                os.path.dirname(os.path.dirname(config.software_home))
+                )
+        if config.client_home is None:
+            config.client_home = client_home(
+                os.path.join(config.instance_home, 'var')
+                )
+
+        # set up defaults for pid_filename and lock_filename if they're
+        # not in the config
+        if config.pid_filename is None:
+            config.pid_filename = os.path.join(config.client_home, 'Z2.pid')
+        if config.lock_filename is None:
+            config.lock_filename = os.path.join(config.client_home, 'Z2.lock')
+
+        # set up a default root filestorage if there are no root storages
+        # mentioned in the config
+        databases = config.databases
+        root_mounts = [ ('/' in db.mount_points) for db in databases ]
+        if not True in root_mounts:
+            from datatypes import DBWrapper, Factory
+            storagefactory = Factory(
+                'ZODB.FileStorage.FileStorage', None,
+                os.path.join(config.client_home, 'Data.fs'))
+            dbfactory = Factory('ZODB.DB', None)
+            databases.append((['/'], DBWrapper(dbfactory, storagefactory)))
+
+        # do command-line overrides
+        import cmdline
+        opt_processor = cmdline.CommandLineOptions()
+        opt_processor(config, self.options)
+
+class ServerFactoryFactory:
+    def __init__(self, config):
+        self._config = config
+        # alias some things for use in server handlers
+        from zLOG.AccessLogger import access_logger
+        self._logger = access_logger
+        import ZODB # :-( required to import user
+        from AccessControl.User import emergency_user
+        if hasattr(emergency_user, '__null_user__'):
+            self._pw = None
+        else:
+            self._pw = emergency_user._getPassword()
+        self._resolver = self.get_dns_resolver()
+        self._read_only = config.zserver_read_only_mode
+        self._default_ip = config.ip_address
+        self._env = config.cgi_environment_variables
+        self._module = 'Zope' # no longer settable
+
+    def get_dns_resolver(self):
+        if self._config.dns_ip_address:
+            from ZServer import resolver
+            return resolver.caching_resolver(self._config.dns_ip_address)
+
+    def http_server(self, section):
+        l = []
+        from ZServer import zhttp_server, zhttp_handler
+        for addr, port in section.ports:
+            def callback(inst, self=self):
+                handler = zhttp_handler(self._module, '', self._env)
+                inst.install_handler(handler)
+                if section.force_connection_close:
+                    handler._force_connection_close = 1
+            serverfactory = Factory('ZServer.zhttp_server', callback,
+                                    ip=addr, port=port,
+                                    resolver=self._resolver,
+                                    logger_object=self._logger)
+            l.append(serverfactory)
+        return l
+
+    def webdav_source_server(self, section):
+        l = []
+        for addr, port in section.ports:
+            def callback(inst, self=self):
+                from ZServer.WebDAVSrcHandler import WebDAVSrcHandler
+                handler = WebDAVSrcHandler(self._module, '', self._env)
+                inst.install_handler(handler)
+                if section.force_connection_close:
+                    handler._force_connection_close = 1
+            serverfactory = Factory('ZServer.zhttp_server', callback,
+                                    ip=addr, port=port,
+                                    resolver=self._resolver,
+                                    logger_object=self._logger)
+            l.append(serverfactory)
+        return l
+
+    def ftp_server(self, section):
+        l = []
+        for addr, port in section.ports:
+            serverfactory = Factory('ZServer.FTPServer', None,
+                                    module=self._module, ip=addr, port=port,
+                                    resolver=self._resolver,
+                                    logger_object=self._logger)
+            l.append(serverfactory)
+        return l
+
+    def pcgi_server(self, section):
+        if not self._read_only:
+            serverfactory = Factory('ZServer.PCGIServer', None,
+                                    module=self._module,
+                                    ip=self._default_ip,
+                                    pcgi_file=section.file,
+                                    resolver=self._resolver,
+                                    logger_object=self._logger)
+            return [serverfactory]
+        return []
+
+    def fcgi_server(self, section):
+        if section.file and section.port:
+            raise ValueError, ("Must specify either 'port' or 'file' in "
+                               "fcgi server configuration, but not both")
+        if section.port:
+            addr = section.port[0]
+            port = section.port[1]
+        else:
+            addr = port = None
+        file = section.file
+        if not self._read_only:
+            serverfactory = Factory('ZServer.FCGIServer', None,
+                                    module=self._module, ip=addr, port=port,
+                                    socket_file=file, resolver=self._resolver,
+                                    logger_object=self._logger)
+            return [serverfactory]
+        return []
+
+    def monitor_server(self, section):
+        if self._pw is None:
+            import zLOG
+            zLOG.LOG("z2", zLOG.WARNING, 'Monitor server not started'
+                     ' because no emergency user exists.')
+            return []
+        l = []
+        for addr, port in section.ports:
+            serverfactory = Factory('ZServer.secure_monitor_server', None,
+                                    password=self._pw,
+                                    hostname=addr,
+                                    port=port)
+            l.append(serverfactory)
+        return l
+
+    def icp_server(self, section):
+        l = []
+        for addr, port in section.ports:
+            serverfactory = Factory('ZServer.ICPServer.ICPServer', None,
+                                    addr, port)
+            l.append(serverfactory)
+        return l
+
+def handleConfig(config, multihandler, options):
+    handlers = {}
+    for name, value in globals().items():
+        if not name.startswith('_'):
+            handlers[name] = value
+    root_handler = _RootHandler(options)
+    handlers['root_handler'] = root_handler
+    return multihandler(handlers)


=== Zope/lib/python/Zope/Startup/zopeschema.xml 1.1 => 1.2 ===
--- /dev/null	Wed Jan 29 15:25:49 2003
+++ Zope/lib/python/Zope/Startup/zopeschema.xml	Wed Jan 29 15:25:15 2003
@@ -0,0 +1,232 @@
+<schema prefix="Zope.Startup.datatypes" handler="root_handler">
+
+  <!-- type definitions -->
+
+  <import package="zLOG"/>
+
+
+  <abstracttype name="server"/>
+
+  <sectiontype name="http-server"
+               implements="server">
+     <multikey name="port" attribute="ports" datatype="inet-address"/>
+     <key name="force-connection-close" datatype="boolean" default="off"/>
+  </sectiontype>
+
+  <sectiontype name="ftp-server"
+               implements="server">
+     <multikey name="port" attribute="ports" datatype="inet-address"/>
+  </sectiontype>
+
+  <sectiontype name="webdav-source-server"
+               implements="server">
+     <multikey name="port" attribute="ports" datatype="inet-address"/>
+     <key name="force-connection-close" datatype="boolean" default="off"/>
+  </sectiontype>
+
+  <sectiontype name="pcgi-server"
+               implements="server">
+    <key name="file" datatype="existing-file"/>
+  </sectiontype>
+
+  <sectiontype name="fcgi-server"
+               implements="server">
+    <key name="port" datatype="inet-address"/>
+    <key name="file" datatype="existing-dirpath"/>
+  </sectiontype>
+
+  <sectiontype name="monitor-server"
+               implements="server">
+     <multikey name="port" attribute="ports" datatype="inet-address"/>
+  </sectiontype>
+
+  <sectiontype name="icp-server"
+               implements="server">
+     <multikey name="port" attribute="ports" datatype="inet-address"/>
+  </sectiontype>
+
+
+  <sectiontype name="logger" datatype=".logger">
+    <description>
+      This "logger" type only applies to access and request ("trace")
+      logging; event logging is handled by the zLOG package, which
+      provides the loghandler type used here.
+    </description>
+    <key name="level" datatype="zLOG.datatypes.logging_level" default="info"/>
+    <multisection type="loghandler" attribute="handlers" name="*"
+                  required="yes"/>
+  </sectiontype>
+
+  <!-- the types that follow will be replaced by an import from the
+       ZODB package -->
+  <abstracttype name="storage"/>
+
+  <sectiontype name="filestorage" datatype=".filestorage"
+               implements="storage">
+    <key name="path" required="yes"/>
+    <key name="create" datatype="boolean"/>
+    <key name="read-only" datatype="boolean"/>
+    <key name="stop"/>
+    <key name="quota" datatype="byte-size"/>
+  </sectiontype>
+
+  <sectiontype name="mappingstorage" datatype=".mappingstorage"
+               implements="storage">
+    <key name="name" default="Mapping Storage"/>
+  </sectiontype>
+
+  <sectiontype name="clientstorage" datatype=".clientstorage"
+               implements="storage">
+    <key name="addr" datatype="inet-address" required="yes"/>
+    <key name="storage" default="1"/>
+    <key name="cache-size" datatype="byte-size" default="20MB"/>
+    <key name="name" default=""/>
+    <key name="client"/>
+    <key name="debug" datatype="boolean"/>
+    <key name="var" datatype="existing-directory"/>
+    <key name="min-disconnect-poll" datatype="time-interval" default="5S"/>
+    <key name="max-disconnect-poll" datatype="time-interval" default="300S"/>
+    <key name="wait" datatype="boolean" default="on"/>
+    <key name="read-only" datatype="boolean" default="off"/>
+    <key name="read-only-fallback" datatype="boolean" default="off"/>
+  </sectiontype>
+
+
+  <sectiontype name="zodb" datatype=".database">
+    <multisection type="storage" name="*" attribute="storages"/>
+    <multikey name="mount-point" attribute="mount_points"
+              datatype=".mount_point"/>
+    <key name="db-class" default="ZODB.DB"/>
+    <key name="cache-size" datatype="integer" default="5000"/>
+    <key name="pool-size" datatype="integer" default="7"/>
+    <key name="cache-deactivate-after" datatype="time-interval" default="60S"/>
+    <key name="version-pool-size" datatype="integer" default="3"/>
+    <key name="version-cache-size" datatype="integer" default="100"/>
+    <key name="version-cache-deactivate-after" datatype="time-interval"
+         default="10S"/>
+  </sectiontype>
+
+  <!-- end of type definitions -->
+
+  <!-- schema begins  -->
+
+  <key name="instance-home" datatype="existing-directory"
+       required="yes" handler="instance_home"/>
+
+  <key name="software-home" datatype="existing-directory"
+       required="yes" handler="software_home"/>
+
+  <key name="zope-home" datatype="existing-directory"
+       handler="zope_home"/>
+
+  <key name="client-home" datatype="existing-directory"
+       handler="client_home"/>
+
+  <key name="pid-filename" datatype="existing-dirpath"/>
+
+  <key name="lock-filename" datatype="existing-dirpath"/>
+
+  <key name="debug-mode" datatype="boolean" default="on"
+       handler="debug_mode"/>
+
+  <key name="effective-user"/>
+
+  <key name="enable-product-installation" datatype="boolean" default="on"
+       handler="enable_product_installation"/>
+
+  <key name="locale" datatype="locale" handler="locale"/>
+
+  <key name="zserver-threads" datatype="integer" default="4"/>
+
+  <key name="python-check-interval" datatype="integer" default="500">
+    <description>
+      Value passed to Python's sys.setcheckinterval() function.  The
+      higher this is, the less frequently the Python interpreter
+      checks for keyboard interrupts.  Setting this to higher values
+      also reduces the frequency of potential thread switches, which
+      can improve the performance of a busy server.
+    </description>
+  </key>
+
+  <key name="use-daemon-process" datatype=".use_daemon_process" default="on"
+       handler="use_daemon_process"/>
+
+  <key name="zserver-read-only-mode" datatype="boolean" default="off"
+       handler="zserver_read_only_mode"/>
+
+  <key name="structured-text-header-level" datatype="integer" default="3"
+       handler="structured_text_header_level"/>
+
+  <key name="maximum-security-manager-stack-size" datatype="integer"
+       default="100" handler="maximum_security_manager_stack_size"/>
+
+  <key name="publisher-profile-file" handler="publisher_profile_file"/>
+
+  <key name="webdav-source-user-agents"/>
+
+  <multikey name="cgi-environment-variable" datatype="key-value"
+            attribute="cgi_environment_variables"/>
+
+  <key name="dns-ip-address" datatype="ipaddr-or-hostname"/>
+
+  <key name="ip-address" datatype="ipaddr-or-hostname"/>
+
+  <key name="http-realm" default="Zope" handler="http_realm"/>
+
+  <key name="automatically-quote-dtml-request-data" datatype="boolean"
+       default="on" handler="automatically_quote_dtml_request_data"/>
+
+  <key name="security-policy-implementation"
+       datatype=".security_policy_implementation"
+       default="C" handler="security_policy_implementation"/>
+
+  <key name="skip-authentication-checking" datatype="boolean"
+       default="off" handler="skip_authentication_checking"/>
+
+  <key name="skip-ownership-checking" datatype="boolean"
+       default="off" handler="skip_ownership_checking"/>
+
+  <key name="maximum-number-of-session-objects" datatype="integer"
+       default="1000" handler="maximum_number_of_session_objects"/>
+
+  <key name="session-add-notify-script-path"
+       handler="session_add_notify_script_path"/>
+
+  <key name="session-delete-notify-script-path"
+       handler="session_add_notify_script_path"/>
+
+  <key name="session-timeout-minutes" datatype="integer"
+       default="20" handler="session_timeout_minutes"/>
+
+  <key name="suppress-all-access-rules" datatype="boolean"
+       default="off" handler="suppress_all_access_rules"/>
+
+  <key name="suppress-all-site-roots" datatype="boolean"
+       default="off" handler="suppress_all_site_roots"/>
+
+  <key name="database-quota-size" datatype="byte-size"
+       handler="database_quota_size"/>
+
+  <key name="read-only-database" datatype="boolean"
+       handler="read_only_database"/>
+
+  <key name="zeo-client-name"
+       handler="zeo_client_name"/>
+
+  <section type="eventlog" name="*" attribute="eventlog">
+    <description>
+      Describes the logging performed by zLOG.LOG() calls.
+    </description>
+  </section>
+
+  <section type="logger" name="access"/>
+ 
+  <section type="logger" name="trace"/>
+
+  <multisection type="server" name="*" attribute="servers"/>
+
+  <multisection type="zodb" name="*" attribute="databases"/>
+
+  <!-- schema ends  -->
+
+</schema>