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