[Zodb-checkins] CVS: Zope/lib/python/zdaemon - Daemon.py:1.21

Jim Fulton cvs-admin at zope.org
Fri Nov 28 11:45:29 EST 2003


Update of /cvs-repository/Zope/lib/python/zdaemon
In directory cvs.zope.org:/tmp/cvs-serv3783/lib/python/zdaemon

Added Files:
	Daemon.py 
Log Message:
Merged Jeremy and Tim's changes from the zodb33-devel-branch.


=== Zope/lib/python/zdaemon/Daemon.py 1.20 => 1.21 ===
--- /dev/null	Fri Nov 28 11:45:29 2003
+++ Zope/lib/python/zdaemon/Daemon.py	Fri Nov 28 11:44:58 2003
@@ -0,0 +1,150 @@
+##############################################################################
+#
+# 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
+#
+##############################################################################
+
+import os, sys, signal
+import zLOG
+
+pyth = sys.executable
+
+class DieNow(Exception):
+    pass
+
+class SignalPasser:
+    """ A class used for passing signal that the daemon receives along to
+    its child """
+    def __init__(self, pid):
+        self.pid = pid
+
+    def __call__(self, signum, frame):
+        # send the signal to our child
+        os.kill(self.pid, signum)
+        # we want to die ourselves if we're signaled with SIGTERM or SIGINT
+        if signum in [signal.SIGTERM, signal.SIGINT]:
+            raise DieNow
+
+def run(argv, pidfile=''):
+    if os.environ.has_key('ZDAEMON_MANAGED'):
+        # We're being run by the child.
+        return
+
+    os.environ['ZDAEMON_MANAGED']='TRUE'
+
+    if not os.environ.has_key('Z_DEBUG_MODE'):
+        detach() # detach from the controlling terminal
+
+    while 1:
+        try:
+            pid = os.fork()
+            if pid:
+                # We're the parent (the daemon process)
+                # pass all "normal" signals along to our child, but don't
+                # respond to them ourselves unless they say "die"!
+                interesting = [1, 2, 3, 10, 12, 15]
+                # ie. HUP, INT, QUIT, USR1, USR2, TERM
+                for sig in interesting:
+                    signal.signal(sig, SignalPasser(pid))
+                pstamp('Started subprocess: pid %s' % pid, zLOG.INFO)
+                write_pidfile(pidfile)
+                p, s = wait(pid) # waitpid will block until child exit
+                log_pid(p, s)
+                if s:
+                    # continue and restart because our child died
+                    # with a nonzero exit code, meaning he bit it in
+                    # an unsavory way (likely a segfault or something)
+                    continue
+                else:
+                    pstamp("zdaemon exiting", zLOG.INFO)
+                    # no need to restart, our child wanted to die.
+                    raise DieNow
+
+            else:
+                # we're the child (Zope/ZEO)
+                args = [pyth]
+                if not __debug__:
+                    # we're running in optimized mode
+                    args.append('-O')
+                os.execv(pyth, tuple(args) + tuple(argv))
+
+        except DieNow:
+            sys.exit()
+
+def detach():
+    # do the funky chicken dance to detach from the terminal
+    pid = os.fork()
+    if pid: sys.exit(0)
+    os.close(0); sys.stdin  = open('/dev/null')
+    os.close(1); sys.stdout = open('/dev/null','w')
+    os.close(2); sys.stderr = open('/dev/null','w')
+    os.setsid()
+
+def write_pidfile(pidfile):
+    if pidfile:
+        pf = open(pidfile, 'w+')
+        pf.write(("%s\n" % os.getpid()))
+        pf.close()
+
+def wait(pid):
+    while 1:
+        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:
+            return p, s
+
+def log_pid(p, s):
+    if os.WIFEXITED(s):
+        es = os.WEXITSTATUS(s)
+        msg = "terminated normally, exit status: %s" % es
+    elif os.WIFSIGNALED(s):
+        signum = os.WTERMSIG(s)
+        signame = get_signal_name(signum)
+        msg = "terminated by signal %s(%s)" % (signame, signum)
+        if hasattr(os, 'WCOREDUMP'):
+            iscore = os.WCOREDUMP(s)
+        else:
+            iscore = s & 0x80
+        if iscore:
+            msg += " (core dumped)"
+    else:
+        # XXX what should we do here?
+        signum = os.WSTOPSIG(s)
+        signame = get_signal_name(signum)
+        msg = "stopped by signal %s(%s)" % (signame, signum)
+    pstamp('Process %s %s' % (p, msg), zLOG.ERROR)
+
+_signals = None
+
+def get_signal_name(n):
+    """Return the symbolic name for signal n.
+
+    Returns 'unknown' if there is no SIG name bound to n in the signal
+    module.
+    """
+    global _signals
+    if _signals is None:
+        _signals = {}
+        for k, v in signal.__dict__.items():
+            startswith = getattr(k, 'startswith', None)
+            if startswith is None:
+                continue
+            if startswith('SIG') and not startswith('SIG_'):
+                _signals[v] = k
+    return _signals.get(n, 'unknown')
+
+def pstamp(message, sev):
+    zLOG.LOG("zdaemon", sev, message)




More information about the Zodb-checkins mailing list