singleapplication.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. #include <QtCore/QElapsedTimer>
  23. #include <QtCore/QByteArray>
  24. #include <QtCore/QSharedMemory>
  25. #include "singleapplication.h"
  26. #include "singleapplication_p.h"
  27. /**
  28. * @brief Constructor. Checks and fires up LocalServer or closes the program
  29. * if another instance already exists
  30. * @param argc
  31. * @param argv
  32. * @param allowSecondary Whether to enable secondary instance support
  33. * @param options Optional flags to toggle specific behaviour
  34. * @param timeout Maximum time blocking functions are allowed during app load
  35. */
  36. SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
  37. : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
  38. {
  39. Q_D( SingleApplication );
  40. #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
  41. // On Android and iOS since the library is not supported fallback to
  42. // standard QApplication behaviour by simply returning at this point.
  43. qWarning() << "SingleApplication is not supported on Android and iOS systems.";
  44. return;
  45. #endif
  46. // Store the current mode of the program
  47. d->options = options;
  48. // Generating an application ID used for identifying the shared memory
  49. // block and QLocalServer
  50. d->genBlockServerName();
  51. // To mitigate QSharedMemory issues with large amount of processes
  52. // attempting to attach at the same time
  53. SingleApplicationPrivate::randomSleep();
  54. #ifdef Q_OS_UNIX
  55. // By explicitly attaching it and then deleting it we make sure that the
  56. // memory is deleted even after the process has crashed on Unix.
  57. d->memory = new QSharedMemory( d->blockServerName );
  58. d->memory->attach();
  59. delete d->memory;
  60. #endif
  61. // Guarantee thread safe behaviour with a shared memory block.
  62. d->memory = new QSharedMemory( d->blockServerName );
  63. // Create a shared memory block
  64. if( d->memory->create( sizeof( InstancesInfo ) )){
  65. // Initialize the shared memory block
  66. if( ! d->memory->lock() ){
  67. qCritical() << "SingleApplication: Unable to lock memory block after create.";
  68. abortSafely();
  69. }
  70. d->initializeMemoryBlock();
  71. } else {
  72. if( d->memory->error() == QSharedMemory::AlreadyExists ){
  73. // Attempt to attach to the memory segment
  74. if( ! d->memory->attach() ){
  75. qCritical() << "SingleApplication: Unable to attach to shared memory block.";
  76. abortSafely();
  77. }
  78. if( ! d->memory->lock() ){
  79. qCritical() << "SingleApplication: Unable to lock memory block after attach.";
  80. abortSafely();
  81. }
  82. } else {
  83. qCritical() << "SingleApplication: Unable to create block.";
  84. abortSafely();
  85. }
  86. }
  87. auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
  88. QElapsedTimer time;
  89. time.start();
  90. // Make sure the shared memory block is initialised and in consistent state
  91. while( true ){
  92. // If the shared memory block's checksum is valid continue
  93. if( d->blockChecksum() == inst->checksum ) break;
  94. // If more than 5s have elapsed, assume the primary instance crashed and
  95. // assume it's position
  96. if( time.elapsed() > 5000 ){
  97. qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
  98. d->initializeMemoryBlock();
  99. }
  100. // Otherwise wait for a random period and try again. The random sleep here
  101. // limits the probability of a collision between two racing apps and
  102. // allows the app to initialise faster
  103. if( ! d->memory->unlock() ){
  104. qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
  105. qDebug() << d->memory->errorString();
  106. }
  107. SingleApplicationPrivate::randomSleep();
  108. if( ! d->memory->lock() ){
  109. qCritical() << "SingleApplication: Unable to lock memory after random wait.";
  110. abortSafely();
  111. }
  112. }
  113. if( inst->primary == false ){
  114. d->startPrimary();
  115. if( ! d->memory->unlock() ){
  116. qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
  117. qDebug() << d->memory->errorString();
  118. }
  119. return;
  120. }
  121. // Check if another instance can be started
  122. if( allowSecondary ){
  123. d->startSecondary();
  124. if( d->options & Mode::SecondaryNotification ){
  125. d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
  126. }
  127. if( ! d->memory->unlock() ){
  128. qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
  129. qDebug() << d->memory->errorString();
  130. }
  131. return;
  132. }
  133. if( ! d->memory->unlock() ){
  134. qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
  135. qDebug() << d->memory->errorString();
  136. }
  137. d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
  138. delete d;
  139. ::exit( EXIT_SUCCESS );
  140. }
  141. SingleApplication::~SingleApplication()
  142. {
  143. Q_D( SingleApplication );
  144. delete d;
  145. }
  146. /**
  147. * Checks if the current application instance is primary.
  148. * @return Returns true if the instance is primary, false otherwise.
  149. */
  150. bool SingleApplication::isPrimary()
  151. {
  152. Q_D( SingleApplication );
  153. return d->server != nullptr;
  154. }
  155. /**
  156. * Checks if the current application instance is secondary.
  157. * @return Returns true if the instance is secondary, false otherwise.
  158. */
  159. bool SingleApplication::isSecondary()
  160. {
  161. Q_D( SingleApplication );
  162. return d->server == nullptr;
  163. }
  164. /**
  165. * Allows you to identify an instance by returning unique consecutive instance
  166. * ids. It is reset when the first (primary) instance of your app starts and
  167. * only incremented afterwards.
  168. * @return Returns a unique instance id.
  169. */
  170. quint32 SingleApplication::instanceId()
  171. {
  172. Q_D( SingleApplication );
  173. return d->instanceNumber;
  174. }
  175. /**
  176. * Returns the OS PID (Process Identifier) of the process running the primary
  177. * instance. Especially useful when SingleApplication is coupled with OS.
  178. * specific APIs.
  179. * @return Returns the primary instance PID.
  180. */
  181. qint64 SingleApplication::primaryPid()
  182. {
  183. Q_D( SingleApplication );
  184. return d->primaryPid();
  185. }
  186. /**
  187. * Returns the username the primary instance is running as.
  188. * @return Returns the username the primary instance is running as.
  189. */
  190. QString SingleApplication::primaryUser()
  191. {
  192. Q_D( SingleApplication );
  193. return d->primaryUser();
  194. }
  195. /**
  196. * Returns the username the current instance is running as.
  197. * @return Returns the username the current instance is running as.
  198. */
  199. QString SingleApplication::currentUser()
  200. {
  201. return SingleApplicationPrivate::getUsername();
  202. }
  203. /**
  204. * Sends message to the Primary Instance.
  205. * @param message The message to send.
  206. * @param timeout the maximum timeout in milliseconds for blocking functions.
  207. * @return true if the message was sent successfuly, false otherwise.
  208. */
  209. bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
  210. {
  211. Q_D( SingleApplication );
  212. // Nobody to connect to
  213. if( isPrimary() ) return false;
  214. // Make sure the socket is connected
  215. if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
  216. return false;
  217. d->socket->write( message );
  218. bool dataWritten = d->socket->waitForBytesWritten( timeout );
  219. d->socket->flush();
  220. return dataWritten;
  221. }
  222. /**
  223. * Cleans up the shared memory block and exits with a failure.
  224. * This function halts program execution.
  225. */
  226. void SingleApplication::abortSafely()
  227. {
  228. Q_D( SingleApplication );
  229. qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
  230. delete d;
  231. ::exit( EXIT_FAILURE );
  232. }