Skip to content

Commit e6c62a5

Browse files
authored
Merge pull request #2 from kang-grace/CLAM-1502-windows-services-implementation
Windows services implementation
2 parents 9a9cfc1 + 522dd23 commit e6c62a5

File tree

6 files changed

+333
-39
lines changed

6 files changed

+333
-39
lines changed

clamd/clamd.c

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@
7575
#include "shared.h"
7676
#include "scanner.h"
7777

78+
#ifdef _WIN32
79+
#include "service.h"
80+
#endif
81+
7882
#include <sys/types.h>
7983
#ifndef WIN32
8084
#include <sys/wait.h>
@@ -94,18 +98,16 @@ static void help(void)
9498
printf("\n");
9599
printf(" --help -h Show this help\n");
96100
printf(" --version -V Show version number\n");
101+
#ifdef _WIN32
102+
printf(" --install-service Install Windows Service\n");
103+
printf(" --uninstall-service Uninstall Windows Service\n");
104+
#endif
97105
printf(" --foreground -F Run in foreground; do not daemonize\n");
98106
printf(" --debug Enable debug mode\n");
99107
printf(" --log=FILE -l FILE Log into FILE\n");
100108
printf(" --config-file=FILE -c FILE Read configuration from FILE\n");
101109
printf("\n");
102110
printf("Pass in - as the filename for stdin.\n");
103-
#ifdef _WIN32
104-
printf("Windows Service:\n");
105-
printf(" --daemon Start in Service mode (internal)\n");
106-
printf(" --install Install Windows Service\n");
107-
printf(" --uninstall Uninstall Windows Service\n");
108-
#endif
109111
printf("\n");
110112
}
111113

@@ -324,21 +326,6 @@ int main(int argc, char **argv)
324326
}
325327
umask(old_umask);
326328

327-
/* windows command line options */
328-
#ifdef _WIN32
329-
if (optget(opts, "daemon")->enabled) {
330-
logg("daemon option enabled\n");
331-
}
332-
333-
if (optget(opts, "install")->enabled) {
334-
logg("install Windows service option enabled\n");
335-
}
336-
337-
if (optget(opts, "uninstall")->enabled) {
338-
logg("uninstall Windows service option enabled\n");
339-
}
340-
#endif
341-
342329
#ifndef _WIN32
343330
/*If the file has already been created by a different user, it will just be
344331
* rewritten by us, but not change the ownership, so do that explicitly.
@@ -354,6 +341,23 @@ int main(int argc, char **argv)
354341
#endif /* _WIN32 */
355342
}
356343

344+
/* windows command line options */
345+
#ifdef _WIN32
346+
347+
if (optget(opts, "install-service")->enabled) {
348+
svc_install("clamd", "ClamAV ClamD",
349+
"Provides virus scanning facilities for ClamAV");
350+
optfree(opts);
351+
return 0;
352+
}
353+
354+
if (optget(opts, "uninstall-service")->enabled) {
355+
svc_uninstall("clamd", 1);
356+
optfree(opts);
357+
return 0;
358+
}
359+
#endif
360+
357361
/* drop privileges */
358362
#ifndef _WIN32
359363
dropPrivRet = drop_privileges(user_name, logg_file);
@@ -642,6 +646,13 @@ int main(int argc, char **argv)
642646
logg("#Max A-C depth set to %u\n", (unsigned int)opt->numarg);
643647
}
644648

