Skip to content

Commit da043e0

Browse files
committed
core/ipc: add ipc server/client
Currently can only kill a remote instance.
1 parent 13b6eea commit da043e0

File tree

14 files changed

+698
-108
lines changed

14 files changed

+698
-108
lines changed

src/core/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ qt_add_library(quickshell-core STATIC
4141
clock.cpp
4242
logging.cpp
4343
paths.cpp
44-
crashinfo.cpp
44+
instanceinfo.cpp
4545
common.cpp
46+
ipc.cpp
4647
)
4748

4849
if (CRASH_REPORTER)

src/core/crashinfo.cpp

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/core/instanceinfo.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include "instanceinfo.hpp"
2+
3+
#include <qdatastream.h>
4+
5+
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
6+
stream << info.instanceId << info.configPath << info.shellId << info.launchTime;
7+
return stream;
8+
}
9+
10+
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
11+
stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime;
12+
return stream;
13+
}
14+
15+
QDataStream& operator<<(QDataStream& stream, const RelaunchInfo& info) {
16+
stream << info.instance << info.noColor << info.sparseLogsOnly;
17+
return stream;
18+
}
19+
20+
QDataStream& operator>>(QDataStream& stream, RelaunchInfo& info) {
21+
stream >> info.instance >> info.noColor >> info.sparseLogsOnly;
22+
return stream;
23+
}
24+
25+
InstanceInfo InstanceInfo::CURRENT = {}; // NOLINT
26+
27+
namespace qs::crash {
28+
29+
CrashInfo CrashInfo::INSTANCE = {}; // NOLINT
30+
31+
}

src/core/crashinfo.hpp renamed to src/core/instanceinfo.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,27 @@
44
#include <qstring.h>
55

66
struct InstanceInfo {
7+
QString instanceId;
78
QString configPath;
89
QString shellId;
910
QString initialWorkdir;
1011
QDateTime launchTime;
12+
13+
static InstanceInfo CURRENT; // NOLINT
14+
};
15+
16+
struct RelaunchInfo {
17+
InstanceInfo instance;
1118
bool noColor = false;
1219
bool sparseLogsOnly = false;
1320
};
1421

1522
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info);
1623
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info);
1724

