Added Windows services for clamd and freshclam

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
kang-grace 4 years ago committed by GitHub
parent 1df4f82f2b
commit 27d51762b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      NEWS.md
  2. 36
      clamd/clamd.c
  3. 2
      common/CMakeLists.txt
  4. 7
      common/optparser.c
  5. 228
      common/service.c
  6. 38
      common/service.h
  7. 32
      freshclam/freshclam.c

@ -67,6 +67,11 @@ ClamAV 0.104.0 includes the following improvements and changes.
Special thanks to Olliver Schinagl for his excellent work creating ClamAV's
new Docker files, image database deployment tooling, and user documentation.
- `clamd` and `freshclam` are now available as Windows services. To install
and run them, use the `--install-service` option and `net start [name]` command.
Special thanks to Gianluigi Tiesi for his original work on this feature.
### Notable changes
The following was added in 0.103.1 and is repeated here for awareness, as

@ -75,6 +75,10 @@
#include "shared.h"
#include "scanner.h"
#ifdef _WIN32
#include "service.h"
#endif
#include <sys/types.h>
#ifndef WIN32
#include <sys/wait.h>
@ -94,6 +98,10 @@ static void help(void)
printf("\n");
printf(" --help -h Show this help\n");
printf(" --version -V Show version number\n");
#ifdef _WIN32
printf(" --install-service Install Windows Service\n");
printf(" --uninstall-service Uninstall Windows Service\n");
#endif
printf(" --foreground -F Run in foreground; do not daemonize\n");
printf(" --debug Enable debug mode\n");
printf(" --log=FILE -l FILE Log into FILE\n");
@ -333,6 +341,22 @@ int main(int argc, char **argv)
#endif /* _WIN32 */
}
#ifdef _WIN32
if (optget(opts, "install-service")->enabled) {
svc_install("clamd", "ClamAV ClamD",
"Provides virus scanning facilities for ClamAV");
optfree(opts);
return 0;
}
if (optget(opts, "uninstall-service")->enabled) {
svc_uninstall("clamd", 1);
optfree(opts);
return 0;
}
#endif
/* drop privileges */
#ifndef _WIN32
dropPrivRet = drop_privileges(user_name, logg_file);
@ -621,6 +645,13 @@ int main(int argc, char **argv)
logg("#Max A-C depth set to %u\n", (unsigned int)opt->numarg);
}
#ifdef _WIN32
if (optget(opts, "daemon")->enabled) {
cl_engine_set_clcb_sigload(engine, svc_checkpoint, NULL);
svc_register("clamd");
}
#endif
if ((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
logg("!%s\n", cl_strerror(ret));
ret = 1;
@ -789,6 +820,11 @@ int main(int argc, char **argv)
#endif
}
#elif defined(_WIN32)
if (optget(opts, "service-mode")->enabled) {
cl_engine_set_clcb_sigload(engine, NULL, NULL);
svc_ready();
}
#endif
if (nlsockets == 0) {

@ -38,6 +38,7 @@ target_sources( common_obj
optparser.h
output.h
tar.h )
target_include_directories( common_obj
PRIVATE ${CMAKE_BINARY_DIR}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
@ -50,6 +51,7 @@ set_target_properties( common_obj PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" )
if(APPLE)
target_sources( common_obj PRIVATE mac/cert_util_mac.m )
elseif(WIN32)
target_sources( common_obj PRIVATE service.c PUBLIC service.h )
target_sources( common_obj PRIVATE win/cert_util_win.c )
else()
target_sources( common_obj PRIVATE linux/cert_util_linux.c )

@ -200,6 +200,13 @@ const struct clam_option __clam_options[] = {
{NULL, "archive-verbose", 'a', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN, "", ""},
#ifdef _WIN32
{NULL, "install-service", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "", ""},
{NULL, "uninstall-service", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "", ""},
{NULL, "daemon", 'd', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD, "", ""},
{NULL, "service-mode", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "", ""},
#endif
/* cmdline only - deprecated */
{NULL, "bytecode-trust-all", 't', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN | OPT_DEPRECATED, "", ""},
{NULL, "http-proxy", 0, CLOPT_TYPE_STRING, NULL, 0, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", ""},

@ -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

@ -60,6 +60,10 @@
#include "output.h"
#include "misc.h"
#ifdef _WIN32
#include "service.h"
#endif
// libfreshclam
#include "libfreshclam.h"
@ -173,6 +177,10 @@ static void help(void)
printf("\n");
printf(" --config-file=FILE Read configuration from FILE.\n");
printf(" --log=FILE -l FILE Log into FILE\n");
#ifdef _WIN32
printf(" --install-service Install Windows Service\n");
printf(" --uninstall-service Uninstall Windows Service\n");
#endif
printf(" --daemon -d Run in daemon mode\n");
printf(" --pid=FILE -p FILE Save daemon's pid in FILE\n");
#ifndef _WIN32
@ -1596,6 +1604,22 @@ int main(int argc, char **argv)
goto done;
}
#ifdef _WIN32
if (optget(opts, "install-service")->enabled) {
svc_install("freshclam", "ClamAV FreshClam",
"Updates virus pattern database for ClamAV");
optfree(opts);
return 0;
}
if (optget(opts, "uninstall-service")->enabled) {
svc_uninstall("freshclam", 1);
optfree(opts);
return 0;
}
#endif
/* check foreground option from command line to override config file */
for (i = 0; i < argc; i += 1) {
if ((memcmp(argv[i], "--foreground", 12) == 0) || (memcmp(argv[i], "-F", 2) == 0)) {
@ -1880,6 +1904,14 @@ int main(int argc, char **argv)
}
#endif
#ifdef _WIN32
if (optget(opts, "service-mode")->enabled) {
mprintf_disabled = 1;
svc_register("freshclam");
svc_ready();
}
#endif
/* Write PID of daemon process to pidfile. */
if ((opt = optget(opts, "PidFile"))->enabled) {
g_pidfile = opt->strarg;

Loading…
Cancel
Save