forked from quickshell/quickshell
core/command: add log --follow
This commit is contained in:
parent
c78381f6d0
commit
a82fbf40c2
8 changed files with 262 additions and 63 deletions
|
@ -1,10 +1,14 @@
|
||||||
#include "logging.hpp"
|
#include "logging.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
#include <qbytearrayview.h>
|
#include <qbytearrayview.h>
|
||||||
|
#include <qcoreapplication.h>
|
||||||
#include <qdatetime.h>
|
#include <qdatetime.h>
|
||||||
#include <qendian.h>
|
#include <qendian.h>
|
||||||
|
#include <qfilesystemwatcher.h>
|
||||||
#include <qhash.h>
|
#include <qhash.h>
|
||||||
#include <qhashfunctions.h>
|
#include <qhashfunctions.h>
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
|
@ -356,6 +360,18 @@ void ThreadLogging::initFs() {
|
||||||
delete detailedFile;
|
delete detailedFile;
|
||||||
detailedFile = nullptr;
|
detailedFile = nullptr;
|
||||||
} else {
|
} else {
|
||||||
|
auto lock = flock {
|
||||||
|
.l_type = F_WRLCK,
|
||||||
|
.l_whence = SEEK_SET,
|
||||||
|
.l_start = 0,
|
||||||
|
.l_len = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fcntl(detailedFile->handle(), F_SETLK, &lock) != 0) { // NOLINT
|
||||||
|
qCWarning(logLogging) << "Unable to set lock marker on detailed log file. --follow from "
|
||||||
|
"other instances will not work.";
|
||||||
|
}
|
||||||
|
|
||||||
qCInfo(logLogging) << "Saving detailed logs to" << path;
|
qCInfo(logLogging) << "Saving detailed logs to" << path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,22 +753,13 @@ bool EncodedLogReader::registerCategory() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool readEncodedLogs(QIODevice* device, bool timestamps, int tail, const QString& rulespec) {
|
bool LogReader::initialize() {
|
||||||
QList<QLoggingRule> rules;
|
this->reader.setDevice(this->file);
|
||||||
|
|
||||||
{
|
|
||||||
QLoggingSettingsParser parser;
|
|
||||||
parser.setContent(rulespec);
|
|
||||||
rules = parser.rules();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reader = EncodedLogReader();
|
|
||||||
reader.setDevice(device);
|
|
||||||
|
|
||||||
bool readable = false;
|
bool readable = false;
|
||||||
quint8 logVersion = 0;
|
quint8 logVersion = 0;
|
||||||
quint8 readerVersion = 0;
|
quint8 readerVersion = 0;
|
||||||
if (!reader.readHeader(&readable, &logVersion, &readerVersion)) {
|
if (!this->reader.readHeader(&readable, &logVersion, &readerVersion)) {
|
||||||
qCritical() << "Failed to read log header.";
|
qCritical() << "Failed to read log header.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -765,29 +772,33 @@ bool readEncodedLogs(QIODevice* device, bool timestamps, int tail, const QString
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LogReader::continueReading() {
|
||||||
auto color = LogManager::instance()->colorLogs;
|
auto color = LogManager::instance()->colorLogs;
|
||||||
|
auto tailRing = RingBuffer<LogMessage>(this->remainingTail);
|
||||||
auto filters = QHash<quint16, CategoryFilter>();
|
|
||||||
|
|
||||||
auto tailRing = RingBuffer<LogMessage>(tail);
|
|
||||||
|
|
||||||
LogMessage message;
|
LogMessage message;
|
||||||
auto stream = QTextStream(stdout);
|
auto stream = QTextStream(stdout);
|
||||||
while (reader.read(&message)) {
|
auto readCursor = this->file->pos();
|
||||||
|
while (this->reader.read(&message)) {
|
||||||
|
readCursor = this->file->pos();
|
||||||
|
|
||||||
CategoryFilter filter;
|
CategoryFilter filter;
|
||||||
if (filters.contains(message.readCategoryId)) {
|
if (this->filters.contains(message.readCategoryId)) {
|
||||||
filter = filters.value(message.readCategoryId);
|
filter = this->filters.value(message.readCategoryId);
|
||||||
} else {
|
} else {
|
||||||
for (const auto& rule: rules) {
|
for (const auto& rule: this->rules) {
|
||||||
filter.applyRule(message.category, rule);
|
filter.applyRule(message.category, rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
filters.insert(message.readCategoryId, filter);
|
this->filters.insert(message.readCategoryId, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.shouldDisplay(message.type)) {
|
if (filter.shouldDisplay(message.type)) {
|
||||||
if (tail == 0) {
|
if (this->remainingTail == 0) {
|
||||||
LogMessage::formatMessage(stream, message, color, timestamps);
|
LogMessage::formatMessage(stream, message, color, this->timestamps);
|
||||||
stream << '\n';
|
stream << '\n';
|
||||||
} else {
|
} else {
|
||||||
tailRing.emplace(message);
|
tailRing.emplace(message);
|
||||||
|
@ -795,19 +806,97 @@ bool readEncodedLogs(QIODevice* device, bool timestamps, int tail, const QString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tail != 0) {
|
if (this->remainingTail != 0) {
|
||||||
for (auto i = tailRing.size() - 1; i != -1; i--) {
|
for (auto i = tailRing.size() - 1; i != -1; i--) {
|
||||||
auto& message = tailRing.at(i);
|
auto& message = tailRing.at(i);
|
||||||
LogMessage::formatMessage(stream, message, color, timestamps);
|
LogMessage::formatMessage(stream, message, color, this->timestamps);
|
||||||
stream << '\n';
|
stream << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stream << Qt::flush;
|
stream << Qt::flush;
|
||||||
|
|
||||||
if (!device->atEnd()) {
|
if (this->file->pos() != readCursor) {
|
||||||
qCritical() << "An error occurred parsing the end of this log file.";
|
qCritical() << "An error occurred parsing the end of this log file.";
|
||||||
qCritical() << "Remaining data:" << device->readAll();
|
qCritical() << "Remaining data:" << this->file->readAll();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogFollower::FcntlWaitThread::run() {
|
||||||
|
auto lock = flock {
|
||||||
|
.l_type = F_RDLCK, // won't block other read locks when we take it
|
||||||
|
.l_whence = SEEK_SET,
|
||||||
|
.l_start = 0,
|
||||||
|
.l_len = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto r = fcntl(this->follower->reader->file->handle(), F_SETLKW, &lock); // NOLINT
|
||||||
|
|
||||||
|
if (r != 0) {
|
||||||
|
qCWarning(logLogging).nospace()
|
||||||
|
<< "Failed to wait for write locks to be removed from log file with error code " << errno
|
||||||
|
<< ": " << qt_error_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LogFollower::follow() {
|
||||||
|
QObject::connect(&this->waitThread, &QThread::finished, this, &LogFollower::onFileLocked);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
&this->fileWatcher,
|
||||||
|
&QFileSystemWatcher::fileChanged,
|
||||||
|
this,
|
||||||
|
&LogFollower::onFileChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
this->fileWatcher.addPath(this->path);
|
||||||
|
this->waitThread.start();
|
||||||
|
|
||||||
|
auto r = QCoreApplication::exec();
|
||||||
|
return r == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogFollower::onFileChanged() {
|
||||||
|
if (!this->reader->continueReading()) {
|
||||||
|
QCoreApplication::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogFollower::onFileLocked() {
|
||||||
|
if (!this->reader->continueReading()) {
|
||||||
|
QCoreApplication::exit(1);
|
||||||
|
} else {
|
||||||
|
QCoreApplication::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readEncodedLogs(
|
||||||
|
QFile* file,
|
||||||
|
const QString& path,
|
||||||
|
bool timestamps,
|
||||||
|
int tail,
|
||||||
|
bool follow,
|
||||||
|
const QString& rulespec
|
||||||
|
) {
|
||||||
|
QList<QLoggingRule> rules;
|
||||||
|
|
||||||
|
{
|
||||||
|
QLoggingSettingsParser parser;
|
||||||
|
parser.setContent(rulespec);
|
||||||
|
rules = parser.rules();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reader = LogReader(file, timestamps, tail, rules);
|
||||||
|
|
||||||
|
if (!reader.initialize()) return false;
|
||||||
|
if (!reader.continueReading()) return false;
|
||||||
|
|
||||||
|
if (follow) {
|
||||||
|
auto follower = LogFollower(&reader, path);
|
||||||
|
return follower.follow();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qdatetime.h>
|
#include <qdatetime.h>
|
||||||
|
#include <qfile.h>
|
||||||
#include <qhash.h>
|
#include <qhash.h>
|
||||||
#include <qlatin1stringview.h>
|
#include <qlatin1stringview.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
@ -130,7 +131,14 @@ private:
|
||||||
LoggingThreadProxy threadProxy;
|
LoggingThreadProxy threadProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool readEncodedLogs(QIODevice* device, bool timestamps, int tail, const QString& rulespec);
|
bool readEncodedLogs(
|
||||||
|
QFile* file,
|
||||||
|
const QString& path,
|
||||||
|
bool timestamps,
|
||||||
|
int tail,
|
||||||
|
bool follow,
|
||||||
|
const QString& rulespec
|
||||||
|
);
|
||||||
|
|
||||||
} // namespace qs::log
|
} // namespace qs::log
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <qbytearrayview.h>
|
#include <qbytearrayview.h>
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qfile.h>
|
#include <qfile.h>
|
||||||
|
#include <qfilesystemwatcher.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qthread.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "logging.hpp"
|
#include "logging.hpp"
|
||||||
|
#include "logging_qtprivate.hpp"
|
||||||
#include "ringbuf.hpp"
|
#include "ringbuf.hpp"
|
||||||
|
|
||||||
namespace qs::log {
|
namespace qs::log {
|
||||||
|
@ -120,4 +126,64 @@ private:
|
||||||
EncodedLogWriter detailedWriter;
|
EncodedLogWriter detailedWriter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LogFollower;
|
||||||
|
|
||||||
|
class LogReader {
|
||||||
|
public:
|
||||||
|
explicit LogReader(
|
||||||
|
QFile* file,
|
||||||
|
bool timestamps,
|
||||||
|
int tail,
|
||||||
|
QList<qt_logging_registry::QLoggingRule> rules
|
||||||
|
)
|
||||||
|
: file(file)
|
||||||
|
, timestamps(timestamps)
|
||||||
|
, remainingTail(tail)
|
||||||
|
, rules(std::move(rules)) {}
|
||||||
|
|
||||||
|
bool initialize();
|
||||||
|
bool continueReading();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFile* file;
|
||||||
|
EncodedLogReader reader;
|
||||||
|
bool timestamps;
|
||||||
|
int remainingTail;
|
||||||
|
QHash<quint16, CategoryFilter> filters;
|
||||||
|
QList<qt_logging_registry::QLoggingRule> rules;
|
||||||
|
|
||||||
|
friend class LogFollower;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LogFollower: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LogFollower(LogReader* reader, QString path): reader(reader), path(std::move(path)) {}
|
||||||
|
|
||||||
|
bool follow();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onFileChanged();
|
||||||
|
void onFileLocked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogReader* reader;
|
||||||
|
QString path;
|
||||||
|
QFileSystemWatcher fileWatcher;
|
||||||
|
|
||||||
|
class FcntlWaitThread: public QThread {
|
||||||
|
public:
|
||||||
|
explicit FcntlWaitThread(LogFollower* follower): follower(follower) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogFollower* follower;
|
||||||
|
};
|
||||||
|
|
||||||
|
FcntlWaitThread waitThread {this};
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace qs::log
|
} // namespace qs::log
|
||||||
|
|
|
@ -16,34 +16,13 @@
|
||||||
#include <qstringview.h>
|
#include <qstringview.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "logging_qtprivate.hpp"
|
||||||
|
|
||||||
namespace qs::log {
|
namespace qs::log {
|
||||||
Q_DECLARE_LOGGING_CATEGORY(logLogging);
|
Q_DECLARE_LOGGING_CATEGORY(logLogging);
|
||||||
|
|
||||||
namespace qt_logging_registry {
|
namespace qt_logging_registry {
|
||||||
|
|
||||||
class QLoggingRule {
|
|
||||||
public:
|
|
||||||
QLoggingRule();
|
|
||||||
QLoggingRule(QStringView pattern, bool enabled);
|
|
||||||
[[nodiscard]] int pass(QLatin1StringView categoryName, QtMsgType type) const;
|
|
||||||
|
|
||||||
enum PatternFlag {
|
|
||||||
FullText = 0x1,
|
|
||||||
LeftFilter = 0x2,
|
|
||||||
RightFilter = 0x4,
|
|
||||||
MidFilter = LeftFilter | RightFilter
|
|
||||||
};
|
|
||||||
Q_DECLARE_FLAGS(PatternFlags, PatternFlag)
|
|
||||||
|
|
||||||
QString category;
|
|
||||||
int messageType;
|
|
||||||
PatternFlags flags;
|
|
||||||
bool enabled;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void parse(QStringView pattern);
|
|
||||||
};
|
|
||||||
|
|
||||||
class QLoggingSettingsParser {
|
class QLoggingSettingsParser {
|
||||||
public:
|
public:
|
||||||
void setContent(QStringView content);
|
void setContent(QStringView content);
|
||||||
|
|
44
src/core/logging_qtprivate.hpp
Normal file
44
src/core/logging_qtprivate.hpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// The logging rule parser from qloggingregistry_p.h and qloggingregistry.cpp.
|
||||||
|
|
||||||
|
// Was unable to properly link the functions when directly using the headers (which we depend
|
||||||
|
// on anyway), so below is a slightly stripped down copy. Making the originals link would
|
||||||
|
// be preferable.
|
||||||
|
|
||||||
|
#include <qflags.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <qstringview.h>
|
||||||
|
|
||||||
|
namespace qs::log {
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(logLogging);
|
||||||
|
|
||||||
|
namespace qt_logging_registry {
|
||||||
|
|
||||||
|
class QLoggingRule {
|
||||||
|
public:
|
||||||
|
QLoggingRule();
|
||||||
|
QLoggingRule(QStringView pattern, bool enabled);
|
||||||
|
[[nodiscard]] int pass(QLatin1StringView categoryName, QtMsgType type) const;
|
||||||
|
|
||||||
|
enum PatternFlag {
|
||||||
|
FullText = 0x1,
|
||||||
|
LeftFilter = 0x2,
|
||||||
|
RightFilter = 0x4,
|
||||||
|
MidFilter = LeftFilter | RightFilter
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(PatternFlags, PatternFlag)
|
||||||
|
|
||||||
|
QString category;
|
||||||
|
int messageType;
|
||||||
|
PatternFlags flags;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void parse(QStringView pattern);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qt_logging_registry
|
||||||
|
|
||||||
|
} // namespace qs::log
|
|
@ -96,6 +96,7 @@ struct CommandState {
|
||||||
bool sparse = false;
|
bool sparse = false;
|
||||||
size_t verbosity = 0;
|
size_t verbosity = 0;
|
||||||
int tail = 0;
|
int tail = 0;
|
||||||
|
bool follow = false;
|
||||||
QStringOption rules;
|
QStringOption rules;
|
||||||
QStringOption readoutRules;
|
QStringOption readoutRules;
|
||||||
QStringOption file;
|
QStringOption file;
|
||||||
|
@ -241,13 +242,10 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto* sub = cli.add_subcommand(
|
auto* sub = cli.add_subcommand("log", "Read quickshell logs.\n")
|
||||||
"log",
|
->description("If --file is specified, the given file will be read.\n"
|
||||||
"Read quickshell logs.\n"
|
"If not, the log of the first launched instance matching"
|
||||||
"If --file is specified, the given file will be read.\n"
|
"the instance selection flags will be read.");
|
||||||
"If not, the log of the first launched instance matching"
|
|
||||||
"the instance selection flags will be read."
|
|
||||||
);
|
|
||||||
|
|
||||||
auto* file = sub->add_option("--file", state.log.file, "Log file to read.");
|
auto* file = sub->add_option("--file", state.log.file, "Log file to read.");
|
||||||
|
|
||||||
|
@ -255,6 +253,9 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||||
->description("Maximum number of lines to print, starting from the bottom.")
|
->description("Maximum number of lines to print, starting from the bottom.")
|
||||||
->check(CLI::Range(1, std::numeric_limits<int>::max(), "INT > 0"));
|
->check(CLI::Range(1, std::numeric_limits<int>::max(), "INT > 0"));
|
||||||
|
|
||||||
|
sub->add_flag("--follow", state.log.follow)
|
||||||
|
->description("Keep reading the log until the logging process terminates.");
|
||||||
|
|
||||||
sub->add_option("-r,--rules", state.log.readoutRules, "Log file to read.")
|
sub->add_option("-r,--rules", state.log.readoutRules, "Log file to read.")
|
||||||
->description("Rules to apply to the log being read, in the format of QT_LOGGING_RULES.");
|
->description("Rules to apply to the log being read, in the format of QT_LOGGING_RULES.");
|
||||||
|
|
||||||
|
@ -267,6 +268,7 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||||
|
|
||||||
{
|
{
|
||||||
auto* sub = cli.add_subcommand("list", "List running quickshell instances.");
|
auto* sub = cli.add_subcommand("list", "List running quickshell instances.");
|
||||||
|
|
||||||
auto* all = sub->add_flag("-a,--all", state.instance.all)
|
auto* all = sub->add_flag("-a,--all", state.instance.all)
|
||||||
->description("List all instances.\n"
|
->description("List all instances.\n"
|
||||||
"If unspecified, only instances of"
|
"If unspecified, only instances of"
|
||||||
|
@ -472,7 +474,14 @@ int readLogFile(CommandState& cmd) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return qs::log::readEncodedLogs(&file, cmd.log.timestamp, cmd.log.tail, *cmd.log.readoutRules)
|
return qs::log::readEncodedLogs(
|
||||||
|
&file,
|
||||||
|
path,
|
||||||
|
cmd.log.timestamp,
|
||||||
|
cmd.log.tail,
|
||||||
|
cmd.log.follow,
|
||||||
|
*cmd.log.readoutRules
|
||||||
|
)
|
||||||
? 0
|
? 0
|
||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,14 @@ QDir QsPaths::crashDir(const QString& id) {
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QsPaths::basePath(const QString& id) {
|
||||||
|
auto path = QsPaths::instance()->baseRunDir()->filePath("by-id");
|
||||||
|
path = QDir(path).filePath(id);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
QString QsPaths::ipcPath(const QString& id) {
|
QString QsPaths::ipcPath(const QString& id) {
|
||||||
auto ipcPath = QsPaths::instance()->baseRunDir()->filePath("by-id");
|
return QDir(QsPaths::basePath(id)).filePath("ipc.sock");
|
||||||
ipcPath = QDir(ipcPath).filePath(id);
|
|
||||||
ipcPath = QDir(ipcPath).filePath("ipc.sock");
|
|
||||||
return ipcPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir* QsPaths::cacheDir() {
|
QDir* QsPaths::cacheDir() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ public:
|
||||||
static QsPaths* instance();
|
static QsPaths* instance();
|
||||||
static void init(QString shellId, QString pathId);
|
static void init(QString shellId, QString pathId);
|
||||||
static QDir crashDir(const QString& id);
|
static QDir crashDir(const QString& id);
|
||||||
|
static QString basePath(const QString& id);
|
||||||
static QString ipcPath(const QString& id);
|
static QString ipcPath(const QString& id);
|
||||||
static bool checkLock(const QString& path, InstanceLockInfo* info = nullptr);
|
static bool checkLock(const QString& path, InstanceLockInfo* info = nullptr);
|
||||||
static QVector<InstanceLockInfo> collectInstances(const QString& path);
|
static QVector<InstanceLockInfo> collectInstances(const QString& path);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue