forked from quickshell/quickshell
core/ipc: add ipc server/client
Currently can only kill a remote instance.
This commit is contained in:
parent
13b6eeaa22
commit
da043e092a
14 changed files with 710 additions and 120 deletions
|
@ -41,8 +41,9 @@ qt_add_library(quickshell-core STATIC
|
||||||
clock.cpp
|
clock.cpp
|
||||||
logging.cpp
|
logging.cpp
|
||||||
paths.cpp
|
paths.cpp
|
||||||
crashinfo.cpp
|
instanceinfo.cpp
|
||||||
common.cpp
|
common.cpp
|
||||||
|
ipc.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CRASH_REPORTER)
|
if (CRASH_REPORTER)
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
#include "crashinfo.hpp"
|
|
||||||
|
|
||||||
#include <qdatastream.h>
|
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
|
|
||||||
stream << info.configPath << info.shellId << info.launchTime << info.noColor;
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
|
|
||||||
stream >> info.configPath >> info.shellId >> info.launchTime >> info.noColor;
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace qs::crash {
|
|
||||||
|
|
||||||
CrashInfo CrashInfo::INSTANCE = {}; // NOLINT
|
|
||||||
|
|
||||||
}
|
|
31
src/core/instanceinfo.cpp
Normal file
31
src/core/instanceinfo.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "instanceinfo.hpp"
|
||||||
|
|
||||||
|
#include <qdatastream.h>
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
|
||||||
|
stream << info.instanceId << info.configPath << info.shellId << info.launchTime;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
|
||||||
|
stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& stream, const RelaunchInfo& info) {
|
||||||
|
stream << info.instance << info.noColor << info.sparseLogsOnly;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream& stream, RelaunchInfo& info) {
|
||||||
|
stream >> info.instance >> info.noColor >> info.sparseLogsOnly;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceInfo InstanceInfo::CURRENT = {}; // NOLINT
|
||||||
|
|
||||||
|
namespace qs::crash {
|
||||||
|
|
||||||
|
CrashInfo CrashInfo::INSTANCE = {}; // NOLINT
|
||||||
|
|
||||||
|
}
|
|
@ -4,10 +4,17 @@
|
||||||
#include <qstring.h>
|
#include <qstring.h>
|
||||||
|
|
||||||
struct InstanceInfo {
|
struct InstanceInfo {
|
||||||
|
QString instanceId;
|
||||||
QString configPath;
|
QString configPath;
|
||||||
QString shellId;
|
QString shellId;
|
||||||
QString initialWorkdir;
|
QString initialWorkdir;
|
||||||
QDateTime launchTime;
|
QDateTime launchTime;
|
||||||
|
|
||||||
|
static InstanceInfo CURRENT; // NOLINT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RelaunchInfo {
|
||||||
|
InstanceInfo instance;
|
||||||
bool noColor = false;
|
bool noColor = false;
|
||||||
bool sparseLogsOnly = false;
|
bool sparseLogsOnly = false;
|
||||||
};
|
};
|
||||||
|
@ -15,6 +22,9 @@ struct InstanceInfo {
|
||||||
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info);
|
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info);
|
||||||
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info);
|
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info);
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& stream, const RelaunchInfo& info);
|
||||||
|
QDataStream& operator>>(QDataStream& stream, RelaunchInfo& info);
|
||||||
|
|
||||||
namespace qs::crash {
|
namespace qs::crash {
|
||||||
|
|
||||||
struct CrashInfo {
|
struct CrashInfo {
|
119
src/core/ipc.cpp
Normal file
119
src/core/ipc.cpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#include "ipc.hpp"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <qlocalserver.h>
|
||||||
|
#include <qlocalsocket.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
|
||||||
|
#include "generation.hpp"
|
||||||
|
#include "paths.hpp"
|
||||||
|
|
||||||
|
namespace qs::ipc {
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(logIpc, "quickshell.ipc", QtWarningMsg);
|
||||||
|
|
||||||
|
IpcServer::IpcServer(const QString& path) {
|
||||||
|
QObject::connect(&this->server, &QLocalServer::newConnection, this, &IpcServer::onNewConnection);
|
||||||
|
|
||||||
|
QLocalServer::removeServer(path);
|
||||||
|
|
||||||
|
if (!this->server.listen(path)) {
|
||||||
|
qCCritical(logIpc) << "Failed to start IPC server on path" << path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCInfo(logIpc) << "Started IPC server on path" << path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcServer::start() {
|
||||||
|
if (auto* run = QsPaths::instance()->instanceRunDir()) {
|
||||||
|
auto path = run->filePath("ipc.sock");
|
||||||
|
new IpcServer(path);
|
||||||
|
} else {
|
||||||
|
qCCritical(logIpc
|
||||||
|
) << "Could not start IPC server as the instance runtime path could not be created.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcServer::onNewConnection() {
|
||||||
|
while (auto* connection = this->server.nextPendingConnection()) {
|
||||||
|
new IpcServerConnection(connection, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcServerConnection::IpcServerConnection(QLocalSocket* socket, IpcServer* server)
|
||||||
|
: QObject(server)
|
||||||
|
, socket(socket) {
|
||||||
|
socket->setParent(this);
|
||||||
|
this->stream.setDevice(socket);
|
||||||
|
QObject::connect(socket, &QLocalSocket::disconnected, this, &IpcServerConnection::onDisconnected);
|
||||||
|
QObject::connect(socket, &QLocalSocket::readyRead, this, &IpcServerConnection::onReadyRead);
|
||||||
|
|
||||||
|
qCInfo(logIpc) << "New IPC connection" << this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcServerConnection::onDisconnected() {
|
||||||
|
qCInfo(logIpc) << "IPC connection disconnected" << this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcServerConnection::onReadyRead() {
|
||||||
|
this->stream.startTransaction();
|
||||||
|
|
||||||
|
this->stream.startTransaction();
|
||||||
|
auto command = IpcCommand::Unknown;
|
||||||
|
this->stream >> command;
|
||||||
|
if (!this->stream.commitTransaction()) return;
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case IpcCommand::Kill:
|
||||||
|
qInfo() << "Exiting due to IPC request.";
|
||||||
|
EngineGeneration::currentGeneration()->quit();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCCritical(logIpc) << "Received invalid IPC command from" << this;
|
||||||
|
this->socket->disconnectFromServer();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->stream.commitTransaction()) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcClient::IpcClient(const QString& path) {
|
||||||
|
QObject::connect(&this->socket, &QLocalSocket::connected, this, &IpcClient::connected);
|
||||||
|
QObject::connect(&this->socket, &QLocalSocket::disconnected, this, &IpcClient::disconnected);
|
||||||
|
QObject::connect(&this->socket, &QLocalSocket::errorOccurred, this, &IpcClient::onError);
|
||||||
|
|
||||||
|
this->socket.connectToServer(path);
|
||||||
|
this->stream.setDevice(&this->socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcClient::isConnected() const { return this->socket.isValid(); }
|
||||||
|
|
||||||
|
void IpcClient::waitForConnected() { this->socket.waitForConnected(); }
|
||||||
|
void IpcClient::waitForDisconnected() { this->socket.waitForDisconnected(); }
|
||||||
|
|
||||||
|
void IpcClient::kill() {
|
||||||
|
qCDebug(logIpc) << "Sending kill command...";
|
||||||
|
this->stream << IpcCommand::Kill;
|
||||||
|
this->socket.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcClient::onError(QLocalSocket::LocalSocketError error) {
|
||||||
|
qCCritical(logIpc) << "Socket Error" << error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcClient::connect(const QString& id, const std::function<void(IpcClient& client)>& callback) {
|
||||||
|
auto path = QsPaths::ipcPath(id);
|
||||||
|
auto client = IpcClient(path);
|
||||||
|
qCDebug(logIpc) << "Connecting to instance" << id << "at" << path;
|
||||||
|
|
||||||
|
client.waitForConnected();
|
||||||
|
if (!client.isConnected()) return false;
|
||||||
|
qCDebug(logIpc) << "Connected.";
|
||||||
|
|
||||||
|
callback(client);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace qs::ipc
|
74
src/core/ipc.hpp
Normal file
74
src/core/ipc.hpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <qlocalserver.h>
|
||||||
|
#include <qlocalsocket.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
namespace qs::ipc {
|
||||||
|
|
||||||
|
enum class IpcCommand : quint8 {
|
||||||
|
Unknown = 0,
|
||||||
|
Kill,
|
||||||
|
};
|
||||||
|
|
||||||
|
class IpcServer: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IpcServer(const QString& path);
|
||||||
|
|
||||||
|
static void start();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onNewConnection();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLocalServer server;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IpcServerConnection: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IpcServerConnection(QLocalSocket* socket, IpcServer* server);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onDisconnected();
|
||||||
|
void onReadyRead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLocalSocket* socket;
|
||||||
|
QDataStream stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IpcClient: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IpcClient(const QString& path);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isConnected() const;
|
||||||
|
void waitForConnected();
|
||||||
|
void waitForDisconnected();
|
||||||
|
|
||||||
|
void kill();
|
||||||
|
|
||||||
|
[[nodiscard]] static bool
|
||||||
|
connect(const QString& id, const std::function<void(IpcClient& client)>& callback);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
static void onError(QLocalSocket::LocalSocketError error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLocalSocket socket;
|
||||||
|
QDataStream stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::ipc
|
|
@ -24,11 +24,13 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
|
|
||||||
#include "crashinfo.hpp"
|
#include "instanceinfo.hpp"
|
||||||
#include "logging_p.hpp"
|
#include "logging_p.hpp"
|
||||||
#include "logging_qtprivate.cpp" // NOLINT
|
#include "logging_qtprivate.cpp" // NOLINT
|
||||||
#include "paths.hpp"
|
#include "paths.hpp"
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(logBare, "quickshell.bare");
|
||||||
|
|
||||||
namespace qs::log {
|
namespace qs::log {
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(logLogging, "quickshell.logging", QtWarningMsg);
|
Q_LOGGING_CATEGORY(logLogging, "quickshell.logging", QtWarningMsg);
|
||||||
|
@ -53,37 +55,41 @@ void LogMessage::formatMessage(
|
||||||
stream << msg.time.toString("yyyy-MM-dd hh:mm:ss.zzz");
|
stream << msg.time.toString("yyyy-MM-dd hh:mm:ss.zzz");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (color) {
|
if (msg.category == "quickshell.bare") {
|
||||||
switch (msg.type) {
|
stream << msg.body;
|
||||||
case QtDebugMsg: stream << "\033[34m DEBUG"; break;
|
|
||||||
case QtInfoMsg: stream << "\033[32m INFO"; break;
|
|
||||||
case QtWarningMsg: stream << "\033[33m WARN"; break;
|
|
||||||
case QtCriticalMsg: stream << "\033[31m ERROR"; break;
|
|
||||||
case QtFatalMsg: stream << "\033[31m FATAL"; break;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
switch (msg.type) {
|
if (color) {
|
||||||
case QtDebugMsg: stream << " DEBUG"; break;
|
switch (msg.type) {
|
||||||
case QtInfoMsg: stream << " INFO"; break;
|
case QtDebugMsg: stream << "\033[34m DEBUG"; break;
|
||||||
case QtWarningMsg: stream << " WARN"; break;
|
case QtInfoMsg: stream << "\033[32m INFO"; break;
|
||||||
case QtCriticalMsg: stream << " ERROR"; break;
|
case QtWarningMsg: stream << "\033[33m WARN"; break;
|
||||||
case QtFatalMsg: stream << " FATAL"; break;
|
case QtCriticalMsg: stream << "\033[31m ERROR"; break;
|
||||||
|
case QtFatalMsg: stream << "\033[31m FATAL"; break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (msg.type) {
|
||||||
|
case QtDebugMsg: stream << " DEBUG"; break;
|
||||||
|
case QtInfoMsg: stream << " INFO"; break;
|
||||||
|
case QtWarningMsg: stream << " WARN"; break;
|
||||||
|
case QtCriticalMsg: stream << " ERROR"; break;
|
||||||
|
case QtFatalMsg: stream << " FATAL"; break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto isDefault = msg.category == "default";
|
||||||
|
|
||||||
|
if (color && !isDefault && msg.type != QtFatalMsg) stream << "\033[97m";
|
||||||
|
|
||||||
|
if (!isDefault) {
|
||||||
|
stream << ' ' << msg.category;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color && msg.type != QtFatalMsg) stream << "\033[0m";
|
||||||
|
|
||||||
|
stream << ": " << msg.body;
|
||||||
|
|
||||||
|
if (color && msg.type == QtFatalMsg) stream << "\033[0m";
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto isDefault = msg.category == "default";
|
|
||||||
|
|
||||||
if (color && !isDefault && msg.type != QtFatalMsg) stream << "\033[97m";
|
|
||||||
|
|
||||||
if (!isDefault) {
|
|
||||||
stream << ' ' << msg.category;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (color && msg.type != QtFatalMsg) stream << "\033[0m";
|
|
||||||
|
|
||||||
stream << ": " << msg.body;
|
|
||||||
|
|
||||||
if (color && msg.type == QtFatalMsg) stream << "\033[0m";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CategoryFilter::shouldDisplay(QtMsgType type) const {
|
bool CategoryFilter::shouldDisplay(QtMsgType type) const {
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(logBare);
|
||||||
|
|
||||||
namespace qs::log {
|
namespace qs::log {
|
||||||
|
|
||||||
struct LogMessage {
|
struct LogMessage {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -8,6 +10,7 @@
|
||||||
#include <CLI/Error.hpp>
|
#include <CLI/Error.hpp>
|
||||||
#include <CLI/Validators.hpp>
|
#include <CLI/Validators.hpp>
|
||||||
#include <qapplication.h>
|
#include <qapplication.h>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
#include <qcoreapplication.h>
|
#include <qcoreapplication.h>
|
||||||
#include <qcryptographichash.h>
|
#include <qcryptographichash.h>
|
||||||
#include <qdatastream.h>
|
#include <qdatastream.h>
|
||||||
|
@ -17,8 +20,12 @@
|
||||||
#include <qguiapplication.h>
|
#include <qguiapplication.h>
|
||||||
#include <qhash.h>
|
#include <qhash.h>
|
||||||
#include <qicon.h>
|
#include <qicon.h>
|
||||||
|
#include <qjsonarray.h>
|
||||||
|
#include <qjsondocument.h>
|
||||||
|
#include <qjsonobject.h>
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qqmldebug.h>
|
#include <qqmldebug.h>
|
||||||
#include <qquickwindow.h>
|
#include <qquickwindow.h>
|
||||||
|
@ -27,10 +34,13 @@
|
||||||
#include <qtenvironmentvariables.h>
|
#include <qtenvironmentvariables.h>
|
||||||
#include <qtextstream.h>
|
#include <qtextstream.h>
|
||||||
#include <qtpreprocessorsupport.h>
|
#include <qtpreprocessorsupport.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "build.hpp"
|
#include "build.hpp"
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include "crashinfo.hpp"
|
#include "instanceinfo.hpp"
|
||||||
|
#include "ipc.hpp"
|
||||||
#include "logging.hpp"
|
#include "logging.hpp"
|
||||||
#include "paths.hpp"
|
#include "paths.hpp"
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
|
@ -40,6 +50,8 @@
|
||||||
#include "../crash/main.hpp"
|
#include "../crash/main.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using qs::ipc::IpcClient;
|
||||||
|
|
||||||
struct CommandInfo {
|
struct CommandInfo {
|
||||||
QString configPath;
|
QString configPath;
|
||||||
QString manifestPath;
|
QString manifestPath;
|
||||||
|
@ -52,6 +64,8 @@ struct CommandInfo {
|
||||||
bool& sparseLogsOnly;
|
bool& sparseLogsOnly;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QString commandConfigPath(QString path, QString manifest, QString config, bool printInfo);
|
||||||
|
|
||||||
void processCommand(int argc, char** argv, CommandInfo& info) {
|
void processCommand(int argc, char** argv, CommandInfo& info) {
|
||||||
QCoreApplication::setApplicationName("quickshell");
|
QCoreApplication::setApplicationName("quickshell");
|
||||||
|
|
||||||
|
@ -162,12 +176,97 @@ void processCommand(int argc, char** argv, CommandInfo& info) {
|
||||||
readLog->add_flag("--no-time", logNoTime, "Do not print timestamps of log messages.");
|
readLog->add_flag("--no-time", logNoTime, "Do not print timestamps of log messages.");
|
||||||
readLog->add_flag("--no-color", info.noColor, "Do not color the log output. (Env:NO_COLOR)");
|
readLog->add_flag("--no-color", info.noColor, "Do not color the log output. (Env:NO_COLOR)");
|
||||||
|
|
||||||
|
/// ---
|
||||||
|
QStringOption instanceId;
|
||||||
|
pid_t instancePid = -1;
|
||||||
|
|
||||||
|
auto sortInstances = [](QVector<InstanceLockInfo>& list) {
|
||||||
|
std::sort(list.begin(), list.end(), [](const InstanceLockInfo& a, const InstanceLockInfo& b) {
|
||||||
|
return a.instance.launchTime < b.instance.launchTime;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
auto selectInstance = [&]() {
|
||||||
|
auto* basePath = QsPaths::instance()->baseRunDir();
|
||||||
|
if (!basePath) exit(-1); // NOLINT
|
||||||
|
|
||||||
|
QString path;
|
||||||
|
InstanceLockInfo instance;
|
||||||
|
|
||||||
|
if (instancePid != -1) {
|
||||||
|
path = QDir(basePath->filePath("by-pid")).filePath(QString::number(instancePid));
|
||||||
|
if (!QsPaths::checkLock(path, &instance)) {
|
||||||
|
qCInfo(logBare) << "No instance found for pid" << instancePid;
|
||||||
|
exit(-1); // NOLINT
|
||||||
|
}
|
||||||
|
} else if (!(*instanceId).isEmpty()) {
|
||||||
|
path = basePath->filePath("by-pid");
|
||||||
|
auto instances = QsPaths::collectInstances(path);
|
||||||
|
|
||||||
|
auto itr =
|
||||||
|
std::remove_if(instances.begin(), instances.end(), [&](const InstanceLockInfo& info) {
|
||||||
|
return !info.instance.instanceId.startsWith(*instanceId);
|
||||||
|
});
|
||||||
|
|
||||||
|
instances.erase(itr, instances.end());
|
||||||
|
|
||||||
|
if (instances.isEmpty()) {
|
||||||
|
qCInfo(logBare) << "No running instances start with" << *instanceId;
|
||||||
|
} else if (instances.length() != 1) {
|
||||||
|
qCInfo(logBare) << "More than one instance starts with" << *instanceId;
|
||||||
|
|
||||||
|
for (auto& instance: instances) {
|
||||||
|
qCInfo(logBare).noquote() << " -" << instance.instance.instanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(-1); // NOLINT
|
||||||
|
} else {
|
||||||
|
instance = instances.value(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto configFilePath =
|
||||||
|
commandConfigPath(info.configPath, info.manifestPath, info.configName, info.printInfo);
|
||||||
|
|
||||||
|
auto pathId =
|
||||||
|
QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||||
|
|
||||||
|
path = QDir(basePath->filePath("by-path")).filePath(pathId);
|
||||||
|
|
||||||
|
auto instances = QsPaths::collectInstances(path);
|
||||||
|
sortInstances(instances);
|
||||||
|
|
||||||
|
if (instances.isEmpty()) {
|
||||||
|
qCInfo(logBare) << "No running instances for" << configFilePath;
|
||||||
|
exit(-1); // NOLINT
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = instances.value(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* instances =
|
||||||
|
app.add_subcommand("instances", "List running quickshell instances.")->fallthrough();
|
||||||
|
|
||||||
|
auto* allInstances =
|
||||||
|
instances->add_flag("-a,--all", "List all instances instead of just the current config.");
|
||||||
|
|
||||||
|
auto* instancesJson = instances->add_flag("-j,--json", "Output the list as a json.");
|
||||||
|
|
||||||
|
auto* kill = app.add_subcommand("kill", "Kill an instance.")->fallthrough();
|
||||||
|
auto* kInstance = app.add_option("-i,--instance", instanceId, "The instance id to kill.");
|
||||||
|
app.add_option("-p,--pid", instancePid, "The process id to kill.")->excludes(kInstance);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
app.parse(argc, argv);
|
app.parse(argc, argv);
|
||||||
} catch (const CLI::ParseError& e) {
|
} catch (const CLI::ParseError& e) {
|
||||||
exit(app.exit(e)); // NOLINT
|
exit(app.exit(e)); // NOLINT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Start log manager - has to happen with an active event loop or offthread can't be started.
|
||||||
|
LogManager::init(!info.noColor, info.sparseLogsOnly);
|
||||||
|
|
||||||
if (*printVersion) {
|
if (*printVersion) {
|
||||||
std::cout << "quickshell pre-release, revision: " << GIT_REVISION << std::endl;
|
std::cout << "quickshell pre-release, revision: " << GIT_REVISION << std::endl;
|
||||||
exit(0); // NOLINT
|
exit(0); // NOLINT
|
||||||
|
@ -183,6 +282,86 @@ void processCommand(int argc, char** argv, CommandInfo& info) {
|
||||||
exit( // NOLINT
|
exit( // NOLINT
|
||||||
qs::log::readEncodedLogs(&file, !logNoTime, *logFilter) ? 0 : -1
|
qs::log::readEncodedLogs(&file, !logNoTime, *logFilter) ? 0 : -1
|
||||||
);
|
);
|
||||||
|
} else if (*instances) {
|
||||||
|
auto* basePath = QsPaths::instance()->baseRunDir();
|
||||||
|
if (!basePath) exit(-1); // NOLINT
|
||||||
|
|
||||||
|
QString path;
|
||||||
|
QString configFilePath;
|
||||||
|
if (*allInstances) {
|
||||||
|
path = basePath->filePath("by-pid");
|
||||||
|
} else {
|
||||||
|
configFilePath =
|
||||||
|
commandConfigPath(info.configPath, info.manifestPath, info.configName, info.printInfo);
|
||||||
|
|
||||||
|
auto pathId =
|
||||||
|
QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||||
|
|
||||||
|
path = QDir(basePath->filePath("by-path")).filePath(pathId);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto instances = QsPaths::collectInstances(path);
|
||||||
|
|
||||||
|
if (instances.isEmpty()) {
|
||||||
|
if (*allInstances) {
|
||||||
|
qCInfo(logBare) << "No running instances.";
|
||||||
|
} else {
|
||||||
|
qCInfo(logBare) << "No running instances for" << configFilePath;
|
||||||
|
qCInfo(logBare) << "Use --all to list all instances.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sortInstances(instances);
|
||||||
|
|
||||||
|
if (*instancesJson) {
|
||||||
|
auto array = QJsonArray();
|
||||||
|
|
||||||
|
for (auto& instance: instances) {
|
||||||
|
auto json = QJsonObject();
|
||||||
|
|
||||||
|
json["id"] = instance.instance.instanceId;
|
||||||
|
json["pid"] = instance.pid;
|
||||||
|
json["shell_id"] = instance.instance.shellId;
|
||||||
|
json["config_path"] = instance.instance.configPath;
|
||||||
|
json["launch_time"] = instance.instance.launchTime.toString(Qt::ISODate);
|
||||||
|
|
||||||
|
array.push_back(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto document = QJsonDocument(array);
|
||||||
|
QTextStream(stdout) << document.toJson(QJsonDocument::Indented);
|
||||||
|
} else {
|
||||||
|
for (auto& instance: instances) {
|
||||||
|
auto launchTimeStr = instance.instance.launchTime.toString("yyyy-MM-dd hh:mm:ss");
|
||||||
|
|
||||||
|
auto runSeconds = instance.instance.launchTime.secsTo(QDateTime::currentDateTime());
|
||||||
|
auto remSeconds = runSeconds % 60;
|
||||||
|
auto runMinutes = (runSeconds - remSeconds) / 60;
|
||||||
|
auto remMinutes = runMinutes % 60;
|
||||||
|
auto runHours = (runMinutes - remMinutes) / 60;
|
||||||
|
auto runtimeStr = QString("%1 hours, %2 minutes, %3 seconds")
|
||||||
|
.arg(runHours)
|
||||||
|
.arg(remMinutes)
|
||||||
|
.arg(remSeconds);
|
||||||
|
|
||||||
|
qCInfo(logBare).noquote().nospace()
|
||||||
|
<< "Instance " << instance.instance.instanceId << ":\n"
|
||||||
|
<< " Process ID: " << instance.pid << '\n'
|
||||||
|
<< " Shell ID: " << instance.instance.shellId << '\n'
|
||||||
|
<< " Config path: " << instance.instance.configPath << '\n'
|
||||||
|
<< " Launch time: " << launchTimeStr << " (running for " << runtimeStr << ")\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit(0); // NOLINT
|
||||||
|
} else if (*kill) {
|
||||||
|
auto instance = selectInstance();
|
||||||
|
|
||||||
|
auto r = IpcClient::connect(instance.instance.instanceId, [&](IpcClient& client) {
|
||||||
|
client.kill();
|
||||||
|
qCInfo(logBare).noquote() << "Killed" << instance.instance.instanceId;
|
||||||
|
});
|
||||||
|
|
||||||
|
exit(r ? 0 : -1); // NOLINT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,6 +552,26 @@ foundp:;
|
||||||
return configFilePath;
|
return configFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
QString base36Encode(T number) {
|
||||||
|
const QString digits = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
do {
|
||||||
|
result.prepend(digits[number % 36]);
|
||||||
|
number /= 36;
|
||||||
|
} while (number > 0);
|
||||||
|
|
||||||
|
for (auto i = 0; i < result.length() / 2; i++) {
|
||||||
|
auto opposite = result.length() - i - 1;
|
||||||
|
auto c = result.at(i);
|
||||||
|
result[i] = result.at(opposite);
|
||||||
|
result[opposite] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int qs_main(int argc, char** argv) {
|
int qs_main(int argc, char** argv) {
|
||||||
#if CRASH_REPORTER
|
#if CRASH_REPORTER
|
||||||
qsCheckCrash(argc, argv);
|
qsCheckCrash(argc, argv);
|
||||||
|
@ -385,6 +584,7 @@ int qs_main(int argc, char** argv) {
|
||||||
QString configFilePath;
|
QString configFilePath;
|
||||||
QString initialWorkdir;
|
QString initialWorkdir;
|
||||||
QString shellId;
|
QString shellId;
|
||||||
|
QString pathId;
|
||||||
|
|
||||||
int debugPort = -1;
|
int debugPort = -1;
|
||||||
bool waitForDebug = false;
|
bool waitForDebug = false;
|
||||||
|
@ -411,11 +611,11 @@ int qs_main(int argc, char** argv) {
|
||||||
file.seek(0);
|
file.seek(0);
|
||||||
|
|
||||||
auto ds = QDataStream(&file);
|
auto ds = QDataStream(&file);
|
||||||
InstanceInfo info;
|
RelaunchInfo info;
|
||||||
ds >> info;
|
ds >> info;
|
||||||
|
|
||||||
configFilePath = info.configPath;
|
configFilePath = info.instance.configPath;
|
||||||
initialWorkdir = info.initialWorkdir;
|
initialWorkdir = info.instance.initialWorkdir;
|
||||||
noColor = info.noColor;
|
noColor = info.noColor;
|
||||||
sparseLogsOnly = info.sparseLogsOnly;
|
sparseLogsOnly = info.sparseLogsOnly;
|
||||||
|
|
||||||
|
@ -426,9 +626,9 @@ int qs_main(int argc, char** argv) {
|
||||||
<< " (Coredumps will be available under that pid.)";
|
<< " (Coredumps will be available under that pid.)";
|
||||||
|
|
||||||
qCritical() << "Further crash information is stored under"
|
qCritical() << "Further crash information is stored under"
|
||||||
<< QsPaths::crashDir(info.shellId, info.launchTime).path();
|
<< QsPaths::crashDir(info.instance.instanceId).path();
|
||||||
|
|
||||||
if (info.launchTime.msecsTo(QDateTime::currentDateTime()) < 10000) {
|
if (info.instance.launchTime.msecsTo(QDateTime::currentDateTime()) < 10000) {
|
||||||
qCritical() << "Quickshell crashed within 10 seconds of launching. Not restarting to avoid "
|
qCritical() << "Quickshell crashed within 10 seconds of launching. Not restarting to avoid "
|
||||||
"a crash loop.";
|
"a crash loop.";
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -452,9 +652,6 @@ int qs_main(int argc, char** argv) {
|
||||||
|
|
||||||
processCommand(argc, argv, command);
|
processCommand(argc, argv, command);
|
||||||
|
|
||||||
// Start log manager - has to happen with an active event loop or offthread can't be started.
|
|
||||||
LogManager::init(!noColor, sparseLogsOnly);
|
|
||||||
|
|
||||||
#if CRASH_REPORTER
|
#if CRASH_REPORTER
|
||||||
// Started after log manager for pretty debug logs. Unlikely anything will crash before this point, but
|
// Started after log manager for pretty debug logs. Unlikely anything will crash before this point, but
|
||||||
// this can be moved if it happens.
|
// this can be moved if it happens.
|
||||||
|
@ -469,7 +666,8 @@ int qs_main(int argc, char** argv) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
shellId = QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
|
pathId = QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||||
|
shellId = pathId;
|
||||||
|
|
||||||
qInfo() << "Config file path:" << configFilePath;
|
qInfo() << "Config file path:" << configFilePath;
|
||||||
|
|
||||||
|
@ -521,12 +719,18 @@ int qs_main(int argc, char** argv) {
|
||||||
|
|
||||||
if (printInfo) return 0;
|
if (printInfo) return 0;
|
||||||
|
|
||||||
#if CRASH_REPORTER
|
auto launchTime = qs::Common::LAUNCH_TIME.toSecsSinceEpoch();
|
||||||
crashHandler.setInstanceInfo(InstanceInfo {
|
InstanceInfo::CURRENT = InstanceInfo {
|
||||||
|
.instanceId = base36Encode(getpid()) + base36Encode(launchTime),
|
||||||
.configPath = configFilePath,
|
.configPath = configFilePath,
|
||||||
.shellId = shellId,
|
.shellId = shellId,
|
||||||
.initialWorkdir = initialWorkdir,
|
.initialWorkdir = initialWorkdir,
|
||||||
.launchTime = qs::Common::LAUNCH_TIME,
|
.launchTime = qs::Common::LAUNCH_TIME,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if CRASH_REPORTER
|
||||||
|
crashHandler.setInstanceInfo(RelaunchInfo {
|
||||||
|
.instance = InstanceInfo::CURRENT,
|
||||||
.noColor = noColor,
|
.noColor = noColor,
|
||||||
.sparseLogsOnly = sparseLogsOnly,
|
.sparseLogsOnly = sparseLogsOnly,
|
||||||
});
|
});
|
||||||
|
@ -536,8 +740,9 @@ int qs_main(int argc, char** argv) {
|
||||||
qputenv(var.toUtf8(), val.toUtf8());
|
qputenv(var.toUtf8(), val.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
QsPaths::init(shellId);
|
QsPaths::init(shellId, pathId);
|
||||||
QsPaths::instance()->linkPidRunDir();
|
QsPaths::instance()->linkRunDir();
|
||||||
|
QsPaths::instance()->linkPathDir();
|
||||||
|
|
||||||
if (auto* cacheDir = QsPaths::instance()->cacheDir()) {
|
if (auto* cacheDir = QsPaths::instance()->cacheDir()) {
|
||||||
auto qmlCacheDir = cacheDir->filePath("qml-engine-cache");
|
auto qmlCacheDir = cacheDir->filePath("qml-engine-cache");
|
||||||
|
@ -617,6 +822,9 @@ int qs_main(int argc, char** argv) {
|
||||||
QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering);
|
QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qs::ipc::IpcServer::start();
|
||||||
|
QsPaths::instance()->createLock();
|
||||||
|
|
||||||
auto root = RootWrapper(configFilePath, shellId);
|
auto root = RootWrapper(configFilePath, shellId);
|
||||||
QGuiApplication::setQuitOnLastWindowClosed(false);
|
QGuiApplication::setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
#include "paths.hpp"
|
#include "paths.hpp"
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <cstdio>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <qdatetime.h>
|
#include <fcntl.h>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qdatastream.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
#include <qtenvironmentvariables.h>
|
#include <qtenvironmentvariables.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "instanceinfo.hpp"
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(logPaths, "quickshell.paths", QtWarningMsg);
|
Q_LOGGING_CATEGORY(logPaths, "quickshell.paths", QtWarningMsg);
|
||||||
|
|
||||||
|
@ -19,17 +22,27 @@ QsPaths* QsPaths::instance() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QsPaths::init(QString shellId) { QsPaths::instance()->shellId = std::move(shellId); }
|
void QsPaths::init(QString shellId, QString pathId) {
|
||||||
|
auto* instance = QsPaths::instance();
|
||||||
|
instance->shellId = std::move(shellId);
|
||||||
|
instance->pathId = std::move(pathId);
|
||||||
|
}
|
||||||
|
|
||||||
QDir QsPaths::crashDir(const QString& shellId, const QDateTime& launchTime) {
|
QDir QsPaths::crashDir(const QString& id) {
|
||||||
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
dir = QDir(dir.filePath("crashes"));
|
dir = QDir(dir.filePath("crashes"));
|
||||||
dir = QDir(dir.filePath(shellId));
|
dir = QDir(dir.filePath(id));
|
||||||
dir = QDir(dir.filePath(QString("run-%1").arg(launchTime.toMSecsSinceEpoch())));
|
|
||||||
|
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QsPaths::ipcPath(const QString& id) {
|
||||||
|
auto ipcPath = QsPaths::instance()->baseRunDir()->filePath("by-id");
|
||||||
|
ipcPath = QDir(ipcPath).filePath(id);
|
||||||
|
ipcPath = QDir(ipcPath).filePath("ipc.sock");
|
||||||
|
return ipcPath;
|
||||||
|
}
|
||||||
|
|
||||||
QDir* QsPaths::cacheDir() {
|
QDir* QsPaths::cacheDir() {
|
||||||
if (this->cacheState == DirState::Unknown) {
|
if (this->cacheState == DirState::Unknown) {
|
||||||
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
@ -77,42 +90,45 @@ QDir* QsPaths::baseRunDir() {
|
||||||
else return &this->mBaseRunDir;
|
else return &this->mBaseRunDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir* QsPaths::runDir() {
|
QDir* QsPaths::shellRunDir() {
|
||||||
if (this->runState == DirState::Unknown) {
|
if (this->shellRunState == DirState::Unknown) {
|
||||||
if (auto* baseRunDir = this->baseRunDir()) {
|
if (auto* baseRunDir = this->baseRunDir()) {
|
||||||
this->mRunDir = QDir(baseRunDir->filePath(this->shellId));
|
this->mShellRunDir = QDir(baseRunDir->filePath("by-shell"));
|
||||||
|
this->mShellRunDir = QDir(this->mShellRunDir.filePath(this->shellId));
|
||||||
|
|
||||||
qCDebug(logPaths) << "Initialized runtime path:" << this->mRunDir.path();
|
qCDebug(logPaths) << "Initialized runtime path:" << this->mShellRunDir.path();
|
||||||
|
|
||||||
if (!this->mRunDir.mkpath(".")) {
|
if (!this->mShellRunDir.mkpath(".")) {
|
||||||
qCCritical(logPaths) << "Could not create runtime directory at" << this->mRunDir.path();
|
qCCritical(logPaths) << "Could not create runtime directory at"
|
||||||
this->runState = DirState::Failed;
|
<< this->mShellRunDir.path();
|
||||||
|
this->shellRunState = DirState::Failed;
|
||||||
} else {
|
} else {
|
||||||
this->runState = DirState::Ready;
|
this->shellRunState = DirState::Ready;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCCritical(logPaths) << "Could not create shell runtime path as it was not possible to "
|
qCCritical(logPaths) << "Could not create shell runtime path as it was not possible to "
|
||||||
"create the base runtime path.";
|
"create the base runtime path.";
|
||||||
|
|
||||||
this->runState = DirState::Failed;
|
this->shellRunState = DirState::Failed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->runState == DirState::Failed) return nullptr;
|
if (this->shellRunState == DirState::Failed) return nullptr;
|
||||||
else return &this->mRunDir;
|
else return &this->mShellRunDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir* QsPaths::instanceRunDir() {
|
QDir* QsPaths::instanceRunDir() {
|
||||||
if (this->instanceRunState == DirState::Unknown) {
|
if (this->instanceRunState == DirState::Unknown) {
|
||||||
auto* runtimeDir = this->runDir();
|
auto* runDir = this->baseRunDir();
|
||||||
|
|
||||||
if (!runtimeDir) {
|
if (!runDir) {
|
||||||
qCCritical(logPaths) << "Cannot create instance runtime directory as main runtim directory "
|
qCCritical(logPaths) << "Cannot create instance runtime directory as main runtim directory "
|
||||||
"could not be created.";
|
"could not be created.";
|
||||||
this->instanceRunState = DirState::Failed;
|
this->instanceRunState = DirState::Failed;
|
||||||
} else {
|
} else {
|
||||||
this->mInstanceRunDir =
|
auto byIdDir = QDir(runDir->filePath("by-id"));
|
||||||
runtimeDir->filePath(QString("run-%1").arg(qs::Common::LAUNCH_TIME.toMSecsSinceEpoch()));
|
|
||||||
|
this->mInstanceRunDir = byIdDir.filePath(InstanceInfo::CURRENT.instanceId);
|
||||||
|
|
||||||
qCDebug(logPaths) << "Initialized instance runtime path:" << this->mInstanceRunDir.path();
|
qCDebug(logPaths) << "Initialized instance runtime path:" << this->mInstanceRunDir.path();
|
||||||
|
|
||||||
|
@ -126,34 +142,162 @@ QDir* QsPaths::instanceRunDir() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->runState == DirState::Failed) return nullptr;
|
if (this->shellRunState == DirState::Failed) return nullptr;
|
||||||
else return &this->mInstanceRunDir;
|
else return &this->mInstanceRunDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QsPaths::linkPidRunDir() {
|
void QsPaths::linkRunDir() {
|
||||||
if (auto* runDir = this->instanceRunDir()) {
|
if (auto* runDir = this->instanceRunDir()) {
|
||||||
auto pidDir = QDir(this->baseRunDir()->filePath("by-pid"));
|
auto pidDir = QDir(this->baseRunDir()->filePath("by-pid"));
|
||||||
|
auto* shellDir = this->shellRunDir();
|
||||||
|
|
||||||
|
if (!shellDir) {
|
||||||
|
qCCritical(logPaths
|
||||||
|
) << "Could not create by-id symlink as the shell runtime path could not be created.";
|
||||||
|
} else {
|
||||||
|
auto shellPath = shellDir->filePath(runDir->dirName());
|
||||||
|
|
||||||
|
QFile::remove(shellPath);
|
||||||
|
auto r =
|
||||||
|
symlinkat(runDir->filesystemCanonicalPath().c_str(), 0, shellPath.toStdString().c_str());
|
||||||
|
|
||||||
|
if (r != 0) {
|
||||||
|
qCCritical(logPaths).nospace()
|
||||||
|
<< "Could not create id symlink to " << runDir->path() << " at " << shellPath
|
||||||
|
<< " with error code " << errno << ": " << qt_error_string();
|
||||||
|
} else {
|
||||||
|
qCDebug(logPaths) << "Created shellid symlink" << shellPath << "to instance runtime path"
|
||||||
|
<< runDir->path();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!pidDir.mkpath(".")) {
|
if (!pidDir.mkpath(".")) {
|
||||||
qCCritical(logPaths) << "Could not create PID symlink directory.";
|
qCCritical(logPaths) << "Could not create PID symlink directory.";
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pidPath = pidDir.filePath(QString::number(getpid()));
|
|
||||||
|
|
||||||
QFile::remove(pidPath);
|
|
||||||
auto r = symlinkat(runDir->filesystemCanonicalPath().c_str(), 0, pidPath.toStdString().c_str());
|
|
||||||
|
|
||||||
if (r != 0) {
|
|
||||||
qCCritical(logPaths).nospace()
|
|
||||||
<< "Could not create PID symlink to " << runDir->path() << " at " << pidPath
|
|
||||||
<< " with error code " << errno << ": " << qt_error_string();
|
|
||||||
} else {
|
} else {
|
||||||
qCDebug(logPaths) << "Created PID symlink" << pidPath << "to instance runtime path"
|
auto pidPath = pidDir.filePath(QString::number(getpid()));
|
||||||
<< runDir->path();
|
|
||||||
|
QFile::remove(pidPath);
|
||||||
|
auto r =
|
||||||
|
symlinkat(runDir->filesystemCanonicalPath().c_str(), 0, pidPath.toStdString().c_str());
|
||||||
|
|
||||||
|
if (r != 0) {
|
||||||
|
qCCritical(logPaths).nospace()
|
||||||
|
<< "Could not create PID symlink to " << runDir->path() << " at " << pidPath
|
||||||
|
<< " with error code " << errno << ": " << qt_error_string();
|
||||||
|
} else {
|
||||||
|
qCDebug(logPaths) << "Created PID symlink" << pidPath << "to instance runtime path"
|
||||||
|
<< runDir->path();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCCritical(logPaths) << "Could not create PID symlink to runtime directory, as the runtime "
|
qCCritical(logPaths) << "Could not create PID symlink to runtime directory, as the runtime "
|
||||||
"directory could not be created.";
|
"directory could not be created.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QsPaths::linkPathDir() {
|
||||||
|
if (auto* runDir = this->shellRunDir()) {
|
||||||
|
auto pathDir = QDir(this->baseRunDir()->filePath("by-path"));
|
||||||
|
|
||||||
|
if (!pathDir.mkpath(".")) {
|
||||||
|
qCCritical(logPaths) << "Could not create path symlink directory.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto linkPath = pathDir.filePath(this->pathId);
|
||||||
|
|
||||||
|
QFile::remove(linkPath);
|
||||||
|
auto r =
|
||||||
|
symlinkat(runDir->filesystemCanonicalPath().c_str(), 0, linkPath.toStdString().c_str());
|
||||||
|
|
||||||
|
if (r != 0) {
|
||||||
|
qCCritical(logPaths).nospace()
|
||||||
|
<< "Could not create path symlink to " << runDir->path() << " at " << linkPath
|
||||||
|
<< " with error code " << errno << ": " << qt_error_string();
|
||||||
|
} else {
|
||||||
|
qCDebug(logPaths) << "Created path symlink" << linkPath << "to shell runtime path"
|
||||||
|
<< runDir->path();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCCritical(logPaths) << "Could not create path symlink to shell runtime directory, as the "
|
||||||
|
"shell runtime directory could not be created.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsPaths::createLock() {
|
||||||
|
if (auto* runDir = this->instanceRunDir()) {
|
||||||
|
auto path = runDir->filePath("instance.lock");
|
||||||
|
auto* file = new QFile(path); // leaked
|
||||||
|
|
||||||
|
if (!file->open(QFile::ReadWrite | QFile::Truncate)) {
|
||||||
|
qCCritical(logPaths) << "Could not create instance lock at" << path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lock = flock {
|
||||||
|
.l_type = F_WRLCK,
|
||||||
|
.l_whence = SEEK_SET,
|
||||||
|
.l_start = 0,
|
||||||
|
.l_len = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fcntl(file->handle(), F_SETLK, &lock) != 0) { // NOLINT
|
||||||
|
qCCritical(logPaths).nospace() << "Could not lock instance lock at " << path
|
||||||
|
<< " with error code " << errno << ": " << qt_error_string();
|
||||||
|
} else {
|
||||||
|
auto stream = QDataStream(file);
|
||||||
|
stream << InstanceInfo::CURRENT;
|
||||||
|
file->flush();
|
||||||
|
qCDebug(logPaths) << "Created instance lock at" << path;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCCritical(logPaths
|
||||||
|
) << "Could not create instance lock, as the instance runtime directory could not be created.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QsPaths::checkLock(const QString& path, InstanceLockInfo* info) {
|
||||||
|
auto file = QFile(QDir(path).filePath("instance.lock"));
|
||||||
|
if (!file.open(QFile::ReadOnly)) return false;
|
||||||
|
|
||||||
|
auto lock = flock {
|
||||||
|
.l_type = F_WRLCK,
|
||||||
|
.l_whence = SEEK_SET,
|
||||||
|
.l_start = 0,
|
||||||
|
.l_len = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
fcntl(file.handle(), F_GETLK, &lock); // NOLINT
|
||||||
|
if (lock.l_type == F_UNLCK) return false;
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
info->pid = lock.l_pid;
|
||||||
|
|
||||||
|
auto stream = QDataStream(&file);
|
||||||
|
stream >> info->instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<InstanceLockInfo> QsPaths::collectInstances(const QString& path) {
|
||||||
|
qCDebug(logPaths) << "Collecting instances from" << path;
|
||||||
|
auto instances = QVector<InstanceLockInfo>();
|
||||||
|
auto dir = QDir(path);
|
||||||
|
|
||||||
|
InstanceLockInfo info;
|
||||||
|
for (auto& entry: dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||||
|
auto path = dir.filePath(entry);
|
||||||
|
|
||||||
|
if (QsPaths::checkLock(path, &info)) {
|
||||||
|
qCDebug(logPaths).nospace() << "Found live instance " << info.instance.instanceId << " (pid "
|
||||||
|
<< info.pid << ") at " << path;
|
||||||
|
|
||||||
|
instances.push_back(info);
|
||||||
|
} else {
|
||||||
|
qCDebug(logPaths) << "Skipped dead instance at" << path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
|
@ -2,17 +2,32 @@
|
||||||
#include <qdatetime.h>
|
#include <qdatetime.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
|
|
||||||
|
#include "instanceinfo.hpp"
|
||||||
|
|
||||||
|
struct InstanceLockInfo {
|
||||||
|
pid_t pid = -1;
|
||||||
|
InstanceInfo instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& stream, const InstanceLockInfo& info);
|
||||||
|
QDataStream& operator>>(QDataStream& stream, InstanceLockInfo& info);
|
||||||
|
|
||||||
class QsPaths {
|
class QsPaths {
|
||||||
public:
|
public:
|
||||||
static QsPaths* instance();
|
static QsPaths* instance();
|
||||||
static void init(QString shellId);
|
static void init(QString shellId, QString pathId);
|
||||||
static QDir crashDir(const QString& shellId, const QDateTime& launchTime);
|
static QDir crashDir(const QString& id);
|
||||||
|
static QString ipcPath(const QString& id);
|
||||||
|
static bool checkLock(const QString& path, InstanceLockInfo* info = nullptr);
|
||||||
|
static QVector<InstanceLockInfo> collectInstances(const QString& path);
|
||||||
|
|
||||||
QDir* cacheDir();
|
QDir* cacheDir();
|
||||||
QDir* baseRunDir();
|
QDir* baseRunDir();
|
||||||
QDir* runDir();
|
QDir* shellRunDir();
|
||||||
QDir* instanceRunDir();
|
QDir* instanceRunDir();
|
||||||
void linkPidRunDir();
|
void linkRunDir();
|
||||||
|
void linkPathDir();
|
||||||
|
void createLock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class DirState {
|
enum class DirState {
|
||||||
|
@ -22,12 +37,13 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
QString shellId;
|
QString shellId;
|
||||||
|
QString pathId;
|
||||||
QDir mCacheDir;
|
QDir mCacheDir;
|
||||||
QDir mBaseRunDir;
|
QDir mBaseRunDir;
|
||||||
QDir mRunDir;
|
QDir mShellRunDir;
|
||||||
QDir mInstanceRunDir;
|
QDir mInstanceRunDir;
|
||||||
DirState cacheState = DirState::Unknown;
|
DirState cacheState = DirState::Unknown;
|
||||||
DirState baseRunState = DirState::Unknown;
|
DirState baseRunState = DirState::Unknown;
|
||||||
DirState runState = DirState::Unknown;
|
DirState shellRunState = DirState::Unknown;
|
||||||
DirState instanceRunState = DirState::Unknown;
|
DirState instanceRunState = DirState::Unknown;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../core/crashinfo.hpp"
|
#include "../core/instanceinfo.hpp"
|
||||||
|
|
||||||
extern char** environ; // NOLINT
|
extern char** environ; // NOLINT
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ void CrashHandler::init() {
|
||||||
qCInfo(logCrashHandler) << "Crash handler initialized.";
|
qCInfo(logCrashHandler) << "Crash handler initialized.";
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::setInstanceInfo(const InstanceInfo& info) {
|
void CrashHandler::setInstanceInfo(const RelaunchInfo& info) {
|
||||||
this->d->infoFd = memfd_create("quickshell:instance_info", MFD_CLOEXEC);
|
this->d->infoFd = memfd_create("quickshell:instance_info", MFD_CLOEXEC);
|
||||||
|
|
||||||
if (this->d->infoFd == -1) {
|
if (this->d->infoFd == -1) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <qtclasshelpermacros.h>
|
#include <qtclasshelpermacros.h>
|
||||||
|
|
||||||
#include "../core/crashinfo.hpp"
|
#include "../core/instanceinfo.hpp"
|
||||||
namespace qs::crash {
|
namespace qs::crash {
|
||||||
|
|
||||||
struct CrashHandlerPrivate;
|
struct CrashHandlerPrivate;
|
||||||
|
@ -14,7 +14,7 @@ public:
|
||||||
Q_DISABLE_COPY_MOVE(CrashHandler);
|
Q_DISABLE_COPY_MOVE(CrashHandler);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void setInstanceInfo(const InstanceInfo& info);
|
void setInstanceInfo(const RelaunchInfo& info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CrashHandlerPrivate* d;
|
CrashHandlerPrivate* d;
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include <qapplication.h>
|
#include <qapplication.h>
|
||||||
#include <qconfig.h>
|
#include <qconfig.h>
|
||||||
#include <qdatastream.h>
|
#include <qdatastream.h>
|
||||||
#include <qdatetime.h>
|
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qfile.h>
|
#include <qfile.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
@ -15,7 +14,7 @@
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "../core/crashinfo.hpp"
|
#include "../core/instanceinfo.hpp"
|
||||||
#include "../core/logging.hpp"
|
#include "../core/logging.hpp"
|
||||||
#include "../core/paths.hpp"
|
#include "../core/paths.hpp"
|
||||||
#include "build.hpp"
|
#include "build.hpp"
|
||||||
|
@ -23,14 +22,14 @@
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(logCrashReporter, "quickshell.crashreporter", QtWarningMsg);
|
Q_LOGGING_CATEGORY(logCrashReporter, "quickshell.crashreporter", QtWarningMsg);
|
||||||
|
|
||||||
void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instanceInfo);
|
void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instance);
|
||||||
|
|
||||||
void qsCheckCrash(int argc, char** argv) {
|
void qsCheckCrash(int argc, char** argv) {
|
||||||
auto fd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD");
|
auto fd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD");
|
||||||
if (fd.isEmpty()) return;
|
if (fd.isEmpty()) return;
|
||||||
auto app = QApplication(argc, argv);
|
auto app = QApplication(argc, argv);
|
||||||
|
|
||||||
InstanceInfo instance;
|
RelaunchInfo info;
|
||||||
|
|
||||||
auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt();
|
auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt();
|
||||||
|
|
||||||
|
@ -42,15 +41,15 @@ void qsCheckCrash(int argc, char** argv) {
|
||||||
file.seek(0);
|
file.seek(0);
|
||||||
|
|
||||||
auto ds = QDataStream(&file);
|
auto ds = QDataStream(&file);
|
||||||
ds >> instance;
|
ds >> info;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogManager::init(!instance.noColor, false);
|
LogManager::init(!info.noColor, false);
|
||||||
auto crashDir = QsPaths::crashDir(instance.shellId, instance.launchTime);
|
auto crashDir = QsPaths::crashDir(info.instance.instanceId);
|
||||||
|
|
||||||
qCInfo(logCrashReporter) << "Starting crash reporter...";
|
qCInfo(logCrashReporter) << "Starting crash reporter...";
|
||||||
|
|
||||||
recordCrashInfo(crashDir, instance);
|
recordCrashInfo(crashDir, info.instance);
|
||||||
|
|
||||||
auto gui = CrashReporterGui(crashDir.path(), crashProc);
|
auto gui = CrashReporterGui(crashDir.path(), crashProc);
|
||||||
gui.show();
|
gui.show();
|
||||||
|
@ -125,8 +124,7 @@ void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instance) {
|
||||||
stream << "===== Quickshell Crash =====\n";
|
stream << "===== Quickshell Crash =====\n";
|
||||||
stream << "Git Revision: " << GIT_REVISION << '\n';
|
stream << "Git Revision: " << GIT_REVISION << '\n';
|
||||||
stream << "Crashed process ID: " << crashProc << '\n';
|
stream << "Crashed process ID: " << crashProc << '\n';
|
||||||
stream << "Run ID: " << QString("run-%1").arg(instance.launchTime.toMSecsSinceEpoch())
|
stream << "Run ID: " << instance.instanceId << '\n';
|
||||||
<< '\n';
|
|
||||||
|
|
||||||
stream << "\n===== Shell Information =====\n";
|
stream << "\n===== Shell Information =====\n";
|
||||||
stream << "Shell ID: " << instance.shellId << '\n';
|
stream << "Shell ID: " << instance.shellId << '\n';
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue