Skip to content

Commit

Permalink
win32: Forte as a Windows service
Browse files Browse the repository at this point in the history
  • Loading branch information
kumajaya committed May 7, 2024
1 parent 1bda348 commit 76d45d7
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 19 deletions.
10 changes: 10 additions & 0 deletions src/arch/utils/mainparam_utils.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*******************************************************************************
* Copyright (c) 2018 fortiss GmbH
* 2024 Samator Indo Gas
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
Expand All @@ -9,6 +10,7 @@
* Contributors:
* Tarik Terzimehic
* - initial API and implementation and/or initial documentation
* Ketut Kumajaya - option to run as a Windows service
*******************************************************************************/

#include <forte_config.h>
Expand Down Expand Up @@ -40,6 +42,10 @@ void listHelp(){
#ifdef FORTE_TRACE_CTF
printf("%-20s Set the output directory for TRACE_CTF\n", " -t <directory>");
#endif //FORTE_TRACE_CTF
#ifdef FORTE_WINDOWS_SERVICE
printf("%-20s To install as a Windows service\n", " -install");
printf("%-20s To remove the service\n", " -remove");
#endif //FORTE_WINDOWS_SERVICE
}

/*!\brief Parses the command line arguments passed to the main function
Expand Down Expand Up @@ -90,6 +96,10 @@ const char *parseCommandLineArguments(int argc, char *arg[]){
barectfSetup(arg[i + 1] ?: "");
break;
#endif //FORTE_TRACE_CTF
#ifdef FORTE_WINDOWS_SERVICE
case 's':
break;
#endif //FORTE_WINDOWS_SERVICE
default: //! Unknown parameter or -h -> Lists the help for FORTE
return "";
}
Expand Down
2 changes: 2 additions & 0 deletions src/arch/win32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#*******************************************************************************
# Copyright (c) 2010 - 2015 ACIN, Profactor GmbH, fortiss GmbH
# 2024 Samator Indo Gas
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0.
Expand All @@ -9,6 +10,7 @@
# Contributors:
# Alois Zoitl, Gerhard Ebenhofer, Martin Melik-Merkumians - initial API and implementation and/or initial documentation
# Christoph Binder - add possibility to configure simulated time
# Ketut Kumajaya - option to run as a Windows service
# *******************************************************************************/

SET(SOURCE_GROUP ${SOURCE_GROUP}\\win32)
Expand Down
10 changes: 9 additions & 1 deletion src/arch/win32/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*******************************************************************************
* Copyright (c) 2010 - 2018 ACIN, Profactor GmbH, fortiss GmbH
* 2024 Samator Indo Gas
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
Expand All @@ -9,6 +10,7 @@
* Contributors:
* Alois Zoitl, Ingo Hegny, Gerhard Ebenhofer - initial API and implementation and/or initial documentation
* Alois Zoitl - cleaned up main, inserted new architecture initilasation api
* Ketut Kumajaya - option to run as a Windows service
*******************************************************************************/
#include "../forte_architecture.h"
#include "../devlog.h"
Expand Down Expand Up @@ -61,7 +63,7 @@ void createDev(const char *paMGRID){
delete poDev;
}

