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