aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/config.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-05-02 10:01:47 +0200
committerGitHub <[email protected]>2023-05-02 10:01:47 +0200
commit075d17f8ada47e990fe94606c3d21df409223465 (patch)
treee50549b766a2f3c354798a54ff73404217b4c9af /src/zenserver/config.cpp
parentfix: bundle shouldn't append content zip to zen (diff)
downloadzen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz
zen-075d17f8ada47e990fe94606c3d21df409223465.zip
moved source directories into `/src` (#264)
* moved source directories into `/src` * updated bundle.lua for new `src` path * moved some docs, icon * removed old test trees
Diffstat (limited to 'src/zenserver/config.cpp')
-rw-r--r--src/zenserver/config.cpp902
1 files changed, 902 insertions, 0 deletions
diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp
new file mode 100644
index 000000000..cff93d67b
--- /dev/null
+++ b/src/zenserver/config.cpp
@@ -0,0 +1,902 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "config.h"
+
+#include "diag/logging.h"
+
+#include <zencore/crypto.h>
+#include <zencore/fmtutils.h>
+#include <zencore/iobuffer.h>
+#include <zencore/string.h>
+#include <zenhttp/zenhttp.h>
+
+ZEN_THIRD_PARTY_INCLUDES_START
+#include <fmt/format.h>
+#include <zencore/logging.h>
+#include <cxxopts.hpp>
+#include <sol/sol.hpp>
+ZEN_THIRD_PARTY_INCLUDES_END
+
+#if ZEN_PLATFORM_WINDOWS
+# include <conio.h>
+#else
+# include <pwd.h>
+#endif
+
+#if ZEN_PLATFORM_WINDOWS
+
+// Used for getting My Documents for default data directory
+# include <ShlObj.h>
+# pragma comment(lib, "shell32.lib")
+
+std::filesystem::path
+PickDefaultStateDirectory()
+{
+ // Pick sensible default
+ PWSTR programDataDir = nullptr;
+ HRESULT hRes = SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &programDataDir);
+
+ if (SUCCEEDED(hRes))
+ {
+ std::filesystem::path finalPath(programDataDir);
+ finalPath /= L"Epic\\Zen\\Data";
+ ::CoTaskMemFree(programDataDir);
+
+ return finalPath;
+ }
+
+ return L"";
+}
+
+#else
+
+std::filesystem::path
+PickDefaultStateDirectory()
+{
+ int UserId = getuid();
+ const passwd* Passwd = getpwuid(UserId);
+ return std::filesystem::path(Passwd->pw_dir) / ".zen";
+}
+
+#endif
+
+void
+ValidateOptions(ZenServerOptions& ServerOptions)
+{
+ if (ServerOptions.EncryptionKey.empty() == false)
+ {
+ const auto Key = zen::AesKey256Bit::FromString(ServerOptions.EncryptionKey);
+
+ if (Key.IsValid() == false)
+ {
+ throw cxxopts::OptionParseException("Invalid AES encryption key");
+ }
+ }
+
+ if (ServerOptions.EncryptionIV.empty() == false)
+ {
+ const auto IV = zen::AesIV128Bit::FromString(ServerOptions.EncryptionIV);
+
+ if (IV.IsValid() == false)
+ {
+ throw cxxopts::OptionParseException("Invalid AES initialization vector");
+ }
+ }
+}
+
+UpstreamCachePolicy
+ParseUpstreamCachePolicy(std::string_view Options)
+{
+ if (Options == "readonly")
+ {
+ return UpstreamCachePolicy::Read;
+ }
+ else if (Options == "writeonly")
+ {
+ return UpstreamCachePolicy::Write;
+ }
+ else if (Options == "disabled")
+ {
+ return UpstreamCachePolicy::Disabled;
+ }
+ else
+ {
+ return UpstreamCachePolicy::ReadWrite;
+ }
+}
+
+ZenObjectStoreConfig
+ParseBucketConfigs(std::span<std::string> Buckets)
+{
+ using namespace std::literals;
+
+ ZenObjectStoreConfig Cfg;
+
+ // split bucket args in the form of "{BucketName};{LocalPath}"
+ for (std::string_view Bucket : Buckets)
+ {
+ ZenObjectStoreConfig::BucketConfig NewBucket;
+
+ if (auto Idx = Bucket.find_first_of(";"); Idx != std::string_view::npos)
+ {
+ NewBucket.Name = Bucket.substr(0, Idx);
+ NewBucket.Directory = Bucket.substr(Idx + 1);
+ }
+ else
+ {
+ NewBucket.Name = Bucket;
+ }
+
+ Cfg.Buckets.push_back(std::move(NewBucket));
+ }
+
+ return Cfg;
+}
+
+void ParseConfigFile(const std::filesystem::path& Path, ZenServerOptions& ServerOptions);
+
+void
+ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
+{
+#if ZEN_WITH_HTTPSYS
+ const char* DefaultHttp = "httpsys";
+#else
+ const char* DefaultHttp = "asio";
+#endif
+
+ // Note to those adding future options; std::filesystem::path-type options
+ // must be read into a std::string first. As of cxxopts-3.0.0 it uses a >>
+ // stream operator to convert argv value into the options type. std::fs::path
+ // expects paths in streams to be quoted but argv paths are unquoted. By
+ // going into a std::string first, paths with whitespace parse correctly.
+ std::string DataDir;
+ std::string ContentDir;
+ std::string AbsLogFile;
+ std::string ConfigFile;
+
+ cxxopts::Options options("zenserver", "Zen Server");
+ options.add_options()("dedicated",
+ "Enable dedicated server mode",
+ cxxopts::value<bool>(ServerOptions.IsDedicated)->default_value("false"));
+ options.add_options()("d, debug", "Enable debugging", cxxopts::value<bool>(ServerOptions.IsDebug)->default_value("false"));
+ options.add_options()("help", "Show command line help");
+ options.add_options()("t, test", "Enable test mode", cxxopts::value<bool>(ServerOptions.IsTest)->default_value("false"));
+ options.add_options()("log-id", "Specify id for adding context to log output", cxxopts::value<std::string>(ServerOptions.LogId));
+ options.add_options()("data-dir", "Specify persistence root", cxxopts::value<std::string>(DataDir));
+ options.add_options()("content-dir", "Frontend content directory", cxxopts::value<std::string>(ContentDir));
+ options.add_options()("abslog", "Path to log file", cxxopts::value<std::string>(AbsLogFile));
+ options.add_options()("config", "Path to Lua config file", cxxopts::value<std::string>(ConfigFile));
+ options.add_options()("no-sentry",
+ "Disable Sentry crash handler",
+ cxxopts::value<bool>(ServerOptions.NoSentry)->default_value("false"));
+
+ options.add_option("security",
+ "",
+ "encryption-aes-key",
+ "256 bit AES encryption key",
+ cxxopts::value<std::string>(ServerOptions.EncryptionKey),
+ "");
+
+ options.add_option("security",
+ "",
+ "encryption-aes-iv",
+ "128 bit AES encryption initialization vector",
+ cxxopts::value<std::string>(ServerOptions.EncryptionIV),
+ "");
+
+ std::string OpenIdProviderName;
+ options.add_option("security",
+ "",
+ "openid-provider-name",
+ "Open ID provider name",
+ cxxopts::value<std::string>(OpenIdProviderName),
+ "Default");
+
+ std::string OpenIdProviderUrl;
+ options.add_option("security", "", "openid-provider-url", "Open ID provider URL", cxxopts::value<std::string>(OpenIdProviderUrl), "");
+
+ std::string OpenIdClientId;
+ options.add_option("security", "", "openid-client-id", "Open ID client ID", cxxopts::value<std::string>(OpenIdClientId), "");
+
+ options
+ .add_option("lifetime", "", "owner-pid", "Specify owning process id", cxxopts::value<int>(ServerOptions.OwnerPid), "<identifier>");
+ options.add_option("lifetime",
+ "",
+ "child-id",
+ "Specify id which can be used to signal parent",
+ cxxopts::value<std::string>(ServerOptions.ChildId),
+ "<identifier>");
+
+#if ZEN_PLATFORM_WINDOWS
+ options.add_option("lifetime",
+ "",
+ "install",
+ "Install zenserver as a Windows service",
+ cxxopts::value<bool>(ServerOptions.InstallService),
+ "");
+ options.add_option("lifetime",
+ "",
+ "uninstall",
+ "Uninstall zenserver as a Windows service",
+ cxxopts::value<bool>(ServerOptions.UninstallService),
+ "");
+#endif
+
+ options.add_option("network",
+ "",
+ "http",
+ "Select HTTP server implementation (asio|httpsys|null)",
+ cxxopts::value<std::string>(ServerOptions.HttpServerClass)->default_value(DefaultHttp),
+ "<http class>");
+
+ options.add_option("network",
+ "p",
+ "port",
+ "Select HTTP port",
+ cxxopts::value<int>(ServerOptions.BasePort)->default_value("1337"),
+ "<port number>");
+
+ options.add_option("network",
+ "",
+ "websocket-port",
+ "Websocket server port",
+ cxxopts::value<int>(ServerOptions.WebSocketPort)->default_value("0"),
+ "<port number>");
+
+ options.add_option("network",
+ "",
+ "websocket-threads",
+ "Number of websocket I/O thread(s) (0 == hardware concurrency)",
+ cxxopts::value<int>(ServerOptions.WebSocketThreads)->default_value("0"),
+ "");
+
+#if ZEN_WITH_TRACE
+ options.add_option("ue-trace",
+ "",
+ "tracehost",
+ "Hostname to send the trace to",
+ cxxopts::value<std::string>(ServerOptions.TraceHost)->default_value(""),
+ "");
+
+ options.add_option("ue-trace",
+ "",
+ "tracefile",
+ "Path to write a trace to",
+ cxxopts::value<std::string>(ServerOptions.TraceFile)->default_value(""),
+ "");
+#endif // ZEN_WITH_TRACE
+
+ options.add_option("diagnostics",
+ "",
+ "crash",
+ "Simulate a crash",
+ cxxopts::value<bool>(ServerOptions.ShouldCrash)->default_value("false"),
+ "");
+
+ std::string UpstreamCachePolicyOptions;
+ options.add_option("cache",
+ "",
+ "upstream-cache-policy",
+ "",
+ cxxopts::value<std::string>(UpstreamCachePolicyOptions)->default_value(""),
+ "Upstream cache policy (readwrite|readonly|writeonly|disabled)");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-url",
+ "URL to a Jupiter instance",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.Url)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-oauth-url",
+ "URL to the OAuth provier",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthUrl)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-oauth-clientid",
+ "The OAuth client ID",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientId)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-oauth-clientsecret",
+ "The OAuth client secret",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientSecret)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-openid-provider",
+ "Name of a registered Open ID provider",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.OpenIdProvider)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-token",
+ "A static authentication token",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.AccessToken)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-namespace",
+ "The Common Blob Store API namespace",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.Namespace)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-jupiter-namespace-ddc",
+ "The lecacy DDC namespace",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.JupiterConfig.DdcNamespace)->default_value(""),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-zen-url",
+ "URL to remote Zen server. Use a comma separated list to choose the one with the best latency.",
+ cxxopts::value<std::vector<std::string>>(ServerOptions.UpstreamCacheConfig.ZenConfig.Urls),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-zen-dns",
+ "DNS that resolves to one or more Zen server instance(s)",
+ cxxopts::value<std::vector<std::string>>(ServerOptions.UpstreamCacheConfig.ZenConfig.Dns),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-thread-count",
+ "Number of threads used for upstream procsssing",
+ cxxopts::value<int32_t>(ServerOptions.UpstreamCacheConfig.UpstreamThreadCount)->default_value("4"),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-connect-timeout-ms",
+ "Connect timeout in millisecond(s). Default 5000 ms.",
+ cxxopts::value<int32_t>(ServerOptions.UpstreamCacheConfig.ConnectTimeoutMilliseconds)->default_value("5000"),
+ "");
+
+ options.add_option("cache",
+ "",
+ "upstream-timeout-ms",
+ "Timeout in millisecond(s). Default 0 ms",
+ cxxopts::value<int32_t>(ServerOptions.UpstreamCacheConfig.TimeoutMilliseconds)->default_value("0"),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-url",
+ "URL to a Horde instance.",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.Url)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-oauth-url",
+ "URL to the OAuth provier",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthUrl)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-oauth-clientid",
+ "The OAuth client ID",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientId)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-oauth-clientsecret",
+ "The OAuth client secret",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientSecret)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-openid-provider",
+ "Name of a registered Open ID provider",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.OpenIdProvider)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-token",
+ "A static authentication token",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.AccessToken)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-storage-url",
+ "URL to a Horde Storage instance.",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.StorageUrl)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-storage-oauth-url",
+ "URL to the OAuth provier",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthUrl)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-storage-oauth-clientid",
+ "The OAuth client ID",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientId)->default_value(""),
+ "");
+
+ options.add_option(
+ "compute",
+ "",
+ "upstream-horde-storage-oauth-clientsecret",
+ "The OAuth client secret",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientSecret)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-storage-openid-provider",
+ "Name of a registered Open ID provider",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOpenIdProvider)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-storage-token",
+ "A static authentication token",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.StorageAccessToken)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-cluster",
+ "The Horde compute cluster id",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.Cluster)->default_value(""),
+ "");
+
+ options.add_option("compute",
+ "",
+ "upstream-horde-namespace",
+ "The Jupiter namespace to use with Horde compute",
+ cxxopts::value<std::string>(ServerOptions.UpstreamCacheConfig.HordeConfig.Namespace)->default_value(""),
+ "");
+
+ options.add_option("gc",
+ "",
+ "gc-enabled",
+ "Whether garbage collection is enabled or not.",
+ cxxopts::value<bool>(ServerOptions.GcConfig.Enabled)->default_value("true"),
+ "");
+
+ options.add_option("gc",
+ "",
+ "gc-small-objects",
+ "Whether garbage collection of small objects is enabled or not.",
+ cxxopts::value<bool>(ServerOptions.GcConfig.CollectSmallObjects)->default_value("true"),
+ "");
+
+ options.add_option("gc",
+ "",
+ "gc-interval-seconds",
+ "Garbage collection interval in seconds. Default set to 3600 (1 hour).",
+ cxxopts::value<int32_t>(ServerOptions.GcConfig.IntervalSeconds)->default_value("3600"),
+ "");
+
+ options.add_option("gc",
+ "",
+ "gc-cache-duration-seconds",
+ "Max duration in seconds before Z$ entries get evicted. Default set to 1209600 (2 weeks)",
+ cxxopts::value<int32_t>(ServerOptions.GcConfig.Cache.MaxDurationSeconds)->default_value("1209600"),
+ "");
+
+ options.add_option("gc",
+ "",
+ "disk-reserve-size",
+ "Size of gc disk reserve in bytes. Default set to 268435456 (256 Mb).",
+ cxxopts::value<uint64_t>(ServerOptions.GcConfig.DiskReserveSize)->default_value("268435456"),
+ "");
+
+ options.add_option("gc",
+ "",
+ "gc-monitor-interval-seconds",
+ "Garbage collection monitoring interval in seconds. Default set to 30 (30 seconds)",
+ cxxopts::value<int32_t>(ServerOptions.GcConfig.MonitorIntervalSeconds)->default_value("30"),
+ "");
+
+ options.add_option("gc",
+ "",
+ "gc-disksize-softlimit",
+ "Garbage collection disk usage soft limit. Default set to 0 (Off).",
+ cxxopts::value<uint64_t>(ServerOptions.GcConfig.Cache.DiskSizeSoftLimit)->default_value("0"),
+ "");
+
+ options.add_option("objectstore",
+ "",
+ "objectstore-enabled",
+ "Whether the object store is enabled or not.",
+ cxxopts::value<bool>(ServerOptions.ObjectStoreEnabled)->default_value("false"),
+ "");
+
+ std::vector<std::string> BucketConfigs;
+ options.add_option("objectstore",
+ "",
+ "objectstore-bucket",
+ "Object store bucket mappings.",
+ cxxopts::value<std::vector<std::string>>(BucketConfigs),
+ "");
+
+ try
+ {
+ auto result = options.parse(argc, argv);
+
+ if (result.count("help"))
+ {
+ zen::logging::ConsoleLog().info("{}", options.help());
+#if ZEN_PLATFORM_WINDOWS
+ zen::logging::ConsoleLog().info("Press any key to exit!");
+ _getch();
+#else
+ // Assume the user's in a terminal on all other platforms and that
+ // they'll use less/more/etc. if need be.
+#endif
+ exit(0);
+ }
+
+ auto MakeSafePath = [](const std::string& Path) {
+#if ZEN_PLATFORM_WINDOWS
+ if (Path.empty())
+ {
+ return Path;
+ }
+
+ std::string FixedPath = Path;
+ std::replace(FixedPath.begin(), FixedPath.end(), '/', '\\');
+ if (!FixedPath.starts_with("\\\\?\\"))
+ {
+ FixedPath.insert(0, "\\\\?\\");
+ }
+ return FixedPath;
+#else
+ return Path;
+#endif
+ };
+
+ ServerOptions.DataDir = MakeSafePath(DataDir);
+ ServerOptions.ContentDir = MakeSafePath(ContentDir);
+ ServerOptions.AbsLogFile = MakeSafePath(AbsLogFile);
+ ServerOptions.ConfigFile = MakeSafePath(ConfigFile);
+ ServerOptions.UpstreamCacheConfig.CachePolicy = ParseUpstreamCachePolicy(UpstreamCachePolicyOptions);
+
+ if (OpenIdProviderUrl.empty() == false)
+ {
+ if (OpenIdClientId.empty())
+ {
+ throw cxxopts::OptionParseException("Invalid OpenID client ID");
+ }
+
+ ServerOptions.AuthConfig.OpenIdProviders.push_back(
+ {.Name = OpenIdProviderName, .Url = OpenIdProviderUrl, .ClientId = OpenIdClientId});
+ }
+
+ ServerOptions.ObjectStoreConfig = ParseBucketConfigs(BucketConfigs);
+
+ if (!ServerOptions.ConfigFile.empty())
+ {
+ ParseConfigFile(ServerOptions.ConfigFile, ServerOptions);
+ }
+ else
+ {
+ ParseConfigFile(ServerOptions.DataDir / "zen_cfg.lua", ServerOptions);
+ }
+
+ ValidateOptions(ServerOptions);
+ }
+ catch (cxxopts::OptionParseException& e)
+ {
+ zen::logging::ConsoleLog().error("Error parsing zenserver arguments: {}\n\n{}", e.what(), options.help());
+
+ throw;
+ }
+
+ if (ServerOptions.DataDir.empty())
+ {
+ ServerOptions.DataDir = PickDefaultStateDirectory();
+ }
+
+ if (ServerOptions.AbsLogFile.empty())
+ {
+ ServerOptions.AbsLogFile = ServerOptions.DataDir / "logs" / "zenserver.log";
+ }
+}
+
+void
+ParseConfigFile(const std::filesystem::path& Path, ZenServerOptions& ServerOptions)
+{
+ zen::IoBuffer LuaScript = zen::IoBufferBuilder::MakeFromFile(Path);
+
+ if (LuaScript)
+ {
+ sol::state lua;
+
+ lua.open_libraries(sol::lib::base);
+
+ lua.set_function("getenv", [&](const std::string env) -> sol::object {
+#if ZEN_PLATFORM_WINDOWS
+ std::wstring EnvVarValue;
+ size_t RequiredSize = 0;
+ std::wstring EnvWide = zen::Utf8ToWide(env);
+ _wgetenv_s(&RequiredSize, nullptr, 0, EnvWide.c_str());
+
+ if (RequiredSize == 0)
+ return sol::make_object(lua, sol::lua_nil);
+
+ EnvVarValue.resize(RequiredSize);
+ _wgetenv_s(&RequiredSize, EnvVarValue.data(), RequiredSize, EnvWide.c_str());
+ return sol::make_object(lua, zen::WideToUtf8(EnvVarValue.c_str()));
+#else
+ ZEN_UNUSED(env);
+ return sol::make_object(lua, sol::lua_nil);
+#endif
+ });
+
+ try
+ {
+ sol::load_result config = lua.load(std::string_view((const char*)LuaScript.Data(), LuaScript.Size()), "zen_cfg");
+
+ if (!config.valid())
+ {
+ sol::error err = config;
+
+ std::string ErrorString = sol::to_string(config.status());
+
+ throw std::runtime_error(fmt::format("{} error: {}", ErrorString, err.what()));
+ }
+
+ config();
+ }
+ catch (std::exception& e)
+ {
+ throw std::runtime_error(fmt::format("failed to load config script ('{}'): {}", Path, e.what()).c_str());
+ }
+
+ if (sol::optional<sol::table> ServerConfig = lua["server"])
+ {
+ if (ServerOptions.DataDir.empty())
+ {
+ if (sol::optional<std::string> Opt = ServerConfig.value()["datadir"])
+ {
+ ServerOptions.DataDir = Opt.value();
+ }
+ }
+
+ if (ServerOptions.ContentDir.empty())
+ {
+ if (sol::optional<std::string> Opt = ServerConfig.value()["contentdir"])
+ {
+ ServerOptions.ContentDir = Opt.value();
+ }
+ }
+
+ if (ServerOptions.AbsLogFile.empty())
+ {
+ if (sol::optional<std::string> Opt = ServerConfig.value()["abslog"])
+ {
+ ServerOptions.AbsLogFile = Opt.value();
+ }
+ }
+
+ ServerOptions.IsDebug = ServerConfig->get_or("debug", ServerOptions.IsDebug);
+ }
+
+ if (sol::optional<sol::table> NetworkConfig = lua["network"])
+ {
+ if (sol::optional<std::string> Opt = NetworkConfig.value()["httpserverclass"])
+ {
+ ServerOptions.HttpServerClass = Opt.value();
+ }
+
+ ServerOptions.BasePort = NetworkConfig->get_or<int>("port", ServerOptions.BasePort);
+ }
+
+ auto UpdateStringValueFromConfig = [](const sol::table& Table, std::string_view Key, std::string& OutValue) {
+ // Update the specified config value unless it has been set, i.e. from command line
+ if (auto MaybeValue = Table.get<sol::optional<std::string>>(Key); MaybeValue.has_value() && OutValue.empty())
+ {
+ OutValue = MaybeValue.value();
+ }
+ };
+
+ if (sol::optional<sol::table> StructuredCacheConfig = lua["cache"])
+ {
+ ServerOptions.StructuredCacheEnabled = StructuredCacheConfig->get_or("enable", ServerOptions.StructuredCacheEnabled);
+
+ if (auto UpstreamConfig = StructuredCacheConfig->get<sol::optional<sol::table>>("upstream"))
+ {
+ std::string Policy = UpstreamConfig->get_or("policy", std::string());
+ ServerOptions.UpstreamCacheConfig.CachePolicy = ParseUpstreamCachePolicy(Policy);
+ ServerOptions.UpstreamCacheConfig.UpstreamThreadCount =
+ UpstreamConfig->get_or("upstreamthreadcount", ServerOptions.UpstreamCacheConfig.UpstreamThreadCount);
+
+ if (auto JupiterConfig = UpstreamConfig->get<sol::optional<sol::table>>("jupiter"))
+ {
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("name"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.Name);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("url"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.Url);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("oauthprovider"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthUrl);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("oauthclientid"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientId);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("oauthclientsecret"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientSecret);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("openidprovider"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OpenIdProvider);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("token"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.AccessToken);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("namespace"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.Namespace);
+ UpdateStringValueFromConfig(JupiterConfig.value(),
+ std::string_view("ddcnamespace"),
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.DdcNamespace);
+ };
+
+ if (auto ZenConfig = UpstreamConfig->get<sol::optional<sol::table>>("zen"))
+ {
+ ServerOptions.UpstreamCacheConfig.ZenConfig.Name = ZenConfig.value().get_or("name", std::string("Zen"));
+
+ if (auto Url = ZenConfig.value().get<sol::optional<std::string>>("url"))
+ {
+ ServerOptions.UpstreamCacheConfig.ZenConfig.Urls.push_back(Url.value());
+ }
+ else if (auto Urls = ZenConfig.value().get<sol::optional<sol::table>>("url"))
+ {
+ for (const auto& Kv : Urls.value())
+ {
+ ServerOptions.UpstreamCacheConfig.ZenConfig.Urls.push_back(Kv.second.as<std::string>());
+ }
+ }
+
+ if (auto Dns = ZenConfig.value().get<sol::optional<std::string>>("dns"))
+ {
+ ServerOptions.UpstreamCacheConfig.ZenConfig.Dns.push_back(Dns.value());
+ }
+ else if (auto DnsArray = ZenConfig.value().get<sol::optional<sol::table>>("dns"))
+ {
+ for (const auto& Kv : DnsArray.value())
+ {
+ ServerOptions.UpstreamCacheConfig.ZenConfig.Dns.push_back(Kv.second.as<std::string>());
+ }
+ }
+ }
+ }
+ }
+
+ if (sol::optional<sol::table> ExecConfig = lua["exec"])
+ {
+ ServerOptions.ExecServiceEnabled = ExecConfig->get_or("enable", ServerOptions.ExecServiceEnabled);
+ }
+
+ if (sol::optional<sol::table> ComputeConfig = lua["compute"])
+ {
+ ServerOptions.ComputeServiceEnabled = ComputeConfig->get_or("enable", ServerOptions.ComputeServiceEnabled);
+
+ if (auto UpstreamConfig = ComputeConfig->get<sol::optional<sol::table>>("upstream"))
+ {
+ if (auto HordeConfig = UpstreamConfig->get<sol::optional<sol::table>>("horde"))
+ {
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("name"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Name);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("url"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Url);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("oauthprovider"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthUrl);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("oauthclientid"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientId);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("oauthclientsecret"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientSecret);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("openidprovider"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OpenIdProvider);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("token"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.AccessToken);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("cluster"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Cluster);
+ UpdateStringValueFromConfig(HordeConfig.value(),
+ std::string_view("namespace"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Namespace);
+ };
+
+ if (auto StorageConfig = UpstreamConfig->get<sol::optional<sol::table>>("storage"))
+ {
+ UpdateStringValueFromConfig(StorageConfig.value(),
+ std::string_view("url"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageUrl);
+ UpdateStringValueFromConfig(StorageConfig.value(),
+ std::string_view("oauthprovider"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthUrl);
+ UpdateStringValueFromConfig(StorageConfig.value(),
+ std::string_view("oauthclientid"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientId);
+ UpdateStringValueFromConfig(StorageConfig.value(),
+ std::string_view("oauthclientsecret"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientSecret);
+ UpdateStringValueFromConfig(StorageConfig.value(),
+ std::string_view("openidprovider"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOpenIdProvider);
+ UpdateStringValueFromConfig(StorageConfig.value(),
+ std::string_view("token"),
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageAccessToken);
+ };
+ }
+ }
+
+ if (sol::optional<sol::table> GcConfig = lua["gc"])
+ {
+ ServerOptions.GcConfig.MonitorIntervalSeconds = GcConfig.value().get_or("monitorintervalseconds", 30);
+ ServerOptions.GcConfig.IntervalSeconds = GcConfig.value().get_or("intervalseconds", 0);
+ ServerOptions.GcConfig.DiskReserveSize = GcConfig.value().get_or("diskreservesize", uint64_t(1u << 28));
+
+ if (sol::optional<sol::table> CacheGcConfig = GcConfig.value()["cache"])
+ {
+ ServerOptions.GcConfig.Cache.MaxDurationSeconds = CacheGcConfig.value().get_or("maxdurationseconds", int32_t(0));
+ ServerOptions.GcConfig.Cache.DiskSizeLimit = CacheGcConfig.value().get_or("disksizelimit", ~uint64_t(0));
+ ServerOptions.GcConfig.Cache.MemorySizeLimit = CacheGcConfig.value().get_or("memorysizelimit", ~uint64_t(0));
+ ServerOptions.GcConfig.Cache.DiskSizeSoftLimit = CacheGcConfig.value().get_or("disksizesoftlimit", 0);
+ }
+
+ if (sol::optional<sol::table> CasGcConfig = GcConfig.value()["cas"])
+ {
+ ServerOptions.GcConfig.Cas.LargeStrategySizeLimit = CasGcConfig.value().get_or("largestrategysizelimit", ~uint64_t(0));
+ ServerOptions.GcConfig.Cas.SmallStrategySizeLimit = CasGcConfig.value().get_or("smallstrategysizelimit", ~uint64_t(0));
+ ServerOptions.GcConfig.Cas.TinyStrategySizeLimit = CasGcConfig.value().get_or("tinystrategysizelimit", ~uint64_t(0));
+ }
+ }
+
+ if (sol::optional<sol::table> SecurityConfig = lua["security"])
+ {
+ if (sol::optional<sol::table> OpenIdProviders = SecurityConfig.value()["openidproviders"])
+ {
+ for (const auto& Kv : OpenIdProviders.value())
+ {
+ if (sol::optional<sol::table> OpenIdProvider = Kv.second.as<sol::table>())
+ {
+ std::string Name = OpenIdProvider.value().get_or("name", std::string("Default"));
+ std::string Url = OpenIdProvider.value().get_or("url", std::string());
+ std::string ClientId = OpenIdProvider.value().get_or("clientid", std::string());
+
+ ServerOptions.AuthConfig.OpenIdProviders.push_back(
+ {.Name = std::move(Name), .Url = std::move(Url), .ClientId = std::move(ClientId)});
+ }
+ }
+ }
+
+ ServerOptions.EncryptionKey = SecurityConfig.value().get_or("encryptionaeskey", std::string());
+ ServerOptions.EncryptionIV = SecurityConfig.value().get_or("encryptionaesiv", std::string());
+ }
+ }
+}