[Zodb-checkins] CVS: Zope/lib/python/zdaemon - SignalPasser.py:1.2 Daemon.py:1.9
Chris McDonough
chrism@zope.com
Tue, 11 Jun 2002 18:02:06 -0400
Update of /cvs-repository/Zope/lib/python/zdaemon
In directory cvs.zope.org:/tmp/cvs-serv24742
Modified Files:
Daemon.py
Added Files:
SignalPasser.py
Log Message:
These patches provide clean signal handling and logfile rotation to Zope.
All Zope process will respond to signals in the specified manner:
SIGHUP - close open database connections and sockets, then restart the
process
SIGTERM - close open database connections and sockets, then shut down.
SIGINT - same as SIGTERM
SIGUSR2 - rotate all Zope log files (z2.log, event log, detailed log)
The common idiom for doing automated logfile rotation will become:
kill -USR2 `cat /path/to/var/z2.pid`
The common idiom for doing "prophylactic" restarts will become:
kill -HUP `cat /path/to/var/z2.pid`
When a process is interrupted via ctrl-C or via a TERM signal (INT, TERM),
all open database connections and sockets will be closed before
the process dies. This will speed up restart time for sites that
use a FileStorage as its index will be written to the filesystem before
shutdown.
Unspecified signals kill the process without doing cleanup.
=== Zope/lib/python/zdaemon/SignalPasser.py 1.1 => 1.2 ===
+#
+# 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
+#
+##############################################################################
+
+""" A module used for passing signals to children """
+
+class SignalPasser:
+ def __init__(self, pid):
+ self.pid = pid
+
+ def __call__(self, signum, frame):
+ import os, sys, signal
+ os.kill(self.pid, signum)
+ if signum in [signal.SIGTERM, signal.SIGINT]:
+ sys.exit(0)
+
+def pass_signals_to_process(pid, signals):
+ import signal
+ for s in signals:
+ signal.signal(s, SignalPasser(pid))
=== Zope/lib/python/zdaemon/Daemon.py 1.8 => 1.9 ===
##############################################################################
-import os, sys, time, signal
-
+import os, sys, time, posix, signal
from ZDaemonLogging import pstamp
import Heartbeat
import zLOG
@@ -74,7 +73,9 @@
pstamp('Aiieee! Process %s %s' % (p, msg),
zLOG.ERROR)
-def run(argv, pidfile=''):
+def run(argv, pidfile='', signals=None):
+ if signals is None:
+ signals = []
if os.environ.has_key('ZDAEMON_MANAGED'):
# We're the child at this point.
return
@@ -100,6 +101,17 @@
raise ForkError
elif pid:
+ # the process we're daemoning for can signify that it
+ # wants us to notify it when we get specific signals
+ #
+ #
+ # we always register TERM and INT so we can reap our child.
+ signals = signals + [signal.SIGTERM, signal.SIGINT]
+ # TERM happens on normal kill
+ # INT happens on Ctrl-C (debug mode)
+ import SignalPasser
+ SignalPasser.pass_signals_to_process(pid, signals)
+
# Parent
pstamp(('Hi, I just forked off a kid: %s' % pid), zLOG.INFO)
# here we want the pid of the parent
@@ -110,9 +122,21 @@
while 1:
if not Heartbeat.BEAT_DELAY:
- p, s = os.waitpid(pid, 0)
+ try:
+ p,s = os.waitpid(pid, 0)
+ except OSError:
+ # catch EINTR, it's raised as a result of
+ # interrupting waitpid with a signal
+ # and we don't care about it.
+ continue
else:
- p, s = os.waitpid(pid, os.WNOHANG)
+ try:
+ p,s = os.waitpid(pid, os.WNOHANG)
+ except OSError:
+ # catch EINTR, it's raised as a result of
+ # interrupting waitpid with a signal
+ # and we don't care about it.
+ p, s = None, None
if not p:
time.sleep(Heartbeat.BEAT_DELAY)
Heartbeat.heartbeat()