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"