Browse code

add support for hardware acceleration in clamd

git-svn: trunk@2239

Tomasz Kojm authored on 2006/09/12 08:10:31
Showing 11 changed files
... ...
@@ -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
     }
... ...
@@ -37,6 +37,7 @@
37 37
 #define CMD10 "END"
38 38
 #define CMD11 "SHUTDOWN"
39 39
 #define CMD12 "FD"
40
+#define CMD13 "MULTISCAN"
40 41
 
41 42
 #include "libclamav/clamav.h"
42 43
 #include "shared/cfgparser.h"
... ...
@@ -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 
... ...
@@ -270,6 +270,10 @@ LocalSocket /tmp/clamd
270 270
 # Default: no
271 271
 #ArchiveBlockMax no
272 272
 
273
+# Enable support for Sensory Networks' NodalCore hardware accelerator.
274
+# Default: no
275
+#HardwareAcceleration yes
276
+
273 277
 
274 278
 ##
275 279
 ## Clamuko settings
... ...
@@ -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},