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