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