diff --git a/src/core/instanceinfo.cpp b/src/core/instanceinfo.cpp index 96097c76..7f0132be 100644 --- a/src/core/instanceinfo.cpp +++ b/src/core/instanceinfo.cpp @@ -3,12 +3,12 @@ #include QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) { - stream << info.instanceId << info.configPath << info.shellId << info.launchTime; + stream << info.instanceId << info.configPath << info.shellId << info.launchTime << info.pid; return stream; } QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) { - stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime; + stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime >> info.pid; return stream; } diff --git a/src/core/instanceinfo.hpp b/src/core/instanceinfo.hpp index f0fc02a0..98ce614f 100644 --- a/src/core/instanceinfo.hpp +++ b/src/core/instanceinfo.hpp @@ -3,12 +3,14 @@ #include #include #include +#include struct InstanceInfo { QString instanceId; QString configPath; QString shellId; QDateTime launchTime; + pid_t pid = -1; static InstanceInfo CURRENT; // NOLINT }; diff --git a/src/core/paths.cpp b/src/core/paths.cpp index 689d99e4..73ce7159 100644 --- a/src/core/paths.cpp +++ b/src/core/paths.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -367,29 +368,30 @@ bool QsPaths::checkLock(const QString& path, InstanceLockInfo* info, bool allowD return true; } -QVector QsPaths::collectInstances(const QString& path, bool fallbackDead) { +QPair, QVector> +QsPaths::collectInstances(const QString& path) { qCDebug(logPaths) << "Collecting instances from" << path; - auto instances = QVector(); + auto liveInstances = QVector(); + auto deadInstances = QVector(); 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, fallbackDead)) { - if (fallbackDead && info.pid != -1) { - fallbackDead = false; - instances.clear(); - } - + if (QsPaths::checkLock(path, &info, true)) { qCDebug(logPaths).nospace() << "Found instance " << info.instance.instanceId << " (pid " << info.pid << ") at " << path; - instances.push_back(info); + if (info.pid == -1) { + deadInstances.push_back(info); + } else { + liveInstances.push_back(info); + } } else { qCDebug(logPaths) << "Skipped potential instance at" << path; } } - return instances; + return qMakePair(liveInstances, deadInstances); } diff --git a/src/core/paths.hpp b/src/core/paths.hpp index baaf9b25..9646ca41 100644 --- a/src/core/paths.hpp +++ b/src/core/paths.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include "instanceinfo.hpp" @@ -22,7 +23,8 @@ public: static QString ipcPath(const QString& id); static bool checkLock(const QString& path, InstanceLockInfo* info = nullptr, bool allowDead = false); - static QVector collectInstances(const QString& path, bool fallbackDead = false); + static QPair, QVector> + collectInstances(const QString& path); QDir* baseRunDir(); QDir* shellRunDir(); diff --git a/src/launch/command.cpp b/src/launch/command.cpp index 1704d9d2..64eb0766 100644 --- a/src/launch/command.cpp +++ b/src/launch/command.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -177,14 +178,33 @@ int selectInstance(CommandState& cmd, InstanceLockInfo* instance, bool deadFallb } } else if (!cmd.instance.id->isEmpty()) { path = basePath->filePath("by-pid"); - auto instances = QsPaths::collectInstances(path, deadFallback); + auto [liveInstances, deadInstances] = QsPaths::collectInstances(path); - instances.removeIf([&](const InstanceLockInfo& info) { + liveInstances.removeIf([&](const InstanceLockInfo& info) { return !info.instance.instanceId.startsWith(*cmd.instance.id); }); + deadInstances.removeIf([&](const InstanceLockInfo& info) { + return !info.instance.instanceId.startsWith(*cmd.instance.id); + }); + + auto instances = liveInstances.isEmpty() && deadFallback ? deadInstances : liveInstances; + if (instances.isEmpty()) { - qCInfo(logBare) << "No running instances start with" << *cmd.instance.id; + if (deadFallback) { + qCInfo(logBare) << "No instances start with" << *cmd.instance.id; + } else { + qCInfo(logBare) << "No running instances start with" << *cmd.instance.id; + + if (!deadInstances.isEmpty()) { + qCInfo(logBare) << "Some dead instances match:"; + + for (auto& instance: deadInstances) { + qCInfo(logBare).noquote() << " -" << instance.instance.instanceId; + } + } + } + return -1; } else if (instances.length() != 1) { qCInfo(logBare) << "More than one instance starts with" << *cmd.instance.id; @@ -208,14 +228,29 @@ int selectInstance(CommandState& cmd, InstanceLockInfo* instance, bool deadFallb path = QDir(basePath->filePath("by-path")).filePath(pathId); - auto instances = QsPaths::collectInstances(path, deadFallback); + auto [liveInstances, deadInstances] = QsPaths::collectInstances(path); + + auto instances = liveInstances; + if (instances.isEmpty() && deadFallback) { + instances = deadInstances; + } + sortInstances( instances, cmd.config.newest || (!instances.empty() && instances.first().pid == -1) ); if (instances.isEmpty()) { - qCInfo(logBare) << "No running instances for" << configFilePath; + if (liveInstances.isEmpty() && deadInstances.length() > 1) { + qCInfo(logBare) << "No running instances for" << configFilePath; + qCInfo(logBare) << "Dead instances:"; + sortInstances(deadInstances, cmd.config.newest); + for (auto& instance: deadInstances) { + qCInfo(logBare).noquote() << " -" << instance.instance.instanceId; + } + } else { + qCInfo(logBare) << "No running instances for" << configFilePath; + } return -1; } @@ -276,7 +311,18 @@ int listInstances(CommandState& cmd) { path = QDir(basePath->filePath("by-path")).filePath(pathId); } - auto instances = QsPaths::collectInstances(path); + auto [liveInstances, deadInstances] = QsPaths::collectInstances(path); + + sortInstances(liveInstances, cmd.config.newest); + + QList instances; + if (cmd.instance.includeDead) { + sortInstances(deadInstances, cmd.config.newest); + instances = std::move(deadInstances); + instances.append(liveInstances); + } else { + instances = std::move(liveInstances); + } if (instances.isEmpty()) { if (cmd.instance.all) { @@ -286,7 +332,6 @@ int listInstances(CommandState& cmd) { qCInfo(logBare) << "Use --all to list all instances."; } } else { - sortInstances(instances, cmd.config.newest); if (cmd.output.json) { auto array = QJsonArray(); @@ -295,7 +340,7 @@ int listInstances(CommandState& cmd) { auto json = QJsonObject(); json["id"] = instance.instance.instanceId; - json["pid"] = instance.pid; + json["pid"] = instance.instance.pid; json["shell_id"] = instance.instance.shellId; json["config_path"] = instance.instance.configPath; json["launch_time"] = instance.instance.launchTime.toString(Qt::ISODate); @@ -319,12 +364,18 @@ int listInstances(CommandState& cmd) { .arg(remMinutes) .arg(remSeconds); + auto isDead = instance.pid == -1; + auto gray = !cmd.log.noColor && isDead; + qCInfo(logBare).noquote().nospace() - << "Instance " << instance.instance.instanceId << ":\n" - << " Process ID: " << instance.pid << '\n' + << (gray ? "\033[90m" : "") << "Instance " << instance.instance.instanceId + << (isDead ? " (dead)" : "") << ":\n" + << " Process ID: " << instance.instance.pid << '\n' << " Shell ID: " << instance.instance.shellId << '\n' << " Config path: " << instance.instance.configPath << '\n' - << " Launch time: " << launchTimeStr << " (running for " << runtimeStr << ")\n"; + << " Launch time: " << launchTimeStr + << (isDead ? "" : " (running for " + runtimeStr + ")") << '\n' + << (gray ? "\033[0m" : ""); } } } diff --git a/src/launch/launch.cpp b/src/launch/launch.cpp index fe28a942..86976672 100644 --- a/src/launch/launch.cpp +++ b/src/launch/launch.cpp @@ -128,6 +128,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio .configPath = args.configPath, .shellId = shellId, .launchTime = qs::Common::LAUNCH_TIME, + .pid = getpid(), }; #if CRASH_REPORTER diff --git a/src/launch/launch_p.hpp b/src/launch/launch_p.hpp index 77808450..7b8fca68 100644 --- a/src/launch/launch_p.hpp +++ b/src/launch/launch_p.hpp @@ -61,6 +61,7 @@ struct CommandState { QStringOption id; pid_t pid = -1; // NOLINT (include) bool all = false; + bool includeDead = false; } instance; struct { diff --git a/src/launch/parsecommand.cpp b/src/launch/parsecommand.cpp index e49ded75..fc16086e 100644 --- a/src/launch/parsecommand.cpp +++ b/src/launch/parsecommand.cpp @@ -163,6 +163,9 @@ int parseCommand(int argc, char** argv, CommandState& state) { sub->add_flag("-j,--json", state.output.json, "Output the list as a json."); + sub->add_flag("--show-dead", state.instance.includeDead) + ->description("Include dead instances in the list."); + addConfigSelection(sub, true)->excludes(all); addLoggingOptions(sub, false, true);