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