aboutsummaryrefslogtreecommitdiff
path: root/zenutil/zenserverprocess.cpp
diff options
context:
space:
mode:
authorPer Larsson <[email protected]>2021-12-14 12:34:47 +0100
committerPer Larsson <[email protected]>2021-12-14 12:34:47 +0100
commitb6c6568e1618f10d2160d836b65e35586e3c740f (patch)
treef6a929cf918850bbba87d0ee67cd3482b2d50e24 /zenutil/zenserverprocess.cpp
parentFixed bug in z$ service returning partial cache records and enable small obje... (diff)
parentPartial revert b363c5b (diff)
downloadzen-b6c6568e1618f10d2160d836b65e35586e3c740f.tar.xz
zen-b6c6568e1618f10d2160d836b65e35586e3c740f.zip
Merged main.
Diffstat (limited to 'zenutil/zenserverprocess.cpp')
-rw-r--r--zenutil/zenserverprocess.cpp222
1 files changed, 110 insertions, 112 deletions
diff --git a/zenutil/zenserverprocess.cpp b/zenutil/zenserverprocess.cpp
index bc8526a20..93886a6b7 100644
--- a/zenutil/zenserverprocess.cpp
+++ b/zenutil/zenserverprocess.cpp
@@ -8,18 +8,24 @@
#include <zencore/logging.h>
#include <zencore/session.h>
#include <zencore/string.h>
+#include <zencore/thread.h>
-#include <atlbase.h>
-#include <shellapi.h>
+#include <atomic>
#include <source_location>
-#include <zencore/windows.h>
+#if ZEN_PLATFORM_WINDOWS
+# include <atlbase.h>
+# include <zencore/windows.h>
+#else
+# include <sys/mman.h>
+#endif
//////////////////////////////////////////////////////////////////////////
namespace zen {
namespace zenutil {
+#if ZEN_PLATFORM_WINDOWS
class SecurityAttributes
{
public:
@@ -53,6 +59,7 @@ namespace zenutil {
}
}
};
+#endif // ZEN_PLATFORM_WINDOWS
} // namespace zenutil
@@ -72,21 +79,35 @@ ZenServerState::~ZenServerState()
m_OurEntry = nullptr;
}
+#if ZEN_PLATFORM_WINDOWS
if (m_Data)
{
UnmapViewOfFile(m_Data);
- m_Data = nullptr;
}
if (m_hMapFile)
{
CloseHandle(m_hMapFile);
}
+#else
+ if (m_Data != nullptr)
+ {
+ munmap(m_Data, m_MaxEntryCount * sizeof(ZenServerEntry));
+ }
+
+ int Fd = int(intptr_t(m_hMapFile));
+ close(Fd);
+#endif
+
+ m_Data = nullptr;
}
void
ZenServerState::Initialize()
{
+ size_t MapSize = m_MaxEntryCount * sizeof(ZenServerEntry);
+
+#if ZEN_PLATFORM_WINDOWS
// 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
// TODO: the fallback to Local instead of Global has a flaw where if you start a non-elevated instance
@@ -94,25 +115,23 @@ ZenServerState::Initialize()
// mapping and the second instance with a global mapping. This kind of elevated/non-elevated
// shouldn't be common, but handling for it should be improved in the future.
- if (HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Global\\ZenMap"))
- {
- m_hMapFile = hMap;
- }
- else if (HANDLE hLocalMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Local\\ZenMap"))
+ HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Global\\ZenMap");
+ if (hMap == NULL)
{
- m_hMapFile = hLocalMap;
+ hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Local\\ZenMap");
}
- else
+
+ if (hMap == NULL)
{
// 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
+ 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)
+ DWORD(MapSize), // maximum object size (low-order DWORD)
+ L"Global\\ZenMap"); // name of mapping object
if (hMap == NULL)
{
@@ -128,21 +147,37 @@ ZenServerState::Initialize()
{
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
+ void* pBuf = MapViewOfFile(hMap, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0, // offset high
0, // offset low
- m_MaxEntryCount * sizeof(ZenServerEntry));
+ DWORD(MapSize));
if (pBuf == NULL)
{
ThrowLastError("Could not map view of Zen server state");
}
+#else
+ int Fd = shm_open("UnrealEngineZen", O_RDWR | O_CREAT, 0666);
+ if (Fd < 0)
+ {
+ ThrowLastError("Could not open a shared memory object");
+ }
+ void* hMap = (void*)intptr_t(Fd);
+ int Result = ftruncate(Fd, MapSize);
+ ZEN_UNUSED(Result);
+
+ void* pBuf = mmap(nullptr, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED, Fd, 0);
+ if (pBuf == MAP_FAILED)
+ {
+ ThrowLastError("Could not map view of Zen server state");
+ }
+#endif
+
+ m_hMapFile = hMap;
m_Data = reinterpret_cast<ZenServerEntry*>(pBuf);
m_IsReadOnly = false;
}
@@ -150,31 +185,47 @@ ZenServerState::Initialize()
bool
ZenServerState::InitializeReadOnly()
{
- if (HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Global\\ZenMap"))
- {
- m_hMapFile = hMap;
- }
- else if (HANDLE hLocalMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Local\\ZenMap"))
+ size_t MapSize = m_MaxEntryCount * sizeof(ZenServerEntry);
+
+#if ZEN_PLATFORM_WINDOWS
+ HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Global\\ZenMap");
+ if (hMap == NULL)
{
- m_hMapFile = hLocalMap;
+ hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Local\\ZenMap");
}
- else
+
+ if (hMap == NULL)
{
return false;
}
- void* pBuf = MapViewOfFile(m_hMapFile, // handle to map object
+ void* pBuf = MapViewOfFile(hMap, // handle to map object
FILE_MAP_READ, // read permission
0, // offset high
0, // offset low
- m_MaxEntryCount * sizeof(ZenServerEntry));
+ MapSize);
if (pBuf == NULL)
{
ThrowLastError("Could not map view of Zen server state");
}
+#else
+ int Fd = shm_open("UnrealEngineZen", O_RDONLY, 0666);
+ if (Fd < 0)
+ {
+ return false;
+ }
+ void* hMap = (void*)intptr_t(Fd);
- m_Data = reinterpret_cast<ZenServerEntry*>(pBuf);
+ void* pBuf = mmap(nullptr, MapSize, PROT_READ, MAP_PRIVATE, Fd, 0);
+ if (pBuf == MAP_FAILED)
+ {
+ ThrowLastError("Could not map read-only view of Zen server state");
+ }
+#endif
+
+ m_hMapFile = hMap;
+ m_Data = reinterpret_cast<ZenServerEntry*>(pBuf);
return true;
}
@@ -209,7 +260,7 @@ ZenServerState::Register(int ListenPort)
{
ZenServerEntry& Entry = m_Data[i];
- if (Entry.ListenPort.load(std::memory_order::memory_order_relaxed) == 0)
+ if (Entry.ListenPort.load(std::memory_order_relaxed) == 0)
{
uint16_t Expected = 0;
if (Entry.ListenPort.compare_exchange_strong(Expected, uint16_t(ListenPort)))
@@ -302,7 +353,7 @@ ZenServerState::ZenServerEntry::AddSponsorProcess(uint32_t PidToAdd)
{
for (std::atomic<uint32_t>& PidEntry : SponsorPids)
{
- if (PidEntry.load(std::memory_order::memory_order_relaxed) == 0)
+ if (PidEntry.load(std::memory_order_relaxed) == 0)
{
uint32_t Expected = 0;
if (PidEntry.compare_exchange_strong(Expected, PidToAdd))
@@ -311,7 +362,7 @@ ZenServerState::ZenServerEntry::AddSponsorProcess(uint32_t PidToAdd)
return true;
}
}
- else if (PidEntry.load(std::memory_order::memory_order_relaxed) == PidToAdd)
+ else if (PidEntry.load(std::memory_order_relaxed) == PidToAdd)
{
// Success, the because pid is already in the list
return true;
@@ -428,10 +479,7 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr
{
ZEN_ASSERT(!m_Process.IsValid()); // Only spawn once
- const std::filesystem::path BaseDir = m_Env.ProgramBaseDir();
- const std::filesystem::path Executable = BaseDir / "zenserver.exe";
-
- const int MyPid = _getpid();
+ const int MyPid = zen::GetCurrentProcessId();
const int ChildId = ++ChildIdCounter;
ExtendableStringBuilder<32> ChildEventName;
@@ -443,10 +491,8 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr
ExtendableStringBuilder<32> LogId;
LogId << "Zen" << ChildId;
- ExtendableWideStringBuilder<512> CommandLine;
- CommandLine << "\"";
- CommandLine.Append(Executable.c_str());
- CommandLine << "\"";
+ ExtendableStringBuilder<512> CommandLine;
+ CommandLine << "zenserver" ZEN_EXE_SUFFIX_LITERAL; // see CreateProc() call for actual binary path
const bool IsTest = m_Env.IsTestEnvironment();
@@ -476,7 +522,7 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr
if (!m_TestDir.empty())
{
CommandLine << " --data-dir ";
- CommandLine << m_TestDir.c_str();
+ PathToUtf8(m_TestDir.c_str(), CommandLine);
}
if (m_MeshEnabled)
@@ -493,87 +539,38 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr
ZEN_DEBUG("Spawning server '{}'", LogId);
- PROCESS_INFORMATION ProcessInfo{};
- STARTUPINFO StartupInfo{.cb = sizeof(STARTUPINFO)};
-
- DWORD CreationFlags = 0;
-
+ uint32_t CreationFlags = 0;
if (!IsTest)
{
- CreationFlags |= CREATE_NEW_CONSOLE;
+ CreationFlags |= CreateProcOptions::Flag_NewConsole;
}
- HANDLE hProcess = NULL;
-
+ const std::filesystem::path BaseDir = m_Env.ProgramBaseDir();
+ const std::filesystem::path Executable = BaseDir / "zenserver" ZEN_EXE_SUFFIX_LITERAL;
+ CreateProcOptions CreateOptions = {
+ .WorkingDirectory = &CurrentDirectory,
+ .Flags = CreationFlags,
+ };
+ CreateProcResult ChildPid = CreateProc(Executable, CommandLine.ToView(), CreateOptions);
+#if ZEN_PLATFORM_WINDOWS
+ if (!ChildPid && ::GetLastError() == ERROR_ELEVATION_REQUIRED)
{
- const bool InheritHandles = false;
- void* Environment = nullptr;
- LPSECURITY_ATTRIBUTES ProcessAttributes = nullptr;
- LPSECURITY_ATTRIBUTES ThreadAttributes = nullptr;
-
- BOOL Success = CreateProcessW(Executable.c_str(),
- (LPWSTR)CommandLine.c_str(),
- ProcessAttributes,
- ThreadAttributes,
- InheritHandles,
- CreationFlags,
- Environment,
- CurrentDirectory.c_str(),
- &StartupInfo,
- &ProcessInfo);
-
- if (Success)
- {
- hProcess = ProcessInfo.hProcess;
- CloseHandle(ProcessInfo.hThread);
- }
- else
- {
- DWORD WinError = ::GetLastError();
-
- if (WinError == ERROR_ELEVATION_REQUIRED)
- {
- // Try launching elevated process
-
- ZEN_DEBUG("Regular spawn failed - spawning elevated server");
-
- SHELLEXECUTEINFO ShellExecuteInfo;
- ZeroMemory(&ShellExecuteInfo, sizeof(ShellExecuteInfo));
- ShellExecuteInfo.cbSize = sizeof(ShellExecuteInfo);
- ShellExecuteInfo.fMask = SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS;
- ShellExecuteInfo.lpFile = Executable.c_str();
- ShellExecuteInfo.lpVerb = TEXT("runas");
- ShellExecuteInfo.nShow = SW_SHOW;
- ShellExecuteInfo.lpParameters = CommandLine.c_str();
-
- if (::ShellExecuteEx(&ShellExecuteInfo))
- {
- WinError = NO_ERROR;
-
- hProcess = ShellExecuteInfo.hProcess;
- }
- }
-
- if (WinError != NO_ERROR)
- {
- std::error_code err(WinError, std::system_category());
-
- ZEN_ERROR("Server spawn failed: {}", err.message());
+ ZEN_DEBUG("Regular spawn failed - spawning elevated server");
+ CreateOptions.Flags |= CreateProcOptions::Flag_Elevated;
+ ChildPid = CreateProc(Executable, CommandLine.ToView(), CreateOptions);
+ }
+#endif
- throw std::system_error(err, "failed to create server process");
- }
- }
+ if (!ChildPid)
+ {
+ ThrowLastError("Server spawn failed");
}
ZEN_DEBUG("Server '{}' spawned OK", LogId);
if (IsTest)
{
- m_Process.Initialize(hProcess);
- }
- else
- {
- CloseHandle(hProcess);
+ m_Process.Initialize(ChildPid);
}
m_ReadyEvent = std::move(ChildEvent);
@@ -637,6 +634,7 @@ ZenServerInstance::WaitUntilReady()
{
if (!m_Process.IsRunning() || !m_Process.IsValid())
{
+ ZEN_INFO("Wait abandoned by invalid process (running={})", m_Process.IsRunning());
return;
}
}