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

Chris McDonough chrism@zope.com
Sat, 15 Jun 2002 00:19:24 -0400


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

Modified Files:
	Daemon.py 
Log Message:
Cleaned up Daemon:

- Removed unused exceptions.

- Refactored fork code and mainloop.

- Removed dependencies on Heartbeat.  Nobody uses this.

- Introduced SignalPasser.

- Hardcoded interesting signals instead of relying on them being passed in.



=== Zope/lib/python/zdaemon/Daemon.py 1.9 => 1.10 ===
 ##############################################################################
 
-import os, sys, time, posix, signal
+import os, sys, time, signal
 from ZDaemonLogging import pstamp
-import Heartbeat
 import zLOG
+from SignalPasser import SignalPasser
 
 pyth = sys.executable
 
-class KidDiedOnMeError(Exception):
+class DieNow(Exception):
     pass
 
-class ExecError(Exception):
-    pass
-
-class ForkError(Exception):
-    pass
+def run(argv, pidfile=''):
+    if os.environ.has_key('ZDAEMON_MANAGED'):
+        # We're being run by the child.
+        return
 
-FORK_ATTEMPTS = 2
+    os.environ['ZDAEMON_MANAGED']='TRUE'
 
-def forkit(attempts = FORK_ATTEMPTS):
-    while attempts:
-        # if at first you don't succeed...
-        attempts = attempts - 1
+    if not os.environ.has_key('Z_DEBUG_MODE'):
+        detach() # detach from the controlling terminal
+    
+    while 1:
         try:
             pid = os.fork()
-        except os.error:
-            pstamp('Houston, the fork failed', zLOG.ERROR)
-            time.sleep(2)
+            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(sig))
+                pstamp('Houston, we have forked: pid %s' % pid, zLOG.INFO)
+                write_pidfile(pidfile)
+                p,s = wait(pid) # waitpid will block until child exit
+                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)
+                    log_pid(p, s)
+                    continue
+                else:
+                    # 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" % 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:
-            pstamp('Houston, we have forked', zLOG.INFO)
-            return pid
+            return p, s
 
 def log_pid(p, s):
     if os.WIFEXITED(s):
@@ -72,99 +123,6 @@
                                             signum)
     pstamp('Aiieee! Process %s %s' % (p, msg),
            zLOG.ERROR)
-    
-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
-    
-    os.environ['ZDAEMON_MANAGED']='TRUE'
-    
-    if not os.environ.has_key('Z_DEBUG_MODE'):
-        # Detach from 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()
-
-    while 1:
-
-        try:
-            pid = forkit()
-
-            if pid is None:
-                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
-                if pidfile:
-                    pf = open(pidfile, 'w+')
-                    pf.write(("%s" % os.getpid()))
-                    pf.close()
-
-                while 1: 
-                    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:
-                        log_pid(p, s)
-                    else:
-                        pstamp(('The kid, %s, died on me.' % pid),
-                               zLOG.WARNING)
-                        raise ForkError
-
-                    raise KidDiedOnMeError
-
-            else:
-                # Child
-                if __debug__:
-                    # non optimized
-                    os.execv(pyth, (pyth,) + tuple(argv))
-                else:
-                    # optimized
-                    os.execv(pyth, (pyth, '-O') + tuple(argv))
-
-        except ExecError:
-            sys.exit()
-        except ForkError:
-            sys.exit()
-        except KidDiedOnMeError:
-            pass
 
 _signals = None