diff options
| author | Per Larsson <[email protected]> | 2021-12-14 12:34:47 +0100 |
|---|---|---|
| committer | Per Larsson <[email protected]> | 2021-12-14 12:34:47 +0100 |
| commit | b6c6568e1618f10d2160d836b65e35586e3c740f (patch) | |
| tree | f6a929cf918850bbba87d0ee67cd3482b2d50e24 /zenutil/zenserverprocess.cpp | |
| parent | Fixed bug in z$ service returning partial cache records and enable small obje... (diff) | |
| parent | Partial revert b363c5b (diff) | |
| download | zen-b6c6568e1618f10d2160d836b65e35586e3c740f.tar.xz zen-b6c6568e1618f10d2160d836b65e35586e3c740f.zip | |
Merged main.
Diffstat (limited to 'zenutil/zenserverprocess.cpp')
| -rw-r--r-- | zenutil/zenserverprocess.cpp | 222 |
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; } } |