| /*
* sai-builder - Windows Service support
*
* Copyright (C) 2019 - 2025 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#if defined(WIN32)
#include <libwebsockets.h>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include "b-private.h"
SERVICE_STATUS g_ServiceStatus = {0};
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceCtrlHandler(DWORD);
static int g_argc;
static const char **g_argv;
#define SERVICE_NAME TEXT("SaiBuilder")
int saib_service_run(int argc, const char **argv)
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{NULL, NULL}
};
/* Capture arguments to pass to the service main */
g_argc = argc;
g_argv = argv;
/* This call blocks until the service is stopped */
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
{
return GetLastError();
}
return 0;
}
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
int rc;
/*
* Note on arguments:
* argc/argv passed to ServiceMain contain arguments specified in the
* "Start parameters" of the Service Control Manager or via "sc start ... args".
* These are distinct from the process command line arguments passed to main().
*
* Typically, a service is configured via its binPath (e.g., "sai-builder.exe --service -c conf").
* These arguments are received by main() and captured in g_argc/g_argv.
* We use these captured arguments to start the app, ensuring configuration is preserved.
*/
g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
if (g_StatusHandle == NULL)
{
return;
}
ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
return;
}
/*
* We don't need a separate worker thread because ServiceMain runs in its own thread
* and we can block here. However, we need to handle the control requests.
*/
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
return;
}
/* Run the app using the arguments passed to the process */
rc = saib_app_run(g_argc, g_argv);
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
if (rc) {
g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
g_ServiceStatus.dwServiceSpecificExitCode = (DWORD)rc;
} else {
g_ServiceStatus.dwWin32ExitCode = 0;
}
g_ServiceStatus.dwCheckPoint = 3;
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
}
VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
switch (CtrlCode)
{
case SERVICE_CONTROL_STOP:
if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
break;
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 4;
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
saib_app_stop();
break;
default:
break;
}
}
#endif
|