aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/config.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenserver/config.cpp')
-rw-r--r--src/zenserver/config.cpp1091
1 files changed, 791 insertions, 300 deletions
diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp
index 0e7aac079..2d286b02a 100644
--- a/src/zenserver/config.cpp
+++ b/src/zenserver/config.cpp
@@ -135,7 +135,795 @@ ParseBucketConfigs(std::span<std::string> Buckets)
return Cfg;
}
-void ParseConfigFile(const std::filesystem::path& Path, ZenServerOptions& ServerOptions);
+static std::string
+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
+};
+
+namespace LuaConfig {
+
+void
+EscapeBackslash(std::string& InOutString)
+{
+ std::size_t BackslashPos = InOutString.find('\\');
+ if (BackslashPos != std::string::npos)
+ {
+ std::size_t Offset = 0;
+ zen::ExtendableStringBuilder<512> PathBuilder;
+ while (BackslashPos != std::string::npos)
+ {
+ PathBuilder.Append(InOutString.substr(Offset, BackslashPos + 1 - Offset));
+ PathBuilder.Append('\\');
+ Offset = BackslashPos + 1;
+ BackslashPos = InOutString.find('\\', Offset);
+ }
+ PathBuilder.Append(InOutString.substr(Offset, BackslashPos));
+ InOutString = PathBuilder.ToString();
+ }
+}
+
+class OptionValue
+{
+public:
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) = 0;
+ virtual void Parse(sol::object Object) = 0;
+
+ virtual ~OptionValue() {}
+};
+
+typedef std::shared_ptr<OptionValue> TOptionValue;
+
+class StringOption : public OptionValue
+{
+public:
+ StringOption(std::string& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override
+ {
+ StringBuilder.Append(fmt::format("\"{}\"", Value));
+ }
+ virtual void Parse(sol::object Object) override { Value = Object.as<std::string>(); }
+ std::string& Value;
+};
+
+class FilePathOption : public OptionValue
+{
+public:
+ FilePathOption(std::filesystem::path& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override
+ {
+ std::string Path = Value.string();
+ EscapeBackslash(Path);
+ StringBuilder.Append(fmt::format("\"{}\"", Path));
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ std::string Str = Object.as<std::string>();
+ if (!Str.empty())
+ {
+ Value = MakeSafePath(Str);
+ }
+ }
+ std::filesystem::path& Value;
+};
+
+class BoolOption : public OptionValue
+{
+public:
+ BoolOption(bool& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override { StringBuilder.Append(Value ? "true" : "false"); }
+ virtual void Parse(sol::object Object) override { Value = Object.as<bool>(); }
+ bool& Value;
+};
+
+class CachePolicyOption : public OptionValue
+{
+public:
+ CachePolicyOption(UpstreamCachePolicy& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override
+ {
+ switch (Value)
+ {
+ case UpstreamCachePolicy::Read:
+ StringBuilder.Append("readonly");
+ break;
+ case UpstreamCachePolicy::Write:
+ StringBuilder.Append("writeonly");
+ break;
+ case UpstreamCachePolicy::Disabled:
+ StringBuilder.Append("disabled");
+ break;
+ case UpstreamCachePolicy::ReadWrite:
+ StringBuilder.Append("readwrite");
+ break;
+ default:
+ ZEN_ASSERT(false);
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ std::string PolicyString = Object.as<std::string>();
+ if (PolicyString == "readonly")
+ {
+ Value = UpstreamCachePolicy::Read;
+ }
+ else if (PolicyString == "writeonly")
+ {
+ Value = UpstreamCachePolicy::Write;
+ }
+ else if (PolicyString == "disabled")
+ {
+ Value = UpstreamCachePolicy::Disabled;
+ }
+ else if (PolicyString == "readwrite")
+ {
+ Value = UpstreamCachePolicy::ReadWrite;
+ }
+ }
+ UpstreamCachePolicy& Value;
+};
+
+template<Integral T>
+class NumberOption : public OptionValue
+{
+public:
+ NumberOption(T& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override { StringBuilder.Append(fmt::format("{}", Value)); }
+ virtual void Parse(sol::object Object) override { Value = Object.as<T>(); }
+ T& Value;
+};
+
+class LuaContainerWriter
+{
+public:
+ LuaContainerWriter(zen::StringBuilderBase& StringBuilder, std::string_view Indent)
+ : StringBuilder(StringBuilder)
+ , InitialIndent(Indent.length())
+ , LocalIndent(Indent)
+ {
+ StringBuilder.Append("{\n");
+ LocalIndent.push_back('\t');
+ }
+ ~LuaContainerWriter()
+ {
+ LocalIndent.pop_back();
+ StringBuilder.Append(LocalIndent);
+ StringBuilder.Append("}");
+ }
+
+ void BeginContainer(std::string_view Name)
+ {
+ StringBuilder.Append(LocalIndent);
+ if (!Name.empty())
+ {
+ StringBuilder.Append(Name);
+ StringBuilder.Append(" = {\n");
+ }
+ else
+ {
+ StringBuilder.Append("{\n");
+ }
+ LocalIndent.push_back('\t');
+ }
+ void WriteValue(std::string_view Name, std::string_view Value)
+ {
+ if (Name.empty())
+ {
+ StringBuilder.Append(fmt::format("{}\"{}\",\n", LocalIndent, Value));
+ }
+ else
+ {
+ StringBuilder.Append(fmt::format("{}{} = \"{}\",\n", LocalIndent, Name, Value));
+ }
+ }
+ void EndContainer()
+ {
+ LocalIndent.pop_back();
+ StringBuilder.Append(LocalIndent);
+ StringBuilder.Append("}");
+ StringBuilder.Append(",\n");
+ }
+
+private:
+ zen::StringBuilderBase& StringBuilder;
+ const std::size_t InitialIndent;
+ std::string LocalIndent;
+};
+
+class StringArrayOption : public OptionValue
+{
+public:
+ StringArrayOption(std::vector<std::string>& Value) : Value(Value) {}
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override
+ {
+ if (Value.empty())
+ {
+ StringBuilder.Append("{}");
+ }
+ if (Value.size() == 1)
+ {
+ StringBuilder.Append(fmt::format("\"{}\"", Value[0]));
+ }
+ else
+ {
+ LuaContainerWriter Writer(StringBuilder, Indent);
+ for (std::string String : Value)
+ {
+ Writer.WriteValue("", String);
+ }
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ if (Object.get_type() == sol::type::string)
+ {
+ Value.push_back(Object.as<std::string>());
+ }
+ else if (Object.get_type() == sol::type::table)
+ {
+ for (const auto& Kv : Object.as<sol::table>())
+ {
+ Value.push_back(Kv.second.as<std::string>());
+ }
+ }
+ }
+
+private:
+ std::vector<std::string>& Value;
+};
+
+class ZenAuthConfigOption : public OptionValue
+{
+public:
+ ZenAuthConfigOption(ZenAuthConfig& Value) : Value(Value) {}
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override
+ {
+ if (Value.OpenIdProviders.empty())
+ {
+ StringBuilder.Append("{}");
+ return;
+ }
+ LuaContainerWriter Writer(StringBuilder, Indent);
+ for (const ZenOpenIdProviderConfig& Config : Value.OpenIdProviders)
+ {
+ Writer.BeginContainer("");
+ {
+ Writer.WriteValue("name", Config.Name);
+ Writer.WriteValue("url", Config.Url);
+ Writer.WriteValue("clientid", Config.ClientId);
+ }
+ Writer.EndContainer();
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ if (sol::optional<sol::table> OpenIdProviders = Object.as<sol::table>())
+ {
+ 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());
+
+ Value.OpenIdProviders.push_back({.Name = std::move(Name), .Url = std::move(Url), .ClientId = std::move(ClientId)});
+ }
+ }
+ }
+ }
+ ZenAuthConfig& Value;
+};
+
+class ZenObjectStoreConfigOption : public OptionValue
+{
+public:
+ ZenObjectStoreConfigOption(ZenObjectStoreConfig& Value) : Value(Value) {}
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override
+ {
+ if (Value.Buckets.empty())
+ {
+ StringBuilder.Append("{}");
+ return;
+ }
+ LuaContainerWriter Writer(StringBuilder, Indent);
+ for (const ZenObjectStoreConfig::BucketConfig& Config : Value.Buckets)
+ {
+ Writer.BeginContainer("");
+ {
+ Writer.WriteValue("name", Config.Name);
+ std::string Directory = Config.Directory.string();
+ EscapeBackslash(Directory);
+ Writer.WriteValue("directory", Directory);
+ }
+ Writer.EndContainer();
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ if (sol::optional<sol::table> Buckets = Object.as<sol::table>())
+ {
+ for (const auto& Kv : Buckets.value())
+ {
+ if (sol::optional<sol::table> Bucket = Kv.second.as<sol::table>())
+ {
+ std::string Name = Bucket.value().get_or("name", std::string("Default"));
+ std::string Directory = Bucket.value().get_or("directory", std::string());
+
+ Value.Buckets.push_back({.Name = std::move(Name), .Directory = MakeSafePath(Directory)});
+ }
+ }
+ }
+ }
+ ZenObjectStoreConfig& Value;
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(std::string& Value)
+{
+ return std::make_shared<StringOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(std::filesystem::path& Value)
+{
+ return std::make_shared<FilePathOption>(Value);
+};
+
+template<Integral T>
+std::shared_ptr<OptionValue>
+MakeOption(T& Value)
+{
+ return std::make_shared<NumberOption<T>>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(bool& Value)
+{
+ return std::make_shared<BoolOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(UpstreamCachePolicy& Value)
+{
+ return std::make_shared<CachePolicyOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(std::vector<std::string>& Value)
+{
+ return std::make_shared<StringArrayOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(ZenAuthConfig& Value)
+{
+ return std::make_shared<ZenAuthConfigOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(ZenObjectStoreConfig& Value)
+{
+ return std::make_shared<ZenObjectStoreConfigOption>(Value);
+};
+
+struct Option
+{
+ std::string CommandLineOptionName;
+ TOptionValue Value;
+};
+
+struct Options
+{
+public:
+ template<typename T>
+ void AddOption(std::string_view Key, T& Value, std::string_view CommandLineOptionName = "")
+ {
+ OptionMap.insert_or_assign(std::string(Key),
+ Option{.CommandLineOptionName = std::string(CommandLineOptionName), .Value = MakeOption(Value)});
+ };
+
+ void Parse(const std::filesystem::path& Path, const cxxopts::ParseResult& CmdLineResult)
+ {
+ 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()));
+#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+ char* EnvVariable = getenv(env.c_str());
+ if (EnvVariable == nullptr)
+ {
+ return sol::make_object(lua, sol::lua_nil);
+ }
+ return sol::make_object(lua, EnvVariable);
+#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());
+ }
+
+ Parse(lua, CmdLineResult);
+ }
+ }
+
+ void Parse(const sol::state& LuaState, const cxxopts::ParseResult& CmdLineResult)
+ {
+ for (auto It : LuaState)
+ {
+ sol::object Key = It.first;
+ sol::type KeyType = Key.get_type();
+ if (KeyType == sol::type::string)
+ {
+ sol::type ValueType = It.second.get_type();
+ switch (ValueType)
+ {
+ case sol::type::table:
+ {
+ std::string Name = Key.as<std::string>();
+ if (Name.starts_with("_"))
+ {
+ continue;
+ }
+ if (Name == "base")
+ {
+ continue;
+ }
+ Traverse(It.second.as<sol::table>(), Name, CmdLineResult);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ void Touch(std::string_view Key) { UsedKeys.insert(std::string(Key)); }
+
+ void Print(zen::StringBuilderBase& SB, const cxxopts::ParseResult& CmdLineResult)
+ {
+ for (auto It : OptionMap)
+ {
+ if (CmdLineResult.count(It.second.CommandLineOptionName) != 0)
+ {
+ UsedKeys.insert(It.first);
+ }
+ }
+
+ std::vector<std::string> SortedKeys(UsedKeys.begin(), UsedKeys.end());
+ std::sort(SortedKeys.begin(), SortedKeys.end());
+ auto GetTablePath = [](const std::string& Key) -> std::vector<std::string> {
+ std::vector<std::string> Path;
+ zen::ForEachStrTok(Key, '.', [&Path](std::string_view Part) {
+ Path.push_back(std::string(Part));
+ return true;
+ });
+ return Path;
+ };
+ std::vector<std::string> CurrentTablePath;
+ std::string Indent;
+ auto It = SortedKeys.begin();
+ for (const std::string& Key : SortedKeys)
+ {
+ std::vector<std::string> KeyPath = GetTablePath(Key);
+ std::string Name = KeyPath.back();
+ KeyPath.pop_back();
+ if (CurrentTablePath != KeyPath)
+ {
+ size_t EqualCount = 0;
+ while (EqualCount < CurrentTablePath.size() && EqualCount < KeyPath.size() &&
+ CurrentTablePath[EqualCount] == KeyPath[EqualCount])
+ {
+ EqualCount++;
+ }
+ while (CurrentTablePath.size() > EqualCount)
+ {
+ CurrentTablePath.pop_back();
+ Indent.pop_back();
+ SB.Append(Indent);
+ SB.Append("}");
+ if (CurrentTablePath.size() == EqualCount && !Indent.empty() && KeyPath.size() >= EqualCount)
+ {
+ SB.Append(",");
+ }
+ SB.Append("\n");
+ if (Indent.empty())
+ {
+ SB.Append("\n");
+ }
+ }
+ while (EqualCount < KeyPath.size())
+ {
+ SB.Append(Indent);
+ SB.Append(KeyPath[EqualCount]);
+ SB.Append(" = {\n");
+ Indent.push_back('\t');
+ CurrentTablePath.push_back(KeyPath[EqualCount]);
+ EqualCount++;
+ }
+ }
+
+ SB.Append(Indent);
+ SB.Append(Name);
+ SB.Append(" = ");
+ OptionMap[Key].Value->Print(Indent, SB);
+ SB.Append(",\n");
+ }
+ while (!CurrentTablePath.empty())
+ {
+ Indent.pop_back();
+ SB.Append(Indent);
+ SB.Append("}\n");
+ CurrentTablePath.pop_back();
+ }
+ }
+
+private:
+ void Traverse(sol::table Table, std::string_view PathPrefix, const cxxopts::ParseResult& CmdLineResult)
+ {
+ for (auto It : Table)
+ {
+ sol::object Key = It.first;
+ sol::type KeyType = Key.get_type();
+ if (KeyType == sol::type::string || KeyType == sol::type::number)
+ {
+ sol::type ValueType = It.second.get_type();
+ switch (ValueType)
+ {
+ case sol::type::table:
+ case sol::type::string:
+ case sol::type::number:
+ case sol::type::boolean:
+ {
+ std::string Name = Key.as<std::string>();
+ if (Name.starts_with("_"))
+ {
+ continue;
+ }
+ Name = std::string(PathPrefix) + "." + Key.as<std::string>();
+ auto OptionIt = OptionMap.find(Name);
+ if (OptionIt != OptionMap.end())
+ {
+ UsedKeys.insert(Name);
+ if (CmdLineResult.count(OptionIt->second.CommandLineOptionName) != 0)
+ {
+ continue;
+ }
+ OptionIt->second.Value->Parse(It.second);
+ continue;
+ }
+ if (ValueType == sol::type::table)
+ {
+ if (Name == "base")
+ {
+ continue;
+ }
+ Traverse(It.second.as<sol::table>(), Name, CmdLineResult);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ std::unordered_map<std::string, Option> OptionMap;
+ std::unordered_set<std::string> UsedKeys;
+};
+
+} // namespace LuaConfig
+
+void
+ParseConfigFile(const std::filesystem::path& Path, ZenServerOptions& ServerOptions, const cxxopts::ParseResult& CmdLineResult)
+{
+ using namespace std::literals;
+
+ LuaConfig::Options LuaOptions;
+
+ ////// server
+ LuaOptions.AddOption("server.dedicated"sv, ServerOptions.IsDedicated, "dedicated"sv);
+ LuaOptions.AddOption("server.logid"sv, ServerOptions.LogId, "log-id"sv);
+ LuaOptions.AddOption("server.sentry.disable"sv, ServerOptions.NoSentry, "no-sentry"sv);
+ LuaOptions.AddOption("server.sentry.allowpersonalinfo"sv, ServerOptions.SentryAllowPII, "sentry-allow-personal-info"sv);
+ LuaOptions.AddOption("server.datadir"sv, ServerOptions.DataDir, "data-dir"sv);
+ LuaOptions.AddOption("server.contentdir"sv, ServerOptions.ContentDir, "content-dir"sv);
+ LuaOptions.AddOption("server.abslog"sv, ServerOptions.AbsLogFile, "abslog"sv);
+ LuaOptions.AddOption("server.debug"sv, ServerOptions.IsDebug, "debug"sv);
+
+ ////// objectstore
+ LuaOptions.AddOption("server.objectstore.enabled"sv, ServerOptions.ObjectStoreEnabled, "objectstore-enabled"sv);
+ LuaOptions.AddOption("server.objectstore.buckets"sv, ServerOptions.ObjectStoreConfig);
+
+ ////// network
+ LuaOptions.AddOption("network.httpserverclass"sv, ServerOptions.HttpServerClass, "http"sv);
+ LuaOptions.AddOption("network.port"sv, ServerOptions.BasePort, "port"sv);
+ LuaOptions.AddOption("network.websocket.port"sv, ServerOptions.WebSocketPort, "websocket-port"sv);
+ LuaOptions.AddOption("network.websocket.threadcount"sv, ServerOptions.WebSocketThreads, "websocket-threads"sv);
+
+ ////// trace
+ LuaOptions.AddOption("trace.host"sv, ServerOptions.TraceHost, "tracehost"sv);
+ LuaOptions.AddOption("trace.file"sv, ServerOptions.TraceFile, "tracefile"sv);
+
+ ////// cache
+ LuaOptions.AddOption("cache.enable"sv, ServerOptions.StructuredCacheEnabled);
+
+ ////// cache.upstream
+ LuaOptions.AddOption("cache.upstream.policy"sv, ServerOptions.UpstreamCacheConfig.CachePolicy, "upstream-cache-policy"sv);
+ LuaOptions.AddOption("cache.upstream.upstreamthreadcount"sv,
+ ServerOptions.UpstreamCacheConfig.UpstreamThreadCount,
+ "upstream-thread-count"sv);
+ LuaOptions.AddOption("cache.upstream.connecttimeoutms"sv,
+ ServerOptions.UpstreamCacheConfig.ConnectTimeoutMilliseconds,
+ "upstream-connect-timeout-ms"sv);
+ LuaOptions.AddOption("cache.upstream.timeoutms"sv, ServerOptions.UpstreamCacheConfig.TimeoutMilliseconds, "upstream-timeout-ms"sv);
+
+ ////// cache.upstream.jupiter
+ LuaOptions.AddOption("cache.upstream.jupiter.name"sv, ServerOptions.UpstreamCacheConfig.JupiterConfig.Name);
+ LuaOptions.AddOption("cache.upstream.jupiter.url"sv, ServerOptions.UpstreamCacheConfig.JupiterConfig.Url, "upstream-jupiter-url"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.oauthprovider"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthUrl,
+ "upstream-jupiter-oauth-url"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.oauthclientid"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientId,
+ "upstream-jupiter-oauth-clientid");
+ LuaOptions.AddOption("cache.upstream.jupiter.oauthclientsecret"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientSecret,
+ "upstream-jupiter-oauth-clientsecret"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.openidprovider"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OpenIdProvider,
+ "upstream-jupiter-openid-provider"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.token"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.AccessToken,
+ "upstream-jupiter-token"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.namespace"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.Namespace,
+ "upstream-jupiter-namespace"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.ddcnamespace"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.DdcNamespace,
+ "upstream-jupiter-namespace-ddc"sv);
+
+ ////// cache.upstream.zen
+ // LuaOptions.AddOption("cache.upstream.zen"sv, ServerOptions.UpstreamCacheConfig.ZenConfig);
+ LuaOptions.AddOption("cache.upstream.zen.name"sv, ServerOptions.UpstreamCacheConfig.ZenConfig.Name);
+ LuaOptions.AddOption("cache.upstream.zen.dns"sv, ServerOptions.UpstreamCacheConfig.ZenConfig.Dns);
+ LuaOptions.AddOption("cache.upstream.zen.url"sv, ServerOptions.UpstreamCacheConfig.ZenConfig.Urls);
+
+ ////// exec
+ LuaOptions.AddOption("exec.enable"sv, ServerOptions.ExecServiceEnabled);
+
+ ////// compute
+ LuaOptions.AddOption("compute.enable"sv, ServerOptions.ComputeServiceEnabled);
+
+ LuaOptions.AddOption("compute.upstream.horde.name"sv, ServerOptions.UpstreamCacheConfig.HordeConfig.Name);
+ LuaOptions.AddOption("compute.upstream.horde.url"sv, ServerOptions.UpstreamCacheConfig.HordeConfig.Url, "upstream-horde-url"sv);
+ LuaOptions.AddOption("compute.upstream.horde.oauthprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthUrl,
+ "upstream-horde-oauth-url"sv);
+ LuaOptions.AddOption("compute.upstream.horde.oauthclientid"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientId,
+ "upstream-horde-oauth-clientid"sv);
+ LuaOptions.AddOption("compute.upstream.horde.oauthclientsecret"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientSecret,
+ "upstream-horde-oauth-clientsecret"sv);
+ LuaOptions.AddOption("compute.upstream.horde.openidprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OpenIdProvider,
+ "upstream-horde-openid-provider"sv);
+ LuaOptions.AddOption("compute.upstream.horde.token"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.AccessToken,
+ "upstream-horde-token"sv);
+ LuaOptions.AddOption("compute.upstream.horde.cluster"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Cluster,
+ "upstream-horde-cluster"sv);
+ LuaOptions.AddOption("compute.upstream.horde.namespace"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Namespace,
+ "upstream-horde-namespace"sv);
+
+ ////// compute storage
+ LuaOptions.AddOption("compute.upstream.storage.name"sv, ServerOptions.UpstreamCacheConfig.HordeConfig.Name);
+ LuaOptions.AddOption("compute.upstream.storage.url"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Url,
+ "upstream-horde-storage-oauth-url"sv);
+ LuaOptions.AddOption("compute.upstream.storage.oauthprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthUrl,
+ "upstream-horde-storage-oauth-url"sv);
+ LuaOptions.AddOption("compute.upstream.storage.oauthclientid"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientId,
+ "upstream-horde-storage-oauth-clientid"sv);
+ LuaOptions.AddOption("compute.upstream.storage.oauthclientsecret"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientSecret,
+ "upstream-horde-storage-oauth-clientsecret"sv);
+ LuaOptions.AddOption("compute.upstream.storage.openidprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOpenIdProvider,
+ "upstream-horde-storage-openid-provider"sv);
+ LuaOptions.AddOption("compute.upstream.storage.token"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageAccessToken,
+ "upstream-horde-storage-token"sv);
+
+ LuaOptions.AddOption("gc.enabled"sv, ServerOptions.GcConfig.Enabled, "gc-enabled"sv);
+
+ LuaOptions.AddOption("gc.monitorintervalseconds"sv, ServerOptions.GcConfig.MonitorIntervalSeconds, "gc-monitor-interval-seconds"sv);
+ LuaOptions.AddOption("gc.intervalseconds"sv, ServerOptions.GcConfig.IntervalSeconds, "gc-interval-seconds"sv);
+ LuaOptions.AddOption("gc.collectsmallobjects"sv, ServerOptions.GcConfig.CollectSmallObjects, "gc-small-objects"sv);
+ LuaOptions.AddOption("gc.diskreservesize"sv, ServerOptions.GcConfig.DiskReserveSize, "disk-reserve-size"sv);
+ LuaOptions.AddOption("gc.disksizesoftlimit"sv, ServerOptions.GcConfig.DiskSizeSoftLimit, "gc-disksize-softlimit"sv);
+ LuaOptions.AddOption("gc.lowdiskspacethreshold"sv,
+ ServerOptions.GcConfig.MinimumFreeDiskSpaceToAllowWrites,
+ "gc-low-diskspace-threshold"sv);
+
+ ////// gc
+ LuaOptions.AddOption("gc.cache.maxdurationseconds"sv, ServerOptions.GcConfig.Cache.MaxDurationSeconds, "gc-cache-duration-seconds"sv);
+
+ ////// security
+ LuaOptions.AddOption("security.encryptionaeskey"sv, ServerOptions.EncryptionKey, "encryption-aes-key"sv);
+ LuaOptions.AddOption("security.encryptionaesiv"sv, ServerOptions.EncryptionIV, "encryption-aes-iv"sv);
+ LuaOptions.AddOption("security.openidproviders"sv, ServerOptions.AuthConfig);
+
+ LuaOptions.Parse(Path, CmdLineResult);
+
+ // These have special command line processing so we make sure we export them if they were configured on command line
+ if (!ServerOptions.AuthConfig.OpenIdProviders.empty())
+ {
+ LuaOptions.Touch("security.openidproviders"sv);
+ }
+ if (!ServerOptions.ObjectStoreConfig.Buckets.empty())
+ {
+ LuaOptions.Touch("server.objectstore.buckets"sv);
+ }
+}
void
ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
@@ -571,25 +1359,6 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
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);
@@ -611,11 +1380,11 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
if (!ServerOptions.ConfigFile.empty())
{
- ParseConfigFile(ServerOptions.ConfigFile, ServerOptions);
+ ParseConfigFile(ServerOptions.ConfigFile, ServerOptions, result);
}
else
{
- ParseConfigFile(ServerOptions.DataDir / "zen_cfg.lua", ServerOptions);
+ ParseConfigFile(ServerOptions.DataDir / "zen_cfg.lua", ServerOptions, result);
}
ValidateOptions(ServerOptions);
@@ -643,281 +1412,3 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
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));
- ServerOptions.GcConfig.DiskSizeSoftLimit = GcConfig.value().get_or("disksizesoftlimit", 0);
- ServerOptions.GcConfig.MinimumFreeDiskSpaceToAllowWrites = GcConfig.value().get_or("lowdiskspacethreshold", 0);
-
- if (sol::optional<sol::table> CacheGcConfig = GcConfig.value()["cache"])
- {
- ServerOptions.GcConfig.Cache.MaxDurationSeconds = CacheGcConfig.value().get_or("maxdurationseconds", int32_t(0));
- }
- if (sol::optional<sol::table> CacheGcConfig = GcConfig.value()["projectstore"])
- {
- ServerOptions.GcConfig.ProjectStore.MaxDurationSeconds = CacheGcConfig.value().get_or("maxdurationseconds", int32_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());
- }
- }
-}