singleapplication_p.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. // W A R N I N G !!!
  24. // -----------------
  25. //
  26. // This file is not part of the SingleApplication API. It is used purely as an
  27. // implementation detail. This header file may change from version to
  28. // version without notice, or may even be removed.
  29. //
  30. #include <cstddef>
  31. #include <cstdlib>
  32. #include <QtCore/QByteArray>
  33. #include <QtCore/QCryptographicHash>
  34. #include <QtCore/QDataStream>
  35. #include <QtCore/QDir>
  36. #include <QtCore/QElapsedTimer>
  37. #include <QtCore/QThread>
  38. #include <QtNetwork/QLocalServer>
  39. #include <QtNetwork/QLocalSocket>
  40. #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
  41. #include <QtCore/QRandomGenerator>
  42. #else
  43. #include <QtCore/QDateTime>
  44. #endif
  45. #include "singleapplication.h"
  46. #include "singleapplication_p.h"
  47. #ifdef Q_OS_UNIX
  48. #include <pwd.h>
  49. #include <sys/types.h>
  50. #include <unistd.h>
  51. #endif
  52. #ifdef Q_OS_WIN
  53. #include <lmcons.h>
  54. #include <windows.h>
  55. #endif
  56. SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication* q_ptr)
  57. : q_ptr(q_ptr)
  58. {
  59. server = nullptr;
  60. socket = nullptr;
  61. memory = nullptr;
  62. instanceNumber = -1;
  63. }
  64. SingleApplicationPrivate::~SingleApplicationPrivate()
  65. {
  66. if (socket != nullptr) {
  67. socket->close();
  68. delete socket;
  69. }
  70. if (memory != nullptr) {
  71. memory->lock();
  72. auto* inst = static_cast<InstancesInfo*>(memory->data());
  73. if (server != nullptr) {
  74. server->close();
  75. delete server;
  76. inst->primary = false;
  77. inst->primaryPid = -1;
  78. inst->primaryUser[0] = '\0';
  79. inst->checksum = blockChecksum();
  80. }
  81. memory->unlock();
  82. delete memory;
  83. }
  84. }
  85. QString SingleApplicationPrivate::getUsername()
  86. {
  87. #ifdef Q_OS_WIN
  88. wchar_t username[UNLEN + 1];
  89. // Specifies size of the buffer on input
  90. DWORD usernameLength = UNLEN + 1;
  91. if (GetUserNameW(username, &usernameLength))
  92. return QString::fromWCharArray(username);
  93. #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
  94. return QString::fromLocal8Bit(qgetenv("USERNAME"));
  95. #else
  96. return qEnvironmentVariable("USERNAME");
  97. #endif
  98. #endif
  99. #ifdef Q_OS_UNIX
  100. QString username;
  101. uid_t uid = geteuid();
  102. struct passwd* pw = getpwuid(uid);
  103. if (pw)
  104. username = QString::fromLocal8Bit(pw->pw_name);
  105. if (username.isEmpty()) {
  106. #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
  107. username = QString::fromLocal8Bit(qgetenv("USER"));
  108. #else
  109. username = qEnvironmentVariable("USER");
  110. #endif
  111. }
  112. return username;
  113. #endif
  114. }
  115. void SingleApplicationPrivate::genBlockServerName()
  116. {
  117. QCryptographicHash appData(QCryptographicHash::Sha256);
  118. appData.addData("SingleApplication", 17);
  119. appData.addData(SingleApplication::app_t::applicationName().toUtf8());
  120. appData.addData(SingleApplication::app_t::organizationName().toUtf8());
  121. appData.addData(SingleApplication::app_t::organizationDomain().toUtf8());
  122. if (!(options & SingleApplication::Mode::ExcludeAppVersion)) {
  123. appData.addData(
  124. SingleApplication::app_t::applicationVersion().toUtf8());
  125. }
  126. if (!(options & SingleApplication::Mode::ExcludeAppPath)) {
  127. #ifdef Q_OS_WIN
  128. appData.addData(
  129. SingleApplication::app_t::applicationFilePath().toLower().toUtf8());
  130. #else
  131. appData.addData(
  132. SingleApplication::app_t::applicationFilePath().toUtf8());
  133. #endif
  134. }
  135. // User level block requires a user specific data in the hash
  136. if (options & SingleApplication::Mode::User) {
  137. appData.addData(getUsername().toUtf8());
  138. }
  139. // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
  140. // server naming requirements.
  141. blockServerName = appData.result().toBase64().replace("/", "_");
  142. }
  143. void SingleApplicationPrivate::initializeMemoryBlock()
  144. {
  145. auto* inst = static_cast<InstancesInfo*>(memory->data());
  146. inst->primary = false;
  147. inst->secondary = 0;
  148. inst->primaryPid = -1;
  149. inst->primaryUser[0] = '\0';
  150. inst->checksum = blockChecksum();
  151. }
  152. void SingleApplicationPrivate::startPrimary()
  153. {
  154. Q_Q(SingleApplication);
  155. // Reset the number of connections
  156. auto* inst = static_cast<InstancesInfo*>(memory->data());
  157. inst->primary = true;
  158. inst->primaryPid = q->applicationPid();
  159. qstrncpy(inst->primaryUser,
  160. getUsername().toUtf8().data(),
  161. sizeof(inst->primaryUser));
  162. inst->checksum = blockChecksum();
  163. instanceNumber = 0;
  164. // Successful creation means that no main process exists
  165. // So we start a QLocalServer to listen for connections
  166. QLocalServer::removeServer(blockServerName);
  167. server = new QLocalServer();
  168. // Restrict access to the socket according to the
  169. // SingleApplication::Mode::User flag on User level or no restrictions
  170. if (options & SingleApplication::Mode::User) {
  171. server->setSocketOptions(QLocalServer::UserAccessOption);
  172. } else {
  173. server->setSocketOptions(QLocalServer::WorldAccessOption);
  174. }
  175. server->listen(blockServerName);
  176. QObject::connect(server,
  177. &QLocalServer::newConnection,
  178. this,
  179. &SingleApplicationPrivate::slotConnectionEstablished);
  180. }
  181. void SingleApplicationPrivate::startSecondary()
  182. {
  183. auto* inst = static_cast<InstancesInfo*>(memory->data());
  184. inst->secondary += 1;
  185. inst->checksum = blockChecksum();
  186. instanceNumber = inst->secondary;
  187. }
  188. bool SingleApplicationPrivate::connectToPrimary(int timeout,
  189. ConnectionType connectionType)
  190. {
  191. QElapsedTimer time;
  192. time.start();
  193. // Connect to the Local Server of the Primary Instance if not already
  194. // connected.
  195. if (socket == nullptr) {
  196. socket = new QLocalSocket();
  197. }
  198. if (socket->state() == QLocalSocket::ConnectedState)
  199. return true;
  200. if (socket->state() != QLocalSocket::ConnectedState) {
  201. while (true) {
  202. randomSleep();
  203. if (socket->state() != QLocalSocket::ConnectingState)
  204. socket->connectToServer(blockServerName);
  205. if (socket->state() == QLocalSocket::ConnectingState) {
  206. socket->waitForConnected(timeout - time.elapsed());
  207. }
  208. // If connected break out of the loop
  209. if (socket->state() == QLocalSocket::ConnectedState)
  210. break;
  211. // If elapsed time since start is longer than the method timeout
  212. // return
  213. if (time.elapsed() >= timeout)
  214. return false;
  215. }
  216. }
  217. // Initialisation message according to the SingleApplication protocol
  218. QByteArray initMsg;
  219. QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
  220. #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
  221. writeStream.setVersion(QDataStream::Qt_5_6);
  222. #endif
  223. writeStream << blockServerName.toLatin1();
  224. writeStream << static_cast<quint8>(connectionType);
  225. writeStream << instanceNumber;
  226. quint16 checksum =
  227. qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
  228. writeStream << checksum;
  229. // The header indicates the message length that follows
  230. QByteArray header;
  231. QDataStream headerStream(&header, QIODevice::WriteOnly);
  232. #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
  233. headerStream.setVersion(QDataStream::Qt_5_6);
  234. #endif
  235. headerStream << static_cast<quint64>(initMsg.length());
  236. socket->write(header);
  237. socket->write(initMsg);
  238. socket->flush();
  239. if (socket->waitForBytesWritten(timeout - time.elapsed()))
  240. return true;
  241. return false;
  242. }
  243. quint16 SingleApplicationPrivate::blockChecksum()
  244. {
  245. return qChecksum(static_cast<const char*>(memory->data()),
  246. offsetof(InstancesInfo, checksum));
  247. }
  248. qint64 SingleApplicationPrivate::primaryPid()
  249. {
  250. qint64 pid;
  251. memory->lock();
  252. auto* inst = static_cast<InstancesInfo*>(memory->data());
  253. pid = inst->primaryPid;
  254. memory->unlock();
  255. return pid;
  256. }
  257. QString SingleApplicationPrivate::primaryUser()
  258. {
  259. QByteArray username;
  260. memory->lock();
  261. auto* inst = static_cast<InstancesInfo*>(memory->data());
  262. username = inst->primaryUser;
  263. memory->unlock();
  264. return QString::fromUtf8(username);
  265. }
  266. /**
  267. * @brief Executed when a connection has been made to the LocalServer
  268. */
  269. void SingleApplicationPrivate::slotConnectionEstablished()
  270. {
  271. QLocalSocket* nextConnSocket = server->nextPendingConnection();
  272. connectionMap.insert(nextConnSocket, ConnectionInfo());
  273. QObject::connect(
  274. nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() {
  275. auto& info = connectionMap[nextConnSocket];
  276. Q_EMIT this->slotClientConnectionClosed(nextConnSocket,
  277. info.instanceId);
  278. });
  279. QObject::connect(
  280. nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() {
  281. connectionMap.remove(nextConnSocket);
  282. nextConnSocket->deleteLater();
  283. });
  284. QObject::connect(
  285. nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() {
  286. auto& info = connectionMap[nextConnSocket];
  287. switch (info.stage) {
  288. case StageHeader:
  289. readInitMessageHeader(nextConnSocket);
  290. break;
  291. case StageBody:
  292. readInitMessageBody(nextConnSocket);
  293. break;
  294. case StageConnected:
  295. Q_EMIT this->slotDataAvailable(nextConnSocket,
  296. info.instanceId);
  297. break;
  298. default:
  299. break;
  300. };
  301. });
  302. }
  303. void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket* sock)
  304. {
  305. if (!connectionMap.contains(sock)) {
  306. return;
  307. }
  308. if (sock->bytesAvailable() < (qint64)sizeof(quint64)) {
  309. return;
  310. }
  311. QDataStream headerStream(sock);
  312. #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
  313. headerStream.setVersion(QDataStream::Qt_5_6);
  314. #endif
  315. // Read the header to know the message length
  316. quint64 msgLen = 0;
  317. headerStream >> msgLen;
  318. ConnectionInfo& info = connectionMap[sock];
  319. info.stage = StageBody;
  320. info.msgLen = msgLen;
  321. if (sock->bytesAvailable() >= (qint64)msgLen) {
  322. readInitMessageBody(sock);
  323. }
  324. }
  325. void SingleApplicationPrivate::readInitMessageBody(QLocalSocket* sock)
  326. {
  327. Q_Q(SingleApplication);
  328. if (!connectionMap.contains(sock)) {
  329. return;
  330. }
  331. ConnectionInfo& info = connectionMap[sock];
  332. if (sock->bytesAvailable() < (qint64)info.msgLen) {
  333. return;
  334. }
  335. // Read the message body
  336. QByteArray msgBytes = sock->read(info.msgLen);
  337. QDataStream readStream(msgBytes);
  338. #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
  339. readStream.setVersion(QDataStream::Qt_5_6);
  340. #endif
  341. // server name
  342. QByteArray latin1Name;
  343. readStream >> latin1Name;
  344. // connection type
  345. ConnectionType connectionType = InvalidConnection;
  346. quint8 connTypeVal = InvalidConnection;
  347. readStream >> connTypeVal;
  348. connectionType = static_cast<ConnectionType>(connTypeVal);
  349. // instance id
  350. quint32 instanceId = 0;
  351. readStream >> instanceId;
  352. // checksum
  353. quint16 msgChecksum = 0;
  354. readStream >> msgChecksum;
  355. const quint16 actualChecksum =
  356. qChecksum(msgBytes.constData(),
  357. static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
  358. bool isValid = readStream.status() == QDataStream::Ok &&
  359. QLatin1String(latin1Name) == blockServerName &&
  360. msgChecksum == actualChecksum;
  361. if (!isValid) {
  362. sock->close();
  363. return;
  364. }
  365. info.instanceId = instanceId;
  366. info.stage = StageConnected;
  367. if (connectionType == NewInstance ||
  368. (connectionType == SecondaryInstance &&
  369. options & SingleApplication::Mode::SecondaryNotification)) {
  370. Q_EMIT q->instanceStarted();
  371. }
  372. if (sock->bytesAvailable() > 0) {
  373. Q_EMIT this->slotDataAvailable(sock, instanceId);
  374. }
  375. }
  376. void SingleApplicationPrivate::slotDataAvailable(QLocalSocket* dataSocket,
  377. quint32 instanceId)
  378. {
  379. Q_Q(SingleApplication);
  380. Q_EMIT q->receivedMessage(instanceId, dataSocket->readAll());
  381. }
  382. void SingleApplicationPrivate::slotClientConnectionClosed(
  383. QLocalSocket* closedSocket,
  384. quint32 instanceId)
  385. {
  386. if (closedSocket->bytesAvailable() > 0)
  387. Q_EMIT slotDataAvailable(closedSocket, instanceId);
  388. }
  389. void SingleApplicationPrivate::randomSleep()
  390. {
  391. #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
  392. QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
  393. #else
  394. qsrand(QDateTime::currentMSecsSinceEpoch() %
  395. std::numeric_limits<uint>::max());
  396. QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) /
  397. RAND_MAX * 10));
  398. #endif
  399. }