From 704554d3a5f2d92b6f0b87641933380e357f9639 Mon Sep 17 00:00:00 2001 From: Kevin Yue Date: Sat, 15 Feb 2020 23:37:30 +0800 Subject: [PATCH] Handle unix signals --- GPService/GPService.pro | 6 +- GPService/gpservice.cpp | 14 ++++ GPService/gpservice.h | 2 + GPService/main.cpp | 10 ++- GPService/sigwatch.cpp | 176 ++++++++++++++++++++++++++++++++++++++++ GPService/sigwatch.h | 59 ++++++++++++++ 6 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 GPService/sigwatch.cpp create mode 100644 GPService/sigwatch.h diff --git a/GPService/GPService.pro b/GPService/GPService.pro index 256352a..b12d1bb 100644 --- a/GPService/GPService.pro +++ b/GPService/GPService.pro @@ -19,11 +19,13 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 HEADERS += \ - gpservice.h + gpservice.h \ + sigwatch.h SOURCES += \ gpservice.cpp \ - main.cpp + main.cpp \ + sigwatch.cpp DBUS_ADAPTORS += gpservice.xml diff --git a/GPService/gpservice.cpp b/GPService/gpservice.cpp index af3e08a..47baa33 100644 --- a/GPService/gpservice.cpp +++ b/GPService/gpservice.cpp @@ -61,6 +61,16 @@ GPService::GPService(QObject *parent) QObject::connect(openconnect, QOverload::of(&QProcess::finished), this, &GPService::onProcessFinished); } +void GPService::quit() +{ + if (openconnect->state() == QProcess::NotRunning) { + exit(0); + } else { + aboutToQuit = true; + openconnect->terminate(); + } +} + void GPService::connect(QString server, QString username, QString passwd) { if (status() != QProcess::NotRunning) { @@ -139,6 +149,10 @@ void GPService::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { log("Openconnect process exited with code " + QString::number(exitCode) + " and exit status " + QVariant::fromValue(exitStatus).toString()); emit disconnected(); + + if (aboutToQuit) { + exit(0); + }; } void GPService::log(QString msg) diff --git a/GPService/gpservice.h b/GPService/gpservice.h index ee0dfe0..345a87b 100644 --- a/GPService/gpservice.h +++ b/GPService/gpservice.h @@ -31,6 +31,7 @@ public slots: void connect(QString server, QString username, QString passwd); void disconnect(); int status(); + void quit(); private slots: void onProcessStarted(); @@ -41,6 +42,7 @@ private slots: private: QProcess *openconnect; + bool aboutToQuit = false; void log(QString msg); static QString findBinary(); diff --git a/GPService/main.cpp b/GPService/main.cpp index 530e1af..dc372ac 100644 --- a/GPService/main.cpp +++ b/GPService/main.cpp @@ -1,6 +1,7 @@ #include #include "gpservice.h" #include "singleapplication.h" +#include "sigwatch.h" int main(int argc, char *argv[]) { @@ -12,7 +13,14 @@ int main(int argc, char *argv[]) return 1; } - new GPService; + GPService service; + + UnixSignalWatcher sigwatch; + sigwatch.watchForSignal(SIGINT); + sigwatch.watchForSignal(SIGTERM); + sigwatch.watchForSignal(SIGQUIT); + sigwatch.watchForSignal(SIGHUP); + QObject::connect(&sigwatch, SIGNAL(unixSignal(int)), &service, SLOT(quit())); return app.exec(); } diff --git a/GPService/sigwatch.cpp b/GPService/sigwatch.cpp new file mode 100644 index 0000000..0374d64 --- /dev/null +++ b/GPService/sigwatch.cpp @@ -0,0 +1,176 @@ +/* + * Unix signal watcher for Qt. + * + * Copyright (C) 2014 Simon Knopp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include "sigwatch.h" + + +/*! + * \brief The UnixSignalWatcherPrivate class implements the back-end signal + * handling for the UnixSignalWatcher. + * + * \see http://qt-project.org/doc/qt-5.0/qtdoc/unix-signals.html + */ +class UnixSignalWatcherPrivate : public QObject +{ + UnixSignalWatcher * const q_ptr; + Q_DECLARE_PUBLIC(UnixSignalWatcher) + +public: + UnixSignalWatcherPrivate(UnixSignalWatcher *q); + ~UnixSignalWatcherPrivate(); + + void watchForSignal(int signal); + static void signalHandler(int signal); + + void _q_onNotify(int sockfd); + +private: + static int sockpair[2]; + QSocketNotifier *notifier; + QList watchedSignals; +}; + + +int UnixSignalWatcherPrivate::sockpair[2]; + +UnixSignalWatcherPrivate::UnixSignalWatcherPrivate(UnixSignalWatcher *q) : + q_ptr(q) +{ + // Create socket pair + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair)) { + qDebug() << "UnixSignalWatcher: socketpair: " << ::strerror(errno); + return; + } + + // Create a notifier for the read end of the pair + notifier = new QSocketNotifier(sockpair[1], QSocketNotifier::Read); + QObject::connect(notifier, SIGNAL(activated(int)), q, SLOT(_q_onNotify(int))); + notifier->setEnabled(true); +} + +UnixSignalWatcherPrivate::~UnixSignalWatcherPrivate() +{ + delete notifier; +} + +/*! + * Registers a handler for the given Unix \a signal. The handler will write to + * a socket pair, the other end of which is connected to a QSocketNotifier. + * This provides a way to break out of the asynchronous context from which the + * signal handler is called and back into the Qt event loop. + */ +void UnixSignalWatcherPrivate::watchForSignal(int signal) +{ + if (watchedSignals.contains(signal)) { + qDebug() << "Already watching for signal" << signal; + return; + } + + // Register a sigaction which will write to the socket pair + struct sigaction sigact; + sigact.sa_handler = UnixSignalWatcherPrivate::signalHandler; + sigact.sa_flags = 0; + ::sigemptyset(&sigact.sa_mask); + sigact.sa_flags |= SA_RESTART; + if (::sigaction(signal, &sigact, NULL)) { + qDebug() << "UnixSignalWatcher: sigaction: " << ::strerror(errno); + return; + } + + watchedSignals.append(signal); +} + +/*! + * Called when a Unix \a signal is received. Write to the socket to wake up the + * QSocketNotifier. + */ +void UnixSignalWatcherPrivate::signalHandler(int signal) +{ + ssize_t nBytes = ::write(sockpair[0], &signal, sizeof(signal)); + Q_UNUSED(nBytes); +} + +/*! + * Called when the signal handler has written to the socket pair. Emits the Unix + * signal as a Qt signal. + */ +void UnixSignalWatcherPrivate::_q_onNotify(int sockfd) +{ + Q_Q(UnixSignalWatcher); + + int signal; + ssize_t nBytes = ::read(sockfd, &signal, sizeof(signal)); + Q_UNUSED(nBytes); + qDebug() << "Caught signal:" << ::strsignal(signal); + emit q->unixSignal(signal); +} + + +/*! + * Create a new UnixSignalWatcher as a child of the given \a parent. + */ +UnixSignalWatcher::UnixSignalWatcher(QObject *parent) : + QObject(parent), + d_ptr(new UnixSignalWatcherPrivate(this)) +{ +} + +/*! + * Destroy this UnixSignalWatcher. + */ +UnixSignalWatcher::~UnixSignalWatcher() +{ + delete d_ptr; +} + +/*! + * Register a signal handler for the given \a signal. + * + * After calling this method you can \c connect() to the unixSignal() Qt signal + * to be notified when the Unix signal is received. + */ +void UnixSignalWatcher::watchForSignal(int signal) +{ + Q_D(UnixSignalWatcher); + d->watchForSignal(signal); +} + +/*! + * \fn void UnixSignalWatcher::unixSignal(int signal) + * Emitted when the given Unix \a signal is received. + * + * watchForSignal() must be called for each Unix signal that you want to receive + * via the unixSignal() Qt signal. If a watcher is watching multiple signals, + * unixSignal() will be emitted whenever *any* of the watched Unix signals are + * received, and the \a signal argument can be inspected to find out which one + * was actually received. + */ + +#include "moc_sigwatch.cpp" diff --git a/GPService/sigwatch.h b/GPService/sigwatch.h new file mode 100644 index 0000000..135fd66 --- /dev/null +++ b/GPService/sigwatch.h @@ -0,0 +1,59 @@ +/* + * Unix signal watcher for Qt. + * + * Copyright (C) 2014 Simon Knopp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SIGWATCH_H +#define SIGWATCH_H + +#include +#include + +class UnixSignalWatcherPrivate; + + +/*! + * \brief The UnixSignalWatcher class converts Unix signals to Qt signals. + * + * To watch for a given signal, e.g. \c SIGINT, call \c watchForSignal(SIGINT) + * and \c connect() your handler to unixSignal(). + */ + +class UnixSignalWatcher : public QObject +{ + Q_OBJECT +public: + explicit UnixSignalWatcher(QObject *parent = 0); + ~UnixSignalWatcher(); + + void watchForSignal(int signal); + +signals: + void unixSignal(int signal); + +private: + UnixSignalWatcherPrivate * const d_ptr; + Q_DECLARE_PRIVATE(UnixSignalWatcher) + Q_PRIVATE_SLOT(d_func(), void _q_onNotify(int)) +}; + +#endif // SIGWATCH_H