mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
fix(platform): handle terminating POSIX signals
Add PosixSignalNotifier utility class to covert POSIX signals to Qt signals and allow qTox to quit appropriately on Linux, FreeBSD and macOS. Might protect from data corruption bugs when application is being terminated during I/O. Fixes: #4470
This commit is contained in:
parent
e606d3cb55
commit
32b97cb927
|
@ -450,6 +450,13 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
|||
)
|
||||
endif()
|
||||
|
||||
if (UNIX)
|
||||
set(${PROJECT_NAME}_SOURCES ${${PROJECT_NAME}_SOURCES}
|
||||
src/platform/posixsignalnotifier.cpp
|
||||
src/platform/posixsignalnotifier.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (PLATFORM_EXTENSIONS)
|
||||
set(${PROJECT_NAME}_SOURCES ${${PROJECT_NAME}_SOURCES}
|
||||
src/platform/autorun.h
|
||||
|
|
15
src/main.cpp
15
src/main.cpp
|
@ -46,6 +46,10 @@
|
|||
#include "platform/install_osx.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
#include "platform/posixsignalnotifier.h"
|
||||
#endif
|
||||
|
||||
#ifdef LOG_TO_FILE
|
||||
static QAtomicPointer<FILE> logFileFile = nullptr;
|
||||
static QList<QByteArray>* logBuffer =
|
||||
|
@ -145,6 +149,17 @@ int main(int argc, char* argv[])
|
|||
qInstallMessageHandler(logMessageHandler);
|
||||
|
||||
QApplication* a = new QApplication(argc, argv);
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
// PosixSignalNotifier is used only for terminating signals,
|
||||
// so it's connected directly to quit() without any filtering.
|
||||
QObject::connect(&PosixSignalNotifier::globalInstance(),
|
||||
&PosixSignalNotifier::activated,
|
||||
a,
|
||||
&QApplication::quit);
|
||||
PosixSignalNotifier::watchCommonTerminatingSignals();
|
||||
#endif
|
||||
|
||||
a->setApplicationName("qTox");
|
||||
a->setOrganizationName("Tox");
|
||||
a->setApplicationVersion("\nGit commit: " + QString(GIT_VERSION));
|
||||
|
|
126
src/platform/posixsignalnotifier.cpp
Normal file
126
src/platform/posixsignalnotifier.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
Copyright © 2017 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "posixsignalnotifier.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h> // sigaction()
|
||||
#include <sys/socket.h> // socketpair()
|
||||
#include <sys/types.h> // may be needed for BSD
|
||||
#include <unistd.h> // close()
|
||||
|
||||
/**
|
||||
* @class PosixSignalNotifier
|
||||
* @brief Class for converting POSIX signals to Qt signals
|
||||
*/
|
||||
|
||||
namespace detail {
|
||||
|
||||
static std::atomic_flag g_signalSocketUsageFlag{ATOMIC_FLAG_INIT};
|
||||
static std::array<int, 2> g_signalSocketPair;
|
||||
|
||||
static void signalHandler(int signum)
|
||||
{
|
||||
// DO NOT call any Qt functions directly, only limited amount of so-called async-signal-safe
|
||||
// functions can be called in signal handlers.
|
||||
// See https://doc.qt.io/qt-4.8/unix-signals.html
|
||||
|
||||
// If test_and_set() returns true, it means it was already in use (only by ~PosixSignalNotifier()),
|
||||
// so we bail out. Our signal handler is blocking, only one will be called (no race between
|
||||
// threads), hence simple implementation.
|
||||
if (g_signalSocketUsageFlag.test_and_set())
|
||||
return;
|
||||
|
||||
::write(g_signalSocketPair[0], &signum, sizeof(signum));
|
||||
|
||||
g_signalSocketUsageFlag.clear();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
PosixSignalNotifier::~PosixSignalNotifier()
|
||||
{
|
||||
while (detail::g_signalSocketUsageFlag.test_and_set()) {
|
||||
// spin-loop until we aquire flag (signal handler might be running and have flag in use)
|
||||
}
|
||||
|
||||
// do not leak sockets
|
||||
::close(detail::g_signalSocketPair[0]);
|
||||
::close(detail::g_signalSocketPair[1]);
|
||||
|
||||
// do not clear the usage flag here, signal handler cannot use socket any more!
|
||||
}
|
||||
|
||||
void PosixSignalNotifier::watchSignal(int signum)
|
||||
{
|
||||
sigset_t blockMask;
|
||||
sigemptyset(&blockMask); // do not prefix with ::, it's a macro on macOS
|
||||
sigaddset(&blockMask, signum); // do not prefix with ::, it's a macro on macOS
|
||||
|
||||
struct sigaction action = {}; // all zeroes by default
|
||||
action.sa_handler = detail::signalHandler;
|
||||
action.sa_mask = blockMask; // allow old signal to finish before new is raised
|
||||
|
||||
if (::sigaction(signum, &action, 0)) {
|
||||
qFatal("Failed to setup signal %d, error = %d", signum, errno);
|
||||
}
|
||||
}
|
||||
|
||||
void PosixSignalNotifier::watchSignals(std::initializer_list<int> signalSet)
|
||||
{
|
||||
for (auto s: signalSet) {
|
||||
watchSignal(s);
|
||||
}
|
||||
}
|
||||
|
||||
void PosixSignalNotifier::watchCommonTerminatingSignals()
|
||||
{
|
||||
watchSignals({SIGHUP, SIGINT, SIGQUIT, SIGTERM});
|
||||
}
|
||||
|
||||
PosixSignalNotifier& PosixSignalNotifier::globalInstance()
|
||||
{
|
||||
static PosixSignalNotifier instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void PosixSignalNotifier::onSignalReceived()
|
||||
{
|
||||
int signum{0};
|
||||
::read(detail::g_signalSocketPair[1], &signum, sizeof(signum));
|
||||
|
||||
qDebug() << "Signal" << signum << "received";
|
||||
emit activated(signum);
|
||||
}
|
||||
|
||||
PosixSignalNotifier::PosixSignalNotifier()
|
||||
{
|
||||
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, detail::g_signalSocketPair.data())) {
|
||||
qFatal("Failed to create socket pair, error = %d", errno);
|
||||
}
|
||||
|
||||
notifier = new QSocketNotifier(detail::g_signalSocketPair[1], QSocketNotifier::Read, this);
|
||||
connect(notifier, &QSocketNotifier::activated, this, &PosixSignalNotifier::onSignalReceived);
|
||||
}
|
53
src/platform/posixsignalnotifier.h
Normal file
53
src/platform/posixsignalnotifier.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright © 2017 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef POSIXSIGNALNOTIFIER_H
|
||||
#define POSIXSIGNALNOTIFIER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QSocketNotifier;
|
||||
|
||||
class PosixSignalNotifier : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~PosixSignalNotifier();
|
||||
|
||||
static void watchSignal(int signum);
|
||||
static void watchSignals(std::initializer_list<int> signalSet);
|
||||
static void watchCommonTerminatingSignals();
|
||||
|
||||
static PosixSignalNotifier& globalInstance();
|
||||
|
||||
signals:
|
||||
void activated(int signal);
|
||||
|
||||
private slots:
|
||||
void onSignalReceived();
|
||||
|
||||
private:
|
||||
PosixSignalNotifier();
|
||||
|
||||
private:
|
||||
QSocketNotifier* notifier{nullptr};
|
||||
};
|
||||
|
||||
#endif // POSIXSIGNALNOTIFIER_H
|
Loading…
Reference in New Issue
Block a user