singleapplication.cpp 9.1 KB

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