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 }