aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-08-09 14:13:30 +0200
committerStefan Boberg <[email protected]>2021-08-09 14:13:30 +0200
commit0b9b482df96d1bd971151fb28614b3faafdaf451 (patch)
tree6c7a1d62fadf43675b546d7fe8badce71267d25f
parentAdded ThrowLastError which accepts a std::source_location (diff)
downloadzen-0b9b482df96d1bd971151fb28614b3faafdaf451.tar.xz
zen-0b9b482df96d1bd971151fb28614b3faafdaf451.zip
Added ZenServerState implementation, used to track and enumerate live Zen server instances
-rw-r--r--zenutil/include/zenserverprocess.h42
-rw-r--r--zenutil/zenserverprocess.cpp248
2 files changed, 290 insertions, 0 deletions
diff --git a/zenutil/include/zenserverprocess.h b/zenutil/include/zenserverprocess.h
index 239841a04..60ed273cb 100644
--- a/zenutil/include/zenserverprocess.h
+++ b/zenutil/include/zenserverprocess.h
@@ -6,6 +6,7 @@
#include <spdlog/spdlog.h>
+#include <atomic>
#include <filesystem>
class ZenServerEnvironment
@@ -58,3 +59,44 @@ private:
std::filesystem::path m_TestDir;
bool m_MeshEnabled = false;
};
+
+/** Shared system state
+ *
+ * Used as a scratchpad to identify running instances etc
+ *
+ * The state lives in a memory-mapped file backed by the swapfile
+ *
+ */
+
+class ZenServerState
+{
+public:
+ ZenServerState();
+ ~ZenServerState();
+
+ struct ZenServerEntry
+ {
+ std::atomic<uint32_t> Pid;
+ std::atomic<uint16_t> ListenPort;
+ uint16_t Flags;
+ uint8_t SessionId[12];
+ uint8_t Padding[12];
+
+ void Reset();
+ };
+
+ static_assert(sizeof(ZenServerEntry) == 32);
+
+ void Initialize();
+ [[nodiscard]] bool InitializeReadOnly();
+ ZenServerEntry* Lookup(int ListenPort);
+ ZenServerEntry* Register(int ListenPort);
+ void Sweep();
+ void Snapshot(std::function<void(const ZenServerEntry&)>&& Callback);
+
+private:
+ void* m_hMapFile = nullptr;
+ ZenServerEntry* m_Data;
+ int m_MaxEntryCount = 4096 / sizeof(ZenServerEntry);
+ ZenServerEntry* m_OurEntry = nullptr;
+};
diff --git a/zenutil/zenserverprocess.cpp b/zenutil/zenserverprocess.cpp
index 9d38d3939..8427e612e 100644
--- a/zenutil/zenserverprocess.cpp
+++ b/zenutil/zenserverprocess.cpp
@@ -2,12 +2,260 @@
#include "zenserverprocess.h"
+#include <zencore/except.h>
#include <zencore/filesystem.h>
#include <zencore/fmtutils.h>
#include <zencore/string.h>
+#include <atlbase.h>
#include <shellapi.h>
#include <spdlog/spdlog.h>
+#include <source_location>
+
+#include <zencore/windows.h>
+
+//////////////////////////////////////////////////////////////////////////
+
+namespace zenutil {
+
+class SecurityAttributes
+{
+public:
+ inline SECURITY_ATTRIBUTES* Attributes() { return &m_Attributes; }
+
+protected:
+ SECURITY_ATTRIBUTES m_Attributes{};
+ SECURITY_DESCRIPTOR m_Sd{};
+};
+
+// Security attributes which allows any user access
+
+class AnyUserSecurityAttributes : public SecurityAttributes
+{
+public:
+ AnyUserSecurityAttributes()
+ {
+ m_Attributes.nLength = sizeof m_Attributes;
+ m_Attributes.bInheritHandle = false; // Disable inheritance
+
+ const BOOL success = InitializeSecurityDescriptor(&m_Sd, SECURITY_DESCRIPTOR_REVISION);
+
+ if (success)
+ {
+ const BOOL bSetOk = SetSecurityDescriptorDacl(&m_Sd, TRUE, (PACL)NULL, FALSE);
+
+ if (bSetOk)
+ {
+ m_Attributes.lpSecurityDescriptor = &m_Sd;
+ }
+ else
+ {
+ zen::ThrowLastError("SetSecurityDescriptorDacl failed", std::source_location::current());
+ }
+ }
+ }
+};
+
+} // namespace zenutil
+
+//////////////////////////////////////////////////////////////////////////
+
+ZenServerState::ZenServerState()
+{
+}
+
+ZenServerState::~ZenServerState()
+{
+ if (m_OurEntry)
+ {
+ // Clean up our entry now that we're leaving
+
+ m_OurEntry->Reset();
+ m_OurEntry = nullptr;
+ }
+
+ if (m_Data)
+ {
+ UnmapViewOfFile(m_Data);
+ m_Data = nullptr;
+ }
+
+ if (m_hMapFile)
+ {
+ CloseHandle(m_hMapFile);
+ }
+}
+
+void
+ZenServerState::Initialize()
+{
+ // TODO: there's a small chance of a race here, this logic could be tightened up with a mutex to
+ // ensure only a single process at a time creates the mapping
+
+ if (HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Global\\ZenMap"))
+ {
+ m_hMapFile = hMap;
+ }
+ else
+ {
+ // Security attributes to enable any user to access state
+ zenutil::AnyUserSecurityAttributes Attrs;
+
+ hMap = CreateFileMapping(INVALID_HANDLE_VALUE, // use paging file
+ Attrs.Attributes(), // allow anyone to access
+ PAGE_READWRITE, // read/write access
+ 0, // maximum object size (high-order DWORD)
+ m_MaxEntryCount * sizeof(ZenServerEntry), // maximum object size (low-order DWORD)
+ L"Global\\ZenMap"); // name of mapping object
+
+ if (hMap == NULL)
+ {
+ zen::ThrowLastError("Could not open or create file mapping object for Zen server state");
+ }
+
+ m_hMapFile = hMap;
+ }
+
+ void* pBuf = MapViewOfFile(m_hMapFile, // handle to map object
+ FILE_MAP_ALL_ACCESS, // read/write permission
+ 0, // offset high
+ 0, // offset low
+ m_MaxEntryCount * sizeof(ZenServerEntry));
+
+ if (pBuf == NULL)
+ {
+ zen::ThrowLastError("Could not map view of Zen server state");
+ }
+
+ m_Data = reinterpret_cast<ZenServerEntry*>(pBuf);
+}
+
+bool
+ZenServerState::InitializeReadOnly()
+{
+ if (HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Global\\ZenMap"))
+ {
+ m_hMapFile = hMap;
+ }
+ else
+ {
+ return false;
+ }
+
+ void* pBuf = MapViewOfFile(m_hMapFile, // handle to map object
+ FILE_MAP_READ, // read permission
+ 0, // offset high
+ 0, // offset low
+ m_MaxEntryCount * sizeof(ZenServerEntry));
+
+ if (pBuf == NULL)
+ {
+ zen::ThrowLastError("Could not map view of Zen server state");
+ }
+
+ m_Data = reinterpret_cast<ZenServerEntry*>(pBuf);
+
+ return true;
+}
+
+ZenServerState::ZenServerEntry*
+ZenServerState::Lookup(int ListenPort)
+{
+ for (int i = 0; i < m_MaxEntryCount; ++i)
+ {
+ if (m_Data[i].ListenPort == ListenPort)
+ {
+ return &m_Data[i];
+ }
+ }
+
+ return nullptr;
+}
+
+ZenServerState::ZenServerEntry*
+ZenServerState::Register(int ListenPort)
+{
+ if (m_Data == nullptr)
+ {
+ return nullptr;
+ }
+
+ // Allocate an entry
+
+ int Pid = zen::GetCurrentProcessId();
+
+ for (int i = 0; i < m_MaxEntryCount; ++i)
+ {
+ ZenServerEntry& Entry = m_Data[i];
+
+ if (Entry.ListenPort.load(std::memory_order::memory_order_relaxed) == 0)
+ {
+ uint16_t Expected = 0;
+ if (Entry.ListenPort.compare_exchange_strong(Expected, ListenPort))
+ {
+ // Successfully allocated entry
+
+ m_OurEntry = &Entry;
+
+ Entry.Pid = Pid;
+
+ return &Entry;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+void
+ZenServerState::Sweep()
+{
+ if (m_Data == nullptr)
+ {
+ return;
+ }
+
+ for (int i = 0; i < m_MaxEntryCount; ++i)
+ {
+ ZenServerEntry& Entry = m_Data[i];
+
+ if (Entry.ListenPort)
+ {
+ if (zen::IsProcessRunning(Entry.Pid) == false)
+ {
+ spdlog::debug("Sweep - pid {} not running, reclaiming entry (port {})", Entry.Pid, Entry.ListenPort);
+
+ Entry.Reset();
+ }
+ }
+ }
+}
+
+void
+ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& Callback)
+{
+ if (m_Data == nullptr)
+ {
+ return;
+ }
+
+ for (int i = 0; i < m_MaxEntryCount; ++i)
+ {
+ ZenServerEntry& Entry = m_Data[i];
+
+ if (Entry.ListenPort)
+ {
+ Callback(Entry);
+ }
+ }
+}
+
+void
+ZenServerState::ZenServerEntry::Reset()
+{
+ Pid = 0;
+ ListenPort = 0;
+}
//////////////////////////////////////////////////////////////////////////