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