qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
posixsignalnotifier.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2017-2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "posixsignalnotifier.h"
21 
22 #include <QDebug>
23 #include <QSocketNotifier>
24 
25 #include <array>
26 #include <atomic>
27 
28 #include <errno.h>
29 #include <signal.h> // sigaction()
30 #include <sys/socket.h> // socketpair()
31 #include <sys/types.h> // may be needed for BSD
32 #include <unistd.h> // close()
33 
39 namespace detail {
40 
41 static std::atomic_flag g_signalSocketUsageFlag = ATOMIC_FLAG_INIT;
42 static std::array<int, 2> g_signalSocketPair;
43 
44 static void signalHandler(int signum)
45 {
46  // DO NOT call any Qt functions directly, only limited amount of so-called async-signal-safe
47  // functions can be called in signal handlers.
48  // See https://doc.qt.io/qt-4.8/unix-signals.html
49 
50  // If test_and_set() returns true, it means it was already in use (only by ~PosixSignalNotifier()),
51  // so we bail out. Our signal handler is blocking, only one will be called (no race between
52  // threads), hence simple implementation.
53  if (g_signalSocketUsageFlag.test_and_set())
54  return;
55 
56  if(::write(g_signalSocketPair[0], &signum, sizeof(signum)) == -1) {
57  // We hardly can do anything more usefull in signal handler, and
58  // any ways it's probably very unexpected error (out of memory?),
59  // since we check socket existance with a flag beforehand.
60  abort();
61  }
62 
63  g_signalSocketUsageFlag.clear();
64 }
65 
66 } // namespace detail
67 
69 {
70  while (detail::g_signalSocketUsageFlag.test_and_set()) {
71  // spin-loop until we aquire flag (signal handler might be running and have flag in use)
72  }
73 
74  // do not leak sockets
75  ::close(detail::g_signalSocketPair[0]);
76  ::close(detail::g_signalSocketPair[1]);
77 
78  // do not clear the usage flag here, signal handler cannot use socket any more!
79 }
80 
82 {
83  sigset_t blockMask;
84  sigemptyset(&blockMask); // do not prefix with ::, it's a macro on macOS
85  sigaddset(&blockMask, signum); // do not prefix with ::, it's a macro on macOS
86 
87  struct sigaction action = {}; // all zeroes by default
88  action.sa_handler = detail::signalHandler;
89  action.sa_mask = blockMask; // allow old signal to finish before new is raised
90 
91  if (::sigaction(signum, &action, nullptr)) {
92  qFatal("Failed to setup signal %d, error = %d", signum, errno);
93  }
94 }
95 
96 void PosixSignalNotifier::watchSignals(std::initializer_list<int> signalSet)
97 {
98  for (auto s: signalSet) {
99  watchSignal(s);
100  }
101 }
102 
104 {
105  watchSignals({SIGHUP, SIGINT, SIGQUIT, SIGTERM});
106 }
107 
109 {
110  static PosixSignalNotifier instance;
111  return instance;
112 }
113 
115 {
116  int signum{0};
117  if (::read(detail::g_signalSocketPair[1], &signum, sizeof(signum)) == -1) {
118  qFatal("Failed to read from signal socket, error = %d", errno);
119  }
120 
121  qDebug() << "Signal" << signum << "received";
122  emit activated(signum);
123 }
124 
126 {
127  if (::socketpair(AF_UNIX, SOCK_STREAM, 0, detail::g_signalSocketPair.data())) {
128  qFatal("Failed to create socket pair, error = %d", errno);
129  }
130 
131  notifier = new QSocketNotifier(detail::g_signalSocketPair[1], QSocketNotifier::Read, this);
132  connect(notifier, &QSocketNotifier::activated, this, &PosixSignalNotifier::onSignalReceived);
133 }
PosixSignalNotifier::watchSignals
static void watchSignals(std::initializer_list< int > signalSet)
Definition: posixsignalnotifier.cpp:96
detail
Definition: posixsignalnotifier.cpp:39
PosixSignalNotifier::~PosixSignalNotifier
~PosixSignalNotifier()
Definition: posixsignalnotifier.cpp:68
posixsignalnotifier.h
PosixSignalNotifier::watchSignal
static void watchSignal(int signum)
Definition: posixsignalnotifier.cpp:81
PosixSignalNotifier::onSignalReceived
void onSignalReceived()
Definition: posixsignalnotifier.cpp:114
PosixSignalNotifier
Class for converting POSIX signals to Qt signals.
Definition: posixsignalnotifier.h:26
PosixSignalNotifier::watchCommonTerminatingSignals
static void watchCommonTerminatingSignals()
Definition: posixsignalnotifier.cpp:103
PosixSignalNotifier::globalInstance
static PosixSignalNotifier & globalInstance()
Definition: posixsignalnotifier.cpp:108
PosixSignalNotifier::PosixSignalNotifier
PosixSignalNotifier()
Definition: posixsignalnotifier.cpp:125
PosixSignalNotifier::activated
void activated(int signal)
PosixSignalNotifier::notifier
QSocketNotifier * notifier
Definition: posixsignalnotifier.h:49