11#include "kdsingleapplication_localsocket_p.h"
14#include <QtCore/QDeadlineTimer>
15#include <QtCore/QTimer>
16#include <QtCore/QLockFile>
17#include <QtCore/QDataStream>
19#include <QtCore/QtDebug>
20#include <QtCore/QLoggingCategory>
22#include <QtNetwork/QLocalServer>
23#include <QtNetwork/QLocalSocket>
35#include <qt_windows.h>
43KDSingleApplicationLocalSocket::KDSingleApplicationLocalSocket(
const QString &name,
QObject *parent)
48 m_socketName = QStringLiteral(
"kdsingleapp-%1-%2-%3")
50 .arg(qEnvironmentVariable(
"XDG_SESSION_ID"), name);
51#elif defined(Q_OS_WIN)
57 BOOL haveSessionId = ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
60 .
arg(haveSessionId ? sessionId : 0)
63#error "KDSingleApplication has not been ported to this platform"
69 qCDebug(kdsaLocalSocket) <<
"Socket name is" << m_socketName;
70 qCDebug(kdsaLocalSocket) <<
"Lock file path is" << lockFilePath;
72 std::unique_ptr<QLockFile> lockFile(
new QLockFile(lockFilePath));
73 lockFile->setStaleLockTime(0);
75 if (!lockFile->tryLock()) {
77 qCDebug(kdsaLocalSocket) <<
"Secondary instance";
81 qCDebug(kdsaLocalSocket) <<
"Primary instance";
83 std::unique_ptr<QLocalServer> server = std::make_unique<QLocalServer>();
84 if (!server->listen(m_socketName)) {
87 if (!server->listen(m_socketName)) {
89 qWarning(
"KDSingleApplication: unable to make the primary instance listen on %ls: %ls",
90 qUtf16Printable(m_socketName),
91 qUtf16Printable(server->errorString()));
98 this, &KDSingleApplicationLocalSocket::handleNewConnection);
100 m_lockFile = std::move(lockFile);
101 m_localServer = std::move(server);
104KDSingleApplicationLocalSocket::~KDSingleApplicationLocalSocket() =
default;
106bool KDSingleApplicationLocalSocket::isPrimaryInstance()
const
108 return m_localServer !=
nullptr;
111bool KDSingleApplicationLocalSocket::sendMessage(
const QByteArray &message,
int timeout)
113 Q_ASSERT(!isPrimaryInstance());
116 qCDebug(kdsaLocalSocket) <<
"Preparing to send message" << message <<
"with timeout" << timeout;
128 }
while (!deadline.hasExpired());
130 qCDebug(kdsaLocalSocket) <<
"Socket state:" << socket.
state() <<
"Timer remaining" << deadline.remainingTime() <<
"Expired?" << deadline.hasExpired();
132 if (deadline.hasExpired()) {
133 qCWarning(kdsaLocalSocket) <<
"Connection timed out";
143 socket.
write(encodedMessage);
146 qCDebug(kdsaLocalSocket) <<
"Wrote message in the socket"
147 <<
"Timer remaining" << deadline.remainingTime() <<
"Expired?" << deadline.hasExpired();
154 qCWarning(kdsaLocalSocket) <<
"Message to primary timed out";
159 qCDebug(kdsaLocalSocket) <<
"Bytes written, now disconnecting"
160 <<
"Timer remaining" << deadline.remainingTime() <<
"Expired?" << deadline.hasExpired();
165 qCDebug(kdsaLocalSocket) <<
"Disconnected -- success!";
170 qCWarning(kdsaLocalSocket) <<
"Disconnection from primary timed out";
174 qCDebug(kdsaLocalSocket) <<
"Disconnected -- success!";
179void KDSingleApplicationLocalSocket::handleNewConnection()
181 Q_ASSERT(m_localServer);
184 while ((socket = m_localServer->nextPendingConnection())) {
185 qCDebug(kdsaLocalSocket) <<
"Got new connection on" << m_socketName <<
"state" << socket->
state();
187 Connection c(std::move(socket));
188 socket = c.socket.get();
190 c.readDataConnection = QObjectConnectionHolder(
192 this, &KDSingleApplicationLocalSocket::readDataFromSecondary));
194 c.secondaryDisconnectedConnection = QObjectConnectionHolder(
196 this, &KDSingleApplicationLocalSocket::secondaryDisconnected));
198 c.abortConnection = QObjectConnectionHolder(
200 this, &KDSingleApplicationLocalSocket::abortConnectionToSecondary));
202 m_clients.push_back(std::move(c));
206 if (readDataFromSecondarySocket(socket))
210 secondarySocketDisconnected(socket);
214template<
typename Container>
217 auto i = std::find_if(container.begin(),
219 [socket](
const auto &c) { return c.socket.get() == socket; });
220 Q_ASSERT(i != container.end());
224template<
typename Container>
227 auto i = std::find_if(container.begin(),
229 [timer](
const auto &c) { return c.timeoutTimer.get() == timer; });
230 Q_ASSERT(i != container.end());
234void KDSingleApplicationLocalSocket::readDataFromSecondary()
237 readDataFromSecondarySocket(socket);
240bool KDSingleApplicationLocalSocket::readDataFromSecondarySocket(
QLocalSocket *socket)
244 c.readData.append(socket->
readAll());
246 qCDebug(kdsaLocalSocket) <<
"Got more data from a secondary. Data read so far:" << c.readData;
250 if (data.
size() >= 1) {
252 qCDebug(kdsaLocalSocket) <<
"Got an invalid protocol version";
261 ds.startTransaction();
265 if (ds.commitTransaction()) {
266 qCDebug(kdsaLocalSocket) <<
"Got a complete message:" << message;
267 Q_EMIT messageReceived(message);
275void KDSingleApplicationLocalSocket::secondaryDisconnected()
278 secondarySocketDisconnected(socket);
281void KDSingleApplicationLocalSocket::secondarySocketDisconnected(
QLocalSocket *socket)
284 Connection c = std::move(*i);
287 qCDebug(kdsaLocalSocket) <<
"Secondary disconnected. Data read:" << c.readData;
290void KDSingleApplicationLocalSocket::abortConnectionToSecondary()
295 Connection c = std::move(*i);
298 qCDebug(kdsaLocalSocket) <<
"Secondary timed out. Data read:" << c.readData;
301KDSingleApplicationLocalSocket::Connection::Connection(
QLocalSocket *_socket)
303 , timeoutTimer(new
QTimer)
static auto findConnectionByTimer(Container &container, QTimer *timer)
static auto findConnectionBySocket(Container &container, QLocalSocket *socket)
static const auto LOCALSOCKET_CONNECTION_TIMEOUT
static const char LOCALSOCKET_PROTOCOL_VERSION
Q_LOGGING_CATEGORY(kdsaLocalSocket, "kdsingleapplication.localsocket", QtWarningMsg)
qint64 write(const char *data, qint64 maxSize)
bool removeServer(const QString &name)
virtual qint64 bytesToWrite() const const override
void connectToServer(QIODevice::OpenMode openMode)
void disconnectFromServer()
QLocalSocket::LocalSocketState state() const const
virtual bool waitForBytesWritten(int msecs) override
bool waitForConnected(int msecs)
bool waitForDisconnected(int msecs)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromUtf8(const char *str, int size)