From a0907e46586806d2f4fea9fdeb0a6241a96b3d1b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 30 Nov 2016 18:55:19 -0800 Subject: [PATCH] Single instance app guarding mechanism --- qgroundcontrol.pro | 2 ++ src/RunGuard.cc | 87 ++++++++++++++++++++++++++++++++++++++++++++++ src/RunGuard.h | 38 ++++++++++++++++++++ src/main.cc | 20 +++++------ 4 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 src/RunGuard.cc create mode 100644 src/RunGuard.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index f05699945..85adc4fab 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -529,6 +529,7 @@ HEADERS += \ src/Joystick/JoystickSDL.h \ src/QGCFileDialog.h \ src/QGCMessageBox.h \ + src/RunGuard.h \ src/ViewWidgets/CustomCommandWidget.h \ src/ViewWidgets/CustomCommandWidgetController.h \ src/ViewWidgets/ViewWidgetController.h \ @@ -677,6 +678,7 @@ SOURCES += \ src/GPS/RTCM/RTCMMavlink.cc \ src/Joystick/JoystickSDL.cc \ src/QGCFileDialog.cc \ + src/RunGuard.cc \ src/ViewWidgets/CustomCommandWidget.cc \ src/ViewWidgets/CustomCommandWidgetController.cc \ src/ViewWidgets/ViewWidgetController.cc \ diff --git a/src/RunGuard.cc b/src/RunGuard.cc new file mode 100644 index 000000000..5e8a49253 --- /dev/null +++ b/src/RunGuard.cc @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "RunGuard.h" + +#include + +namespace +{ + +QString generateKeyHash( const QString& key, const QString& salt ) +{ + QByteArray data; + + data.append( key.toUtf8() ); + data.append( salt.toUtf8() ); + data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex(); + + return data; +} + +} + +RunGuard::RunGuard( const QString& key ) + : key( key ) + , memLockKey( generateKeyHash( key, "_memLockKey" ) ) + , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) ) + , sharedMem( sharedmemKey ) + , memLock( memLockKey, 1 ) +{ + memLock.acquire(); + { + QSharedMemory fix( sharedmemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/ + fix.attach(); + } + memLock.release(); +} + +RunGuard::~RunGuard() +{ + release(); +} + +bool RunGuard::isAnotherRunning() +{ + if ( sharedMem.isAttached() ) + return false; + + memLock.acquire(); + const bool isRunning = sharedMem.attach(); + if ( isRunning ) + sharedMem.detach(); + memLock.release(); + + return isRunning; +} + +bool RunGuard::tryToRun() +{ + if ( isAnotherRunning() ) // Extra check + return false; + + memLock.acquire(); + const bool result = sharedMem.create( sizeof( quint64 ) ); + memLock.release(); + if ( !result ) + { + release(); + return false; + } + + return true; +} + +void RunGuard::release() +{ + memLock.acquire(); + if ( sharedMem.isAttached() ) + sharedMem.detach(); + memLock.release(); +} diff --git a/src/RunGuard.h b/src/RunGuard.h new file mode 100644 index 000000000..0e82f5b62 --- /dev/null +++ b/src/RunGuard.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#ifndef RunGuard_H +#define RunGuard_H + +#include +#include +#include + +class RunGuard +{ +public: + RunGuard( const QString& key ); + ~RunGuard(); + + bool isAnotherRunning(); + bool tryToRun(); + void release(); + +private: + const QString key; + const QString memLockKey; + const QString sharedmemKey; + + QSharedMemory sharedMem; + QSystemSemaphore memLock; + + Q_DISABLE_COPY( RunGuard ) +}; + +#endif diff --git a/src/main.cc b/src/main.cc index 80000cd33..4dec260fa 100644 --- a/src/main.cc +++ b/src/main.cc @@ -27,10 +27,9 @@ #include "QGCApplication.h" #include "AppMessages.h" -#define SINGLE_INSTANCE_PORT 14499 - #ifndef __mobile__ #include "QGCSerialPortInfo.h" + #include "RunGuard.h" #endif #ifdef UNITTEST_BUILD @@ -103,6 +102,13 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) int main(int argc, char *argv[]) { +#ifndef __mobile__ + RunGuard guard("QGroundControlRunGuardKey"); + if (!guard.tryToRun()) { + return 0; + } +#endif + #ifdef Q_OS_UNIX //Force writing to the console on UNIX/BSD devices if (!qEnvironmentVariableIsSet("QT_LOGGING_TO_CONSOLE")) @@ -112,16 +118,6 @@ int main(int argc, char *argv[]) // install the message handler AppMessages::installHandler(); -#ifndef __mobile__ - //-- Test for another instance already running. If that's the case, we simply exit. - QHostAddress host("127.0.0.1"); - QUdpSocket socket; - if(!socket.bind(host, SINGLE_INSTANCE_PORT, QAbstractSocket::DontShareAddress)) { - qWarning() << "Another instance already running. Exiting."; - exit(-1); - } -#endif - #ifdef Q_OS_MAC #ifndef __ios__ // Prevent Apple's app nap from screwing us over -- 2.22.0