QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
SignalHandler.cc
Go to the documentation of this file.
1#include "SignalHandler.h"
2
3#include <QtCore/QCoreApplication>
4#ifdef Q_OS_WIN
5#include <QtCore/QWinEventNotifier>
6#else
7#include <QtCore/QSocketNotifier>
8#endif
9
10#include "QGCLoggingCategory.h"
11
12QGC_LOGGING_CATEGORY(SignalHandlerLog, "Utilities.SignalHandler")
13
14std::atomic<SignalHandler*> SignalHandler::s_current{nullptr};
15
17 : QObject(parent)
18{
19 s_current.store(this, std::memory_order_release);
20 qCDebug(SignalHandlerLog) << this;
21}
22
23#ifdef Q_OS_WIN
24
25#include <QtCore/QWinEventNotifier>
26#include <qt_windows.h>
27
29static BOOL WINAPI _consoleCtrlHandler(DWORD evt)
30{
31 return SignalHandler::consoleCtrlHandler(static_cast<unsigned long>(evt)) ? TRUE : FALSE;
32}
33
34int SignalHandler::consoleCtrlHandler(unsigned long evt)
35{
36 switch (evt) {
37 case CTRL_C_EVENT:
38 case CTRL_BREAK_EVENT:
39 case CTRL_CLOSE_EVENT:
40 case CTRL_LOGOFF_EVENT:
41 case CTRL_SHUTDOWN_EVENT: {
43 if (!self || !self->_signalEvent || (self->_signalEvent == reinterpret_cast<Qt::HANDLE>(INVALID_HANDLE_VALUE))) {
44 return 0;
45 }
46 (void) SetEvent(static_cast<HANDLE>(self->_signalEvent)); // non-blocking
47 return 1;
48 }
49 default:
50 return 0;
51 }
52}
53
55{
56 if (_notifier) {
57 _notifier->setEnabled(false);
58 }
59
60 if (_signalEvent && (_signalEvent != reinterpret_cast<Qt::HANDLE>(INVALID_HANDLE_VALUE))) {
61 (void) CloseHandle(static_cast<HANDLE>(_signalEvent));
62 _signalEvent = reinterpret_cast<Qt::HANDLE>(INVALID_HANDLE_VALUE);
63 }
64
65 (void) SetConsoleCtrlHandler(&_consoleCtrlHandler, FALSE);
66 s_current.store(nullptr, std::memory_order_release);
67
68 qCDebug(SignalHandlerLog) << this;
69}
70
72{
73 // Create auto-reset event. Initial state = non-signaled.
74 _signalEvent = reinterpret_cast<Qt::HANDLE>(CreateEventW(/*lpEventAttributes*/ nullptr, /*bManualReset*/ FALSE, /*bInitialState*/ FALSE, /*lpName*/ nullptr));
75 if (!_signalEvent || (_signalEvent == reinterpret_cast<Qt::HANDLE>(INVALID_HANDLE_VALUE))) {
76 return 1;
77 }
78
79 _notifier = new QWinEventNotifier(static_cast<HANDLE>(_signalEvent), this);
80 (void) connect(_notifier, &QWinEventNotifier::activated, this, [this]([[maybe_unused]] HANDLE handle) {
81 // Auto-reset event already consumed. No drain needed.
82 if (!std::exchange(_sigIntTriggered, true)) {
83 qCDebug(SignalHandlerLog) << "Console event—press Ctrl+C again to exit immediately";
84 QCoreApplication::quit();
85 } else {
86 qCDebug(SignalHandlerLog) << "Caught second SIGINT—exiting immediately";
87 _exit(0);
88 }
89 });
90
91 if (!SetConsoleCtrlHandler(&_consoleCtrlHandler, TRUE)) {
92 return 2;
93 }
94
95 return 0;
96}
97
98#else // POSIX
99
100#include <signal.h>
101#include <sys/socket.h>
102#include <unistd.h>
103#include <errno.h>
104#include <string.h>
105#include <fcntl.h>
106
107#include <QtCore/QSocketNotifier>
108
110{
111 struct sigaction sa{};
112 sa.sa_handler = SIG_DFL;
113 (void) sigaction(SIGINT, &sa, nullptr);
114 (void) sigaction(SIGTERM, &sa, nullptr);
115
116 if (_notifierInt) {
117 _notifierInt->setEnabled(false);
118 }
119 if (_notifierTerm) {
120 _notifierTerm->setEnabled(false);
121 }
122
123 if (_sigIntFd[0] >= 0) {
124 (void) ::close(_sigIntFd[0]);
125 _sigIntFd[0] = -1;
126 }
127 if (_sigIntFd[1] >= 0) {
128 (void) ::close(_sigIntFd[1]);
129 _sigIntFd[1] = -1;
130 }
131 if (_sigTermFd[0] >= 0) {
132 (void) ::close(_sigTermFd[0]);
133 _sigTermFd[0] = -1;
134 }
135 if (_sigTermFd[1] >= 0) {
136 (void) ::close(_sigTermFd[1]);
137 _sigTermFd[1] = -1;
138 }
139
140 s_current.store(nullptr, std::memory_order_release);
141 qCDebug(SignalHandlerLog) << this;
142}
143
145{
146 // Datagram socketpairs: read end = [0], write end = [1]
147 if (::socketpair(AF_UNIX, SOCK_DGRAM, 0, _sigIntFd)) {
148 qCCritical(SignalHandlerLog) << "Failed to create SIGINT socketpair:" << strerror(errno);
149 return 1;
150 }
151 if (::socketpair(AF_UNIX, SOCK_DGRAM, 0, _sigTermFd)) {
152 qCCritical(SignalHandlerLog) << "Failed to create SIGTERM socketpair:" << strerror(errno);
153 return 2;
154 }
155
156 // Close-on-exec to prevent fd leaks into children
157 (void) fcntl(_sigIntFd[0], F_SETFD, fcntl(_sigIntFd[0], F_GETFD, 0) | FD_CLOEXEC);
158 (void) fcntl(_sigIntFd[1], F_SETFD, fcntl(_sigIntFd[1], F_GETFD, 0) | FD_CLOEXEC);
159 (void) fcntl(_sigTermFd[0], F_SETFD, fcntl(_sigTermFd[0], F_GETFD, 0) | FD_CLOEXEC);
160 (void) fcntl(_sigTermFd[1], F_SETFD, fcntl(_sigTermFd[1], F_GETFD, 0) | FD_CLOEXEC);
161
162 // Non-blocking read ends
163 (void) fcntl(_sigIntFd[0], F_SETFL, fcntl(_sigIntFd[0], F_GETFL, 0) | O_NONBLOCK);
164 (void) fcntl(_sigTermFd[0], F_SETFL, fcntl(_sigTermFd[0], F_GETFL, 0) | O_NONBLOCK);
165
166 _notifierInt = new QSocketNotifier(_sigIntFd[0], QSocketNotifier::Read, this);
167 _notifierTerm = new QSocketNotifier(_sigTermFd[0], QSocketNotifier::Read, this);
168 (void) connect(_notifierInt, &QSocketNotifier::activated, this, &SignalHandler::_onSigInt);
169 (void) connect(_notifierTerm, &QSocketNotifier::activated, this, &SignalHandler::_onSigTerm);
170
171 struct sigaction sa_int{};
172 sigemptyset(&sa_int.sa_mask);
173 sa_int.sa_flags = 0;
174 sa_int.sa_handler = SignalHandler::_intSignalHandler;
175 if (sigaction(SIGINT, &sa_int, nullptr)) {
176 return 3;
177 }
178
179 struct sigaction sa_term{};
180 sigemptyset(&sa_term.sa_mask);
181 sa_term.sa_flags = 0;
182 sa_term.sa_handler = SignalHandler::_termSignalHandler;
183 if (sigaction(SIGTERM, &sa_term, nullptr)) {
184 return 4;
185 }
186
187 return 0;
188}
189
190void SignalHandler::_onSigInt()
191{
192 char b;
193 [[maybe_unused]] const ssize_t n = ::read(_sigIntFd[0], &b, 1); // single-byte drain
194
195 if (!std::exchange(_sigIntTriggered, true)) {
196 qCInfo(SignalHandlerLog) << "Caught SIGINT—press Ctrl+C again to exit immediately";
197 QCoreApplication::quit();
198 } else {
199 qCDebug(SignalHandlerLog) << "Caught second SIGINT—exiting immediately";
200 _exit(0);
201 }
202}
203
204void SignalHandler::_onSigTerm()
205{
206 char b;
207 [[maybe_unused]] const ssize_t n = ::read(_sigTermFd[0], &b, 1); // single-byte drain
208
209 qCDebug(SignalHandlerLog) << "Caught SIGTERM—shutting down gracefully";
210 QCoreApplication::quit();
211}
212
213void SignalHandler::_intSignalHandler(int signum)
214{
215 if (signum != SIGINT) {
216 return;
217 }
218 const SignalHandler* self = current();
219 if (!self) {
220 return;
221 }
222 const char b = 1;
223 [[maybe_unused]] const ssize_t n = ::write(self->_sigIntFd[1], &b, sizeof(b)); // no logging from signal handler
224}
225
226void SignalHandler::_termSignalHandler(int signum)
227{
228 if (signum != SIGTERM) {
229 return;
230 }
231 const SignalHandler* self = current();
232 if (!self) {
233 return;
234 }
235 const char b = 1;
236 [[maybe_unused]] const ssize_t n = ::write(self->_sigTermFd[1], &b, sizeof(b)); // no logging from signal handler
237}
238
239#endif // POSIX
#define QGC_LOGGING_CATEGORY(name, categoryStr)
SignalHandler(QObject *parent=nullptr)
static SignalHandler * current()
int setupSignalHandlers()
~SignalHandler() override