diff options
| author | Dan Engelbrecht <[email protected]> | 2024-04-18 12:44:26 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2024-04-18 12:44:26 +0200 |
| commit | 6d634ab59c05adf1ba028d95b16031a7f8e8db2a (patch) | |
| tree | 87e8215fed65506f98f6838b2af08c4ffd5819f0 /src | |
| parent | zen startup hardening (#49) (diff) | |
| download | zen-6d634ab59c05adf1ba028d95b16031a7f8e8db2a.tar.xz zen-6d634ab59c05adf1ba028d95b16031a7f8e8db2a.zip | |
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
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/status_cmd.cpp | 40 | ||||
| -rw-r--r-- | src/zen/cmds/status_cmd.h | 8 | ||||
| -rw-r--r-- | src/zen/cmds/up_cmd.cpp | 45 | ||||
| -rw-r--r-- | src/zen/cmds/up_cmd.h | 8 | ||||
| -rw-r--r-- | src/zencore/compactbinary.cpp | 22 | ||||
| -rw-r--r-- | src/zencore/include/zencore/process.h | 3 | ||||
| -rw-r--r-- | src/zencore/process.cpp | 105 | ||||
| -rw-r--r-- | src/zenserver/main.cpp | 22 | ||||
| -rw-r--r-- | src/zenutil/include/zenutil/zenserverprocess.h | 21 | ||||
| -rw-r--r-- | src/zenutil/zenserverprocess.cpp | 113 |
10 files changed, 317 insertions, 70 deletions
diff --git a/src/zen/cmds/status_cmd.cpp b/src/zen/cmds/status_cmd.cpp index cc936835a..16754e747 100644 --- a/src/zen/cmds/status_cmd.cpp +++ b/src/zen/cmds/status_cmd.cpp @@ -2,6 +2,7 @@ #include "status_cmd.h" +#include <zencore/compactbinary.h> #include <zencore/fmtutils.h> #include <zencore/logging.h> #include <zencore/string.h> @@ -12,6 +13,8 @@ namespace zen { StatusCommand::StatusCommand() { + m_Options.add_option("", "p", "port", "Host port", cxxopts::value(m_Port)->default_value("0"), "<hostport>"); + m_Options.add_option("", "", "data-dir", "Path to data directory to inspect for running server", cxxopts::value(m_DataDir), "<file>"); } StatusCommand::~StatusCommand() = default; @@ -19,7 +22,30 @@ StatusCommand::~StatusCommand() = default; int StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { - ZEN_UNUSED(GlobalOptions, argc, argv); + ZEN_UNUSED(GlobalOptions); + + if (!ParseOptions(argc, argv)) + { + return 0; + } + + uint16_t EffectivePort = 0; + if (!m_DataDir.empty()) + { + if (!std::filesystem::is_regular_file(m_DataDir / ".lock")) + { + ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir); + return 1; + } + LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock"))); + std::string Reason; + if (!ValidateLockFileInfo(Info, Reason)) + { + ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); + return 1; + } + EffectivePort = Info.EffectiveListenPort; + } ZenServerState State; if (!State.InitializeReadOnly()) @@ -31,9 +57,15 @@ StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE("{:>5} {:>6} {:>24}", "port", "pid", "session"); State.Snapshot([&](const ZenServerState::ZenServerEntry& Entry) { - StringBuilder<25> SessionStringBuilder; - Entry.GetSessionId().ToString(SessionStringBuilder); - ZEN_CONSOLE("{:>5} {:>6} {:>24}", Entry.EffectiveListenPort.load(), Entry.Pid.load(), SessionStringBuilder); + bool MatchesAnyPort = (m_Port == 0) && (EffectivePort == 0); + bool MatchesEffectivePort = (EffectivePort != 0) && (Entry.EffectiveListenPort.load() == EffectivePort); + bool MatchesDesiredPort = (m_Port != 0) && (Entry.DesiredListenPort.load() == m_Port); + if (MatchesAnyPort || MatchesEffectivePort || MatchesDesiredPort) + { + StringBuilder<25> SessionStringBuilder; + Entry.GetSessionId().ToString(SessionStringBuilder); + ZEN_CONSOLE("{:>5} {:>6} {:>24}", Entry.EffectiveListenPort.load(), Entry.Pid.load(), SessionStringBuilder); + } }); return 0; diff --git a/src/zen/cmds/status_cmd.h b/src/zen/cmds/status_cmd.h index 98f72e651..2dc742944 100644 --- a/src/zen/cmds/status_cmd.h +++ b/src/zen/cmds/status_cmd.h @@ -4,6 +4,8 @@ #include "../zen.h" +#include <zencore/filesystem.h> + namespace zen { class StatusCommand : public ZenCmdBase @@ -16,7 +18,11 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{"status", "Show zen status"}; + int GetLockFileEffectivePort() const; + + cxxopts::Options m_Options{"status", "Show zen status"}; + uint16_t m_Port; + std::filesystem::path m_DataDir; }; } // namespace zen diff --git a/src/zen/cmds/up_cmd.cpp b/src/zen/cmds/up_cmd.cpp index 61a901fd6..3095211b6 100644 --- a/src/zen/cmds/up_cmd.cpp +++ b/src/zen/cmds/up_cmd.cpp @@ -2,7 +2,9 @@ #include "up_cmd.h" +#include <zencore/compactbinary.h> #include <zencore/filesystem.h> +#include <zencore/fmtutils.h> #include <zencore/logging.h> #include <zencore/process.h> #include <zencore/timer.h> @@ -48,10 +50,9 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) }; std::vector<EntryInfo> RunningEntries; State.Snapshot([&RunningEntries, DesiredPort = this->m_Port](const zen::ZenServerState::ZenServerEntry& Entry) { - uint32_t Pid = Entry.Pid.load(); - if (IsProcessRunning(Pid) && (DesiredPort == 0 || Entry.DesiredListenPort.load() == DesiredPort)) + if (DesiredPort == 0 || Entry.DesiredListenPort.load() == DesiredPort) { - RunningEntries.push_back(EntryInfo{.Pid = Pid, + RunningEntries.push_back(EntryInfo{.Pid = Entry.Pid.load(), .DesiredPort = Entry.DesiredListenPort.load(), .EffectivePort = Entry.EffectiveListenPort.load()}); } @@ -86,6 +87,7 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!Server.WaitUntilReady(Timeout)) { ZEN_ERROR("zen server launch failed (timed out)"); + return 1; } else { @@ -101,6 +103,7 @@ AttachCommand::AttachCommand() { m_Options.add_option("", "p", "port", "Host port", cxxopts::value(m_Port)->default_value("8558"), "<hostport>"); m_Options.add_option("lifetime", "", "owner-pid", "Specify owning process id", cxxopts::value(m_OwnerPid), "<identifier>"); + m_Options.add_option("", "", "data-dir", "Path to data directory to inspect for running server", cxxopts::value(m_DataDir), "<file>"); } AttachCommand::~AttachCommand() = default; @@ -119,6 +122,24 @@ AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Instance.Initialize(); Instance.Sweep(); ZenServerState::ZenServerEntry* Entry = Instance.Lookup(m_Port); + + if (!m_DataDir.empty()) + { + if (!std::filesystem::is_regular_file(m_DataDir / ".lock")) + { + ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir); + return 1; + } + LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock"))); + std::string Reason; + if (!ValidateLockFileInfo(Info, Reason)) + { + ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); + return 1; + } + Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort); + } + if (!Entry) { ZEN_WARN("no zen server instance to add sponsor process to"); @@ -142,6 +163,7 @@ DownCommand::DownCommand() m_Options.add_option("", "p", "port", "Host port", cxxopts::value(m_Port)->default_value("0"), "<hostport>"); 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>"); } DownCommand::~DownCommand() = default; @@ -167,6 +189,23 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_ProgramBaseDir = ExePath.parent_path(); } + if (!m_DataDir.empty()) + { + if (!std::filesystem::is_regular_file(m_DataDir / ".lock")) + { + ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir); + return 1; + } + LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock"))); + std::string Reason; + if (!ValidateLockFileInfo(Info, Reason)) + { + ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); + return 1; + } + Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort); + } + if (Entry) { int EntryPort = (int)Entry->DesiredListenPort.load(); diff --git a/src/zen/cmds/up_cmd.h b/src/zen/cmds/up_cmd.h index 5e6af9d08..365f26002 100644 --- a/src/zen/cmds/up_cmd.h +++ b/src/zen/cmds/up_cmd.h @@ -34,9 +34,10 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{"attach", "Add a sponsor process to a running zen service"}; - uint16_t m_Port; - int m_OwnerPid; + cxxopts::Options m_Options{"attach", "Add a sponsor process to a running zen service"}; + uint16_t m_Port; + int m_OwnerPid; + std::filesystem::path m_DataDir; }; class DownCommand : public ZenCmdBase @@ -53,6 +54,7 @@ private: uint16_t m_Port; bool m_ForceTerminate = false; std::filesystem::path m_ProgramBaseDir; + std::filesystem::path m_DataDir; }; } // namespace zen diff --git a/src/zencore/compactbinary.cpp b/src/zencore/compactbinary.cpp index 0907f8a2e..029889e85 100644 --- a/src/zencore/compactbinary.cpp +++ b/src/zencore/compactbinary.cpp @@ -1421,25 +1421,43 @@ LoadCompactBinary(BinaryReader& Ar, BufferAllocator Allocator) CbObject LoadCompactBinaryObject(IoBuffer&& Payload) { + if (Payload.GetSize() == 0) + { + return CbObject(); + } return CbObject{SharedBuffer(std::move(Payload))}; } CbObject LoadCompactBinaryObject(const IoBuffer& Payload) { + if (Payload.GetSize() == 0) + { + return CbObject(); + } return CbObject{SharedBuffer(Payload)}; } CbObject LoadCompactBinaryObject(CompressedBuffer&& Payload) { - return CbObject{SharedBuffer(Payload.DecompressToComposite().Flatten())}; + CompositeBuffer Decompressed = std::move(Payload).DecompressToComposite(); + if (Decompressed.GetSize() == 0) + { + return CbObject(); + } + return CbObject{std::move(Decompressed).Flatten()}; } CbObject LoadCompactBinaryObject(const CompressedBuffer& Payload) { - return CbObject{SharedBuffer(Payload.DecompressToComposite().Flatten())}; + CompositeBuffer Decompressed = Payload.DecompressToComposite(); + if (Decompressed.GetSize() == 0) + { + return CbObject(); + } + return CbObject{std::move(Decompressed).Flatten()}; } ////////////////////////////////////////////////////////////////////////// diff --git a/src/zencore/include/zencore/process.h b/src/zencore/include/zencore/process.h index 6dce2d264..dbf5837e2 100644 --- a/src/zencore/include/zencore/process.h +++ b/src/zencore/include/zencore/process.h @@ -95,7 +95,8 @@ ZENCORE_API bool IsProcessRunning(int pid, std::error_code& OutEc); ZENCORE_API int GetCurrentProcessId(); int GetProcessId(CreateProcResult ProcId); -std::error_code FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle); +std::filesystem::path GetProcessExecutablePath(int Pid, std::error_code& OutEc); +std::error_code FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle); void process_forcelink(); // internal diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index df2a87352..8fddc9562 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -813,6 +813,54 @@ GetProcessId(CreateProcResult ProcId) #endif } +std::filesystem::path +GetProcessExecutablePath(int Pid, std::error_code& OutEc) +{ +#if ZEN_PLATFORM_WINDOWS + HANDLE ModuleSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, (DWORD)Pid); + if (ModuleSnapshotHandle != INVALID_HANDLE_VALUE) + { + auto __ = MakeGuard([&]() { CloseHandle(ModuleSnapshotHandle); }); + MODULEENTRY32 ModuleEntry; + ModuleEntry.dwSize = sizeof(MODULEENTRY32); + if (Module32First(ModuleSnapshotHandle, (LPMODULEENTRY32)&ModuleEntry)) + { + std::filesystem::path ProcessExecutablePath(ModuleEntry.szExePath); + return ProcessExecutablePath; + } + OutEc = MakeErrorCodeFromLastError(); + return {}; + } + OutEc = MakeErrorCodeFromLastError(); + return {}; +#endif // ZEN_PLATFORM_WINDOWS +#if ZEN_PLATFORM_MAC + char Buffer[PROC_PIDPATHINFO_MAXSIZE]; + int Res = proc_pidpath(Pid, Buffer, sizeof(Buffer)); + if (Res > 0) + { + std::filesystem::path ProcessExecutablePath(Buffer); + return ProcessExecutablePath; + } + OutEc = MakeErrorCodeFromLastError(); + return {}; +#endif // ZEN_PLATFORM_MAC +#if ZEN_PLATFORM_LINUX + std::filesystem::path EntryPath = std::filesystem::path("/proc") / fmt::format("{}", Pid); + std::filesystem::path ExeLinkPath = EntryPath / "exe"; + char Link[4096]; + ssize_t BytesRead = readlink(ExeLinkPath.c_str(), Link, sizeof(Link) - 1); + if (BytesRead > 0) + { + Link[BytesRead] = '\0'; + std::filesystem::path ProcessExecutablePath(Link); + return ProcessExecutablePath; + } + OutEc = MakeErrorCodeFromLastError(); + return {}; +#endif // ZEN_PLATFORM_LINUX +} + std::error_code FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle) { @@ -832,23 +880,22 @@ FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHand { if (ExecutableImage.filename() == Entry.szExeFile) { - HANDLE ModuleSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, Entry.th32ProcessID); - if (ModuleSnapshotHandle != INVALID_HANDLE_VALUE) + std::error_code Ec; + std::filesystem::path EntryPath = GetProcessExecutablePath(Entry.th32ProcessID, Ec); + if (!Ec) { - auto __ = MakeGuard([&]() { CloseHandle(ModuleSnapshotHandle); }); - MODULEENTRY32 ModuleEntry; - ModuleEntry.dwSize = sizeof(MODULEENTRY32); - if (Module32First(ModuleSnapshotHandle, (LPMODULEENTRY32)&ModuleEntry)) + if (EntryPath == ExecutableImage) { - std::filesystem::path EntryPath(ModuleEntry.szExePath); - if (EntryPath == ExecutableImage) + HANDLE Handle = + OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, Entry.th32ProcessID); + if (Handle == NULL) + { + return MakeErrorCodeFromLastError(); + } + DWORD ExitCode = 0; + GetExitCodeProcess(Handle, &ExitCode); + if (ExitCode == STILL_ACTIVE) { - HANDLE Handle = - OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, Entry.th32ProcessID); - if (Handle == NULL) - { - return MakeErrorCodeFromLastError(); - } OutHandle.Initialize((void*)Handle); return {}; } @@ -876,16 +923,15 @@ FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHand char Buffer[PROC_PIDPATHINFO_MAXSIZE]; for (uint32_t ProcIndex = 0; ProcIndex < ProcCount; ProcIndex++) { - if (Processes[ProcIndex].kp_proc.p_stat != SZOMB) + pid_t Pid = Processes[ProcIndex].kp_proc.p_pid; + std::error_code Ec; + std::filesystem::path EntryPath = GetProcessExecutablePath(Pid, Ec); + if (!Ec) { - pid_t Pid = Processes[ProcIndex].kp_proc.p_pid; - int Res = proc_pidpath(Pid, Buffer, sizeof(Buffer)); - if (Res > 0) + if (EntryPath == ExecutableImage) { - std::filesystem::path EntryPath(Buffer); - if (EntryPath == ExecutableImage) + if (Processes[ProcIndex].kp_proc.p_stat != SZOMB) { - std::error_code Ec; OutHandle.Initialize(Pid, Ec); return Ec; } @@ -912,20 +958,15 @@ FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHand for (uint32_t Pid : RunningPids) { - char Status = GetPidStatus(Pid); - if (Status && (Status != 'Z')) + std::error_code Ec; + std::filesystem::path EntryPath = GetProcessExecutablePath((int)Pid, Ec); + if (!Ec) { - std::filesystem::path EntryPath = std::filesystem::path("/proc") / fmt::format("{}", Pid); - std::filesystem::path ExeLinkPath = EntryPath / "exe"; - char Link[4096]; - ssize_t BytesRead = readlink(ExeLinkPath.c_str(), Link, sizeof(Link) - 1); - if (BytesRead > 0) + if (EntryPath == ExecutableImage) { - Link[BytesRead] = '\0'; - std::filesystem::path ExePath(Link); - if (ExePath == ExecutableImage) + char Status = GetPidStatus(Pid); + if (Status && (Status != 'Z')) { - std::error_code Ec; OutHandle.Initialize(Pid, Ec); return Ec; } diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index 2d2b24bbb..8715f5447 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -153,23 +153,23 @@ ZenEntryPoint::Run() std::filesystem::path LockFilePath = m_ServerOptions.DataDir / ".lock"; - bool IsReady = false; - - auto MakeLockData = [&] { - CbObjectWriter Cbo; - Cbo << "pid" << GetCurrentProcessId() << "data" << PathToUtf8(m_ServerOptions.DataDir) << "port" << m_ServerOptions.BasePort - << "session_id" << GetSessionId() << "ready" << IsReady; - return Cbo.Save(); + auto MakeLockData = [&](bool IsReady) { + return MakeLockFilePayload({.Pid = GetCurrentProcessId(), + .SessionId = GetSessionId(), + .EffectiveListenPort = gsl::narrow<uint16_t>(m_ServerOptions.BasePort), + .Ready = IsReady, + .DataDir = m_ServerOptions.DataDir, + .ExecutablePath = GetRunningExecutablePath()}); }; - m_LockFile.Create(LockFilePath, MakeLockData(), Ec); + m_LockFile.Create(LockFilePath, MakeLockData(false), Ec); if (Ec) { ZEN_WARN("Unable to grab lock at '{}' (error: '{}'), retrying", LockFilePath, Ec.message()); Sleep(500); - m_LockFile.Create(LockFilePath, MakeLockData(), Ec); + m_LockFile.Create(LockFilePath, MakeLockData(false), Ec); if (Ec) { ZEN_WARN("ERROR: Unable to grab lock at '{}' (error: '{}')", LockFilePath, Ec.message()); @@ -255,9 +255,7 @@ ZenEntryPoint::Run() // to be able to communicate readiness with the parent Server.SetIsReadyFunc([&] { - IsReady = true; - - m_LockFile.Update(MakeLockData(), Ec); + m_LockFile.Update(MakeLockData(true), Ec); if (!m_ServerOptions.ChildId.empty()) { diff --git a/src/zenutil/include/zenutil/zenserverprocess.h b/src/zenutil/include/zenutil/zenserverprocess.h index 720b30766..af5125471 100644 --- a/src/zenutil/include/zenutil/zenserverprocess.h +++ b/src/zenutil/include/zenutil/zenserverprocess.h @@ -15,6 +15,8 @@ namespace zen { +class CbObject; + /** Zen Server Environment configuration This class allows a user to configure where a spawned server should place any @@ -171,10 +173,11 @@ public: void Initialize(); [[nodiscard]] bool InitializeReadOnly(); - [[nodiscard]] ZenServerEntry* Lookup(int DesiredListenPort); + [[nodiscard]] ZenServerEntry* Lookup(int DesiredListenPort) const; + [[nodiscard]] ZenServerEntry* LookupByEffectivePort(int Port) const; ZenServerEntry* Register(int DesiredListenPort); void Sweep(); - void Snapshot(std::function<void(const ZenServerEntry&)>&& Callback); + void Snapshot(std::function<void(const ZenServerEntry&)>&& Callback) const; inline bool IsReadOnly() const { return m_IsReadOnly; } private: @@ -185,4 +188,18 @@ private: bool m_IsReadOnly = true; }; +struct LockFileInfo +{ + int32_t Pid; + Oid SessionId; + uint16_t EffectiveListenPort; + bool Ready; + std::filesystem::path DataDir; + std::filesystem::path ExecutablePath; +}; + +CbObject MakeLockFilePayload(const LockFileInfo& Info); +LockFileInfo ReadLockFilePayload(const CbObject& Payload); +bool ValidateLockFileInfo(const LockFileInfo& Info, std::string& OutReason); + } // namespace zen 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 <zencore/compactbinary.h> +#include <zencore/compactbinarybuilder.h> #include <zencore/except.h> #include <zencore/filesystem.h> #include <zencore/fmtutils.h> @@ -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) { @@ -258,6 +260,27 @@ ZenServerState::Lookup(int DesiredListenPort) } 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) { if (m_Data == nullptr) @@ -324,7 +347,7 @@ ZenServerState::Sweep() } void -ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& Callback) +ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& Callback) const { if (m_Data == nullptr) { @@ -333,11 +356,14 @@ ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& 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 |