int main(int argc, char *arg[]){
int _main(int argc, char *arg[]){

if(CForteArchitecture::initialize()){

Expand All @@ -83,3 +85,9 @@ int main(int argc, char *arg[]){
}
return 0;
}

#ifndef FORTE_WINDOWS_SERVICE
int main(int argc, char *arg[]){
return _main(argc, arg);
}
#endif
5 changes: 2 additions & 3 deletions src/arch/win32/service/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
# Ketut Kumajaya - initial API and implementation and/or initial documentation
# *******************************************************************************/
if(FORTE_WINDOWS_SERVICE)
forte_add_sourcefile_cpp(CppWindowsService.cpp)
forte_add_sourcefile_hcpp(SampleService ServiceBase ServiceInstaller)
forte_add_sourcefile_h(ThreadPool.h)
forte_add_to_executable_h(SampleService ServiceBase ServiceInstaller ThreadPool)
forte_add_to_executable_cpp(CppWindowsService SampleService ServiceBase ServiceInstaller)
endif()
99 changes: 88 additions & 11 deletions src/arch/win32/service/CppWindowsService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Project: CppWindowsService
* Copyright (c) Microsoft Corporation.
*
* 2024, Ketut Kumajaya - modified for FORTE to run as a Windows service
*
* The file defines the entry point of the application. According to the
* arguments in the command line, the function installs or uninstalls or
* starts the service by calling into different routines.
Expand All @@ -17,23 +19,37 @@
\***************************************************************************/

#pragma region Includes
#include <stdio.h>
#include <cstdio>
#include <windows.h>
#include "ServiceInstaller.h"
#include "ServiceBase.h"
#include "SampleService.h"
#pragma endregion


#include <io.h>
#include <filesystem>
#include <cstring>
#include <vector>
#include "../../devlog.h"
#include "../../forte_stringFunctions.h"

#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

int _main(int argc, char *arg[]);


//
// Settings of the service
//

// Internal name of the service
#define SERVICE_NAME (PWSTR)L"CppWindowsService"
#define SERVICE_NAME (PWSTR)L"4diac-forte"

// Displayed name of the service
#define SERVICE_DISPLAY_NAME (PWSTR)L"CppWindowsService Sample Service"
#define SERVICE_DISPLAY_NAME (PWSTR)L"4diac FORTE Runtime for Windows"

// Service start options.
#define SERVICE_START_TYPE SERVICE_DEMAND_START
Expand All @@ -42,12 +58,28 @@
#define SERVICE_DEPENDENCIES (PWSTR)L""

// The name of the account under which the service should run
#define SERVICE_ACCOUNT (PWSTR)L"NT AUTHORITY\\LocalService"
#define SERVICE_ACCOUNT (PWSTR)L"LocalSystem"

// The password to the service account name
#define SERVICE_PASSWORD NULL


// Modified from https://stackoverflow.com/a/21376268
void EnableLogRedirection(const std::wstring &fname, std::FILE *fstream)
{
// Duplicate stdout/stderr and give it a new file descriptor
int fd = _dup((fstream == stdout) ? STDOUT_FILENO : STDERR_FILENO);
if (fd != -1)
{
// Re-open stdout/stderr to the new file
_wfreopen(fname.c_str(), (PWSTR)L"w", fstream);
_close(fd);
}

fflush(fstream);
}


//
// FUNCTION: wmain(int, wchar_t *[])
//
Expand All @@ -65,6 +97,22 @@
//
int wmain(int argc, wchar_t *argv[])
{
int ret = -1;

// User can edit forte command line arguments in
// HKLM\SYSTEM\CurrentControlSet\Services\4diac-forte\ImagePath

// Reconstruct command line arguments from wide to narrow string
std::vector<char*> l_argv;
for (int i = 0; i < argc; ++i)
{
std::string str = forte_wstringToString(argv[i]);
char* buf = new char[str.length() + 1];
std::strcpy(buf, str.c_str());
l_argv.push_back(buf);
}
l_argv.push_back(nullptr); // argv[argc] is this null pointer

if ((argc > 1) && ((*argv[1] == L'-' || (*argv[1] == L'/'))))
{
if (_wcsicmp(L"install", argv[1] + 1) == 0)
Expand All @@ -86,19 +134,48 @@ int wmain(int argc, wchar_t *argv[])
// "-remove" or "/remove".
UninstallService(SERVICE_NAME);
}
else if (_wcsicmp(L"service", argv[1] + 1) == 0 || _wcsicmp(L"s", argv[1] + 1) == 0)
{
// Start setup stdout/stderr redirection
wchar_t szPath[MAX_PATH];
if (GetModuleFileNameW(NULL, szPath, ARRAYSIZE(szPath)) == 0) {
DEVLOG_ERROR("GetModuleFileNameW failed w/err 0x%08lx\n", GetLastError());
}

std::filesystem::path fullpath(szPath);
// Disable logging with "-service nolog" argument
if (std::filesystem::exists(fullpath) && _wcsicmp(L"nolog", argv[2]) != 0) {
std::wstring logfile = fullpath.replace_extension("log").wstring();
EnableLogRedirection(logfile, stdout);
logfile = fullpath.replace_extension("err").wstring();
EnableLogRedirection(logfile, stderr);
}

CSampleService service(SERVICE_DISPLAY_NAME, static_cast<int>(l_argv.size() - 1), l_argv.data());
if (!CServiceBase::Run(service))
{
DEVLOG_ERROR("Service failed to run w/err 0x%08lx\n", GetLastError());
}
}
else
{
ret = _main(static_cast<int>(l_argv.size() - 1), l_argv.data());
}
}
else
{
wprintf(L"Parameters:\n");
wprintf(L" -install to install the service.\n");
wprintf(L" -remove to remove the service.\n");
ret = _main(static_cast<int>(l_argv.size() - 1), l_argv.data());
}

CSampleService service(SERVICE_NAME);
if (!CServiceBase::Run(service))
// Clear the command line arguments
for(auto &buf : l_argv)
{
if (buf != nullptr)
{
wprintf(L"Service failed to run w/err 0x%08lx\n", GetLastError());
delete[] buf;
}
}
l_argv.clear();

return 0;
return ret;
}
36 changes: 34 additions & 2 deletions src/arch/win32/service/SampleService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Project: CppWindowsService
* Copyright (c) Microsoft Corporation.
*
* 2024, Ketut Kumajaya - modified for FORTE to run as a Windows service
*
* Provides a sample service class that derives from the service base class -
* CServiceBase. The sample service logs the service start and stop
* information to the Application event log, and shows how to run the main
Expand All @@ -23,13 +25,25 @@
#pragma endregion


#include <sstream>
#include "../../devlog.h"
#include "../../forte_stringFunctions.h"

void endForte(int paSig);
int _main(int argc, char *arg[]);


CSampleService::CSampleService(PWSTR pszServiceName,
int argc,
char *argv[],
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue)
: CServiceBase(pszServiceName, fCanStop, fCanShutdown, fCanPauseContinue)
{
m_fStopping = FALSE;
m_argc = argc;
m_argv = argv;

// Create a manual-reset event that is not signaled at first to indicate
// the stopped signal of the service.
Expand Down Expand Up @@ -78,8 +92,15 @@ CSampleService::~CSampleService(void)
//
void CSampleService::OnStart(DWORD, LPWSTR*)
{
// Log command line arguments
std::ostringstream stream;
for (int i = 0; i < m_argc; ++i)
{
stream << " " << m_argv[i];
}

// Log a service start message to the Application log.
WriteEventLogEntry((PWSTR)L"CppWindowsService in OnStart",
WriteEventLogEntry((PWSTR)(forte_stringToWstring(("FORTE service started:" + stream.str())).c_str()),
EVENTLOG_INFORMATION_TYPE);

// Queue the main service function for execution in a worker thread.
Expand All @@ -98,7 +119,14 @@ void CSampleService::ServiceWorkerThread(void)
// Periodically check if the service is stopping.
while (!m_fStopping)
{
// Log a prosess start message
DEVLOG_INFO("FORTE process start\n");

// Perform main service function here...
_main(m_argc, m_argv);

// Log a prosess stop message
DEVLOG_INFO("FORTE process end\n");

::Sleep(2000); // Simulate some lengthy operations.
}
Expand All @@ -123,12 +151,16 @@ void CSampleService::ServiceWorkerThread(void)
void CSampleService::OnStop()
{
// Log a service stop message to the Application log.
WriteEventLogEntry((PWSTR)L"CppWindowsService in OnStop",
WriteEventLogEntry((PWSTR)L"FORTE service stopped",
EVENTLOG_INFORMATION_TYPE);

// Indicate that the service is stopping and wait for the finish of the
// main service function (ServiceWorkerThread).
m_fStopping = TRUE;

// Kill FORTE process
endForte(0);

if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
{
throw GetLastError();
Expand Down
7 changes: 7 additions & 0 deletions src/arch/win32/service/SampleService.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Project: CppWindowsService
* Copyright (c) Microsoft Corporation.
*
* 2024, Ketut Kumajaya - modified for FORTE to run as a Windows service
*
* Provides a sample service class that derives from the service base class -
* CServiceBase. The sample service logs the service start and stop
* information to the Application event log, and shows how to run the main
Expand All @@ -27,6 +29,8 @@ class CSampleService : public CServiceBase
public:

CSampleService(PWSTR pszServiceName,
int argc,
char *argv[],
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);
Expand All @@ -43,4 +47,7 @@ class CSampleService : public CServiceBase

BOOL m_fStopping;
HANDLE m_hStoppedEvent;

int m_argc;
char **m_argv;
};
2 changes: 1 addition & 1 deletion src/arch/win32/service/ServiceBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

#pragma region Includes
#include "ServiceBase.h"
#include <assert.h>
#include <cassert>
#include <strsafe.h>
#pragma endregion

Expand Down
Loading

0 comments on commit 76d45d7

Please sign in to comment.