aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-08-06 18:39:13 +0200
committerStefan Boberg <[email protected]>2021-08-06 18:39:13 +0200
commitd1fdbbecaeacf751e66d2e37e191d1eaf44660f9 (patch)
tree58cd901cd0a66219d4c3e3066c6e2f64331e4efd
parentRepurposing test utility code to enable server control via zen (diff)
downloadzen-d1fdbbecaeacf751e66d2e37e191d1eaf44660f9.tar.xz
zen-d1fdbbecaeacf751e66d2e37e191d1eaf44660f9.zip
Added support for defining test/non-test server environments
-rw-r--r--zencore/filesystem.cpp2
-rw-r--r--zencore/include/zencore/thread.h2
-rw-r--r--zenserver-test/zenserver-test.cpp4
-rw-r--r--zenserver/config.cpp2
-rw-r--r--zenserver/zenserver.cpp2
-rw-r--r--zenutil/include/zenserverprocess.h24
-rw-r--r--zenutil/zenserverprocess.cpp152
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);
}