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