Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / scripts / sai-server.service
Author[]google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.c...> 2025-12-03 14:48 UTC
Committer[]Andy Green <andy@warmcat.com> 2025-12-03 14:58 UTC
Tree063b6ddb8415ba8f22bfed7733b2d415068d0a8f   Raw Patch
 
Add native Windows Service support to sai-builder
Add native Windows Service support to sai-builder

Refactors the main application loop into `saib_app_run` to allow execution from a service context.
Adds `b-win32-service.c` to implement Windows Service API hooks (`ServiceMain`, `ServiceCtrlHandler`).
Updates `CMakeLists.txt` to include the service support on Windows builds.
Ensures command-line arguments are propagated correctly when running as a service.
diff --git a/src/builder/CMakeLists.txt b/src/builder/CMakeLists.txt index 1b1f9a3..8768b0d 100644 --- a/src/builder/CMakeLists.txt +++ b/src/builder/CMakeLists.txt @@ -3,6 +3,7 @@ set(CPACK_DEBIAN_BUILDER_PACKAGE_NAME "sai-builder") set(SRCS b-sai.c + b-win32-service.c b-conf.c b-ws-server.c b-nspawn.c diff --git a/src/builder/b-private.h b/src/builder/b-private.h index 63ad022..f61c09b 100644 --- a/src/builder/b-private.h +++ b/src/builder/b-private.h @@ -320,6 +320,12 @@ suspender_destroy(void); int saib_reassess_idle_situation(void); +int +saib_app_run(int argc, const char **argv); + +void +saib_app_stop(void); + #if defined(__APPLE__) int saib_need_wakelock(void); diff --git a/src/builder/b-sai.c b/src/builder/b-sai.c index 38dda21..3f968b8 100644 --- a/src/builder/b-sai.c +++ b/src/builder/b-sai.c @@ -424,8 +424,14 @@ sai_ns_destroy(struct sai_nspawn *ns) free(ns); } +void saib_app_stop(void) +{ + interrupted = 1; + lws_cancel_service(builder.context); +} -int main(int argc, const char **argv) +int +saib_app_run(int argc, const char **argv) { int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; struct lws_context_creation_info info; @@ -664,3 +670,17 @@ bail: return 0; } + +#if defined(WIN32) +extern int saib_service_run(int argc, const char **argv); +#endif + +int main(int argc, const char **argv) +{ +#if defined(WIN32) + if (lws_cmdline_option(argc, argv, "--service")) + return saib_service_run(argc, argv); +#endif + + return saib_app_run(argc, argv); +} diff --git a/src/builder/b-win32-service.c b/src/builder/b-win32-service.c new file mode 100644 index 0000000..aacab5d --- /dev/null +++ b/src/builder/b-win32-service.c @@ -0,0 +1,151 @@ +/* + * 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 <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
Page fetched 0s ago, creation time: 3ms (vhost etag hits: 0%, cache hits: 0%)