mirror of https://github.com/Cisco-Talos/clamav
Added feature to start FreshClam & Clamd as Windows services Special thanks to Gianluigi Tiesi for allowing us to integrate this feature from ClamWin directly into ClamAV. Added internal --service-mode option for FreshClam and ClamD This is used when Windows starts FreshClam or ClamD as a service so that they will register with the service manager. Code found in service.c.pull/181/head
parent
1df4f82f2b
commit
27d51762b3
@ -0,0 +1,228 @@ |
||||
/*
|
||||
* Copyright (C) 2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
||||
* Copyright (C) 2008-2010 Gianluigi Tiesi <sherpya@netfarm.it> |
||||
* |
||||
* Authors: Gianluigi Tiesi |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* This program 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 General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||
* MA 02110-1301, USA. |
||||
*/ |
||||
|
||||
#include <platform.h> |
||||
#include <winsvc.h> |
||||
|
||||
#include "service.h" |
||||
#include "output.h" |
||||
|
||||
static SERVICE_STATUS svc; |
||||
static SERVICE_STATUS_HANDLE svc_handle; |
||||
static SERVICE_TABLE_ENTRYA DT[] = {{"Service", ServiceMain}, {NULL, NULL}}; |
||||
|
||||
static HANDLE evStart; |
||||
static HANDLE DispatcherThread; |
||||
static int checkpoint_every = 5000; |
||||
|
||||
int svc_uninstall(const char *name, int verbose) |
||||
{ |
||||
SC_HANDLE sm, svc; |
||||
int ret = 1; |
||||
|
||||
if (!(sm = OpenSCManagerA(NULL, NULL, DELETE))) { |
||||
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) |
||||
fprintf(stderr, "Windows Services are not supported on this Platform\n"); |
||||
else |
||||
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError()); |
||||
return 0; |
||||
} |
||||
|
||||
if ((svc = OpenServiceA(sm, name, DELETE))) { |
||||
if (DeleteService(svc)) { |
||||
if (verbose) printf("Service %s successfully removed\n", name); |
||||
} else { |
||||
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError()); |
||||
ret = 0; |
||||
} |
||||
} else { |
||||
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) { |
||||
if (verbose) printf("Service %s does not exist\n", name); |
||||
} else { |
||||
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError()); |
||||
ret = 0; |
||||
} |
||||
} |
||||
|
||||
if (svc) CloseServiceHandle(svc); |
||||
CloseServiceHandle(sm); |
||||
return ret; |
||||
} |
||||
|
||||
int svc_install(const char *name, const char *dname, const char *desc) |
||||
{ |
||||
SC_HANDLE sm, svc; |
||||
char modulepath[MAX_PATH]; |
||||
char binpath[MAX_PATH]; |
||||
SERVICE_DESCRIPTIONA sdesc = {(char *)desc}; |
||||
|
||||
if (!GetModuleFileName(NULL, modulepath, MAX_PATH - 1)) { |
||||
fprintf(stderr, "Unable to get the executable name (%d)\n", GetLastError()); |
||||
return 0; |
||||
} |
||||
|
||||
if (!svc_uninstall(name, 0)) return 0; |
||||
|
||||
if (!(sm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CREATE_SERVICE | DELETE))) { |
||||
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) |
||||
fprintf(stderr, "Windows Services are not supported on this Platform\n"); |
||||
else |
||||
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError()); |
||||
return 0; |
||||
} |
||||
|
||||
if (strchr(modulepath, ' ')) |
||||
snprintf(binpath, MAX_PATH - 1, "\"%s\" --daemon --service-mode", modulepath); |
||||
else |
||||
snprintf(binpath, MAX_PATH - 1, "%s --daemon --service-mode", modulepath); |
||||
|
||||
svc = CreateServiceA(sm, name, dname, SERVICE_CHANGE_CONFIG, |
||||
SERVICE_WIN32_OWN_PROCESS, |
||||
SERVICE_DEMAND_START, |
||||
SERVICE_ERROR_NORMAL, |
||||
binpath, |
||||
NULL, /* Load group order */ |
||||
NULL, /* Tag Id */ |
||||
NULL, /* Dependencies */ |
||||
NULL, /* User -> Local System */ |
||||
""); |
||||
|
||||
if (!svc) { |
||||
fprintf(stderr, "Unable to Create Service %s (%d)\n", name, GetLastError()); |
||||
CloseServiceHandle(sm); |
||||
return 0; |
||||
} |
||||
|
||||
/* ChangeServiceConfig2A() */ |
||||
if (!ChangeServiceConfig2A(svc, SERVICE_CONFIG_DESCRIPTION, &sdesc)) |
||||
fprintf(stderr, "Unable to set description for Service %s (%d)\n", name, GetLastError()); |
||||
|
||||
CloseServiceHandle(svc); |
||||
CloseServiceHandle(sm); |
||||
|
||||
printf("Service %s successfully created.\n", name); |
||||
printf("Use 'net start %s' and 'net stop %s' to start/stop the service.\n", name, name); |
||||
return 1; |
||||
} |
||||
|
||||
static void svc_getcpvalue(const char *name) |
||||
{ |
||||
HKEY hKey; |
||||
DWORD dwType; |
||||
DWORD value, vlen = sizeof(DWORD); |
||||
char subkey[MAX_PATH]; |
||||
|
||||
snprintf(subkey, MAX_PATH - 1, "SYSTEM\\CurrentControlSet\\Services\\%s", name); |
||||
|
||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) |
||||
return; |
||||
|
||||
if ((RegQueryValueExA(hKey, "Checkpoint", NULL, &dwType, (LPBYTE)&value, &vlen) == ERROR_SUCCESS) && |
||||
(vlen == sizeof(DWORD) && (dwType == REG_DWORD))) |
||||
checkpoint_every = value; |
||||
|
||||
RegCloseKey(hKey); |
||||
} |
||||
|
||||
void svc_register(const char *name) |
||||
{ |
||||
DWORD tid; |
||||
DT->lpServiceName = (char *)name; |
||||
svc_getcpvalue(name); |
||||
|
||||
evStart = CreateEvent(NULL, TRUE, FALSE, NULL); |
||||
DispatcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartServiceCtrlDispatcherA, (LPVOID)DT, 0, &tid); |
||||
} |
||||
|
||||
void svc_ready(void) |
||||
{ |
||||
WaitForSingleObject(evStart, INFINITE); |
||||
|
||||
svc.dwCurrentState = SERVICE_RUNNING; |
||||
svc.dwControlsAccepted |= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; |
||||
svc.dwCheckPoint = 0; |
||||
|
||||
if (!SetServiceStatus(svc_handle, &svc)) { |
||||
logg("[service] SetServiceStatus() failed with %d\n", GetLastError()); |
||||
exit(1); |
||||
} |
||||
} |
||||
|
||||
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context) |
||||
{ |
||||
if (svc.dwCurrentState == SERVICE_START_PENDING) { |
||||
svc.dwCheckPoint++; |
||||
if ((svc.dwCheckPoint % checkpoint_every) == 0) |
||||
SetServiceStatus(svc_handle, &svc); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void WINAPI ServiceCtrlHandler(DWORD code) |
||||
{ |
||||
switch (code) { |
||||
case SERVICE_CONTROL_STOP: |
||||
case SERVICE_CONTROL_SHUTDOWN: |
||||
svc.dwCurrentState = SERVICE_STOPPED; |
||||
svc.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); |
||||
SetServiceStatus(svc_handle, &svc); |
||||
return; |
||||
case SERVICE_CONTROL_INTERROGATE: |
||||
break; |
||||
} |
||||
|
||||
SetServiceStatus(svc_handle, &svc); |
||||
} |
||||
|
||||
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType) |
||||
{ |
||||
if (CtrlType == CTRL_C_EVENT) { |
||||
SetConsoleCtrlHandler(cw_stop_ctrl_handler, FALSE); |
||||
fprintf(stderr, "Control+C pressed, aborting...\n"); |
||||
exit(0); |
||||
} |
||||
return TRUE; |
||||
} |
||||
|
||||
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv) |
||||
{ |
||||
svc.dwServiceType = SERVICE_WIN32; |
||||
svc.dwCurrentState = SERVICE_START_PENDING; |
||||
svc.dwControlsAccepted = 0; |
||||
svc.dwWin32ExitCode = NO_ERROR; |
||||
svc.dwServiceSpecificExitCode = 0; |
||||
svc.dwCheckPoint = 0; |
||||
svc.dwWaitHint = 0; |
||||
|
||||
if (!(svc_handle = RegisterServiceCtrlHandlerA(DT->lpServiceName, ServiceCtrlHandler))) { |
||||
logg("[service] RegisterServiceCtrlHandler() failed with %d\n", GetLastError()); |
||||
exit(1); |
||||
} |
||||
|
||||
if (!SetServiceStatus(svc_handle, &svc)) { |
||||
logg("[service] SetServiceStatus() failed with %d\n", GetLastError()); |
||||
exit(1); |
||||
} |
||||
|
||||
SetEvent(evStart); |
||||
WaitForSingleObject(DispatcherThread, INFINITE); |
||||
cw_stop_ctrl_handler(CTRL_C_EVENT); |
||||
} |
@ -0,0 +1,38 @@ |
||||
/*
|
||||
* Copyright (C) 2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
||||
* Copyright (C) 2008-2010 Gianluigi Tiesi <sherpya@netfarm.it> |
||||
* |
||||
* Authors: Gianluigi Tiesi |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* This program 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 General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||
* MA 02110-1301, USA. |
||||
*/ |
||||
|
||||
#ifndef __SERVICE_H |
||||
#define __SERVICE_H |
||||
|
||||
#include <platform.h> |
||||
#include <winsvc.h> |
||||
|
||||
int svc_uninstall(const char *name, int verbose); |
||||
int svc_install(const char *name, const char *dname, const char *desc); |
||||
static void svc_getcpvalue(const char *name); |
||||
void svc_register(const char *name); |
||||
void svc_ready(void); |
||||
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context); |
||||
void WINAPI ServiceCtrlHandler(DWORD code); |
||||
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType); |
||||
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); |
||||
|
||||
#endif |
Loading…
Reference in new issue