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), m_notifier(nullptr)
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, sigFd))
92  {
93  cerr << "Couldn't create socketpair" << endl;
94  return;
95  }
96  m_notifier = new QSocketNotifier(sigFd[1], QSocketNotifier::Read, this);
97  connect(m_notifier, SIGNAL(activated(int)), this, SLOT(handleSignal()));
98 
99  QList<int>::iterator it = signallist.begin();
100  for( ; it != signallist.end(); ++it )
101  {
102  int signum = *it;
103  if (!s_defaultHandlerList.contains(signum))
104  {
105  cerr << "No default handler for signal " << signum << endl;
106  continue;
107  }
108 
109  SetHandlerPrivate(signum, nullptr);
110  }
111 #endif
112 }
113 
115 {
116  s_singleton = nullptr;
117 
118 #ifndef _WIN32
119  if (m_notifier)
120  {
121  ::close(sigFd[0]);
122  ::close(sigFd[1]);
123  delete m_notifier;
124  }
125 
126  QMutexLocker locker(&m_sigMapLock);
127  QMap<int, SigHandlerFunc>::iterator it = m_sigMap.begin();
128  for ( ; it != m_sigMap.end(); ++it)
129  {
130  int signum = it.key();
131  signal(signum, SIG_DFL);
132  }
133 
134  m_sigMap.clear();
135 #endif
136 }
137 
138 void SignalHandler::Init(QList<int> &signallist, QObject *parent)
139 {
140  QMutexLocker locker(&s_singletonLock);
141  if (!s_singleton)
142  s_singleton = new SignalHandler(signallist, parent);
143 }
144 
146 {
147  QMutexLocker locker(&s_singletonLock);
148  if (s_singleton)
149  delete s_singleton;
150 }
151 
152 
153 void SignalHandler::SetHandler(int signum, SigHandlerFunc handler)
154 {
155  QMutexLocker locker(&s_singletonLock);
156  if (s_singleton)
157  s_singleton->SetHandlerPrivate(signum, handler);
158 }
159 
161 {
162 #ifndef _WIN32
163  const char *signame = strsignal(signum);
164  QString signal_name = signame ?
165  QString(signame) : QString("Unknown(%1)").arg(signum);
166 
167  bool sa_handler_already_set = false;
168  {
169  QMutexLocker locker(&m_sigMapLock);
170  sa_handler_already_set = m_sigMap.contains(signum);
171  if (m_sigMap.value(signum, nullptr) && (handler != nullptr))
172  {
173  LOG(VB_GENERAL, LOG_WARNING,
174  QString("Warning %1 signal handler overridden")
175  .arg(signal_name));
176  }
177  m_sigMap[signum] = handler;
178  }
179 
180  if (!sa_handler_already_set)
181  {
182  struct sigaction sa;
183  sa.sa_sigaction = SignalHandler::signalHandler;
184  sigemptyset(&sa.sa_mask);
185  sa.sa_flags = SA_RESTART | SA_SIGINFO;
186  if (m_sigStack)
187  sa.sa_flags |= SA_ONSTACK;
188 
189  sig_str_init(signum, qPrintable(signal_name));
190 
191  sigaction(signum, &sa, nullptr);
192  }
193 
194  LOG(VB_GENERAL, LOG_INFO, QString("Setup %1 handler").arg(signal_name));
195 #endif
196 }
197 
198 typedef struct {
199  int signum;
200  int code;
201  int pid;
202  int uid;
203  uint64_t value;
204 } SignalInfo;
205 
206 void SignalHandler::signalHandler(int signum, siginfo_t *info, void *context)
207 {
208  SignalInfo signalInfo;
209 
210  (void)context;
211  signalInfo.signum = signum;
212 #ifdef _WIN32
213  (void)info;
214  signalInfo.code = 0;
215  signalInfo.pid = 0;
216  signalInfo.uid = 0;
217  signalInfo.value = 0;
218 #else
219  signalInfo.code = (info ? info->si_code : 0);
220  signalInfo.pid = (info ? (int)info->si_pid : 0);
221  signalInfo.uid = (info ? (int)info->si_uid : 0);
222  signalInfo.value = (info ? *(uint64_t *)&info->si_value : 0);
223 #endif
224 
225  // Keep trying if there's no room to write, but stop on error (-1)
226  int index = 0;
227  int size = sizeof(SignalInfo);
228  char *buffer = (char *)&signalInfo;
229  do {
230  int written = ::write(sigFd[0], &buffer[index], size);
231  // If there's an error, the signal will not be seen be the application,
232  // but we can't keep trying.
233  if (written < 0)
234  break;
235  index += written;
236  size -= written;
237  } while (size > 0);
238 
239  // One must not return from SEGV, ILL, BUS or FPE. When these
240  // are raised by the program itself they will immediately get
241  // re-raised on return.
242  //
243  // We also handle SIGABRT the same way. While it is safe to
244  // return from the signal handler for SIGABRT doing so means
245  // SIGABRT will fail when the UI thread is deadlocked; but
246  // SIGABRT is the signal one uses to get a core of a
247  // deadlocked program.
248  switch (signum)
249  {
250  case SIGSEGV:
251  case SIGILL:
252 #ifndef _WIN32
253  case SIGBUS:
254 #endif
255  case SIGFPE:
256  case SIGABRT:
257  // clear the signal handler so if it reoccurs we get instant death.
258  signal(signum, SIG_DFL);
259 
260  // Wait for UI event loop to handle this, however we may be
261  // blocking it if this signal occured in the UI thread.
262  // Note, can not use usleep() as it is not a signal safe function.
263  sleep(1);
264 
265  if (!s_exit_program)
266  {
267  // log something to console.. regular logging should be kaput
268  // NOTE: This needs to use write rather than cout or printf as
269  // we need to stick to system calls that are known to be
270  // signal-safe. write is, the other two aren't.
271  int d = 0;
272  if (signum < SIG_STR_COUNT)
273  d+=::write(STDERR_FILENO, sig_str[signum], sig_str_len[signum]);
274  (void) d; // quiet ignoring return value warning.
275  }
276 
277  // call the default signal handler. This will kill the application.
278  raise(signum);
279  break;
280  case SIGINT:
281  case SIGTERM:
282  // clear the signal handler so if it reoccurs we get instant death.
283  signal(signum, SIG_DFL);
284  break;
285  }
286 }
287 
289 {
290 #ifndef _WIN32
291  m_notifier->setEnabled(false);
292 
293  SignalInfo signalInfo;
294  int ret = ::read(sigFd[1], &signalInfo, sizeof(SignalInfo));
295  bool infoComplete = (ret == sizeof(SignalInfo));
296  int signum = (infoComplete ? signalInfo.signum : 0);
297 
298  if (infoComplete)
299  {
300  char *signame = strsignal(signum);
301  signame = strdup(signame ? signame : "Unknown Signal");
302  LOG(VB_GENERAL, LOG_CRIT,
303  QString("Received %1: Code %2, PID %3, UID %4, Value 0x%5")
304  .arg(signame) .arg(signalInfo.code) .arg(signalInfo.pid)
305  .arg(signalInfo.uid) .arg(signalInfo.value,8,16,QChar('0')));
306  free(signame);
307  }
308 
309  SigHandlerFunc handler = nullptr;
310  bool allowNullHandler = false;
311 
312 #if ! CONFIG_DARWIN
313  if (signum == SIGRTMIN)
314  {
315  // glibc idiots seem to have made SIGRTMIN a macro that expands to a
316  // function, so we can't do this in the switch below.
317  // This uses the default handler to just get us here and to ignore it.
318  allowNullHandler = true;
319  }
320 #endif
321 
322  switch (signum)
323  {
324  case SIGINT:
325  case SIGTERM:
326  m_sigMapLock.lock();
327  handler = m_sigMap.value(signum, nullptr);
328  m_sigMapLock.unlock();
329 
330  if (handler)
331  handler();
332  else
333  QCoreApplication::exit(0);
334  s_exit_program = true;
335  break;
336  case SIGSEGV:
337  case SIGABRT:
338  case SIGBUS:
339  case SIGFPE:
340  case SIGILL:
341  usleep(100000);
342  s_exit_program = true;
343  break;
344  default:
345  m_sigMapLock.lock();
346  handler = m_sigMap.value(signum, nullptr);
347  m_sigMapLock.unlock();
348  if (handler)
349  {
350  handler();
351  }
352  else if (!allowNullHandler)
353  {
354  LOG(VB_GENERAL, LOG_CRIT, QString("Received unexpected signal %1")
355  .arg(signum));
356  }
357  break;
358  }
359 
360  m_notifier->setEnabled(true);
361 #endif
362 }
363 
364 /*
365  * vim:ts=4:sw=4:ai:et:si:sts=4
366  */
def write(text, progress=True)
Definition: mythburn.py:279
char * sig_str[SIG_STR_COUNT]
static void Init(QList< int > &signallist, QObject *parent=nullptr)
unsigned int uint
Definition: compat.h:140
unsigned sleep(unsigned int x)
Definition: compat.h:152
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
void siginfo_t
static const uint16_t * d
uint64_t value
static int sigFd[2]
static void signalHandler(int signum, siginfo_t *info, void *context)
static void Done(void)
SignalHandler(QList< int > &signallist, QObject *parent)
void SetHandlerPrivate(int signal, SigHandlerFunc handler)
const char * name
Definition: ParseText.cpp:339
static QMutex s_singletonLock
QMutex m_sigMapLock
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void(* SigHandlerFunc)(void)
#define SIG_STR_COUNT
static SignalHandler * s_singleton
static void SetHandler(int signal, SigHandlerFunc handler)
static volatile bool s_exit_program
uint sig_str_len[SIG_STR_COUNT]
static void sig_str_init(int sig, const char *name)
QMap< int, SigHandlerFunc > m_sigMap