aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenutil')
-rw-r--r--src/zenutil/commandlineoptions.cpp27
-rw-r--r--src/zenutil/consul/consul.cpp140
-rw-r--r--src/zenutil/include/zenutil/commandlineoptions.h13
-rw-r--r--src/zenutil/include/zenutil/consul.h47
-rw-r--r--src/zenutil/include/zenutil/logging/rotatingfilesink.h3
-rw-r--r--src/zenutil/include/zenutil/zenserverprocess.h35
-rw-r--r--src/zenutil/service.cpp54
-rw-r--r--src/zenutil/zenserverprocess.cpp120
8 files changed, 385 insertions, 54 deletions
diff --git a/src/zenutil/commandlineoptions.cpp b/src/zenutil/commandlineoptions.cpp
index 81699361b..d94564843 100644
--- a/src/zenutil/commandlineoptions.cpp
+++ b/src/zenutil/commandlineoptions.cpp
@@ -2,7 +2,11 @@
#include <zenutil/commandlineoptions.h>
+#include <zencore/string.h>
#include <filesystem>
+
+#include <zencore/windows.h>
+
#if ZEN_WITH_TESTS
# include <zencore/testing.h>
#endif // ZEN_WITH_TESTS
@@ -160,6 +164,29 @@ RemoveQuotes(const std::string_view& Arg)
return Arg;
}
+CommandLineConverter::CommandLineConverter(int& argc, char**& argv)
+{
+#if ZEN_PLATFORM_WINDOWS
+ LPWSTR RawCommandLine = GetCommandLineW();
+ std::string CommandLine = WideToUtf8(RawCommandLine);
+ Args = ParseCommandLine(CommandLine);
+#else
+ Args.reserve(argc);
+ for (int I = 0; I < argc; I++)
+ {
+ std::string Arg(argv[I]);
+ if ((!Arg.empty()) && (Arg != " "))
+ {
+ Args.emplace_back(std::move(Arg));
+ }
+ }
+#endif
+ RawArgs = StripCommandlineQuotes(Args);
+
+ argc = static_cast<int>(RawArgs.size());
+ argv = RawArgs.data();
+}
+
#if ZEN_WITH_TESTS
void
diff --git a/src/zenutil/consul/consul.cpp b/src/zenutil/consul/consul.cpp
new file mode 100644
index 000000000..6ddebf97a
--- /dev/null
+++ b/src/zenutil/consul/consul.cpp
@@ -0,0 +1,140 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenutil/consul.h>
+
+#include <zencore/except_fmt.h>
+#include <zencore/fmtutils.h>
+#include <zencore/logging.h>
+#include <zencore/process.h>
+#include <zencore/string.h>
+#include <zencore/timer.h>
+
+#include <fmt/format.h>
+
+namespace zen::consul {
+
+//////////////////////////////////////////////////////////////////////////
+
+struct ConsulProcess::Impl
+{
+ Impl(std::string_view BaseUri) : m_HttpClient(BaseUri) {}
+ ~Impl() = default;
+
+ void SpawnConsulAgent()
+ {
+ if (m_ProcessHandle.IsValid())
+ {
+ return;
+ }
+
+ CreateProcOptions Options;
+ Options.Flags |= CreateProcOptions::Flag_Windows_NewProcessGroup;
+
+ CreateProcResult Result = CreateProc("consul" ZEN_EXE_SUFFIX_LITERAL, "consul" ZEN_EXE_SUFFIX_LITERAL " agent -dev", Options);
+
+ if (Result)
+ {
+ m_ProcessHandle.Initialize(Result);
+
+ Stopwatch Timer;
+
+ // Poll to check when the agent is ready
+
+ do
+ {
+ Sleep(100);
+ HttpClient::Response Resp = m_HttpClient.Get("v1/status/leader");
+ if (Resp)
+ {
+ ZEN_INFO("Consul agent started successfully (waited {})", NiceTimeSpanMs(Timer.GetElapsedTimeMs()));
+
+ return;
+ }
+ } while (Timer.GetElapsedTimeMs() < 10000);
+ }
+
+ // Report failure!
+
+ ZEN_WARN("Consul agent failed to start within timeout period");
+ }
+
+ void StopConsulAgent()
+ {
+ if (!m_ProcessHandle.IsValid())
+ {
+ return;
+ }
+
+ // This waits for the process to exit and also resets the handle
+ m_ProcessHandle.Kill();
+ }
+
+private:
+ ProcessHandle m_ProcessHandle;
+ HttpClient m_HttpClient;
+};
+
+ConsulProcess::ConsulProcess() : m_Impl(std::make_unique<Impl>("http://localhost:8500/"))
+{
+}
+
+ConsulProcess::~ConsulProcess()
+{
+}
+
+void
+ConsulProcess::SpawnConsulAgent()
+{
+ m_Impl->SpawnConsulAgent();
+}
+
+void
+ConsulProcess::StopConsulAgent()
+{
+ m_Impl->StopConsulAgent();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+ConsulClient::ConsulClient(std::string_view BaseUri) : m_HttpClient(BaseUri)
+{
+}
+
+ConsulClient::~ConsulClient()
+{
+}
+
+void
+ConsulClient::SetKeyValue(std::string_view Key, std::string_view Value)
+{
+ IoBuffer ValueBuffer = IoBufferBuilder::MakeFromMemory(MakeMemoryView(Value));
+ HttpClient::Response Result =
+ m_HttpClient.Put(fmt::format("v1/kv/{}", Key), ValueBuffer, {{"Content-Type", "text/plain"}, {"Accept", "application/json"}});
+ if (!Result)
+ {
+ throw runtime_error("ConsulClient::SetKeyValue() failed to set key '{}' ({})", Key, Result.ErrorMessage(""));
+ }
+}
+
+std::string
+ConsulClient::GetKeyValue(std::string_view Key)
+{
+ HttpClient::Response Result = m_HttpClient.Get(fmt::format("v1/kv/{}?raw", Key));
+ if (!Result)
+ {
+ throw runtime_error("ConsulClient::GetKeyValue() failed to get key '{}' ({})", Key, Result.ErrorMessage(""));
+ }
+ return Result.ToText();
+}
+
+void
+ConsulClient::DeleteKey(std::string_view Key)
+{
+ HttpClient::Response Result = m_HttpClient.Delete(fmt::format("v1/kv/{}", Key));
+ if (!Result)
+ {
+ throw runtime_error("ConsulClient::DeleteKey() failed to delete key '{}' ({})", Key, Result.ErrorMessage(""));
+ }
+}
+
+} // namespace zen::consul
diff --git a/src/zenutil/include/zenutil/commandlineoptions.h b/src/zenutil/include/zenutil/commandlineoptions.h
index d6a171242..01cceedb1 100644
--- a/src/zenutil/include/zenutil/commandlineoptions.h
+++ b/src/zenutil/include/zenutil/commandlineoptions.h
@@ -22,6 +22,19 @@ std::vector<char*> StripCommandlineQuotes(std::vector<std::string>& InOutArgs)
std::filesystem::path StringToPath(const std::string_view& Path);
std::string_view RemoveQuotes(const std::string_view& Arg);
+class CommandLineConverter
+{
+public:
+ CommandLineConverter(int& argc, char**& argv);
+
+ int ArgC = 0;
+ char** ArgV = nullptr;
+
+private:
+ std::vector<std::string> Args;
+ std::vector<char*> RawArgs;
+};
+
void commandlineoptions_forcelink(); // internal
} // namespace zen
diff --git a/src/zenutil/include/zenutil/consul.h b/src/zenutil/include/zenutil/consul.h
new file mode 100644
index 000000000..08871fa66
--- /dev/null
+++ b/src/zenutil/include/zenutil/consul.h
@@ -0,0 +1,47 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <zenbase/zenbase.h>
+#include <zenhttp/httpclient.h>
+
+#include <string>
+#include <string_view>
+
+namespace zen::consul {
+
+class ConsulClient
+{
+public:
+ ConsulClient(std::string_view BaseUri);
+ ~ConsulClient();
+
+ ConsulClient(const ConsulClient&) = delete;
+ ConsulClient& operator=(const ConsulClient&) = delete;
+
+ void SetKeyValue(std::string_view Key, std::string_view Value);
+ std::string GetKeyValue(std::string_view Key);
+ void DeleteKey(std::string_view Key);
+
+private:
+ HttpClient m_HttpClient;
+};
+
+class ConsulProcess
+{
+public:
+ ConsulProcess();
+ ~ConsulProcess();
+
+ ConsulProcess(const ConsulProcess&) = delete;
+ ConsulProcess& operator=(const ConsulProcess&) = delete;
+
+ void SpawnConsulAgent();
+ void StopConsulAgent();
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> m_Impl;
+};
+
+} // namespace zen::consul
diff --git a/src/zenutil/include/zenutil/logging/rotatingfilesink.h b/src/zenutil/include/zenutil/logging/rotatingfilesink.h
index 4d10f3794..8901b7779 100644
--- a/src/zenutil/include/zenutil/logging/rotatingfilesink.h
+++ b/src/zenutil/include/zenutil/logging/rotatingfilesink.h
@@ -11,6 +11,7 @@ ZEN_THIRD_PARTY_INCLUDES_START
#include <spdlog/sinks/sink.h>
ZEN_THIRD_PARTY_INCLUDES_END
+#include <atomic>
#include <filesystem>
namespace zen::logging {
@@ -248,7 +249,7 @@ private:
const std::size_t m_MaxSize;
const std::size_t m_MaxFiles;
BasicFile m_CurrentFile;
- bool m_NeedFlush = false;
+ std::atomic<bool> m_NeedFlush = false;
};
} // namespace zen::logging
diff --git a/src/zenutil/include/zenutil/zenserverprocess.h b/src/zenutil/include/zenutil/zenserverprocess.h
index 0da63285b..d0402640b 100644
--- a/src/zenutil/include/zenutil/zenserverprocess.h
+++ b/src/zenutil/include/zenutil/zenserverprocess.h
@@ -34,8 +34,10 @@ public:
void Initialize(std::filesystem::path ProgramBaseDir);
void InitializeForTest(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir, std::string_view ServerClass = "");
+ void InitializeForHub(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir, std::string_view ServerClass = "");
std::filesystem::path CreateNewTestDir();
+ std::filesystem::path CreateChildDir(std::string_view ChildName);
std::filesystem::path ProgramBaseDir() const { return m_ProgramBaseDir; }
std::filesystem::path GetTestRootDir(std::string_view Path);
inline bool IsInitialized() const { return m_IsInitialized; }
@@ -43,11 +45,18 @@ public:
inline std::string_view GetServerClass() const { return m_ServerClass; }
inline uint16_t GetNewPortNumber() { return m_NextPortNumber.fetch_add(1); }
+ // The defaults will work for a single root process only. For hierarchical
+ // setups (e.g., hub managing storage servers), we need to be able to
+ // allocate distinct child IDs and ports to avoid overlap/conflicts.
+ static void SetBaseChildId(int InitialValue);
+ void SetNextPortNumber(uint16_t NewValue) { m_NextPortNumber = NewValue; }
+
private:
std::filesystem::path m_ProgramBaseDir;
- std::filesystem::path m_TestBaseDir;
+ std::filesystem::path m_ChildProcessBaseDir;
bool m_IsInitialized = false;
bool m_IsTestInstance = false;
+ bool m_IsHubInstance = false;
std::string m_ServerClass;
std::atomic_uint16_t m_NextPortNumber{20000};
};
@@ -60,10 +69,19 @@ private:
Especially useful for automated testing but can also be used for
management tools.
+ This is also used by zenserver in hub mode, for managing storage
+ server instances.
+
*/
struct ZenServerInstance
{
- ZenServerInstance(ZenServerEnvironment& TestEnvironment);
+ enum class ServerMode
+ {
+ kStorageServer, // default
+ kHubServer,
+ };
+
+ ZenServerInstance(ZenServerEnvironment& TestEnvironment, ServerMode Mode = ServerMode::kStorageServer);
~ZenServerInstance();
int Shutdown();
@@ -72,6 +90,7 @@ struct ZenServerInstance
[[nodiscard]] bool WaitUntilReady(int Timeout);
[[nodiscard]] bool WaitUntilExited(int Timeout, std::error_code& OutEc);
void EnableTermination() { m_Terminate = true; }
+ void EnableShutdownOnDestroy() { m_ShutdownOnDestroy = true; }
void DisableShutdownOnDestroy() { m_ShutdownOnDestroy = false; }
void Detach();
inline int GetPid() const { return m_Process.Pid(); }
@@ -81,7 +100,11 @@ struct ZenServerInstance
bool Terminate();
std::string GetLogOutput() const;
- void SetTestDir(std::filesystem::path TestDir);
+ inline ServerMode GetServerMode() const { return m_ServerMode; }
+
+ inline void SetServerExecutablePath(std::filesystem::path ExecutablePath) { m_ServerExecutablePath = ExecutablePath; }
+
+ void SetDataDir(std::filesystem::path TestDir);
inline void SpawnServer(std::string_view AdditionalServerArgs = std::string_view())
{
@@ -117,11 +140,13 @@ private:
std::unique_ptr<NamedEvent> m_ShutdownEvent;
bool m_Terminate = false;
bool m_ShutdownOnDestroy = true;
- std::filesystem::path m_TestDir;
- uint16_t m_BasePort = 0;
+ std::filesystem::path m_DataDir;
+ uint16_t m_BasePort = 0;
+ ServerMode m_ServerMode = ServerMode::kStorageServer;
std::optional<int> m_OwnerPid;
std::string m_Name;
std::filesystem::path m_OutputCapturePath;
+ std::filesystem::path m_ServerExecutablePath;
void CreateShutdownEvent(int BasePort);
void SpawnServer(int BasePort, std::string_view AdditionalServerArgs, int WaitTimeoutMs);
diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp
index 103fdaa2f..f2a925e61 100644
--- a/src/zenutil/service.cpp
+++ b/src/zenutil/service.cpp
@@ -229,12 +229,13 @@ namespace {
std::filesystem::path GetPListPath(const std::string& DaemonName)
{
- const std::filesystem::path PListFolder = "/Library/LaunchDaemons";
+ char* HomeDir = getenv("HOME");
+ std::filesystem::path PListFolder = HomeDir;
+ PListFolder /= "Library/LaunchAgents";
return PListFolder / (DaemonName + ".plist");
}
- std::string BuildPlist(std::string_view ServiceName,
- const std::filesystem::path& ExecutablePath,
+ std::string BuildPlist(const std::filesystem::path& ExecutablePath,
std::string_view CommandLineOptions,
std::string_view DaemonName,
bool Debug)
@@ -265,14 +266,8 @@ namespace {
" <key>RunAtLoad</key>\n"
" <true/>\n"
" \n"
- // " <key>KeepAlive</key>\n"
- // " <true/>\n"
- // " \n"
- " <key>StandardOutPath</key>\n"
- " <string>/var/log/{}.log</string>\n"
- " \n"
- " <key>StandardErrorPath</key>\n"
- " <string>/var/log/{}.err.log</string>\n"
+ " <key>KeepAlive</key>\n"
+ " <true/>\n"
" \n"
" <key>Debug</key>\n"
" <{}/>\n"
@@ -282,22 +277,7 @@ namespace {
DaemonName,
ExecutablePath,
ProgramArguments.ToView(),
- ServiceName,
- ServiceName,
Debug ? "true"sv : "false"sv);
-
- // "<key>Sockets</key>"
- // "<dict>"
- // "<key>Listeners</key>"
- // "<dict>"
- // "<key>SockServiceName</key>"
- // "<string>{}</string>" // Listen socket
- // "<key>SockType</key>"
- // "<string>tcp</string>"
- // "<key>SockFamily</key>"
- // "<string>IPv4</string>"
- // "</dict>"
- // "</dict>"
}
#endif // ZEN_PLATFORM_MAC
@@ -752,9 +732,8 @@ StopService(std::string_view ServiceName)
std::error_code
InstallService(std::string_view ServiceName, const ServiceSpec& Spec)
{
- // TODO: Do we need to create a separate user for the service or is running as the default service user OK?
const std::string DaemonName = GetDaemonName(ServiceName);
- std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true);
+ std::string PList = BuildPlist(Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true);
const std::filesystem::path PListPath = GetPListPath(DaemonName);
ZEN_INFO("Writing launchd plist to {}", PListPath.string());
@@ -910,8 +889,14 @@ StartService(std::string_view ServiceName)
{
const std::string DaemonName = GetDaemonName(ServiceName);
const std::filesystem::path PListPath = GetPListPath(DaemonName);
+ std::pair<int, std::string> User = ExecuteProgram("id -u");
+
+ if (User.first != 0)
+ {
+ return MakeErrorCode(User.first);
+ }
- std::pair<int, std::string> Res = ExecuteProgram(fmt::format("launchctl bootstrap system {}", PListPath));
+ std::pair<int, std::string> Res = ExecuteProgram(fmt::format("launchctl bootstrap gui/{} {}", User.second, PListPath));
if (Res.first != 0)
{
return MakeErrorCode(Res.first);
@@ -926,13 +911,18 @@ StopService(std::string_view ServiceName)
const std::string DaemonName = GetDaemonName(ServiceName);
const std::filesystem::path PListPath = GetPListPath(DaemonName);
- /*
- std::pair<int, std::string> Res = ExecuteProgram(fmt::format("launchctl bootout system ", PListPath.));
+ std::pair<int, std::string> User = ExecuteProgram("id -u");
+
+ if (User.first != 0)
+ {
+ return MakeErrorCode(User.first);
+ }
+
+ std::pair<int, std::string> Res = ExecuteProgram(fmt::format("launchctl bootout gui/{} {}", User.second, PListPath));
if (Res.first != 0)
{
return MakeErrorCode(Res.first);
}
- */
return {};
}
diff --git a/src/zenutil/zenserverprocess.cpp b/src/zenutil/zenserverprocess.cpp
index 6a93f0c63..ef2a4fda5 100644
--- a/src/zenutil/zenserverprocess.cpp
+++ b/src/zenutil/zenserverprocess.cpp
@@ -31,6 +31,19 @@
namespace zen {
+// this needs to key off the current process child-id, in order to avoid conflicts
+// in situations where we have a tree of zenserver child processes (such as in hub
+// tests)
+
+std::atomic<int> ChildIdCounter{0};
+
+void
+ZenServerEnvironment::SetBaseChildId(int InitialValue)
+{
+ ZEN_ASSERT(ChildIdCounter == 0);
+ ChildIdCounter = InitialValue;
+}
+
namespace zenutil {
#if ZEN_PLATFORM_WINDOWS
class SecurityAttributes
@@ -507,8 +520,8 @@ ZenServerEnvironment::InitializeForTest(std::filesystem::path ProgramBaseDir,
{
using namespace std::literals;
- m_ProgramBaseDir = ProgramBaseDir;
- m_TestBaseDir = TestBaseDir;
+ m_ProgramBaseDir = ProgramBaseDir;
+ m_ChildProcessBaseDir = TestBaseDir;
ZEN_INFO("Program base dir is '{}'", ProgramBaseDir);
ZEN_INFO("Cleaning test base dir '{}'", TestBaseDir);
@@ -536,6 +549,59 @@ ZenServerEnvironment::InitializeForTest(std::filesystem::path ProgramBaseDir,
}
}
+void
+ZenServerEnvironment::InitializeForHub(std::filesystem::path ProgramBaseDir,
+ std::filesystem::path ChildBaseDir,
+ std::string_view ServerClass)
+{
+ using namespace std::literals;
+
+ m_ProgramBaseDir = ProgramBaseDir;
+ m_ChildProcessBaseDir = ChildBaseDir;
+
+ ZEN_INFO("Program base dir is '{}'", m_ProgramBaseDir);
+ ZEN_INFO("Cleaning child base dir '{}'", m_ChildProcessBaseDir);
+ DeleteDirectories(m_ChildProcessBaseDir.c_str());
+
+ m_IsHubInstance = true;
+ m_IsInitialized = true;
+
+ if (ServerClass.empty())
+ {
+#if ZEN_WITH_HTTPSYS
+ if (!zen::windows::IsRunningOnWine())
+ {
+ m_ServerClass = "httpsys"sv;
+
+ return;
+ }
+#endif
+
+ m_ServerClass = "asio"sv;
+ }
+ else
+ {
+ m_ServerClass = ServerClass;
+ }
+}
+
+std::filesystem::path
+ZenServerEnvironment::CreateChildDir(std::string_view ChildName)
+{
+ using namespace std::literals;
+
+ std::filesystem::path ChildPath = m_ChildProcessBaseDir / ChildName;
+
+ if (!IsDir(ChildPath))
+ {
+ ZEN_INFO("Creating new test dir @ '{}'", ChildPath);
+
+ CreateDirectories(ChildPath);
+ }
+
+ return ChildPath;
+}
+
std::filesystem::path
ZenServerEnvironment::CreateNewTestDir()
{
@@ -544,7 +610,7 @@ ZenServerEnvironment::CreateNewTestDir()
ExtendableWideStringBuilder<256> TestDir;
TestDir << "test"sv << int64_t(ZenServerTestCounter.fetch_add(1));
- std::filesystem::path TestPath = m_TestBaseDir / TestDir.c_str();
+ std::filesystem::path TestPath = m_ChildProcessBaseDir / TestDir.c_str();
ZEN_ASSERT(!IsDir(TestPath));
ZEN_INFO("Creating new test dir @ '{}'", TestPath);
@@ -566,11 +632,11 @@ ZenServerEnvironment::GetTestRootDir(std::string_view Path)
//////////////////////////////////////////////////////////////////////////
-std::atomic<int> ChildIdCounter{0};
-
-ZenServerInstance::ZenServerInstance(ZenServerEnvironment& TestEnvironment) : m_Env(TestEnvironment)
+ZenServerInstance::ZenServerInstance(ZenServerEnvironment& TestEnvironment, ServerMode Mode) : m_Env(TestEnvironment), m_ServerMode(Mode)
{
ZEN_ASSERT(TestEnvironment.IsInitialized());
+
+ m_ServerMode = Mode;
}
ZenServerInstance::~ZenServerInstance()
@@ -632,7 +698,7 @@ ZenServerInstance::Shutdown()
{
Stopwatch Timer;
ZEN_DEBUG("Waiting for zenserver process {} ({}) to shut down", m_Name, m_Process.Pid());
- while (!m_Process.Wait(1000))
+ while (!m_Process.Wait(2000))
{
if (!m_Process.IsValid())
{
@@ -710,6 +776,22 @@ ZenServerInstance::SpawnServer(std::string_view ServerArgs, bool OpenConsole, in
SpawnServerInternal(ChildId, ServerArgs, OpenConsole, WaitTimeoutMs);
}
+std::string_view
+ToString(ZenServerInstance::ServerMode Mode)
+{
+ using namespace std::literals;
+
+ switch (Mode)
+ {
+ case ZenServerInstance::ServerMode::kStorageServer:
+ return "storage"sv;
+ case ZenServerInstance::ServerMode::kHubServer:
+ return "hub"sv;
+ default:
+ return "invalid"sv;
+ }
+}
+
void
ZenServerInstance::SpawnServerInternal(int ChildId, std::string_view ServerArgs, bool OpenConsole, int WaitTimeoutMs)
{
@@ -721,6 +803,12 @@ ZenServerInstance::SpawnServerInternal(int ChildId, std::string_view ServerArgs,
ExtendableStringBuilder<512> CommandLine;
CommandLine << "zenserver" ZEN_EXE_SUFFIX_LITERAL; // see CreateProc() call for actual binary path
+
+ if (m_ServerMode == ServerMode::kHubServer)
+ {
+ CommandLine << " hub";
+ }
+
CommandLine << " --child-id " << ChildEventName;
if (!ServerArgs.empty())
@@ -730,7 +818,7 @@ ZenServerInstance::SpawnServerInternal(int ChildId, std::string_view ServerArgs,
std::filesystem::path CurrentDirectory = std::filesystem::current_path();
- ZEN_DEBUG("Spawning server '{}'", m_Name);
+ ZEN_DEBUG("Spawning {} server '{}'", ToString(m_ServerMode), m_Name);
uint32_t CreationFlags = 0;
if (OpenConsole)
@@ -738,8 +826,9 @@ ZenServerInstance::SpawnServerInternal(int ChildId, std::string_view ServerArgs,
CreationFlags |= CreateProcOptions::Flag_NewConsole;
}
- const std::filesystem::path BaseDir = m_Env.ProgramBaseDir();
- const std::filesystem::path Executable = BaseDir / "zenserver" ZEN_EXE_SUFFIX_LITERAL;
+ const std::filesystem::path BaseDir = m_Env.ProgramBaseDir();
+ const std::filesystem::path Executable =
+ m_ServerExecutablePath.empty() ? (BaseDir / "zenserver" ZEN_EXE_SUFFIX_LITERAL) : m_ServerExecutablePath;
const std::filesystem::path OutputPath =
OpenConsole ? std::filesystem::path{} : std::filesystem::temp_directory_path() / ("zenserver_" + m_Name + ".log");
CreateProcOptions CreateOptions = {.WorkingDirectory = &CurrentDirectory, .Flags = CreationFlags, .StdoutFile = OutputPath};
@@ -799,8 +888,7 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr
const int ChildId = AssignName();
ExtendableStringBuilder<512> CommandLine;
-
- const bool IsTest = m_Env.IsTestEnvironment();
+ const bool IsTest = m_Env.IsTestEnvironment();
if (IsTest)
{
@@ -835,10 +923,10 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr
m_BasePort = gsl::narrow_cast<uint16_t>(BasePort);
}
- if (!m_TestDir.empty())
+ if (!m_DataDir.empty())
{
CommandLine << " --data-dir ";
- PathToUtf8(m_TestDir.c_str(), CommandLine);
+ PathToUtf8(m_DataDir.c_str(), CommandLine);
}
if (!AdditionalServerArgs.empty())
@@ -1028,10 +1116,10 @@ ZenServerInstance::GetBaseUri() const
}
void
-ZenServerInstance::SetTestDir(std::filesystem::path TestDir)
+ZenServerInstance::SetDataDir(std::filesystem::path TestDir)
{
ZEN_ASSERT(!m_Process.IsValid());
- m_TestDir = TestDir;
+ m_DataDir = TestDir;
}
bool