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