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 Special thanks to Olliver Schinagl for his excellent work creating ClamAV's
new Docker files, image database deployment tooling, and user documentation. 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 ### Notable changes
The following was added in 0.103.1 and is repeated here for awareness, as The following was added in 0.103.1 and is repeated here for awareness, as

@ -75,6 +75,10 @@
#include "shared.h" #include "shared.h"
#include "scanner.h" #include "scanner.h"
#ifdef _WIN32
#include "service.h"
#endif
#include <sys/types.h> #include <sys/types.h>
#ifndef WIN32 #ifndef WIN32
#include <sys/wait.h> #include <sys/wait.h>
@ -94,6 +98,10 @@ static void help(void)
printf("\n"); printf("\n");
printf(" --help -h Show this help\n"); printf(" --help -h Show this help\n");
printf(" --version -V Show version number\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(" --foreground -F Run in foreground; do not daemonize\n");
printf(" --debug Enable debug mode\n"); printf(" --debug Enable debug mode\n");
printf(" --log=FILE -l FILE Log into FILE\n"); printf(" --log=FILE -l FILE Log into FILE\n");
@ -333,6 +341,22 @@ int main(int argc, char **argv)
#endif /* _WIN32 */ #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 */ /* drop privileges */
#ifndef _WIN32 #ifndef _WIN32
dropPrivRet = drop_privileges(user_name, logg_file); 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); 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))) { if ((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
logg("!%s\n", cl_strerror(ret)); logg("!%s\n", cl_strerror(ret));
ret = 1; ret = 1;
@ -789,6 +820,11 @@ int main(int argc, char **argv)
#endif #endif
} }
#elif defined(_WIN32)
if (optget(opts, "service-mode")->enabled) {
cl_engine_set_clcb_sigload(engine, NULL, NULL);
svc_ready();
}
#endif #endif
if (nlsockets == 0) { if (nlsockets == 0) {

@ -38,6 +38,7 @@ target_sources( common_obj
optparser.h optparser.h
output.h output.h
tar.h ) tar.h )
target_include_directories( common_obj target_include_directories( common_obj
PRIVATE ${CMAKE_BINARY_DIR} PRIVATE ${CMAKE_BINARY_DIR}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
@ -50,6 +51,7 @@ set_target_properties( common_obj PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" )
if(APPLE) if(APPLE)
target_sources( common_obj PRIVATE mac/cert_util_mac.m ) target_sources( common_obj PRIVATE mac/cert_util_mac.m )
elseif(WIN32) elseif(WIN32)
target_sources( common_obj PRIVATE service.c PUBLIC service.h )
target_sources( common_obj PRIVATE win/cert_util_win.c ) target_sources( common_obj PRIVATE win/cert_util_win.c )
else() else()
target_sources( common_obj PRIVATE linux/cert_util_linux.c ) 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, "", ""}, {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 */ /* cmdline only - deprecated */
{NULL, "bytecode-trust-all", 't', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN | OPT_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, "", ""}, {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 "output.h"
#include "misc.h" #include "misc.h"
#ifdef _WIN32
#include "service.h"
#endif
// libfreshclam // libfreshclam
#include "libfreshclam.h" #include "libfreshclam.h"
@ -173,6 +177,10 @@ static void help(void)
printf("\n"); printf("\n");
printf(" --config-file=FILE Read configuration from FILE.\n"); printf(" --config-file=FILE Read configuration from FILE.\n");
printf(" --log=FILE -l FILE Log into 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(" --daemon -d Run in daemon mode\n");
printf(" --pid=FILE -p FILE Save daemon's pid in FILE\n"); printf(" --pid=FILE -p FILE Save daemon's pid in FILE\n");
#ifndef _WIN32 #ifndef _WIN32
@ -1596,6 +1604,22 @@ int main(int argc, char **argv)
goto done; 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 */ /* check foreground option from command line to override config file */
for (i = 0; i < argc; i += 1) { for (i = 0; i < argc; i += 1) {
if ((memcmp(argv[i], "--foreground", 12) == 0) || (memcmp(argv[i], "-F", 2) == 0)) { if ((memcmp(argv[i], "--foreground", 12) == 0) || (memcmp(argv[i], "-F", 2) == 0)) {
@ -1880,6 +1904,14 @@ int main(int argc, char **argv)
} }
#endif #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. */ /* Write PID of daemon process to pidfile. */
if ((opt = optget(opts, "PidFile"))->enabled) { if ((opt = optget(opts, "PidFile"))->enabled) {
g_pidfile = opt->strarg; g_pidfile = opt->strarg;

Loading…
Cancel
Save