Browse code

UseProcesses

git-svn-id: file:///var/lib/svn/clamav-devel/trunk/clamav-devel@130 77e5149b-7576-45b1-b177-96237e5ba77b

Tomasz Kojm authored on 2003/11/29 12:38:55
Showing 12 changed files
... ...
@@ -1,3 +1,7 @@
1
+Sat Nov 29 04:35:03 CET 2003 (tk)
2
+---------------------------------
3
+  * clamd: UseProcesses: use processes instead of threads (initial version)
4
+
1 5
 Thu Nov 27 00:51:03 CET 2003 (tk)
2 6
 ---------------------------------
3 7
   * clamd: also update database timestamps after RELOAD and not only SelfCheck
... ...
@@ -30,7 +30,8 @@ clamd_SOURCES = \
30 30
     tcpserver.h \
31 31
     localserver.c \
32 32
     localserver.h \
33
-    server.c \
33
+    server-proc.c \
34
+    server-th.c \
34 35
     server.h \
35 36
     scanner.c \
36 37
     scanner.h \
... ...
@@ -127,7 +127,8 @@ install_sh = @install_sh@
127 127
 @BUILD_CLAMD_TRUE@    tcpserver.h \
128 128
 @BUILD_CLAMD_TRUE@    localserver.c \
129 129
 @BUILD_CLAMD_TRUE@    localserver.h \
130
-@BUILD_CLAMD_TRUE@    server.c \
130
+@BUILD_CLAMD_TRUE@    server-proc.c \
131
+@BUILD_CLAMD_TRUE@    server-th.c \
131 132
 @BUILD_CLAMD_TRUE@    server.h \
132 133
 @BUILD_CLAMD_TRUE@    scanner.c \
133 134
 @BUILD_CLAMD_TRUE@    scanner.h \
... ...
@@ -156,10 +157,10 @@ PROGRAMS = $(sbin_PROGRAMS)
156 156
 
157 157
 @BUILD_CLAMD_TRUE@am_clamd_OBJECTS = options.$(OBJEXT) cfgfile.$(OBJEXT) \
158 158
 @BUILD_CLAMD_TRUE@	clamd.$(OBJEXT) tcpserver.$(OBJEXT) \
159
-@BUILD_CLAMD_TRUE@	localserver.$(OBJEXT) server.$(OBJEXT) \
160
-@BUILD_CLAMD_TRUE@	scanner.$(OBJEXT) others.$(OBJEXT) \
161
-@BUILD_CLAMD_TRUE@	clamuko.$(OBJEXT) dazukoio.$(OBJEXT) \
162
-@BUILD_CLAMD_TRUE@	tests.$(OBJEXT)
159
+@BUILD_CLAMD_TRUE@	localserver.$(OBJEXT) server-proc.$(OBJEXT) \
160
+@BUILD_CLAMD_TRUE@	server-th.$(OBJEXT) scanner.$(OBJEXT) \
161
+@BUILD_CLAMD_TRUE@	others.$(OBJEXT) clamuko.$(OBJEXT) \
162
+@BUILD_CLAMD_TRUE@	dazukoio.$(OBJEXT) tests.$(OBJEXT)
163 163
 clamd_OBJECTS = $(am_clamd_OBJECTS)
164 164
 @BUILD_CLAMD_TRUE@clamd_DEPENDENCIES = ../clamscan/getopt.o
165 165
 @BUILD_CLAMD_FALSE@clamd_DEPENDENCIES =
... ...
@@ -173,8 +174,8 @@ am__depfiles_maybe = depfiles
173 173
 @AMDEP_TRUE@	./$(DEPDIR)/clamuko.Po ./$(DEPDIR)/dazukoio.Po \
174 174
 @AMDEP_TRUE@	./$(DEPDIR)/localserver.Po ./$(DEPDIR)/options.Po \
175 175
 @AMDEP_TRUE@	./$(DEPDIR)/others.Po ./$(DEPDIR)/scanner.Po \
176
-@AMDEP_TRUE@	./$(DEPDIR)/server.Po ./$(DEPDIR)/tcpserver.Po \
177
-@AMDEP_TRUE@	./$(DEPDIR)/tests.Po
176
+@AMDEP_TRUE@	./$(DEPDIR)/server-proc.Po ./$(DEPDIR)/server-th.Po \
177
+@AMDEP_TRUE@	./$(DEPDIR)/tcpserver.Po ./$(DEPDIR)/tests.Po
178 178
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
179 179
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
180 180
 LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \
... ...
@@ -241,7 +242,8 @@ distclean-compile:
241 241
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
242 242
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/others.Po@am__quote@
243 243
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scanner.Po@am__quote@
244
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@
244
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-proc.Po@am__quote@
245
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-th.Po@am__quote@
245 246
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcpserver.Po@am__quote@
246 247
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tests.Po@am__quote@
247 248
 
... ...
@@ -58,6 +58,7 @@ struct cfgstruct *parsecfg(const char *cfgfile)
58 58
 	    {"MaxConnectionQueueLength", OPT_NUM},
59 59
 	    {"StreamSaveToDisk", OPT_NOARG},
60 60
 	    {"StreamMaxLength", OPT_COMPSIZE},
61
+	    {"UseProcesses", OPT_NOARG},
61 62
 	    {"MaxThreads", OPT_NUM},
62 63
 	    {"ThreadTimeout", OPT_NUM},
63 64
 	    {"MaxDirectoryRecursion", OPT_NUM},
... ...
@@ -96,6 +96,10 @@ int localserver(const struct optstruct *opt, const struct cfgstruct *copt, struc
96 96
 	exit(1);
97 97
     }
98 98
 
99
-    acceptloop(sockfd, root, copt);
99
+    if(cfgopt(copt, "UseProcesses"))
100
+	acceptloop_proc(sockfd, root, copt);
101
+    else
102
+	acceptloop_th(sockfd, root, copt);
103
+
100 104
     return 0;
101 105
 }
... ...
@@ -100,7 +100,7 @@ int dirscan(const char *dirname, char **virname, unsigned long int *scanned, con
100 100
 					ret = 2;
101 101
 				} else if(scanret != CL_CLEAN) {
102 102
 				    mdprintf(odesc, "%s: %s ERROR\n", fname, cl_strerror(scanret));
103
-				    logg("%s: %s\n ERROR", fname, cl_strerror(scanret));
103
+				    logg("%s: %s ERROR\n", fname, cl_strerror(scanret));
104 104
 				}
105 105
 		    }
106 106
 
