aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-09-17 23:18:20 +0200
committerStefan Boberg <[email protected]>2021-09-17 23:18:48 +0200
commit0ee89539ead8631b02953ddf601770aefa557edb (patch)
treeef2e09d747bb3d7497507362bda560b905d478d6
parentAdded IsInteractiveSession() query to help identify if the process is running... (diff)
downloadzen-0ee89539ead8631b02953ddf601770aefa557edb.tar.xz
zen-0ee89539ead8631b02953ddf601770aefa557edb.zip
zenserver can now run as a Windows service. We'll still need to improve how data files are found as the current defaults are relative to the user directory which ends up being in the Windows folder when running as the local system user
-rw-r--r--zenhttp/httpnull.cpp4
-rw-r--r--zenhttp/httpnull.h2
-rw-r--r--zenhttp/httpsys.cpp23
-rw-r--r--zenhttp/httpuws.cpp4
-rw-r--r--zenhttp/httpuws.h2
-rw-r--r--zenhttp/include/zenhttp/httpserver.h2
-rw-r--r--zenserver/windows/service.cpp246
-rw-r--r--zenserver/windows/service.h21
-rw-r--r--zenserver/zenserver.cpp79
9 files changed, 201 insertions, 182 deletions
diff --git a/zenhttp/httpnull.cpp b/zenhttp/httpnull.cpp
index 57cba13d3..e49051ac5 100644
--- a/zenhttp/httpnull.cpp
+++ b/zenhttp/httpnull.cpp
@@ -28,8 +28,10 @@ HttpNullServer::Initialize(int BasePort)
}
void
-HttpNullServer::Run(bool TestMode)
+HttpNullServer::Run(bool IsInteractiveSession)
{
+ const bool TestMode = !IsInteractiveSession;
+
if (TestMode == false)
{
zen::logging::ConsoleLog().info("Zen Server running (null HTTP). Press ESC or Q to quit");
diff --git a/zenhttp/httpnull.h b/zenhttp/httpnull.h
index b15b1b123..867bbe4d2 100644
--- a/zenhttp/httpnull.h
+++ b/zenhttp/httpnull.h
@@ -19,7 +19,7 @@ public:
virtual void RegisterService(HttpService& Service) override;
virtual void Initialize(int BasePort) override;
- virtual void Run(bool TestMode) override;
+ virtual void Run(bool IsInteractiveSession) override;
virtual void RequestExit() override;
private:
diff --git a/zenhttp/httpsys.cpp b/zenhttp/httpsys.cpp
index c2d4ef14c..b5313021c 100644
--- a/zenhttp/httpsys.cpp
+++ b/zenhttp/httpsys.cpp
@@ -707,29 +707,30 @@ HttpSysServer::StartServer()
}
void
-HttpSysServer::Run(bool TestMode)
+HttpSysServer::Run(bool IsInteractive)
{
- if (TestMode == false)
+ if (IsInteractive)
{
zen::logging::ConsoleLog().info("Zen Server running. Press ESC or Q to quit");
}
do
{
- int WaitTimeout = -1;
+ //int WaitTimeout = -1;
+ int WaitTimeout = 100;
- if (!TestMode)
+ if (IsInteractive)
{
WaitTimeout = 1000;
- }
-
- if (!TestMode && _kbhit() != 0)
- {
- char c = (char)_getch();
- if (c == 27 || c == 'Q' || c == 'q')
+ if (_kbhit() != 0)
{
- RequestApplicationExit(0);
+ char c = (char)_getch();
+
+ if (c == 27 || c == 'Q' || c == 'q')
+ {
+ RequestApplicationExit(0);
+ }
}
}
diff --git a/zenhttp/httpuws.cpp b/zenhttp/httpuws.cpp
index 992809b17..e062e7747 100644
--- a/zenhttp/httpuws.cpp
+++ b/zenhttp/httpuws.cpp
@@ -37,8 +37,10 @@ HttpUwsServer::Initialize(int BasePort)
}
void
-HttpUwsServer::Run(bool TestMode)
+HttpUwsServer::Run(bool IsInteractive)
{
+ const bool TestMode = !IsInteractive;
+
if (TestMode == false)
{
zen::logging::ConsoleLog().info("Zen Server running (null HTTP). Press ESC or Q to quit");
diff --git a/zenhttp/httpuws.h b/zenhttp/httpuws.h
index ec55ae2fd..5e300202f 100644
--- a/zenhttp/httpuws.h
+++ b/zenhttp/httpuws.h
@@ -16,7 +16,7 @@ public:
virtual void RegisterService(HttpService& Service) override;
virtual void Initialize(int BasePort) override;
- virtual void Run(bool TestMode) override;
+ virtual void Run(bool IsInteractiveSession) override;
virtual void RequestExit() override;
private:
diff --git a/zenhttp/include/zenhttp/httpserver.h b/zenhttp/include/zenhttp/httpserver.h
index ed6075c92..6a7dc8a70 100644
--- a/zenhttp/include/zenhttp/httpserver.h
+++ b/zenhttp/include/zenhttp/httpserver.h
@@ -167,7 +167,7 @@ class HttpServer : public RefCounted
public:
virtual void RegisterService(HttpService& Service) = 0;
virtual void Initialize(int BasePort) = 0;
- virtual void Run(bool TestMode) = 0;
+ virtual void Run(bool IsInteractiveSession) = 0;
virtual void RequestExit() = 0;
};
diff --git a/zenserver/windows/service.cpp b/zenserver/windows/service.cpp
index 7a7864b39..bd80e0c2c 100644
--- a/zenserver/windows/service.cpp
+++ b/zenserver/windows/service.cpp
@@ -1,3 +1,5 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
#include "service.h"
#include <zencore/zencore.h>
@@ -12,14 +14,19 @@ SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
-void SvcInstall(void);
-void WINAPI SvcCtrlHandler(DWORD);
-void WINAPI SvcMain(DWORD, LPTSTR*);
+void SvcInstall(void);
void ReportSvcStatus(DWORD, DWORD, DWORD);
-void SvcInit(DWORD, LPTSTR*);
void SvcReportEvent(LPTSTR);
+WindowsService::WindowsService()
+{
+}
+
+WindowsService::~WindowsService()
+{
+}
+
//
// Purpose:
// Installs a service in the SCM database
@@ -31,7 +38,7 @@ void SvcReportEvent(LPTSTR);
// None
//
VOID
-SvcInstall()
+WindowsService::Install()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
@@ -84,17 +91,8 @@ SvcInstall()
CloseServiceHandle(schSCManager);
}
-//
-// Purpose:
-// Deletes a service from the SCM database
-//
-// Parameters:
-// None
-//
-// Return value:
-// None
-//
-void SvcDelete()
+void
+WindowsService::Delete()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
@@ -137,6 +135,89 @@ void SvcDelete()
CloseServiceHandle(schSCManager);
}
+WindowsService* gSvc;
+
+void WINAPI
+CallMain(DWORD, LPSTR*)
+{
+ gSvc->SvcMain();
+}
+
+int
+WindowsService::ServiceMain()
+{
+ if (zen::IsInteractiveSession())
+ {
+ // Not actually running as a service
+ return Run();
+ }
+ else
+ {
+ gSvc = this;
+
+ SERVICE_TABLE_ENTRY DispatchTable[] = {{(LPWSTR)SVCNAME, (LPSERVICE_MAIN_FUNCTION)&CallMain}, {NULL, NULL}};
+
+ // This call returns when the service has stopped.
+ // The process should simply terminate when the call returns.
+
+ if (!StartServiceCtrlDispatcher(DispatchTable))
+ {
+ SvcReportEvent((LPTSTR)L"StartServiceCtrlDispatcher");
+ }
+ }
+
+ return 0;
+}
+
+int
+WindowsService::SvcMain()
+{
+ // Register the handler function for the service
+
+ gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler);
+
+ if (!gSvcStatusHandle)
+ {
+ SvcReportEvent((LPTSTR)TEXT("RegisterServiceCtrlHandler"));
+
+ return 1;
+ }
+
+ // These SERVICE_STATUS members remain as set here
+
+ gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ gSvcStatus.dwServiceSpecificExitCode = 0;
+
+ // Report initial status to the SCM
+
+ ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
+
+ // Create an event. The control handler function, SvcCtrlHandler,
+ // signals this event when it receives the stop control code.
+
+ ghSvcStopEvent = CreateEvent(NULL, // default security attributes
+ TRUE, // manual reset event
+ FALSE, // not signaled
+ NULL); // no name
+
+ if (ghSvcStopEvent == NULL)
+ {
+ ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 0);
+
+ return 1;
+ }
+
+ // Report running status when initialization is complete.
+
+ ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
+
+ int ReturnCode = Run();
+
+ ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
+
+ return ReturnCode;
+}
+
//
// Purpose:
// Retrieves and displays the current service configuration.
@@ -147,7 +228,8 @@ void SvcDelete()
// Return value:
// None
//
-void __stdcall DoQuerySvc()
+void
+DoQuerySvc()
{
SC_HANDLE schSCManager{};
SC_HANDLE schService{};
@@ -249,6 +331,7 @@ cleanup:
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
+
//
// Purpose:
// Disables the service.
@@ -259,7 +342,8 @@ cleanup:
// Return value:
// None
//
-VOID __stdcall DoDisableSvc()
+void
+DoDisableSvc()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
@@ -384,7 +468,8 @@ VOID __stdcall DoEnableSvc()
// Return value:
// None
//
-VOID __stdcall DoUpdateSvcDesc()
+void
+DoUpdateSvcDesc()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
@@ -435,100 +520,6 @@ VOID __stdcall DoUpdateSvcDesc()
//
// Purpose:
-// Entry point for the service
-//
-// Parameters:
-// dwArgc - Number of arguments in the lpszArgv array
-// lpszArgv - Array of strings. The first string is the name of
-// the service and subsequent strings are passed by the process
-// that called the StartService function to start the service.
-//
-// Return value:
-// None.
-//
-VOID WINAPI
-SvcMain(DWORD dwArgc, LPTSTR* lpszArgv)
-{
- // Register the handler function for the service
-
- gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler);
-
- if (!gSvcStatusHandle)
- {
- SvcReportEvent((LPTSTR)TEXT("RegisterServiceCtrlHandler"));
- return;
- }
-
- // These SERVICE_STATUS members remain as set here
-
- gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- gSvcStatus.dwServiceSpecificExitCode = 0;
-
- // Report initial status to the SCM
-
- ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
-
- // Perform service-specific initialization and work.
-
- SvcInit(dwArgc, lpszArgv);
-}
-
-//
-// Purpose:
-// The service code
-//
-// Parameters:
-// dwArgc - Number of arguments in the lpszArgv array
-// lpszArgv - Array of strings. The first string is the name of
-// the service and subsequent strings are passed by the process
-// that called the StartService function to start the service.
-//
-// Return value:
-// None
-//
-VOID
-SvcInit(DWORD dwArgc, LPTSTR* lpszArgv)
-{
- // TO_DO: Declare and set any required variables.
- // Be sure to periodically call ReportSvcStatus() with
- // SERVICE_START_PENDING. If initialization fails, call
- // ReportSvcStatus with SERVICE_STOPPED.
-
- ZEN_UNUSED(lpszArgv, dwArgc);
-
- // Create an event. The control handler function, SvcCtrlHandler,
- // signals this event when it receives the stop control code.
-
- ghSvcStopEvent = CreateEvent(NULL, // default security attributes
- TRUE, // manual reset event
- FALSE, // not signaled
- NULL); // no name
-
- if (ghSvcStopEvent == NULL)
- {
- ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 0);
- return;
- }
-
- // Report running status when initialization is complete.
-
- ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
-
- // TO_DO: Perform work until service stops.
-
- while (1)
- {
- // Check whether to stop the service.
-
- WaitForSingleObject(ghSvcStopEvent, INFINITE);
-
- ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
- return;
- }
-}
-
-//
-// Purpose:
// Sets the current service status and reports it to the SCM.
//
// Parameters:
@@ -565,21 +556,13 @@ ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
}
-//
-// Purpose:
-// Called by SCM whenever a control code is sent to the service
-// using the ControlService function.
-//
-// Parameters:
-// dwCtrl - control code
-//
-// Return value:
-// None
-//
-VOID WINAPI
-SvcCtrlHandler(DWORD dwCtrl)
+void
+WindowsService::SvcCtrlHandler(DWORD dwCtrl)
{
// Handle the requested control code.
+ //
+ // Called by SCM whenever a control code is sent to the service
+ // using the ControlService function.
switch (dwCtrl)
{
@@ -589,8 +572,9 @@ SvcCtrlHandler(DWORD dwCtrl)
// Signal the service to stop.
SetEvent(ghSvcStopEvent);
- ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
+ zen::RequestApplicationExit(0);
+ ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
return;
case SERVICE_CONTROL_INTERROGATE:
@@ -645,11 +629,3 @@ SvcReportEvent(LPTSTR szFunction)
// DeregisterEventSource(hEventSource);
//}
}
-
-WindowsServiceBase::WindowsServiceBase()
-{
-}
-
-WindowsServiceBase::~WindowsServiceBase()
-{
-}
diff --git a/zenserver/windows/service.h b/zenserver/windows/service.h
index 0f76d7447..7c9610983 100644
--- a/zenserver/windows/service.h
+++ b/zenserver/windows/service.h
@@ -2,14 +2,19 @@
#pragma once
-void SvcInstall(void);
-void SvcDelete();
-
-class WindowsServiceBase
+class WindowsService
{
public:
- WindowsServiceBase();
- ~WindowsServiceBase();
+ WindowsService();
+ ~WindowsService();
+
+ virtual int Run() = 0;
+
+ int ServiceMain();
+
+ static void Install();
+ static void Delete();
-private:
-}; \ No newline at end of file
+ int SvcMain();
+ static void __stdcall SvcCtrlHandler(unsigned long);
+};
diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp
index 83580b288..ea4a2915e 100644
--- a/zenserver/zenserver.cpp
+++ b/zenserver/zenserver.cpp
@@ -316,7 +316,9 @@ public:
__debugbreak();
}
- m_Http->Run(m_TestMode);
+ const bool IsInteractiveMode = zen::IsInteractiveSession() && !m_TestMode;
+
+ m_Http->Run(IsInteractiveMode);
ZEN_INFO(ZEN_APP_NAME " exiting");
@@ -436,33 +438,29 @@ private:
} // namespace zen
-int
-main(int argc, char* argv[])
+class ZenWindowsService : public WindowsService
{
- using namespace zen;
-
- mi_version();
-
- ZenServerOptions GlobalOptions;
- ZenServiceConfig ServiceConfig;
- ParseGlobalCliOptions(argc, argv, GlobalOptions, ServiceConfig);
- InitializeLogging(GlobalOptions);
-
-#if ZEN_PLATFORM_WINDOWS
- if (GlobalOptions.InstallService)
+public:
+ ZenWindowsService(ZenServerOptions& GlobalOptions, ZenServiceConfig& ServiceConfig)
+ : m_GlobalOptions(GlobalOptions)
+ , m_ServiceConfig(ServiceConfig)
{
- SvcInstall();
-
- std::exit(0);
}
- if (GlobalOptions.UninstallService)
- {
- SvcDelete();
+ ZenWindowsService(const ZenWindowsService&) = delete;
+ ZenWindowsService& operator=(const ZenWindowsService&) = delete;
- std::exit(0);
- }
-#endif
+ virtual int Run() override;
+
+private:
+ ZenServerOptions& m_GlobalOptions;
+ ZenServiceConfig& m_ServiceConfig;
+};
+
+int
+ZenWindowsService::Run()
+{
+ using namespace zen;
#if USE_SENTRY
// Initialize sentry.io client
@@ -474,6 +472,9 @@ main(int argc, char* argv[])
auto _ = zen::MakeGuard([] { sentry_close(); });
#endif
+ auto& GlobalOptions = m_GlobalOptions;
+ auto& ServiceConfig = m_ServiceConfig;
+
try
{
// Prototype config system, we'll see how this pans out
@@ -559,3 +560,35 @@ main(int argc, char* argv[])
return 0;
}
+
+int
+main(int argc, char* argv[])
+{
+ using namespace zen;
+
+ mi_version();
+
+ ZenServerOptions GlobalOptions;
+ ZenServiceConfig ServiceConfig;
+ ParseGlobalCliOptions(argc, argv, GlobalOptions, ServiceConfig);
+ InitializeLogging(GlobalOptions);
+
+#if ZEN_PLATFORM_WINDOWS
+ if (GlobalOptions.InstallService)
+ {
+ WindowsService::Install();
+
+ std::exit(0);
+ }
+
+ if (GlobalOptions.UninstallService)
+ {
+ WindowsService::Delete();
+
+ std::exit(0);
+ }
+#endif
+
+ ZenWindowsService App(GlobalOptions, ServiceConfig);
+ return App.ServiceMain();
+}