MythTV  master
signalhandling.cpp
Go to the documentation of this file.
1 #include <QtGlobal>
2 #include <QObject>
3 #include <QSocketNotifier>
4 #include <QCoreApplication>
5 #include <QList>
6 
7 #include <csignal>
8 #include <cstdint>
9 #include <cstdlib> // for free
10 #include <iostream>
11 #include <string>
12 #include <sys/types.h>
13 #include <unistd.h>
14 #ifndef _WIN32
15 #include <sys/socket.h>
16 #endif
17 
18 #include "compat.h"
19 #include "mythlogging.h"
20 #include "loggingserver.h"
21 #include "exitcodes.h"
22 #include "signalhandling.h"
23 
24 std::array<int,2> SignalHandler::s_sigFd;
25 volatile bool SignalHandler::s_exit_program = false;
28 
29 static const std::array<const int, 6
30 #ifndef _WIN32
31  + 1
32 #ifndef Q_OS_DARWIN
33  + 1
34 #endif // Q_OS_DARWIN
35 #endif // _WIN32
37 {
38  SIGINT, SIGTERM, SIGSEGV, SIGABRT, SIGFPE, SIGILL,
39 #ifndef _WIN32
40  SIGBUS,
41 #ifndef Q_OS_DARWIN
42  SIGRTMIN, // not necessarily constexpr
43 #endif // Q_OS_DARWIN
44 #endif // _WIN32
45 };
46 
47 // We may need to write out signal info using just the write() function
48 // so we create an array of C strings + measure their lengths.
49 static constexpr size_t SIG_STR_COUNT { 256 };
50 std::array<std::string,SIG_STR_COUNT> sig_str;
51 
52 static void sig_str_init(size_t sig, const char *name)
53 {
54  if (sig >= sig_str.size())
55  return;
56 
57  sig_str[sig] = qPrintable(QString("Handling %1\n").arg(name));
58 }
59 
60 static void sig_str_init(void)
61 {
62  for (size_t i = 0; i < sig_str.size(); i++)
63  sig_str_init(i, qPrintable(QString("Signal %1").arg(i)));
64 }
65 
66 SignalHandler::SignalHandler(QObject *parent) :
67  QObject(parent)
68 {
69  s_exit_program = false; // set here due to "C++ static initializer madness"
70  sig_str_init();
71 
72 #ifndef _WIN32
73  //NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
74  m_sigStack = new char[SIGSTKSZ];
75  stack_t stack;
76  stack.ss_sp = m_sigStack;
77  stack.ss_flags = 0;
78  stack.ss_size = SIGSTKSZ;
79 
80  // Carry on without the signal stack if it fails
81  if (sigaltstack(&stack, nullptr) == -1)
82  {
83  std::cerr << "Couldn't create signal stack!" << std::endl;
84  delete [] m_sigStack;
85  m_sigStack = nullptr;
86  }
87 
88  if (::socketpair(AF_UNIX, SOCK_STREAM, 0, s_sigFd.data()))
89  {
90  std::cerr << "Couldn't create socketpair" << std::endl;
91  return;
92  }
93  m_notifier = new QSocketNotifier(s_sigFd[1], QSocketNotifier::Read, this);
94  connect(m_notifier, &QSocketNotifier::activated, this, &SignalHandler::handleSignal);
95 
96  for (const int signum : kDefaultSignalList)
97  {
98  SetHandlerPrivate(signum, nullptr);
99  }
100  SetHandlerPrivate(SIGHUP, logSigHup);
101 #endif // _WIN32
102 }
103 
105 {
106  s_singleton = nullptr;
107 
108 #ifndef _WIN32
109  if (m_notifier)
110  {
111  ::close(s_sigFd[0]);
112  ::close(s_sigFd[1]);
113  delete m_notifier;
114  }
115 
116  QMutexLocker locker(&m_sigMapLock);
117  // NOLINTNEXTLINE(modernize-loop-convert)
118  for (auto it = m_sigMap.begin(); it != m_sigMap.end(); ++it)
119  {
120  int signum = it.key();
121  signal(signum, SIG_DFL);
122  }
123 
124  m_sigMap.clear();
125 #endif
126 }
127 
128 void SignalHandler::Init(QObject *parent)
129 {
130  QMutexLocker locker(&s_singletonLock);
131  if (!s_singleton)
132  s_singleton = new SignalHandler(parent);
133 }
134 
136 {
137  QMutexLocker locker(&s_singletonLock);
138  delete s_singleton;
139 }
140 
141 
142 void SignalHandler::SetHandler(int signum, SigHandlerFunc handler)
143 {
144  QMutexLocker locker(&s_singletonLock);
145  if (s_singleton)
146  s_singleton->SetHandlerPrivate(signum, handler);
147 }
148 
149 void SignalHandler::SetHandlerPrivate([[maybe_unused]] int signum,
150  [[maybe_unused]] SigHandlerFunc handler)
151 {
152 #ifndef _WIN32
153  const char *signame = strsignal(signum);
154  QString signal_name = signame ?
155  QString(signame) : QString("Unknown(%1)").arg(signum);
156 
157  bool sa_handler_already_set = false;
158  {
159  QMutexLocker locker(&m_sigMapLock);
160  sa_handler_already_set = m_sigMap.contains(signum);
161  if (m_sigMap.value(signum, nullptr) && (handler != nullptr))
162  {
163  LOG(VB_GENERAL, LOG_WARNING,
164  QString("Warning %1 signal handler overridden")
165  .arg(signal_name));
166  }
167  m_sigMap[signum] = handler;
168  }
169 
170  if (!sa_handler_already_set)
171  {
172  struct sigaction sa {};
173  sa.sa_sigaction = SignalHandler::signalHandler;
174  sigemptyset(&sa.sa_mask);
175  sa.sa_flags = SA_RESTART | SA_SIGINFO;
176  if (m_sigStack)
177  sa.sa_flags |= SA_ONSTACK;
178 
179  sig_str_init(signum, qPrintable(signal_name));
180 
181  sigaction(signum, &sa, nullptr);
182  }
183 
184  LOG(VB_GENERAL, LOG_INFO, QString("Setup %1 handler").arg(signal_name));
185 #endif
186 }
187 
188 struct SignalInfo {
189  int m_signum;
190  int m_code;
191  int m_pid;
192  int m_uid;
193  uint64_t m_value;
194 };
195 
197  [[maybe_unused]] siginfo_t *info,
198  [[maybe_unused]] void *context)
199 {
200  SignalInfo signalInfo {};
201 
202  signalInfo.m_signum = signum;
203 #ifdef _WIN32
204  signalInfo.m_code = 0;
205  signalInfo.m_pid = 0;
206  signalInfo.m_uid = 0;
207  signalInfo.m_value = 0;
208 #else
209  signalInfo.m_code = (info ? info->si_code : 0);
210  signalInfo.m_pid = (info ? (int)info->si_pid : 0);
211  signalInfo.m_uid = (info ? (int)info->si_uid : 0);
212  signalInfo.m_value = (info ? info->si_value.sival_int : 0);
213 #endif
214 
215  // Keep trying if there's no room to write, but stop on error (-1)
216  int index = 0;
217  int size = sizeof(SignalInfo);
218  char *buffer = (char *)&signalInfo;
219  do {
220  int written = ::write(s_sigFd[0], &buffer[index], size);
221  // If there's an error, the signal will not be seen be the application,
222  // but we can't keep trying.
223  if (written < 0)
224  break;
225  index += written;
226  size -= written;
227  } while (size > 0);
228 
229  // One must not return from SEGV, ILL, BUS or FPE. When these
230  // are raised by the program itself they will immediately get
231  // re-raised on return.
232  //
233  // We also handle SIGABRT the same way. While it is safe to
234  // return from the signal handler for SIGABRT doing so means
235  // SIGABRT will fail when the UI thread is deadlocked; but
236  // SIGABRT is the signal one uses to get a core of a
237  // deadlocked program.
238  switch (signum)
239  {
240  case SIGSEGV:
241  case SIGILL:
242 #ifndef _WIN32
243  case SIGBUS:
244 #endif
245  case SIGFPE:
246  case SIGABRT:
247  // clear the signal handler so if it reoccurs we get instant death.
248  signal(signum, SIG_DFL);
249 
250  // Wait for UI event loop to handle this, however we may be
251  // blocking it if this signal occured in the UI thread.
252  // Note, can not use usleep() as it is not a signal safe function.
253  sleep(1);
254 
255  if (!s_exit_program)
256  {
257  // log something to console.. regular logging should be kaput
258  // NOTE: This needs to use write rather than cout or printf as
259  // we need to stick to system calls that are known to be
260  // signal-safe. write is, the other two aren't.
261  int d = 0;
262  if (signum < static_cast<int>(sig_str.size()))
263  d+=::write(STDERR_FILENO, sig_str[signum].c_str(), sig_str[signum].size());
264  (void) d; // quiet ignoring return value warning.
265  }
266 
267  // call the default signal handler. This will kill the application.
268  raise(signum);
269  break;
270  case SIGINT:
271  case SIGTERM:
272  // clear the signal handler so if it reoccurs we get instant death.
273  signal(signum, SIG_DFL);
274  break;
275  }
276 }
277 
279 {
280 #ifndef _WIN32
281  m_notifier->setEnabled(false);
282 
283  SignalInfo signalInfo {};
284  int ret = ::read(s_sigFd[1], &signalInfo, sizeof(SignalInfo));
285  bool infoComplete = (ret == sizeof(SignalInfo));
286  int signum = (infoComplete ? signalInfo.m_signum : 0);
287 
288  if (infoComplete)
289  {
290  QString signame = strsignal(signum);
291  if (signame.isEmpty())
292  signame = "Unknown Signal";
293  LOG(VB_GENERAL, LOG_CRIT,
294  QString("Received %1: Code %2, PID %3, UID %4, Value 0x%5")
295  .arg(signame) .arg(signalInfo.m_code) .arg(signalInfo.m_pid)
296  .arg(signalInfo.m_uid) .arg(signalInfo.m_value,8,16,QChar('0')));
297  }
298 
299  SigHandlerFunc handler = nullptr;
300  bool allowNullHandler = false;
301 
302 #ifndef Q_OS_DARWIN
303  if (signum == SIGRTMIN)
304  {
305  // glibc idiots seem to have made SIGRTMIN a macro that expands to a
306  // function, so we can't do this in the switch below.
307  // This uses the default handler to just get us here and to ignore it.
308  allowNullHandler = true;
309  }
310 #endif // Q_OS_DARWIN
311 
312  switch (signum)
313  {
314  case SIGINT:
315  case SIGTERM:
316  m_sigMapLock.lock();
317  handler = m_sigMap.value(signum, nullptr);
318  m_sigMapLock.unlock();
319 
320  if (handler)
321  handler();
322  else
323  QCoreApplication::exit(0);
324  s_exit_program = true;
325  break;
326  case SIGSEGV:
327  case SIGABRT:
328  case SIGBUS:
329  case SIGFPE:
330  case SIGILL:
331  usleep(100000);
332  s_exit_program = true;
333  break;
334  default:
335  m_sigMapLock.lock();
336  handler = m_sigMap.value(signum, nullptr);
337  m_sigMapLock.unlock();
338  if (handler)
339  {
340  handler();
341  }
342  else if (!allowNullHandler)
343  {
344  LOG(VB_GENERAL, LOG_CRIT, QString("Received unexpected signal %1")
345  .arg(signum));
346  }
347  break;
348  }
349 
350  m_notifier->setEnabled(true);
351 #endif // _WIN32
352 }
353 
354 /*
355  * vim:ts=4:sw=4:ai:et:si:sts=4
356  */
SignalHandler::s_sigFd
static std::array< int, 2 > s_sigFd
Definition: signalhandling.h:51
SignalHandler::SignalHandler
SignalHandler(QObject *parent)
Definition: signalhandling.cpp:66
SignalHandler::m_notifier
QSocketNotifier * m_notifier
Definition: signalhandling.h:53
SigHandlerFunc
void(*)(void) SigHandlerFunc
Definition: signalhandling.h:23
SignalHandler
A container object to handle UNIX signals in the Qt space correctly.
Definition: signalhandling.h:26
SignalInfo::m_signum
int m_signum
Definition: signalhandling.cpp:189
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
SignalHandler::m_sigMapLock
QMutex m_sigMapLock
Definition: signalhandling.h:56
SignalHandler::s_singletonLock
static QMutex s_singletonLock
Definition: signalhandling.h:59
sig_str
std::array< std::string, SIG_STR_COUNT > sig_str
Definition: signalhandling.cpp:50
mythburn.write
def write(text, progress=True)
Definition: mythburn.py:308
SignalHandler::handleSignal
void handleSignal(void)
Definition: signalhandling.cpp:278
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
SignalHandler::s_exit_program
static volatile bool s_exit_program
Definition: signalhandling.h:52
SignalInfo::m_value
uint64_t m_value
Definition: signalhandling.cpp:193
SignalInfo::m_uid
int m_uid
Definition: signalhandling.cpp:192
close
#define close
Definition: compat.h:43
SignalHandler::SetHandlerPrivate
void SetHandlerPrivate(int signum, SigHandlerFunc handler)
Definition: signalhandling.cpp:149
SignalHandler::~SignalHandler
~SignalHandler() override
Definition: signalhandling.cpp:104
SignalInfo::m_pid
int m_pid
Definition: signalhandling.cpp:191
mythlogging.h
SIG_STR_COUNT
static constexpr size_t SIG_STR_COUNT
Definition: signalhandling.cpp:49
signalhandling.h
SignalHandler::s_singleton
static SignalHandler * s_singleton
Definition: signalhandling.h:60
compat.h
SignalHandler::signalHandler
static void signalHandler(int signum, siginfo_t *info, void *context)
Definition: signalhandling.cpp:196
kDefaultSignalList
static const std::array< const int, 6 > kDefaultSignalList
Definition: signalhandling.cpp:37
SIGHUP
#define SIGHUP
Definition: compat.h:134
sig_str_init
static void sig_str_init(size_t sig, const char *name)
Definition: signalhandling.cpp:52
SignalHandler::Init
static void Init(QObject *parent=nullptr)
Definition: signalhandling.cpp:128
SignalInfo::m_code
int m_code
Definition: signalhandling.cpp:190
SignalInfo
Definition: signalhandling.cpp:188
siginfo_t
void siginfo_t
Definition: signalhandling.h:20
SignalHandler::SetHandler
static void SetHandler(int signum, SigHandlerFunc handler)
Definition: signalhandling.cpp:142
SignalHandler::m_sigMap
QMap< int, SigHandlerFunc > m_sigMap
Definition: signalhandling.h:57
azlyrics.info
dictionary info
Definition: azlyrics.py:7
SignalHandler::m_sigStack
char * m_sigStack
Definition: signalhandling.h:54
d
static const iso6937table * d
Definition: iso6937tables.cpp:1025
exitcodes.h
loggingserver.h
SignalHandler::Done
static void Done(void)
Definition: signalhandling.cpp:135