649+
#ifdef _WIN32
650+
if (optget(opts, "daemon")->enabled) {
651+
cl_engine_set_clcb_sigload(engine, svc_checkpoint, NULL);
652+
svc_register("clamd");
653+
}
654+
#endif
655+
645656
if ((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
646657
logg("!%s\n", cl_strerror(ret));
647658
ret = 1;
@@ -810,6 +821,11 @@ int main(int argc, char **argv)
810821
#endif
811822
}
812823

824+
#elif defined(_WIN32)
825+
if (optget(opts, "daemon")->enabled) {
826+
cl_engine_set_clcb_sigload(engine, NULL, NULL);
827+
svc_ready();
828+
}
813829
#endif
814830

815831
if (nlsockets == 0) {

common/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ target_sources( common_obj
3838
optparser.h
3939
output.h
4040
tar.h )
41+
4142
target_include_directories( common_obj
4243
PRIVATE ${CMAKE_BINARY_DIR}
4344
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
@@ -50,6 +51,7 @@ set_target_properties( common_obj PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" )
5051
if(APPLE)
5152
target_sources( common_obj PRIVATE mac/cert_util_mac.m )
5253
elseif(WIN32)
54+
target_sources( common_obj PRIVATE service.c PUBLIC service.h )
5355
target_sources( common_obj PRIVATE win/cert_util_win.c )
5456
else()
5557
target_sources( common_obj PRIVATE linux/cert_util_linux.c )

common/optparser.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ const struct clam_option __clam_options[] = {
201201
{NULL, "archive-verbose", 'a', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN, "", ""},
202202

203203
#ifdef _WIN32
204-
{NULL, "install", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "", ""},
205-
{NULL, "uninstall", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "", ""},
204+
{NULL, "install-service", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "", ""},
205+
{NULL, "uninstall-service", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "", ""},
206206
{NULL, "daemon", 'd', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD, "", ""},
207207
#endif
208208

common/service.c

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
* Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3+
* Copyright (C) 2007-2013 Sourcefire, Inc.
4+
*
5+
* Authors: Gianluigi Tiesi
6+
*
7+
* This program is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License version 2 as
9+
* published by the Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19+
* MA 02110-1301, USA.
20+
*/
21+
22+
#include <platform.h>
23+
#include <winsvc.h>
24+
25+
#include "service.h"
26+
#include "output.h"
27+
28+
static SERVICE_STATUS svc;
29+
static SERVICE_STATUS_HANDLE svc_handle;
30+
static SERVICE_TABLE_ENTRYA DT[] = {{"Service", ServiceMain}, {NULL, NULL}};
31+
32+
static HANDLE evStart;
33+
static HANDLE DispatcherThread;
34+
static int checkpoint_every = 5000;
35+
36+
int svc_uninstall(const char *name, int verbose)
37+
{
38+
SC_HANDLE sm, svc;
39+
int ret = 1;
40+
41+
if (!(sm = OpenSCManagerA(NULL, NULL, DELETE))) {
42+
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
43+
fprintf(stderr, "Windows Services are not supported on this Platform\n");
44+
else
45+
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
46+
return 0;
47+
}
48+
49+
if ((svc = OpenServiceA(sm, name, DELETE))) {
50+
if (DeleteService(svc)) {
51+
if (verbose) printf("Service %s successfully removed\n", name);
52+
} else {
53+
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
54+
ret = 0;
55+
}
56+
} else {
57+
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
58+
if (verbose) printf("Service %s does not exist\n", name);
59+
} else {
60+
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
61+
ret = 0;
62+
}
63+
}
64+
65+
if (svc) CloseServiceHandle(svc);
66+
CloseServiceHandle(sm);
67+
return ret;
68+
}
69+
70+
int svc_install(const char *name, const char *dname, const char *desc)
71+
{
72+
SC_HANDLE sm, svc;
73+
char modulepath[MAX_PATH];
74+
char binpath[MAX_PATH];
75+
SERVICE_DESCRIPTIONA sdesc = {(char *)desc};
76+
77+
if (!GetModuleFileName(NULL, modulepath, MAX_PATH - 1)) {
78+
fprintf(stderr, "Unable to get the executable name (%d)\n", GetLastError());
79+
return 0;
80+
}
81+
82+
if (!svc_uninstall(name, 0)) return 0;
83+
84+
if (!(sm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CREATE_SERVICE | DELETE))) {
85+
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
86+
fprintf(stderr, "Windows Services are not supported on this Platform\n");
87+
else
88+
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
89+
return 0;
90+
}
91+
92+
if (strchr(modulepath, ' '))
93+
snprintf(binpath, MAX_PATH - 1, "\"%s\" --daemon", modulepath);
94+
else
95+
snprintf(binpath, MAX_PATH - 1, "%s --daemon", modulepath);
96+
97+
svc = CreateServiceA(sm, name, dname, SERVICE_CHANGE_CONFIG,
98+
SERVICE_WIN32_OWN_PROCESS,
99+
SERVICE_DEMAND_START,
100+
SERVICE_ERROR_NORMAL,
101+
binpath,
102+
NULL, /* Load group order */
103+
NULL, /* Tag Id */
104+
NULL, /* Dependencies */
105+
NULL, /* User -> Local System */
106+
"");
107+
108+
if (!svc) {
109+
fprintf(stderr, "Unable to Create Service %s (%d)\n", name, GetLastError());
110+
CloseServiceHandle(sm);
111+
return 0;
112+
}
113+
114+
/* ChangeServiceConfig2A() */
115+
if (!ChangeServiceConfig2A(svc, SERVICE_CONFIG_DESCRIPTION, &sdesc))
116+
fprintf(stderr, "Unable to set description for Service %s (%d)\n", name, GetLastError());
117+
118+
CloseServiceHandle(svc);
119+
CloseServiceHandle(sm);
120+
121+
printf("Service %s successfully created.\n", name);
122+
printf("Use 'net start %s' and 'net stop %s' to start/stop the service.\n", name, name);
123+
return 1;
124+
}
125+
126+
static void svc_getcpvalue(const char *name)
127+
{
128+
HKEY hKey;
129+
DWORD dwType;
130+
DWORD value, vlen = sizeof(DWORD);
131+
char subkey[MAX_PATH];
132+
133+
snprintf(subkey, MAX_PATH - 1, "SYSTEM\\CurrentControlSet\\Services\\%s", name);
134+
135+
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
136+
return;
137+
138+
if ((RegQueryValueExA(hKey, "Checkpoint", NULL, &dwType, (LPBYTE)&value, &vlen) == ERROR_SUCCESS) &&
139+
(vlen == sizeof(DWORD) && (dwType == REG_DWORD)))
140+
checkpoint_every = value;
141+
142+
RegCloseKey(hKey);
143+
}
144+
145+
void svc_register(const char *name)
146+
{
147+
DWORD tid;
148+
DT->lpServiceName = (char *)name;
149+
svc_getcpvalue(name);
150+
151+
evStart = CreateEvent(NULL, TRUE, FALSE, NULL);
152+
DispatcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartServiceCtrlDispatcherA, (LPVOID)DT, 0, &tid);
153+
}
154+
155+
void svc_ready(void)
156+
{
157+
WaitForSingleObject(evStart, INFINITE);
158+
159+
svc.dwCurrentState = SERVICE_RUNNING;
160+
svc.dwControlsAccepted |= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
161+
svc.dwCheckPoint = 0;
162+
163+
if (!SetServiceStatus(svc_handle, &svc)) {
164+
logg("[service] SetServiceStatus() failed with %d\n", GetLastError());
165+
exit(1);
166+
}
167+
}
168+
169+
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context)
170+
{
171+
if (svc.dwCurrentState == SERVICE_START_PENDING) {
172+
svc.dwCheckPoint++;
173+
if ((svc.dwCheckPoint % checkpoint_every) == 0)
174+
SetServiceStatus(svc_handle, &svc);
175+
}
176+
return 0;
177+
}
178+
179+
void WINAPI ServiceCtrlHandler(DWORD code)
180+
{
181+
switch (code) {
182+
case SERVICE_CONTROL_STOP:
183+
case SERVICE_CONTROL_SHUTDOWN:
184+
svc.dwCurrentState = SERVICE_STOPPED;
185+
svc.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
186+
SetServiceStatus(svc_handle, &svc);
187+
return;
188+
case SERVICE_CONTROL_INTERROGATE:
189+
break;
190+
}
191+
192+
SetServiceStatus(svc_handle, &svc);
193+
}
194+
195+
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType)
196+
{
197+
if (CtrlType == CTRL_C_EVENT) {
198+
SetConsoleCtrlHandler(cw_stop_ctrl_handler, FALSE);
199+
fprintf(stderr, "Control+C pressed, aborting...\n");
200+
exit(0);
201+
}
202+
return TRUE;
203+
}
204+
205+
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv)
206+
{
207+
svc.dwServiceType = SERVICE_WIN32;
208+
svc.dwCurrentState = SERVICE_START_PENDING;
209+
svc.dwControlsAccepted = 0;
210+
svc.dwWin32ExitCode = NO_ERROR;
211+
svc.dwServiceSpecificExitCode = 0;
212+
svc.dwCheckPoint = 0;
213+
svc.dwWaitHint = 0;
214+
215+
if (!(svc_handle = RegisterServiceCtrlHandlerA(DT->lpServiceName, ServiceCtrlHandler))) {
216+
logg("[service] RegisterServiceCtrlHandler() failed with %d\n", GetLastError());
217+
exit(1);
218+
}
219+
220+
if (!SetServiceStatus(svc_handle, &svc)) {
221+
logg("[service] SetServiceStatus() failed with %d\n", GetLastError());
222+
exit(1);
223+
}
224+
225+
SetEvent(evStart);
226+
WaitForSingleObject(DispatcherThread, INFINITE);
227+
cw_stop_ctrl_handler(CTRL_C_EVENT);
228+
}

0 commit comments

Comments
 (0)