1*9c5db199SXin Li# -*- coding: utf-8 -*- 2*9c5db199SXin Li# Copyright (c) 2011-2012 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Li"""Signal related functionality.""" 7*9c5db199SXin Li 8*9c5db199SXin Lifrom __future__ import print_function 9*9c5db199SXin Li 10*9c5db199SXin Liimport signal 11*9c5db199SXin Liimport contextlib 12*9c5db199SXin Li 13*9c5db199SXin Li 14*9c5db199SXin Lidef RelaySignal(handler, signum, frame): 15*9c5db199SXin Li """Notify a listener returned from getsignal of receipt of a signal. 16*9c5db199SXin Li 17*9c5db199SXin Li Returns: 18*9c5db199SXin Li True if it was relayed to the target, False otherwise. 19*9c5db199SXin Li False in particular occurs if the target isn't relayable. 20*9c5db199SXin Li """ 21*9c5db199SXin Li if handler in (None, signal.SIG_IGN): 22*9c5db199SXin Li return True 23*9c5db199SXin Li elif handler == signal.SIG_DFL: 24*9c5db199SXin Li # This scenario is a fairly painful to handle fully, thus we just 25*9c5db199SXin Li # state we couldn't handle it and leave it to client code. 26*9c5db199SXin Li return False 27*9c5db199SXin Li handler(signum, frame) 28*9c5db199SXin Li return True 29*9c5db199SXin Li 30*9c5db199SXin Li 31*9c5db199SXin Lidef SignalModuleUsable(_signal=signal.signal, _SIGUSR1=signal.SIGUSR1): 32*9c5db199SXin Li """Verify that the signal module is usable and won't segfault on us. 33*9c5db199SXin Li 34*9c5db199SXin Li See http://bugs.python.org/issue14173. This function detects if the 35*9c5db199SXin Li signals module is no longer safe to use (which only occurs during 36*9c5db199SXin Li final stages of the interpreter shutdown) and heads off a segfault 37*9c5db199SXin Li if signal.* was accessed. 38*9c5db199SXin Li 39*9c5db199SXin Li This shouldn't be used by anything other than functionality that is 40*9c5db199SXin Li known and unavoidably invoked by finalizer code during python shutdown. 41*9c5db199SXin Li 42*9c5db199SXin Li Finally, the default args here are intentionally binding what we need 43*9c5db199SXin Li from the signal module to do the necessary test; invoking code shouldn't 44*9c5db199SXin Li pass any options, nor should any developer ever remove those default 45*9c5db199SXin Li options. 46*9c5db199SXin Li 47*9c5db199SXin Li Note that this functionality is intended to be removed just as soon 48*9c5db199SXin Li as all consuming code installs their own SIGTERM handlers. 49*9c5db199SXin Li """ 50*9c5db199SXin Li # Track any signals we receive while doing the check. 51*9c5db199SXin Li received, actual = [], None 52*9c5db199SXin Li def handler(signum, frame): 53*9c5db199SXin Li received.append([signum, frame]) 54*9c5db199SXin Li try: 55*9c5db199SXin Li # Play with sigusr1, since it's not particularly used. 56*9c5db199SXin Li actual = _signal(_SIGUSR1, handler) 57*9c5db199SXin Li _signal(_SIGUSR1, actual) 58*9c5db199SXin Li return True 59*9c5db199SXin Li except (TypeError, AttributeError, SystemError, ValueError): 60*9c5db199SXin Li # The first three exceptions can be thrown depending on the state of the 61*9c5db199SXin Li # signal module internal Handlers array; we catch all, and interpret it 62*9c5db199SXin Li # as if we were invoked during sys.exit cleanup. 63*9c5db199SXin Li # The last exception can be thrown if we're trying to be used in a thread 64*9c5db199SXin Li # which is not the main one. This can come up with standard python modules 65*9c5db199SXin Li # such as BaseHTTPServer.HTTPServer. 66*9c5db199SXin Li return False 67*9c5db199SXin Li finally: 68*9c5db199SXin Li # And now relay those signals to the original handler. Not all may 69*9c5db199SXin Li # be delivered- the first may throw an exception for example. Not our 70*9c5db199SXin Li # problem however. 71*9c5db199SXin Li for signum, frame in received: 72*9c5db199SXin Li actual(signum, frame) 73*9c5db199SXin Li 74*9c5db199SXin Li 75*9c5db199SXin Li@contextlib.contextmanager 76*9c5db199SXin Lidef DeferSignals(*args): 77*9c5db199SXin Li """Context Manger to defer signals during a critical block. 78*9c5db199SXin Li 79*9c5db199SXin Li If a signal comes in for the masked signals, the original handler 80*9c5db199SXin Li is ran after the critical block has exited. 81*9c5db199SXin Li 82*9c5db199SXin Li Args: 83*9c5db199SXin Li args: Which signals to ignore. If none are given, defaults to 84*9c5db199SXin Li SIGINT and SIGTERM. 85*9c5db199SXin Li """ 86*9c5db199SXin Li signals = args 87*9c5db199SXin Li if not signals: 88*9c5db199SXin Li signals = [signal.SIGINT, signal.SIGTERM, signal.SIGALRM] 89*9c5db199SXin Li 90*9c5db199SXin Li # Rather than directly setting the handler, we first pull the handlers, then 91*9c5db199SXin Li # set the new handler. The ordering has to be done this way to ensure that 92*9c5db199SXin Li # if someone passes in a bad signum (or a signal lands prior to starting the 93*9c5db199SXin Li # critical block), we can restore things to pristine state. 94*9c5db199SXin Li handlers = dict((signum, signal.getsignal(signum)) for signum in signals) 95*9c5db199SXin Li 96*9c5db199SXin Li received = [] 97*9c5db199SXin Li def handler(signum, frame): 98*9c5db199SXin Li received.append((signum, frame)) 99*9c5db199SXin Li 100*9c5db199SXin Li try: 101*9c5db199SXin Li for signum in signals: 102*9c5db199SXin Li signal.signal(signum, handler) 103*9c5db199SXin Li 104*9c5db199SXin Li yield 105*9c5db199SXin Li 106*9c5db199SXin Li finally: 107*9c5db199SXin Li for signum, original in handlers.items(): 108*9c5db199SXin Li signal.signal(signum, original) 109*9c5db199SXin Li 110*9c5db199SXin Li for signum, frame in received: 111*9c5db199SXin Li RelaySignal(handlers[signum], signum, frame) 112*9c5db199SXin Li 113*9c5db199SXin Li 114*9c5db199SXin Lidef StrSignal(sig_num): 115*9c5db199SXin Li """Convert a signal number to the symbolic name 116*9c5db199SXin Li 117*9c5db199SXin Li Note: Some signal number have multiple names, so you might get 118*9c5db199SXin Li back a confusing result like "SIGIOT|SIGABRT". Since they have 119*9c5db199SXin Li the same signal number, it's impossible to say which one is right. 120*9c5db199SXin Li 121*9c5db199SXin Li Args: 122*9c5db199SXin Li sig_num: The numeric signal you wish to convert 123*9c5db199SXin Li 124*9c5db199SXin Li Returns: 125*9c5db199SXin Li A string of the signal name(s) 126*9c5db199SXin Li """ 127*9c5db199SXin Li # Handle realtime signals first since they are unnamed. 128*9c5db199SXin Li if sig_num >= signal.SIGRTMIN and sig_num < signal.SIGRTMAX: 129*9c5db199SXin Li return 'SIGRT_%i' % sig_num 130*9c5db199SXin Li 131*9c5db199SXin Li # Probe the module looking for matching signal constant. 132*9c5db199SXin Li sig_names = [] 133*9c5db199SXin Li for name, num in signal.__dict__.items(): 134*9c5db199SXin Li if name.startswith('SIG') and num == sig_num: 135*9c5db199SXin Li sig_names.append(name) 136*9c5db199SXin Li if sig_names: 137*9c5db199SXin Li return '|'.join(sig_names) 138*9c5db199SXin Li else: 139*9c5db199SXin Li return 'SIG_%i' % sig_num 140