From b2160255d360b38dbe4996c030e25733f7b912ed Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Mon, 26 Aug 2019 02:00:47 +0200 Subject: [PATCH] SingleApplication: Use geteuid / getpwuid to get username and reformat sources --- 3rdparty/singleapplication/CMakeLists.txt | 11 + 3rdparty/singleapplication/config.h.in | 2 + .../singleapplication/singleapplication.cpp | 225 +++---- .../singleapplication/singleapplication.h | 171 +++--- .../singleapplication/singleapplication_p.cpp | 572 +++++++++--------- .../singleapplication/singleapplication_p.h | 110 ++-- .../singlecoreapplication.cpp | 220 +++---- .../singleapplication/singlecoreapplication.h | 168 ++--- .../singlecoreapplication_p.cpp | 572 +++++++++--------- .../singlecoreapplication_p.h | 110 ++-- 10 files changed, 1127 insertions(+), 1034 deletions(-) create mode 100644 3rdparty/singleapplication/config.h.in diff --git a/3rdparty/singleapplication/CMakeLists.txt b/3rdparty/singleapplication/CMakeLists.txt index ed11f9d83..c026c807a 100644 --- a/3rdparty/singleapplication/CMakeLists.txt +++ b/3rdparty/singleapplication/CMakeLists.txt @@ -1,9 +1,17 @@ cmake_minimum_required(VERSION 2.8.11) +include(CheckIncludeFiles) +include(CheckFunctionExists) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive") +if(CMAKE_VERSION VERSION_GREATER 3.0) + check_function_exists(geteuid HAVE_GETEUID) + check_function_exists(getpwuid HAVE_GETPWUID) +endif() + set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp) set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h) QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS}) @@ -15,3 +23,6 @@ set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h) QT5_WRAP_CPP(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS}) ADD_LIBRARY(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC}) target_link_libraries(singlecoreapplication Qt5::Core Qt5::Widgets Qt5::Network) + +configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h") +include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/3rdparty/singleapplication/config.h.in b/3rdparty/singleapplication/config.h.in new file mode 100644 index 000000000..e7c1343f0 --- /dev/null +++ b/3rdparty/singleapplication/config.h.in @@ -0,0 +1,2 @@ +#cmakedefine HAVE_GETEUID +#cmakedefine HAVE_GETPWUID diff --git a/3rdparty/singleapplication/singleapplication.cpp b/3rdparty/singleapplication/singleapplication.cpp index f400a5e2a..25debf399 100644 --- a/3rdparty/singleapplication/singleapplication.cpp +++ b/3rdparty/singleapplication/singleapplication.cpp @@ -20,156 +20,165 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include -#include -#include -#include -#include -#include +// +// W A R N I N G !!! +// ----------------- +// +// This is a modified version of SingleApplication, +// The original version is at: +// +// https://github.com/itay-grudev/SingleApplication +// +// + +#include +#include +#include +#include +#include +#include +#include #include "singleapplication.h" #include "singleapplication_p.h" /** - * @brief Constructor. Checks and fires up LocalServer or closes the program - * if another instance already exists + * @brief Constructor. + * Checks and fires up LocalServer or closes the program if another instance already exists * @param argc * @param argv * @param {bool} allowSecondaryInstances */ -SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout ) - : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) ) -{ - Q_D(SingleApplication); +SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout) + : app_t(argc, argv), d_ptr(new SingleApplicationPrivate(this)) { - // Store the current mode of the program - d->options = options; + Q_D(SingleApplication); - // Generating an application ID used for identifying the shared memory - // block and QLocalServer - d->genBlockServerName(); + // Store the current mode of the program + d->options = options; + + // Generating an application ID used for identifying the shared memory block and QLocalServer + d->genBlockServerName(); #ifdef Q_OS_UNIX - // By explicitly attaching it and then deleting it we make sure that the - // memory is deleted even after the process has crashed on Unix. - d->memory = new QSharedMemory( d->blockServerName ); - d->memory->attach(); - delete d->memory; + // By explicitly attaching it and then deleting it we make sure that the + // memory is deleted even after the process has crashed on Unix. + d->memory = new QSharedMemory(d->blockServerName); + d->memory->attach(); + delete d->memory; #endif - // Guarantee thread safe behaviour with a shared memory block. - d->memory = new QSharedMemory( d->blockServerName ); + // Guarantee thread safe behaviour with a shared memory block. + d->memory = new QSharedMemory(d->blockServerName); - // Create a shared memory block - if( d->memory->create( sizeof( InstancesInfo ) ) ) { - // Initialize the shared memory block - d->memory->lock(); - d->initializeMemoryBlock(); - d->memory->unlock(); - } else { - // Attempt to attach to the memory segment - if( ! d->memory->attach() ) { - qCritical() << "SingleApplication: Unable to attach to shared memory block."; - qCritical() << d->memory->errorString(); - delete d; - ::exit( EXIT_FAILURE ); - } + // Create a shared memory block + if (d->memory->create(sizeof(InstancesInfo))) { + // Initialize the shared memory block + d->memory->lock(); + d->initializeMemoryBlock(); + d->memory->unlock(); + } + else { + // Attempt to attach to the memory segment + if (! d->memory->attach()) { + qCritical() << "SingleApplication: Unable to attach to shared memory block."; + qCritical() << d->memory->errorString(); + delete d; + ::exit(EXIT_FAILURE); } + } - InstancesInfo* inst = static_cast( d->memory->data() ); - QTime time; - time.start(); + InstancesInfo* inst = static_cast(d->memory->data()); + QTime time; + time.start(); - // Make sure the shared memory block is initialised and in consistent state - while( true ) { - d->memory->lock(); + // Make sure the shared memory block is initialised and in consistent state + while (true) { + d->memory->lock(); - if( d->blockChecksum() == inst->checksum ) break; + if (d->blockChecksum() == inst->checksum) break; - if( time.elapsed() > 5000 ) { - qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure."; - d->initializeMemoryBlock(); - } - - d->memory->unlock(); - - // Random sleep here limits the probability of a collision between two racing apps - qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits::max() ); - QThread::sleep( 8 + static_cast ( static_cast ( qrand() ) / RAND_MAX * 10 ) ); - } - - if( inst->primary == false) { - d->startPrimary(); - d->memory->unlock(); - return; - } - - // Check if another instance can be started - if( allowSecondary ) { - inst->secondary += 1; - inst->checksum = d->blockChecksum(); - d->instanceNumber = inst->secondary; - d->startSecondary(); - if( d->options & Mode::SecondaryNotification ) { - d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance ); - } - d->memory->unlock(); - return; + if (time.elapsed() > 5000) { + qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure."; + d->initializeMemoryBlock(); } d->memory->unlock(); - d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance ); + // Random sleep here limits the probability of a collision between two racing apps + qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits::max()); + QThread::sleep(8 + static_cast (static_cast (qrand()) / RAND_MAX * 10)); + } - delete d; + if (inst->primary == false) { + d->startPrimary(); + d->memory->unlock(); + return; + } + + // Check if another instance can be started + if (allowSecondary) { + inst->secondary += 1; + inst->checksum = d->blockChecksum(); + d->instanceNumber = inst->secondary; + d->startSecondary(); + if (d->options & Mode::SecondaryNotification) { + d->connectToPrimary(timeout, SingleApplicationPrivate::SecondaryInstance); + } + d->memory->unlock(); + return; + } + + d->memory->unlock(); + + d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance); + + delete d; + + ::exit(EXIT_SUCCESS); - ::exit( EXIT_SUCCESS ); } /** * @brief Destructor */ -SingleApplication::~SingleApplication() -{ - Q_D(SingleApplication); - delete d; +SingleApplication::~SingleApplication() { + Q_D(SingleApplication); + delete d; } -bool SingleApplication::isPrimary() -{ - Q_D(SingleApplication); - return d->server != nullptr; +bool SingleApplication::isPrimary() { + Q_D(SingleApplication); + return d->server != nullptr; } -bool SingleApplication::isSecondary() -{ - Q_D(SingleApplication); - return d->server == nullptr; +bool SingleApplication::isSecondary() { + Q_D(SingleApplication); + return d->server == nullptr; } -quint32 SingleApplication::instanceId() -{ - Q_D(SingleApplication); - return d->instanceNumber; +quint32 SingleApplication::instanceId() { + Q_D(SingleApplication); + return d->instanceNumber; } -qint64 SingleApplication::primaryPid() -{ - Q_D(SingleApplication); - return d->primaryPid(); +qint64 SingleApplication::primaryPid() { + Q_D(SingleApplication); + return d->primaryPid(); } -bool SingleApplication::sendMessage( QByteArray message, int timeout ) -{ - Q_D(SingleApplication); +bool SingleApplication::sendMessage(QByteArray message, int timeout) { - // Nobody to connect to - if( isPrimary() ) return false; + Q_D(SingleApplication); - // Make sure the socket is connected - d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ); + // Nobody to connect to + if (isPrimary()) return false; + + // Make sure the socket is connected + d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect); + + d->socket->write(message); + bool dataWritten = d->socket->waitForBytesWritten(timeout); + d->socket->flush(); + return dataWritten; - d->socket->write( message ); - bool dataWritten = d->socket->waitForBytesWritten( timeout ); - d->socket->flush(); - return dataWritten; } diff --git a/3rdparty/singleapplication/singleapplication.h b/3rdparty/singleapplication/singleapplication.h index 1ced4c05b..b4bac1f33 100644 --- a/3rdparty/singleapplication/singleapplication.h +++ b/3rdparty/singleapplication/singleapplication.h @@ -20,12 +20,23 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// +// W A R N I N G !!! +// ----------------- +// +// This is a modified version of SingleApplication, +// The original version is at: +// +// https://github.com/itay-grudev/SingleApplication +// +// + #ifndef SINGLEAPPLICATION_H #define SINGLEAPPLICATION_H -#include +#include #include -#include +#include class SingleApplicationPrivate; @@ -34,95 +45,95 @@ class SingleApplicationPrivate; * Application * @see QCoreApplication */ -class SingleApplication : public QApplication -{ - Q_OBJECT +class SingleApplication : public QApplication { + Q_OBJECT - typedef QApplication app_t; + typedef QApplication app_t; public: - /** - * @brief Mode of operation of SingleApplication. - * Whether the block should be user-wide or system-wide and whether the - * primary instance should be notified when a secondary instance had been - * started. - * @note Operating system can restrict the shared memory blocks to the same - * user, in which case the User/System modes will have no effect and the - * block will be user wide. - * @enum - */ - enum Mode { - User = 1 << 0, - System = 1 << 1, - SecondaryNotification = 1 << 2, - ExcludeAppVersion = 1 << 3, - ExcludeAppPath = 1 << 4 - }; - Q_DECLARE_FLAGS(Options, Mode) + /** + * @brief Mode of operation of SingleApplication. + * Whether the block should be user-wide or system-wide and whether the + * primary instance should be notified when a secondary instance had been + * started. + * @note Operating system can restrict the shared memory blocks to the same + * user, in which case the User/System modes will have no effect and the + * block will be user wide. + * @enum + */ + enum Mode { + User = 1 << 0, + System = 1 << 1, + SecondaryNotification = 1 << 2, + ExcludeAppVersion = 1 << 3, + ExcludeAppPath = 1 << 4 + }; + Q_DECLARE_FLAGS(Options, Mode) - /** - * @brief Intitializes a SingleApplication instance with argc command line - * arguments in argv - * @arg {int &} argc - Number of arguments in argv - * @arg {const char *[]} argv - Supplied command line arguments - * @arg {bool} allowSecondary - Whether to start the instance as secondary - * if there is already a primary instance. - * @arg {Mode} mode - Whether for the SingleApplication block to be applied - * User wide or System wide. - * @arg {int} timeout - Timeout to wait in milliseconds. - * @note argc and argv may be changed as Qt removes arguments that it - * recognizes - * @note Mode::SecondaryNotification only works if set on both the primary - * instance and the secondary instance. - * @note The timeout is just a hint for the maximum time of blocking - * operations. It does not guarantee that the SingleApplication - * initialisation will be completed in given time, though is a good hint. - * Usually 4*timeout would be the worst case (fail) scenario. - * @see See the corresponding QAPPLICATION_CLASS constructor for reference - */ - explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 ); - ~SingleApplication(); + /** + * @brief Intitializes a SingleApplication instance with argc command line + * arguments in argv + * @arg {int &} argc - Number of arguments in argv + * @arg {const char *[]} argv - Supplied command line arguments + * @arg {bool} allowSecondary - Whether to start the instance as secondary + * if there is already a primary instance. + * @arg {Mode} mode - Whether for the SingleApplication block to be applied + * User wide or System wide. + * @arg {int} timeout - Timeout to wait in milliseconds. + * @note argc and argv may be changed as Qt removes arguments that it + * recognizes + * @note Mode::SecondaryNotification only works if set on both the primary + * instance and the secondary instance. + * @note The timeout is just a hint for the maximum time of blocking + * operations. It does not guarantee that the SingleApplication + * initialisation will be completed in given time, though is a good hint. + * Usually 4*timeout would be the worst case (fail) scenario. + * @see See the corresponding QAPPLICATION_CLASS constructor for reference + */ + explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 ); + ~SingleApplication(); - /** - * @brief Returns if the instance is the primary instance - * @returns {bool} - */ - bool isPrimary(); + /** + * @brief Returns if the instance is the primary instance + * @returns {bool} + */ + bool isPrimary(); - /** - * @brief Returns if the instance is a secondary instance - * @returns {bool} - */ - bool isSecondary(); + /** + * @brief Returns if the instance is a secondary instance + * @returns {bool} + */ + bool isSecondary(); - /** - * @brief Returns a unique identifier for the current instance - * @returns {qint32} - */ - quint32 instanceId(); + /** + * @brief Returns a unique identifier for the current instance + * @returns {qint32} + */ + quint32 instanceId(); - /** - * @brief Returns the process ID (PID) of the primary instance - * @returns {qint64} - */ - qint64 primaryPid(); + /** + * @brief Returns the process ID (PID) of the primary instance + * @returns {qint64} + */ + qint64 primaryPid(); - /** - * @brief Sends a message to the primary instance. Returns true on success. - * @param {int} timeout - Timeout for connecting - * @returns {bool} - * @note sendMessage() will return false if invoked from the primary - * instance. - */ - bool sendMessage( QByteArray message, int timeout = 1000 ); + /** + * @brief Sends a message to the primary instance. Returns true on success. + * @param {int} timeout - Timeout for connecting + * @returns {bool} + * @note sendMessage() will return false if invoked from the primary + * instance. + */ + bool sendMessage( QByteArray message, int timeout = 1000 ); -Q_SIGNALS: - void instanceStarted(); - void receivedMessage( quint32 instanceId, QByteArray message ); + signals: + void instanceStarted(); + void receivedMessage( quint32 instanceId, QByteArray message ); + + private: + SingleApplicationPrivate *d_ptr; + Q_DECLARE_PRIVATE(SingleApplication) -private: - SingleApplicationPrivate *d_ptr; - Q_DECLARE_PRIVATE(SingleApplication) }; Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options) diff --git a/3rdparty/singleapplication/singleapplication_p.cpp b/3rdparty/singleapplication/singleapplication_p.cpp index f556b697e..2cad43725 100644 --- a/3rdparty/singleapplication/singleapplication_p.cpp +++ b/3rdparty/singleapplication/singleapplication_p.cpp @@ -24,370 +24,386 @@ // W A R N I N G !!! // ----------------- // -// This file is not part of the SingleApplication API. It is used purely as an -// implementation detail. This header file may change from version to -// version without notice, or may even be removed. +// This is a modified version of SingleApplication, +// The original version is at: // +// https://github.com/itay-grudev/SingleApplication +// +// + +#include "config.h" + +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef Q_OS_UNIX +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include #include "singleapplication.h" #include "singleapplication_p.h" #ifdef Q_OS_WIN - #include - #include +# include +# include #endif -SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr ) - : q_ptr( q_ptr ) -{ - server = nullptr; - socket = nullptr; - memory = nullptr; - instanceNumber = -1; +SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *q_ptr) + : q_ptr(q_ptr) { + + server = nullptr; + socket = nullptr; + memory = nullptr; + instanceNumber = -1; + } -SingleApplicationPrivate::~SingleApplicationPrivate() -{ - if( socket != nullptr ) { - socket->close(); - delete socket; - } +SingleApplicationPrivate::~SingleApplicationPrivate() { - memory->lock(); - InstancesInfo* inst = static_cast(memory->data()); - if( server != nullptr ) { - server->close(); - delete server; - inst->primary = false; - inst->primaryPid = -1; - inst->checksum = blockChecksum(); - } - memory->unlock(); + if (socket != nullptr) { + socket->close(); + delete socket; + } - delete memory; -} - -void SingleApplicationPrivate::genBlockServerName() -{ - QCryptographicHash appData( QCryptographicHash::Sha256 ); - appData.addData( "SingleApplication", 17 ); - appData.addData( SingleApplication::app_t::applicationName().toUtf8() ); - appData.addData( SingleApplication::app_t::organizationName().toUtf8() ); - appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() ); - - if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) { - appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() ); - } - - if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) { -#ifdef Q_OS_WIN - appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() ); -#else - appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() ); -#endif - } - - // User level block requires a user specific data in the hash - if( options & SingleApplication::Mode::User ) { -#ifdef Q_OS_WIN - wchar_t username [ UNLEN + 1 ]; - // Specifies size of the buffer on input - DWORD usernameLength = UNLEN + 1; - if( GetUserNameW( username, &usernameLength ) ) { - appData.addData( QString::fromWCharArray(username).toUtf8() ); - } else { - appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() ); - } -#endif -#ifdef Q_OS_UNIX - appData.addData(qgetenv("USER")); -#endif - } - - // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with - // server naming requirements. - blockServerName = appData.result().toBase64().replace("/", "_"); -} - -void SingleApplicationPrivate::initializeMemoryBlock() -{ - InstancesInfo* inst = static_cast( memory->data() ); + memory->lock(); + InstancesInfo* inst = static_cast(memory->data()); + if (server != nullptr) { + server->close(); + delete server; inst->primary = false; - inst->secondary = 0; inst->primaryPid = -1; inst->checksum = blockChecksum(); + } + memory->unlock(); + + delete memory; + } -void SingleApplicationPrivate::startPrimary() -{ - Q_Q(SingleApplication); +void SingleApplicationPrivate::genBlockServerName() { - // Successful creation means that no main process exists - // So we start a QLocalServer to listen for connections - QLocalServer::removeServer( blockServerName ); - server = new QLocalServer(); + QCryptographicHash appData(QCryptographicHash::Sha256); + appData.addData("SingleApplication", 17); + appData.addData(SingleApplication::app_t::applicationName().toUtf8()); + appData.addData(SingleApplication::app_t::organizationName().toUtf8()); + appData.addData(SingleApplication::app_t::organizationDomain().toUtf8()); - // Restrict access to the socket according to the - // SingleApplication::Mode::User flag on User level or no restrictions - if( options & SingleApplication::Mode::User ) { - server->setSocketOptions( QLocalServer::UserAccessOption ); - } else { - server->setSocketOptions( QLocalServer::WorldAccessOption ); + if (!(options & SingleApplication::Mode::ExcludeAppVersion)) { + appData.addData(SingleApplication::app_t::applicationVersion().toUtf8()); + } + + if (! (options & SingleApplication::Mode::ExcludeAppPath)) { +#ifdef Q_OS_WIN + appData.addData(SingleApplication::app_t::applicationFilePath().toLower().toUtf8()); +#else + appData.addData(SingleApplication::app_t::applicationFilePath().toUtf8()); +#endif + } + + // User level block requires a user specific data in the hash + if (options & SingleApplication::Mode::User) { +#ifdef Q_OS_UNIX + QByteArray username; +#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID) + uid_t uid = geteuid(); + if (uid != -1) { + struct passwd *pw = getpwuid(uid); + if (pw) { + username = pw->pw_name; + } } + qDebug() << username; +#endif + if (username.isEmpty()) username = qgetenv("USER"); + appData.addData(username); +#endif +#ifdef Q_OS_WIN + wchar_t username [ UNLEN + 1 ]; + // Specifies size of the buffer on input + DWORD usernameLength = UNLEN + 1; + if (GetUserNameW(username, &usernameLength)) { + appData.addData(QString::fromWCharArray(username).toUtf8()); + } +#endif + } - server->listen( blockServerName ); - QObject::connect( - server, - &QLocalServer::newConnection, - this, - &SingleApplicationPrivate::slotConnectionEstablished - ); + // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements. + blockServerName = appData.result().toBase64().replace("/", "_"); - // Reset the number of connections - InstancesInfo* inst = static_cast ( memory->data() ); - - inst->primary = true; - inst->primaryPid = q->applicationPid(); - inst->checksum = blockChecksum(); - - instanceNumber = 0; } -void SingleApplicationPrivate::startSecondary() -{ +void SingleApplicationPrivate::initializeMemoryBlock() { + + InstancesInfo* inst = static_cast(memory->data()); + inst->primary = false; + inst->secondary = 0; + inst->primaryPid = -1; + inst->checksum = blockChecksum(); + } -void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType ) -{ - // Connect to the Local Server of the Primary Instance if not already - // connected. - if( socket == nullptr ) { - socket = new QLocalSocket(); - } +void SingleApplicationPrivate::startPrimary() { - // If already connected - we are done; - if( socket->state() == QLocalSocket::ConnectedState ) - return; + Q_Q(SingleApplication); - // If not connect - if( socket->state() == QLocalSocket::UnconnectedState || - socket->state() == QLocalSocket::ClosingState ) { - socket->connectToServer( blockServerName ); - } + // Successful creation means that no main process exists + // So we start a QLocalServer to listen for connections + QLocalServer::removeServer(blockServerName); + server = new QLocalServer(); - // Wait for being connected - if( socket->state() == QLocalSocket::ConnectingState ) { - socket->waitForConnected( msecs ); - } + // Restrict access to the socket according to the + // SingleApplication::Mode::User flag on User level or no restrictions + if (options & SingleApplication::Mode::User) { + server->setSocketOptions(QLocalServer::UserAccessOption); + } + else { + server->setSocketOptions(QLocalServer::WorldAccessOption); + } - // Initialisation message according to the SingleApplication protocol - if( socket->state() == QLocalSocket::ConnectedState ) { - // Notify the parent that a new instance had been started; - QByteArray initMsg; - QDataStream writeStream(&initMsg, QIODevice::WriteOnly); + server->listen(blockServerName); + QObject::connect(server, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished); + + // Reset the number of connections + InstancesInfo* inst = static_cast (memory->data()); + + inst->primary = true; + inst->primaryPid = q->applicationPid(); + inst->checksum = blockChecksum(); + + instanceNumber = 0; + +} + +void SingleApplicationPrivate::startSecondary() {} + +void SingleApplicationPrivate::connectToPrimary(int msecs, ConnectionType connectionType) { + + // Connect to the Local Server of the Primary Instance if not already connected. + if (socket == nullptr) { + socket = new QLocalSocket(); + } + + // If already connected - we are done; + if (socket->state() == QLocalSocket::ConnectedState) + return; + + // If not connect + if (socket->state() == QLocalSocket::UnconnectedState || + socket->state() == QLocalSocket::ClosingState) { + socket->connectToServer(blockServerName); + } + + // Wait for being connected + if (socket->state() == QLocalSocket::ConnectingState) { + socket->waitForConnected(msecs); + } + + // Initialisation message according to the SingleApplication protocol + if (socket->state() == QLocalSocket::ConnectedState) { + // Notify the parent that a new instance had been started; + QByteArray initMsg; + QDataStream writeStream(&initMsg, QIODevice::WriteOnly); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - writeStream.setVersion(QDataStream::Qt_5_6); + writeStream.setVersion(QDataStream::Qt_5_6); #endif - writeStream << blockServerName.toLatin1(); - writeStream << static_cast(connectionType); - writeStream << instanceNumber; - quint16 checksum = qChecksum(initMsg.constData(), static_cast(initMsg.length())); - writeStream << checksum; + writeStream << blockServerName.toLatin1(); + writeStream << static_cast(connectionType); + writeStream << instanceNumber; + quint16 checksum = qChecksum(initMsg.constData(), static_cast(initMsg.length())); + writeStream << checksum; - // The header indicates the message length that follows - QByteArray header; - QDataStream headerStream(&header, QIODevice::WriteOnly); + // The header indicates the message length that follows + QByteArray header; + QDataStream headerStream(&header, QIODevice::WriteOnly); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - headerStream.setVersion(QDataStream::Qt_5_6); + headerStream.setVersion(QDataStream::Qt_5_6); #endif - headerStream << static_cast ( initMsg.length() ); + headerStream << static_cast (initMsg.length()); + + socket->write(header); + socket->write(initMsg); + socket->flush(); + socket->waitForBytesWritten(msecs); + } - socket->write( header ); - socket->write( initMsg ); - socket->flush(); - socket->waitForBytesWritten( msecs ); - } } -quint16 SingleApplicationPrivate::blockChecksum() -{ - return qChecksum( - static_cast ( memory->data() ), - offsetof( InstancesInfo, checksum ) - ); +quint16 SingleApplicationPrivate::blockChecksum() { + + return qChecksum(static_cast (memory->data()), offsetof(InstancesInfo, checksum)); + } -qint64 SingleApplicationPrivate::primaryPid() -{ - qint64 pid; +qint64 SingleApplicationPrivate::primaryPid() { - memory->lock(); - InstancesInfo* inst = static_cast( memory->data() ); - pid = inst->primaryPid; - memory->unlock(); + qint64 pid; + + memory->lock(); + InstancesInfo* inst = static_cast(memory->data()); + pid = inst->primaryPid; + memory->unlock(); + + return pid; - return pid; } /** * @brief Executed when a connection has been made to the LocalServer */ -void SingleApplicationPrivate::slotConnectionEstablished() -{ - QLocalSocket *nextConnSocket = server->nextPendingConnection(); - connectionMap.insert(nextConnSocket, ConnectionInfo()); +void SingleApplicationPrivate::slotConnectionEstablished() { - QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, - [nextConnSocket, this]() { - auto &info = connectionMap[nextConnSocket]; - Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId ); - } - ); + QLocalSocket *nextConnSocket = server->nextPendingConnection(); + connectionMap.insert(nextConnSocket, ConnectionInfo()); - QObject::connect(nextConnSocket, &QLocalSocket::disconnected, - [nextConnSocket, this](){ - connectionMap.remove(nextConnSocket); - nextConnSocket->deleteLater(); - } - ); + QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, + [nextConnSocket, this]() { + auto &info = connectionMap[nextConnSocket]; + Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId); + } + ); + + QObject::connect(nextConnSocket, &QLocalSocket::disconnected, + [nextConnSocket, this](){ + connectionMap.remove(nextConnSocket); + nextConnSocket->deleteLater(); + } + ); + + QObject::connect(nextConnSocket, &QLocalSocket::readyRead, + [nextConnSocket, this]() { + auto &info = connectionMap[nextConnSocket]; + switch(info.stage) { + case StageHeader: + readInitMessageHeader(nextConnSocket); + break; + case StageBody: + readInitMessageBody(nextConnSocket); + break; + case StageConnected: + Q_EMIT this->slotDataAvailable(nextConnSocket, info.instanceId); + break; + default: + break; + }; + } + ); - QObject::connect(nextConnSocket, &QLocalSocket::readyRead, - [nextConnSocket, this]() { - auto &info = connectionMap[nextConnSocket]; - switch(info.stage) { - case StageHeader: - readInitMessageHeader(nextConnSocket); - break; - case StageBody: - readInitMessageBody(nextConnSocket); - break; - case StageConnected: - Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId ); - break; - default: - break; - }; - } - ); } -void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock ) -{ - if (!connectionMap.contains( sock )) { - return; - } +void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) { - if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) { - return; - } + if (!connectionMap.contains(sock)) { + return; + } - QDataStream headerStream( sock ); + if (sock->bytesAvailable() < (qint64) sizeof(quint64)) { + return; + } + + QDataStream headerStream(sock); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - headerStream.setVersion( QDataStream::Qt_5_6 ); + headerStream.setVersion(QDataStream::Qt_5_6); #endif - // Read the header to know the message length - quint64 msgLen = 0; - headerStream >> msgLen; - ConnectionInfo &info = connectionMap[sock]; - info.stage = StageBody; - info.msgLen = msgLen; + // Read the header to know the message length + quint64 msgLen = 0; + headerStream >> msgLen; + ConnectionInfo &info = connectionMap[sock]; + info.stage = StageBody; + info.msgLen = msgLen; + + if (sock->bytesAvailable() >= (qint64) msgLen) { + readInitMessageBody(sock); + } - if ( sock->bytesAvailable() >= (qint64) msgLen ) { - readInitMessageBody( sock ); - } } -void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock ) -{ - Q_Q(SingleApplication); +void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) { - if (!connectionMap.contains( sock )) { - return; - } + Q_Q(SingleApplication); - ConnectionInfo &info = connectionMap[sock]; - if( sock->bytesAvailable() < ( qint64 )info.msgLen ) { - return; - } + if (!connectionMap.contains(sock)) { + return; + } - // Read the message body - QByteArray msgBytes = sock->read(info.msgLen); - QDataStream readStream(msgBytes); + ConnectionInfo &info = connectionMap[sock]; + if (sock->bytesAvailable() < (qint64)info.msgLen) { + return; + } + + // Read the message body + QByteArray msgBytes = sock->read(info.msgLen); + QDataStream readStream(msgBytes); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - readStream.setVersion( QDataStream::Qt_5_6 ); + readStream.setVersion(QDataStream::Qt_5_6); #endif - // server name - QByteArray latin1Name; - readStream >> latin1Name; + // server name + QByteArray latin1Name; + readStream >> latin1Name; - // connection type - ConnectionType connectionType = InvalidConnection; - quint8 connTypeVal = InvalidConnection; - readStream >> connTypeVal; - connectionType = static_cast ( connTypeVal ); + // connection type + ConnectionType connectionType = InvalidConnection; + quint8 connTypeVal = InvalidConnection; + readStream >> connTypeVal; + connectionType = static_cast (connTypeVal); - // instance id - quint32 instanceId = 0; - readStream >> instanceId; + // instance id + quint32 instanceId = 0; + readStream >> instanceId; - // checksum - quint16 msgChecksum = 0; - readStream >> msgChecksum; + // checksum + quint16 msgChecksum = 0; + readStream >> msgChecksum; - const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast( msgBytes.length() - sizeof( quint16 ) ) ); + const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast(msgBytes.length() - sizeof(quint16))); - bool isValid = readStream.status() == QDataStream::Ok && - QLatin1String(latin1Name) == blockServerName && - msgChecksum == actualChecksum; + bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum; - if( !isValid ) { - sock->close(); - return; - } + if (!isValid) { + sock->close(); + return; + } - info.instanceId = instanceId; - info.stage = StageConnected; + info.instanceId = instanceId; + info.stage = StageConnected; - if( connectionType == NewInstance || - ( connectionType == SecondaryInstance && - options & SingleApplication::Mode::SecondaryNotification ) ) - { - Q_EMIT q->instanceStarted(); - } + if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleApplication::Mode::SecondaryNotification)) { + Q_EMIT q->instanceStarted(); + } + + if (sock->bytesAvailable() > 0) { + Q_EMIT this->slotDataAvailable(sock, instanceId); + } - if (sock->bytesAvailable() > 0) { - Q_EMIT this->slotDataAvailable( sock, instanceId ); - } } -void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId ) -{ - Q_Q(SingleApplication); - Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() ); +void SingleApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, quint32 instanceId) { + + Q_Q(SingleApplication); + Q_EMIT q->receivedMessage(instanceId, dataSocket->readAll()); + } -void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId ) -{ - if( closedSocket->bytesAvailable() > 0 ) - Q_EMIT slotDataAvailable( closedSocket, instanceId ); +void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, quint32 instanceId) { + + if (closedSocket->bytesAvailable() > 0) + Q_EMIT slotDataAvailable(closedSocket, instanceId); + } diff --git a/3rdparty/singleapplication/singleapplication_p.h b/3rdparty/singleapplication/singleapplication_p.h index e2c361fb9..7e752b12f 100644 --- a/3rdparty/singleapplication/singleapplication_p.h +++ b/3rdparty/singleapplication/singleapplication_p.h @@ -24,76 +24,80 @@ // W A R N I N G !!! // ----------------- // -// This file is not part of the SingleApplication API. It is used purely as an -// implementation detail. This header file may change from version to -// version without notice, or may even be removed. +// This is a modified version of SingleApplication, +// The original version is at: +// +// https://github.com/itay-grudev/SingleApplication +// // #ifndef SINGLEAPPLICATION_P_H #define SINGLEAPPLICATION_P_H -#include -#include -#include +#include +#include +#include +#include +#include + #include "singleapplication.h" struct InstancesInfo { - bool primary; - quint32 secondary; - qint64 primaryPid; - quint16 checksum; + bool primary; + quint32 secondary; + qint64 primaryPid; + quint16 checksum; }; struct ConnectionInfo { - explicit ConnectionInfo() : - msgLen(0), instanceId(0), stage(0) {} - qint64 msgLen; - quint32 instanceId; - quint8 stage; + explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {} + qint64 msgLen; + quint32 instanceId; + quint8 stage; }; class SingleApplicationPrivate : public QObject { -Q_OBJECT -public: - enum ConnectionType : quint8 { - InvalidConnection = 0, - NewInstance = 1, - SecondaryInstance = 2, - Reconnect = 3 - }; - enum ConnectionStage : quint8 { - StageHeader = 0, - StageBody = 1, - StageConnected = 2, - }; - Q_DECLARE_PUBLIC(SingleApplication) + Q_OBJECT + public: + enum ConnectionType : quint8 { + InvalidConnection = 0, + NewInstance = 1, + SecondaryInstance = 2, + Reconnect = 3 + }; + enum ConnectionStage : quint8 { + StageHeader = 0, + StageBody = 1, + StageConnected = 2, + }; + Q_DECLARE_PUBLIC(SingleApplication) - SingleApplicationPrivate( SingleApplication *q_ptr ); - ~SingleApplicationPrivate(); + SingleApplicationPrivate( SingleApplication *q_ptr ); + ~SingleApplicationPrivate(); - void genBlockServerName(); - void initializeMemoryBlock(); - void startPrimary(); - void startSecondary(); - void connectToPrimary(int msecs, ConnectionType connectionType ); - quint16 blockChecksum(); - qint64 primaryPid(); - void readInitMessageHeader(QLocalSocket *socket); - void readInitMessageBody(QLocalSocket *socket); + void genBlockServerName(); + void initializeMemoryBlock(); + void startPrimary(); + void startSecondary(); + void connectToPrimary(int msecs, ConnectionType connectionType ); + quint16 blockChecksum(); + qint64 primaryPid(); + void readInitMessageHeader(QLocalSocket *socket); + void readInitMessageBody(QLocalSocket *socket); - SingleApplication *q_ptr; - QSharedMemory *memory; - QLocalSocket *socket; - QLocalServer *server; - quint32 instanceNumber; - QString blockServerName; - SingleApplication::Options options; - QMap connectionMap; + SingleApplication *q_ptr; + QSharedMemory *memory; + QLocalSocket *socket; + QLocalServer *server; + quint32 instanceNumber; + QString blockServerName; + SingleApplication::Options options; + QMap connectionMap; -public Q_SLOTS: - void slotConnectionEstablished(); - void slotDataAvailable( QLocalSocket*, quint32 ); - void slotClientConnectionClosed( QLocalSocket*, quint32 ); + public slots: + void slotConnectionEstablished(); + void slotDataAvailable(QLocalSocket*, quint32); + void slotClientConnectionClosed(QLocalSocket*, quint32); }; -#endif // SINGLEAPPLICATION_P_H +#endif // SINGLEAPPLICATION_P_H diff --git a/3rdparty/singleapplication/singlecoreapplication.cpp b/3rdparty/singleapplication/singlecoreapplication.cpp index 498d2b34b..8ff93200e 100644 --- a/3rdparty/singleapplication/singlecoreapplication.cpp +++ b/3rdparty/singleapplication/singlecoreapplication.cpp @@ -20,12 +20,24 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// +// W A R N I N G !!! +// ----------------- +// +// This is a modified version of SingleApplication, +// The original version is at: +// +// https://github.com/itay-grudev/SingleApplication +// +// + +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include "singlecoreapplication.h" #include "singlecoreapplication_p.h" @@ -37,139 +49,137 @@ * @param argv * @param {bool} allowSecondaryInstances */ -SingleCoreApplication::SingleCoreApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout ) - : app_t( argc, argv ), d_ptr( new SingleCoreApplicationPrivate( this ) ) -{ - Q_D(SingleCoreApplication); +SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout) + : app_t(argc, argv), d_ptr(new SingleCoreApplicationPrivate(this)) { - // Store the current mode of the program - d->options = options; + Q_D(SingleCoreApplication); - // Generating an application ID used for identifying the shared memory - // block and QLocalServer - d->genBlockServerName(); + // Store the current mode of the program + d->options = options; + + // Generating an application ID used for identifying the shared memory + // block and QLocalServer + d->genBlockServerName(); #ifdef Q_OS_UNIX - // By explicitly attaching it and then deleting it we make sure that the - // memory is deleted even after the process has crashed on Unix. - d->memory = new QSharedMemory( d->blockServerName ); - d->memory->attach(); - delete d->memory; + // By explicitly attaching it and then deleting it we make sure that the + // memory is deleted even after the process has crashed on Unix. + d->memory = new QSharedMemory(d->blockServerName); + d->memory->attach(); + delete d->memory; #endif - // Guarantee thread safe behaviour with a shared memory block. - d->memory = new QSharedMemory( d->blockServerName ); + // Guarantee thread safe behaviour with a shared memory block. + d->memory = new QSharedMemory(d->blockServerName); - // Create a shared memory block - if( d->memory->create( sizeof( InstancesInfo ) ) ) { - // Initialize the shared memory block - d->memory->lock(); - d->initializeMemoryBlock(); - d->memory->unlock(); - } else { - // Attempt to attach to the memory segment - if( ! d->memory->attach() ) { - qCritical() << "SingleCoreApplication: Unable to attach to shared memory block."; - qCritical() << d->memory->errorString(); - delete d; - ::exit( EXIT_FAILURE ); - } + // Create a shared memory block + if (d->memory->create(sizeof(InstancesInfo))) { + // Initialize the shared memory block + d->memory->lock(); + d->initializeMemoryBlock(); + d->memory->unlock(); + } + else { + // Attempt to attach to the memory segment + if (!d->memory->attach()) { + qCritical() << "SingleCoreApplication: Unable to attach to shared memory block."; + qCritical() << d->memory->errorString(); + delete d; + ::exit(EXIT_FAILURE); } + } - InstancesInfo* inst = static_cast( d->memory->data() ); - QTime time; - time.start(); + InstancesInfo* inst = static_cast(d->memory->data()); + QTime time; + time.start(); - // Make sure the shared memory block is initialised and in consistent state - while( true ) { - d->memory->lock(); + // Make sure the shared memory block is initialised and in consistent state + while (true) { + d->memory->lock(); - if( d->blockChecksum() == inst->checksum ) break; + if(d->blockChecksum() == inst->checksum) break; - if( time.elapsed() > 5000 ) { - qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure."; - d->initializeMemoryBlock(); - } - - d->memory->unlock(); - - // Random sleep here limits the probability of a collision between two racing apps - qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits::max() ); - QThread::sleep( 8 + static_cast ( static_cast ( qrand() ) / RAND_MAX * 10 ) ); - } - - if( inst->primary == false) { - d->startPrimary(); - d->memory->unlock(); - return; - } - - // Check if another instance can be started - if( allowSecondary ) { - inst->secondary += 1; - inst->checksum = d->blockChecksum(); - d->instanceNumber = inst->secondary; - d->startSecondary(); - if( d->options & Mode::SecondaryNotification ) { - d->connectToPrimary( timeout, SingleCoreApplicationPrivate::SecondaryInstance ); - } - d->memory->unlock(); - return; + if (time.elapsed() > 5000) { + qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure."; + d->initializeMemoryBlock(); } d->memory->unlock(); - d->connectToPrimary( timeout, SingleCoreApplicationPrivate::NewInstance ); + // Random sleep here limits the probability of a collision between two racing apps + qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits::max()); + QThread::sleep(8 + static_cast (static_cast (qrand()) / RAND_MAX * 10)); + } - delete d; + if (inst->primary == false) { + d->startPrimary(); + d->memory->unlock(); + return; + } + + // Check if another instance can be started + if (allowSecondary) { + inst->secondary += 1; + inst->checksum = d->blockChecksum(); + d->instanceNumber = inst->secondary; + d->startSecondary(); + if(d->options & Mode::SecondaryNotification) { + d->connectToPrimary(timeout, SingleCoreApplicationPrivate::SecondaryInstance); + } + d->memory->unlock(); + return; + } + + d->memory->unlock(); + + d->connectToPrimary(timeout, SingleCoreApplicationPrivate::NewInstance); + + delete d; + + ::exit(EXIT_SUCCESS); - ::exit( EXIT_SUCCESS ); } /** * @brief Destructor */ -SingleCoreApplication::~SingleCoreApplication() -{ - Q_D(SingleCoreApplication); - delete d; +SingleCoreApplication::~SingleCoreApplication() { + Q_D(SingleCoreApplication); + delete d; } -bool SingleCoreApplication::isPrimary() -{ - Q_D(SingleCoreApplication); - return d->server != nullptr; +bool SingleCoreApplication::isPrimary() { + Q_D(SingleCoreApplication); + return d->server != nullptr; } -bool SingleCoreApplication::isSecondary() -{ - Q_D(SingleCoreApplication); - return d->server == nullptr; +bool SingleCoreApplication::isSecondary() { + Q_D(SingleCoreApplication); + return d->server == nullptr; } -quint32 SingleCoreApplication::instanceId() -{ - Q_D(SingleCoreApplication); - return d->instanceNumber; +quint32 SingleCoreApplication::instanceId() { + Q_D(SingleCoreApplication); + return d->instanceNumber; } -qint64 SingleCoreApplication::primaryPid() -{ - Q_D(SingleCoreApplication); - return d->primaryPid(); +qint64 SingleCoreApplication::primaryPid() { + Q_D(SingleCoreApplication); + return d->primaryPid(); } -bool SingleCoreApplication::sendMessage( QByteArray message, int timeout ) -{ - Q_D(SingleCoreApplication); +bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) { - // Nobody to connect to - if( isPrimary() ) return false; + Q_D(SingleCoreApplication); - // Make sure the socket is connected - d->connectToPrimary( timeout, SingleCoreApplicationPrivate::Reconnect ); + // Nobody to connect to + if(isPrimary()) return false; + + // Make sure the socket is connected + d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect); + + d->socket->write(message); + bool dataWritten = d->socket->waitForBytesWritten(timeout); + d->socket->flush(); + return dataWritten; - d->socket->write( message ); - bool dataWritten = d->socket->waitForBytesWritten( timeout ); - d->socket->flush(); - return dataWritten; } diff --git a/3rdparty/singleapplication/singlecoreapplication.h b/3rdparty/singleapplication/singlecoreapplication.h index 661e726ea..18a95ad5d 100644 --- a/3rdparty/singleapplication/singlecoreapplication.h +++ b/3rdparty/singleapplication/singlecoreapplication.h @@ -20,12 +20,23 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// +// W A R N I N G !!! +// ----------------- +// +// This is a modified version of SingleApplication, +// The original version is at: +// +// https://github.com/itay-grudev/SingleApplication +// +// + #ifndef SINGLECOREAPPLICATION_H #define SINGLECOREAPPLICATION_H -#include +#include #include -#include +#include class SingleCoreApplicationPrivate; @@ -34,95 +45,94 @@ class SingleCoreApplicationPrivate; * Application * @see QCoreApplication */ -class SingleCoreApplication : public QCoreApplication -{ - Q_OBJECT +class SingleCoreApplication : public QCoreApplication { + Q_OBJECT - typedef QCoreApplication app_t; + typedef QCoreApplication app_t; public: - /** - * @brief Mode of operation of SingleCoreApplication. - * Whether the block should be user-wide or system-wide and whether the - * primary instance should be notified when a secondary instance had been - * started. - * @note Operating system can restrict the shared memory blocks to the same - * user, in which case the User/System modes will have no effect and the - * block will be user wide. - * @enum - */ - enum Mode { - User = 1 << 0, - System = 1 << 1, - SecondaryNotification = 1 << 2, - ExcludeAppVersion = 1 << 3, - ExcludeAppPath = 1 << 4 - }; - Q_DECLARE_FLAGS(Options, Mode) + /** + * @brief Mode of operation of SingleCoreApplication. + * Whether the block should be user-wide or system-wide and whether the + * primary instance should be notified when a secondary instance had been + * started. + * @note Operating system can restrict the shared memory blocks to the same + * user, in which case the User/System modes will have no effect and the + * block will be user wide. + * @enum + */ + enum Mode { + User = 1 << 0, + System = 1 << 1, + SecondaryNotification = 1 << 2, + ExcludeAppVersion = 1 << 3, + ExcludeAppPath = 1 << 4 + }; + Q_DECLARE_FLAGS(Options, Mode) - /** - * @brief Intitializes a SingleCoreApplication instance with argc command line - * arguments in argv - * @arg {int &} argc - Number of arguments in argv - * @arg {const char *[]} argv - Supplied command line arguments - * @arg {bool} allowSecondary - Whether to start the instance as secondary - * if there is already a primary instance. - * @arg {Mode} mode - Whether for the SingleCoreApplication block to be applied - * User wide or System wide. - * @arg {int} timeout - Timeout to wait in milliseconds. - * @note argc and argv may be changed as Qt removes arguments that it - * recognizes - * @note Mode::SecondaryNotification only works if set on both the primary - * instance and the secondary instance. - * @note The timeout is just a hint for the maximum time of blocking - * operations. It does not guarantee that the SingleCoreApplication - * initialisation will be completed in given time, though is a good hint. - * Usually 4*timeout would be the worst case (fail) scenario. - * @see See the corresponding QAPPLICATION_CLASS constructor for reference - */ - explicit SingleCoreApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 ); - ~SingleCoreApplication(); + /** + * @brief Intitializes a SingleCoreApplication instance with argc command line + * arguments in argv + * @arg {int &} argc - Number of arguments in argv + * @arg {const char *[]} argv - Supplied command line arguments + * @arg {bool} allowSecondary - Whether to start the instance as secondary + * if there is already a primary instance. + * @arg {Mode} mode - Whether for the SingleCoreApplication block to be applied + * User wide or System wide. + * @arg {int} timeout - Timeout to wait in milliseconds. + * @note argc and argv may be changed as Qt removes arguments that it + * recognizes + * @note Mode::SecondaryNotification only works if set on both the primary + * instance and the secondary instance. + * @note The timeout is just a hint for the maximum time of blocking + * operations. It does not guarantee that the SingleCoreApplication + * initialisation will be completed in given time, though is a good hint. + * Usually 4*timeout would be the worst case (fail) scenario. + * @see See the corresponding QAPPLICATION_CLASS constructor for reference + */ + explicit SingleCoreApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 ); + ~SingleCoreApplication(); - /** - * @brief Returns if the instance is the primary instance - * @returns {bool} - */ - bool isPrimary(); + /** + * @brief Returns if the instance is the primary instance + * @returns {bool} + */ + bool isPrimary(); - /** - * @brief Returns if the instance is a secondary instance - * @returns {bool} - */ - bool isSecondary(); + /** + * @brief Returns if the instance is a secondary instance + * @returns {bool} + */ + bool isSecondary(); - /** - * @brief Returns a unique identifier for the current instance - * @returns {qint32} - */ - quint32 instanceId(); + /** + * @brief Returns a unique identifier for the current instance + * @returns {qint32} + */ + quint32 instanceId(); - /** - * @brief Returns the process ID (PID) of the primary instance - * @returns {qint64} - */ - qint64 primaryPid(); + /** + * @brief Returns the process ID (PID) of the primary instance + * @returns {qint64} + */ + qint64 primaryPid(); - /** - * @brief Sends a message to the primary instance. Returns true on success. - * @param {int} timeout - Timeout for connecting - * @returns {bool} - * @note sendMessage() will return false if invoked from the primary - * instance. - */ - bool sendMessage( QByteArray message, int timeout = 1000 ); + /** + * @brief Sends a message to the primary instance. Returns true on success. + * @param {int} timeout - Timeout for connecting + * @returns {bool} + * @note sendMessage() will return false if invoked from the primary + * instance. + */ + bool sendMessage( QByteArray message, int timeout = 1000 ); -Q_SIGNALS: - void instanceStarted(); - void receivedMessage( quint32 instanceId, QByteArray message ); + signals: + void instanceStarted(); + void receivedMessage( quint32 instanceId, QByteArray message ); private: - SingleCoreApplicationPrivate *d_ptr; - Q_DECLARE_PRIVATE(SingleCoreApplication) + SingleCoreApplicationPrivate *d_ptr; + Q_DECLARE_PRIVATE(SingleCoreApplication) }; Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options) diff --git a/3rdparty/singleapplication/singlecoreapplication_p.cpp b/3rdparty/singleapplication/singlecoreapplication_p.cpp index cd4d3ed74..5533e04c3 100644 --- a/3rdparty/singleapplication/singlecoreapplication_p.cpp +++ b/3rdparty/singleapplication/singlecoreapplication_p.cpp @@ -24,370 +24,386 @@ // W A R N I N G !!! // ----------------- // -// This file is not part of the SingleCoreApplication API. It is used purely as an -// implementation detail. This header file may change from version to -// version without notice, or may even be removed. +// This is a modified version of SingleApplication, +// The original version is at: // +// https://github.com/itay-grudev/SingleApplication +// +// + +#include "config.h" + +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef Q_OS_UNIX +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include #include "singlecoreapplication.h" #include "singlecoreapplication_p.h" #ifdef Q_OS_WIN - #include - #include +# include +# include #endif -SingleCoreApplicationPrivate::SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr ) - : q_ptr( q_ptr ) -{ - server = nullptr; - socket = nullptr; - memory = nullptr; - instanceNumber = -1; +SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *q_ptr) + : q_ptr(q_ptr) { + + server = nullptr; + socket = nullptr; + memory = nullptr; + instanceNumber = -1; + } -SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() -{ - if( socket != nullptr ) { - socket->close(); - delete socket; - } +SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() { - memory->lock(); - InstancesInfo* inst = static_cast(memory->data()); - if( server != nullptr ) { - server->close(); - delete server; - inst->primary = false; - inst->primaryPid = -1; - inst->checksum = blockChecksum(); - } - memory->unlock(); + if (socket != nullptr) { + socket->close(); + delete socket; + } - delete memory; -} - -void SingleCoreApplicationPrivate::genBlockServerName() -{ - QCryptographicHash appData( QCryptographicHash::Sha256 ); - appData.addData( "SingleApplication", 17 ); - appData.addData( SingleCoreApplication::app_t::applicationName().toUtf8() ); - appData.addData( SingleCoreApplication::app_t::organizationName().toUtf8() ); - appData.addData( SingleCoreApplication::app_t::organizationDomain().toUtf8() ); - - if( ! (options & SingleCoreApplication::Mode::ExcludeAppVersion) ) { - appData.addData( SingleCoreApplication::app_t::applicationVersion().toUtf8() ); - } - - if( ! (options & SingleCoreApplication::Mode::ExcludeAppPath) ) { -#ifdef Q_OS_WIN - appData.addData( SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8() ); -#else - appData.addData( SingleCoreApplication::app_t::applicationFilePath().toUtf8() ); -#endif - } - - // User level block requires a user specific data in the hash - if( options & SingleCoreApplication::Mode::User ) { -#ifdef Q_OS_WIN - wchar_t username [ UNLEN + 1 ]; - // Specifies size of the buffer on input - DWORD usernameLength = UNLEN + 1; - if( GetUserNameW( username, &usernameLength ) ) { - appData.addData( QString::fromWCharArray(username).toUtf8() ); - } else { - appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() ); - } -#endif -#ifdef Q_OS_UNIX - appData.addData(qgetenv("USER")); -#endif - } - - // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with - // server naming requirements. - blockServerName = appData.result().toBase64().replace("/", "_"); -} - -void SingleCoreApplicationPrivate::initializeMemoryBlock() -{ - InstancesInfo* inst = static_cast( memory->data() ); + memory->lock(); + InstancesInfo* inst = static_cast(memory->data()); + if (server != nullptr) { + server->close(); + delete server; inst->primary = false; - inst->secondary = 0; inst->primaryPid = -1; inst->checksum = blockChecksum(); + } + memory->unlock(); + + delete memory; + } -void SingleCoreApplicationPrivate::startPrimary() -{ - Q_Q(SingleCoreApplication); +void SingleCoreApplicationPrivate::genBlockServerName() { - // Successful creation means that no main process exists - // So we start a QLocalServer to listen for connections - QLocalServer::removeServer( blockServerName ); - server = new QLocalServer(); + QCryptographicHash appData(QCryptographicHash::Sha256); + appData.addData("SingleApplication", 17); + appData.addData(SingleCoreApplication::app_t::applicationName().toUtf8()); + appData.addData(SingleCoreApplication::app_t::organizationName().toUtf8()); + appData.addData(SingleCoreApplication::app_t::organizationDomain().toUtf8()); - // Restrict access to the socket according to the - // SingleCoreApplication::Mode::User flag on User level or no restrictions - if( options & SingleCoreApplication::Mode::User ) { - server->setSocketOptions( QLocalServer::UserAccessOption ); - } else { - server->setSocketOptions( QLocalServer::WorldAccessOption ); + if (!(options & SingleCoreApplication::Mode::ExcludeAppVersion)) { + appData.addData(SingleCoreApplication::app_t::applicationVersion().toUtf8()); + } + + if (!(options & SingleCoreApplication::Mode::ExcludeAppPath)) { +#ifdef Q_OS_WIN + appData.addData(SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8()); +#else + appData.addData(SingleCoreApplication::app_t::applicationFilePath().toUtf8()); +#endif + } + + // User level block requires a user specific data in the hash + if (options & SingleCoreApplication::Mode::User) { +#ifdef Q_OS_UNIX + QByteArray username; +#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID) + uid_t uid = geteuid(); + if (uid != -1) { + struct passwd *pw = getpwuid(uid); + if (pw) { + username = pw->pw_name; + } } + qDebug() << username; +#endif + if (username.isEmpty()) username = qgetenv("USER"); + appData.addData(username); +#endif +#ifdef Q_OS_WIN + wchar_t username [ UNLEN + 1 ]; + // Specifies size of the buffer on input + DWORD usernameLength = UNLEN + 1; + if (GetUserNameW(username, &usernameLength)) { + appData.addData(QString::fromWCharArray(username).toUtf8()); + } +#endif + } - server->listen( blockServerName ); - QObject::connect( - server, - &QLocalServer::newConnection, - this, - &SingleCoreApplicationPrivate::slotConnectionEstablished - ); + // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements. + blockServerName = appData.result().toBase64().replace("/", "_"); - // Reset the number of connections - InstancesInfo* inst = static_cast ( memory->data() ); - - inst->primary = true; - inst->primaryPid = q->applicationPid(); - inst->checksum = blockChecksum(); - - instanceNumber = 0; } -void SingleCoreApplicationPrivate::startSecondary() -{ +void SingleCoreApplicationPrivate::initializeMemoryBlock() { + + InstancesInfo* inst = static_cast(memory->data()); + inst->primary = false; + inst->secondary = 0; + inst->primaryPid = -1; + inst->checksum = blockChecksum(); + } -void SingleCoreApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType ) -{ - // Connect to the Local Server of the Primary Instance if not already - // connected. - if( socket == nullptr ) { - socket = new QLocalSocket(); - } +void SingleCoreApplicationPrivate::startPrimary() { - // If already connected - we are done; - if( socket->state() == QLocalSocket::ConnectedState ) - return; + Q_Q(SingleCoreApplication); - // If not connect - if( socket->state() == QLocalSocket::UnconnectedState || - socket->state() == QLocalSocket::ClosingState ) { - socket->connectToServer( blockServerName ); - } + // Successful creation means that no main process exists + // So we start a QLocalServer to listen for connections + QLocalServer::removeServer(blockServerName); + server = new QLocalServer(); - // Wait for being connected - if( socket->state() == QLocalSocket::ConnectingState ) { - socket->waitForConnected( msecs ); - } + // Restrict access to the socket according to the + // SingleCoreApplication::Mode::User flag on User level or no restrictions + if (options & SingleCoreApplication::Mode::User) { + server->setSocketOptions(QLocalServer::UserAccessOption); + } + else { + server->setSocketOptions(QLocalServer::WorldAccessOption); + } - // Initialisation message according to the SingleCoreApplication protocol - if( socket->state() == QLocalSocket::ConnectedState ) { - // Notify the parent that a new instance had been started; - QByteArray initMsg; - QDataStream writeStream(&initMsg, QIODevice::WriteOnly); + server->listen(blockServerName); + QObject::connect(server, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished); + + // Reset the number of connections + InstancesInfo* inst = static_cast (memory->data()); + + inst->primary = true; + inst->primaryPid = q->applicationPid(); + inst->checksum = blockChecksum(); + + instanceNumber = 0; + +} + +void SingleCoreApplicationPrivate::startSecondary() {} + +void SingleCoreApplicationPrivate::connectToPrimary(int msecs, ConnectionType connectionType) { + + // Connect to the Local Server of the Primary Instance if not already connected. + if (socket == nullptr) { + socket = new QLocalSocket(); + } + + // If already connected - we are done; + if (socket->state() == QLocalSocket::ConnectedState) + return; + + // If not connect + if (socket->state() == QLocalSocket::UnconnectedState || + socket->state() == QLocalSocket::ClosingState) { + socket->connectToServer(blockServerName); + } + + // Wait for being connected + if (socket->state() == QLocalSocket::ConnectingState) { + socket->waitForConnected(msecs); + } + + // Initialisation message according to the SingleCoreApplication protocol + if (socket->state() == QLocalSocket::ConnectedState) { + // Notify the parent that a new instance had been started; + QByteArray initMsg; + QDataStream writeStream(&initMsg, QIODevice::WriteOnly); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - writeStream.setVersion(QDataStream::Qt_5_6); + writeStream.setVersion(QDataStream::Qt_5_6); #endif - writeStream << blockServerName.toLatin1(); - writeStream << static_cast(connectionType); - writeStream << instanceNumber; - quint16 checksum = qChecksum(initMsg.constData(), static_cast(initMsg.length())); - writeStream << checksum; + writeStream << blockServerName.toLatin1(); + writeStream << static_cast(connectionType); + writeStream << instanceNumber; + quint16 checksum = qChecksum(initMsg.constData(), static_cast(initMsg.length())); + writeStream << checksum; - // The header indicates the message length that follows - QByteArray header; - QDataStream headerStream(&header, QIODevice::WriteOnly); + // The header indicates the message length that follows + QByteArray header; + QDataStream headerStream(&header, QIODevice::WriteOnly); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - headerStream.setVersion(QDataStream::Qt_5_6); + headerStream.setVersion(QDataStream::Qt_5_6); #endif - headerStream << static_cast ( initMsg.length() ); + headerStream << static_cast (initMsg.length()); + + socket->write(header); + socket->write(initMsg); + socket->flush(); + socket->waitForBytesWritten(msecs); + } - socket->write( header ); - socket->write( initMsg ); - socket->flush(); - socket->waitForBytesWritten( msecs ); - } } -quint16 SingleCoreApplicationPrivate::blockChecksum() -{ - return qChecksum( - static_cast ( memory->data() ), - offsetof( InstancesInfo, checksum ) - ); +quint16 SingleCoreApplicationPrivate::blockChecksum() { + + return qChecksum(static_cast (memory->data()), offsetof(InstancesInfo, checksum)); + } -qint64 SingleCoreApplicationPrivate::primaryPid() -{ - qint64 pid; +qint64 SingleCoreApplicationPrivate::primaryPid() { - memory->lock(); - InstancesInfo* inst = static_cast( memory->data() ); - pid = inst->primaryPid; - memory->unlock(); + qint64 pid; + + memory->lock(); + InstancesInfo* inst = static_cast(memory->data()); + pid = inst->primaryPid; + memory->unlock(); + + return pid; - return pid; } /** * @brief Executed when a connection has been made to the LocalServer */ -void SingleCoreApplicationPrivate::slotConnectionEstablished() -{ - QLocalSocket *nextConnSocket = server->nextPendingConnection(); - connectionMap.insert(nextConnSocket, ConnectionInfo()); +void SingleCoreApplicationPrivate::slotConnectionEstablished() { - QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, - [nextConnSocket, this]() { - auto &info = connectionMap[nextConnSocket]; - Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId ); - } - ); + QLocalSocket *nextConnSocket = server->nextPendingConnection(); + connectionMap.insert(nextConnSocket, ConnectionInfo()); - QObject::connect(nextConnSocket, &QLocalSocket::disconnected, - [nextConnSocket, this](){ - connectionMap.remove(nextConnSocket); - nextConnSocket->deleteLater(); - } - ); + QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, + [nextConnSocket, this]() { + auto &info = connectionMap[nextConnSocket]; + Q_EMIT this->slotClientConnectionClosed(nextConnSocket, info.instanceId); + } + ); + + QObject::connect(nextConnSocket, &QLocalSocket::disconnected, + [nextConnSocket, this](){ + connectionMap.remove(nextConnSocket); + nextConnSocket->deleteLater(); + } + ); + + QObject::connect(nextConnSocket, &QLocalSocket::readyRead, + [nextConnSocket, this]() { + auto &info = connectionMap[nextConnSocket]; + switch(info.stage) { + case StageHeader: + readInitMessageHeader(nextConnSocket); + break; + case StageBody: + readInitMessageBody(nextConnSocket); + break; + case StageConnected: + Q_EMIT this->slotDataAvailable(nextConnSocket, info.instanceId); + break; + default: + break; + }; + } + ); - QObject::connect(nextConnSocket, &QLocalSocket::readyRead, - [nextConnSocket, this]() { - auto &info = connectionMap[nextConnSocket]; - switch(info.stage) { - case StageHeader: - readInitMessageHeader(nextConnSocket); - break; - case StageBody: - readInitMessageBody(nextConnSocket); - break; - case StageConnected: - Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId ); - break; - default: - break; - }; - } - ); } -void SingleCoreApplicationPrivate::readInitMessageHeader( QLocalSocket *sock ) -{ - if (!connectionMap.contains( sock )) { - return; - } +void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) { - if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) { - return; - } + if (!connectionMap.contains(sock)) { + return; + } - QDataStream headerStream( sock ); + if (sock->bytesAvailable() < (qint64)sizeof(quint64)) { + return; + } + + QDataStream headerStream(sock); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - headerStream.setVersion( QDataStream::Qt_5_6 ); + headerStream.setVersion(QDataStream::Qt_5_6); #endif - // Read the header to know the message length - quint64 msgLen = 0; - headerStream >> msgLen; - ConnectionInfo &info = connectionMap[sock]; - info.stage = StageBody; - info.msgLen = msgLen; + // Read the header to know the message length + quint64 msgLen = 0; + headerStream >> msgLen; + ConnectionInfo &info = connectionMap[sock]; + info.stage = StageBody; + info.msgLen = msgLen; + + if (sock->bytesAvailable() >= (qint64) msgLen) { + readInitMessageBody(sock); + } - if ( sock->bytesAvailable() >= (qint64) msgLen ) { - readInitMessageBody( sock ); - } } -void SingleCoreApplicationPrivate::readInitMessageBody( QLocalSocket *sock ) -{ - Q_Q(SingleCoreApplication); +void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) { - if (!connectionMap.contains( sock )) { - return; - } + Q_Q(SingleCoreApplication); - ConnectionInfo &info = connectionMap[sock]; - if( sock->bytesAvailable() < ( qint64 )info.msgLen ) { - return; - } + if (!connectionMap.contains(sock)) { + return; + } - // Read the message body - QByteArray msgBytes = sock->read(info.msgLen); - QDataStream readStream(msgBytes); + ConnectionInfo &info = connectionMap[sock]; + if (sock->bytesAvailable() < (qint64)info.msgLen) { + return; + } + + // Read the message body + QByteArray msgBytes = sock->read(info.msgLen); + QDataStream readStream(msgBytes); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - readStream.setVersion( QDataStream::Qt_5_6 ); + readStream.setVersion(QDataStream::Qt_5_6); #endif - // server name - QByteArray latin1Name; - readStream >> latin1Name; + // server name + QByteArray latin1Name; + readStream >> latin1Name; - // connection type - ConnectionType connectionType = InvalidConnection; - quint8 connTypeVal = InvalidConnection; - readStream >> connTypeVal; - connectionType = static_cast ( connTypeVal ); + // connection type + ConnectionType connectionType = InvalidConnection; + quint8 connTypeVal = InvalidConnection; + readStream >> connTypeVal; + connectionType = static_cast (connTypeVal); - // instance id - quint32 instanceId = 0; - readStream >> instanceId; + // instance id + quint32 instanceId = 0; + readStream >> instanceId; - // checksum - quint16 msgChecksum = 0; - readStream >> msgChecksum; + // checksum + quint16 msgChecksum = 0; + readStream >> msgChecksum; - const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast( msgBytes.length() - sizeof( quint16 ) ) ); + const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast(msgBytes.length() - sizeof(quint16))); - bool isValid = readStream.status() == QDataStream::Ok && - QLatin1String(latin1Name) == blockServerName && - msgChecksum == actualChecksum; + bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum; - if( !isValid ) { - sock->close(); - return; - } + if (!isValid) { + sock->close(); + return; + } - info.instanceId = instanceId; - info.stage = StageConnected; + info.instanceId = instanceId; + info.stage = StageConnected; - if( connectionType == NewInstance || - ( connectionType == SecondaryInstance && - options & SingleCoreApplication::Mode::SecondaryNotification ) ) - { - Q_EMIT q->instanceStarted(); - } + if (connectionType == NewInstance || (connectionType == SecondaryInstance && options & SingleCoreApplication::Mode::SecondaryNotification)) { + Q_EMIT q->instanceStarted(); + } + + if (sock->bytesAvailable() > 0) { + Q_EMIT this->slotDataAvailable(sock, instanceId); + } - if (sock->bytesAvailable() > 0) { - Q_EMIT this->slotDataAvailable( sock, instanceId ); - } } -void SingleCoreApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId ) -{ - Q_Q(SingleCoreApplication); - Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() ); +void SingleCoreApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, quint32 instanceId) { + + Q_Q(SingleCoreApplication); + Q_EMIT q->receivedMessage(instanceId, dataSocket->readAll()); + } -void SingleCoreApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId ) -{ - if( closedSocket->bytesAvailable() > 0 ) - Q_EMIT slotDataAvailable( closedSocket, instanceId ); +void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, quint32 instanceId) { + + if (closedSocket->bytesAvailable() > 0) + Q_EMIT slotDataAvailable(closedSocket, instanceId); + } diff --git a/3rdparty/singleapplication/singlecoreapplication_p.h b/3rdparty/singleapplication/singlecoreapplication_p.h index 98d373845..54dcab392 100644 --- a/3rdparty/singleapplication/singlecoreapplication_p.h +++ b/3rdparty/singleapplication/singlecoreapplication_p.h @@ -24,76 +24,80 @@ // W A R N I N G !!! // ----------------- // -// This file is not part of the SingleCoreApplication API. It is used purely as an -// implementation detail. This header file may change from version to -// version without notice, or may even be removed. +// This is a modified version of SingleApplication, +// The original version is at: +// +// https://github.com/itay-grudev/SingleApplication +// // #ifndef SINGLECOREAPPLICATION_P_H #define SINGLECOREAPPLICATION_P_H -#include -#include -#include +#include +#include +#include +#include +#include + #include "singlecoreapplication.h" struct InstancesInfo { - bool primary; - quint32 secondary; - qint64 primaryPid; - quint16 checksum; + bool primary; + quint32 secondary; + qint64 primaryPid; + quint16 checksum; }; struct ConnectionInfo { - explicit ConnectionInfo() : - msgLen(0), instanceId(0), stage(0) {} - qint64 msgLen; - quint32 instanceId; - quint8 stage; + explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {} + qint64 msgLen; + quint32 instanceId; + quint8 stage; }; class SingleCoreApplicationPrivate : public QObject { -Q_OBJECT -public: - enum ConnectionType : quint8 { - InvalidConnection = 0, - NewInstance = 1, - SecondaryInstance = 2, - Reconnect = 3 - }; - enum ConnectionStage : quint8 { - StageHeader = 0, - StageBody = 1, - StageConnected = 2, - }; - Q_DECLARE_PUBLIC(SingleCoreApplication) + Q_OBJECT + public: + enum ConnectionType : quint8 { + InvalidConnection = 0, + NewInstance = 1, + SecondaryInstance = 2, + Reconnect = 3 + }; + enum ConnectionStage : quint8 { + StageHeader = 0, + StageBody = 1, + StageConnected = 2, + }; + Q_DECLARE_PUBLIC(SingleCoreApplication) - SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr ); - ~SingleCoreApplicationPrivate(); + SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr ); + ~SingleCoreApplicationPrivate(); - void genBlockServerName(); - void initializeMemoryBlock(); - void startPrimary(); - void startSecondary(); - void connectToPrimary(int msecs, ConnectionType connectionType ); - quint16 blockChecksum(); - qint64 primaryPid(); - void readInitMessageHeader(QLocalSocket *socket); - void readInitMessageBody(QLocalSocket *socket); + void genBlockServerName(); + void initializeMemoryBlock(); + void startPrimary(); + void startSecondary(); + void connectToPrimary(int msecs, ConnectionType connectionType ); + quint16 blockChecksum(); + qint64 primaryPid(); + void readInitMessageHeader(QLocalSocket *socket); + void readInitMessageBody(QLocalSocket *socket); - SingleCoreApplication *q_ptr; - QSharedMemory *memory; - QLocalSocket *socket; - QLocalServer *server; - quint32 instanceNumber; - QString blockServerName; - SingleCoreApplication::Options options; - QMap connectionMap; + SingleCoreApplication *q_ptr; + QSharedMemory *memory; + QLocalSocket *socket; + QLocalServer *server; + quint32 instanceNumber; + QString blockServerName; + SingleCoreApplication::Options options; + QMap connectionMap; -public Q_SLOTS: - void slotConnectionEstablished(); - void slotDataAvailable( QLocalSocket*, quint32 ); - void slotClientConnectionClosed( QLocalSocket*, quint32 ); + public slots: + void slotConnectionEstablished(); + void slotDataAvailable(QLocalSocket*, quint32); + void slotClientConnectionClosed(QLocalSocket*, quint32); }; -#endif // SINGLECOREAPPLICATION_P_H +#endif // SINGLECOREAPPLICATION_P_H