File indexing completed on 2024-05-12 05:52:02

0001 // The MIT License (MIT)
0002 //
0003 // Copyright (c) Itay Grudev 2015 - 2020
0004 //
0005 // Permission is hereby granted, free of charge, to any person obtaining a copy
0006 // of this software and associated documentation files (the "Software"), to deal
0007 // in the Software without restriction, including without limitation the rights
0008 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0009 // copies of the Software, and to permit persons to whom the Software is
0010 // furnished to do so, subject to the following conditions:
0011 //
0012 // The above copyright notice and this permission notice shall be included in
0013 // all copies or substantial portions of the Software.
0014 //
0015 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0018 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0020 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0021 // THE SOFTWARE.
0022 
0023 #include <QtCore/QElapsedTimer>
0024 #include <QtCore/QByteArray>
0025 #include <QtCore/QSharedMemory>
0026 
0027 #include "singleapplication.h"
0028 #include "singleapplication_p.h"
0029 
0030 /**
0031  * @brief Constructor. Checks and fires up LocalServer or closes the program
0032  * if another instance already exists
0033  * @param argc
0034  * @param argv
0035  * @param allowSecondary Whether to enable secondary instance support
0036  * @param options Optional flags to toggle specific behaviour
0037  * @param timeout Maximum time blocking functions are allowed during app load
0038  */
0039 SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
0040     : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
0041 {
0042     Q_D( SingleApplication );
0043 
0044 #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
0045     // On Android and iOS since the library is not supported fallback to
0046     // standard QApplication behaviour by simply returning at this point.
0047     qWarning() << "SingleApplication is not supported on Android and iOS systems.";
0048     return;
0049 #endif
0050 
0051     // Store the current mode of the program
0052     d->options = options;
0053 
0054     // Add any unique user data
0055     if ( ! userData.isEmpty() )
0056         d->addAppData( userData );
0057 
0058     // Generating an application ID used for identifying the shared memory
0059     // block and QLocalServer
0060     d->genBlockServerName();
0061 
0062     // To mitigate QSharedMemory issues with large amount of processes
0063     // attempting to attach at the same time
0064     SingleApplicationPrivate::randomSleep();
0065 
0066 #ifdef Q_OS_UNIX
0067     // By explicitly attaching it and then deleting it we make sure that the
0068     // memory is deleted even after the process has crashed on Unix.
0069     d->memory = new QSharedMemory( d->blockServerName );
0070     d->memory->attach();
0071     delete d->memory;
0072 #endif
0073     // Guarantee thread safe behaviour with a shared memory block.
0074     d->memory = new QSharedMemory( d->blockServerName );
0075 
0076     // Create a shared memory block
0077     if( d->memory->create( sizeof( InstancesInfo ) )){
0078         // Initialize the shared memory block
0079         if( ! d->memory->lock() ){
0080           qCritical() << "SingleApplication: Unable to lock memory block after create.";
0081           abortSafely();
0082         }
0083         d->initializeMemoryBlock();
0084     } else {
0085         if( d->memory->error() == QSharedMemory::AlreadyExists ){
0086           // Attempt to attach to the memory segment
0087           if( ! d->memory->attach() ){
0088               qCritical() << "SingleApplication: Unable to attach to shared memory block.";
0089               abortSafely();
0090           }
0091           if( ! d->memory->lock() ){
0092             qCritical() << "SingleApplication: Unable to lock memory block after attach.";
0093             abortSafely();
0094           }
0095         } else {
0096           qCritical() << "SingleApplication: Unable to create block.";
0097           abortSafely();
0098         }
0099     }
0100 
0101     auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
0102     QElapsedTimer time;
0103     time.start();
0104 
0105     // Make sure the shared memory block is initialised and in consistent state
0106     while( true ){
0107       // If the shared memory block's checksum is valid continue
0108       if( d->blockChecksum() == inst->checksum ) break;
0109 
0110       // If more than 5s have elapsed, assume the primary instance crashed and
0111       // assume it's position
0112       if( time.elapsed() > 5000 ){
0113           qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
0114           d->initializeMemoryBlock();
0115       }
0116 
0117       // Otherwise wait for a random period and try again. The random sleep here
0118       // limits the probability of a collision between two racing apps and
0119       // allows the app to initialise faster
0120       if( ! d->memory->unlock() ){
0121         qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
0122         qDebug() << d->memory->errorString();
0123       }
0124       SingleApplicationPrivate::randomSleep();
0125       if( ! d->memory->lock() ){
0126         qCritical() << "SingleApplication: Unable to lock memory after random wait.";
0127         abortSafely();
0128       }
0129     }
0130 
0131     if( inst->primary == false ){
0132         d->startPrimary();
0133         if( ! d->memory->unlock() ){
0134           qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
0135           qDebug() << d->memory->errorString();
0136         }
0137         return;
0138     }
0139 
0140     // Check if another instance can be started
0141     if( allowSecondary ){
0142         d->startSecondary();
0143         if( d->options & Mode::SecondaryNotification ){
0144             d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
0145         }
0146         if( ! d->memory->unlock() ){
0147           qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
0148           qDebug() << d->memory->errorString();
0149         }
0150         return;
0151     }
0152 
0153     if( ! d->memory->unlock() ){
0154       qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
0155       qDebug() << d->memory->errorString();
0156     }
0157 
0158     d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
0159 
0160     delete d;
0161 
0162     ::exit( EXIT_SUCCESS );
0163 }
0164 
0165 SingleApplication::~SingleApplication()
0166 {
0167     Q_D( SingleApplication );
0168     delete d;
0169 }
0170 
0171 /**
0172  * Checks if the current application instance is primary.
0173  * @return Returns true if the instance is primary, false otherwise.
0174  */
0175 bool SingleApplication::isPrimary() const
0176 {
0177     Q_D( const SingleApplication );
0178     return d->server != nullptr;
0179 }
0180 
0181 /**
0182  * Checks if the current application instance is secondary.
0183  * @return Returns true if the instance is secondary, false otherwise.
0184  */
0185 bool SingleApplication::isSecondary() const
0186 {
0187     Q_D( const SingleApplication );
0188     return d->server == nullptr;
0189 }
0190 
0191 /**
0192  * Allows you to identify an instance by returning unique consecutive instance
0193  * ids. It is reset when the first (primary) instance of your app starts and
0194  * only incremented afterwards.
0195  * @return Returns a unique instance id.
0196  */
0197 quint32 SingleApplication::instanceId() const
0198 {
0199     Q_D( const SingleApplication );
0200     return d->instanceNumber;
0201 }
0202 
0203 /**
0204  * Returns the OS PID (Process Identifier) of the process running the primary
0205  * instance. Especially useful when SingleApplication is coupled with OS.
0206  * specific APIs.
0207  * @return Returns the primary instance PID.
0208  */
0209 qint64 SingleApplication::primaryPid() const
0210 {
0211     Q_D( const SingleApplication );
0212     return d->primaryPid();
0213 }
0214 
0215 /**
0216  * Returns the username the primary instance is running as.
0217  * @return Returns the username the primary instance is running as.
0218  */
0219 QString SingleApplication::primaryUser() const
0220 {
0221     Q_D( const SingleApplication );
0222     return d->primaryUser();
0223 }
0224 
0225 /**
0226  * Returns the username the current instance is running as.
0227  * @return Returns the username the current instance is running as.
0228  */
0229 QString SingleApplication::currentUser() const
0230 {
0231     return SingleApplicationPrivate::getUsername();
0232 }
0233 
0234 /**
0235  * Sends message to the Primary Instance.
0236  * @param message The message to send.
0237  * @param timeout the maximum timeout in milliseconds for blocking functions.
0238  * @param sendMode mode of operation
0239  * @return true if the message was sent successfuly, false otherwise.
0240  */
0241 bool SingleApplication::sendMessage( const QByteArray &message, int timeout, SendMode sendMode )
0242 {
0243     Q_D( SingleApplication );
0244 
0245     // Nobody to connect to
0246     if( isPrimary() ) return false;
0247 
0248     // Make sure the socket is connected
0249     if( ! d->connectToPrimary( timeout,  SingleApplicationPrivate::Reconnect ) )
0250       return false;
0251 
0252     return d->writeConfirmedMessage( timeout, message, sendMode );
0253 }
0254 
0255 /**
0256  * Cleans up the shared memory block and exits with a failure.
0257  * This function halts program execution.
0258  */
0259 void SingleApplication::abortSafely()
0260 {
0261     Q_D( SingleApplication );
0262 
0263     qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
0264     delete d;
0265     ::exit( EXIT_FAILURE );
0266 }
0267 
0268 QStringList SingleApplication::userData() const
0269 {
0270     Q_D( const SingleApplication );
0271     return d->appData();
0272 }
0273 
0274 #include "moc_singleapplication.cpp"