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();