blob: 52a3b178e459a5c3218fabe013740039e0961c2d [file] [log] [blame]
/*
* 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;
}