Commit e9f0f6f8 authored by Fred Drake's avatar Fred Drake

Merge startup code from the new-install-branch.

parent 5b55ca0d
##############################################################################
#
# 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: 1.4 $'[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.
--config Use an alternate configuration from a local file.
or a URL. Default: 'zope.conf'.
If a URL is specified, it must begin with
'http://'.
File example: '/home/chrism/zope.conf'
URL example: 'http://www.zope.org/zope.conf'
Additional options:
%s
Commands:
help [<command>]
start
stop
restart
logopenclose
status
show [<info>*]
run <script_filename>
debug
write_inituser username password
If no commands supplied, runs as an interactive read-eval-print
interpreter.
"""
def start(config_location):
""" Called by stub zopectl.py in an instance_home """
_ZopeCtlCmd().main(config_location)
class ZopeCtl:
"""
Workhorse engine for controlling the appserver.
"""
config = None
def __init__( self, reporter ):
self._reporter = reporter
#
# Command implementation
#
def start( self, arg ):
"""
Start the Zope appserver.
Syntax: start [arg1, arg2, ..]
All arguments are passed to the zope.py command line.
"""
lock_status = self.lockFile()
if lock_status:
self._report('Error: cannot start Zope. Another Zope '
'instance has locked the "%s" file. Use "stop" '
'to stop it.' % self._getConfigValue('lock_filename'))
self._report()
return
self._report('Starting Zope')
opts = self._getCommandLineOpts()
loc = self._getConfigLocation()
if loc.find(' ') != -1:
loc = '"%s"' % loc
config_location = '--config=%s' % loc
start_script = os.path.join(self._getSoftwareHome(), 'zope.py')
start_script = cmdquote(start_script)
args = [start_script] + [config_location] + opts + [arg]
wait = self._getConfigValue('debug_mode')
try:
self._spawnPython(*args, **{'wait':wait})
except KeyboardInterrupt:
if not wait:
raise
def restart( self, arg ):
"""
Restart the Zope appserver.
Syntax: restart
"""
self.stop()
time.sleep(3)
self.start( arg )
def quit(self, arg=None):
""" Exit the controller. """
self._report('Exiting')
sys.exit(0)
def logopenclose( self, arg=None ):
"""
Open and close the Zope appserver logfiles. Works only
under a POSIX-compliant OS and under Zope 2.6+.
Syntax: logopenclose
"""
if os.name == 'posix':
self._report('Opening and closing Zope logfiles')
status = self._kill( signal.SIGUSR2)
if status:
self._report('Could not open and close logfiles (check event '
'log for further information)')
else:
self._report('Log files opened and closed successfully')
else:
self._report(
'Cannot open and close logfiles on non-Posix platforms.'
)
def status( self, arg=None ):
"""
Print a message showing the status of the Zope appserver,
including:
- Whether Zope is up or down
- PIDs of appserver processes
Syntax: status
"""
"""
Report on process ids
"""
lock_status = self.lockFile()
self._report('%-20s : %s' % ('Running', lock_status and 'yes' or 'no'))
if not lock_status:
return
pidfile_name = self._getConfigValue('pid_filename')
try:
pids = get_pids(pidfile_name)
except IOError:
self._report('Pid file %s could not be found' % pidfile_name)
self._report('Could not report status (maybe Zope isnt running?)')
return
self._report( '%-20s : %s' % ( 'Main PID', pids[0] ))
def stop(self, arg=None):
"""
Stop the Zope appserver, using the signal name passed in 'arg'.
Syntax: stop
"""
status = self._kill(signal.SIGTERM)
if status:
self._report('Could not stop Zope (maybe already stopped or '
'pending start?)')
else:
self._report('Zope stopped successfully')
return status
def shell(self, arg=None):
"""
Run a command using the system shell. Commands passed to zopectl
which start with a '!' will also be interpreted by the
system shell.
Syntax: [ shell [command] | !command ]
Examples:
'shell vi zope.conf'
'!vi zope.conf'
"""
if sys.platform == 'win32':
os.system('cmd "%s"' % arg)
elif os.name == 'posix':
os.system('sh -c "%s"' % arg)
else:
self._report('No shell known for system %s' % os.name)
def _kill( self, sig):
try:
pidfile_name = self._getConfigValue('pid_filename')
pids = get_pids(pidfile_name)
pid = pids[-1]
except IOError:
self._report('Pid file %s could not be found' % pidfile_name)
return 1
status = kill(pid, sig)
return status
def show( self, what=None ):
"""
Print a message showing all or part of the current
Zope configuration. 'show' alone implies 'show config'.
Syntax: show [option]
Options:
config Combo of 'options', 'python', 'config-path' and
'command-line'
command-line Command-line opts that will be passed to zope.py
when 'start' is invoked
options All config-file specified options
python Python version and path info
config-path Filename or url used to obtain config data.
"""
if type( what ) is type( '' ):
what = what.split( ' ' )
whatsit = {}
KNOWN = (
'python',
'config',
'command-line',
'options',
'config-path',
)
unknown = []
for asked in what:
if asked in KNOWN:
whatsit[ asked ] = 1
elif asked.strip():
unknown.append( 'Unknown query: %s' % asked )
if not whatsit:
whatsit['config'] = 1
if whatsit.get( 'config' ):
whatsit[ 'options' ] = 1
whatsit[ 'command-line' ] = 1
whatsit[ 'python' ] = 1
whatsit[ 'config-path' ] = 1
for unk in unknown:
self._report( unk )
if whatsit.get( 'python' ):
self._showPython()
if whatsit.get( 'config-path' ):
self._showConfigPath()
if whatsit.get( 'command-line' ):
self._showCommandLine()
if whatsit.get( 'options' ):
self._showConfigValues()
def _getRunCmd(self):
swhome = self._getSoftwareHome()
return(
"import os, sys; os.environ['EVENT_LOG_SEVERITY']='300'; "
"sys.path.insert(0, '%s'); from Zope.Startup import configure; "
"configure('%s', %s); import Zope; app=Zope.app()"
% ( swhome, self._getConfigLocation(), self._getCommandLineOpts())
)
def run( self, arg ):
"""
Run a Python script, connected to the Zope appserver.
Syntax: run [script_path]
"""
cmd = self._getRunCmd() + "; execfile( '%s' )" % arg
cmd = cmdquote(cmd)
self._report( 'Running script: %s' % arg )
try:
status = self._spawnPython('-c', cmd)
except KeyboardInterrupt:
pass
def debug( self, arg ):
"""
Start a Python interpreter connected to the Zope appserver.
Syntax: debug
"""
cmd = self._getRunCmd()
cmd = cmdquote(cmd)
msg = ('Starting debugger. The name "app" will be bound to the Zope '
'"root object" when you enter interactive mode.')
self._report( msg )
try:
status = self._spawnPython('-i', '-c', cmd)
except KeyboardInterrupt:
pass
def write_inituser( self, args ):
"""
Write a file named 'inituser' to the current directory with
the username and password specified as arguments. Writing this
file to a new instance home directory will bootstrap the instance
home for login with an initial username/password combination.
Syntax: write_inituser username password
"""
fname = 'inituser'
from Zope.Startup.misc import zpasswd
if type( args ) is type( '' ):
args = args.split( ' ' )
if len(args) != 2:
self._report('Syntax: write_inituser username password')
return
username, password = args
password = zpasswd.generate_passwd(password, 'SHA')
try:
inituser = open(fname, 'w')
except IOError:
self._report('Could not open %s file (permissions?)' % fname)
return
inituser.write('%s:%s' % (username, password))
self._report('Wrote %s' % os.path.abspath(fname))
def reload(self, args):
"""
Reload your Zope configuration file.
"""
self._reconfigure()
self._report('Reloaded configuration from %s' %
self._getConfigLocation())
#
# Helper functions
#
def _report( self, msg='', level=1 ):
msg = TextBlockFormatter.format(msg, max_width=73, indent=6)
self._reporter( msg, level )
def _help( self, method_name ):
self._report( normalizeDocstring( getattr( self, method_name ) ) )
def _reconfigure(self):
self._config = configure(self._config_location,
self._getCommandLineOpts())
def _setConfigLocation(self, config_location):
self._config_location = config_location
def _getConfigLocation(self):
return self._config_location
def _getSoftwareHome(self):
return self._getConfigValue('software_home')
def _getConfigValue(self, name):
return getattr(self._config, name)
def _setCommandLineOpts( self, l ):
self._cmdline = l
def _getCommandLineOpts(self):
return self._cmdline
def _spawnPython(self, *args, **kw):
if not kw.has_key('wait'):
startup = os.P_WAIT
elif kw.has_key('wait') and kw['wait']:
startup = os.P_WAIT
else:
startup = os.P_NOWAIT
args = list(args)
args = [sys.executable] + args
status = os.spawnv(startup, sys.executable, args)
return status
def _checkService( self, host, port, socket_path=None ):
"""
Return 1 if server is found at (host, port), 0 otherwise.
"""
import socket
if socket_path is None:
address = ( host, int(port) )
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else:
address = socket_path
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
s.connect( address )
except socket.error:
return 0
else:
return 1
def _showDict( self, d ):
if d:
keys = [ ( k.lower(), k ) for k in d.keys() ]
keys.sort()
for kl, k in keys:
self._report( '%-20s : %s' % ( k, d[ k ] ) )
else:
self._report( 'Unknown!' )
def _showPython( self ):
self._report()
self._report( 'Python Information:' )
self._report()
info = sys.version_info
version = '%d.%d' % ( info[0], info[1] )
if info[2]:
version += '.%d' % info[2]
if info[4]:
version += ' (%s-%d)' % ( info[3], info[4] )
else:
version += ' (%s)' % info[3]
self._report( '%-20s : %s' % ( 'Version', version ) )
self._report( '%-20s : %s' % ( 'Platform', sys.platform ) )
self._report( '%-20s : %s' % ( 'Executable', sys.executable ) )
self._report( '%-20s : %s' % ( 'Working directory'
, os.getcwd() ) )
self._report( '%-20s :' % 'Path' )
self._report()
for p in sys.path:
self._report( ' %s' % p )
self._report()
def _showConfigPath( self ):
self._report()
self._report('Configuration Path: %s' % self._getConfigLocation())
self._report()
def _showConfigValues( self ):
self._report()
self._report( 'Config-file specified values:' )
self._report()
self._report( str(self._config) )
self._report()
def _showCommandLine(self):
self._report()
self._report('Command Line:')
self._report()
self._report(' '.join(self._getCommandLineOpts()))
self._report()
def lockFile(self):
filename = self._getConfigValue('lock_filename')
if not os.path.exists(filename):
return 0
file = open(filename, 'r+')
try:
lock_file(file)
except:
return 1
return 0
def normalizeDocstring(obj):
"""Remove leading and trailing whitespace from each line of a docstring."""
lines = [line.strip() for line in obj.__doc__.split('\n')]
return '\n'.join(lines)
def _MAKEDO( command ):
"""
Work around insistence of 'Cmd.help' on printing docstrings with
full indentation; we fabricate a function which has the docstring
it expects, using the one from ZopeCtl as a starting point;
the generated function is suitable for use as 'do_command' within
the Cmd-derived class.
"""
def xdo( self, args=None, command=command ):
getattr( self._engine, command )( args )
xdo.func_doc = normalizeDocstring( getattr( ZopeCtl, command ) )
return xdo
#
# Read-eval-print interpreter
#
class _ZopeCtlCmd( cmd.Cmd ):
"""
Interactive command processor.
"""
def __init__( self, completekey='tab', prompt='zopectl> ', verbosity=1 ):
self.cmdqueue = []
if completekey and readline:
readline.set_completer(self.complete)
readline.parse_and_bind(completekey + ': complete')
self._engine = ZopeCtl( self._report )
self.prompt = prompt
self._verbosity = verbosity
def _report( self, msg='', level=1 ):
if self._verbosity >= level:
print msg
def emptyline(self):
pass
def default( self, line ):
if line == 'EOF':
self._report()
return 1
try:
tokens = line.split()
method, args = tokens[0], ' '.join( tokens[1:] )
method = getattr( self._engine, method, None )
if method is not None:
method( args )
return None
except:
pass
return cmd.Cmd.default( self, line )
def parseline(self, line):
line = line.strip()
if not line:
return None, None, line
elif line[0] == '?':
line = 'help ' + line[1:]
elif line[0] == '!':
if hasattr(self, 'do_shell'):
line = 'shell ' + line[1:]
else:
return None, None, line
i, n = 0, len(line)
while i < n and line[i] in self.identchars: i = i+1
cmd, arg = line[:i], line[i:].strip()
return cmd, arg, line
def completedefault(self, *ignored):
return []
def completenames(self, text, *ignored):
dotext = 'do_'+text
return [a[3:] for a in self.get_names() if a.startswith(dotext)]
def complete(self, text, state):
"""Return the next possible completion for 'text'.
If a command has not been entered, then complete against command list.
Otherwise try to call complete_<command> to get list of completions.
"""
if state == 0:
import readline
origline = readline.get_line_buffer()
line = origline.lstrip()
stripped = len(origline) - len(line)
begidx = readline.get_begidx() - stripped
endidx = readline.get_endidx() - stripped
if begidx>0:
cmd, args, foo = self.parseline(line)
if cmd == '':
compfunc = self.completedefault
else:
try:
compfunc = getattr(self, 'complete_' + cmd)
except AttributeError:
compfunc = self.completedefault
else:
compfunc = self.completenames
self.completion_matches = compfunc(text, line, begidx, endidx)
try:
return self.completion_matches[state]
except IndexError:
return None
def get_names(self):
# Inheritance says we have to look in class and
# base classes; order is not important.
names = []
classes = [self.__class__]
while classes:
aclass = classes[0]
if aclass.__bases__:
classes = classes + list(aclass.__bases__)
names = names + dir(aclass)
del classes[0]
return names
def complete_help(self, *args):
return self.completenames(*args)
def complete_show(self, text, *ignored):
meta = ['config', 'options', 'python', 'command-line',
'config-path']
return [a for a in meta if a.startswith(text) ]
do_start = _MAKEDO( 'start' )
do_restart = _MAKEDO( 'restart' )
do_logopenclose = _MAKEDO( 'logopenclose' )
do_stop = _MAKEDO( 'stop' )
do_status = _MAKEDO( 'status' )
do_show = _MAKEDO( 'show' )
do_run = _MAKEDO( 'run' )
do_debug = _MAKEDO( 'debug' )
do_quit = _MAKEDO( 'quit' )
do_shell = _MAKEDO( 'shell' )
do_write_inituser = _MAKEDO( 'write_inituser')
do_reload = _MAKEDO( 'reload' )
#
# Command-line processing
#
def usage( self ):
opts = getOptionDescriptions()
self._report( USAGE % opts)
def main( self, config_location ):
# add relevant options to options list
longopts = [ "help", "config="]
shortopts = "h"
start_shortopts, start_longopts = getOptions()
optcopy = sys.argv[1:]
try:
opts, args = getopt.getopt(optcopy, start_shortopts+shortopts,
start_longopts+longopts)
except getopt.GetoptError, v:
print v
self.usage()
sys.exit(127)
for k, v in opts:
if k in ('-h', '--help'):
self.usage()
sys.exit(0)
elif k == '--config':
config_location = v
i = 0
# remove --config= from optcopy
for item in optcopy:
if item.startswith('--config='):
break
i = i + 1
optcopy.pop(i)
self._engine._setConfigLocation(config_location)
if not args:
self._engine._setCommandLineOpts(optcopy)
else:
self._engine._setCommandLineOpts(optcopy[len(args):])
self._engine._reconfigure()
if args:
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
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
""" Startup package. Responsible for startup configuration of Zope """
import os
import sys
import socket
import re
import ZConfig
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 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(cfg):
# 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
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 = zLOG.EventLogger.EventLogger.logger
event_logger.addHandler(startup_handler)
# set a locale if one has been specified in the config
if cfg.locale:
do_locale(cfg.locale)
# Increase the number of threads
import ZServer
ZServer.setNumberOfThreads(cfg.zserver_threads)
# Start ZServer servers before we setuid so we can bind to low ports:
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.'
)
servers = []
for server in cfg.servers:
# create the server from the server factory
# set up in the config
try:
servers.append(server.create())
except socket.error:
raise ZConfig.ConfigurationError(socket_err
% server.servertype())
cfg.servers = servers
# 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
from Zope.Startup.misc.lock_file import lock_file
lock_filename = cfg.lock_filename
try:
if os.path.exists(lock_filename):
os.unlink(lock_filename)
LOCK_FILE = open(lock_filename, 'w')
lock_file(LOCK_FILE)
except IOError:
pass
# Now that we've successfully setuid'd, we can log to
# somewhere other than stderr. Activate the configured logs:
if cfg.access is not None:
cfg.access()
if cfg.trace is not None:
cfg.trace()
# flush buffered startup messages to event logger
event_logger.removeHandler(startup_handler)
if cfg.eventlog is not None:
logger = cfg.eventlog()
startup_handler.flushBufferTo(logger)
zLOG.LOG('Zope', zLOG.INFO, 'Ready to handle requests')
# Start Medusa, Ye Hass!
try:
import Lifetime
Lifetime.loop()
sys.exit(ZServer.exit_code)
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
))
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
)
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 = [
'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 ('-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)
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Datatypes for the Zope schema for use with ZConfig."""
import os
# 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 cgi_environment(section):
return section.environ
# Datatype for the access and trace logs
# (the loghandler datatypes come from the zLOG package)
class LoggerFactory:
"""
A factory 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, section):
self.name = section.getSectionName()
self.level = section.level
self.handler_factories = section.handlers
self.resolved = None
def __call__(self):
if self.resolved is None:
# 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
# DNS resolver
def dns_resolver(hostname):
from ZServer.medusa import resolver
return resolver.caching_resolver(hostname)
# Datatype for the root configuration object
# (adds the softwarehome and zopehome fields; default values for some
# computed paths)
def root_config(section):
here = os.path.dirname(os.path.abspath(__file__))
swhome = os.path.dirname(os.path.dirname(here))
section.softwarehome = swhome
section.zopehome = os.path.dirname(os.path.dirname(swhome))
if section.cgi_environment is None:
section.cgi_environment = {}
if section.clienthome is None:
section.clienthome = os.path.join(section.instancehome, "var")
# set up defaults for pid_filename and lock_filename if they're
# not in the config
if section.pid_filename is None:
section.pid_filename = os.path.join(section.clienthome, 'Z2.pid')
if section.lock_filename is None:
section.lock_filename = os.path.join(section.clienthome, 'Z2.lock')
return section
import os
# top-level key handlers
def _setenv(name, value):
if isinstance(value, str):
os.environ[name] = value
else:
os.environ[name] = `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 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
def root_handler(config):
""" Mutate the configuration with defaults and perform
fixups of values that require knowledge about configuration
values outside of their context. """
# if no servers are defined, create default http server and ftp server
if not config.servers:
import ZServer.datatypes
config.servers = [
ZServer.datatypes.HTTPServerFactory(_DummyServerConfig(8080)),
ZServer.datatypes.FTPServerFactory(_DummyServerConfig(8021)),
]
# prepare servers:
for factory in config.servers:
factory.prepare(config.ip_address or '',
config.dns_resolver,
"Zope",
config.cgi_environment,
config.port_base)
class _DummyServerConfig:
class _Thing:
pass
def __init__(self, port):
import socket
self.address = self._Thing()
self.address.family = socket.AF_INET
self.address.address = '', port
self.force_connection_close = 0
def handleConfig(config, multihandler):
handlers = {}
for name, value in globals().items():
if not name.startswith('_'):
handlers[name] = value
return multihandler(handlers)
##############################################################################
#
# Copyright (c) 2001, 2002 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
#
##############################################################################
"""
Revision information:
$Id: TextBlockFormatter.py,v 1.4 2003/03/18 21:37:49 fdrake Exp $
"""
import string, math
def format(text, max_width=80, indent=0, trailing_lines_indent_more=0):
text = string.expandtabs(string.replace(text, '\r', ''))
lines = string.split(text, '\n')
aggregate = []
for line in lines:
if len(line) <= max_width-1:
# this line is short enough to output
aggregate.append(line)
else:
lines = splitlongline(line, max_width)
aggregate.extend(lines)
out = []
i = 0
for line in aggregate:
spaces = ' ' * indent
if i != 0 and trailing_lines_indent_more:
spaces = spaces + (' ' * trailing_lines_indent_more)
out.append('%s%s' % (spaces, line))
i = i + 1
return string.join(out, '\n')
def splitword(word, max_width=80, linepos=0):
# some lines may have single words that exceed the max_width
# We want to break apart long words into as many chunks as necessary
if len(word) <= max_width:
return [word]
first_chunk_len = max_width-1-linepos
firstchunk = word[:first_chunk_len]
word = word[first_chunk_len:]
numchunks = int(math.ceil(len(word) / float(max_width-1)))
index = 0
tmp = [firstchunk]
for chunknum in range(numchunks):
chunk = word[index:index+max_width-1]
tmp.append(chunk)
index = index + max_width-1
return tmp
def splitlongline(line, max_width=80):
# split a "long" line defined by max_width into a list of lines
line = string.strip(line)
words = string.split(line, ' ')
wordnum = 0
# iterate over all the words in the line, extending the word list
# necessary for too-long words
aggregate = []
linelen = 0
wordnum = 0
while words:
word = words.pop(0)
if not word: continue
if len(word) > max_width:
new_words = splitword(word, max_width, linelen)
word = new_words[0]
for new_word in new_words[1:]:
words.insert(wordnum, new_word)
wordnum = wordnum + 1
if words:
next_word = words[0]
else:
next_word = None
if next_word is None:
aggregate.append(word)
wordnum = wordnum + 1
continue
maybe_len = linelen + len(word) + len(next_word)
if maybe_len >= max_width-1:
aggregate.append(word)
aggregate.append(None)
linelen = 0
else:
aggregate.append(word)
linelen = linelen + len(word) + 1
wordnum = wordnum + 1
s = ""
last = None
for item in aggregate:
if item is None:
s = '%s\n' % s
elif last is None:
s = '%s%s' % (s, item)
else:
s = '%s %s' % (s, item)
last = item
return string.split(s, '\n')
long = """
To turn a component into a product you must fulfill many contracts. For the most part these contracts are not yet defined in terms of interfaces. Instead you must subclass from base classes that implement the contracts. This makes building products confusing, and this is an area that we are actively working on improving. Hereisalonglinethatshouldbecaughtandbrokenupbytheformatter,hopefullyitllgetsplitupwhenirunitthroughthere."""
long2 = """
Hereisalonglinethatshouldbecaughtandbrokenupbytheformatter,hopefullyitllgetsplitupwhenirunitthroughthere."""
long3 = """
To turn a component into a product you must fulfill many contracts. For the most part these contracts are not yet defined in terms of interfaces.
Instead you must subclass from base classes that implement the contracts. This makes building products confusing, and this is an area that we are
actively working on improving. Hereisalonglinethatshouldbecaughtandbrokenupbytheformatter,hopefullyitllgetsplitupwhenirunitthroughthere."""
if __name__ == '__main__':
print format(long, 60, 10)
print format(long)
print format(long2, 60, 10)
print format(long2)
print format(long3, 60, 10)
print format(long3)
""" Placeholder module file """
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
"""Utility function for file locking.
This module provides a platform-specific function which uses the
best-available strategy for locking a file object.
"""
try:
import fcntl
except ImportError:
# Try windows-specific code:
try:
# We expect this module to exist, but the LockFile function may not.
from ZODB.winlock import LockFile
except ImportError:
# we don't understand any kind of locking, forget it
def lock_file(file):
pass
else:
# Windows
def lock_file(file):
un = file.fileno()
LockFile(un, 0, 0, 1, 0) # just lock the first byte, who cares
else:
# Unix-specific locking:
def lock_file(file):
fcntl.flock(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
#!/usr/bin/env python
##############################################################################
#
# 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 user bootstrap system"""
__version__='$Revision: 1.4 $ '[11:-2]
import sys, sha, binascii, random, getopt, getpass, os
try:
from crypt import crypt
except ImportError:
crypt = None
def generate_salt():
"""Generate a salt value for the crypt function."""
salt_choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789./")
return random.choice(salt_choices)+random.choice(salt_choices)
def generate_passwd(password, encoding):
encoding=encoding.upper()
if encoding == 'SHA':
pw = '{SHA}' + binascii.b2a_base64(sha.new(password).digest())[:-1]
elif encoding == 'CRYPT':
pw = '{CRYPT}' + crypt(password, generate_salt())
elif encoding == 'CLEARTEXT':
pw = password
return pw
def write_generated_password(home, ac_path, username):
pw_choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789!")
acfile=open(ac_path, 'w')
pw = ''
for i in range(8):
pw = pw + random.choice(pw_choices)
acfile.write('%s:%s' % (username, generate_passwd(pw, 'SHA')))
acfile.close()
os.system('chmod 644 %s' % ac_path)
return pw
def write_access(home, user='', group=''):
ac_path=os.path.join(home, 'access')
if not os.path.exists(ac_path):
print '-'*78
print 'creating default access file'
pw = write_generated_password(home, ac_path, 'emergency')
print """Note:
The emergency user name and password are 'emergency'
and '%s'.
You can change the emergency name and password with the
zpasswd script. To find out more, type:
%s zpasswd.py
""" % (pw, sys.executable)
import do; do.ch(ac_path, user, group)
def write_inituser(home, user='', group=''):
ac_path=os.path.join(home, 'inituser')
if not os.path.exists(ac_path):
print '-'*78
print 'creating default inituser file'
pw = write_generated_password(home, ac_path, 'admin')
print """Note:
The initial user name and password are 'admin'
and '%s'.
You can change the name and password through the web
interface or using the 'zpasswd.py' script.
""" % pw
import do; do.ch(ac_path, user, group)
def main(argv):
short_options = ':u:p:e:d:'
long_options = ['username=',
'password=',
'encoding=',
'domains=']
usage = """Usage: %s [options] filename
If this program is called without command-line options, it will prompt
for all necessary information. The available options are:
-u / --username=
Set the username to be used for the initial user or the emergency user
-p / --password=
Set the password
-e / --encoding=
Set the encryption/encoding rules. Defaults to SHA-1. OPTIONAL
-d / --domains=
Set the domain names that the user user can log in from. Defaults to
any. OPTIONAL.
Filename is required and should be the name of the file to store the
information in (usually "inituser" or "access").
Copyright (C) 1999, 2000 Digital Creations, Inc.
""" % argv[0]
try:
if len(argv) < 2:
raise "CommandLineError"
optlist, args = getopt.getopt(sys.argv[1:], short_options, long_options)
if len(args) != 1:
raise "CommandLineError"
access_file = open(args[0], 'w')
if len(optlist) > 0:
# Set the sane defaults
username = ''
encoding = 'SHA'
domains = ''
for opt in optlist:
if (opt[0] == '-u') or (opt[0] == '--username'):
username = opt[1]
elif (opt[0] == '-p') or (opt[0] == '--password'):
password = opt[1]
elif (opt[0] == '-e') or (opt[0] == '--encoding'):
encoding = opt[1]
elif (opt[0] == '-d') or (opt[0] == '--domains'):
domains = ":" + opt[1]
# Verify that we got what we need
if not username or not password:
raise "CommandLineError"
access_file.write(username + ':' +
generate_passwd(password, encoding) +
domains)
else:
# Run through the prompts
while 1:
username = raw_input("Username: ")
if username != '':
break
while 1:
password = getpass.getpass("Password: ")
verify = getpass.getpass("Verify password: ")
if verify == password:
break
else:
password = verify = ''
print "Password mismatch, please try again..."
while 1:
print """
Please choose a format from:
SHA - SHA-1 hashed password (default)
CRYPT - UNIX-style crypt password
CLEARTEXT - no protection
"""
encoding = raw_input("Encoding: ")
if encoding == '':
encoding = 'SHA'
break
if encoding.upper() in ['SHA', 'CRYPT', 'CLEARTEXT']:
break
domains = raw_input("Domain restrictions: ")
if domains: domains = ":" + domains
access_file.write(username + ":" +
generate_passwd(password, encoding) +
domains)
except "CommandLineError":
sys.stderr.write(usage)
sys.exit(1)
# If called from the command line
if __name__=='__main__': main(sys.argv)
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Command-line processor for Zope."""
import os
import zdaemon.zdoptions
class ZopeOptions(zdaemon.zdoptions.ZDOptions):
schemadir = os.path.dirname(os.path.abspath(__file__))
schemafile = "zopeschema.xml"
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Tests of the Zope.Startup package."""
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Test that the Zope schema can be loaded."""
import os
import cStringIO
import tempfile
import unittest
import ZConfig
import Zope.Startup
from Zope.Startup import datatypes
from App.config import getConfiguration
TEMPNAME = tempfile.mktemp()
class StartupTestCase(unittest.TestCase):
def load_config_text(self, text):
# We have to create a directory of our own since the existence
# of the directory is checked. This handles this in a
# platform-independent way.
schema = Zope.Startup.getSchema()
sio = cStringIO.StringIO(
text.replace("<<INSTANCE_HOME>>", TEMPNAME))
os.mkdir(TEMPNAME)
try:
conf, handler = ZConfig.loadConfigFile(schema, sio)
finally:
os.rmdir(TEMPNAME)
self.assertEqual(conf.instancehome, TEMPNAME)
return conf
def test_load_config_template(self):
schema = Zope.Startup.getSchema()
cfg = getConfiguration()
fn = os.path.join(cfg.zopehome, "skel", "etc", "zope.conf.in")
f = open(fn)
text = f.read()
f.close()
self.load_config_text(text)
def test_cgi_environment(self):
conf = self.load_config_text("""\
# instancehome is here since it's required
instancehome <<INSTANCE_HOME>>
<cgi-environment>
HEADER value
ANOTHER value2
</cgi-environment>
""")
items = conf.cgi_environment.items()
items.sort()
self.assertEqual(items, [("ANOTHER", "value2"), ("HEADER", "value")])
def test_access_and_trace_logs(self):
fn = tempfile.mktemp()
conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
<logger access>
<logfile>
path %s
</logfile>
</logger>
""" % fn)
self.assert_(isinstance(conf.access, datatypes.LoggerFactory))
self.assertEqual(conf.access.name, "access")
self.assertEqual(conf.access.handler_factories[0].section.path, fn)
self.assert_(conf.trace is None)
def test_dns_resolver(self):
from ZServer.medusa import resolver
conf = self.load_config_text("""\
instancehome <<INSTANCE_HOME>>
dns-server localhost
""")
self.assert_(isinstance(conf.dns_resolver, resolver.caching_resolver))
def test_suite():
return unittest.makeSuite(StartupTestCase)
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")
<schema prefix="Zope.Startup.datatypes"
datatype=".root_config"
handler="root_handler">
<!-- type definitions -->
<import package="zLOG"/>
<import package="ZODB"/>
<import package="ZServer"/>
<sectiontype name="logger" datatype=".LoggerFactory">
<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>
<sectiontype name="cgi-environment"
datatype=".cgi_environment"
keytype="identifier">
<key name="+" attribute="environ"/>
</sectiontype>
<sectiontype name="zoperunner">
<description>
This section describes the options for zopectl. These options
have no default value specified in the schema; in some cases,
zopectl calculates a dynamic default, in others, the feature
associated with the option is disabled.
For those options that also have corresponding command-line
options, the command line option (short and long form) are given
here too.
</description>
<key name="daemon" datatype="boolean"
required="no" default="false">
<description>
Command-line option: -d or --daemon.
If this option is true, zdrun.py runs in the background as a
true daemon. It forks an child process which becomes the
subprocess manager, while the parent exits (making the shell
that started it believe it is done). The child process also
does the following:
- if the directory option is set, change into that directory
- redirect stdin, stdout and stderr to /dev/null
- call setsid() so it becomes a session leader
- call umask(022)
</description>
</key>
<key name="backoff-limit" datatype="integer"
required="no" default="10">
<description>
Command-line option: -b or --backoff-limit.
When the subprocess crashes, zdrun.py inserts a one-second
delay before it restarts it. When the subprocess crashes
again right away, the delay is incremented by one second, and
so on. What happens when the delay has reached the value of
backoff-limit (in seconds), depends on the value of the
forever option. If forever is false, zdrun.py gives up at
this point, and exits. An always-crashing subprocess will
have been restarted exactly backoff-limit times in this case.
If forever is true, zdrun.py continues to attempt to restart
the process, keeping the delay at backoff-limit seconds.
If the subprocess stays up for more than backoff-limit
seconds, the delay is reset to 1 second.
</description>
</key>
<key name="forever" datatype="boolean"
required="no" default="false">
<description>
Command-line option: -f or --forever.
If this option is true, zdrun.py will keep restarting a
crashing subprocess forever. If it is false, it will give up
after backoff-limit crashes in a row. See the description of
backoff-limit for details.
</description>
</key>
<key name="hang-around" datatype="boolean"
required="no" default="false">
<description>
If this option is true, the zdrun.py process will remain even
when the daemon subprocess is stopped. In this case, zopectl
will restart zdrun.py as necessary. If this option is false,
zdrun.py will exit when the daemon subprocess is stopped
(unless zdrun.py intends to restart it).
</description>
</key>
<key name="default-to-interactive" datatype="boolean"
required="no" default="true">
<description>
If this option is true, zopectl enters interactive mode
when it is invoked without a positional command argument. If
it is false, you must use the -i or --interactive command line
option to zopectl to enter interactive mode.
</description>
</key>
<key name="prompt" datatype="string"
required="no" default="zopectl>">
<description>
The prompt shown by zopectl program.
</description>
</key>
</sectiontype>
<!-- end of type definitions -->
<!-- schema begins -->
<key name="instancehome" datatype="existing-directory"
required="yes">
<description>
The top-level directory which contains the "instance" of the
application server.
</description>
</key>
<key name="clienthome" datatype="existing-directory">
<description>
The directory used to store the file-storage used to back the
ZODB database by default, as well as other files used by the
Zope application server to control the run-time behavior.
</description>
<metadefault>$INSTANCE_HOME/var</metadefault>
</key>
<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="zserver-read-only-mode" datatype="boolean" default="off"
handler="zserver_read_only_mode">
<description>
If this variable is set, then the database is opened in read
only mode. If this variable is set to a string parsable by
DateTime.DateTime, then the database is opened read-only as of
the time given. Note that changes made by another process after
the database has been opened are not visible.
</description>
</key>
<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"/>
<section type="cgi-environment" attribute="cgi_environment" name="*"/>
<key name="dns-server" datatype=".dns_resolver" attribute="dns_resolver"/>
<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"/>
<key name="port-base" datatype="integer" default="0">
<description>
Base port number that gets added to the specific port numbers
specified for the individual servers.
</description>
</key>
<multisection type="database" name="*" attribute="databases"/>
<section type="zoperunner" name="*" attribute="runner"/>
</schema>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment