From 6d634ab59c05adf1ba028d95b16031a7f8e8db2a Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Thu, 18 Apr 2024 12:44:26 +0200 Subject: improved lock file handling (#50) - Feature: `zen down` - --`data-dir` to specify a data directory to deduce which zen instance to bring down - Feature: `zen attach` - --`data-dir` to specify a data directory to deduce which zen instance to attach to222 - Feature: `zen status` - --`port` filter running zen instances based on port - --`data-dir` filter running zen instances based on information in the data directory - Improvement: Trying to load a compact binary object from an empty file no longer causes access violation --- src/zenutil/zenserverprocess.cpp | 113 +++++++++++++++++++++++++++++++++++---- 1 file changed, 103 insertions(+), 10 deletions(-) (limited to 'src/zenutil/zenserverprocess.cpp') diff --git a/src/zenutil/zenserverprocess.cpp b/src/zenutil/zenserverprocess.cpp index 384371df3..9a2ec9774 100644 --- a/src/zenutil/zenserverprocess.cpp +++ b/src/zenutil/zenserverprocess.cpp @@ -2,6 +2,8 @@ #include "zenutil/zenserverprocess.h" +#include +#include #include #include #include @@ -237,7 +239,7 @@ ZenServerState::InitializeReadOnly() } ZenServerState::ZenServerEntry* -ZenServerState::Lookup(int DesiredListenPort) +ZenServerState::Lookup(int DesiredListenPort) const { for (int i = 0; i < m_MaxEntryCount; ++i) { @@ -257,6 +259,27 @@ ZenServerState::Lookup(int DesiredListenPort) return nullptr; } +ZenServerState::ZenServerEntry* +ZenServerState::LookupByEffectivePort(int Port) const +{ + for (int i = 0; i < m_MaxEntryCount; ++i) + { + uint16_t EntryPort = m_Data[i].EffectiveListenPort; + if (EntryPort != 0) + { + if (EntryPort == Port) + { + if (IsProcessRunning(m_Data[i].Pid)) + { + return &m_Data[i]; + } + } + } + } + + return nullptr; +} + ZenServerState::ZenServerEntry* ZenServerState::Register(int DesiredListenPort) { @@ -324,7 +347,7 @@ ZenServerState::Sweep() } void -ZenServerState::Snapshot(std::function&& Callback) +ZenServerState::Snapshot(std::function&& Callback) const { if (m_Data == nullptr) { @@ -333,11 +356,14 @@ ZenServerState::Snapshot(std::function&& Callback) for (int i = 0; i < m_MaxEntryCount; ++i) { - ZenServerEntry& Entry = m_Data[i]; + const ZenServerEntry& Entry = m_Data[i]; - if (Entry.DesiredListenPort) + if (Entry.Pid != 0 && Entry.DesiredListenPort) { - Callback(Entry); + if (IsProcessRunning(Entry.Pid.load())) + { + Callback(Entry); + } } } } @@ -696,11 +722,8 @@ ZenServerInstance::AttachToRunningServer(int BasePort) else { State.Snapshot([&](const ZenServerState::ZenServerEntry& InEntry) { - if (IsProcessRunning(InEntry.Pid.load())) - { - ZEN_INFO("Found entry pid {}, baseport {}", InEntry.Pid.load(), InEntry.DesiredListenPort.load()); - Entry = &InEntry; - } + ZEN_INFO("Found entry pid {}, baseport {}", InEntry.Pid.load(), InEntry.DesiredListenPort.load()); + Entry = &InEntry; }); } @@ -850,4 +873,74 @@ ZenServerInstance::Terminate() return true; } +CbObject +MakeLockFilePayload(const LockFileInfo& Info) +{ + CbObjectWriter Cbo; + Cbo << "pid" << Info.Pid << "data" << PathToUtf8(Info.DataDir) << "port" << Info.EffectiveListenPort << "session_id" << Info.SessionId + << "ready" << Info.Ready << "executable" << PathToUtf8(Info.ExecutablePath); + return Cbo.Save(); +} +LockFileInfo +ReadLockFilePayload(const CbObject& Payload) +{ + LockFileInfo Info; + Info.Pid = Payload["pid"].AsInt32(); + Info.SessionId = Payload["session_id"].AsObjectId(); + Info.EffectiveListenPort = Payload["port"].AsUInt16(); + Info.Ready = Payload["ready"].AsBool(); + Info.DataDir = Payload["data"].AsU8String(); + Info.ExecutablePath = Payload["executable"].AsU8String(); + return Info; +} + +bool +ValidateLockFileInfo(const LockFileInfo& Info, std::string& OutReason) +{ + if (Info.Pid == 0) + { + OutReason = fmt::format("process ({}) is invalid", Info.Pid); + return false; + } + if (!IsProcessRunning(Info.Pid)) + { + OutReason = fmt::format("process ({}) is not running", Info.Pid); + return false; + } + if (Info.SessionId == Oid::Zero) + { + OutReason = fmt::format("session id ({}) is not valid", Info.SessionId); + return false; + } + if (Info.EffectiveListenPort == 0) + { + OutReason = fmt::format("listen port ({}) is not valid", Info.EffectiveListenPort); + return false; + } + if (!std::filesystem::is_directory(Info.DataDir)) + { + OutReason = fmt::format("data directory ('{}') does not exist", Info.DataDir); + return false; + } + if (!Info.ExecutablePath.empty()) + { + std::error_code Ec; + std::filesystem::path PidPath = GetProcessExecutablePath(Info.Pid, Ec); + if (Ec) + { + OutReason = fmt::format("failed to find executable path of process ('{}'), {}", Info.Pid, Ec.message()); + return false; + } + if (PidPath != Info.ExecutablePath) + { + OutReason = fmt::format("executable path of process ({}: '{}') does not match executable path '{}'", + Info.Pid, + PidPath, + Info.ExecutablePath); + return false; + } + } + return true; +} + } // namespace zen -- cgit v1.2.3