107 107
new file mode 100644
... ...
@@ -0,0 +1,339 @@
0
+/*
1
+ *  Copyright (C) 2002, 2003 Tomasz Kojm <zolw@konarski.edu.pl>
2
+ *
3
+ *  This program is free software; you can redistribute it and/or modify
4
+ *  it under the terms of the GNU General Public License as published by
5
+ *  the Free Software Foundation; either version 2 of the License, or
6
+ *  (at your option) any later version.
7
+ *
8
+ *  This program is distributed in the hope that it will be useful,
9
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+ *  GNU General Public License for more details.
12
+ *
13
+ *  You should have received a copy of the GNU General Public License
14
+ *  along with this program; if not, write to the Free Software
15
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
+ */
17
+
18
+#include <stdio.h>
19
+#include <stdlib.h>
20
+#include <string.h>
21
+#include <unistd.h>
22
+#include <sys/types.h>
23
+#include <sys/socket.h>
24
+#include <sys/wait.h>
25
+#include <pthread.h>
26
+#include <time.h>
27
+#include <signal.h>
28
+
29
+#include "cfgfile.h"
30
+#include "others.h"
31
+#include "defaults.h"
32
+#include "scanner.h"
33
+#include "server.h"
34
+#include "clamuko.h"
35
+#include "tests.h"
36
+
37
+#define CMD1 "SCAN"
38
+#define CMD2 "RAWSCAN"
39
+#define CMD3 "QUIT"
40
+#define CMD4 "RELOAD"
41
+#define CMD5 "PING"
42
+#define CMD6 "CONTSCAN"
43
+#define CMD7 "VERSION"
44
+#define CMD8 "STREAM"
45
+#define CMD9 "STREAM2"
46
+
47
+
48
+int procscan(const char *name, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt, int odesc, short contscan)
49
+{
50
+	int pid;
51
+	struct cfgstruct *cpt;
52
+
53
+    switch(pid = fork()) {
54
+	case -1:
55
+	    logg("!Can't fork()\n");
56
+	    return -1;
57
+
58
+	case 0:
59
+	    if((cpt = cfgopt(copt, "ThreadTimeout")))
60
+	        alarm(cpt->numarg);
61
+	    else
62
+	        alarm(CL_DEFAULT_SCANTIMEOUT);
63
+
64
+	    if(!name)
65
+		scanstream(odesc, NULL, root, limits, options, copt);
66
+	    else
67
+		scan(name, NULL, root, limits, options, copt, odesc, contscan);
68
+
69
+	    exit(0);
70
+
71
+	default:
72
+	    return pid;
73
+    }
74
+
75
+    return -1;
76
+}
77
+
78
+int acceptd = -1;
79
+
80
+int acceptloop_proc(int socketd, struct cl_node *root, const struct cfgstruct *copt)
81
+{
82
+	int i, j, bread, options = 0, maxwait, childs, *session, status,
83
+	    virnum, need_wait, ret;
84
+	struct cfgstruct *cpt;
85
+	struct cl_limits limits;
86
+	struct sigaction sigact;
87
+	char buff[1025];
88
+	const char *dbdir;
89
+	sigset_t sigset;
90
+	mode_t old_umask;
91
+
92
+    /* save the PID */
93
+    if((cpt = cfgopt(copt, "PidFile"))) {
94
+	    FILE *fd;
95
+	    old_umask = umask(0006);
96
+	if((fd = fopen(cpt->strarg, "w")) == NULL) {
97
+	    logg("!Can't save PID in file %s\n", cpt->strarg);
98
+	} else {
99
+	    fprintf(fd, "%d", getpid());
100
+	    fclose(fd);
101
+	}
102
+	umask(old_umask);
103
+    }
104
+
105
+    logg("*Listening daemon: PID: %d\n", getpid());
106
+
107
+    if((cpt = cfgopt(copt, "MaxThreads")))
108
+	childs = cpt->numarg;
109
+    else
110
+	childs = CL_DEFAULT_MAXTHREADS;
111
+
112
+    logg("Maximal number of childs: %d\n", childs);
113
+    session = (int *) mcalloc(childs, sizeof(int));
114
+
115
+    if((cpt = cfgopt(copt, "DataDirectory")))
116
+	dbdir = cpt->strarg;
117
+    else
118
+        dbdir = cl_retdbdir();
119
+
120
+    if(cfgopt(copt, "ScanArchive")) {
121
+
122
+	/* set up limits */
123
+	memset(&limits, 0, sizeof(struct cl_limits));
124
+
125
+	if((cpt = cfgopt(copt, "ArchiveMaxFileSize"))) {
126
+	    if((limits.maxfilesize = cpt->numarg))
127
+		logg("Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
128
+	    else
129
+		logg("^Archive: File size limit protection disabled.\n");
130
+	} else {
131
+	    limits.maxfilesize = 10485760;
132
+	    logg("^USING HARDCODED LIMIT: Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
133
+	}
134
+
135
+	if((cpt = cfgopt(copt, "ArchiveMaxRecursion"))) {
136
+	    if((limits.maxreclevel = cpt->numarg))
137
+		logg("Archive: Recursion level limit set to %d.\n", limits.maxreclevel);
138
+	    else
139
+		logg("^Archive: Recursion level limit protection disabled.\n");
140
+	} else {
141
+	    limits.maxreclevel = 5;
142
+	    logg("^USING HARDCODED LIMIT: Archive: Recursion level set to %d.\n", limits.maxreclevel);
143
+	}
144
+
145
+	if((cpt = cfgopt(copt, "ArchiveMaxFiles"))) {
146
+	    if((limits.maxfiles = cpt->numarg))
147
+		logg("Archive: Files limit set to %d.\n", limits.maxfiles);
148
+	    else
149
+		logg("^Archive: Files limit protection disabled.\n");
150
+	} else {
151
+	    limits.maxfiles = 1000;
152
+	    logg("^USING HARDCODED LIMIT: Archive: Files limit set to %d.\n", limits.maxfiles);
153
+	}
154
+
155
+	if(cfgopt(copt, "ArchiveLimitMemoryUsage")) {
156
+	    limits.archivememlim = 1;
157
+	    logg("Archive: Limited memory usage.\n", limits.maxfiles);
158
+	} else
159
+	    limits.archivememlim = 0;
160
+
161
+	
162
+	logg("Archive support enabled.\n");
163
+	options |= CL_ARCHIVE;
164
+
165
+	if(cfgopt(copt, "ScanRAR")) {
166
+	    logg("RAR support enabled.\n");
167
+	} else {
168
+	    logg("RAR support disabled.\n");
169
+	    options |= CL_DISABLERAR;
170
+	}
171
+
172
+    } else {
173
+	logg("Archive support disabled.\n");
174
+    }
175
+
176
+    if(cfgopt(copt, "ScanMail")) { 
177
+	logg("Mail support enabled.\n");
178
+	options |= CL_MAIL;
179
+    } else {
180
+	logg("Mail support disabled.\n");
181
+    }
182
+
183
+    /* set up signal handling */
184
+
185
+    sigfillset(&sigset);
186
+    sigdelset(&sigset, SIGINT);
187
+    sigdelset(&sigset, SIGTERM);
188
+    sigdelset(&sigset, SIGSEGV);
189
+    sigdelset(&sigset, SIGHUP);
190
+    sigprocmask(SIG_SETMASK, &sigset, NULL);
191
+
192
+    /* SIGINT, SIGTERM, SIGSEGV */
193
+    sigact.sa_handler = sighandler;
194
+    sigemptyset(&sigact.sa_mask);
195
+    sigaddset(&sigact.sa_mask, SIGINT);
196
+    sigaddset(&sigact.sa_mask, SIGTERM);
197
+    sigaddset(&sigact.sa_mask, SIGHUP);
198
+    sigaction(SIGINT, &sigact, NULL);
199
+    sigaction(SIGTERM, &sigact, NULL);
200
+#ifndef CL_DEBUG
201
+    sigaction(SIGSEGV, &sigact, NULL);
202
+#endif
203
+    sigaction(SIGHUP, &sigact, NULL);
204
+
205
+
206
+    while(1) {
207
+
208
+	for(i = 0; ; i++) { /* find a free session */
209
+
210
+	    /* free all finished childs */
211
+	    for(j = 0; j <= childs; j++)
212
+		if(session[j] && waitpid(session[j], &status, WNOHANG))
213
+		    session[j] = 0;
214
+
215
+	    if(i == childs)
216
+		i = 0;
217
+
218
+	    if(!session[i])
219
+		break;
220
+	}
221
+
222
+	if((acceptd = accept(socketd, NULL, NULL)) == -1) {
223
+	    logg("!accept() failed.\n");
224
+	    /* exit ? */
225
+	    continue;
226
+	}
227
+
228
+	if((bread = read(acceptd, buff, 1024)) == -1) {
229
+	    logg("!read(desc %d) failed.\n", acceptd);
230
+	} else {
231
+
232
+	    buff[bread] = 0;
233
+	    chomp(buff);
234
+
235
+	    if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */
236
+		session[i] = procscan(buff + strlen(CMD1) + 1, root, &limits, options, copt, acceptd, 0);
237
+
238
+	    } else if(!strncmp(buff, CMD2, strlen(CMD2))) { /* RAWSCAN */
239
+		options &= ~CL_ARCHIVE;
240
+		session[i] = procscan(buff + strlen(CMD2) + 1, root, NULL, options, copt, acceptd, 0);
241
+
242
+	    } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */
243
+		kill(0, SIGTERM);
244
+
245
+	    } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */
246
+		mdprintf(acceptd, "RELOADING\n");
247
+
248
+		/* wait until all childs are finished */
249
+		do {
250
+		    need_wait = 0;
251
+
252
+		    /* free all finished childs */
253
+		    for(i = 0; i <= childs; i++)
254
+			if(session[i] && waitpid(session[i], &status, WNOHANG))
255
+			    session[i] = 0;
256
+
257
+		    for(i = 0; i <= childs; i++)
258
+			if(session[i])
259
+			    need_wait = 1;
260
+
261
+		    if(need_wait)
262
+			usleep(200000);
263
+
264
+		} while(need_wait);
265
+
266
+		cl_freetrie(root);
267
+		root = NULL;
268
+		logg("Reading databases from %s\n", dbdir);
269
+		//cl_statfree(&dbstat);
270
+		//cl_statinidir(dbdir, &dbstat);
271
+		virnum = 0;
272
+		if((ret = cl_loaddbdir(dbdir, &root, &virnum))) {
273
+		    logg("!%s\n", cl_strerror(ret));
274
+		    exit(1);
275
+		}
276
+
277
+		if(!root) {
278
+		    logg("!Database initialization problem.\n");
279
+		    exit(1);
280
+		} else {
281
+		    cl_buildtrie(root);
282
+		    /* check integrity */
283
+		    if(!testsignature(root)) {
284
+			logg("!Unable to detect test signature.\n");
285
+			exit(1);
286
+		    }
287
+
288
+		    logg("Database correctly reloaded (%d viruses)\n", virnum);
289
+		    mdprintf(acceptd, "RELOADED\n");
290
+		}
291
+
292
+	    } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */
293
+		mdprintf(acceptd, "PONG\n");
294
+
295
+	    } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */
296
+		session[i] = procscan(buff + strlen(CMD6) + 1, root, &limits, options, copt, acceptd, 1);
297
+
298
+	    } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */
299
+		mdprintf(acceptd, "clamd / ClamAV version "VERSION"\n");
300
+
301
+	    } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */
302
+		session[i] = procscan(NULL, root, &limits, options, copt, acceptd, 0);
303
+	    }
304
+	}
305
+
306
+	close(acceptd);
307
+    }
308
+}
309
+
310
+void sighandler(int sig)
311
+{
312
+	time_t currtime;
313
+
314
+    switch(sig) {
315
+	case SIGINT:
316
+	case SIGTERM:
317
+	    time(&currtime);
318
+	    logg("--- Process %d stopped at %s", getpid(), ctime(&currtime));
319
+	    exit(0);
320
+	    break; /* not reached */
321
+
322
+#ifndef CL_DEBUG
323
+	case SIGSEGV:
324
+	    logg("Segmentation fault :-( Bye..\n");
325
+	    exit(11); /* probably not reached at all */
326
+	    break; /* not reached */
327
+#endif
328
+	case SIGHUP:
329
+	    sighup = 1;
330
+	    logg("SIGHUP catched: log file re-opened.\n");
331
+	    break;
332
+	case SIGALRM:
333
+	    if(acceptd > 0)
334
+		mdprintf(acceptd, "Session (PID %d): Time out ERROR\n", getpid());
335
+	    logg("Session (PID %d) stopped due to timeout.\n", getpid());
336
+	    exit(0);
337
+    }
338
+}
0 339
new file mode 100644
... ...
@@ -0,0 +1,648 @@
0
+/*
1
+ *  Copyright (C) 2002, 2003 Tomasz Kojm <zolw@konarski.edu.pl>
2
+ *
3
+ *  This program is free software; you can redistribute it and/or modify
4
+ *  it under the terms of the GNU General Public License as published by
5
+ *  the Free Software Foundation; either version 2 of the License, or
6
+ *  (at your option) any later version.
7
+ *
8
+ *  This program is distributed in the hope that it will be useful,
9
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+ *  GNU General Public License for more details.
12
+ *
13
+ *  You should have received a copy of the GNU General Public License
14
+ *  along with this program; if not, write to the Free Software
15
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
+ */
17
+
18
+#include <stdio.h>
19
+#include <stdlib.h>
20
+#include <string.h>
21
+#include <unistd.h>
22
+#include <sys/types.h>
23
+#include <sys/socket.h>
24
+#include <pthread.h>
25
+#include <time.h>
26
+#include <signal.h>
27
+
28
+#include "cfgfile.h"
29
+#include "others.h"
30
+#include "defaults.h"
31
+#include "scanner.h"
32
+#include "server.h"
33
+#include "clamuko.h"
34
+#include "tests.h"
35
+
36
+#define THREXIT					    \
37
+    close(ths[tharg->sid].desc);		    \
38
+    ths[tharg->sid].active = 0;			    \
39
+    /* this mutex is rather useless */		    \
40
+    /* pthread_mutex_unlock(&ths[tharg->sid].mutex);   */ \
41
+    free(tharg);				    \
42
+    return NULL
43
+
44
+#define CMD1 "SCAN"
45
+#define CMD2 "RAWSCAN"
46
+#define CMD3 "QUIT"
47
+#define CMD4 "RELOAD"
48
+#define CMD5 "PING"
49
+#define CMD6 "CONTSCAN"
50
+#define CMD7 "VERSION"
51
+#define CMD8 "STREAM"
52
+#define CMD9 "STREAM2"
53
+
54
+#ifdef CLAMUKO
55
+pthread_t clamukoid;
56
+#endif
57
+
58
+void *threadscanner(void *arg)
59
+{
60
+	struct thrarg *tharg = (struct thrarg *) arg;
61
+	char buff[32769];
62
+	sigset_t sigset;
63
+	int bread, options, maxwait = CL_DEFAULT_MAXWHILEWAIT;
64
+
65
+
66
+    /* ignore all signals */
67
+    sigfillset(&sigset);
68
+    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
69
+
70
+    if((bread = read(ths[tharg->sid].desc, buff, 1024)) == -1) {
71
+	logg("!Session(%d): read() failed.\n", tharg->sid);
72
+	THREXIT;
73
+    }
74
+
75
+    buff[bread] = 0;
76
+    chomp(buff);
77
+
78
+    if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */
79
+	scan(buff + strlen(CMD1) + 1, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt, ths[tharg->sid].desc, 0);
80
+
81
+    } else if(!strncmp(buff, CMD2, strlen(CMD2))) { /* RAWSCAN */
82
+	options = tharg->options & ~CL_ARCHIVE;
83
+	scan(buff + strlen(CMD2) + 1, NULL, tharg->root, NULL, options, tharg->copt, ths[tharg->sid].desc, 0);
84
+
85
+    } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */
86
+	if(!progexit)
87
+	    kill(progpid, SIGTERM);
88
+
89
+    } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */
90
+	mdprintf(ths[tharg->sid].desc, "RELOADING\n");
91
+	reload = 1;
92
+
93
+    } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */
94
+	mdprintf(ths[tharg->sid].desc, "PONG\n");
95
+
96
+    } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */
97
+	scan(buff + strlen(CMD6) + 1, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt, ths[tharg->sid].desc, 1);
98
+
99
+    } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */
100
+	mdprintf(ths[tharg->sid].desc, "clamd / ClamAV version "VERSION"\n");
101
+
102
+    } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */
103
+	scanstream(ths[tharg->sid].desc, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt);
104
+    }
105
+    /* else if(!strncmp(buff, CMD9, strlen(CMD9))) {
106
+	scanstream2(ths[tharg->sid].desc, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt);
107
+    }*/
108
+
109
+    THREXIT;
110
+}
111
+
112
+/* this function takes care for threads, exit and various checks */
113
+
114
+void *threadwatcher(void *arg)
115
+{
116
+	struct thrwarg *thwarg = (struct thrwarg *) arg;
117
+	struct cfgstruct *cpt;
118
+	sigset_t sigset;
119
+	int i, j, ret, maxwait, virnum;
120
+	unsigned long int timer = 0;
121
+	unsigned int timeout, threads, selfchk;
122
+	short int need_wait = 0, do_loop = 0, db_problem = 0;
123
+	const char *dbdir;
124
+	struct cl_stat dbstat;
125
+
126
+
127
+    /* ignore all signals (except for SIGSEGV) */
128
+    sigfillset(&sigset);
129
+    sigdelset(&sigset, SIGSEGV);
130
+    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
131
+
132
+#ifdef C_LINUX
133
+    logg("*ThreadWatcher: Started in process %d\n", getpid());
134
+#endif
135
+
136
+    if((cpt = cfgopt(thwarg->copt, "MaxThreads")))
137
+	threads = cpt->numarg;
138
+    else
139
+	threads = CL_DEFAULT_MAXTHREADS;
140
+
141
+    if((cpt = cfgopt(thwarg->copt, "SelfCheck")))
142
+	selfchk = cpt->numarg;
143
+    else
144
+	selfchk = CL_DEFAULT_SELFCHECK;
145
+
146
+    if(!selfchk) {
147
+	logg("^Self checking disabled.\n");
148
+    } else
149
+	logg("Self checking every %d seconds.\n", selfchk);
150
+
151
+    if((cpt = cfgopt(thwarg->copt, "ThreadTimeout")))
152
+	timeout = cpt->numarg;
153
+    else
154
+	timeout = CL_DEFAULT_SCANTIMEOUT;
155
+
156
+    if(!timeout) {
157
+	logg("^Timeout disabled.\n");
158
+    } else
159
+	logg("Timeout set to %d seconds.\n", timeout);
160
+
161
+    if((cpt = cfgopt(thwarg->copt, "DataDirectory")))
162
+	dbdir = cpt->strarg;
163
+    else
164
+	dbdir = cl_retdbdir();
165
+
166
+    memset(&dbstat, 0, sizeof(struct cl_stat));
167
+    cl_statinidir(dbdir, &dbstat);
168
+
169
+    for(i = 0; ; i++) {
170
+
171
+        if(i == threads)
172
+	    i = 0;
173
+
174
+	/* check time */
175
+        if(ths[i].active) /* races are harmless here (timeout is re-set) */
176
+	    if(time(NULL) - ths[i].start > timeout) {
177
+		pthread_cancel(ths[i].id);
178
+		mdprintf(ths[i].desc, "Session(%d): Time out ERROR\n", i);
179
+		close(ths[i].desc);
180
+		logg("Session %d stopped due to timeout.\n", i);
181
+		ths[i].active = 0;
182
+//		pthread_mutex_unlock(&ths[i].mutex);
183
+	    }
184
+
185
+	/* cancel all threads in case of quit */
186
+	if(progexit == 1) {
187
+#ifdef CLAMUKO
188
+	    /* stop clamuko */
189
+	    if(clamuko_running) {
190
+		logg("Stopping Clamuko... (id %d)\n", clamukoid);
191
+		pthread_kill(clamukoid, SIGUSR1);
192
+		/* we must wait for Dazuko unregistration */
193
+		maxwait = CL_DEFAULT_MAXWHILEWAIT * 5;
194
+		while(clamuko_running && maxwait--)
195
+		    usleep(200000);
196
+
197
+		if(!maxwait && clamuko_running)
198
+		    logg("!Critical error: Can't stop Clamuko.\n");
199
+	    }
200
+#endif
201
+
202
+	    for(j = 0; j < threads; j++)
203
+		if(ths[j].active) {
204
+		    pthread_cancel(ths[j].id);
205
+		    mdprintf(ths[j].desc, "Session(%d): Stopped (exiting)\n", j);
206
+		    close(ths[j].desc);
207
+		    logg("Session %d stopped (exiting).\n", j);
208
+//		    pthread_mutex_unlock(&ths[j].mutex);
209
+		}
210
+#ifndef C_BSD
211
+	    logg("*Freeing trie structure.\n");
212
+	    cl_freetrie(*thwarg->root);
213
+#endif
214
+	    logg("*Shutting down the main socket.\n");
215
+	    shutdown(thwarg->socketd, 2);
216
+	    logg("*Closing the main socket.\n");
217
+	    close(thwarg->socketd);
218
+	    if((cpt = cfgopt(thwarg->copt, "LocalSocket"))) {
219
+		if(unlink(cpt->strarg) == -1)
220
+		    logg("!Can't unlink the socket file %s\n", cpt->strarg);
221
+		else
222
+		    logg("Socket file removed.\n");
223
+	    }
224
+
225
+	    if((cpt = cfgopt(thwarg->copt, "PidFile"))) {
226
+		if(unlink(cpt->strarg) == -1)
227
+		    logg("!Can't unlink the pid file %s\n", cpt->strarg);
228
+		else
229
+		    logg("Pid file removed.\n");
230
+	    }
231
+
232
+	    progexit = 2;
233
+	    logg("*Exit level %d, ThreadWatcher termination.\n", progexit);
234
+	    return NULL;
235
+	}
236
+
237
+
238
+	/* do self checks */
239
+	if(selfchk && (db_problem || !(timer % selfchk))) {
240
+	    /* check the integrity of the database */
241
+	    if(!reload) {
242
+
243
+		if(cl_statchkdir(&dbstat) == 1) {
244
+		    logg("SelfCheck: Database modification detected. Forcing reload.\n");
245
+		    reload = 1;
246
+		    cl_statfree(&dbstat);
247
+		    cl_statinidir(dbdir, &dbstat);
248
+		} else
249
+		    logg("SelfCheck: Database status OK.\n");
250
+
251
+		if(!testsignature(*thwarg->root)) {
252
+		    if(db_problem) {
253
+			logg("!SelfCheck: Unable to repair internal structure. Exiting.\n");
254
+			kill(progpid, SIGTERM);
255
+			continue;
256
+		    }
257
+		    /* oops */
258
+		    logg("!SelfCheck: Unable to detect test signature, forcing database reload.\n");
259
+		    db_problem = 1;
260
+		    reload = 1;
261
+		} else {
262
+		    logg("*SelfCheck: Integrity OK\n");
263
+		    db_problem = 0;
264
+		}
265
+	    }
266
+	}
267
+
268
+	timer++;
269
+
270
+	/* reload the database */
271
+	if(reload) {
272
+
273
+	    /* make sure the main thread doesn't start new threads */
274
+	    do {
275
+		usleep(200000);
276
+	    } while(!main_accept && !main_reload);
277
+
278
+	    /* wait until all working threads are finished */
279
+	    do {
280
+		need_wait = 0;
281
+		for(j = 0; j < threads; j++)
282
+		    if(ths[j].active) {
283
+			if(time(NULL) - ths[j].start > timeout) {
284
+			    do_loop = 1;
285
+			    break;
286
+			} else need_wait = 1;
287
+		    }
288
+
289
+#ifdef CLAMUKO
290
+		if(clamuko_scanning && !clamuko_reload)
291
+		    need_wait = 1;
292
+#endif
293
+
294
+		if(need_wait)
295
+		    usleep(200000);
296
+
297
+		if(progexit == 1)
298
+		    break;
299
+
300
+	    } while(need_wait);
301
+
302
+	    if(progexit == 1) {
303
+		reload = 0;
304
+		continue;
305
+	    }
306
+
307
+	    if(do_loop) {
308
+		/* some threads must be stopped in the next iteration,
309
+		 * reload is still == 1
310
+		 */
311
+		logg("Database reload: some threads must be stopped in the next iteration.\n");
312
+		do_loop = 0;
313
+		continue;
314
+	    }
315
+
316
+	    /* relase old structure */
317
+	    cl_freetrie(*thwarg->root);
318
+	    *thwarg->root = NULL;
319
+
320
+	    /* reload */
321
+
322
+	    logg("Reading databases from %s\n", dbdir);
323
+
324
+	    cl_statfree(&dbstat);
325
+	    cl_statinidir(dbdir, &dbstat);
326
+	    virnum = 0;
327
+	    if((ret = cl_loaddbdir(dbdir, &*thwarg->root, &virnum))) {
328
+		logg("!%s\n", cl_strerror(ret));
329
+		kill(progpid, SIGTERM);
330
+		/* we stay in reload == 1, so all threads are waiting */
331
+		continue;
332
+	    }
333
+
334
+	    if(! *thwarg->root) {
335
+		logg("!Database initialization problem.\n");
336
+		kill(progpid, SIGTERM);
337
+	    } else {
338
+		cl_buildtrie(*thwarg->root);
339
+		/* check integrity */
340
+		if(!testsignature(*thwarg->root)) {
341
+		    logg("!Unable to detect test signature.\n");
342
+		    kill(progpid, SIGTERM);
343
+		}
344
+
345
+		logg("Database correctly reloaded (%d viruses)\n", virnum);
346
+	    }
347
+
348
+	    reload = 0;
349
+	}
350
+
351
+	sleep(1);
352
+    }
353
+
354
+    return NULL;
355
+}
356
+
357
+int threads;
358
+pthread_t watcherid;
359
+
360
+int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *copt)
361
+{
362
+	int acceptd, i, options = 0, maxwait;
363
+	struct cfgstruct *cpt;
364
+	struct thrarg *tharg;
365
+	struct thrwarg thwarg;
366
+	struct cl_limits limits;
367
+	pthread_attr_t thattr;
368
+	struct sigaction sigact;
369
+	sigset_t sigset;
370
+	mode_t old_umask;
371
+
372
+#if defined(C_BIGSTACK) || defined(C_BSD)
373
+	size_t stacksize;
374
+#endif
375
+
376
+
377
+    /* save the PID */
378
+    if((cpt = cfgopt(copt, "PidFile"))) {
379
+	    FILE *fd;
380
+	    old_umask = umask(0006);
381
+	if((fd = fopen(cpt->strarg, "w")) == NULL) {
382
+	    logg("!Can't save PID in file %s\n", cpt->strarg);
383
+	} else {
384
+	    fprintf(fd, "%d", getpid());
385
+	    fclose(fd);
386
+	}
387
+	umask(old_umask);
388
+    }
389
+
390
+    logg("*Listening daemon: PID: %d\n", getpid());
391
+
392
+    if((cpt = cfgopt(copt, "MaxThreads")))
393
+	threads = cpt->numarg;
394
+    else
395
+	threads = CL_DEFAULT_MAXTHREADS;
396
+
397
+    logg("Maximal number of threads: %d\n", threads);
398
+
399
+    ths = (struct thrsession *) mcalloc(threads, sizeof(struct thrsession));
400
+ 
401
+    for(i = 0; i < threads; i++)
402
+	pthread_mutex_init(&ths[i].mutex, NULL);
403
+
404
+
405
+    if(cfgopt(copt, "ScanArchive") || cfgopt(copt, "ClamukoScanArchive")) {
406
+
407
+	/* set up limits */
408
+	memset(&limits, 0, sizeof(struct cl_limits));
409
+
410
+	if((cpt = cfgopt(copt, "ArchiveMaxFileSize"))) {
411
+	    if((limits.maxfilesize = cpt->numarg))
412
+		logg("Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
413
+	    else
414
+		logg("^Archive: File size limit protection disabled.\n");
415
+	} else {
416
+	    limits.maxfilesize = 10485760;
417
+	    logg("^USING HARDCODED LIMIT: Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
418
+	}
419
+
420
+	if((cpt = cfgopt(copt, "ArchiveMaxRecursion"))) {
421
+	    if((limits.maxreclevel = cpt->numarg))
422
+		logg("Archive: Recursion level limit set to %d.\n", limits.maxreclevel);
423
+	    else
424
+		logg("^Archive: Recursion level limit protection disabled.\n");
425
+	} else {
426
+	    limits.maxreclevel = 5;
427
+	    logg("^USING HARDCODED LIMIT: Archive: Recursion level set to %d.\n", limits.maxreclevel);
428
+	}
429
+
430
+	if((cpt = cfgopt(copt, "ArchiveMaxFiles"))) {
431
+	    if((limits.maxfiles = cpt->numarg))
432
+		logg("Archive: Files limit set to %d.\n", limits.maxfiles);
433
+	    else
434
+		logg("^Archive: Files limit protection disabled.\n");
435
+	} else {
436
+	    limits.maxfiles = 1000;
437
+	    logg("^USING HARDCODED LIMIT: Archive: Files limit set to %d.\n", limits.maxfiles);
438
+	}
439
+
440
+	if(cfgopt(copt, "ArchiveLimitMemoryUsage")) {
441
+	    limits.archivememlim = 1;
442
+	    logg("Archive: Limited memory usage.\n", limits.maxfiles);
443
+	} else
444
+	    limits.archivememlim = 0;
445
+    }
446
+
447
+    if(cfgopt(copt, "ScanArchive")) { 
448
+	logg("Archive support enabled.\n");
449
+	options |= CL_ARCHIVE;
450
+
451
+	if(cfgopt(copt, "ScanRAR")) {
452
+	    logg("RAR support enabled.\n");
453
+	} else {
454
+	    logg("RAR support disabled.\n");
455
+	    options |= CL_DISABLERAR;
456
+	}
457
+    } else {
458
+	logg("Archive support disabled.\n");
459
+    }
460
+
461
+    if(cfgopt(copt, "ScanMail")) { 
462
+	logg("Mail files support enabled.\n");
463
+	options |= CL_MAIL;
464
+    } else {
465
+	logg("Mail files support disabled.\n");
466
+    }
467
+
468
+    /* initialize important global variables */
469
+    progexit = 0;
470
+    progpid = 0;
471
+    reload = 0;
472
+#ifdef CLAMUKO
473
+    clamuko_scanning = 0;
474
+    clamuko_running = 0;
475
+#endif
476
+
477
+    pthread_attr_init(&thattr);
478
+    pthread_attr_setdetachstate(&thattr, PTHREAD_CREATE_DETACHED);
479
+
480
+    /* run clamuko */
481
+    if(cfgopt(copt, "ClamukoScanOnLine"))
482
+#ifdef CLAMUKO
483
+    {
484
+	tharg = (struct thrarg *) mcalloc(1, sizeof(struct thrarg));
485
+	tharg->copt = copt;
486
+	tharg->root = root;
487
+	tharg->limits = &limits;
488
+	tharg->options = options;
489
+	clamuko_reload = 0;
490
+
491
+	pthread_create(&clamukoid, &thattr, clamukoth, tharg);
492
+    }
493
+#else
494
+	logg("!Clamuko is not available.\n");
495
+#endif
496
+
497
+    /* start thread watcher */
498
+    thwarg.socketd = socketd;
499
+    thwarg.copt = copt;
500
+    thwarg.root = &root;
501
+    pthread_create(&watcherid, &thattr, threadwatcher, &thwarg);
502
+
503
+    /* set up signal handling */
504
+
505
+    sigfillset(&sigset);
506
+    sigdelset(&sigset, SIGINT);
507
+    sigdelset(&sigset, SIGTERM);
508
+    sigdelset(&sigset, SIGSEGV);
509
+    sigdelset(&sigset, SIGHUP);
510
+    sigprocmask(SIG_SETMASK, &sigset, NULL);
511
+
512
+    /* SIGINT, SIGTERM, SIGSEGV */
513
+    sigact.sa_handler = sighandler_th;
514
+    sigemptyset(&sigact.sa_mask);
515
+    sigaddset(&sigact.sa_mask, SIGINT);
516
+    sigaddset(&sigact.sa_mask, SIGTERM);
517
+    sigaddset(&sigact.sa_mask, SIGHUP);
518
+    sigaction(SIGINT, &sigact, NULL);
519
+    sigaction(SIGTERM, &sigact, NULL);
520
+#ifndef CL_DEBUG
521
+    sigaction(SIGSEGV, &sigact, NULL);
522
+#endif
523
+    sigaction(SIGHUP, &sigact, NULL);
524
+
525
+    /* we need to save program's PID, because under Linux each thread
526
+     * has another PID, it works with other OSes as well
527
+     */
528
+    progpid = getpid();
529
+
530
+#if defined(C_BIGSTACK) || defined(C_BSD)
531
+    /*
532
+     * njh@bandsman.co.uk:
533
+     * libclamav/scanners.c uses a *huge* buffer
534
+     * (128K not BUFSIZ from stdio.h).
535
+     * We need to allow for that.
536
+     */
537
+    pthread_attr_getstacksize(&thattr, &stacksize);
538
+    cli_dbgmsg("set stacksize to %u\n", stacksize + SCANBUFF + 64 * 1024);
539
+    pthread_attr_setstacksize(&thattr, stacksize + SCANBUFF + 64 * 1024);
540
+#endif
541
+
542
+    while(1) {
543
+
544
+	/* find a free session */
545
+	for(i = 0; ; i++) {
546
+	    if(i == threads)
547
+		i = 0;
548
+
549
+	    if(!ths[i].active) {
550
+		/* logg("*Found free slot: %d\n", i); */
551
+		break;
552
+	    }
553
+	}
554
+
555
+
556
+	main_accept = 1;
557
+	if((acceptd = accept(socketd, NULL, NULL)) == -1) {
558
+	    logg("!accept() failed.\n");
559
+	    /* exit ? */
560
+	    continue;
561
+	}
562
+	main_accept = 0;
563
+
564
+	if(reload) { /* do not start new threads */
565
+	    main_reload = 1;
566
+	    logg("*Main thread: database reloading (waiting).\n");
567
+	    maxwait = CL_DEFAULT_MAXWHILEWAIT;
568
+	    while(reload && maxwait--)
569
+		sleep(1);
570
+
571
+	    if(!maxwait && reload) {
572
+		logg("!Database reloading failed (time exceeded). Exit forced.\n");
573
+		progexit = 1;
574
+		sleep(10);
575
+		exit(1);
576
+	    }
577
+
578
+	    logg("*Main thread: database reloaded.\n");
579
+	    main_reload = 0;
580
+	}
581
+
582
+	tharg = (struct thrarg *) mcalloc(1, sizeof(struct thrarg));
583
+	tharg->copt = copt;
584
+	tharg->sid = i;
585
+	tharg->root = root;
586
+	tharg->limits = &limits;
587
+	tharg->options = options;
588
+
589
+	ths[i].desc = acceptd;
590
+	ths[i].start = time(NULL);
591
+	ths[i].active = 1; /* the structure must be activated exactly here
592
+			    * because we will surely create a race condition 
593
+			    * in other places (if activated in the new thread
594
+			    * there * will be a race in the main thread (it
595
+			    * may assign the same thread session once more);
596
+			    * if activated after pthread_create() the new
597
+			    * thread may be already finished).
598
+			    */
599
+
600
+	if(pthread_create(&ths[i].id, &thattr, threadscanner, tharg)) {
601
+	    logg("!Session(%d) did not start. Dropping connection.", i);
602
+	    close(acceptd);
603
+	    ths[i].active = 0;
604
+	}
605
+    }
606
+}
607
+
608
+void sighandler_th(int sig)
609
+{
610
+	time_t currtime;
611
+	int maxwait = CL_DEFAULT_MAXWHILEWAIT * 5, i;
612
+
613
+    switch(sig) {
614
+	case SIGINT:
615
+	case SIGTERM:
616
+	    progexit = 1;
617
+	    logg("*Signal %d caught -> exiting.\n", sig);
618
+
619
+	    while(progexit != 2 && maxwait--)
620
+		usleep(200000);
621
+
622
+	    if(!maxwait && progexit != 2)
623
+		logg("!Critical error: Cannot reach exit level 2.\n");
624
+
625
+	    time(&currtime);
626
+	    logg("--- Stopped at %s", ctime(&currtime));
627
+	    exit(0);
628
+	    break; /* not reached */
629
+
630
+#ifndef CL_DEBUG
631
+	case SIGSEGV:
632
+	    logg("Segmentation fault :-( Bye..\n");
633
+
634
+	    for(i = 0; i < threads; i++)
635
+		if(ths[i].active)
636
+		    pthread_kill(ths[i].id, 9);
637
+
638
+	    pthread_kill(watcherid, 9);
639
+	    exit(11); /* probably not reached at all */
640
+	    break; /* not reached */
641
+#endif
642
+	case SIGHUP:
643
+	    sighup = 1;
644
+	    logg("SIGHUP catched: log file re-opened.\n");
645
+	    break;
646
+    }
647
+}
0 648
deleted file mode 100644
... ...
@@ -1,646 +0,0 @@
1
-/*
2
- *  Copyright (C) 2002, 2003 Tomasz Kojm <zolw@konarski.edu.pl>
3
- *
4
- *  This program is free software; you can redistribute it and/or modify
5
- *  it under the terms of the GNU General Public License as published by
6
- *  the Free Software Foundation; either version 2 of the License, or
7
- *  (at your option) any later version.
8
- *
9
- *  This program is distributed in the hope that it will be useful,
10
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
- *  GNU General Public License for more details.
13
- *
14
- *  You should have received a copy of the GNU General Public License
15
- *  along with this program; if not, write to the Free Software
16
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
- */
18
-
19
-#include <stdio.h>
20
-#include <stdlib.h>
21
-#include <string.h>
22
-#include <unistd.h>
23
-#include <sys/types.h>
24
-#include <sys/socket.h>
25
-#include <pthread.h>
26
-#include <time.h>
27
-#include <signal.h>
28
-
29
-#include "cfgfile.h"
30
-#include "others.h"
31
-#include "defaults.h"
32
-#include "scanner.h"
33
-#include "server.h"
34
-#include "clamuko.h"
35
-#include "tests.h"
36
-
37
-#define THREXIT					    \
38
-    close(ths[tharg->sid].desc);		    \
39
-    ths[tharg->sid].active = 0;			    \
40
-    /* this mutex is rather useless */		    \
41
-    /* pthread_mutex_unlock(&ths[tharg->sid].mutex);   */ \
42
-    free(tharg);				    \
43
-    return NULL
44
-
45
-#define CMD1 "SCAN"
46
-#define CMD2 "RAWSCAN"
47
-#define CMD3 "QUIT"
48
-#define CMD4 "RELOAD"
49
-#define CMD5 "PING"
50
-#define CMD6 "CONTSCAN"
51
-#define CMD7 "VERSION"
52
-#define CMD8 "STREAM"
53
-#define CMD9 "STREAM2"
54
-
55
-#ifdef CLAMUKO
56
-pthread_t clamukoid;
57
-#endif
58
-
59
-void *threadscanner(void *arg)
60
-{
61
-	struct thrarg *tharg = (struct thrarg *) arg;
62
-	char buff[32769];
63
-	sigset_t sigset;
64
-	int bread, options, maxwait = CL_DEFAULT_MAXWHILEWAIT;
65
-
66
-
67
-    /* ignore all signals */
68
-    sigfillset(&sigset);
69
-    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
70
-
71
-    if((bread = read(ths[tharg->sid].desc, buff, 1024)) == -1) {
72
-	logg("!Session(%d): read() failed.\n", tharg->sid);
73
-	THREXIT;
74
-    }
75
-
76
-    buff[bread] = 0;
77
-    chomp(buff);
78
-
79
-    if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */
80
-	scan(buff + strlen(CMD1) + 1, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt, ths[tharg->sid].desc, 0);
81
-
82
-    } else if(!strncmp(buff, CMD2, strlen(CMD2))) { /* RAWSCAN */
83
-	options = tharg->options & ~CL_ARCHIVE;
84
-	scan(buff + strlen(CMD2) + 1, NULL, tharg->root, NULL, options, tharg->copt, ths[tharg->sid].desc, 0);
85
-
86
-    } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */
87
-	if(!progexit)
88
-	    kill(progpid, SIGTERM);
89
-
90
-    } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */
91
-	mdprintf(ths[tharg->sid].desc, "RELOADING\n");
92
-	reload = 1;
93
-
94
-    } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */
95
-	mdprintf(ths[tharg->sid].desc, "PONG\n");
96
-
97
-    } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */
98
-	scan(buff + strlen(CMD6) + 1, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt, ths[tharg->sid].desc, 1);
99
-
100
-    } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */
101
-	mdprintf(ths[tharg->sid].desc, "clamd / ClamAV version "VERSION"\n");
102
-
103
-    } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */
104
-	scanstream(ths[tharg->sid].desc, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt);
105
-    }
106
-    /* else if(!strncmp(buff, CMD9, strlen(CMD9))) {
107
-	scanstream2(ths[tharg->sid].desc, NULL, tharg->root, tharg->limits, tharg->options, tharg->copt);
108
-    }*/
109
-
110
-    THREXIT;
111
-}
112
-
113
-/* this function takes care for threads, exit and various checks */
114
-
115
-void *threadwatcher(void *arg)
116
-{
117
-	struct thrwarg *thwarg = (struct thrwarg *) arg;
118
-	struct cfgstruct *cpt;
119
-	sigset_t sigset;
120
-	int i, j, ret, maxwait, virnum;
121
-	unsigned long int timer = 0;
122
-	unsigned int timeout, threads, selfchk;
123
-	short int need_wait = 0, do_loop = 0, db_problem = 0;
124
-	const char *dbdir;
125
-	struct cl_stat dbstat;
126
-
127
-
128
-    /* ignore all signals (except for SIGSEGV) */
129
-    sigfillset(&sigset);
130
-    sigdelset(&sigset, SIGSEGV);
131
-    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
132
-
133
-#ifdef C_LINUX
134
-    logg("*ThreadWatcher: Started in process %d\n", getpid());
135
-#endif
136
-
137
-    if((cpt = cfgopt(thwarg->copt, "MaxThreads")))
138
-	threads = cpt->numarg;
139
-    else
140
-	threads = CL_DEFAULT_MAXTHREADS;
141
-
142
-    if((cpt = cfgopt(thwarg->copt, "SelfCheck")))
143
-	selfchk = cpt->numarg;
144
-    else
145
-	selfchk = CL_DEFAULT_SELFCHECK;
146
-
147
-    if(!selfchk) {
148
-	logg("^Self checking disabled.\n");
149
-    } else
150
-	logg("Self checking every %d seconds.\n", selfchk);
151
-
152
-    if((cpt = cfgopt(thwarg->copt, "ThreadTimeout")))
153
-	timeout = cpt->numarg;
154
-    else
155
-	timeout = CL_DEFAULT_SCANTIMEOUT;
156
-
157
-    if(!timeout) {
158
-	logg("^Timeout disabled.\n");
159
-    } else
160
-	logg("Timeout set to %d seconds.\n", timeout);
161
-
162
-    if((cpt = cfgopt(thwarg->copt, "DataDirectory")))
163
-	dbdir = cpt->strarg;
164
-    else
165
-	dbdir = cl_retdbdir();
166
-
167
-    memset(&dbstat, 0, sizeof(struct cl_stat));
168
-    cl_statinidir(dbdir, &dbstat);
169
-
170
-    for(i = 0; ; i++) {
171
-
172
-        if(i == threads)
173
-	    i = 0;
174
-
175
-	/* check time */
176
-        if(ths[i].active) /* races are harmless here (timeout is re-set) */
177
-	    if(time(NULL) - ths[i].start > timeout) {
178
-		pthread_cancel(ths[i].id);
179
-		mdprintf(ths[i].desc, "Session(%d): Time out ERROR\n", i);
180
-		close(ths[i].desc);
181
-		logg("Session %d stopped due to timeout.\n", i);
182
-		ths[i].active = 0;
183
-//		pthread_mutex_unlock(&ths[i].mutex);
184
-	    }
185
-
186
-	/* cancel all threads in case of quit */
187
-	if(progexit == 1) {
188
-#ifdef CLAMUKO
189
-	    /* stop clamuko */
190
-	    if(clamuko_running) {
191
-		logg("Stopping Clamuko... (id %d)\n", clamukoid);
192
-		pthread_kill(clamukoid, SIGUSR1);
193
-		/* we must wait for Dazuko unregistration */
194
-		maxwait = CL_DEFAULT_MAXWHILEWAIT * 5;
195
-		while(clamuko_running && maxwait--)
196
-		    usleep(200000);
197
-
198
-		if(!maxwait && clamuko_running)
199
-		    logg("!Critical error: Can't stop Clamuko.\n");
200
-	    }
201
-#endif
202
-
203
-	    for(j = 0; j < threads; j++)
204
-		if(ths[j].active) {
205
-		    pthread_cancel(ths[j].id);
206
-		    mdprintf(ths[j].desc, "Session(%d): Stopped (exiting)\n", j);
207
-		    close(ths[j].desc);
208
-		    logg("Session %d stopped (exiting).\n", j);
209
-//		    pthread_mutex_unlock(&ths[j].mutex);
210
-		}
211
-#ifndef C_BSD
212
-	    logg("*Freeing trie structure.\n");
213
-	    cl_freetrie(*thwarg->root);
214
-#endif
215
-	    logg("*Shutting down the main socket.\n");
216
-	    shutdown(thwarg->socketd, 2);
217
-	    logg("*Closing the main socket.\n");
218
-	    close(thwarg->socketd);
219
-	    if((cpt = cfgopt(thwarg->copt, "LocalSocket"))) {
220
-		if(unlink(cpt->strarg) == -1)
221
-		    logg("!Can't unlink the socket file %s\n", cpt->strarg);
222
-		else
223
-		    logg("Socket file removed.\n");
224
-	    }
225
-
226
-	    if((cpt = cfgopt(thwarg->copt, "PidFile"))) {
227
-		if(unlink(cpt->strarg) == -1)
228
-		    logg("!Can't unlink the pid file %s\n", cpt->strarg);
229
-		else
230
-		    logg("Pid file removed.\n");
231
-	    }
232
-
233
-	    progexit = 2;
234
-	    logg("*Exit level %d, ThreadWatcher termination.\n", progexit);
235
-	    return NULL;
236
-	}
237
-
238
-
239
-	/* do self checks */
240
-	if(selfchk && (db_problem || !(timer % selfchk))) {
241
-	    /* check the integrity of the database */
242
-	    if(!reload) {
243
-
244
-		if(cl_statchkdir(&dbstat) == 1) {
245
-		    logg("SelfCheck: Database modification detected. Forcing reload.\n");
246
-		    reload = 1;
247
-		    cl_statfree(&dbstat);
248
-		    cl_statinidir(dbdir, &dbstat);
249
-		} else
250
-		    logg("SelfCheck: Database status OK.\n");
251
-
252
-		if(!testsignature(*thwarg->root)) {
253
-		    if(db_problem) {
254
-			logg("!SelfCheck: Unable to repair internal structure. Exiting.\n");
255
-			kill(progpid, SIGTERM);
256
-			continue;
257
-		    }
258
-		    /* oops */
259
-		    logg("!SelfCheck: Unable to detect test signature, forcing database reload.\n");
260
-		    db_problem = 1;
261
-		    reload = 1;
262
-		} else {
263
-		    logg("*SelfCheck: Integrity OK\n");
264
-		    db_problem = 0;
265
-		}
266
-	    }
267
-	}
268
-
269
-	timer++;
270
-
271
-	/* reload the database */
272
-	if(reload) {
273
-
274
-	    /* make sure the main thread doesn't start new threads */
275
-	    do {
276
-		usleep(200000);
277
-	    } while(!main_accept && !main_reload);
278
-
279
-	    /* wait until all working threads are finished */
280
-	    do {
281
-		need_wait = 0;
282
-		for(j = 0; j < threads; j++)
283
-		    if(ths[j].active) {
284
-			if(time(NULL) - ths[j].start > timeout) {
285
-			    do_loop = 1;
286
-			    break;
287
-			} else need_wait = 1;
288
-		    }
289
-
290
-#ifdef CLAMUKO
291
-		if(clamuko_scanning && !clamuko_reload)
292
-		    need_wait = 1;
293
-#endif
294
-
295
-		if(need_wait)
296
-		    usleep(200000);
297
-
298
-		if(progexit == 1)
299
-		    break;
300
-
301
-	    } while(need_wait);
302
-
303
-	    if(progexit == 1) {
304
-		reload = 0;
305
-		continue;
306
-	    }
307
-
308
-	    if(do_loop) {
309
-		/* some threads must be stopped in the next iteration,
310
-		 * reload is still == 1
311
-		 */
312
-		logg("Database reload: some threads must be stopped in the next iteration.\n");
313
-		do_loop = 0;
314
-		continue;
315
-	    }
316
-
317
-	    /* relase old structure */
318
-	    cl_freetrie(*thwarg->root);
319
-	    *thwarg->root = NULL;
320
-
321
-	    /* reload */
322
-
323
-	    logg("Reading databases from %s\n", dbdir);
324
-
325
-	    cl_statfree(&dbstat);
326
-	    cl_statinidir(dbdir, &dbstat);
327
-	    virnum = 0;
328
-	    if((ret = cl_loaddbdir(dbdir, &*thwarg->root, &virnum))) {
329
-		logg("!%s\n", cl_strerror(ret));
330
-		kill(progpid, SIGTERM);
331
-		/* we stay in reload == 1, so all threads are waiting */
332
-		continue;
333
-	    }
334
-
335
-	    if(! *thwarg->root) {
336
-		logg("!Database initialization problem.\n");
337
-		kill(progpid, SIGTERM);
338
-	    } else {
339
-		cl_buildtrie(*thwarg->root);
340
-		/* check integrity */
341
-		if(!testsignature(*thwarg->root))
342
-		    logg("!Unable to detect test signature.\n");
343
-
344
-		logg("Database correctly reloaded (%d viruses)\n", virnum);
345
-	    }
346
-
347
-	    reload = 0;
348
-	}
349
-
350
-	sleep(1);
351
-    }
352
-
353
-    return NULL;
354
-}
355
-
356
-int threads;
357
-pthread_t watcherid;
358
-
359
-int acceptloop(int socketd, struct cl_node *root, const struct cfgstruct *copt)
360
-{
361
-	int acceptd, i, options = 0, maxwait;
362
-	struct cfgstruct *cpt;
363
-	struct thrarg *tharg;
364
-	struct thrwarg thwarg;
365
-	struct cl_limits limits;
366
-	pthread_attr_t thattr;
367
-	struct sigaction sigact;
368
-	sigset_t sigset;
369
-	mode_t old_umask;
370
-
371
-#if defined(C_BIGSTACK) || defined(C_BSD)
372
-	size_t stacksize;
373
-#endif
374
-
375
-
376
-    /* save the PID */
377
-    if((cpt = cfgopt(copt, "PidFile"))) {
378
-	    FILE *fd;
379
-	    old_umask = umask(0006);
380
-	if((fd = fopen(cpt->strarg, "w")) == NULL) {
381
-	    logg("!Can't save PID in file %s\n", cpt->strarg);
382
-	} else {
383
-	    fprintf(fd, "%d", getpid());
384
-	    fclose(fd);
385
-	}
386
-	umask(old_umask);
387
-    }
388
-
389
-    logg("*Listening daemon: PID: %d\n", getpid());
390
-
391
-    if((cpt = cfgopt(copt, "MaxThreads")))
392
-	threads = cpt->numarg;
393
-    else
394
-	threads = CL_DEFAULT_MAXTHREADS;
395
-
396
-    logg("Maximal number of threads: %d\n", threads);
397
-
398
-    ths = (struct thrsession *) mcalloc(threads, sizeof(struct thrsession));
399
- 
400
-    for(i = 0; i < threads; i++)
401
-	pthread_mutex_init(&ths[i].mutex, NULL);
402
-
403
-
404
-    if(cfgopt(copt, "ScanArchive") || cfgopt(copt, "ClamukoScanArchive")) {
405
-
406
-	/* set up limits */
407
-	memset(&limits, 0, sizeof(struct cl_limits));
408
-
409
-	if((cpt = cfgopt(copt, "ArchiveMaxFileSize"))) {
410
-	    if((limits.maxfilesize = cpt->numarg))
411
-		logg("Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
412
-	    else
413
-		logg("^Archive: File size limit protection disabled.\n");
414
-	} else {
415
-	    limits.maxfilesize = 10485760;
416
-	    logg("^USING HARDCODED LIMIT: Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
417
-	}
418
-
419
-	if((cpt = cfgopt(copt, "ArchiveMaxRecursion"))) {
420
-	    if((limits.maxreclevel = cpt->numarg))
421
-		logg("Archive: Recursion level limit set to %d.\n", limits.maxreclevel);
422
-	    else
423
-		logg("^Archive: Recursion level limit protection disabled.\n");
424
-	} else {
425
-	    limits.maxreclevel = 5;
426
-	    logg("^USING HARDCODED LIMIT: Archive: Recursion level set to %d.\n", limits.maxreclevel);
427
-	}
428
-
429
-	if((cpt = cfgopt(copt, "ArchiveMaxFiles"))) {
430
-	    if((limits.maxfiles = cpt->numarg))
431
-		logg("Archive: Files limit set to %d.\n", limits.maxfiles);
432
-	    else
433
-		logg("^Archive: Files limit protection disabled.\n");
434
-	} else {
435
-	    limits.maxfiles = 1000;
436
-	    logg("^USING HARDCODED LIMIT: Archive: Files limit set to %d.\n", limits.maxfiles);
437
-	}
438
-
439
-	if(cfgopt(copt, "ArchiveLimitMemoryUsage")) {
440
-	    limits.archivememlim = 1;
441
-	    logg("Archive: Limited memory usage.\n", limits.maxfiles);
442
-	} else
443
-	    limits.archivememlim = 0;
444
-    }
445
-
446
-    if(cfgopt(copt, "ScanArchive")) { 
447
-	logg("Archive support enabled.\n");
448
-	options |= CL_ARCHIVE;
449
-
450
-	if(cfgopt(copt, "ScanRAR")) {
451
-	    logg("RAR support enabled.\n");
452
-	} else {
453
-	    logg("RAR support disabled.\n");
454
-	    options |= CL_DISABLERAR;
455
-	}
456
-    } else {
457
-	logg("Archive support disabled.\n");
458
-    }
459
-
460
-    if(cfgopt(copt, "ScanMail")) { 
461
-	logg("Mail files support enabled.\n");
462
-	options |= CL_MAIL;
463
-    } else {
464
-	logg("Mail files support disabled.\n");
465
-    }
466
-
467
-    /* initialize important global variables */
468
-    progexit = 0;
469
-    progpid = 0;
470
-    reload = 0;
471
-#ifdef CLAMUKO
472
-    clamuko_scanning = 0;
473
-    clamuko_running = 0;
474
-#endif
475
-
476
-    pthread_attr_init(&thattr);
477
-    pthread_attr_setdetachstate(&thattr, PTHREAD_CREATE_DETACHED);
478
-
479
-    /* run clamuko */
480
-    if(cfgopt(copt, "ClamukoScanOnLine"))
481
-#ifdef CLAMUKO
482
-    {
483
-	tharg = (struct thrarg *) mcalloc(1, sizeof(struct thrarg));
484
-	tharg->copt = copt;
485
-	tharg->root = root;
486
-	tharg->limits = &limits;
487
-	tharg->options = options;
488
-	clamuko_reload = 0;
489
-
490
-	pthread_create(&clamukoid, &thattr, clamukoth, tharg);
491
-    }
492
-#else
493
-	logg("!Clamuko is not available.\n");
494
-#endif
495
-
496
-    /* start thread watcher */
497
-    thwarg.socketd = socketd;
498
-    thwarg.copt = copt;
499
-    thwarg.root = &root;
500
-    pthread_create(&watcherid, &thattr, threadwatcher, &thwarg);
501
-
502
-    /* set up signal handling */
503
-
504
-    sigfillset(&sigset);
505
-    sigdelset(&sigset, SIGINT);
506
-    sigdelset(&sigset, SIGTERM);
507
-    sigdelset(&sigset, SIGSEGV);
508
-    sigdelset(&sigset, SIGHUP);
509
-    sigprocmask(SIG_SETMASK, &sigset, NULL);
510
-
511
-    /* SIGINT, SIGTERM, SIGSEGV */
512
-    sigact.sa_handler = sighandler;
513
-    sigemptyset(&sigact.sa_mask);
514
-    sigaddset(&sigact.sa_mask, SIGINT);
515
-    sigaddset(&sigact.sa_mask, SIGTERM);
516
-    sigaddset(&sigact.sa_mask, SIGHUP);
517
-    sigaction(SIGINT, &sigact, NULL);
518
-    sigaction(SIGTERM, &sigact, NULL);
519
-#ifndef CL_DEBUG
520
-    sigaction(SIGSEGV, &sigact, NULL);
521
-#endif
522
-    sigaction(SIGHUP, &sigact, NULL);
523
-
524
-    /* we need to save program's PID, because under Linux each thread
525
-     * has another PID, it works with other OSes as well
526
-     */
527
-    progpid = getpid();
528
-
529
-#if defined(C_BIGSTACK) || defined(C_BSD)
530
-    /*
531
-     * njh@bandsman.co.uk:
532
-     * libclamav/scanners.c uses a *huge* buffer
533
-     * (128K not BUFSIZ from stdio.h).
534
-     * We need to allow for that.
535
-     */
536
-    pthread_attr_getstacksize(&thattr, &stacksize);
537
-    cli_dbgmsg("set stacksize to %u\n", stacksize + SCANBUFF + 64 * 1024);
538
-    pthread_attr_setstacksize(&thattr, stacksize + SCANBUFF + 64 * 1024);
539
-#endif
540
-
541
-    while(1) {
542
-
543
-	/* find a free session */
544
-	for(i = 0; ; i++) {
545
-	    if(i == threads)
546
-		i = 0;
547
-
548
-	    if(!ths[i].active) {
549
-		/* logg("*Found free slot: %d\n", i); */
550
-		break;
551
-	    }
552
-	}
553
-
554
-
555
-	main_accept = 1;
556
-	if((acceptd = accept(socketd, NULL, NULL)) == -1) {
557
-	    logg("!accept() failed.\n");
558
-	    /* exit ? */
559
-	    continue;
560
-	}
561
-	main_accept = 0;
562
-
563
-	if(reload) { /* do not start new threads */
564
-	    main_reload = 1;
565
-	    logg("*Main thread: database reloading (waiting).\n");
566
-	    maxwait = CL_DEFAULT_MAXWHILEWAIT;
567
-	    while(reload && maxwait--)
568
-		sleep(1);
569
-
570
-	    if(!maxwait && reload) {
571
-		logg("!Database reloading failed (time exceeded). Exit forced.\n");
572
-		progexit = 1;
573
-		sleep(10);
574
-		exit(1);
575
-	    }
576
-
577
-	    logg("*Main thread: database reloaded.\n");
578
-	    main_reload = 0;
579
-	}
580
-
581
-	tharg = (struct thrarg *) mcalloc(1, sizeof(struct thrarg));
582
-	tharg->copt = copt;
583
-	tharg->sid = i;
584
-	tharg->root = root;
585
-	tharg->limits = &limits;
586
-	tharg->options = options;
587
-
588
-	ths[i].desc = acceptd;
589
-	ths[i].start = time(NULL);
590
-	ths[i].active = 1; /* the structure must be activated exactly here
591
-			    * because we will surely create a race condition 
592
-			    * in other places (if activated in the new thread
593
-			    * there * will be a race in the main thread (it
594
-			    * may assign the same thread session once more);
595
-			    * if activated after pthread_create() the new
596
-			    * thread may be already finished).
597
-			    */
598
-
599
-	if(pthread_create(&ths[i].id, &thattr, threadscanner, tharg)) {
600
-	    logg("!Session(%d) did not start. Dropping connection.", i);
601
-	    close(acceptd);
602
-	    ths[i].active = 0;
603
-	}
604
-    }
605
-}
606
-
607
-void sighandler(int sig)
608
-{
609
-	time_t currtime;
610
-	int maxwait = CL_DEFAULT_MAXWHILEWAIT * 5, i;
611
-
612
-    switch(sig) {
613
-	case SIGINT:
614
-	case SIGTERM:
615
-	    progexit = 1;
616
-	    logg("*Signal %d caught -> exiting.\n", sig);
617
-
618
-	    while(progexit != 2 && maxwait--)
619
-		usleep(200000);
620
-
621
-	    if(!maxwait && progexit != 2)
622
-		logg("!Critical error: Cannot reach exit level 2.\n");
623
-
624
-	    time(&currtime);
625
-	    logg("--- Stopped at %s", ctime(&currtime));
626
-	    exit(0);
627
-	    break; /* not reached */
628
-
629
-#ifndef CL_DEBUG
630
-	case SIGSEGV:
631
-	    logg("Segmentation fault :-( Bye..\n");
632
-
633
-	    for(i = 0; i < threads; i++)
634
-		if(ths[i].active)
635
-		    pthread_kill(ths[i].id, 9);
636
-
637
-	    pthread_kill(watcherid, 9);
638
-	    exit(11); /* probably not reached at all */
639
-	    break; /* not reached */
640
-#endif
641
-	case SIGHUP:
642
-	    sighup = 1;
643
-	    logg("SIGHUP catched: log file re-opened.\n");
644
-	    break;
645
-    }
646
-}
... ...
@@ -50,8 +50,10 @@ short int progexit; /* exit steering variable */
50 50
 int progpid; /* clamd pid */
51 51
 short int reload, clamuko_reload, main_accept, main_reload;
52 52
 
53
-int acceptloop(int socketd, struct cl_node *root, const struct cfgstruct *copt);
53
+int acceptloop_proc(int socketd, struct cl_node *root, const struct cfgstruct *copt);
54
+int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *copt);
54 55
 void sighandler(int sig);
56
+void sighandler_th(int sig);
55 57
 void daemonize(void);
56 58
 void sigsegv(int sig);
57 59
 
... ...
@@ -90,6 +90,10 @@ int tcpserver(const struct optstruct *opt, const struct cfgstruct *copt, struct
90 90
 	exit(1);
91 91
     }
92 92
 
93
-    acceptloop(sockfd, root, copt);
93
+    if(cfgopt(copt, "UseProcesses"))
94
+	acceptloop_proc(sockfd, root, copt);
95
+    else
96
+	acceptloop_th(sockfd, root, copt);
97
+
94 98
     return 0;
95 99
 }
... ...
@@ -75,6 +75,9 @@ LocalSocket /tmp/clamd
75 75
 # Close the connection if this limit is exceeded.
76 76
 #StreamMaxLength 10M
77 77
 
78
+# Use processes instead of threads (thread directives apply to processes too)
79
+#UseProcesses
80
+
78 81
 # Maximal number of a threads running at the same time.
79 82
 # Default is 5, and it should be sufficient for a typical workstation.
80 83
 # You may need to increase threads number for a server machine.