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>
36#include <qt_windows.h>
47KDSingleApplicationLocalSocket::KDSingleApplicationLocalSocket(
const QString &name, KDSingleApplication::Options options,
QObject *parent)
51 m_socketName = QStringLiteral(
"kdsingleapp");
55 m_socketName += QStringLiteral(
"-");
56 uid_t uid = ::getuid();
57 struct passwd *pw = ::getpwuid(uid);
60 m_socketName += username;
66 QString sessionId = qEnvironmentVariable(
"XDG_SESSION_ID");
68 m_socketName += QStringLiteral(
"-");
69 m_socketName += sessionId;
72#elif defined(Q_OS_WIN)
77 DWORD usernameLen = UNLEN + 1;
78 wchar_t username[UNLEN + 1];
79 if (GetUserNameW(username, &usernameLen)) {
80 m_socketName += QStringLiteral(
"-");
86 BOOL haveSessionId = ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
88 m_socketName += QStringLiteral(
"-");
93#error "KDSingleApplication has not been ported to this platform"
96 m_socketName += QStringLiteral(
"-");
102 qCDebug(kdsaLocalSocket) <<
"Socket name is" << m_socketName;
103 qCDebug(kdsaLocalSocket) <<
"Lock file path is" << lockFilePath;
105 std::unique_ptr<QLockFile> lockFile(
new QLockFile(lockFilePath));
106 lockFile->setStaleLockTime(0);
108 if (!lockFile->tryLock()) {
110 qCDebug(kdsaLocalSocket) <<
"Secondary instance";
114 qCDebug(kdsaLocalSocket) <<
"Primary instance";
116 std::unique_ptr<QLocalServer> server = std::make_unique<QLocalServer>();
117 if (!server->listen(m_socketName)) {
120 if (!server->listen(m_socketName)) {
122 qWarning(
"KDSingleApplication: unable to make the primary instance listen on %ls: %ls",
123 qUtf16Printable(m_socketName),
124 qUtf16Printable(server->errorString()));
131 this, &KDSingleApplicationLocalSocket::handleNewConnection);
133 m_lockFile = std::move(lockFile);
134 m_localServer = std::move(server);
137KDSingleApplicationLocalSocket::~KDSingleApplicationLocalSocket() =
default;
139bool KDSingleApplicationLocalSocket::isPrimaryInstance()
const
141 return m_localServer !=
nullptr;
144bool KDSingleApplicationLocalSocket::sendMessage(
const QByteArray &message,
int timeout)
146 Q_ASSERT(!isPrimaryInstance());
149 qCDebug(kdsaLocalSocket) <<
"Preparing to send message" << message <<
"with timeout" << timeout;
161 }
while (!deadline.hasExpired());
163 qCDebug(kdsaLocalSocket) <<
"Socket state:" << socket.
state() <<
"Timer remaining" << deadline.remainingTime() <<
"Expired?" << deadline.hasExpired();
165 if (deadline.hasExpired()) {
166 qCWarning(kdsaLocalSocket) <<
"Connection timed out";
176 socket.
write(encodedMessage);
179 qCDebug(kdsaLocalSocket) <<
"Wrote message in the socket"
180 <<
"Timer remaining" << deadline.remainingTime() <<
"Expired?" << deadline.hasExpired();
187 qCWarning(kdsaLocalSocket) <<
"Message to primary timed out";
192 qCDebug(kdsaLocalSocket) <<
"Bytes written, now disconnecting"
193 <<
"Timer remaining" << deadline.remainingTime() <<
"Expired?" << deadline.hasExpired();
198 qCDebug(kdsaLocalSocket) <<
"Disconnected -- success!";
203 qCWarning(kdsaLocalSocket) <<
"Disconnection from primary timed out";
207 qCDebug(kdsaLocalSocket) <<
"Disconnected -- success!";
212void KDSingleApplicationLocalSocket::handleNewConnection()
214 Q_ASSERT(m_localServer);
217 while ((socket = m_localServer->nextPendingConnection())) {
218 qCDebug(kdsaLocalSocket) <<
"Got new connection on" << m_socketName <<
"state" << socket->
state();
220 Connection c(socket);
221 socket = c.socket.get();
223 c.readDataConnection = QObjectConnectionHolder(
225 this, &KDSingleApplicationLocalSocket::readDataFromSecondary));
227 c.secondaryDisconnectedConnection = QObjectConnectionHolder(
229 this, &KDSingleApplicationLocalSocket::secondaryDisconnected));
231 c.abortConnection = QObjectConnectionHolder(
233 this, &KDSingleApplicationLocalSocket::abortConnectionToSecondary));
235 m_clients.push_back(std::move(c));
239 if (readDataFromSecondarySocket(socket))
243 secondarySocketDisconnected(socket);
247template<
typename Container>
250 auto i = std::find_if(container.begin(),
252 [socket](
const auto &c) { return c.socket.get() == socket; });
253 Q_ASSERT(i != container.end());
257template<
typename Container>
260 auto i = std::find_if(container.begin(),
262 [timer](
const auto &c) { return c.timeoutTimer.get() == timer; });
263 Q_ASSERT(i != container.end());
267void KDSingleApplicationLocalSocket::readDataFromSecondary()
270 readDataFromSecondarySocket(socket);
273bool KDSingleApplicationLocalSocket::readDataFromSecondarySocket(
QLocalSocket *socket)
277 c.readData.append(socket->
readAll());
279 qCDebug(kdsaLocalSocket) <<
"Got more data from a secondary. Data read so far:" << c.readData;
283 if (data.
size() >= 1) {
285 qCDebug(kdsaLocalSocket) <<
"Got an invalid protocol version";
294 ds.startTransaction();
298 if (ds.commitTransaction()) {
299 qCDebug(kdsaLocalSocket) <<
"Got a complete message:" << message;
300 Q_EMIT messageReceived(message);
308void KDSingleApplicationLocalSocket::secondaryDisconnected()
311 secondarySocketDisconnected(socket);
314void KDSingleApplicationLocalSocket::secondarySocketDisconnected(
QLocalSocket *socket)
317 Connection c = std::move(*i);
320 qCDebug(kdsaLocalSocket) <<
"Secondary disconnected. Data read:" << c.readData;
323void KDSingleApplicationLocalSocket::abortConnectionToSecondary()
328 Connection c = std::move(*i);
331 qCDebug(kdsaLocalSocket) <<
"Secondary timed out. Data read:" << c.readData;
334KDSingleApplicationLocalSocket::Connection::Connection(
QLocalSocket *_socket)
336 , timeoutTimer(new
QTimer)
@ IncludeSessionInSocketName
@ IncludeUsernameInSocketName
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 fromUtf8(const char *str, int size)
QString fromWCharArray(const wchar_t *string, int size)
bool isEmpty() const const
QString number(int n, int base)