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