aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2024-04-18 12:44:26 +0200
committerGitHub Enterprise <[email protected]>2024-04-18 12:44:26 +0200
commit6d634ab59c05adf1ba028d95b16031a7f8e8db2a (patch)
tree87e8215fed65506f98f6838b2af08c4ffd5819f0 /src
parentzen startup hardening (#49) (diff)
downloadzen-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.cpp40
-rw-r--r--src/zen/cmds/status_cmd.h8
-rw-r--r--src/zen/cmds/up_cmd.cpp45
-rw-r--r--src/zen/cmds/up_cmd.h8
-rw-r--r--src/zencore/compactbinary.cpp22
-rw-r--r--src/zencore/include/zencore/process.h3
-rw-r--r--src/zencore/process.cpp105
-rw-r--r--src/zenserver/main.cpp22
-rw-r--r--src/zenutil/include/zenutil/zenserverprocess.h21
-rw-r--r--src/zenutil/zenserverprocess.cpp113
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