MythTV master
signalhandling.cpp
Go to the documentation of this file.
1#include <QtGlobal>
2#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3#include <QtSystemDetection>
4#endif
5#include <QObject>
6#include <QSocketNotifier>
7#include <QCoreApplication>
8#include <QList>
9
10#include <csignal>
11#include <cstdint>
12#include <cstdlib> // for free
13#include <iostream>
14#include <string>
15#include <thread>
16#include <sys/types.h>
17#include <unistd.h>
18#ifndef Q_OS_WINDOWS
19#include <sys/socket.h>
20#endif
21
22#include "compat.h"
23#include "mythlogging.h"
24#include "loggingserver.h"
25#include "exitcodes.h"
26#include "signalhandling.h"
27
28std::array<int,2> SignalHandler::s_sigFd;
29volatile bool SignalHandler::s_exit_program = false;
32
33static const std::array<const int, 6
34#ifndef Q_OS_WINDOWS
35 + 1
36#if !defined(Q_OS_DARWIN) && !defined(Q_OS_OPENBSD)
37 + 1
38#endif // Q_OS_DARWIN
39#endif // Q_OS_WINDOWS
41{
42 SIGINT, SIGTERM, SIGSEGV, SIGABRT, SIGFPE, SIGILL,
43#ifndef Q_OS_WINDOWS
44 SIGBUS,
45#if !defined(Q_OS_DARWIN) && !defined(Q_OS_OPENBSD)
46 SIGRTMIN, // not necessarily constexpr
47#endif // Q_OS_DARWIN
48#endif // Q_OS_WINDOWS
49};
50
51// We may need to write out signal info using just the write() function
52// so we create an array of C strings + measure their lengths.
53static constexpr size_t SIG_STR_COUNT { 256 };
54std::array<std::string,SIG_STR_COUNT> sig_str;
55
56static void sig_str_init(size_t sig, const char *name)
57{
58 if (sig >= sig_str.size())
59 return;
60
61 sig_str[sig] = qPrintable(QString("Handling %1\n").arg(name));
62}
63
64static void sig_str_init(void)
65{
66 for (size_t i = 0; i < sig_str.size(); i++)
67 sig_str_init(i, qPrintable(QString("Signal %1").arg(i)));
68}
69
71 QObject(parent)
72{
73 s_exit_program = false; // set here due to "C++ static initializer madness"
75
76#ifndef Q_OS_WINDOWS
77 //NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
78 m_sigStack = new char[SIGSTKSZ];
79 stack_t stack;
80 stack.ss_sp = m_sigStack;
81 stack.ss_flags = 0;
82 stack.ss_size = SIGSTKSZ;
83
84 // Carry on without the signal stack if it fails
85 if (sigaltstack(&stack, nullptr) == -1)
86 {
87 std::cerr << "Couldn't create signal stack!" << std::endl;
88 delete [] m_sigStack;
89 m_sigStack = nullptr;
90 }
91
92 if (::socketpair(AF_UNIX, SOCK_STREAM, 0, s_sigFd.data()))
93 {
94 std::cerr << "Couldn't create socketpair" << std::endl;
95 return;
96 }
97 m_notifier = new QSocketNotifier(s_sigFd[1], QSocketNotifier::Read, this);
98 connect(m_notifier, &QSocketNotifier::activated, this, &SignalHandler::handleSignal);
99
100 for (const int signum : kDefaultSignalList)
101 {
102 SetHandlerPrivate(signum, nullptr);
103 }
104 SetHandlerPrivate(SIGHUP, logSigHup);
105#endif // Q_OS_WINDOWS
106}
107
109{
110 s_singleton = nullptr;
111
112#ifndef Q_OS_WINDOWS
113 if (m_notifier)
114 {
115 ::close(s_sigFd[0]);
116 ::close(s_sigFd[1]);
117 delete m_notifier;
118 }
119
120 QMutexLocker locker(&m_sigMapLock);
121 // NOLINTNEXTLINE(modernize-loop-convert)
122 for (auto it = m_sigMap.begin(); it != m_sigMap.end(); ++it)
123 {
124 int signum = it.key();
125 signal(signum, SIG_DFL);
126 }
127
128 m_sigMap.clear();
129
130 delete [] m_sigStack;
131 m_sigStack = nullptr;
132#endif
133}
134
135void SignalHandler::Init(QObject *parent)
136{
137 QMutexLocker locker(&s_singletonLock);
138 if (!s_singleton)
139 s_singleton = new SignalHandler(parent);
140}
141
143{
144 QMutexLocker locker(&s_singletonLock);
145 delete s_singleton;
146}
147
148
150{
151 QMutexLocker locker(&s_singletonLock);
152 if (s_singleton)
153 s_singleton->SetHandlerPrivate(signum, handler);
154}
155
156void SignalHandler::SetHandlerPrivate([[maybe_unused]] int signum,
157 [[maybe_unused]] SigHandlerFunc handler)
158{
159#ifndef Q_OS_WINDOWS
160 const char *signame = strsignal(signum);
161 QString signal_name = signame ?
162 QString(signame) : QString("Unknown(%1)").arg(signum);
163
164 bool sa_handler_already_set = false;
165 {
166 QMutexLocker locker(&m_sigMapLock);
167 sa_handler_already_set = m_sigMap.contains(signum);
168 if (m_sigMap.value(signum, nullptr) && (handler != nullptr))
169 {
170 LOG(VB_GENERAL, LOG_WARNING,
171 QString("Warning %1 signal handler overridden")
172 .arg(signal_name));
173 }
174 m_sigMap[signum] = handler;
175 }
176
177 if (!sa_handler_already_set)
178 {
179 struct sigaction sa {};
180 sa.sa_sigaction = SignalHandler::signalHandler;
181 sigemptyset(&sa.sa_mask);
182 sa.sa_flags = SA_RESTART | SA_SIGINFO;
183 if (m_sigStack)
184 sa.sa_flags |= SA_ONSTACK;
185
186 sig_str_init(signum, qPrintable(signal_name));
187
188 sigaction(signum, &sa, nullptr);
189 }
190
191 LOG(VB_GENERAL, LOG_INFO, QString("Setup %1 handler").arg(signal_name));
192#endif
193}
194
198 int m_pid;
199 int m_uid;
200 uint64_t m_value;
201};
202
204 [[maybe_unused]] siginfo_t *info,
205 [[maybe_unused]] void *context)
206{
207 SignalInfo signalInfo {};
208
209 signalInfo.m_signum = signum;
210#ifdef Q_OS_WINDOWS
211 signalInfo.m_code = 0;
212 signalInfo.m_pid = 0;
213 signalInfo.m_uid = 0;
214 signalInfo.m_value = 0;
215#else
216 signalInfo.m_code = (info ? info->si_code : 0);
217 signalInfo.m_pid = (info ? (int)info->si_pid : 0);
218 signalInfo.m_uid = (info ? (int)info->si_uid : 0);
219 signalInfo.m_value = (info ? info->si_value.sival_int : 0);
220#endif
221
222 // Keep trying if there's no room to write, but stop on error (-1)
223 int index = 0;
224 int size = sizeof(SignalInfo);
225 char *buffer = (char *)&signalInfo;
226 while (size > 0)
227 {
228 int written = ::write(s_sigFd[0], &buffer[index], size);
229 // If there's an error, the signal will not be seen be the application,
230 // but we can't keep trying.
231 if (written < 0)
232 break;
233 index += written;
234 size -= written;
235 }
236
237 // One must not return from SEGV, ILL, BUS or FPE. When these
238 // are raised by the program itself they will immediately get
239 // re-raised on return.
240 //
241 // We also handle SIGABRT the same way. While it is safe to
242 // return from the signal handler for SIGABRT doing so means
243 // SIGABRT will fail when the UI thread is deadlocked; but
244 // SIGABRT is the signal one uses to get a core of a
245 // deadlocked program.
246 switch (signum)
247 {
248 case SIGSEGV:
249 case SIGILL:
250#ifndef Q_OS_WINDOWS
251 case SIGBUS:
252#endif
253 case SIGFPE:
254 case SIGABRT:
255 // clear the signal handler so if it reoccurs we get instant death.
256 signal(signum, SIG_DFL);
257
258 // Wait for UI event loop to handle this, however we may be
259 // blocking it if this signal occured in the UI thread.
260 // Note, we cannot use std::this_thread::sleep_for()
261 // since 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 < static_cast<int>(sig_str.size()))
272 d+=::write(STDERR_FILENO, sig_str[signum].c_str(), sig_str[signum].size());
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 Q_OS_WINDOWS
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.m_signum : 0);
296
297 if (infoComplete)
298 {
299 QString signame = strsignal(signum);
300 if (signame.isEmpty())
301 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.m_code) .arg(signalInfo.m_pid)
305 .arg(signalInfo.m_uid) .arg(signalInfo.m_value,8,16,QChar('0')));
306 }
307
308 SigHandlerFunc handler = nullptr;
309 bool allowNullHandler = false;
310
311#if !defined(Q_OS_DARWIN) && !defined(Q_OS_OPENBSD)
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 // !defined(Q_OS_DARWIN) && !defined(Q_OS_OPENBSD)
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 std::this_thread::sleep_for(100ms);
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 // Q_OS_WINDOWS
361}
362
363/*
364 * vim:ts=4:sw=4:ai:et:si:sts=4
365 */
A container object to handle UNIX signals in the Qt space correctly.
static QMutex s_singletonLock
static std::array< int, 2 > s_sigFd
static SignalHandler * s_singleton
void SetHandlerPrivate(int signum, SigHandlerFunc handler)
QSocketNotifier * m_notifier
~SignalHandler() override
static void SetHandler(int signum, SigHandlerFunc handler)
static void signalHandler(int signum, siginfo_t *info, void *context)
static void Done(void)
QMap< int, SigHandlerFunc > m_sigMap
SignalHandler(QObject *parent)
static volatile bool s_exit_program
void handleSignal(void)
static void Init(QObject *parent=nullptr)
QMutex m_sigMapLock
#define SIGHUP
Definition: compat.h:78
#define close
Definition: compat.h:28
static const iso6937table * d
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
dictionary info
Definition: azlyrics.py:7
def read(device=None, features=[])
Definition: disc.py:35
def write(text, progress=True)
Definition: mythburn.py:306
static constexpr size_t SIG_STR_COUNT
static const std::array< const int, 6 > kDefaultSignalList
static void sig_str_init(size_t sig, const char *name)
std::array< std::string, SIG_STR_COUNT > sig_str
void siginfo_t
void(*)(void) SigHandlerFunc
uint64_t m_value