diff --git a/lib/utils.py b/lib/utils.py index ceacc9b5ea51f76738835b4f03e183c457427573..7548a7e15f26b06db0a6ba4f64b2c0014f953098 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -40,6 +40,7 @@ import select import fcntl import resource import logging +import signal from cStringIO import StringIO @@ -1094,3 +1095,71 @@ def LockFile(fd): if err.errno == errno.EAGAIN: raise errors.LockError("File already locked") raise + + +class SignalHandler(object): + """Generic signal handler class. + + It automatically restores the original handler when deconstructed or when + Reset() is called. You can either pass your own handler function in or query + the "called" attribute to detect whether the signal was sent. + + """ + def __init__(self, signum): + """Constructs a new SignalHandler instance. + + @param signum: Single signal number or set of signal numbers + + """ + if isinstance(signum, (int, long)): + self.signum = set([signum]) + else: + self.signum = set(signum) + + self.called = False + + self._previous = {} + try: + for signum in self.signum: + # Setup handler + prev_handler = signal.signal(signum, self._HandleSignal) + try: + self._previous[signum] = prev_handler + except: + # Restore previous handler + signal.signal(signum, prev_handler) + raise + except: + # Reset all handlers + self.Reset() + # Here we have a race condition: a handler may have already been called, + # but there's not much we can do about it at this point. + raise + + def __del__(self): + self.Reset() + + def Reset(self): + """Restore previous handler. + + """ + for signum, prev_handler in self._previous.items(): + signal.signal(signum, prev_handler) + # If successful, remove from dict + del self._previous[signum] + + def Clear(self): + """Unsets "called" flag. + + This function can be used in case a signal may arrive several times. + + """ + self.called = False + + def _HandleSignal(self, signum, frame): + """Actual signal handling function. + + """ + # This is not nice and not absolutely atomic, but it appears to be the only + # solution in Python -- there are no atomic types. + self.called = True