1 // The MIT License (MIT)
2 //
3 // Copyright (c) Itay Grudev 2015 - 2020
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 
23 #include <QtCore/QElapsedTimer>
24 #include <QtCore/QByteArray>
25 #include <QtCore/QSharedMemory>
26 
27 #include "singleapplication.h"
28 #include "singleapplication_p.h"
29 
30 /**
31  * @brief Constructor. Checks and fires up LocalServer or closes the program
32  * if another instance already exists
33  * @param argc
34  * @param argv
35  * @param allowSecondary Whether to enable secondary instance support
36  * @param options Optional flags to toggle specific behaviour
37  * @param timeout Maximum time blocking functions are allowed during app load
38  */
SingleApplication(int & argc,char * argv[],bool allowSecondary,Options options,int timeout,const QString & userData)39 SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
40     : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
41 {
42     Q_D( SingleApplication );
43 
44 #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
45     // On Android and iOS since the library is not supported fallback to
46     // standard QApplication behaviour by simply returning at this point.
47     qWarning() << "SingleApplication is not supported on Android and iOS systems.";
48     return;
49 #endif
50 
51     // Store the current mode of the program
52     d->options = options;
53 
54     // Add any unique user data
55     if ( ! userData.isEmpty() )
56         d->addAppData( userData );
57 
58     // Generating an application ID used for identifying the shared memory
59     // block and QLocalServer
60     d->genBlockServerName();
61 
62     // To mitigate QSharedMemory issues with large amount of processes
63     // attempting to attach at the same time
64     SingleApplicationPrivate::randomSleep();
65 
66 #ifdef Q_OS_UNIX
67     // By explicitly attaching it and then deleting it we make sure that the
68     // memory is deleted even after the process has crashed on Unix.
69     d->memory = new QSharedMemory( d->blockServerName );
70     d->memory->attach();
71     delete d->memory;
72 #endif
73     // Guarantee thread safe behaviour with a shared memory block.
74     d->memory = new QSharedMemory( d->blockServerName );
75 
76     // Create a shared memory block
77     if( d->memory->create( sizeof( InstancesInfo ) )){
78         // Initialize the shared memory block
79         if( ! d->memory->lock() ){
80           qCritical() << "SingleApplication: Unable to lock memory block after create.";
81           abortSafely();
82         }
83         d->initializeMemoryBlock();
84     } else {
85         if( d->memory->error() == QSharedMemory::AlreadyExists ){
86           // Attempt to attach to the memory segment
87           if( ! d->memory->attach() ){
88               qCritical() << "SingleApplication: Unable to attach to shared memory block.";
89               abortSafely();
90           }
91           if( ! d->memory->lock() ){
92             qCritical() << "SingleApplication: Unable to lock memory block after attach.";
93             abortSafely();
94           }
95         } else {
96           qCritical() << "SingleApplication: Unable to create block.";
97           abortSafely();
98         }
99     }
100 
101     auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
102     QElapsedTimer time;
103     time.start();
104 
105     // Make sure the shared memory block is initialised and in consistent state
106     while( true ){
107       // If the shared memory block's checksum is valid continue
108       if( d->blockChecksum() == inst->checksum ) break;
109 
110       // If more than 5s have elapsed, assume the primary instance crashed and
111       // assume it's position
112       if( time.elapsed() > 5000 ){
113           qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
114           d->initializeMemoryBlock();
115       }
116 
117       // Otherwise wait for a random period and try again. The random sleep here
118       // limits the probability of a collision between two racing apps and
119       // allows the app to initialise faster
120       if( ! d->memory->unlock() ){
121         qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
122         qDebug() << d->memory->errorString();
123       }
124       SingleApplicationPrivate::randomSleep();
125       if( ! d->memory->lock() ){
126         qCritical() << "SingleApplication: Unable to lock memory after random wait.";
127         abortSafely();
128       }
129     }
130 
131     if( inst->primary == false ){
132         d->startPrimary();
133         if( ! d->memory->unlock() ){
134           qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
135           qDebug() << d->memory->errorString();
136         }
137         return;
138     }
139 
140     // Check if another instance can be started
141     if( allowSecondary ){
142         d->startSecondary();
143         if( d->options & Mode::SecondaryNotification ){
144             d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
145         }
146         if( ! d->memory->unlock() ){
147           qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
148           qDebug() << d->memory->errorString();
149         }
150         return;
151     }
152 
153     if( ! d->memory->unlock() ){
154       qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
155       qDebug() << d->memory->errorString();
156     }
157 
158     d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
159 
160     delete d;
161 
162     ::exit( EXIT_SUCCESS );
163 }
164 
~SingleApplication()165 SingleApplication::~SingleApplication()
166 {
167     Q_D( SingleApplication );
168     delete d;
169 }
170 
171 /**
172  * Checks if the current application instance is primary.
173  * @return Returns true if the instance is primary, false otherwise.
174  */
isPrimary() const175 bool SingleApplication::isPrimary() const
176 {
177     Q_D( const SingleApplication );
178     return d->server != nullptr;
179 }
180 
181 /**
182  * Checks if the current application instance is secondary.
183  * @return Returns true if the instance is secondary, false otherwise.
184  */
isSecondary() const185 bool SingleApplication::isSecondary() const
186 {
187     Q_D( const SingleApplication );
188     return d->server == nullptr;
189 }
190 
191 /**
192  * Allows you to identify an instance by returning unique consecutive instance
193  * ids. It is reset when the first (primary) instance of your app starts and
194  * only incremented afterwards.
195  * @return Returns a unique instance id.
196  */
instanceId() const197 quint32 SingleApplication::instanceId() const
198 {
199     Q_D( const SingleApplication );
200     return d->instanceNumber;
201 }
202 
203 /**
204  * Returns the OS PID (Process Identifier) of the process running the primary
205  * instance. Especially useful when SingleApplication is coupled with OS.
206  * specific APIs.
207  * @return Returns the primary instance PID.
208  */
primaryPid() const209 qint64 SingleApplication::primaryPid() const
210 {
211     Q_D( const SingleApplication );
212     return d->primaryPid();
213 }
214 
215 /**
216  * Returns the username the primary instance is running as.
217  * @return Returns the username the primary instance is running as.
218  */
primaryUser() const219 QString SingleApplication::primaryUser() const
220 {
221     Q_D( const SingleApplication );
222     return d->primaryUser();
223 }
224 
225 /**
226  * Returns the username the current instance is running as.
227  * @return Returns the username the current instance is running as.
228  */
currentUser() const229 QString SingleApplication::currentUser() const
230 {
231     return SingleApplicationPrivate::getUsername();
232 }
233 
234 /**
235  * Sends message to the Primary Instance.
236  * @param message The message to send.
237  * @param timeout the maximum timeout in milliseconds for blocking functions.
238  * @return true if the message was sent successfuly, false otherwise.
239  */
sendMessage(const QByteArray & message,int timeout)240 bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
241 {
242     Q_D( SingleApplication );
243 
244     // Nobody to connect to
245     if( isPrimary() ) return false;
246 
247     // Make sure the socket is connected
248     if( ! d->connectToPrimary( timeout,  SingleApplicationPrivate::Reconnect ) )
249       return false;
250 
251     return d->writeConfirmedMessage( timeout, message );
252 }
253 
254 /**
255  * Cleans up the shared memory block and exits with a failure.
256  * This function halts program execution.
257  */
abortSafely()258 void SingleApplication::abortSafely()
259 {
260     Q_D( SingleApplication );
261 
262     qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
263     delete d;
264     ::exit( EXIT_FAILURE );
265 }
266 
userData() const267 QStringList SingleApplication::userData() const
268 {
269     Q_D( const SingleApplication );
270     return d->appData();
271 }
272