Commit e9f0f6f8 authored by Fred Drake's avatar Fred Drake

Merge startup code from the new-install-branch.

parent 5b55ca0d
This diff is collapsed.
##############################################################################
#
# 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