From 198691182b1cf0fbfa3664045768c3af583bbf9e Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Wed, 18 Jan 2017 00:15:33 +0100 Subject: [PATCH 1/4] Implement clean shutdown after receiving Unix signals, resolves #169 The database wasn't saved properly and lockfiles were not removed when receiving the signals SIGINT, SIGTERM, SIGQUIT or SIGHUP. This patch implements signal handling and performs a clean shutdown after receiving SIGINT SIGTERM or SIGQUIT and ignores SIGHUP. Since this uses POSIX syscalls for signal and socket handling, there is no Windows implementation at the moment. --- src/gui/Application.cpp | 67 ++++++++++++++++++++++++++++++++++++++++- src/gui/Application.h | 17 ++++++++++- src/gui/MainWindow.h | 2 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index d982f22ca..8c4a21f8b 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -17,12 +17,21 @@ */ #include "Application.h" +#include "MainWindow.h" #include #include +#include #include "autotype/AutoType.h" +#if defined(Q_OS_UNIX) +#include +#include +#include +#endif + +class MainWindow; #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) class XcbEventFilter : public QAbstractNativeEventFilter { @@ -64,13 +73,16 @@ public: Application::Application(int& argc, char** argv) : QApplication(argc, argv) - , m_mainWindow(nullptr) + , m_mainWindow(nullptr), m_unixSignalNotifier(nullptr) { #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) installNativeEventFilter(new XcbEventFilter()); #elif defined(Q_OS_WIN) installNativeEventFilter(new WinEventFilter()); #endif +#if defined(Q_OS_UNIX) + registerUnixSignals(); +#endif } void Application::setMainWindow(QWidget* mainWindow) @@ -98,3 +110,56 @@ bool Application::event(QEvent* event) return QApplication::event(event); } + +#if defined(Q_OS_UNIX) +int Application::unixSignalSocket[2]; + +void Application::registerUnixSignals() +{ + int result = ::socketpair(AF_UNIX, SOCK_STREAM, 0, unixSignalSocket); + Q_ASSERT(0 == result); + if (0 != result) { + // do not register handles when socket creation failed, otherwise + // application will be unresponsive to signals such as SIGINT or SIGTERM + return; + } + + QVector const handledSignals = { SIGQUIT, SIGINT, SIGTERM, SIGHUP }; + for (auto s: handledSignals) { + struct sigaction sigAction; + + sigAction.sa_handler = handleUnixSignal; + sigemptyset(&sigAction.sa_mask); + sigAction.sa_flags = 0 | SA_RESTART; + sigaction(s, &sigAction, nullptr); + } + + m_unixSignalNotifier = new QSocketNotifier(unixSignalSocket[1], QSocketNotifier::Read, this); + connect(m_unixSignalNotifier, SIGNAL(activated(int)), this, SLOT(quitBySignal())); +} + +void Application::handleUnixSignal(int sig) +{ + switch (sig) { + case SIGQUIT: + case SIGINT: + case SIGTERM: + { + char buf = 0; + ::write(unixSignalSocket[0], &buf, sizeof(buf)); + return; + } + case SIGHUP: + return; + } +} + +void Application::quitBySignal() +{ + char buf; + ::read(unixSignalSocket[1], &buf, sizeof(buf)); + + if (nullptr != m_mainWindow) + static_cast(m_mainWindow)->appExit(); +} +#endif diff --git a/src/gui/Application.h b/src/gui/Application.h index 149b61ddf..44e03d2f8 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -21,6 +21,8 @@ #include +class QSocketNotifier; + class Application : public QApplication { Q_OBJECT @@ -34,8 +36,21 @@ public: Q_SIGNALS: void openFile(const QString& filename); -private: +private Q_SLOTS: + void quitBySignal(); + +private: QWidget* m_mainWindow; + +#if defined(Q_OS_UNIX) + /** + * Register Unix signals such as SIGINT and SIGTERM for clean shutdown. + */ + void registerUnixSignals(); + QSocketNotifier* m_unixSignalNotifier; + static void handleUnixSignal(int sig); + static int unixSignalSocket[2]; +#endif }; #endif // KEEPASSX_APPLICATION_H diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index cf2c9cd96..ab9924a75 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -42,6 +42,7 @@ public: public Q_SLOTS: void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString()); + void appExit(); protected: void closeEvent(QCloseEvent* event) override; @@ -68,7 +69,6 @@ private Q_SLOTS: void applySettingsChanges(); void trayIconTriggered(QSystemTrayIcon::ActivationReason reason); void toggleWindow(); - void appExit(); void lockDatabasesAfterInactivity(); void repairDatabase(); From b5cf6c7161090bc8a266bdc11145ecf446d336de Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Wed, 18 Jan 2017 00:33:47 +0100 Subject: [PATCH 2/4] Add missing #ifdef around slot --- src/gui/Application.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/Application.h b/src/gui/Application.h index 44e03d2f8..9bfe4d549 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -37,9 +37,11 @@ Q_SIGNALS: void openFile(const QString& filename); private Q_SLOTS: +#if defined(Q_OS_UNIX) void quitBySignal(); +#endif -private: +private: QWidget* m_mainWindow; #if defined(Q_OS_UNIX) From 4eb39dc5ff06e247c022cb33d3803331270f432a Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Wed, 18 Jan 2017 00:39:36 +0100 Subject: [PATCH 3/4] Remove obsolete forward-declaration and disable QSocketNotifier after firing --- src/gui/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index 8c4a21f8b..cfc87c101 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -31,7 +31,6 @@ #include #endif -class MainWindow; #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) class XcbEventFilter : public QAbstractNativeEventFilter { @@ -156,6 +155,7 @@ void Application::handleUnixSignal(int sig) void Application::quitBySignal() { + m_unixSignalNotifier->setEnabled(false); char buf; ::read(unixSignalSocket[1], &buf, sizeof(buf)); From 70727895f7cbbbe7dad17cf9d0fa73969c58eb58 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Tue, 24 Jan 2017 22:24:34 -0500 Subject: [PATCH 4/4] Added ifdef guard --- src/gui/Application.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index cfc87c101..26d9d2283 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -72,7 +72,10 @@ public: Application::Application(int& argc, char** argv) : QApplication(argc, argv) - , m_mainWindow(nullptr), m_unixSignalNotifier(nullptr) + , m_mainWindow(nullptr) +#ifdef Q_OS_UNIX + , m_unixSignalNotifier(nullptr) +#endif { #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) installNativeEventFilter(new XcbEventFilter());