Commit 23aba32e authored by Fred Drake's avatar Fred Drake

Update from chrism-install-branch.

parent 6b523189
##############################################################################
#
# 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.2 $'[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()
import sys
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 ) )
import os
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
""" 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
)
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)
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
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)
##############################################################################
#
# 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.2 2003/01/29 20:25:17 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 """
_marker = []
def importer(name):
components = name.split('.')
start = components[0]
g = globals()
package = __import__(start, g, g)
modulenames = [start]
for component in components[1:]:
modulenames.append(component)
try:
package = getattr(package, component)
except AttributeError:
name = '.'.join(modulenames)
package = __import__(name, g, g, component)
return package
class Factory:
"""
A generic wrapper for instance construction and function calling used
to delay construction/call until necessary. The class path is the dotted
name to the class or function, args are the positional args, kw are the
keyword args. If it is specified, 'callback' is a function which will be
called back after constructing an instance or calling a function. It must
take the instance (or the result of the function) as a single argument.
"""
def __init__(self, class_path, callback, *args, **kw):
self.class_path = class_path
self.callback = callback
self.setArgs(list(args), kw)
self.resolved = _marker
def __repr__(self):
return ('<Factory instance for class "%s" with positional args "%s" '
'and keword args "%s"' % (self.class_path, self.args, self.kw))
__str__ = __repr__
def __call__(self):
if self.resolved is _marker:
package = importer(self.class_path)
inst = package(*self.args, **self.kw)
if self.callback:
self.callback(inst)
self.resolved = inst
return self.resolved
def setArgs(self, args, kw):
self.args = args
self.kw = kw
def getArgs(self):
return (self.args, self.kw)
##############################################################################
#
# 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
#
##############################################################################
""" Utilities for file locking """
try:
import fcntl
def lock_file(file):
un=file.fileno()
fcntl.flock(un, fcntl.LOCK_EX | fcntl.LOCK_NB)
except:
# Try windows-specific code:
try:
from ZODB.winlock import LockFile
def lock_file(file):
un=file.fileno()
LockFile(un,0,0,1,0) # just lock the first byte, who cares
except:
# we don't understand any kind of locking, forget it
def lock_file(file):
pass
#!/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.2 $ '[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) 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
#
##############################################################################
"""
ZServer as a NT service.
The serice starts up and monitors a ZServer process.
Features:
* When you start the service it starts ZServer
* When you stop the serivice it stops ZServer
* It monitors ZServer and restarts it if it exits abnormally
* If ZServer is shutdown from the web, the service stops.
* If ZServer cannot be restarted, the service stops.
Usage:
Installation
The ZServer service should be installed by the Zope Windows
installer. You can manually install, uninstall the service from
the commandline.
ZService.py [options] install|update|remove|start [...]
|stop|restart [...]|debug [...]
Options for 'install' and 'update' commands only:
--username domain\username : The Username the service is to run
under
--password password : The password for the username
--startup [manual|auto|disabled] : How the service starts,
default = manual
Commands
install : Installs the service
update : Updates the service, use this when you change
ZServer.py
remove : Removes the service
start : Starts the service, this can also be done from the
services control panel
stop : Stops the service, this can also be done from the
services control panel
restart : Restarts the service
debug : Runs the service in debug mode
You can view the usage options by running ZServer.py without any
arguments.
Note: you may have to register the Python service program first,
win32\pythonservice.exe /register
Starting Zope
Start Zope by clicking the 'start' button in the services control
panel. You can set Zope to automatically start at boot time by
choosing 'Auto' startup by clicking the 'statup' button.
Stopping Zope
Stop Zope by clicking the 'stop' button in the services control
panel. You can also stop Zope through the web by going to the
Zope control panel and by clicking 'Shutdown'.
Event logging
Zope events are logged to the NT application event log. Use the
event viewer to keep track of Zope events.
Registry Settings
You can change how the service starts ZServer by editing a registry
key.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\
<Service Name>\Parameters\start
The value of this key is the command which the service uses to
start ZServer. For example:
"C:\Program Files\Zope\bin\python.exe"
"C:\Program Files\Zope\z2.py" -w 8888
TODO:
* Integrate it into the Windows installer.
* Add ZLOG logging in addition to event log logging.
* Make it easier to run multiple Zope services with one Zope install
This script does for NT the same sort of thing zdaemon.py does for UNIX.
Requires Python win32api extensions.
"""
__version__ = '$Revision: 1.2 $'[11:-2]
import sys, os, time, imp, getopt
import win32api
def magic_import(modulename, filename):
# by Mark Hammond
try:
# See if it does import first!
return __import__(modulename)
except ImportError:
pass
# win32 can find the DLL name.
h = win32api.LoadLibrary(filename)
found = win32api.GetModuleFileName(h)
# Python can load the module
mod = imp.load_module(modulename, None, found, ('.dll', 'rb',
imp.C_EXTENSION))
# inject it into the global module list.
sys.modules[modulename] = mod
# And finally inject it into the namespace.
globals()[modulename] = mod
win32api.FreeLibrary(h)
magic_import('pywintypes','pywintypes21.dll')
import win32serviceutil, win32service, win32event, win32process
# servicemanager comes as a builtin if we're running via PythonService.exe,
# but it's not available outside
try:
import servicemanager
except:
pass
class NTService(win32serviceutil.ServiceFramework):
# Some trickery to determine the service name. The WISE
# installer will write an svcname.txt to the ZServer dir
# that we can use to figure out our service name.
restart_min_time=5 # if ZServer restarts before this many
# seconds then we have a problem, and
# need to stop the service.
_svc_name_= 'Zope'
_svc_display_name_ = _svc_name_
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcDoRun(self):
self.start_zserver()
while 1:
rc=win32event.WaitForMultipleObjects(
(self.hWaitStop, self.hZServer), 0, win32event.INFINITE)
if rc - win32event.WAIT_OBJECT_0 == 0:
break
else:
self.restart_zserver()
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING, 5000)
def SvcStop(self):
servicemanager.LogInfoMsg('Stopping Zope.')
try:
self.stop_zserver()
except:
pass
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def restart_zserver(self):
if time.time() - self.last_start_time < self.restart_min_time:
servicemanager.LogErrorMsg('Zope died and could not be restarted.')
self.SvcStop()
code=win32process.GetExitCodeProcess(self.hZServer)
if code == 0:
# Exited with a normal status code,
# assume that shutdown is intentional.
self.SvcStop()
else:
servicemanager.LogWarningMsg('Restarting Zope.')
self.start_zserver()
def start_zserver(self):
sc=self.get_start_command()
result=win32process.CreateProcess(None, sc,
None, None, 0, 0, None, None, win32process.STARTUPINFO())
self.hZServer=result[0]
self.last_start_time=time.time()
servicemanager.LogInfoMsg('Starting Zope.')
def stop_zserver(self):
try:
win32process.TerminateProcess(self.hZServer,0)
except:
pass
result=win32process.CreateProcess(None, self.get_stop_command(),
None, None, 0, 0, None, None, win32process.STARTUPINFO())
return result
def get_start_command(self):
return win32serviceutil.GetServiceCustomOption(self,'start', None)
def get_stop_command(self):
cmd = win32serviceutil.GetServiceCustomOption(self,'stop', None)
def set_start_command(value):
"sets the ZServer start command if the start command is not already set"
current=win32serviceutil.GetServiceCustomOption(NTService,
'start', None)
if current is None:
win32serviceutil.SetServiceCustomOption(NTService,'start',value)
def set_stop_command(value):
"sets the ZServer start command if the start command is not already set"
current=win32serviceutil.GetServiceCustomOption(NTService,
'stop', None)
if current is None:
win32serviceutil.SetServiceCustomOption(NTService,'stop',value)
if __name__=='__main__':
dn = os.path.dirname
zope_home = dn(dn(dn(dn(sys.argv[0]))))
win32serviceutil.HandleCommandLine(ZServerService)
if 'install' in args:
command='"%s" "%s"' % (sys.executable,
os.path.join(zope_home, 'bin', 'zope.py'))
set_start_command(command)
print "Setting Zope start command to:", command
""" Placeholder module file """
##############################################################################
#
# 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 unittest
import Zope.Startup
class StartupTestCase(unittest.TestCase):
def test_load_schema(self):
Zope.Startup.getSchema()
def test_suite():
return unittest.makeSuite(StartupTestCase)
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")
<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>
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