qtlocalpeer.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. qDebug() << "-----------" << lockFile.fileName();
  80. }
  81. bool QtLocalPeer::isClient()
  82. {
  83. if (lockFile.isLocked())
  84. return false;
  85. if (!lockFile.lock(QtLockedFile::WriteLock, false))
  86. return true;
  87. if (!QLocalServer::removeServer(socketName))
  88. qWarning("QtSingleCoreApplication: could not cleanup socket");
  89. bool res = server->listen(socketName);
  90. if (!res)
  91. qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
  92. QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection);
  93. return false;
  94. }
  95. bool QtLocalPeer::sendMessage(const QString &message, int timeout, bool block)
  96. {
  97. if (!isClient())
  98. return false;
  99. QLocalSocket socket;
  100. bool connOk = false;
  101. for (int i = 0; i < 2; i++) {
  102. // Try twice, in case the other instance is just starting up
  103. socket.connectToServer(socketName);
  104. connOk = socket.waitForConnected(timeout/2);
  105. if (connOk || i)
  106. break;
  107. int ms = 250;
  108. #if defined(Q_OS_WIN)
  109. Sleep(DWORD(ms));
  110. #else
  111. struct timespec ts = {ms / 1000, (ms % 1000) * 1000 * 1000};
  112. nanosleep(&ts, NULL);
  113. #endif
  114. }
  115. if (!connOk)
  116. return false;
  117. QByteArray uMsg(message.toUtf8());
  118. QDataStream ds(&socket);
  119. ds.writeBytes(uMsg.constData(), uMsg.size());
  120. bool res = socket.waitForBytesWritten(timeout);
  121. res &= socket.waitForReadyRead(timeout); // wait for ack
  122. res &= (socket.read(qstrlen(ack)) == ack);
  123. if (block) // block until peer disconnects
  124. socket.waitForDisconnected(-1);
  125. return res;
  126. }
  127. void QtLocalPeer::receiveConnection()
  128. {
  129. QLocalSocket* socket = server->nextPendingConnection();
  130. if (!socket)
  131. return;
  132. // Why doesn't Qt have a blocking stream that takes care of this shait???
  133. while (socket->bytesAvailable() < static_cast<int>(sizeof(quint32))) {
  134. if (!socket->isValid()) // stale request
  135. return;
  136. socket->waitForReadyRead(1000);
  137. }
  138. QDataStream ds(socket);
  139. QByteArray uMsg;
  140. quint32 remaining;
  141. ds >> remaining;
  142. uMsg.resize(remaining);
  143. int got = 0;
  144. char* uMsgBuf = uMsg.data();
  145. //qDebug() << "RCV: remaining" << remaining;
  146. do {
  147. got = ds.readRawData(uMsgBuf, remaining);
  148. remaining -= got;
  149. uMsgBuf += got;
  150. //qDebug() << "RCV: got" << got << "remaining" << remaining;
  151. } while (remaining && got >= 0 && socket->waitForReadyRead(2000));
  152. //### error check: got<0
  153. if (got < 0) {
  154. qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString();
  155. delete socket;
  156. return;
  157. }
  158. // ### async this
  159. QString message = QString::fromUtf8(uMsg.constData(), uMsg.size());
  160. socket->write(ack, qstrlen(ack));
  161. socket->waitForBytesWritten(1000);
  162. emit messageReceived(message, socket); // ##(might take a long time to return)
  163. }
  164. } // namespace SharedTools