| /* |
| * Copyright 2022 Richard Hughes <richard@hughsie.com> |
| * |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| * |
| * - sc create fwupd start="auto" binPath="C:\Program Files (x86)\fwupd\bin\fwupd.exe" |
| * - sc delete fwupd |
| */ |
| |
| #define G_LOG_DOMAIN "FuMain" |
| |
| #include "config.h" |
| |
| #include <fwupdplugin.h> |
| |
| #include <windows.h> |
| |
| #include "fu-daemon.h" |
| #include "fu-debug.h" |
| |
| /* nocheck:static */ |
| static SERVICE_STATUS gSvcStatus = {.dwServiceType = SERVICE_WIN32_OWN_PROCESS, |
| .dwServiceSpecificExitCode = 0}; |
| static SERVICE_STATUS_HANDLE gSvcStatusHandle = 0; |
| static FuDaemon *gDaemon = NULL; |
| |
| static void |
| fu_main_svc_report_status(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) |
| { |
| static DWORD check_point = 1; /* nocheck:static */ |
| |
| gSvcStatus.dwCurrentState = dwCurrentState; |
| gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; |
| gSvcStatus.dwWaitHint = dwWaitHint; |
| |
| if (dwCurrentState == SERVICE_START_PENDING) |
| gSvcStatus.dwControlsAccepted = 0; |
| else |
| gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; |
| |
| if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED) |
| gSvcStatus.dwCheckPoint = 0; |
| else |
| gSvcStatus.dwCheckPoint = check_point++; |
| |
| SetServiceStatus(gSvcStatusHandle, &gSvcStatus); |
| } |
| |
| static gboolean |
| fu_main_svc_control_stop_cb(gpointer user_data) |
| { |
| FuDaemon *daemon = FU_DAEMON(user_data); |
| g_autoptr(GError) error = NULL; |
| if (!fu_daemon_stop(daemon, &error)) |
| g_warning("Failed to stop daemon, will wait: %s\n", error->message); |
| return G_SOURCE_REMOVE; |
| } |
| |
| static void |
| fu_main_svc_control_cb(DWORD dwCtrl) |
| { |
| switch (dwCtrl) { |
| case SERVICE_CONTROL_STOP: |
| fu_main_svc_report_status(SERVICE_STOP_PENDING, NO_ERROR, 0); |
| /* there is no user_data, because global state with threads is completely fine */ |
| g_idle_add(fu_main_svc_control_stop_cb, gDaemon); |
| fu_main_svc_report_status(gSvcStatus.dwCurrentState, NO_ERROR, 0); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void |
| fu_main_svc_main_cb(DWORD dwArgc, LPSTR *lpszArgv) |
| { |
| g_autoptr(FuDaemon) daemon = gDaemon = fu_daemon_new(); |
| g_autoptr(GError) error = NULL; |
| g_autoptr(GOptionContext) context = g_option_context_new(NULL); |
| |
| /* parse debugging args */ |
| g_option_context_add_group(context, fu_debug_get_option_group()); |
| if (!g_option_context_parse(context, (gint *)&dwArgc, &lpszArgv, &error)) { |
| g_printerr("Failed to parse command line: %s\n", error->message); |
| return; |
| } |
| |
| gSvcStatusHandle = RegisterServiceCtrlHandlerA((LPSTR) "fwupd", fu_main_svc_control_cb); |
| if (gSvcStatusHandle == NULL) { |
| g_warning("RegisterServiceCtrlHandlerA failed [%u]", (guint)GetLastError()); |
| return; |
| } |
| |
| /* set up the daemon, which includes coldplugging devices -- then run it */ |
| fu_main_svc_report_status(SERVICE_START_PENDING, NO_ERROR, 1000); |
| if (!fu_daemon_setup(daemon, FWUPD_DBUS_SOCKET_ADDRESS, &error)) { |
| g_warning("Failed to load daemon: %s", error->message); |
| return; |
| } |
| fu_main_svc_report_status(SERVICE_RUNNING, NO_ERROR, 0); |
| if (!fu_daemon_start(daemon, &error)) { |
| g_warning("Failed to start daemon: %s\n", error->message); |
| return; |
| } |
| fu_main_svc_report_status(SERVICE_STOPPED, NO_ERROR, 0); |
| } |
| |
| static int |
| fu_main_console(int argc, char *argv[]) |
| { |
| g_autoptr(FuDaemon) daemon = gDaemon = fu_daemon_new(); |
| g_autoptr(GError) error = NULL; |
| g_autoptr(GOptionContext) context = g_option_context_new(NULL); |
| |
| /* parse debugging args */ |
| g_option_context_add_group(context, fu_debug_get_option_group()); |
| if (!g_option_context_parse(context, &argc, &argv, &error)) { |
| g_printerr("Failed to parse command line: %s\n", error->message); |
| return EXIT_FAILURE; |
| } |
| |
| /* set up the daemon, which includes coldplugging devices -- then run it */ |
| if (!fu_daemon_setup(daemon, FWUPD_DBUS_SOCKET_ADDRESS, &error)) { |
| g_printerr("Failed to load daemon: %s\n", error->message); |
| return EXIT_FAILURE; |
| } |
| if (!fu_daemon_start(daemon, &error)) { |
| g_printerr("Failed to start daemon: %s\n", error->message); |
| return EXIT_FAILURE; |
| } |
| |
| /* success */ |
| g_message("daemon ready for requests"); |
| return EXIT_SUCCESS; |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| SERVICE_TABLE_ENTRYA svc_table[] = {{(LPSTR) "fwupd", fu_main_svc_main_cb}, {NULL, NULL}}; |
| if (!StartServiceCtrlDispatcherA(svc_table)) { |
| /* program is being run as a console application rather than as a service */ |
| if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) |
| return fu_main_console(argc, argv); |
| g_printerr("StartServiceCtrlDispatcherA failed [%u]\n", (guint)GetLastError()); |
| return EXIT_FAILURE; |
| } |
| return EXIT_SUCCESS; |
| } |