[Zodb-checkins] SVN: ZODB/branches/3.4/ Port ZEO changes for
Windows from ZODB 3.2.
Tim Peters
tim.one at comcast.net
Fri Apr 22 18:34:31 EDT 2005
Log message for revision 30120:
Port ZEO changes for Windows from ZODB 3.2.
In addition, repaired the useless default logging in runzeo.py.
Here's Sidnei's Zope 2.7 checkin comment:
* Borrow Zope's 'Signal' mechanism for Windows, if available, to
implement clean shutdown and log rotation handlers for Windows.
* Back to creating a .PID for ZEO, so external programs that wish to set
the 'signal' can get the PID and therefore derive the signal name.
Currently only necessary on Windows but created on all platforms which
implement os.getpid(), as long as the 'pid-filename' option is set,
or the 'INSTANCE_HOME' environment variable can be found.
Changed:
U ZODB/branches/3.4/NEWS.txt
U ZODB/branches/3.4/src/ZEO/component.xml
U ZODB/branches/3.4/src/ZEO/mkzeoinst.py
U ZODB/branches/3.4/src/ZEO/runzeo.py
-=-
Modified: ZODB/branches/3.4/NEWS.txt
===================================================================
--- ZODB/branches/3.4/NEWS.txt 2005-04-22 21:15:00 UTC (rev 30119)
+++ ZODB/branches/3.4/NEWS.txt 2005-04-22 22:34:30 UTC (rev 30120)
@@ -2,6 +2,33 @@
==========================
Release date: DD-MMM-2005
+ZEO
+---
+
+The default logging setup in ``runzeo.py`` was broken. It was changed
+so that running ``runzeo.py`` from a command line now, and without using
+a config file, prints output to the console much as ZODB 3.2 did.
+
+ZEO on Windows
+--------------
+
+Thanks to Mark Hammond for these ``runzeo.py`` enhancements on Windows:
+
+- A pid file (containing the process id as a decimal string) is created now
+ for a ZEO server started via ``runzeo.py``. External programs can
+ read the pid from this file and derive a "signal name" used in a new
+ signal-emulation scheme for Windows. This is only necessary on Windows,
+ but the pid file is created on all platforms that implement
+ ``os.getpid()``, as long as the ``pid-filename`` option is set, or
+ environment variable ``INSTANCE_HOME`` is defined. The ``pid-filename``
+ option can be set in a ZEO config file, or passed as the new ``--pid-file``
+ argument to ``runzeo.py``.
+
+- If available, ``runzeo.py`` now uses Zope's new 'Signal' mechanism for
+ Windows, to implement clean shutdown and log rotation handlers for Windows.
+ Note that the Python in use on the ZEO server must also have the Python
+ Win32 extensions installed for this to be useful.
+
DemoStorage
-----------
Modified: ZODB/branches/3.4/src/ZEO/component.xml
===================================================================
--- ZODB/branches/3.4/src/ZEO/component.xml 2005-04-22 21:15:00 UTC (rev 30119)
+++ ZODB/branches/3.4/src/ZEO/component.xml 2005-04-22 22:34:30 UTC (rev 30120)
@@ -93,6 +93,15 @@
</description>
</key>
+ <key name="pid-filename" datatype="existing-dirpath"
+ required="no">
+ <description>
+ The full path to the file in which to write the ZEO server's Process ID
+ at startup. If omitted, $INSTANCE/var/ZEO.pid is used.
+ </description>
+ <metadefault>$INSTANCE/var/ZEO.pid (or $clienthome/ZEO.pid)</metadefault>
+ </key>
+
</sectiontype>
</component>
Modified: ZODB/branches/3.4/src/ZEO/mkzeoinst.py
===================================================================
--- ZODB/branches/3.4/src/ZEO/mkzeoinst.py 2005-04-22 21:15:00 UTC (rev 30119)
+++ ZODB/branches/3.4/src/ZEO/mkzeoinst.py 2005-04-22 22:34:30 UTC (rev 30120)
@@ -47,6 +47,7 @@
address %(port)d
read-only false
invalidation-queue-size 100
+ # pid-filename $INSTANCE/var/ZEO.pid
# monitor-address PORT
# transaction-timeout SECONDS
</zeo>
Modified: ZODB/branches/3.4/src/ZEO/runzeo.py
===================================================================
--- ZODB/branches/3.4/src/ZEO/runzeo.py 2005-04-22 21:15:00 UTC (rev 30119)
+++ ZODB/branches/3.4/src/ZEO/runzeo.py 2005-04-22 22:34:30 UTC (rev 30120)
@@ -24,6 +24,9 @@
-t/--timeout TIMEOUT -- transaction timeout in seconds (default no timeout)
-h/--help -- print this usage message and exit
-m/--monitor ADDRESS -- address of monitor server ([HOST:]PORT or PATH)
+--pid-file PATH -- relative path to output file containing this process's pid;
+ default $(INSTANCE_HOME)/var/ZEO.pid but only if envar
+ INSTANCE_HOME is defined
Unless -C is specified, -a and -f are required.
"""
@@ -50,12 +53,15 @@
message = "(%s) %s" % (_pid, msg)
logger.log(level, message, exc_info=exc_info)
-
def parse_address(arg):
# Caution: Not part of the official ZConfig API.
obj = ZConfig.datatypes.SocketAddress(arg)
return obj.family, obj.address
+def windows_shutdown_handler():
+ # Called by the signal mechanism on Windows to perform shutdown.
+ import asyncore
+ asyncore.close_all()
class ZEOOptionsMixin:
@@ -104,6 +110,8 @@
None, 'auth-database=')
self.add('auth_realm', 'zeo.authentication_realm',
None, 'auth-realm=')
+ self.add('pid_file', 'zeo.pid_filename',
+ None, 'pid-file=')
class ZEOOptions(ZDOptions, ZEOOptionsMixin):
@@ -126,6 +134,7 @@
self.setup_default_logging()
self.check_socket()
self.clear_socket()
+ self.make_pidfile()
try:
self.open_storages()
self.setup_signals()
@@ -134,15 +143,20 @@
finally:
self.close_storages()
self.clear_socket()
+ self.remove_pidfile()
def setup_default_logging(self):
if self.options.config_logger is not None:
return
# No log file is configured; default to stderr.
- logger = logging.getLogger()
+ root = logging.getLogger()
+ root.setLevel(logging.INFO)
+ fmt = logging.Formatter(
+ "------\n%(asctime)s %(levelname)s %(name)s %(message)s",
+ "%Y-%m-%dT%H:%M:%S")
handler = logging.StreamHandler()
- handler.setLevel(logging.INFO)
- logger.addHandler(handler)
+ handler.setFormatter(fmt)
+ root.addHandler(handler)
def check_socket(self):
if self.can_connect(self.options.family, self.options.address):
@@ -182,6 +196,8 @@
method is called without additional arguments.
"""
if os.name != "posix":
+ if os.name == "nt":
+ self.setup_win32_signals()
return
if hasattr(signal, 'SIGXFSZ'):
signal.signal(signal.SIGXFSZ, signal.SIG_IGN) # Special case
@@ -193,6 +209,27 @@
method()
signal.signal(sig, wrapper)
+ def setup_win32_signals(self):
+ # Borrow the Zope Signals package win32 support, if available.
+ # Signals does a check/log for the availability of pywin32.
+ try:
+ import Signals.Signals
+ except ImportError:
+ logger.debug("Signals package not found. "
+ "Windows-specific signal handler "
+ "will *not* be installed.")
+ return
+ SignalHandler = Signals.Signals.SignalHandler
+ if SignalHandler is not None: # may be None if no pywin32.
+ SignalHandler.registerHandler(signal.SIGTERM,
+ windows_shutdown_handler)
+ SignalHandler.registerHandler(signal.SIGINT,
+ windows_shutdown_handler)
+ # Can use the log rotate handler too.
+ from Signals.Signals import logfileRotateHandler
+ SIGUSR2 = 12 # not in signal module on Windows.
+ SignalHandler.registerHandler(SIGUSR2, logfileRotateHandler)
+
def create_server(self):
from ZEO.StorageServer import StorageServer
self.server = StorageServer(
@@ -237,7 +274,53 @@
log("failed to close storage %r" % name,
level=logging.EXCEPTION, exc_info=True)
+ def _get_pidfile(self):
+ pidfile = self.options.pid_file
+ # 'pidfile' is marked as not required.
+ if not pidfile:
+ # Try to find a reasonable location if the pidfile is not
+ # set. If we are running in a Zope environment, we can
+ # safely assume INSTANCE_HOME.
+ instance_home = os.environ.get("INSTANCE_HOME")
+ if not instance_home:
+ # If all our attempts failed, just log a message and
+ # proceed.
+ logger.debug("'pidfile' option not set, and 'INSTANCE_HOME' "
+ "environment variable could not be found. "
+ "Cannot guess pidfile location.")
+ return
+ self.options.pid_file = os.path.join(instance_home,
+ "var", "ZEO.pid")
+ def make_pidfile(self):
+ if not self.options.read_only:
+ self._get_pidfile()
+ pidfile = self.options.pid_file
+ if pidfile is None:
+ return
+ pid = os.getpid()
+ try:
+ if os.path.exists(pidfile):
+ os.unlink(pidfile)
+ f = open(pidfile, 'w')
+ print >> f, pid
+ f.close()
+ log("created PID file '%s'" % pidfile)
+ except IOError:
+ logger.error("PID file '%s' cannot be opened" % pidfile)
+
+ def remove_pidfile(self):
+ if not self.options.read_only:
+ pidfile = self.options.pid_file
+ if pidfile is None:
+ return
+ try:
+ if os.path.exists(pidfile):
+ os.unlink(pidfile)
+ log("removed PID file '%s'" % pidfile)
+ except IOError:
+ logger.error("PID file '%s' could not be removed" % pidfile)
+
# Signal names
signames = None
More information about the Zodb-checkins
mailing list