diff options
Diffstat (limited to 'src/zenserver/config/config.cpp')
| -rw-r--r-- | src/zenserver/config/config.cpp | 212 |
1 files changed, 205 insertions, 7 deletions
diff --git a/src/zenserver/config/config.cpp b/src/zenserver/config/config.cpp index e36352dae..6449159fd 100644 --- a/src/zenserver/config/config.cpp +++ b/src/zenserver/config/config.cpp @@ -12,12 +12,16 @@ #include <zencore/compactbinaryutil.h> #include <zencore/compactbinaryvalidation.h> #include <zencore/except.h> +#include <zencore/filesystem.h> #include <zencore/fmtutils.h> #include <zencore/iobuffer.h> #include <zencore/logging.h> #include <zencore/string.h> #include <zenutil/config/commandlineoptions.h> #include <zenutil/config/environmentoptions.h> +#include <zenutil/consoletui.h> + +#include <charconv> ZEN_THIRD_PARTY_INCLUDES_START #include <fmt/format.h> @@ -130,6 +134,7 @@ ZenServerConfiguratorBase::AddCommonConfigOptions(LuaConfig::Options& LuaOptions // server LuaOptions.AddOption("server.dedicated"sv, ServerOptions.IsDedicated, "dedicated"sv); + LuaOptions.AddOption("server.allowportprobing"sv, ServerOptions.AllowPortProbing, "allow-port-probing"sv); LuaOptions.AddOption("server.sentry.disable"sv, ServerOptions.SentryConfig.Disable, "no-sentry"sv); LuaOptions.AddOption("server.sentry.allowpersonalinfo"sv, ServerOptions.SentryConfig.AllowPII, "sentry-allow-personal-info"sv); LuaOptions.AddOption("server.sentry.dsn"sv, ServerOptions.SentryConfig.Dsn, "sentry-dsn"sv); @@ -144,10 +149,16 @@ ZenServerConfiguratorBase::AddCommonConfigOptions(LuaConfig::Options& LuaOptions ////// network + LuaOptions.AddOption("network.httpclientbackend"sv, ServerOptions.HttpClient.Backend, "httpclient"sv); LuaOptions.AddOption("network.httpserverclass"sv, ServerOptions.HttpConfig.ServerClass, "http"sv); LuaOptions.AddOption("network.httpserverthreads"sv, ServerOptions.HttpConfig.ThreadCount, "http-threads"sv); LuaOptions.AddOption("network.port"sv, ServerOptions.BasePort, "port"sv); LuaOptions.AddOption("network.forceloopback"sv, ServerOptions.HttpConfig.ForceLoopback, "http-forceloopback"sv); + LuaOptions.AddOption("network.unixsocket"sv, ServerOptions.HttpConfig.UnixSocketPath, "unix-socket"sv); + LuaOptions.AddOption("network.nonetwork"sv, ServerOptions.HttpConfig.NoNetwork, "no-network"sv); + LuaOptions.AddOption("network.https.port"sv, ServerOptions.HttpConfig.HttpsPort, "https-port"sv); + LuaOptions.AddOption("network.https.certfile"sv, ServerOptions.HttpConfig.CertFile, "cert-file"sv); + LuaOptions.AddOption("network.https.keyfile"sv, ServerOptions.HttpConfig.KeyFile, "key-file"sv); #if ZEN_WITH_HTTPSYS LuaOptions.AddOption("network.httpsys.async.workthreads"sv, @@ -159,6 +170,10 @@ ZenServerConfiguratorBase::AddCommonConfigOptions(LuaConfig::Options& LuaOptions LuaOptions.AddOption("network.httpsys.requestlogging"sv, ServerOptions.HttpConfig.HttpSys.IsRequestLoggingEnabled, "httpsys-enable-request-logging"sv); + LuaOptions.AddOption("network.httpsys.httpsport"sv, ServerOptions.HttpConfig.HttpSys.HttpsPort, "httpsys-https-port"sv); + LuaOptions.AddOption("network.httpsys.certthumbprint"sv, ServerOptions.HttpConfig.HttpSys.CertThumbprint, "httpsys-cert-thumbprint"sv); + LuaOptions.AddOption("network.httpsys.certstorename"sv, ServerOptions.HttpConfig.HttpSys.CertStoreName, "httpsys-cert-store"sv); + LuaOptions.AddOption("network.httpsys.httpsonly"sv, ServerOptions.HttpConfig.HttpSys.HttpsOnly, "httpsys-https-only"sv); #endif #if ZEN_WITH_TRACE @@ -188,6 +203,8 @@ struct ZenServerCmdLineOptions std::string DataDir; std::string BaseSnapshotDir; std::string SecurityConfigPath; + std::string UnixSocketPath; + std::string PortStr; ZenLoggingCmdLineOptions LoggingOptions; @@ -208,8 +225,11 @@ ZenServerCmdLineOptions::AddCliOptions(cxxopts::Options& options, ZenServerConfi #endif options.add_options()("dedicated", - "Enable dedicated server mode", + "Enable dedicated server mode, disables '--allow-port-probing' and allocates more resources.", cxxopts::value<bool>(ServerOptions.IsDedicated)->default_value("false")); + options.add_options()("allow-port-probing", + "Allow searching for an available port, disabled if '--dedicated' is enabled.", + cxxopts::value<bool>(ServerOptions.AllowPortProbing)->default_value("true")); options.add_options()("d, debug", "Enable debugging", cxxopts::value<bool>(ServerOptions.IsDebug)->default_value("false")); options.add_options()("clean", "Clean out all state at startup", @@ -292,7 +312,7 @@ ZenServerCmdLineOptions::AddCliOptions(cxxopts::Options& options, ZenServerConfi "p", "port", "Select HTTP port", - cxxopts::value<int>(ServerOptions.BasePort)->default_value("8558"), + cxxopts::value<std::string>(PortStr)->default_value("auto"), "<port number>"); options.add_option("network", @@ -304,6 +324,41 @@ ZenServerCmdLineOptions::AddCliOptions(cxxopts::Options& options, ZenServerConfi options.add_option("network", "", + "unix-socket", + "Unix domain socket path to listen on (in addition to TCP)", + cxxopts::value<std::string>(UnixSocketPath), + "<path>"); + + options.add_option("network", + "", + "no-network", + "Disable TCP/HTTPS listeners; only accept connections via --unix-socket", + cxxopts::value<bool>(ServerOptions.HttpConfig.NoNetwork)->default_value("false"), + ""); + + options.add_option("network", + "", + "https-port", + "HTTPS listen port (0 = disabled)", + cxxopts::value<int>(ServerOptions.HttpConfig.HttpsPort)->default_value("0"), + "<port>"); + + options.add_option("network", + "", + "cert-file", + "Path to PEM certificate chain file for HTTPS", + cxxopts::value<std::string>(ServerOptions.HttpConfig.CertFile), + "<path>"); + + options.add_option("network", + "", + "key-file", + "Path to PEM private key file for HTTPS", + cxxopts::value<std::string>(ServerOptions.HttpConfig.KeyFile), + "<path>"); + + options.add_option("network", + "", "security-config-path", "Path to http security configuration file", cxxopts::value<std::string>(SecurityConfigPath), @@ -330,10 +385,45 @@ ZenServerCmdLineOptions::AddCliOptions(cxxopts::Options& options, ZenServerConfi "Enables Httpsys request logging", cxxopts::value<bool>(ServerOptions.HttpConfig.HttpSys.IsRequestLoggingEnabled), "<httpsys request logging>"); + + options.add_option("httpsys", + "", + "httpsys-https-port", + "HTTPS listen port for http.sys (0 = disabled)", + cxxopts::value<int>(ServerOptions.HttpConfig.HttpSys.HttpsPort)->default_value("0"), + "<port>"); + + options.add_option("httpsys", + "", + "httpsys-cert-thumbprint", + "SHA-1 certificate thumbprint for auto SSL binding", + cxxopts::value<std::string>(ServerOptions.HttpConfig.HttpSys.CertThumbprint), + "<thumbprint>"); + + options.add_option("httpsys", + "", + "httpsys-cert-store", + "Windows certificate store name for SSL binding", + cxxopts::value<std::string>(ServerOptions.HttpConfig.HttpSys.CertStoreName)->default_value("MY"), + "<store name>"); + + options.add_option("httpsys", + "", + "httpsys-https-only", + "Disable HTTP listener when HTTPS is active", + cxxopts::value<bool>(ServerOptions.HttpConfig.HttpSys.HttpsOnly)->default_value("false"), + ""); #endif options.add_option("network", "", + "httpclient", + "Select HTTP client implementation", + cxxopts::value<std::string>(ServerOptions.HttpClient.Backend)->default_value("curl"), + "<http client>"); + + options.add_option("network", + "", "http", "Select HTTP server implementation (asio|" #if ZEN_WITH_HTTPSYS @@ -389,14 +479,112 @@ ZenServerCmdLineOptions::ApplyOptions(cxxopts::Options& options, ZenServerConfig throw std::runtime_error(fmt::format("'--snapshot-dir' ('{}') must be a directory", ServerOptions.BaseSnapshotDir)); } - ServerOptions.SystemRootDir = MakeSafeAbsolutePath(SystemRootDir); - ServerOptions.DataDir = MakeSafeAbsolutePath(DataDir); - ServerOptions.ContentDir = MakeSafeAbsolutePath(ContentDir); - ServerOptions.ConfigFile = MakeSafeAbsolutePath(ConfigFile); - ServerOptions.BaseSnapshotDir = MakeSafeAbsolutePath(BaseSnapshotDir); + SystemRootDir = ExpandEnvironmentVariables(SystemRootDir); + ServerOptions.SystemRootDir = MakeSafeAbsolutePath(SystemRootDir); + + DataDir = ExpandEnvironmentVariables(DataDir); + ServerOptions.DataDir = MakeSafeAbsolutePath(DataDir); + + ContentDir = ExpandEnvironmentVariables(ContentDir); + ServerOptions.ContentDir = MakeSafeAbsolutePath(ContentDir); + + ConfigFile = ExpandEnvironmentVariables(ConfigFile); + ServerOptions.ConfigFile = MakeSafeAbsolutePath(ConfigFile); + + BaseSnapshotDir = ExpandEnvironmentVariables(BaseSnapshotDir); + ServerOptions.BaseSnapshotDir = MakeSafeAbsolutePath(BaseSnapshotDir); + + ExpandEnvironmentVariables(SecurityConfigPath); ServerOptions.SecurityConfigPath = MakeSafeAbsolutePath(SecurityConfigPath); + if (!UnixSocketPath.empty()) + { + UnixSocketPath = ExpandEnvironmentVariables(UnixSocketPath); + ServerOptions.HttpConfig.UnixSocketPath = MakeSafeAbsolutePath(UnixSocketPath); + } + + if (PortStr != "auto") + { + int Port = 0; + auto [Ptr, Ec] = std::from_chars(PortStr.data(), PortStr.data() + PortStr.size(), Port); + if (Ec != std::errc{} || Ptr != PortStr.data() + PortStr.size() || Port <= 0 || Port > 65535) + { + throw OptionParseException(fmt::format("invalid port '{}': expected 'auto' or a number between 1 and 65535", PortStr), + options.help()); + } + ServerOptions.BasePort = Port; + } + LoggingOptions.ApplyOptions(ServerOptions.LoggingConfig); + +#if ZEN_WITH_HTTPSYS + // Validate HTTPS options + const auto& HttpSys = ServerOptions.HttpConfig.HttpSys; + if (HttpSys.HttpsOnly && HttpSys.HttpsPort == 0) + { + throw OptionParseException("'--httpsys-https-only' requires '--httpsys-https-port' to be set", options.help()); + } + if (!HttpSys.CertThumbprint.empty() && HttpSys.CertThumbprint.size() != 40) + { + throw OptionParseException("'--httpsys-cert-thumbprint' must be exactly 40 hex characters (SHA-1)", options.help()); + } + if (!HttpSys.CertThumbprint.empty()) + { + for (char Ch : HttpSys.CertThumbprint) + { + if (!((Ch >= '0' && Ch <= '9') || (Ch >= 'a' && Ch <= 'f') || (Ch >= 'A' && Ch <= 'F'))) + { + throw OptionParseException("'--httpsys-cert-thumbprint' contains non-hex characters", options.help()); + } + } + } + if (HttpSys.HttpsPort > 0 && HttpSys.HttpsPort == ServerOptions.BasePort && !HttpSys.HttpsOnly) + { + throw OptionParseException("'--httpsys-https-port' must differ from '--port' when both HTTP and HTTPS are active", options.help()); + } +#endif + + // Validate --no-network + if (ServerOptions.HttpConfig.NoNetwork) + { + if (ServerOptions.HttpConfig.UnixSocketPath.empty()) + { + throw OptionParseException("'--no-network' requires '--unix-socket' to be set", options.help()); + } +#if ZEN_WITH_HTTPSYS + if (ServerOptions.HttpConfig.ServerClass == "httpsys") + { + throw OptionParseException("'--no-network' is not compatible with '--http=httpsys'", options.help()); + } +#endif + if (ServerOptions.HttpConfig.ServerClass.empty()) + { + ServerOptions.HttpConfig.ServerClass = "asio"; + } + } + + // Validate generic HTTPS options (used by ASIO backend) + if (ServerOptions.HttpConfig.HttpsPort > 0) + { + if (ServerOptions.HttpConfig.CertFile.empty() || ServerOptions.HttpConfig.KeyFile.empty()) + { + throw OptionParseException("'--https-port' requires both '--cert-file' and '--key-file' to be set", options.help()); + } + if (!std::filesystem::exists(ServerOptions.HttpConfig.CertFile)) + { + throw OptionParseException(fmt::format("'--cert-file' path '{}' does not exist", ServerOptions.HttpConfig.CertFile), + options.help()); + } + if (!std::filesystem::exists(ServerOptions.HttpConfig.KeyFile)) + { + throw OptionParseException(fmt::format("'--key-file' path '{}' does not exist", ServerOptions.HttpConfig.KeyFile), + options.help()); + } + if (ServerOptions.HttpConfig.HttpsPort == ServerOptions.BasePort) + { + throw OptionParseException("'--https-port' must differ from '--port'", options.help()); + } + } } ////////////////////////////////////////////////////////////////////////// @@ -423,6 +611,7 @@ ZenServerConfiguratorBase::Configure(int argc, char* argv[]) } cxxopts::Options options("zenserver", "Zen Storage Server"); + options.set_width(TuiConsoleColumns(80)); ZenServerCmdLineOptions BaseOptions; BaseOptions.AddCliOptions(options, m_ServerOptions); @@ -487,6 +676,14 @@ ZenServerConfiguratorBase::Configure(int argc, char* argv[]) } ValidateOptions(); // subclass validation + + // Resolve auto port: subclass ValidateOptions may have set a + // mode-specific default; if BasePort is still 0, fall back to the + // global default. + if (m_ServerOptions.BasePort == 0) + { + m_ServerOptions.BasePort = ZenServerConfig::kDefaultBasePort; + } } catch (const OptionParseException& e) { @@ -511,6 +708,7 @@ ZenServerConfiguratorBase::Configure(int argc, char* argv[]) } m_ServerOptions.HttpConfig.IsDedicatedServer = m_ServerOptions.IsDedicated; + m_ServerOptions.HttpConfig.AllowPortProbing = !m_ServerOptions.IsDedicated && m_ServerOptions.AllowPortProbing; } void |