aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/main.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-10-06 10:45:48 +0200
committerGitHub <[email protected]>2023-10-06 10:45:48 +0200
commitfb70324d37282910d7fa3047f4ec290d0c5a94b1 (patch)
treea1bc82fcfdb96eb5b461742b613fcbb63f816a54 /src/zenserver/main.cpp
parentreject known bad bucket names in structured cache (#452) (diff)
downloadzen-fb70324d37282910d7fa3047f4ec290d0c5a94b1.tar.xz
zen-fb70324d37282910d7fa3047f4ec290d0c5a94b1.zip
zenserver project restructuring (#442)
Diffstat (limited to 'src/zenserver/main.cpp')
-rw-r--r--src/zenserver/main.cpp390
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;
+ }
+}