Falkon Develop
Cross-platform Qt-based web browser
qtlocalpeer.cpp
Go to the documentation of this file.
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt Solutions component.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** You may use this file under the terms of the BSD license as follows:
10**
11** "Redistribution and use in source and binary forms, with or without
12** modification, are permitted provided that the following conditions are
13** met:
14** * Redistributions of source code must retain the above copyright
15** notice, this list of conditions and the following disclaimer.
16** * Redistributions in binary form must reproduce the above copyright
17** notice, this list of conditions and the following disclaimer in
18** the documentation and/or other materials provided with the
19** distribution.
20** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21** of its contributors may be used to endorse or promote products derived
22** from this software without specific prior written permission.
23**
24**
25** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41
42#include "qtlocalpeer.h"
43#include <QCoreApplication>
44#include <QDataStream>
45#include <QTime>
46#include <QRegExp>
47
48#include "../config.h"
49#if defined(Q_OS_LINUX) && !defined(DISABLE_DBUS)
50#define USE_DBUS
51#endif
52
53#if defined(Q_OS_WIN)
54#include <QLibrary>
55#include <qt_windows.h>
56typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
57static PProcessIdToSessionId pProcessIdToSessionId = 0;
58#endif
59#if defined(Q_OS_UNIX)
60#include <ctime>
61#include <sys/types.h>
62#include <unistd.h>
63#endif
64
65namespace QtLP_Private {
66#include "qtlockedfile.cpp"
67#if defined(Q_OS_WIN)
68#include "qtlockedfile_win.cpp"
69#else
70#include "qtlockedfile_unix.cpp"
71#endif
72}
73
74#ifdef USE_DBUS
75#include <QDBusConnection>
76#include <QDBusConnectionInterface>
77#include <QDBusAbstractAdaptor>
78
79class QtSingleAppDBusInterface : public QDBusAbstractAdaptor
80{
81 Q_OBJECT
82 Q_CLASSINFO("D-Bus Interface", "org.kde.QtSingleApplication")
83
84public:
85 explicit QtSingleAppDBusInterface(QObject *parent)
86 : QDBusAbstractAdaptor(parent)
87 {
88 }
89
90public Q_SLOTS:
91 void SendMessage(const QString &message)
92 {
93 Q_EMIT messageReceived(message);
94 }
95
96Q_SIGNALS:
97 void messageReceived(const QString &message);
98};
99#endif
100
101const char* QtLocalPeer::ack = "ack";
102
103QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
104 : QObject(parent), id(appId)
105{
106 QString prefix = id;
107 if (id.isEmpty()) {
108 id = QCoreApplication::applicationFilePath();
109#if defined(Q_OS_WIN)
110 id = id.toLower();
111#endif
112 prefix = id.section(QLatin1Char('/'), -1);
113 }
114 prefix = QRegExp(QStringLiteral("[^a-zA-Z]")).removeIn(prefix);
115 prefix.truncate(6);
116
117 QByteArray idc = id.toUtf8();
118 quint16 idNum = qChecksum(QByteArrayView(idc.constData(), idc.size()));
119 socketName = QLatin1String("qtsingleapp-") + prefix
120 + QLatin1Char('-') + QString::number(idNum, 16);
121
122#if defined(Q_OS_WIN)
123 if (!pProcessIdToSessionId) {
124 QLibrary lib("kernel32");
125 pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
126 }
127 if (pProcessIdToSessionId) {
128 DWORD sessionId = 0;
129 pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
130 socketName += QLatin1Char('-') + QString::number(sessionId, 16);
131 }
132#else
133 socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
134#endif
135
136#ifdef USE_DBUS
137 if (!QDBusConnection::sessionBus().isConnected()) {
138 qCritical("Failed to connect to session bus!");
139 }
140 m_dbusRegistered = QDBusConnection::sessionBus().registerService(id);
141 if (m_dbusRegistered) {
142 auto *iface = new QtSingleAppDBusInterface(this);
143 connect(iface, &QtSingleAppDBusInterface::messageReceived, this, &QtLocalPeer::messageReceived);
144 QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), this);
145 }
146#else
147 server = new QLocalServer(this);
148 QString lockName = QDir(QDir::tempPath()).absolutePath()
149 + QLatin1Char('/') + socketName
150 + QLatin1String("-lockfile");
151 lockFile.setFileName(lockName);
152 lockFile.open(QIODevice::ReadWrite);
153#endif
154}
155
156
157
159{
160#ifdef USE_DBUS
161 if (m_dbusRegistered) {
162 return false;
163 }
164 return QDBusConnection::sessionBus().interface()->isServiceRegistered(id).value();
165#else
166 if (lockFile.isLocked())
167 return false;
168
170 return true;
171
172 bool res = server->listen(socketName);
173#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
174 // ### Workaround
175 if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
176 QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName);
177 res = server->listen(socketName);
178 }
179#endif
180 if (!res)
181 qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
182 QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection);
183 return false;
184#endif
185}
186
187
188bool QtLocalPeer::sendMessage(const QString &message, int timeout)
189{
190 if (!isClient())
191 return false;
192
193#ifdef USE_DBUS
194 QDBusMessage msg = QDBusMessage::createMethodCall(id, QStringLiteral("/"),
195 QStringLiteral("org.kde.QtSingleApplication"),
196 QStringLiteral("SendMessage"));
197 msg << message;
198 return QDBusConnection::sessionBus().call(msg, QDBus::Block, timeout).type() == QDBusMessage::ReplyMessage;
199#else
200 QLocalSocket socket;
201 bool connOk = false;
202 for(int i = 0; i < 2; i++) {
203 // Try twice, in case the other instance is just starting up
204 socket.connectToServer(socketName);
205 connOk = socket.waitForConnected(timeout/2);
206 if (connOk || i)
207 break;
208 int ms = 250;
209#if defined(Q_OS_WIN)
210 Sleep(DWORD(ms));
211#else
212 struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
213 nanosleep(&ts, NULL);
214#endif
215 }
216 if (!connOk)
217 return false;
218
219 QByteArray uMsg(message.toUtf8());
220 QDataStream ds(&socket);
221 ds.writeBytes(uMsg.constData(), uMsg.size());
222 bool res = socket.waitForBytesWritten(timeout);
223 if (res) {
224 res &= socket.waitForReadyRead(timeout); // wait for ack
225 if (res)
226 res &= (socket.read(qstrlen(ack)) == ack);
227 }
228 return res;
229#endif
230}
231
233{
234#ifndef USE_DBUS
235 lockFile.remove();
236#endif
237}
238
240{
241#ifndef USE_DBUS
242 QLocalSocket* socket = server->nextPendingConnection();
243 if (!socket)
244 return;
245
246 while (true) {
247 if (socket->state() == QLocalSocket::UnconnectedState) {
248 qWarning("QtLocalPeer: Peer disconnected");
249 delete socket;
250 return;
251 }
252 if (socket->bytesAvailable() >= qint64(sizeof(quint32)))
253 break;
254 socket->waitForReadyRead();
255 }
256
257 QDataStream ds(socket);
258 QByteArray uMsg;
259 quint32 remaining;
260 ds >> remaining;
261 uMsg.resize(remaining);
262 int got = 0;
263 char* uMsgBuf = uMsg.data();
264 do {
265 got = ds.readRawData(uMsgBuf, remaining);
266 remaining -= got;
267 uMsgBuf += got;
268 } while (remaining && got >= 0 && socket->waitForReadyRead(2000));
269 if (got < 0) {
270 qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
271 delete socket;
272 return;
273 }
274 QString message(QString::fromUtf8(uMsg));
275 socket->write(ack, qstrlen(ack));
276 socket->waitForBytesWritten(1000);
277 socket->waitForDisconnected(1000); // make sure client reads ack
278 delete socket;
279 Q_EMIT messageReceived(message); //### (might take a long time to return)
280#endif
281}
282
283#include "qtlocalpeer.moc"
bool open(OpenMode mode) override
bool lock(LockMode mode, bool block=true)
QString socketName
Definition: qtlocalpeer.h:70
void messageReceived(const QString &message)
bool isClient()
QString id
Definition: qtlocalpeer.h:69
bool sendMessage(const QString &message, int timeout)
QtLocalPeer(QObject *parent=nullptr, const QString &appId=QString())
QtLP_Private::QtLockedFile lockFile
Definition: qtlocalpeer.h:72
QLocalServer * server
Definition: qtlocalpeer.h:71
void removeLockedFile()
void receiveConnection()
i
Definition: i18n.py:23