Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Implement clean shutdown after receiving Unix signals, resolves #169 #170

Merged
merged 4 commits into from
Jan 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions src/gui/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,20 @@
*/

#include "Application.h"
#include "MainWindow.h"

#include <QAbstractNativeEventFilter>
#include <QFileOpenEvent>
#include <QSocketNotifier>

#include "autotype/AutoType.h"

#if defined(Q_OS_UNIX)
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#endif

#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
class XcbEventFilter : public QAbstractNativeEventFilter
{
Expand Down Expand Up @@ -65,12 +73,18 @@ class WinEventFilter : public QAbstractNativeEventFilter
Application::Application(int& argc, char** argv)
: QApplication(argc, argv)
, m_mainWindow(nullptr)
#ifdef Q_OS_UNIX
, m_unixSignalNotifier(nullptr)
#endif
{
#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)
Expand Down Expand Up @@ -98,3 +112,57 @@ 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<int> 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()
{
m_unixSignalNotifier->setEnabled(false);
char buf;
::read(unixSignalSocket[1], &buf, sizeof(buf));

if (nullptr != m_mainWindow)
static_cast<MainWindow*>(m_mainWindow)->appExit();
}
#endif
17 changes: 17 additions & 0 deletions src/gui/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#include <QApplication>

class QSocketNotifier;

class Application : public QApplication
{
Q_OBJECT
Expand All @@ -34,8 +36,23 @@ class Application : public QApplication
Q_SIGNALS:
void openFile(const QString& filename);

private Q_SLOTS:
#if defined(Q_OS_UNIX)
void quitBySignal();
#endif

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
2 changes: 1 addition & 1 deletion src/gui/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class MainWindow : public QMainWindow
public Q_SLOTS:
void openDatabase(const QString& fileName, const QString& pw = QString(),
const QString& keyFile = QString());
void appExit();

protected:
void closeEvent(QCloseEvent* event) override;
Expand All @@ -68,7 +69,6 @@ private Q_SLOTS:
void applySettingsChanges();
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
void toggleWindow();
void appExit();
void lockDatabasesAfterInactivity();
void repairDatabase();

Expand Down