qtlocalpeer.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2016 The Qt Company Ltd.
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of Qt Creator.
  7. **
  8. ** Commercial License Usage
  9. ** Licensees holding valid commercial Qt licenses may use this file in
  10. ** accordance with the commercial license agreement provided with the
  11. ** Software or, alternatively, in accordance with the terms contained in
  12. ** a written agreement between you and The Qt Company. For licensing terms
  13. ** and conditions see https://www.qt.io/terms-conditions. For further
  14. ** information use the contact form at https://www.qt.io/contact-us.
  15. **
  16. ** GNU General Public License Usage
  17. ** Alternatively, this file may be used under the terms of the GNU
  18. ** General Public License version 3 as published by the Free Software
  19. ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
  20. ** included in the packaging of this file. Please review the following
  21. ** information to ensure the GNU General Public License requirements will
  22. ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
  23. **
  24. ****************************************************************************/
  25. #include "qtlocalpeer.h"
  26. #include <QCoreApplication>
  27. #include <QDataStream>
  28. #include <QTime>
  29. #if defined(Q_OS_WIN)
  30. #include <QLibrary>
  31. #include <qt_windows.h>
  32. typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
  33. static PProcessIdToSessionId pProcessIdToSessionId = 0;
  34. #endif
  35. #if defined(Q_OS_UNIX)
  36. #include <time.h>
  37. #include <unistd.h>
  38. #endif
  39. namespace SharedTools {
  40. static const char ack[] = "ack";
  41. QString QtLocalPeer::appSessionId(const QString &appId)
  42. {
  43. QByteArray idc = appId.toUtf8();
  44. #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
  45. quint16 idNum = qChecksum(idc);
  46. #else
  47. quint16 idNum = qChecksum(idc.constData(), idc.size());
  48. #endif
  49. //### could do: two 16bit checksums over separate halves of id, for a 32bit result - improved uniqeness probability. Every-other-char split would be best.
  50. QString res = QLatin1String("qtsingleapplication-")
  51. + QString::number(idNum, 16);
  52. #if defined(Q_OS_WIN)
  53. if (!pProcessIdToSessionId) {
  54. QLibrary lib(QLatin1String("kernel32"));
  55. pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
  56. }
  57. if (pProcessIdToSessionId) {
  58. DWORD sessionId = 0;
  59. pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
  60. res += QLatin1Char('-') + QString::number(sessionId, 16);
  61. }
  62. #else
  63. res += QLatin1Char('-') + QString::number(::getuid(), 16);
  64. #endif
  65. return res;
  66. }
  67. QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
  68. : QObject(parent), id(appId)
  69. {
  70. if (id.isEmpty())
  71. id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win
  72. socketName = appSessionId(id);
  73. server = new QLocalServer(this);
  74. QString lockName = QDir(QDir::tempPath()).absolutePath()
  75. + QLatin1Char('/') + socketName
  76. + QLatin1String("-lockfile");
  77. lockFile.setFileName(lockName);
  78. lockFile.open(QIODevice::ReadWrite);
  79. }
  80. bool QtLocalPeer::isClient()
  81. {
  82. if (lockFile.isLocked())
  83. return false;
  84. if (!lockFile.lock(QtLockedFile::WriteLock, false))
  85. return true;
  86. if (!QLocalServer::removeServer(socketName))
  87. qWarning("QtSingleCoreApplication: could not cleanup socket");
  88. bool res = server->listen(socketName);
  89. if (!res)
  90. qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
  91. QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection);
  92. return false;
  93. }
  94. bool QtLocalPeer::sendMessage(const QString &message, int timeout, bool block)
  95. {
  96. if (!isClient())
  97. return false;
  98. QLocalSocket socket;
  99. bool connOk = false;
  100. for (int i = 0; i < 2; i++) {
  101. // Try twice, in case the other instance is just starting up
  102. socket.connectToServer(socketName);
  103. connOk = socket.waitForConnected(timeout/2);
  104. if (connOk || i)
  105. break;
  106. int ms = 250;
  107. #if defined(Q_OS_WIN)
  108. Sleep(DWORD(ms));
  109. #else
  110. struct timespec ts = {ms / 1000, (ms % 1000) * 1000 * 1000};
  111. nanosleep(&ts, NULL);
  112. #endif
  113. }
  114. if (!connOk)
  115. return false;
  116. QByteArray uMsg(message.toUtf8());
  117. QDataStream ds(&socket);
  118. ds.writeBytes(uMsg.constData(), uMsg.size());
  119. bool res = socket.waitForBytesWritten(timeout);
  120. res &= socket.waitForReadyRead(timeout); // wait for ack
  121. res &= (socket.read(qstrlen(ack)) == ack);
  122. if (block) // block until peer disconnects
  123. socket.waitForDisconnected(-1);
  124. return res;
  125. }
  126. void QtLocalPeer::receiveConnection()
  127. {
  128. QLocalSocket* socket = server->nextPendingConnection();
  129. if (!socket)
  130. return;
  131. // Why doesn't Qt have a blocking stream that takes care of this shait???
  132. while (socket->bytesAvailable() < static_cast<int>(sizeof(quint32))) {
  133. if (!socket->isValid()) // stale request
  134. return;
  135. socket->waitForReadyRead(1000);
  136. }
  137. QDataStream ds(socket);
  138. QByteArray uMsg;
  139. quint32 remaining;
  140. ds >> remaining;
  141. uMsg.resize(remaining);
  142. int got = 0;
  143. char* uMsgBuf = uMsg.data();
  144. //qDebug() << "RCV: remaining" << remaining;
  145. do {
  146. got = ds.readRawData(uMsgBuf, remaining);
  147. remaining -= got;
  148. uMsgBuf += got;
  149. //qDebug() << "RCV: got" << got << "remaining" << remaining;
  150. } while (remaining && got >= 0 && socket->waitForReadyRead(2000));
  151. //### error check: got<0
  152. if (got < 0) {
  153. qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString();
  154. delete socket;
  155. return;
  156. }
  157. // ### async this
  158. QString message = QString::fromUtf8(uMsg.constData(), uMsg.size());
  159. socket->write(ack, qstrlen(ack));
  160. socket->waitForBytesWritten(1000);
  161. emit messageReceived(message, socket); // ##(might take a long time to return)
  162. }
  163. } // namespace SharedTools