aboutsummaryrefslogtreecommitdiff
path: root/src/zen/cmds/up_cmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zen/cmds/up_cmd.cpp')
-rw-r--r--src/zen/cmds/up_cmd.cpp162
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 =