diff options
Diffstat (limited to 'src/zen/cmds/up_cmd.cpp')
| -rw-r--r-- | src/zen/cmds/up_cmd.cpp | 162 |
1 files changed, 145 insertions, 17 deletions
diff --git a/src/zen/cmds/up_cmd.cpp b/src/zen/cmds/up_cmd.cpp index 809a41bb6..1f23e6819 100644 --- a/src/zen/cmds/up_cmd.cpp +++ b/src/zen/cmds/up_cmd.cpp @@ -2,6 +2,7 @@ #include "up_cmd.h" +#include <zencore/basicfile.h> #include <zencore/compactbinary.h> #include <zencore/compactbinaryutil.h> #include <zencore/filesystem.h> @@ -12,6 +13,57 @@ namespace zen { +namespace { + + bool TryShutdownByPid(ZenServerState& Instance, uint32_t Pid, const std::filesystem::path& ProgramBaseDir, bool ForceTerminate) + { + Instance.Sweep(); + + uint16_t DesiredPort = 0; + Instance.Snapshot([&](const ZenServerState::ZenServerEntry& Entry) { + if (Entry.Pid.load() == Pid) + { + DesiredPort = Entry.DesiredListenPort.load(); + } + }); + + ZenServerState::ZenServerEntry* Entry = (DesiredPort != 0) ? Instance.Lookup(DesiredPort) : nullptr; + if (Entry && Entry->Pid.load() != Pid) + { + Entry = nullptr; + } + + if (Entry) + { + if (ShutdownZenServer(ConsoleLog(), Instance, Entry, ProgramBaseDir)) + { + return true; + } + } + + std::error_code Ec; + ProcessHandle Proc; + Proc.Initialize(int(Pid), Ec); + if (!Ec && Proc.IsValid() && !Proc.IsRunning()) + { + return true; + } + + if (ForceTerminate && !Ec && Proc.IsValid() && Proc.IsRunning()) + { + ZEN_CONSOLE_WARN("Hard terminating zen process with pid ({})", Pid); + if (Proc.Terminate(0)) + { + ZEN_CONSOLE("Terminate complete"); + return true; + } + } + + return false; + } + +} // namespace + UpCommand::UpCommand() { m_Options.add_option("", "p", "port", "Host port", cxxopts::value(m_Port)->default_value("0"), "<hostport>"); @@ -40,12 +92,19 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) throw OptionParseException("'--show-console' conflicts with '--show-log'", m_Options.help()); } + if (m_ProgramBaseDir.empty()) + { + m_ProgramBaseDir = GetRunningExecutablePath().parent_path(); + } + MakeSafeAbsolutePathInPlace(m_ProgramBaseDir); + std::optional<int> StartResult = StartupZenServer(ConsoleLog(), - {.ProgramBaseDir = m_ProgramBaseDir, - .Port = m_Port, - .OpenConsole = m_ShowConsole, - .ShowLog = m_ShowLog, - .ExtraArgs = GlobalOptions.PassthroughCommandLine}); + {.ProgramBaseDir = m_ProgramBaseDir, + .Port = m_Port, + .OpenConsole = m_ShowConsole, + .ShowLog = m_ShowLog, + .ExtraArgs = GlobalOptions.PassthroughCommandLine, + .EnableExecutionHistory = GlobalOptions.EnableExecutionHistory}); if (!StartResult.has_value()) { ZEN_CONSOLE("Zen server already running"); @@ -80,6 +139,11 @@ AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return; } + if (!m_DataDir.empty()) + { + MakeSafeAbsolutePathInPlace(m_DataDir); + } + ZenServerState Instance; Instance.Initialize(); Instance.Sweep(); @@ -87,9 +151,9 @@ AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!m_DataDir.empty()) { - if (!IsFile(m_DataDir / ".lock")) + if (!LockFile::IsHeldLive(m_DataDir / ".lock", /*AttemptCleanup*/ false)) { - throw std::runtime_error(fmt::format("Lock file does not exist in directory '{}'", m_DataDir)); + throw std::runtime_error(fmt::format("No live zen server holding lock file in directory '{}'", m_DataDir)); } CbValidateError ValidateResult = CbValidateError::None; if (CbObject LockFileObject = @@ -134,6 +198,13 @@ DownCommand::DownCommand() m_Options.add_option("", "f", "force", "Force terminate if graceful shutdown fails", cxxopts::value(m_ForceTerminate), "<force>"); m_Options.add_option("", "b", "base-dir", "Parent folder of server executable", cxxopts::value(m_ProgramBaseDir), "<directory>"); m_Options.add_option("", "", "data-dir", "Path to data directory to inspect for running server", cxxopts::value(m_DataDir), "<file>"); + m_Options.add_option("", "", "pid", "Shut down zen server process by PID", cxxopts::value(m_Pid)->default_value("0"), "<pid>"); + m_Options.add_option("", + "", + "executable", + "Shut down all zen server processes matching executable path", + cxxopts::value(m_ExecutablePath), + "<path>"); } DownCommand::~DownCommand() = default; @@ -148,16 +219,77 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return; } + const bool HasPid = m_Pid != 0; + const bool HasExecutable = !m_ExecutablePath.empty(); + const bool HasDataDir = !m_DataDir.empty(); + const int SelectorCount = int(m_All) + int(HasPid) + int(HasExecutable) + int(HasDataDir); + if (SelectorCount > 1) + { + throw OptionParseException("--all, --pid, --executable, and --data-dir are mutually exclusive", m_Options.help()); + } + if (m_ProgramBaseDir.empty()) { std::filesystem::path ExePath = GetRunningExecutablePath(); m_ProgramBaseDir = ExePath.parent_path(); } + MakeSafeAbsolutePathInPlace(m_ProgramBaseDir); + if (!m_DataDir.empty()) + { + MakeSafeAbsolutePathInPlace(m_DataDir); + } + if (!m_ExecutablePath.empty() && m_ExecutablePath.has_parent_path()) + { + MakeSafeAbsolutePathInPlace(m_ExecutablePath); + } // Discover executing instances ZenServerState Instance; Instance.Initialize(); + if (HasPid) + { + if (!TryShutdownByPid(Instance, m_Pid, m_ProgramBaseDir, m_ForceTerminate)) + { + throw std::runtime_error(fmt::format("Failed to shut down zen process with pid {}, use --force to hard terminate", m_Pid)); + } + ZEN_CONSOLE("Zen server with pid {} is down", m_Pid); + return; + } + + if (HasExecutable) + { + int ShutdownCount = 0; + while (true) + { + ProcessHandle Proc; + std::error_code Ec = FindProcess(m_ExecutablePath, Proc, /*IncludeSelf*/ false); + if (Ec) + { + throw std::system_error(Ec, fmt::format("FindProcess failed for '{}'", m_ExecutablePath)); + } + if (!Proc.IsValid()) + { + break; + } + const uint32_t Pid = uint32_t(Proc.Pid()); + if (!TryShutdownByPid(Instance, Pid, m_ProgramBaseDir, m_ForceTerminate)) + { + throw std::runtime_error(fmt::format("Failed to shut down zen process with pid {}, use --force to hard terminate", Pid)); + } + ++ShutdownCount; + } + if (ShutdownCount == 0) + { + ZEN_CONSOLE("No zen server processes matching executable '{}'", m_ExecutablePath); + } + else + { + ZEN_CONSOLE("Shut down {} zen server instance(s) matching executable '{}'", ShutdownCount, m_ExecutablePath); + } + return; + } + if (m_All) { struct EntryInfo @@ -184,15 +316,10 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) int FailCount = 0; for (const EntryInfo& Info : Entries) { - Instance.Sweep(); - ZenServerState::ZenServerEntry* Entry = Instance.Lookup(Info.Port); - if (Entry && Entry->Pid.load() == Info.Pid) + if (!TryShutdownByPid(Instance, Info.Pid, m_ProgramBaseDir, m_ForceTerminate)) { - if (!ShutdownZenServer(ConsoleLog(), Instance, Entry, m_ProgramBaseDir)) - { - ZEN_CONSOLE_WARN("Failed to shutdown server on port {} (pid {})", Info.Port, Info.Pid); - ++FailCount; - } + ZEN_CONSOLE_WARN("Failed to shutdown server on port {} (pid {})", Info.Port, Info.Pid); + ++FailCount; } } @@ -207,9 +334,10 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!m_DataDir.empty()) { - if (!IsFile(m_DataDir / ".lock")) + if (!LockFile::IsHeldLive(m_DataDir / ".lock", /*AttemptCleanup*/ true)) { - throw std::runtime_error(fmt::format("Lock file does not exist in directory '{}'", m_DataDir)); + ZEN_CONSOLE("No live zen server holding lock file in '{}', nothing to do", m_DataDir); + return; } CbValidateError ValidateResult = CbValidateError::None; if (CbObject LockFileObject = |