diff options
| author | Stefan Boberg <[email protected]> | 2021-08-06 18:39:13 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2021-08-06 18:39:13 +0200 |
| commit | d1fdbbecaeacf751e66d2e37e191d1eaf44660f9 (patch) | |
| tree | 58cd901cd0a66219d4c3e3066c6e2f64331e4efd | |
| parent | Repurposing test utility code to enable server control via zen (diff) | |
| download | zen-d1fdbbecaeacf751e66d2e37e191d1eaf44660f9.tar.xz zen-d1fdbbecaeacf751e66d2e37e191d1eaf44660f9.zip | |
Added support for defining test/non-test server environments
| -rw-r--r-- | zencore/filesystem.cpp | 2 | ||||
| -rw-r--r-- | zencore/include/zencore/thread.h | 2 | ||||
| -rw-r--r-- | zenserver-test/zenserver-test.cpp | 4 | ||||
| -rw-r--r-- | zenserver/config.cpp | 2 | ||||
| -rw-r--r-- | zenserver/zenserver.cpp | 2 | ||||
| -rw-r--r-- | zenutil/include/zenserverprocess.h | 24 | ||||
| -rw-r--r-- | zenutil/zenserverprocess.cpp | 152 |
7 files changed, 137 insertions, 51 deletions
diff --git a/zencore/filesystem.cpp b/zencore/filesystem.cpp index 7b123eb4e..908810773 100644 --- a/zencore/filesystem.cpp +++ b/zencore/filesystem.cpp @@ -610,7 +610,7 @@ PathFromHandle(void* NativeHandle) return FullPath; } -std::filesystem::path +std::filesystem::path GetRunningExecutablePath() { TCHAR ExePath[MAX_PATH]; diff --git a/zencore/include/zencore/thread.h b/zencore/include/zencore/thread.h index bf27d75ae..8a0adbe9f 100644 --- a/zencore/include/zencore/thread.h +++ b/zencore/include/zencore/thread.h @@ -103,7 +103,7 @@ public: }; /** Basic abstraction of a named (system wide) mutex primitive - */ + */ class NamedMutex { public: diff --git a/zenserver-test/zenserver-test.cpp b/zenserver-test/zenserver-test.cpp index 7a2031508..8ac486d05 100644 --- a/zenserver-test/zenserver-test.cpp +++ b/zenserver-test/zenserver-test.cpp @@ -658,7 +658,7 @@ main(int argc, char** argv) std::filesystem::path ProgramBaseDir = std::filesystem::path(argv[0]).parent_path(); std::filesystem::path TestBaseDir = ProgramBaseDir.parent_path().parent_path() / ".test"; - TestEnv.Initialize(ProgramBaseDir, TestBaseDir); + TestEnv.InitializeForTest(ProgramBaseDir, TestBaseDir); spdlog::info("Running tests...(base dir: '{}')", TestBaseDir); return doctest::Context(argc, argv).run(); @@ -1325,7 +1325,7 @@ TEST_CASE("exec.basic") Zen1.SpawnServer(PortNumber); Zen1.WaitUntilReady(); - std::filesystem::path TreePath = TestEnv.RootDir("test/remote1"); + std::filesystem::path TreePath = TestEnv.GetTestRootDir("test/remote1"); { RemoteExecutionRequest RemoteRequest("localhost", PortNumber, TreePath); diff --git a/zenserver/config.cpp b/zenserver/config.cpp index 588f3ddd1..79433f20c 100644 --- a/zenserver/config.cpp +++ b/zenserver/config.cpp @@ -82,7 +82,7 @@ ParseGlobalCliOptions(int argc, char* argv[], ZenServerOptions& GlobalOptions, Z "<port number>"); options.add_option("network", - "m", + "m", "mesh", "Enable mesh network", cxxopts::value<bool>(ServiceConfig.MeshEnabled)->default_value("true"), diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp index 307cb75ee..bf1528d98 100644 --- a/zenserver/zenserver.cpp +++ b/zenserver/zenserver.cpp @@ -12,12 +12,12 @@ #include <zenstore/cas.h> #include <zenstore/cidstore.h> -#include <exception> #include <fmt/format.h> #include <mimalloc-new-delete.h> #include <mimalloc.h> #include <spdlog/spdlog.h> #include <asio.hpp> +#include <exception> #include <list> #include <lua.hpp> #include <optional> diff --git a/zenutil/include/zenserverprocess.h b/zenutil/include/zenserverprocess.h index fbe48abe3..239841a04 100644 --- a/zenutil/include/zenserverprocess.h +++ b/zenutil/include/zenserverprocess.h @@ -14,17 +14,20 @@ public: ZenServerEnvironment(); ~ZenServerEnvironment(); - void Initialize(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir); + void Initialize(std::filesystem::path ProgramBaseDir); + void InitializeForTest(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir); std::filesystem::path CreateNewTestDir(); std::filesystem::path ProgramBaseDir() const { return m_ProgramBaseDir; } - std::filesystem::path RootDir(std::string_view Path); - bool IsInitialized() const { return m_IsInitialized; } + std::filesystem::path GetTestRootDir(std::string_view Path); + inline bool IsInitialized() const { return m_IsInitialized; } + inline bool IsTestEnvironment() const { return m_IsTestInstance; } private: std::filesystem::path m_ProgramBaseDir; std::filesystem::path m_TestBaseDir; - bool m_IsInitialized = false; + bool m_IsInitialized = false; + bool m_IsTestInstance = false; }; struct ZenServerInstance @@ -32,10 +35,11 @@ struct ZenServerInstance ZenServerInstance(ZenServerEnvironment& TestEnvironment); ~ZenServerInstance(); - void SignalShutdown() { m_ShutdownEvent.Set(); } - void WaitUntilReady() { m_ReadyEvent.Wait(); } - void EnableTermination() { m_Terminate = true; } - void EnableMesh() { m_MeshEnabled = true; } + void SignalShutdown() { m_ShutdownEvent.Set(); } + void WaitUntilReady(); + [[nodiscard]] bool WaitUntilReady(int Timeout); + void EnableTermination() { m_Terminate = true; } + void EnableMesh() { m_MeshEnabled = true; } void SetTestDir(std::filesystem::path TestDir) { @@ -43,10 +47,10 @@ struct ZenServerInstance m_TestDir = TestDir; } - void SpawnServer(int BasePort); + void SpawnServer(int BasePort = 0); private: - ZenServerEnvironment& m_Env; + ZenServerEnvironment& m_Env; zen::ProcessHandle m_Process; zen::Event m_ReadyEvent; zen::Event m_ShutdownEvent; diff --git a/zenutil/zenserverprocess.cpp b/zenutil/zenserverprocess.cpp index f58a9f166..9d38d3939 100644 --- a/zenutil/zenserverprocess.cpp +++ b/zenutil/zenserverprocess.cpp @@ -6,6 +6,7 @@ #include <zencore/fmtutils.h> #include <zencore/string.h> +#include <shellapi.h> #include <spdlog/spdlog.h> ////////////////////////////////////////////////////////////////////////// @@ -21,7 +22,17 @@ ZenServerEnvironment::~ZenServerEnvironment() } void -ZenServerEnvironment::Initialize(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir) +ZenServerEnvironment::Initialize(std::filesystem::path ProgramBaseDir) +{ + m_ProgramBaseDir = ProgramBaseDir; + + spdlog::debug("Program base dir is '{}'", ProgramBaseDir); + + m_IsInitialized = true; +} + +void +ZenServerEnvironment::InitializeForTest(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir) { m_ProgramBaseDir = ProgramBaseDir; m_TestBaseDir = TestBaseDir; @@ -30,7 +41,8 @@ ZenServerEnvironment::Initialize(std::filesystem::path ProgramBaseDir, std::file spdlog::info("Cleaning test base dir '{}'", TestBaseDir); zen::DeleteDirectories(TestBaseDir.c_str()); - m_IsInitialized = true; + m_IsTestInstance = true; + m_IsInitialized = true; } std::filesystem::path @@ -51,7 +63,7 @@ ZenServerEnvironment::CreateNewTestDir() } std::filesystem::path -ZenServerEnvironment::RootDir(std::string_view Path) +ZenServerEnvironment::GetTestRootDir(std::string_view Path) { std::filesystem::path Root = m_ProgramBaseDir.parent_path().parent_path(); @@ -112,12 +124,21 @@ ZenServerInstance::SpawnServer(int BasePort) zen::ExtendableWideStringBuilder<128> CommandLine; CommandLine << "\""; CommandLine.Append(Executable.c_str()); - CommandLine << "\" --test --owner-pid "; - CommandLine << MyPid; - CommandLine << " "; - CommandLine << "--port " << BasePort; + CommandLine << "\""; + + const bool IsTest = m_Env.IsTestEnvironment(); + + if (IsTest) + { + CommandLine << " --test --owner-pid " << MyPid << " --log-id " << LogId; + } + CommandLine << " --child-id " << ChildEventName; - CommandLine << " --log-id " << LogId; + + if (BasePort) + { + CommandLine << " --port " << BasePort; + } if (!m_TestDir.empty()) { @@ -135,39 +156,100 @@ ZenServerInstance::SpawnServer(int BasePort) spdlog::debug("Spawning server"); PROCESS_INFORMATION ProcessInfo{}; - STARTUPINFO Sinfo{.cb = sizeof(STARTUPINFO)}; - - DWORD CreationFlags = 0; // CREATE_NEW_CONSOLE; - 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(), - &Sinfo, - &ProcessInfo); - - if (Success == FALSE) + STARTUPINFO StartupInfo{.cb = sizeof(STARTUPINFO)}; + + DWORD CreationFlags = 0; + + if (!IsTest) + { + CreationFlags |= CREATE_NEW_CONSOLE; + } + + HANDLE hProcess = NULL; + { - std::error_code err(::GetLastError(), std::system_category()); + 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 - spdlog::error("Server spawn failed: {}", err.message()); + spdlog::debug("Regular spawn failed - spawning elevated server"); - throw std::system_error(err, "failed to create server process"); + 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()); + + spdlog::error("Server spawn failed: {}", err.message()); + + throw std::system_error(err, "failed to create server process"); + } + } } spdlog::debug("Server spawned OK"); - CloseHandle(ProcessInfo.hThread); + if (IsTest) + { + m_Process.Initialize(hProcess); + m_ShutdownEvent = std::move(ChildShutdownEvent); + } + else + { + CloseHandle(hProcess); + } + + m_ReadyEvent = std::move(ChildEvent); +} - m_Process.Initialize(ProcessInfo.hProcess); - m_ReadyEvent = std::move(ChildEvent); - m_ShutdownEvent = std::move(ChildShutdownEvent); +void +ZenServerInstance::WaitUntilReady() +{ + m_ReadyEvent.Wait(); +} + +bool +ZenServerInstance::WaitUntilReady(int Timeout) +{ + return m_ReadyEvent.Wait(Timeout); } |