xref: /aosp_15_r20/external/autotest/utils/frozen_chromite/lib/signals.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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