25+
QDataStream& operator<<(QDataStream& stream, const RelaunchInfo& info);
26+
QDataStream& operator>>(QDataStream& stream, RelaunchInfo& info);
27+
1828
namespace qs::crash {
1929

2030
struct CrashInfo {

src/core/ipc.cpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#include "ipc.hpp"
2+
#include <functional>
3+
4+
#include <qlocalserver.h>
5+
#include <qlocalsocket.h>
6+
#include <qlogging.h>
7+
#include <qloggingcategory.h>
8+
#include <qobject.h>
9+
10+
#include "generation.hpp"
11+
#include "paths.hpp"
12+
13+
namespace qs::ipc {
14+
15+
Q_LOGGING_CATEGORY(logIpc, "quickshell.ipc", QtWarningMsg);
16+
17+
IpcServer::IpcServer(const QString& path) {
18+
QObject::connect(&this->server, &QLocalServer::newConnection, this, &IpcServer::onNewConnection);
19+
20+
QLocalServer::removeServer(path);
21+
22+
if (!this->server.listen(path)) {
23+
qCCritical(logIpc) << "Failed to start IPC server on path" << path;
24+
return;
25+
}
26+
27+
qCInfo(logIpc) << "Started IPC server on path" << path;
28+
}
29+
30+
void IpcServer::start() {
31+
if (auto* run = QsPaths::instance()->instanceRunDir()) {
32+
auto path = run->filePath("ipc.sock");
33+
new IpcServer(path);
34+
} else {
35+
qCCritical(logIpc
36+
) << "Could not start IPC server as the instance runtime path could not be created.";
37+
}
38+
}
39+
40+
void IpcServer::onNewConnection() {
41+
while (auto* connection = this->server.nextPendingConnection()) {
42+
new IpcServerConnection(connection, this);
43+
}
44+
}
45+
46+
IpcServerConnection::IpcServerConnection(QLocalSocket* socket, IpcServer* server)
47+
: QObject(server)
48+
, socket(socket) {
49+
socket->setParent(this);
50+
this->stream.setDevice(socket);
51+
QObject::connect(socket, &QLocalSocket::disconnected, this, &IpcServerConnection::onDisconnected);
52+
QObject::connect(socket, &QLocalSocket::readyRead, this, &IpcServerConnection::onReadyRead);
53+
54+
qCInfo(logIpc) << "New IPC connection" << this;
55+
}
56+
57+
void IpcServerConnection::onDisconnected() {
58+
qCInfo(logIpc) << "IPC connection disconnected" << this;
59+
}
60+
61+
void IpcServerConnection::onReadyRead() {
62+
this->stream.startTransaction();
63+
64+
this->stream.startTransaction();
65+
auto command = IpcCommand::Unknown;
66+
this->stream >> command;
67+
if (!this->stream.commitTransaction()) return;
68+
69+
switch (command) {
70+
case IpcCommand::Kill:
71+
qInfo() << "Exiting due to IPC request.";
72+
EngineGeneration::currentGeneration()->quit();
73+
break;
74+
default:
75+
qCCritical(logIpc) << "Received invalid IPC command from" << this;
76+
this->socket->disconnectFromServer();
77+
break;
78+
}
79+
80+
if (!this->stream.commitTransaction()) return;
81+
}
82+
83+
IpcClient::IpcClient(const QString& path) {
84+
QObject::connect(&this->socket, &QLocalSocket::connected, this, &IpcClient::connected);
85+
QObject::connect(&this->socket, &QLocalSocket::disconnected, this, &IpcClient::disconnected);
86+
QObject::connect(&this->socket, &QLocalSocket::errorOccurred, this, &IpcClient::onError);
87+
88+
this->socket.connectToServer(path);
89+
this->stream.setDevice(&this->socket);
90+
}
91+
92+
bool IpcClient::isConnected() const { return this->socket.isValid(); }
93+
94+
void IpcClient::waitForConnected() { this->socket.waitForConnected(); }
95+
void IpcClient::waitForDisconnected() { this->socket.waitForDisconnected(); }
96+
97+
void IpcClient::kill() {
98+
qCDebug(logIpc) << "Sending kill command...";
99+
this->stream << IpcCommand::Kill;
100+
this->socket.flush();
101+
}
102+
103+
void IpcClient::onError(QLocalSocket::LocalSocketError error) {
104+
qCCritical(logIpc) << "Socket Error" << error;
105+
}
106+
107+
bool IpcClient::connect(const QString& id, const std::function<void(IpcClient& client)>& callback) {
108+
auto path = QsPaths::ipcPath(id);
109+
auto client = IpcClient(path);
110+
qCDebug(logIpc) << "Connecting to instance" << id << "at" << path;
111+
112+
client.waitForConnected();
113+
if (!client.isConnected()) return false;
114+
qCDebug(logIpc) << "Connected.";
115+
116+
callback(client);
117+
return true;
118+
}
119+
} // namespace qs::ipc

src/core/ipc.hpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#pragma once
2+
3+
#include <functional>
4+
5+
#include <qlocalserver.h>
6+
#include <qlocalsocket.h>
7+
#include <qobject.h>
8+
#include <qtmetamacros.h>
9+
10+
namespace qs::ipc {
11+
12+
enum class IpcCommand : quint8 {
13+
Unknown = 0,
14+
Kill,
15+
};
16+
17+
class IpcServer: public QObject {
18+
Q_OBJECT;
19+
20+
public:
21+
explicit IpcServer(const QString& path);
22+
23+
static void start();
24+
25+
private slots:
26+
void onNewConnection();
27+
28+
private:
29+
QLocalServer server;
30+
};
31+
32+
class IpcServerConnection: public QObject {
33+
Q_OBJECT;
34+
35+
public:
36+
explicit IpcServerConnection(QLocalSocket* socket, IpcServer* server);
37+
38+
private slots:
39+
void onDisconnected();
40+
void onReadyRead();
41+
42+
private:
43+
QLocalSocket* socket;
44+
QDataStream stream;
45+
};
46+
47+
class IpcClient: public QObject {
48+
Q_OBJECT;
49+
50+
public:
51+
explicit IpcClient(const QString& path);
52+
53+
[[nodiscard]] bool isConnected() const;
54+
void waitForConnected();
55+
void waitForDisconnected();
56+
57+
void kill();
58+
59+
[[nodiscard]] static bool
60+
connect(const QString& id, const std::function<void(IpcClient& client)>& callback);
61+
62+
signals:
63+
void connected();
64+
void disconnected();
65+
66+
private slots:
67+
static void onError(QLocalSocket::LocalSocketError error);
68+
69+
private:
70+
QLocalSocket socket;
71+
QDataStream stream;
72+
};
73+
74+
} // namespace qs::ipc

src/core/logging.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
#include <sys/mman.h>
2525
#include <sys/sendfile.h>
2626

27-
#include "crashinfo.hpp"
27+
#include "instanceinfo.hpp"
2828
#include "logging_p.hpp"
2929
#include "logging_qtprivate.cpp" // NOLINT
3030
#include "paths.hpp"
3131

32+
Q_LOGGING_CATEGORY(logBare, "quickshell.bare");
33+
3234
namespace qs::log {
3335

3436
Q_LOGGING_CATEGORY(logLogging, "quickshell.logging", QtWarningMsg);
@@ -53,37 +55,41 @@ void LogMessage::formatMessage(
5355
stream << msg.time.toString("yyyy-MM-dd hh:mm:ss.zzz");
5456
}
5557

56-
if (color) {
57-
switch (msg.type) {
58-
case QtDebugMsg: stream << "\033[34m DEBUG"; break;
59-
case QtInfoMsg: stream << "\033[32m INFO"; break;
60-
case QtWarningMsg: stream << "\033[33m WARN"; break;
61-
case QtCriticalMsg: stream << "\033[31m ERROR"; break;
62-
case QtFatalMsg: stream << "\033[31m FATAL"; break;
63-
}
58+
if (msg.category == "quickshell.bare") {
59+
stream << msg.body;
6460
} else {
65-
switch (msg.type) {
66-
case QtDebugMsg: stream << " DEBUG"; break;
67-
case QtInfoMsg: stream << " INFO"; break;
68-
case QtWarningMsg: stream << " WARN"; break;
69-
case QtCriticalMsg: stream << " ERROR"; break;
70-
case QtFatalMsg: stream << " FATAL"; break;
61+
if (color) {
62+
switch (msg.type) {
63+
case QtDebugMsg: stream << "\033[34m DEBUG"; break;
64+
case QtInfoMsg: stream << "\033[32m INFO"; break;
65+
case QtWarningMsg: stream << "\033[33m WARN"; break;
66+
case QtCriticalMsg: stream << "\033[31m ERROR"; break;
67+
case QtFatalMsg: stream << "\033[31m FATAL"; break;
68+
}
69+
} else {
70+
switch (msg.type) {
71+
case QtDebugMsg: stream << " DEBUG"; break;
72+
case QtInfoMsg: stream << " INFO"; break;
73+
case QtWarningMsg: stream << " WARN"; break;
74+
case QtCriticalMsg: stream << " ERROR"; break;
75+
case QtFatalMsg: stream << " FATAL"; break;
76+
}
7177
}
72-
}
7378

74-
const auto isDefault = msg.category == "default";
79+
const auto isDefault = msg.category == "default";
7580

76-
if (color && !isDefault && msg.type != QtFatalMsg) stream << "\033[97m";
81+
if (color && !isDefault && msg.type != QtFatalMsg) stream << "\033[97m";
7782

78-
if (!isDefault) {
79-
stream << ' ' << msg.category;
80-
}
83+
if (!isDefault) {
84+
stream << ' ' << msg.category;
85+
}
8186

82-
if (color && msg.type != QtFatalMsg) stream << "\033[0m";
87+
if (color && msg.type != QtFatalMsg) stream << "\033[0m";
8388

84-
stream << ": " << msg.body;
89+
stream << ": " << msg.body;
8590

86-
if (color && msg.type == QtFatalMsg) stream << "\033[0m";
91+
if (color && msg.type == QtFatalMsg) stream << "\033[0m";
92+
}
8793
}
8894

8995
bool CategoryFilter::shouldDisplay(QtMsgType type) const {

src/core/logging.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <qobject.h>
1212
#include <qtmetamacros.h>
1313

14+
Q_DECLARE_LOGGING_CATEGORY(logBare);
15+
1416
namespace qs::log {
1517

1618
struct LogMessage {

0 commit comments

Comments
 (0)