diff options
| author | Stefan Boberg <[email protected]> | 2023-10-06 10:45:48 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-10-06 10:45:48 +0200 |
| commit | fb70324d37282910d7fa3047f4ec290d0c5a94b1 (patch) | |
| tree | a1bc82fcfdb96eb5b461742b613fcbb63f816a54 /src/zenserver/main.cpp | |
| parent | reject known bad bucket names in structured cache (#452) (diff) | |
| download | zen-fb70324d37282910d7fa3047f4ec290d0c5a94b1.tar.xz zen-fb70324d37282910d7fa3047f4ec290d0c5a94b1.zip | |
zenserver project restructuring (#442)
Diffstat (limited to 'src/zenserver/main.cpp')
| -rw-r--r-- | src/zenserver/main.cpp | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp new file mode 100644 index 000000000..18b907534 --- /dev/null +++ b/src/zenserver/main.cpp @@ -0,0 +1,390 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "zenserver.h" + +#include <zencore/compactbinarybuilder.h> +#include <zencore/compactbinaryvalidation.h> +#include <zencore/config.h> +#include <zencore/filesystem.h> +#include <zencore/fmtutils.h> +#include <zencore/logging.h> +#include <zencore/scopeguard.h> +#include <zencore/session.h> +#include <zencore/string.h> +#include <zencore/thread.h> +#include <zencore/timer.h> +#include <zencore/trace.h> +#include <zenhttp/httpserver.h> + +#include "config.h" +#include "diag/logging.h" +#include "sentryintegration.h" + +#if ZEN_USE_MIMALLOC +ZEN_THIRD_PARTY_INCLUDES_START +# include <mimalloc-new-delete.h> +# include <mimalloc.h> +ZEN_THIRD_PARTY_INCLUDES_END +#endif + +#if ZEN_PLATFORM_WINDOWS +# include <zencore/windows.h> +# include "windows/service.h" +#endif + +////////////////////////////////////////////////////////////////////////// +// We don't have any doctest code in this file but this is needed to bring +// in some shared code into the executable + +#if ZEN_WITH_TESTS +# define ZEN_TEST_WITH_RUNNER 1 +# include <zencore/testing.h> +#endif + +#include <memory> + +namespace zen::utils { +std::atomic_uint32_t SignalCounter[NSIG] = {0}; + +static void +SignalCallbackHandler(int SigNum) +{ + if (SigNum >= 0 && SigNum < NSIG) + { + SignalCounter[SigNum].fetch_add(1); + } +} +} // namespace zen::utils + +namespace zen { + +using namespace std::literals; + +//////////////////////////////////////////////////////////////////////////////// + +class ZenEntryPoint +{ +public: + ZenEntryPoint(ZenServerOptions& ServerOptions); + ZenEntryPoint(const ZenEntryPoint&) = delete; + ZenEntryPoint& operator=(const ZenEntryPoint&) = delete; + int Run(); + +private: + ZenServerOptions& m_ServerOptions; + LockFile m_LockFile; +}; + +ZenEntryPoint::ZenEntryPoint(ZenServerOptions& ServerOptions) : m_ServerOptions(ServerOptions) +{ +} + +int +ZenEntryPoint::Run() +{ +#if ZEN_USE_SENTRY + SentryIntegration Sentry; + + if (m_ServerOptions.NoSentry == false) + { + std::string SentryDatabasePath = PathToUtf8(m_ServerOptions.DataDir / ".sentry-native"); + std::string SentryAttachmentPath = PathToUtf8(m_ServerOptions.AbsLogFile); + + Sentry.Initialize(SentryDatabasePath, SentryAttachmentPath, m_ServerOptions.SentryAllowPII); + } +#endif + + try + { + // Mutual exclusion and synchronization + ZenServerState ServerState; + ServerState.Initialize(); + ServerState.Sweep(); + + ZenServerState::ZenServerEntry* Entry = ServerState.Lookup(m_ServerOptions.BasePort); + + if (Entry) + { + if (m_ServerOptions.OwnerPid) + { + ZEN_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(), + m_ServerOptions.OwnerPid); + + Entry->AddSponsorProcess(m_ServerOptions.OwnerPid); + + std::exit(0); + } + else + { + ZEN_WARN("Exiting since there is already a process listening to port {} (pid: {})", + m_ServerOptions.BasePort, + Entry->Pid.load()); + std::exit(1); + } + } + + std::error_code Ec; + + std::filesystem::path LockFilePath = m_ServerOptions.DataDir / ".lock"; + + bool IsReady = false; + + auto MakeLockData = [&] { + CbObjectWriter Cbo; + Cbo << "pid" << GetCurrentProcessId() << "data" << PathToUtf8(m_ServerOptions.DataDir) << "port" << m_ServerOptions.BasePort + << "session_id" << GetSessionId() << "ready" << IsReady; + return Cbo.Save(); + }; + + m_LockFile.Create(LockFilePath, MakeLockData(), Ec); + + if (Ec) + { + ZEN_WARN("ERROR: Unable to grab lock at '{}' (error: '{}')", LockFilePath, Ec.message()); + + std::exit(99); + } + + InitializeLogging(m_ServerOptions); + +#if ZEN_USE_SENTRY + Sentry.LogStartupInformation(); +#endif + + MaximizeOpenFileCount(); + + 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); + + Entry = ServerState.Register(m_ServerOptions.BasePort); + + if (m_ServerOptions.OwnerPid) + { + Entry->AddSponsorProcess(m_ServerOptions.OwnerPid); + } + + ZenServer Server; + Server.SetDataRoot(m_ServerOptions.DataDir); + Server.SetContentRoot(m_ServerOptions.ContentDir); + Server.SetTestMode(m_ServerOptions.IsTest); + Server.SetDedicatedMode(m_ServerOptions.IsDedicated); + + auto ServerCleanup = MakeGuard([&Server] { Server.Cleanup(); }); + + int EffectiveBasePort = Server.Initialize(m_ServerOptions, Entry); + + Entry->EffectiveListenPort = uint16_t(EffectiveBasePort); + if (EffectiveBasePort != m_ServerOptions.BasePort) + { + ZEN_INFO(ZEN_APP_NAME " - relocated to base port {}", EffectiveBasePort); + m_ServerOptions.BasePort = EffectiveBasePort; + } + + std::unique_ptr<std::thread> ShutdownThread; + std::unique_ptr<NamedEvent> ShutdownEvent; + + ExtendableStringBuilder<64> ShutdownEventName; + ShutdownEventName << "Zen_" << m_ServerOptions.BasePort << "_Shutdown"; + ShutdownEvent.reset(new NamedEvent{ShutdownEventName}); + + // Monitor shutdown signals + + ShutdownThread.reset(new std::thread{[&] { + SetCurrentThreadName("shutdown_monitor"); + + ZEN_INFO("shutdown monitor thread waiting for shutdown signal '{}'", ShutdownEventName); + + if (ShutdownEvent->Wait()) + { + if (!IsApplicationExitRequested()) + { + ZEN_INFO("shutdown signal received"); + Server.RequestExit(0); + } + } + else + { + ZEN_INFO("shutdown signal wait() failed"); + } + }}); + + auto CleanupShutdown = MakeGuard([&ShutdownEvent, &ShutdownThread] { + if (ShutdownEvent) + { + ShutdownEvent->Set(); + } + if (ShutdownThread && ShutdownThread->joinable()) + { + ShutdownThread->join(); + } + }); + + // If we have a parent process, establish the mechanisms we need + // to be able to communicate readiness with the parent + + Server.SetIsReadyFunc([&] { + IsReady = true; + + m_LockFile.Update(MakeLockData(), Ec); + + if (!m_ServerOptions.ChildId.empty()) + { + NamedEvent ParentEvent{m_ServerOptions.ChildId}; + ParentEvent.Set(); + } + }); + + Server.Run(); + } + catch (std::exception& e) + { + SPDLOG_CRITICAL("Caught exception in main: {}", e.what()); + if (!IsApplicationExitRequested()) + { + RequestApplicationExit(1); + } + } + + ShutdownLogging(); + + return ApplicationExitCode(); +} + +////////////////////////////////////////////////////////////////////////// + +#if ZEN_PLATFORM_WINDOWS + +class ZenWindowsService : public WindowsService +{ +public: + ZenWindowsService(ZenServerOptions& ServerOptions) : m_EntryPoint(ServerOptions) {} + + ZenWindowsService(const ZenWindowsService&) = delete; + ZenWindowsService& operator=(const ZenWindowsService&) = delete; + + virtual int Run() override; + +private: + ZenEntryPoint m_EntryPoint; +}; + +int +ZenWindowsService::Run() +{ + return m_EntryPoint.Run(); +} + +#endif // ZEN_PLATFORM_WINDOWS + +} // namespace zen + +////////////////////////////////////////////////////////////////////////// + +#if ZEN_WITH_TESTS +int +test_main(int argc, char** argv) +{ + zen::zencore_forcelinktests(); + zen::zenhttp_forcelinktests(); + zen::zenstore_forcelinktests(); + zen::z$_forcelink(); + zen::z$service_forcelink(); + + zen::logging::InitializeLogging(); + spdlog::set_level(spdlog::level::debug); + + zen::MaximizeOpenFileCount(); + + return ZEN_RUN_TESTS(argc, argv); +} +#endif + +int +main(int argc, char* argv[]) +{ + using namespace zen; + +#if ZEN_USE_MIMALLOC + mi_version(); +#endif + + if (argc >= 2) + { + if (argv[1] == "test"sv) + { +#if ZEN_WITH_TESTS + return test_main(argc, argv); +#else + fprintf(stderr, "test option not available in release mode!\n"); + exit(5); +#endif + } + } + + signal(SIGINT, utils::SignalCallbackHandler); + + try + { + ZenServerOptions ServerOptions; + ParseCliOptions(argc, argv, ServerOptions); + + if (!std::filesystem::exists(ServerOptions.DataDir)) + { + ServerOptions.IsFirstRun = true; + std::filesystem::create_directories(ServerOptions.DataDir); + } + +#if ZEN_WITH_TRACE + if (ServerOptions.TraceHost.size()) + { + TraceStart(ServerOptions.TraceHost.c_str(), TraceType::Network); + } + else if (ServerOptions.TraceFile.size()) + { + TraceStart(ServerOptions.TraceFile.c_str(), TraceType::File); + } + else + { + TraceInit(); + } + atexit(TraceShutdown); +#endif // ZEN_WITH_TRACE + +#if ZEN_PLATFORM_WINDOWS + if (ServerOptions.InstallService) + { + WindowsService::Install(); + + std::exit(0); + } + + if (ServerOptions.UninstallService) + { + WindowsService::Delete(); + + std::exit(0); + } + + ZenWindowsService App(ServerOptions); + return App.ServiceMain(); +#else + if (ServerOptions.InstallService || ServerOptions.UninstallService) + { + throw std::runtime_error("Service mode is not supported on this platform"); + } + + ZenEntryPoint App(ServerOptions); + return App.Run(); +#endif + } + catch (std::exception& Ex) + { + fprintf(stderr, "ERROR: Caught exception in main: '%s'", Ex.what()); + + return 1; + } +} |