[linux] fix bug 59389

This commit is contained in:
SimplestStudio
2023-08-18 14:15:50 +03:00
parent 7d4339e94f
commit 1d330c2e1f
4 changed files with 107 additions and 362 deletions

View File

@ -153,12 +153,8 @@ SOURCES += \
# src/components/casclabel.cpp
updmodule:!build_xp {
INCLUDEPATH += $$PWD/extras/update-daemon/src/classes
HEADERS += $$PWD/src/cupdatemanager.h \
$$PWD/extras/update-daemon/src/classes/csocket.h
SOURCES += $$PWD/src/cupdatemanager.cpp \
$$PWD/extras/update-daemon/src/classes/csocket.cpp
HEADERS += $$PWD/src/cupdatemanager.h
SOURCES += $$PWD/src/cupdatemanager.cpp
}
RESOURCES += $$PWD/resources.qrc
@ -199,6 +195,8 @@ core_linux {
QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN/converter\'"
QMAKE_LFLAGS += -static-libstdc++ -static-libgcc
INCLUDEPATH += $$PWD/extras/update-daemon/src/classes
HEADERS += $$PWD/src/windows/platform_linux/cx11decoration.h \
#$$PWD/src/windows/platform_linux/gtk_addon.h \
$$PWD/src/windows/platform_linux/cwindowplatform.h \
@ -210,7 +208,8 @@ core_linux {
$$PWD/src/platform_linux/gtkprintdialog.h \
$$PWD/src/platform_linux/gtkmessage.h \
$$PWD/src/platform_linux/gtkutils.h \
$$PWD/src/platform_linux/xcbutils.h
$$PWD/src/platform_linux/xcbutils.h \
$$PWD/extras/update-daemon/src/classes/csocket.h
SOURCES += $$PWD/src/windows/platform_linux/cx11decoration.cpp \
#$$PWD/src/windows/platform_linux/gtk_addon.cpp \
@ -224,7 +223,8 @@ core_linux {
$$PWD/src/platform_linux/gtkprintdialog.cpp \
$$PWD/src/platform_linux/gtkmessage.cpp \
$$PWD/src/platform_linux/gtkutils.cpp \
$$PWD/src/platform_linux/xcbutils.cpp
$$PWD/src/platform_linux/xcbutils.cpp \
$$PWD/extras/update-daemon/src/classes/csocket.cpp
updmodule {
HEADERS += $$PWD/src/platform_linux/updatedialog.h
@ -272,8 +272,11 @@ core_windows {
OTHER_FILES += $$PWD/res/manifest/DesktopEditors.exe.manifest
updmodule:!build_xp {
HEADERS += $$PWD/src/platform_win/updatedialog.h
SOURCES += $$PWD/src/platform_win/updatedialog.cpp
INCLUDEPATH += $$PWD/extras/update-daemon/src/classes
HEADERS += $$PWD/src/platform_win/updatedialog.h \
$$PWD/extras/update-daemon/src/classes/csocket.h
SOURCES += $$PWD/src/platform_win/updatedialog.cpp \
$$PWD/extras/update-daemon/src/classes/csocket.cpp
}
LIBS += -lwininet \

View File

@ -127,6 +127,7 @@ typedef unsigned char BYTE;
#define APP_PORT 12010
#define SVC_PORT 12011
#define INSTANCE_SVC_PORT 12012
#define INSTANCE_APP_PORT 13012
#define BTN_TEXT_CANCEL QObject::tr("Cancel")
#define BTN_TEXT_YES QObject::tr("Yes")

View File

@ -1,332 +1,77 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2016
//
// 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 <cstdlib>
#include "utils.h"
#include <QtCore/QMutex>
#include <QtCore/QSemaphore>
#include <QtCore/QSharedMemory>
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QDataStream>
#ifdef Q_OS_UNIX
#include <signal.h>
#include <unistd.h>
#endif
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "singleapplication.h"
#include "defines.h"
#include <QThread>
struct InstancesInfo {
bool primary;
uint8_t secondary;
};
#define RETRIES_DELAY_MS 100
class SingleApplicationPrivate {
public:
Q_DECLARE_PUBLIC(SingleApplication)
SingleApplicationPrivate( SingleApplication *q_ptr ) : q_ptr( q_ptr ) {
server = NULL;
}
~SingleApplicationPrivate()
{
cleanUp();
}
void startPrimary( bool resetMemory )
{
Q_Q(SingleApplication);
#ifdef Q_OS_UNIX
// Handle any further termination signals to ensure the
// QSharedMemory block is deleted even if the process crashes
crashHandler();
#endif
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer( memory->key() );
server = new QLocalServer();
server->listen( memory->key() );
QObject::connect(server, &QLocalServer::newConnection, q, &SingleApplication::slotConnectionEstablished);
// Reset the number of connections
memory->lock();
InstancesInfo* inst = (InstancesInfo*)memory->data();
if( resetMemory ){
inst->primary = true;
inst->secondary = 0;
} else {
inst->primary = true;
}
memory->unlock();
}
void startSecondary()
{
#ifdef Q_OS_UNIX
// Handle any further termination signals to ensure the
// QSharedMemory block is deleted even if the process crashes
crashHandler();
#endif
//notifyPrimary();
}
void notifyPrimary(const QByteArray &data, int timeout)
{
// Connect to the Local Server of the main process to notify it
// that a new process had been started
QLocalSocket socket;
socket.connectToServer( memory->key() );
// Notify the parent that a new instance had been started;
if (socket.waitForConnected( timeout )) {
// Send input arguments to the parent
socket.write(data);
socket.waitForBytesWritten();
}
socket.close();
}
#ifdef Q_OS_UNIX
void crashHandler()
{
// This guarantees the program will work even with multiple
// instances of SingleApplication in different threads.
// Which in my opinion is idiotic, but lets handle that too.
{
sharedMemMutex.lock();
sharedMem.append( this );
sharedMemMutex.unlock();
}
// Handle any further termination signals to ensure the
// QSharedMemory block is deleted even if the process crashes
signal( SIGHUP, SingleApplicationPrivate::terminate ); // 1
signal( SIGINT, SingleApplicationPrivate::terminate ); // 2
signal( SIGQUIT, SingleApplicationPrivate::terminate ); // 3
signal( SIGILL, SingleApplicationPrivate::terminate ); // 4
signal( SIGABRT, SingleApplicationPrivate::terminate ); // 6
signal( SIGFPE, SingleApplicationPrivate::terminate ); // 8
signal( SIGBUS, SingleApplicationPrivate::terminate ); // 10
signal( SIGSEGV, SingleApplicationPrivate::terminate ); // 11
signal( SIGSYS, SingleApplicationPrivate::terminate ); // 12
signal( SIGPIPE, SingleApplicationPrivate::terminate ); // 13
signal( SIGALRM, SingleApplicationPrivate::terminate ); // 14
signal( SIGTERM, SingleApplicationPrivate::terminate ); // 15
signal( SIGXCPU, SingleApplicationPrivate::terminate ); // 24
signal( SIGXFSZ, SingleApplicationPrivate::terminate ); // 25
}
static void terminate( int signum )
{
while( ! sharedMem.empty() ) {
delete sharedMem.back();
sharedMem.pop_back();
}
::exit( 128 + signum );
}
static QList<SingleApplicationPrivate*> sharedMem;
static QMutex sharedMemMutex;
#endif
void cleanUp() {
memory->lock();
InstancesInfo* inst = (InstancesInfo*)memory->data();
if( server != NULL ) {
server->close();
inst->primary = false;
} else if (inst) {
if( inst->secondary > 0 )
inst->secondary -= 1;
}
memory->unlock();
delete memory;
}
QSharedMemory *memory;
SingleApplication *q_ptr;
QLocalServer *server;
};
#ifdef Q_OS_UNIX
QList<SingleApplicationPrivate*> SingleApplicationPrivate::sharedMem;
QMutex SingleApplicationPrivate::sharedMemMutex;
#endif
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program
* if another instance already exists
* @param argc
* @param argv
*/
SingleApplication::SingleApplication( int &argc, char *argv[], const QString& servername)
: SingleApplication(argc, argv, servername, 0)
{}
SingleApplication::SingleApplication( int &argc, char *argv[], const QString& servername, uint8_t secondaryInstances )
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
SingleApplication::SingleApplication(int &argc, char *argv[], const QString&) :
QApplication(argc, argv),
m_socket(new CSocket(0, INSTANCE_APP_PORT))
{
Q_D(SingleApplication);
// Check command line arguments for the force primary and secondary flags
#ifdef Q_OS_UNIX
bool forcePrimary = false;
#endif
bool secondary = false;
for( int i = 0; i < argc; ++i ) {
if( strcmp( argv[i], "--secondary" ) == 0 ) {
secondary = true;
#ifndef Q_OS_UNIX
break;
#endif
}
#ifdef Q_OS_UNIX
QString s = QString(argv[i]);
if( strcmp( argv[i], "--primary" ) == 0 ) {
secondary = false;
forcePrimary = true;
break;
}
#endif
if (m_socket->isPrimaryInstance()) {
m_isPrimary = true;
m_socket->onMessageReceived([=](void *buff, size_t size) {
QString data = QString::fromLocal8Bit((const char*)buff, size);
QMetaObject::invokeMethod(this, "invokeSignal", Qt::QueuedConnection, Q_ARG(QString, data));
});
}
// QString serverName = app_t::organizationName() + app_t::applicationName();
// serverName.replace( QRegExp("[^\\w\\-. ]"), "" );
QString serverName = servername + ":" + QString::fromStdWString(Utils::systemUserName());
// QSystemSemaphore _semaphore("asc:desktop:editors", 1);
// _semaphore.acquire();
// Guarantee thread safe behaviour with a shared memory block. Also by
// attaching to it and deleting it we make sure that the memory i deleted
// even if the process had crashed
d->memory = new QSharedMemory( serverName );
d->memory->attach();
delete d->memory;
d->memory = new QSharedMemory( serverName );
// Create a shared memory block with a minimum size of 1 byte
#ifdef Q_OS_UNIX
if( d->memory->create( sizeof(InstancesInfo) ) || forcePrimary ) {
#else
if( d->memory->create( sizeof(InstancesInfo) ) ) {
#endif
d->startPrimary( true );
return;
} else {
// Attempt to attach to the memory segment
if( d->memory->attach() ) {
d->memory->lock();
InstancesInfo* inst = (InstancesInfo*)d->memory->data();
if( ! inst->primary ) {
d->startPrimary( false );
d->memory->unlock();
return;
}
// Check if another instance can be started
if( secondary && inst->secondary < secondaryInstances ) {
inst->secondary += 1;
d->startSecondary();
d->memory->unlock();
return;
}
d->memory->unlock();
}
}
// _semaphore.release();
//d->notifyPrimary();
//delete d;
//qWarning() << "DesktopEditors had been run already";
//::exit(EXIT_SUCCESS);
}
/**
* @brief Destructor
*/
SingleApplication::~SingleApplication()
{
Q_D(SingleApplication);
delete d;
if (m_socket)
delete m_socket, m_socket = nullptr;
}
bool SingleApplication::isPrimary()
{
Q_D(SingleApplication);
return d->server != NULL;
return m_isPrimary;
}
bool SingleApplication::isSecondary()
bool SingleApplication::sendMessage(const QByteArray &msg)
{
Q_D(SingleApplication);
return d->server == NULL;
}
bool SingleApplication::sendMessage(const QByteArray &data)
{
Q_D(SingleApplication);
if (isPrimary())
if (m_isPrimary)
return false;
d->notifyPrimary(data, 3000);
return true;
QThread::msleep(RETRIES_DELAY_MS);
CSocket socket(INSTANCE_APP_PORT, 0);
return socket.sendMessage((void*)msg.data(), msg.size());
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplication::slotConnectionEstablished()
void SingleApplication::invokeSignal(const QString &data)
{
Q_D(SingleApplication);
QLocalSocket *socket = d->server->nextPendingConnection();
// while (socket->bytesAvailable() < (int)sizeof(quint32))
// socket->waitForReadyRead();
if( !socket->waitForConnected() )
return;
if( !socket->waitForReadyRead() )
return;
QByteArray buffer;
QDataStream stream(&buffer, QIODevice::ReadOnly);
buffer.append(socket->readAll());
socket->close();
delete socket;
// Send input arguments the window
Q_EMIT receivedMessage(buffer);
emit receivedMessage(data.toUtf8());
}

View File

@ -1,66 +1,62 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2016
//
// 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.
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#ifndef SINGLE_APPLICATION_H
#define SINGLE_APPLICATION_H
#include <QtCore/QtGlobal>
#include <QApplication>
#include <QByteArray>
#include "csocket.h"
#ifndef QAPPLICATION_CLASS
#define QAPPLICATION_CLASS QApplication
#endif
#include QT_STRINGIFY(QAPPLICATION_CLASS)
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multipe instances of the same Application
* @see QApplication
*/
class SingleApplication : public QAPPLICATION_CLASS
class SingleApplication : public QApplication
{
Q_OBJECT
Q_DECLARE_PRIVATE(SingleApplication)
typedef QAPPLICATION_CLASS app_t;
public:
explicit SingleApplication( int &argc, char *argv[], const QString& servername);
explicit SingleApplication( int &argc, char *argv[], const QString& servername, uint8_t secondaryInstances);
explicit SingleApplication( int &argc, char *argv[], const QString& servername = QString());
~SingleApplication();
bool isPrimary();
bool isSecondary();
bool sendMessage(const QByteArray&);
signals:
void receivedMessage(QByteArray message);
private Q_SLOTS:
void slotConnectionEstablished();
private slots:
void invokeSignal(const QString&);
private:
SingleApplicationPrivate *d_ptr;
CSocket *m_socket = nullptr;
bool m_isPrimary = false;
};
#endif // SINGLE_APPLICATION_H