aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/zenserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenserver/zenserver.cpp')
-rw-r--r--src/zenserver/zenserver.cpp260
1 files changed, 210 insertions, 50 deletions
diff --git a/src/zenserver/zenserver.cpp b/src/zenserver/zenserver.cpp
index 5fd35d9b4..087b40d6a 100644
--- a/src/zenserver/zenserver.cpp
+++ b/src/zenserver/zenserver.cpp
@@ -23,9 +23,11 @@
#include <zencore/timer.h>
#include <zencore/trace.h>
#include <zencore/workthreadpool.h>
+#include <zenhttp/httpclient.h>
#include <zenhttp/httpserver.h>
#include <zenhttp/security/passwordsecurityfilter.h>
#include <zentelemetry/otlptrace.h>
+#include <zenutil/authutils.h>
#include <zenutil/service.h>
#include <zenutil/workerpools.h>
#include <zenutil/zenserverprocess.h>
@@ -46,6 +48,20 @@ ZEN_THIRD_PARTY_INCLUDES_END
//////////////////////////////////////////////////////////////////////////
+#ifndef ZEN_WITH_COMPUTE_SERVICES
+# define ZEN_WITH_COMPUTE_SERVICES 0
+#endif
+
+#ifndef ZEN_WITH_HORDE
+# define ZEN_WITH_HORDE 0
+#endif
+
+#ifndef ZEN_WITH_NOMAD
+# define ZEN_WITH_NOMAD 0
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+
#include "config/config.h"
#include "diag/logging.h"
@@ -85,6 +101,7 @@ ZenServerBase::Initialize(const ZenServerConfig& ServerOptions, ZenServerState::
ZEN_MEMSCOPE(GetZenserverTag());
m_IsPowerCycle = ServerOptions.IsPowerCycle;
+ m_NoNetwork = ServerOptions.HttpConfig.NoNetwork;
const std::string MutexName = fmt::format("zen_{}", ServerOptions.BasePort);
@@ -131,6 +148,14 @@ ZenServerBase::Initialize(const ZenServerConfig& ServerOptions, ZenServerState::
EnqueueSigIntTimer();
+ // Configure HTTP client back-end
+
+ const std::string HttpClientBackend = ToLower(ServerOptions.HttpClient.Backend);
+ zen::SetDefaultHttpClientBackend(HttpClientBackend);
+ ZEN_INFO("Using '{}' as HTTP client backend", HttpClientBackend);
+
+ // Initialize HTTP server
+
m_Http = CreateHttpServer(ServerOptions.HttpConfig);
int EffectiveBasePort = m_Http->Initialize(ServerOptions.BasePort, ServerOptions.DataDir);
if (EffectiveBasePort == 0)
@@ -155,6 +180,7 @@ ZenServerBase::Initialize(const ZenServerConfig& ServerOptions, ZenServerState::
m_StatusService.RegisterHandler("status", *this);
m_Http->RegisterService(m_StatusService);
+ m_Http->RegisterService(m_StatsService);
m_StatsReporter.Initialize(ServerOptions.StatsConfig);
if (ServerOptions.StatsConfig.Enabled)
@@ -162,10 +188,40 @@ ZenServerBase::Initialize(const ZenServerConfig& ServerOptions, ZenServerState::
EnqueueStatsReportingTimer();
}
- m_HealthService.SetHealthInfo({.DataRoot = ServerOptions.DataDir,
- .AbsLogPath = ServerOptions.LoggingConfig.AbsLogFile,
- .HttpServerClass = std::string(ServerOptions.HttpConfig.ServerClass),
- .BuildVersion = std::string(ZEN_CFG_VERSION_BUILD_STRING_FULL)});
+ // clang-format off
+ HealthServiceInfo HealthInfo {
+ .DataRoot = ServerOptions.DataDir,
+ .AbsLogPath = ServerOptions.LoggingConfig.AbsLogFile,
+ .HttpServerClass = std::string(ServerOptions.HttpConfig.ServerClass),
+ .BuildVersion = std::string(ZEN_CFG_VERSION_BUILD_STRING_FULL),
+ .Port = EffectiveBasePort,
+ .Pid = GetCurrentProcessId(),
+ .IsDedicated = ServerOptions.IsDedicated,
+ .StartTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now().time_since_epoch()).count(),
+ .BuildOptions = {
+ {"ZEN_ADDRESS_SANITIZER", ZEN_ADDRESS_SANITIZER != 0},
+ {"ZEN_THREAD_SANITIZER", ZEN_THREAD_SANITIZER != 0},
+ {"ZEN_MEMORY_SANITIZER", ZEN_MEMORY_SANITIZER != 0},
+ {"ZEN_LEAK_SANITIZER", ZEN_LEAK_SANITIZER != 0},
+ {"ZEN_USE_SENTRY", ZEN_USE_SENTRY != 0},
+ {"ZEN_WITH_TESTS", ZEN_WITH_TESTS != 0},
+ {"ZEN_USE_MIMALLOC", ZEN_USE_MIMALLOC != 0},
+ {"ZEN_USE_RPMALLOC", ZEN_USE_RPMALLOC != 0},
+ {"ZEN_WITH_HTTPSYS", ZEN_WITH_HTTPSYS != 0},
+ {"ZEN_WITH_MEMTRACK", ZEN_WITH_MEMTRACK != 0},
+ {"ZEN_WITH_TRACE", ZEN_WITH_TRACE != 0},
+ {"ZEN_WITH_COMPUTE_SERVICES", ZEN_WITH_COMPUTE_SERVICES != 0},
+ {"ZEN_WITH_HORDE", ZEN_WITH_HORDE != 0},
+ {"ZEN_WITH_NOMAD", ZEN_WITH_NOMAD != 0},
+ },
+ .RuntimeConfig = BuildSettingsList(ServerOptions),
+ };
+ // clang-format on
+
+ HealthInfo.RuntimeConfig.emplace(HealthInfo.RuntimeConfig.begin() + 2, "EffectivePort"sv, fmt::to_string(EffectiveBasePort));
+
+ m_HealthService.SetHealthInfo(std::move(HealthInfo));
LogSettingsSummary(ServerOptions);
@@ -175,18 +231,35 @@ ZenServerBase::Initialize(const ZenServerConfig& ServerOptions, ZenServerState::
void
ZenServerBase::Finalize()
{
+ m_StatsService.RegisterHandler("http", *m_Http);
+
+ m_Http->SetDefaultRedirect("/dashboard/");
+
// Register health service last so if we return "OK" for health it means all services have been properly initialized
m_Http->RegisterService(m_HealthService);
}
void
+ZenServerBase::ShutdownServices()
+{
+ m_StatsService.UnregisterHandler("http", *m_Http);
+ m_StatsService.Shutdown();
+}
+
+void
ZenServerBase::GetBuildOptions(StringBuilderBase& OutOptions, char Separator) const
{
ZEN_MEMSCOPE(GetZenserverTag());
OutOptions << "ZEN_ADDRESS_SANITIZER=" << (ZEN_ADDRESS_SANITIZER ? "1" : "0");
OutOptions << Separator;
+ OutOptions << "ZEN_THREAD_SANITIZER=" << (ZEN_THREAD_SANITIZER ? "1" : "0");
+ OutOptions << Separator;
+ OutOptions << "ZEN_MEMORY_SANITIZER=" << (ZEN_MEMORY_SANITIZER ? "1" : "0");
+ OutOptions << Separator;
+ OutOptions << "ZEN_LEAK_SANITIZER=" << (ZEN_LEAK_SANITIZER ? "1" : "0");
+ OutOptions << Separator;
OutOptions << "ZEN_USE_SENTRY=" << (ZEN_USE_SENTRY ? "1" : "0");
OutOptions << Separator;
OutOptions << "ZEN_WITH_TESTS=" << (ZEN_WITH_TESTS ? "1" : "0");
@@ -199,6 +272,8 @@ ZenServerBase::GetBuildOptions(StringBuilderBase& OutOptions, char Separator) co
OutOptions << Separator;
OutOptions << "ZEN_WITH_MEMTRACK=" << (ZEN_WITH_MEMTRACK ? "1" : "0");
OutOptions << Separator;
+ OutOptions << "ZEN_WITH_COMPUTE_SERVICES=" << (ZEN_WITH_COMPUTE_SERVICES ? "1" : "0");
+ OutOptions << Separator;
OutOptions << "ZEN_WITH_TRACE=" << (ZEN_WITH_TRACE ? "1" : "0");
}
@@ -386,32 +461,43 @@ ZenServerBase::CheckSigInt()
void
ZenServerBase::HandleStatusRequest(HttpServerRequest& Request)
{
+ auto Metrics = m_MetricsTracker.Query();
+
CbObjectWriter Cbo;
Cbo << "ok" << true;
Cbo << "state" << ToString(m_CurrentState);
+ Cbo << "hostname" << GetMachineName();
+ Cbo << "cpuUsagePercent" << Metrics.CpuUsagePercent;
+ Cbo << "serverMode" << std::string_view(m_ServerMode);
+
+ std::vector<std::string> IpAddresses = GetLocalIpAddresses();
+ Cbo.BeginArray("ipAddresses"sv);
+ for (const std::string& Ip : IpAddresses)
+ {
+ Cbo << Ip;
+ }
+ Cbo.EndArray();
+
Request.WriteResponse(HttpResponseCode::OK, Cbo.Save());
}
-void
-ZenServerBase::LogSettingsSummary(const ZenServerConfig& ServerConfig)
+std::vector<std::pair<std::string_view, std::string>>
+ZenServerBase::BuildSettingsList(const ZenServerConfig& ServerConfig)
{
// clang-format off
- std::list<std::pair<std::string_view, std::string>> Settings = {
- {"DataDir"sv, fmt::format("{}", ServerConfig.DataDir)},
- {"AbsLogFile"sv, fmt::format("{}", ServerConfig.LoggingConfig.AbsLogFile)},
+ std::vector<std::pair<std::string_view, std::string>> Settings = {
{"SystemRootDir"sv, fmt::format("{}", ServerConfig.SystemRootDir)},
{"ContentDir"sv, fmt::format("{}", ServerConfig.ContentDir)},
{"BasePort"sv, fmt::to_string(ServerConfig.BasePort)},
+ {"CoreLimit"sv, fmt::to_string(ServerConfig.CoreLimit)},
+ {"MemoryAllocator"sv, std::string(GMalloc->GetName())},
+ {"AsioVersion"sv, fmt::format("{}.{}.{}", ASIO_VERSION / 100000, (ASIO_VERSION / 100) % 1000, ASIO_VERSION % 100)},
{"IsDebug"sv, fmt::to_string(ServerConfig.IsDebug)},
{"IsCleanStart"sv, fmt::to_string(ServerConfig.IsCleanStart)},
- {"IsPowerCycle"sv, fmt::to_string(ServerConfig.IsPowerCycle)},
{"IsTest"sv, fmt::to_string(ServerConfig.IsTest)},
{"Detach"sv, fmt::to_string(ServerConfig.Detach)},
{"NoConsoleOutput"sv, fmt::to_string(ServerConfig.LoggingConfig.NoConsoleOutput)},
{"QuietConsole"sv, fmt::to_string(ServerConfig.LoggingConfig.QuietConsole)},
- {"CoreLimit"sv, fmt::to_string(ServerConfig.CoreLimit)},
- {"IsDedicated"sv, fmt::to_string(ServerConfig.IsDedicated)},
- {"ShouldCrash"sv, fmt::to_string(ServerConfig.ShouldCrash)},
{"ChildId"sv, ServerConfig.ChildId},
{"LogId"sv, ServerConfig.LoggingConfig.LogId},
{"Sentry DSN"sv, ServerConfig.SentryConfig.Dsn.empty() ? "not set" : ServerConfig.SentryConfig.Dsn},
@@ -423,10 +509,29 @@ ZenServerBase::LogSettingsSummary(const ZenServerConfig& ServerConfig)
if (ServerConfig.StatsConfig.Enabled)
{
- Settings.emplace_back("Statsd Host", ServerConfig.StatsConfig.StatsdHost);
- Settings.emplace_back("Statsd Port", fmt::to_string(ServerConfig.StatsConfig.StatsdPort));
+ Settings.emplace_back("Statsd Host"sv, ServerConfig.StatsConfig.StatsdHost);
+ Settings.emplace_back("Statsd Port"sv, fmt::to_string(ServerConfig.StatsConfig.StatsdPort));
}
+ return Settings;
+}
+
+void
+ZenServerBase::LogSettingsSummary(const ZenServerConfig& ServerConfig)
+{
+ auto Settings = BuildSettingsList(ServerConfig);
+
+ // Log-only entries not needed in RuntimeConfig
+ // clang-format off
+ Settings.insert(Settings.begin(), {
+ {"DataDir"sv, fmt::format("{}", ServerConfig.DataDir)},
+ {"AbsLogFile"sv, fmt::format("{}", ServerConfig.LoggingConfig.AbsLogFile)},
+ });
+ // clang-format on
+ Settings.emplace_back("IsDedicated"sv, fmt::to_string(ServerConfig.IsDedicated));
+ Settings.emplace_back("AllowPortProbing"sv, fmt::to_string(ServerConfig.AllowPortProbing));
+ Settings.emplace_back("ShouldCrash"sv, fmt::to_string(ServerConfig.ShouldCrash));
+
size_t MaxWidth = 0;
for (const auto& Setting : Settings)
{
@@ -538,6 +643,10 @@ ZenServerMain::Run()
uint32_t AttachSponsorProcessRetriesLeft = 3;
ZenServerState::ZenServerEntry* Entry = ServerState.Lookup(m_ServerOptions.BasePort);
+ // NOTE: ZEN_CONSOLE_WARN/INFO is used in this block and the lock file block below
+ // (instead of ZEN_WARN/INFO) because InitializeLogging() has not been called yet at
+ // this point. ZEN_WARN/INFO would silently discard messages before the logging system
+ // is initialized.
while (Entry)
{
if (m_ServerOptions.OwnerPid)
@@ -547,27 +656,29 @@ ZenServerMain::Run()
{
if (Ec)
{
- ZEN_WARN(ZEN_APP_NAME
- " exiting, sponsor owner pid {} can not be checked for running state, reason: '{}'. Will not add sponsor "
- "to process "
- "listening to port {} (pid: {})",
- m_ServerOptions.OwnerPid,
- Ec.message(),
- m_ServerOptions.BasePort,
- Entry->Pid.load());
+ ZEN_CONSOLE_WARN(
+ ZEN_APP_NAME
+ " exiting, sponsor owner pid {} can not be checked for running state, reason: '{}'. Will not add sponsor "
+ "to process "
+ "listening to port {} (pid: {})",
+ m_ServerOptions.OwnerPid,
+ Ec.message(),
+ m_ServerOptions.BasePort,
+ Entry->Pid.load());
}
else
{
- ZEN_WARN(ZEN_APP_NAME
- " exiting, sponsor owner pid {} is no longer running, will not add sponsor to process listening to port "
- "{} (pid: {})",
- m_ServerOptions.OwnerPid,
- m_ServerOptions.BasePort,
- Entry->Pid.load());
+ ZEN_CONSOLE_WARN(
+ ZEN_APP_NAME
+ " exiting, sponsor owner pid {} is no longer running, will not add sponsor to process listening to port "
+ "{} (pid: {})",
+ m_ServerOptions.OwnerPid,
+ m_ServerOptions.BasePort,
+ Entry->Pid.load());
}
std::exit(1);
}
- ZEN_INFO(
+ ZEN_CONSOLE_INFO(
"Looks like there is already a process listening to this port {} (pid: {}), attaching owner pid {} to running instance",
m_ServerOptions.BasePort,
Entry->Pid.load(),
@@ -585,18 +696,40 @@ ZenServerMain::Run()
}
else
{
- ZEN_WARN(ZEN_APP_NAME " exiting, failed to add sponsor owner pid {} to process listening to port {} (pid: {})",
- m_ServerOptions.OwnerPid,
- m_ServerOptions.BasePort,
- Entry->Pid.load());
- std::exit(1);
+ // The entry's process failed to pick up our sponsor request after
+ // multiple attempts. Before reclaiming the entry, verify that the
+ // PID does not still belong to a zenserver process. If it does, the
+ // server is alive but unresponsive – fall back to the original error
+ // path. If the PID is gone or belongs to a different executable the
+ // entry is genuinely stale and safe to reclaim.
+ const int StalePid = Entry->Pid.load();
+ std::error_code ExeEc;
+ std::filesystem::path PidExePath = GetProcessExecutablePath(StalePid, ExeEc);
+ const bool PidIsZenServer = !ExeEc && (PidExePath.filename() == GetRunningExecutablePath().filename());
+ if (PidIsZenServer)
+ {
+ ZEN_CONSOLE_WARN(ZEN_APP_NAME
+ " exiting, failed to add sponsor to process on port {} "
+ "(pid {}); that pid is still a running zenserver instance",
+ m_ServerOptions.BasePort,
+ StalePid);
+ std::exit(1);
+ }
+ ZEN_CONSOLE_WARN(
+ "Failed to add sponsor to process on port {} (pid {}); "
+ "pid belongs to '{}' – assuming stale entry and reclaiming",
+ m_ServerOptions.BasePort,
+ StalePid,
+ ExeEc ? "<unknown>" : PidExePath.filename().string());
+ Entry->Reset();
+ Entry = nullptr;
}
}
else
{
- ZEN_WARN(ZEN_APP_NAME " exiting, there is already a process listening to port {} (pid: {})",
- m_ServerOptions.BasePort,
- Entry->Pid.load());
+ ZEN_CONSOLE_WARN(ZEN_APP_NAME " exiting, there is already a process listening to port {} (pid: {})",
+ m_ServerOptions.BasePort,
+ Entry->Pid.load());
std::exit(1);
}
}
@@ -609,19 +742,19 @@ ZenServerMain::Run()
if (Ec)
{
- ZEN_INFO(ZEN_APP_NAME " unable to grab lock at '{}' (reason: '{}'), retrying", LockFilePath, Ec.message());
+ ZEN_CONSOLE_INFO(ZEN_APP_NAME " unable to grab lock at '{}' (reason: '{}'), retrying", LockFilePath, Ec.message());
Sleep(100);
m_LockFile.Create(LockFilePath, MakeLockData(false), Ec);
if (Ec)
{
- ZEN_INFO(ZEN_APP_NAME " unable to grab lock at '{}' (reason: '{}'), retrying", LockFilePath, Ec.message());
+ ZEN_CONSOLE_INFO(ZEN_APP_NAME " unable to grab lock at '{}' (reason: '{}'), retrying", LockFilePath, Ec.message());
Sleep(500);
m_LockFile.Create(LockFilePath, MakeLockData(false), Ec);
if (Ec)
{
- ZEN_WARN(ZEN_APP_NAME " exiting, unable to grab lock at '{}' (reason: '{}')", LockFilePath, Ec.message());
+ ZEN_CONSOLE_WARN(ZEN_APP_NAME " exiting, unable to grab lock at '{}' (reason: '{}')", LockFilePath, Ec.message());
std::exit(99);
}
}
@@ -640,9 +773,31 @@ ZenServerMain::Run()
ZEN_INFO(ZEN_APP_NAME " - using lock file at '{}'", LockFilePath);
ZEN_INFO(ZEN_APP_NAME " - starting on port {}, version '{}'", m_ServerOptions.BasePort, ZEN_CFG_VERSION_BUILD_STRING_FULL);
+ ZEN_INFO(ZEN_APP_NAME " - memory allocator: {}", GMalloc->GetName());
+ ZEN_INFO(ZEN_APP_NAME " - asio: {}.{}.{}", ASIO_VERSION / 100000, (ASIO_VERSION / 100) % 1000, ASIO_VERSION % 100);
Entry = ServerState.Register(m_ServerOptions.BasePort);
+ if (!Entry)
+ {
+ throw std::runtime_error(
+ fmt::format("Failed to register server on port {} in shared state (all slots occupied)", m_ServerOptions.BasePort));
+ }
+
+ // Publish per-instance extended info (e.g. UDS path) via a small shared memory
+ // section keyed by SessionId so clients can discover it during Snapshot() enumeration.
+ {
+ InstanceInfoData InstanceData;
+ InstanceData.UnixSocketPath = m_ServerOptions.HttpConfig.UnixSocketPath;
+ m_InstanceInfo.Create(GetSessionId(), InstanceData);
+ Entry->SignalHasInstanceInfo();
+ }
+
+ if (m_ServerOptions.HttpConfig.NoNetwork)
+ {
+ Entry->SignalNoNetwork();
+ }
+
if (m_ServerOptions.OwnerPid)
{
// We are adding a sponsor process to our own entry, can't wait for pick since the code is not run until later
@@ -655,25 +810,29 @@ ZenServerMain::Run()
}
catch (const AssertException& AssertEx)
{
- ZEN_CRITICAL(ZEN_APP_NAME " caught assert exception in main for process {}: {}",
- zen::GetCurrentProcessId(),
- AssertEx.FullDescription());
+ ZEN_CONSOLE_CRITICAL(ZEN_APP_NAME " caught assert exception in main for process {}: {}",
+ zen::GetCurrentProcessId(),
+ AssertEx.FullDescription());
RequestApplicationExit(1);
}
catch (const std::system_error& e)
{
- ZEN_CRITICAL(ZEN_APP_NAME " caught system error exception in main for process {}: {} ({})",
- zen::GetCurrentProcessId(),
- e.what(),
- e.code().value());
+ ZEN_CONSOLE_CRITICAL(ZEN_APP_NAME " caught system error exception in main for process {}: {} ({})",
+ zen::GetCurrentProcessId(),
+ e.what(),
+ e.code().value());
RequestApplicationExit(1);
}
catch (const std::exception& e)
{
- ZEN_CRITICAL(ZEN_APP_NAME " caught exception in main for process {}: {}", zen::GetCurrentProcessId(), e.what());
+ ZEN_CONSOLE_CRITICAL(ZEN_APP_NAME " caught exception in main for process {}: {}", zen::GetCurrentProcessId(), e.what());
RequestApplicationExit(1);
}
+#if ZEN_USE_SENTRY
+ Sentry.Close();
+#endif
+
ShutdownServerLogging();
ReportServiceStatus(ServiceStatus::Stopped);
@@ -699,7 +858,8 @@ ZenServerMain::MakeLockData(bool IsReady)
.EffectiveListenPort = gsl::narrow<uint16_t>(m_ServerOptions.BasePort),
.Ready = IsReady,
.DataDir = m_ServerOptions.DataDir,
- .ExecutablePath = GetRunningExecutablePath()});
+ .ExecutablePath = GetRunningExecutablePath(),
+ .UnixSocketPath = m_ServerOptions.HttpConfig.UnixSocketPath});
};
} // namespace zen