1/*
2 SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-only
5*/
6
7#include "ksignalhandler.h"
8#include "kcoreaddons_debug.h"
9#include <QSocketNotifier>
10#include <QTimer>
11
12#ifndef Q_OS_WIN
13#include <cerrno>
14#include <fcntl.h>
15#include <signal.h>
16#include <sys/socket.h>
17#include <unistd.h>
18#endif
19
20class KSignalHandlerPrivate : public QObject
21{
22public:
23 static void signalHandler(int signal);
24 void handleSignal();
25
26 QSet<int> m_signalsRegistered;
27 static int signalFd[2];
28 QSocketNotifier *m_handler = nullptr;
29
30 KSignalHandler *q;
31};
32int KSignalHandlerPrivate::signalFd[2];
33
34KSignalHandler::KSignalHandler()
35 : d(new KSignalHandlerPrivate)
36{
37 d->q = this;
38#ifndef Q_OS_WIN
39 if (::socketpair(AF_UNIX, SOCK_STREAM, protocol: 0, fds: KSignalHandlerPrivate::signalFd)) {
40 qCWarning(KCOREADDONS_DEBUG) << "Couldn't create a socketpair";
41 return;
42 }
43
44 // ensure the sockets are not leaked to child processes, SOCK_CLOEXEC not supported on macOS
45 fcntl(fd: KSignalHandlerPrivate::signalFd[0], F_SETFD, FD_CLOEXEC);
46 fcntl(fd: KSignalHandlerPrivate::signalFd[1], F_SETFD, FD_CLOEXEC);
47
48 QTimer::singleShot(0, [this] {
49 d->m_handler = new QSocketNotifier(KSignalHandlerPrivate::signalFd[1], QSocketNotifier::Read, this);
50 connect(d->m_handler, &QSocketNotifier::activated, d.get(), &KSignalHandlerPrivate::handleSignal);
51 });
52#endif
53}
54
55KSignalHandler::~KSignalHandler()
56{
57#ifndef Q_OS_WIN
58 for (int sig : std::as_const(d->m_signalsRegistered)) {
59 signal(sig, nullptr);
60 }
61 close(fd: KSignalHandlerPrivate::signalFd[0]);
62 close(fd: KSignalHandlerPrivate::signalFd[1]);
63#endif
64}
65
66void KSignalHandler::watchSignal(int signalToTrack)
67{
68 d->m_signalsRegistered.insert(signalToTrack);
69#ifndef Q_OS_WIN
70 signal(sig: signalToTrack, handler: KSignalHandlerPrivate::signalHandler);
71#endif
72}
73
74void KSignalHandlerPrivate::signalHandler(int signal)
75{
76#ifndef Q_OS_WIN
77 const int ret = ::write(fd: signalFd[0], buf: &signal, n: sizeof(signal));
78 if (ret != sizeof(signal)) {
79 qCWarning(KCOREADDONS_DEBUG) << "signalHandler couldn't write for signal" << strsignal(sig: signal) << " Got error:" << strerror(errno);
80 }
81#endif
82}
83
84void KSignalHandlerPrivate::handleSignal()
85{
86#ifndef Q_OS_WIN
87 m_handler->setEnabled(false);
88 int signal;
89 const int ret = ::read(fd: KSignalHandlerPrivate::signalFd[1], buf: &signal, nbytes: sizeof(signal));
90 if (ret != sizeof(signal)) {
91 qCWarning(KCOREADDONS_DEBUG) << "handleSignal couldn't read signal for fd" << KSignalHandlerPrivate::signalFd[1] << " Got error:" << strerror(errno);
92 return;
93 }
94 m_handler->setEnabled(true);
95
96 Q_EMIT q->signalReceived(signal);
97#endif
98}
99
100KSignalHandler *KSignalHandler::self()
101{
102 static KSignalHandler s_self;
103 return &s_self;
104}
105
106#include "moc_ksignalhandler.cpp"
107

source code of kcoreaddons/src/lib/util/ksignalhandler.cpp