[Zodb-checkins] CVS: Zope/lib/python/zdaemon - SignalPasser.py:1.1.2.1 Daemon.py:1.2.2.1

Chris McDonough chrism@zope.com
Fri, 15 Mar 2002 14:36:04 -0500


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

Modified Files:
      Tag: chrism_logrotate_branch
	Daemon.py 
Added Files:
      Tag: chrism_logrotate_branch
	SignalPasser.py 
Log Message:
Patched up z2.py and some of asyncore to be able to deal with signals.

In this branch, Zope will:

- open and close all of its logfiles upon receving a USR2 signal

- restart "cleanly" upon receiving a HUP signal

- shutdown "cleanly" upon receiving a TERM or INT signal.



=== Added File Zope/lib/python/zdaemon/SignalPasser.py ===
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.2 => 1.2.2.1 ===
 ##############################################################################
 
-import os, sys, time, signal, posix
+import os, sys, time, posix, signal
+ALL_SIGNALS = ['SIGABRT', 'SIGALRM', 'SIGBUS', 'SIGCHLD', 'SIGCLD', 'SIGCONT', 'SIGFPE', 'SIGHUP', 'SIGILL', 'SIGINT', 'SIGIO', 'SIGIOT', 'SIGKILL', 'SIGPIPE', 'SIGPOLL', 'SIGPROF', 'SIGPWR', 'SIGQUIT', 'SIGSEGV', 'SIGSTOP', 'SIGSYS', 'SIGTERM', 'SIGTRAP', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU', 'SIGURG', 'SIGUSR1', 'SIGUSR2', 'SIGVTALRM', 'SIGWINCH', 'SIGXCPU', 'SIGXFSZ']
+
 
 from ZDaemonLogging import pstamp
 import Heartbeat
@@ -43,7 +45,9 @@
             pstamp('Houston, we have forked', zLOG.INFO)
             return pid
 
-def run(argv, pidfile=''):
+import signal
+
+def run(argv, pidfile='', signals=[]):
     if os.environ.has_key('ZDAEMON_MANAGED'):
         # We're the child at this point.
         return
@@ -66,7 +70,18 @@
                 raise ForkError
 
             elif pid:
-                # Parent 
+                # 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
                 if pidfile:
@@ -74,24 +89,44 @@
                     pf.write(("%s" % os.getpid()))
                     pf.close()
 
-                while 1: 
-                    if not Heartbeat.BEAT_DELAY:
-                        p,s = os.waitpid(pid, 0)
-                    else:
-                        p,s = os.waitpid(pid, os.WNOHANG)
-                        if not p:
-                            time.sleep(Heartbeat.BEAT_DELAY)
-                            Heartbeat.heartbeat()
-                            continue
-                    if s:
-                        pstamp(('Aiieee! %s exited with error code: %s' 
-                                % (p, s)), zLOG.ERROR)
-                    else:
-                        pstamp(('The kid, %s, died on me.' % pid),
-                               zLOG.WARNING)
-                        raise ForkError
-
-                    raise KidDiedOnMeError
+                while 1:
+                    try:
+                        if not Heartbeat.BEAT_DELAY:
+                            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:
+                            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()
+                                continue
+                        if s:
+                            pstamp(('Aiieee! %s exited with error code: %s' 
+                                    % (p, s)), zLOG.ERROR)
+                        else:
+                            pstamp(('The kid, %s, died on me.' % pid),
+                                   zLOG.WARNING)
+                            raise ForkError
+
+                        raise KidDiedOnMeError
+                    except OSError, num:
+                        if num == 4:
+                            # This is likely an interrupted system call
+                            # inside os.waitpid caused by a signal.
+                            # Though we have to catch it, we don't really
+                            # care about it and we just pass.
+                            pass
 
             else:
                 # Child