git-svn: trunk@2239
Tomasz Kojm authored on 2006/09/12 08:10:31... | ... |
@@ -1,3 +1,9 @@ |
1 |
+Tue Sep 12 01:04:39 CEST 2006 (tk) |
|
2 |
+---------------------------------- |
|
3 |
+ * clamd, clamdscan: add support for hardware acceleration |
|
4 |
+ * etc/clamd.conf: add HardwareAcceleration option |
|
5 |
+ * clamd: add MULTISCAN command (for scanning directories with multiple threads) |
|
6 |
+ |
|
1 | 7 |
Sun Sep 10 22:40:20 BST 2006 (njh) |
2 | 8 |
---------------------------------- |
3 | 9 |
* clamav-milter: Fix possible underrun in load balanced configurations |
... | ... |
@@ -281,6 +281,14 @@ int main(int argc, char **argv) |
281 | 281 |
if(cfgopt(copt, "LeaveTemporaryFiles")->enabled) |
282 | 282 |
cl_settempdir(NULL, 1); |
283 | 283 |
|
284 |
+ /* fork into background */ |
|
285 |
+ if(!cfgopt(copt, "Foreground")->enabled) { |
|
286 |
+ daemonize(); |
|
287 |
+ if(!debug_mode) |
|
288 |
+ chdir("/"); |
|
289 |
+ } else |
|
290 |
+ foreground = 1; |
|
291 |
+ |
|
284 | 292 |
/* load the database(s) */ |
285 | 293 |
dbdir = cfgopt(copt, "DatabaseDirectory")->strarg; |
286 | 294 |
logg("#Reading databases from %s\n", dbdir); |
... | ... |
@@ -290,6 +298,15 @@ int main(int argc, char **argv) |
290 | 290 |
logg("Not loading phishing signatures.\n"); |
291 | 291 |
} |
292 | 292 |
|
293 |
+ if(!cfgopt(copt, "HardwareAcceleration")->enabled) { |
|
294 |
+#ifdef HAVE_HWACCEL |
|
295 |
+ dboptions |= CL_DB_HWACCEL; |
|
296 |
+ logg("Enabling support for hardware acceleration.\n"); |
|
297 |
+#else |
|
298 |
+ logg("^Support for hardware acceleration not compiled in.\n"); |
|
299 |
+#endif |
|
300 |
+ } |
|
301 |
+ |
|
293 | 302 |
if((ret = cl_load(dbdir, &root, &sigs, dboptions))) { |
294 | 303 |
logg("!%s\n", cl_strerror(ret)); |
295 | 304 |
logg_close(); |
... | ... |
@@ -312,14 +329,6 @@ int main(int argc, char **argv) |
312 | 312 |
return 1; |
313 | 313 |
} |
314 | 314 |
|
315 |
- /* fork into background */ |
|
316 |
- if(!cfgopt(copt, "Foreground")->enabled) { |
|
317 |
- daemonize(); |
|
318 |
- if(!debug_mode) |
|
319 |
- chdir("/"); |
|
320 |
- } else |
|
321 |
- foreground = 1; |
|
322 |
- |
|
323 | 315 |
if(tcpsock) { |
324 | 316 |
lsockets[nlsockets] = tcpserver(copt); |
325 | 317 |
if(lsockets[nlsockets] == -1) { |
... | ... |
@@ -70,7 +70,7 @@ dev_t procdev; /* /proc device */ |
70 | 70 |
# endif |
71 | 71 |
#endif |
72 | 72 |
|
73 |
-static int checksymlink(const char *path) |
|
73 |
+int checksymlink(const char *path) |
|
74 | 74 |
{ |
75 | 75 |
struct stat statbuf; |
76 | 76 |
|
... | ... |
@@ -255,7 +255,7 @@ int scan(const char *filename, unsigned long int *scanned, const struct cl_node |
255 | 255 |
if(!ret) |
256 | 256 |
mdprintf(odesc, "%s: OK\n", filename); |
257 | 257 |
|
258 |
- mdprintf(odesc, "\n"); /* Terminate response with a blank line boundary */ |
|
258 |
+ /* mdprintf(odesc, "\n"); */ /* Terminate response with a blank line boundary */ |
|
259 | 259 |
return ret; |
260 | 260 |
} |
261 | 261 |
|
... | ... |
@@ -31,4 +31,6 @@ int scanfd(const int fd, unsigned long int *scanned, const struct cl_node *root, |
31 | 31 |
|
32 | 32 |
int scanstream(int odesc, unsigned long int *scanned, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt); |
33 | 33 |
|
34 |
+int checksymlink(const char *path); |
|
35 |
+ |
|
34 | 36 |
#endif |
... | ... |
@@ -26,6 +26,7 @@ |
26 | 26 |
#include <string.h> |
27 | 27 |
#include <unistd.h> |
28 | 28 |
#include <sys/types.h> |
29 |
+#include <dirent.h> |
|
29 | 30 |
#include <sys/socket.h> |
30 | 31 |
#include <sys/time.h> |
31 | 32 |
#include <pthread.h> |
... | ... |
@@ -45,8 +46,166 @@ |
45 | 45 |
#include "server.h" |
46 | 46 |
#include "clamuko.h" |
47 | 47 |
#include "session.h" |
48 |
+#include "thrmgr.h" |
|
49 |
+#include "shared.h" |
|
48 | 50 |
|
49 | 51 |
static pthread_mutex_t ctime_mutex = PTHREAD_MUTEX_INITIALIZER; |
52 |
+extern int progexit; |
|
53 |
+ |
|
54 |
+struct multi_tag { |
|
55 |
+ int sd; |
|
56 |
+ int options; |
|
57 |
+ const struct cfgstruct *copt; |
|
58 |
+ char *fname; |
|
59 |
+ const struct cl_node *root; |
|
60 |
+ const struct cl_limits *limits; |
|
61 |
+}; |
|
62 |
+ |
|
63 |
+void multiscanfile(void *arg) |
|
64 |
+{ |
|
65 |
+ struct multi_tag *tag = (struct multi_tag *) arg; |
|
66 |
+ const char *virname; |
|
67 |
+ sigset_t sigset; |
|
68 |
+ int ret; |
|
69 |
+ |
|
70 |
+ |
|
71 |
+ /* ignore all signals */ |
|
72 |
+ sigfillset(&sigset); |
|
73 |
+ pthread_sigmask(SIG_SETMASK, &sigset, NULL); |
|
74 |
+ |
|
75 |
+ ret = cl_scanfile(tag->fname, &virname, NULL, tag->root, tag->limits, tag->options); |
|
76 |
+ |
|
77 |
+ if(ret == CL_VIRUS) { |
|
78 |
+ mdprintf(tag->sd, "%s: %s FOUND\n", tag->fname, virname); |
|
79 |
+ logg("%s: %s FOUND\n", tag->fname, virname); |
|
80 |
+ virusaction(tag->fname, virname, tag->copt); |
|
81 |
+ } else if(ret != CL_CLEAN) { |
|
82 |
+ mdprintf(tag->sd, "%s: %s ERROR\n", tag->fname, cl_strerror(ret)); |
|
83 |
+ logg("%s: %s ERROR\n", tag->fname, cl_strerror(ret)); |
|
84 |
+ } else if(logok) { |
|
85 |
+ logg("%s: OK\n", tag->fname); |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ free(tag->fname); |
|
89 |
+ free(tag); |
|
90 |
+ return; |
|
91 |
+} |
|
92 |
+ |
|
93 |
+static int multiscan(const char *dirname, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt, int odesc, unsigned int *reclev, threadpool_t *multi_pool) |
|
94 |
+{ |
|
95 |
+ DIR *dd; |
|
96 |
+ struct dirent *dent; |
|
97 |
+#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2) |
|
98 |
+ union { |
|
99 |
+ struct dirent d; |
|
100 |
+ char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; |
|
101 |
+ } result; |
|
102 |
+#endif |
|
103 |
+ struct stat statbuf; |
|
104 |
+ char *fname; |
|
105 |
+ int scanret = 0; |
|
106 |
+ unsigned int maxdirrec = 0; |
|
107 |
+ struct multi_tag *scandata; |
|
108 |
+ |
|
109 |
+ |
|
110 |
+ maxdirrec = cfgopt(copt, "MaxDirectoryRecursion")->numarg; |
|
111 |
+ if(maxdirrec) { |
|
112 |
+ if(*reclev > maxdirrec) { |
|
113 |
+ logg("*multiscan: Directory recursion limit exceeded at %s\n", dirname); |
|
114 |
+ return 0; |
|
115 |
+ } |
|
116 |
+ (*reclev)++; |
|
117 |
+ } |
|
118 |
+ |
|
119 |
+ if((dd = opendir(dirname)) != NULL) { |
|
120 |
+#ifdef HAVE_READDIR_R_3 |
|
121 |
+ while(!readdir_r(dd, &result.d, &dent) && dent) { |
|
122 |
+#elif defined(HAVE_READDIR_R_2) |
|
123 |
+ while((dent = (struct dirent *) readdir_r(dd, &result.d))) { |
|
124 |
+#else |
|
125 |
+ while((dent = readdir(dd))) { |
|
126 |
+#endif |
|
127 |
+ if (!is_fd_connected(odesc)) { |
|
128 |
+ logg("multiscan: Client disconnected\n"); |
|
129 |
+ closedir(dd); |
|
130 |
+ return -1; |
|
131 |
+ } |
|
132 |
+ |
|
133 |
+ if(progexit) { |
|
134 |
+ closedir(dd); |
|
135 |
+ return -1; |
|
136 |
+ } |
|
137 |
+ |
|
138 |
+#ifndef C_INTERIX |
|
139 |
+ if(dent->d_ino) |
|
140 |
+#endif |
|
141 |
+ { |
|
142 |
+ if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) { |
|
143 |
+ /* build the full name */ |
|
144 |
+ fname = (char *) mcalloc(strlen(dirname) + strlen(dent->d_name) + 2, sizeof(char)); |
|
145 |
+ if(!fname) { |
|
146 |
+ logg("!multiscan: Can't allocate memory for fname\n"); |
|
147 |
+ closedir(dd); |
|
148 |
+ return -1; |
|
149 |
+ } |
|
150 |
+ sprintf(fname, "%s/%s", dirname, dent->d_name); |
|
151 |
+ |
|
152 |
+ /* stat the file */ |
|
153 |
+ if(lstat(fname, &statbuf) != -1) { |
|
154 |
+ if((S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) || (S_ISLNK(statbuf.st_mode) && (checksymlink(fname) == 1) && cfgopt(copt, "FollowDirectorySymlinks")->enabled)) { |
|
155 |
+ if(multiscan(fname, root, limits, options, copt, odesc, reclev, multi_pool) == -1) { |
|
156 |
+ free(fname); |
|
157 |
+ closedir(dd); |
|
158 |
+ return -1; |
|
159 |
+ } |
|
160 |
+ } else { |
|
161 |
+ if(S_ISREG(statbuf.st_mode) || (S_ISLNK(statbuf.st_mode) && (checksymlink(fname) == 2) && cfgopt(copt, "FollowFileSymlinks")->enabled)) { |
|
162 |
+ |
|
163 |
+#ifdef C_LINUX |
|
164 |
+ if(procdev && (statbuf.st_dev == procdev)) |
|
165 |
+ scanret = CL_CLEAN; |
|
166 |
+ else |
|
167 |
+#endif |
|
168 |
+ { |
|
169 |
+ scandata = (struct multi_tag *) mmalloc(sizeof(struct multi_tag)); |
|
170 |
+ if(!scandata) { |
|
171 |
+ logg("!multiscan: Can't allocate memory for scandata\n"); |
|
172 |
+ free(fname); |
|
173 |
+ closedir(dd); |
|
174 |
+ return -1; |
|
175 |
+ } |
|
176 |
+ scandata->sd = odesc; |
|
177 |
+ scandata->options = options; |
|
178 |
+ scandata->copt = copt; |
|
179 |
+ scandata->fname = fname; |
|
180 |
+ scandata->root = root; |
|
181 |
+ scandata->limits = limits; |
|
182 |
+ if(!thrmgr_dispatch(multi_pool, scandata)) { |
|
183 |
+ logg("!multiscan: thread dispatch failed for multi_pool (file %s)\n", fname); |
|
184 |
+ mdprintf(odesc, "ERROR: Can't scan file %s\n", fname); |
|
185 |
+ free(fname); |
|
186 |
+ free(scandata); |
|
187 |
+ closedir(dd); |
|
188 |
+ return -1; |
|
189 |
+ } |
|
190 |
+ |
|
191 |
+ while(!multi_pool->thr_idle) /* non-critical */ |
|
192 |
+ usleep(200); |
|
193 |
+ } |
|
194 |
+ } |
|
195 |
+ } |
|
196 |
+ } |
|
197 |
+ } |
|
198 |
+ } |
|
199 |
+ } |
|
200 |
+ closedir(dd); |
|
201 |
+ } else { |
|
202 |
+ return -1; |
|
203 |
+ } |
|
204 |
+ |
|
205 |
+ (*reclev)--; |
|
206 |
+ return 0; |
|
207 |
+} |
|
50 | 208 |
|
51 | 209 |
int command(int desc, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt, int timeout) |
52 | 210 |
{ |
... | ... |
@@ -139,6 +298,51 @@ int command(int desc, const struct cl_node *root, const struct cl_limits *limits |
139 | 139 |
scanfd(fd, NULL, root, limits, options, copt, desc); |
140 | 140 |
close(fd); /* FIXME: should we close it here? */ |
141 | 141 |
|
142 |
+ } else if(!strncmp(buff, CMD13, strlen(CMD13))) { /* MULTISCAN */ |
|
143 |
+ threadpool_t *multi_pool; |
|
144 |
+ int idletimeout = cfgopt(copt, "IdleTimeout")->numarg; |
|
145 |
+ int max_threads = cfgopt(copt, "MaxThreads")->numarg; |
|
146 |
+ int ret; |
|
147 |
+ unsigned int reclev = 0; |
|
148 |
+ const char *path = buff + strlen(CMD13) + 1; |
|
149 |
+ const char *virname; |
|
150 |
+ struct stat sb; |
|
151 |
+ |
|
152 |
+ if(stat(path, &sb) == -1) { |
|
153 |
+ mdprintf(desc, "Can't stat file %s\n", path); |
|
154 |
+ return -1; |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ if(S_ISDIR(sb.st_mode)) { |
|
158 |
+ if((multi_pool = thrmgr_new(max_threads, idletimeout, multiscanfile)) == NULL) { |
|
159 |
+ logg("!thrmgr_new failed for multi_pool\n"); |
|
160 |
+ mdprintf(desc, "ERROR: thrmgr_new failed for multi_pool\n"); |
|
161 |
+ return -1; |
|
162 |
+ } |
|
163 |
+ |
|
164 |
+ ret = multiscan(path, root, limits, options, copt, desc, &reclev, multi_pool); |
|
165 |
+ thrmgr_destroy(multi_pool); |
|
166 |
+ |
|
167 |
+ if(ret < 0) |
|
168 |
+ return -1; |
|
169 |
+ |
|
170 |
+ } else { |
|
171 |
+ ret = cl_scanfile(path, &virname, NULL, root, limits, options); |
|
172 |
+ |
|
173 |
+ if(ret == CL_VIRUS) { |
|
174 |
+ mdprintf(desc, "%s: %s FOUND\n", path, virname); |
|
175 |
+ logg("%s: %s FOUND\n", path, virname); |
|
176 |
+ virusaction(path, virname, copt); |
|
177 |
+ } else if(ret != CL_CLEAN) { |
|
178 |
+ mdprintf(desc, "%s: %s ERROR\n", path, cl_strerror(ret)); |
|
179 |
+ logg("%s: %s ERROR\n", path, cl_strerror(ret)); |
|
180 |
+ } else { |
|
181 |
+ mdprintf(desc, "%s: OK\n", path); |
|
182 |
+ if(logok) |
|
183 |
+ logg("%s: OK\n", path); |
|
184 |
+ } |
|
185 |
+ } |
|
186 |
+ |
|
142 | 187 |
} else { |
143 | 188 |
mdprintf(desc, "UNKNOWN COMMAND\n"); |
144 | 189 |
} |
... | ... |
@@ -58,6 +58,7 @@ |
58 | 58 |
|
59 | 59 |
void move_infected(const char *filename, const struct optstruct *opt); |
60 | 60 |
int notremoved = 0, notmoved = 0; |
61 |
+static int hwaccel = 0; |
|
61 | 62 |
|
62 | 63 |
static int dsresult(int sockd, const struct optstruct *opt) |
63 | 64 |
{ |
... | ... |
@@ -118,14 +119,14 @@ static int dsresult(int sockd, const struct optstruct *opt) |
118 | 118 |
return infected ? infected : (waserror ? -1 : 0); |
119 | 119 |
} |
120 | 120 |
|
121 |
-static int dsfile(int sockd, const char *filename, const struct optstruct *opt) |
|
121 |
+static int dsfile(int sockd, const char *scantype, const char *filename, const struct optstruct *opt) |
|
122 | 122 |
{ |
123 | 123 |
int ret; |
124 | 124 |
char *scancmd; |
125 | 125 |
|
126 | 126 |
|
127 | 127 |
scancmd = mcalloc(strlen(filename) + 20, sizeof(char)); |
128 |
- sprintf(scancmd, "CONTSCAN %s", filename); |
|
128 |
+ sprintf(scancmd, "%s %s", scantype, filename); |
|
129 | 129 |
|
130 | 130 |
if(write(sockd, scancmd, strlen(scancmd)) <= 0) { |
131 | 131 |
logg("^Can't write to the socket.\n"); |
... | ... |
@@ -341,6 +342,7 @@ static int dconnect(const struct optstruct *opt) |
341 | 341 |
if((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { |
342 | 342 |
perror("socket()"); |
343 | 343 |
logg("^Can't create the socket.\n"); |
344 |
+ freecfg(copt); |
|
344 | 345 |
return -1; |
345 | 346 |
} |
346 | 347 |
|
... | ... |
@@ -348,6 +350,7 @@ static int dconnect(const struct optstruct *opt) |
348 | 348 |
close(sockd); |
349 | 349 |
perror("connect()"); |
350 | 350 |
logg("^Can't connect to clamd.\n"); |
351 |
+ freecfg(copt); |
|
351 | 352 |
return -1; |
352 | 353 |
} |
353 | 354 |
|
... | ... |
@@ -356,6 +359,7 @@ static int dconnect(const struct optstruct *opt) |
356 | 356 |
if((sockd = socket(SOCKET_INET, SOCK_STREAM, 0)) < 0) { |
357 | 357 |
perror("socket()"); |
358 | 358 |
logg("^Can't create the socket.\n"); |
359 |
+ freecfg(copt); |
|
359 | 360 |
return -1; |
360 | 361 |
} |
361 | 362 |
|
... | ... |
@@ -367,6 +371,7 @@ static int dconnect(const struct optstruct *opt) |
367 | 367 |
close(sockd); |
368 | 368 |
perror("gethostbyname()"); |
369 | 369 |
logg("^Can't lookup clamd hostname.\n"); |
370 |
+ freecfg(copt); |
|
370 | 371 |
return -1; |
371 | 372 |
} |
372 | 373 |
server2.sin_addr = *(struct in_addr *) he->h_addr_list[0]; |
... | ... |
@@ -376,14 +381,23 @@ static int dconnect(const struct optstruct *opt) |
376 | 376 |
close(sockd); |
377 | 377 |
perror("connect()"); |
378 | 378 |
logg("^Can't connect to clamd.\n"); |
379 |
+ freecfg(copt); |
|
379 | 380 |
return -1; |
380 | 381 |
} |
381 | 382 |
|
382 | 383 |
} else { |
383 | 384 |
logg("^Clamd is not configured properly.\n"); |
385 |
+ freecfg(copt); |
|
384 | 386 |
return -1; |
385 | 387 |
} |
386 | 388 |
|
389 |
+#ifdef HAVE_HWACCEL |
|
390 |
+ if(cfgopt(copt, "HardwareAcceleration")->enabled) |
|
391 |
+ hwaccel = 1; |
|
392 |
+#endif |
|
393 |
+ |
|
394 |
+ freecfg(copt); |
|
395 |
+ |
|
387 | 396 |
return sockd; |
388 | 397 |
} |
389 | 398 |
|
... | ... |
@@ -392,12 +406,20 @@ int client(const struct optstruct *opt, int *infected) |
392 | 392 |
char cwd[200], *fullpath; |
393 | 393 |
int sockd, ret, errors = 0; |
394 | 394 |
struct stat sb; |
395 |
+ const char *scantype; |
|
395 | 396 |
|
396 | 397 |
|
397 | 398 |
*infected = 0; |
398 | 399 |
|
399 |
- /* parse argument list */ |
|
400 |
+ /* TODO: add a cmdline option to allow using MULTISCAN on systems |
|
401 |
+ * without hardware accelerators (but with multiple CPUs) |
|
402 |
+ */ |
|
403 |
+ if(hwaccel) |
|
404 |
+ scantype = "MULTISCAN"; |
|
405 |
+ else |
|
406 |
+ scantype = "CONTSCAN"; |
|
400 | 407 |
|
408 |
+ /* parse argument list */ |
|
401 | 409 |
if(opt->filename == NULL || strlen(opt->filename) == 0) { |
402 | 410 |
/* scan current directory */ |
403 | 411 |
if(!getcwd(cwd, 200)) { |
... | ... |
@@ -408,7 +430,7 @@ int client(const struct optstruct *opt, int *infected) |
408 | 408 |
if((sockd = dconnect(opt)) < 0) |
409 | 409 |
return 2; |
410 | 410 |
|
411 |
- if((ret = dsfile(sockd, cwd, opt)) >= 0) |
|
411 |
+ if((ret = dsfile(sockd, scantype, cwd, opt)) >= 0) |
|
412 | 412 |
*infected += ret; |
413 | 413 |
else |
414 | 414 |
errors++; |
... | ... |
@@ -466,7 +488,7 @@ int client(const struct optstruct *opt, int *infected) |
466 | 466 |
if((sockd = dconnect(opt)) < 0) |
467 | 467 |
return 2; |
468 | 468 |
|
469 |
- if((ret = dsfile(sockd, fullpath, opt)) >= 0) |
|
469 |
+ if((ret = dsfile(sockd, scantype, fullpath, opt)) >= 0) |
|
470 | 470 |
*infected += ret; |
471 | 471 |
else |
472 | 472 |
errors++; |
... | ... |
@@ -36,6 +36,9 @@ Scan a file or directory (recursively) with archive support disabled. A full pat |
36 | 36 |
\fBCONTSCAN file/directory\fR |
37 | 37 |
Scan a file or directory (recursively) with archive support enabled and continue scanning even when virus is found. A full path is required. |
38 | 38 |
.TP |
39 |
+\fBMULTISCAN file/directory\fR |
|
40 |
+Scan directories with multiple threads. |
|
41 |
+.TP |
|
39 | 42 |
\fBSTREAM\fR |
40 | 43 |
Scan stream \- on this command clamd will return "PORT number" and you can connect to that port and send a data to scan. |
41 | 44 |
.SH "OPTIONS" |
... | ... |
@@ -274,6 +274,11 @@ Mark archives as viruses (e.g RAR.ExceededFileSize, Zip.ExceededFilesLimit) if A |
274 | 274 |
.br |
275 | 275 |
Default: disabled |
276 | 276 |
.TP |
277 |
+\fBHardwareAcceleration\fR |
|
278 |
+Enable support for Sensory Networks' NodalCore hardware accelerator. |
|
279 |
+.br |
|
280 |
+Default: disabled |
|
281 |
+.TP |
|
277 | 282 |
\fBClamukoScanOnAccess\fR |
278 | 283 |
Enable Clamuko. Dazuko (/dev/dazuko) must be configured and running. |
279 | 284 |
.br |
... | ... |
@@ -82,6 +82,7 @@ struct cfgoption cfg_options[] = { |
82 | 82 |
{"AllowSupplementaryGroups", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
83 | 83 |
{"SelfCheck", OPT_NUM, 1800, NULL, 0, OPT_CLAMD}, |
84 | 84 |
{"VirusEvent", OPT_FULLSTR, -1, NULL, 0, OPT_CLAMD}, |
85 |
+ {"HardwareAcceleration", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
|
85 | 86 |
{"ClamukoScanOnAccess", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
86 | 87 |
{"ClamukoScanOnOpen", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
87 | 88 |
{"ClamukoScanOnClose", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |