##############################################################################
#
# 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, time, signal
from ZDaemonLogging import pstamp
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
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\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)
# We'd like to report whether a core file
# was produced, but there isn't a standard
# way to check. It seems that some
# (many?) Unixes use bit 0x80 in the wait
# status, but how to tell? A simple
# alternative is to assume that no core
# file was produced if the wait status is
# exactly equal to the signal. Otherwise,
# there might be a core file and it's
# useful to print the wait status.
if signum != s:
msg += ", wait status: %s" % signum
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'):
_signals[v] = k
return _signals.get(n, 'unknown')
|