File indexing completed on 2024-05-19 04:38:45
0001 /**************************************************************************** 0002 ** 0003 ** Copyright (C) 2016 The Qt Company Ltd. 0004 ** Contact: https://www.qt.io/licensing/ 0005 ** 0006 ** This file is part of Qt Creator. 0007 ** 0008 ** Commercial License Usage 0009 ** Licensees holding valid commercial Qt licenses may use this file in 0010 ** accordance with the commercial license agreement provided with the 0011 ** Software or, alternatively, in accordance with the terms contained in 0012 ** a written agreement between you and The Qt Company. For licensing terms 0013 ** and conditions see https://www.qt.io/terms-conditions. For further 0014 ** information use the contact form at https://www.qt.io/contact-us. 0015 ** 0016 ** GNU General Public License Usage 0017 ** Alternatively, this file may be used under the terms of the GNU 0018 ** General Public License version 3 as published by the Free Software 0019 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 0020 ** included in the packaging of this file. Please review the following 0021 ** information to ensure the GNU General Public License requirements will 0022 ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 0023 ** 0024 ****************************************************************************/ 0025 0026 #include "qtlocalpeer.h" 0027 0028 #include <QCoreApplication> 0029 #include <QDataStream> 0030 #include <QTime> 0031 0032 #if defined(Q_OS_WIN) 0033 #include <QLibrary> 0034 #include <qt_windows.h> 0035 typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); 0036 static PProcessIdToSessionId pProcessIdToSessionId = 0; 0037 #endif 0038 0039 #if defined(Q_OS_UNIX) 0040 #include <time.h> 0041 #include <unistd.h> 0042 #endif 0043 0044 namespace SharedTools { 0045 0046 static const char ack[] = "ack"; 0047 0048 QString QtLocalPeer::appSessionId(const QString &appId) 0049 { 0050 QByteArray idc = appId.toUtf8(); 0051 quint16 idNum = qChecksum(idc.constData(), idc.size()); 0052 //### could do: two 16bit checksums over separate halves of id, for a 32bit result - improved uniqeness probability. Every-other-char split would be best. 0053 0054 QString res = QLatin1String("qtsingleapplication-") 0055 + QString::number(idNum, 16); 0056 #if defined(Q_OS_WIN) 0057 if (!pProcessIdToSessionId) { 0058 QLibrary lib(QLatin1String("kernel32")); 0059 pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); 0060 } 0061 if (pProcessIdToSessionId) { 0062 DWORD sessionId = 0; 0063 pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); 0064 res += QLatin1Char('-') + QString::number(sessionId, 16); 0065 } 0066 #else 0067 res += QLatin1Char('-') + QString::number(::getuid(), 16); 0068 #endif 0069 return res; 0070 } 0071 0072 QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId) 0073 : QObject(parent), id(appId) 0074 { 0075 if (id.isEmpty()) 0076 id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win 0077 0078 socketName = appSessionId(id); 0079 server = new QLocalServer(this); 0080 QString lockName = QDir(QDir::tempPath()).absolutePath() 0081 + QLatin1Char('/') + socketName 0082 + QLatin1String("-lockfile"); 0083 lockFile.setFileName(lockName); 0084 lockFile.open(QIODevice::ReadWrite); 0085 } 0086 0087 bool QtLocalPeer::isClient() 0088 { 0089 if (lockFile.isLocked()) 0090 return false; 0091 0092 if (!lockFile.lock(QtLockedFile::WriteLock, false)) 0093 return true; 0094 0095 if (!QLocalServer::removeServer(socketName)) 0096 qWarning("QtSingleCoreApplication: could not cleanup socket"); 0097 bool res = server->listen(socketName); 0098 if (!res) 0099 qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); 0100 QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); 0101 return false; 0102 } 0103 0104 bool QtLocalPeer::sendMessage(const QString &message, int timeout, bool block) 0105 { 0106 if (!isClient()) 0107 return false; 0108 0109 QLocalSocket socket; 0110 bool connOk = false; 0111 for (int i = 0; i < 2; i++) { 0112 // Try twice, in case the other instance is just starting up 0113 socket.connectToServer(socketName); 0114 connOk = socket.waitForConnected(timeout/2); 0115 if (connOk || i) 0116 break; 0117 int ms = 250; 0118 #if defined(Q_OS_WIN) 0119 Sleep(DWORD(ms)); 0120 #else 0121 struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; 0122 nanosleep(&ts, NULL); 0123 #endif 0124 } 0125 if (!connOk) 0126 return false; 0127 0128 QByteArray uMsg(message.toUtf8()); 0129 QDataStream ds(&socket); 0130 ds.writeBytes(uMsg.constData(), uMsg.size()); 0131 bool res = socket.waitForBytesWritten(timeout); 0132 res &= socket.waitForReadyRead(timeout); // wait for ack 0133 res &= (socket.read(qstrlen(ack)) == ack); 0134 if (block) // block until peer disconnects 0135 socket.waitForDisconnected(-1); 0136 return res; 0137 } 0138 0139 void QtLocalPeer::receiveConnection() 0140 { 0141 QLocalSocket* socket = server->nextPendingConnection(); 0142 if (!socket) 0143 return; 0144 0145 // Why doesn't Qt have a blocking stream that takes care of this shait??? 0146 while (socket->bytesAvailable() < static_cast<int>(sizeof(quint32))) { 0147 if (!socket->isValid()) // stale request 0148 return; 0149 socket->waitForReadyRead(1000); 0150 } 0151 QDataStream ds(socket); 0152 QByteArray uMsg; 0153 quint32 remaining; 0154 ds >> remaining; 0155 uMsg.resize(remaining); 0156 int got = 0; 0157 char* uMsgBuf = uMsg.data(); 0158 //qDebug() << "RCV: remaining" << remaining; 0159 do { 0160 got = ds.readRawData(uMsgBuf, remaining); 0161 remaining -= got; 0162 uMsgBuf += got; 0163 //qDebug() << "RCV: got" << got << "remaining" << remaining; 0164 } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); 0165 //### error check: got<0 0166 if (got < 0) { 0167 qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString(); 0168 delete socket; 0169 return; 0170 } 0171 // ### async this 0172 QString message = QString::fromUtf8(uMsg.constData(), uMsg.size()); 0173 socket->write(ack, qstrlen(ack)); 0174 socket->waitForBytesWritten(1000); 0175 emit messageReceived(message, socket); // ##(might take a long time to return) 0176 } 0177 0178 } // namespace SharedTools