Browse code

limit the internal queue of requests to MaxConnectionQueueLength

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

Tomasz Kojm authored on 2005/06/22 23:54:28
Showing 11 changed files
... ...
@@ -1,3 +1,10 @@
1
+Wed Jun 22 16:43:43 CEST 2005 (tk)
2
+----------------------------------
3
+  * clamd: limit the internal queue of requests to MaxConnectionQueueLength
4
+	   and add new option MaxConnectionQueueTime (maximum number of seconds
5
+	   a connection can be queued and unprocessed without being aborted).
6
+	   Patch by Mark Pizzolato <clamav-devel*subscriptions.pizzolato.net>
7
+
1 8
 Sun Jun 19 13:11:20 BST 2005 (njh)
2 9
 ----------------------------------
3 10
   * libclamav/mbox.c:	Changed debug message when no text part is found in
... ...
@@ -126,7 +133,7 @@ Sat May 21 23:06:03 BST 2005 (njh)
126 126
 Sat May 21 02:17:08 CEST 2005 (tk)
127 127
 ----------------------------------
128 128
   * Remove the use of tmpfile(). Patch by Mark Pizzolato
129
-    <clamav-devel@subscriptions.pizzolato.net>, full list of changes:
129
+    <clamav-devel*subscriptions.pizzolato.net>, full list of changes:
130 130
 
131 131
   * libclamav/others.c
132 132
     - in cli_gentempname call cli_rndnum with 256 instead of 255 to get the
... ...
@@ -445,8 +445,12 @@ int scanstream(int odesc, unsigned long int *scanned, const struct cl_node *root
445 445
 	    break;
446 446
     }
447 447
 
448
-    lseek(tmpd, 0, SEEK_SET);
449
-    ret = cl_scandesc(tmpd, &virname, scanned, root, limits, options);
448
+    if(retval == 1) {
449
+	lseek(tmpd, 0, SEEK_SET);
450
+	ret = cl_scandesc(tmpd, &virname, scanned, root, limits, options);
451
+    } else {
452
+    	ret = -1;
453
+    }
450 454
     close(tmpd);
451 455
     if(!cfgopt(copt, "LeaveTemporaryFiles")->enabled)
452 456
 	unlink(tmpname);
... ...
@@ -460,8 +464,10 @@ int scanstream(int odesc, unsigned long int *scanned, const struct cl_node *root
460 460
 	logg("stream %d: %s FOUND\n", port, virname);
461 461
 	virusaction("stream", virname, copt);
462 462
     } else if(ret != CL_CLEAN) {
463
-	mdprintf(odesc, "stream: %s ERROR\n", cl_strerror(ret));
464
-	logg("stream %d: %s ERROR\n", port, cl_strerror(ret));
463
+    	if(retval == 1) {
464
+	    mdprintf(odesc, "stream: %s ERROR\n", cl_strerror(ret));
465
+	    logg("stream %d: %s ERROR\n", port, cl_strerror(ret));
466
+	}
465 467
     } else {
466 468
 	mdprintf(odesc, "stream: OK\n");
467 469
         if(logok)
... ...
@@ -45,12 +45,14 @@
45 45
 #define FALSE (0)
46 46
 #define TRUE (1)
47 47
 
48
-int progexit = 0;
49
-pthread_mutex_t exit_mutex;
50
-int reload = 0;
51
-time_t reloaded_time = 0;
52
-pthread_mutex_t reload_mutex;
53
-int sighup = 0;
48
+volatile int progexit = 0;
49
+static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
50
+volatile int reload = 0;
51
+volatile time_t reloaded_time = 0;
52
+static pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER;
53
+volatile int sighup = 0;
54
+static pthread_mutex_t session_mutex = PTHREAD_MUTEX_INITIALIZER;
55
+volatile int session_count = 0;
54 56
 
55 57
 typedef struct client_conn_tag {
56 58
     int sd;
... ...
@@ -58,6 +60,7 @@ typedef struct client_conn_tag {
58 58
     const struct cfgstruct *copt;
59 59
     struct cl_node *root;
60 60
     time_t root_timestamp;
61
+    time_t queue_time;
61 62
     const struct cl_limits *limits;
62 63
     pid_t mainpid;
63 64
 } client_conn_t;
... ...
@@ -74,6 +77,24 @@ void scanner_thread(void *arg)
74 74
     sigfillset(&sigset);
75 75
     pthread_sigmask(SIG_SETMASK, &sigset, NULL);
76 76
 
77
+    timeout = cfgopt(conn->copt, "MaxConnectionQueueTime")->numarg;
78
+    if(timeout && (conn->queue_time+timeout < time(NULL))) {
79
+	logg("^Aborting Stale connection request\n");
80
+	close(conn->sd);
81
+	cl_free(conn->root);
82
+	free(conn);
83
+	return;
84
+    }
85
+    pthread_mutex_lock(&exit_mutex);
86
+    if(progexit) {
87
+	pthread_mutex_unlock(&exit_mutex);
88
+	logg("^Dropping connection request to exit\n");
89
+	close(conn->sd);
90
+	cl_free(conn->root);
91
+	free(conn);
92
+	return;
93
+    }
94
+    pthread_mutex_unlock(&exit_mutex);
77 95
     timeout = cfgopt(conn->copt, "ReadTimeout")->numarg;
78 96
     if(!timeout)
79 97
     	timeout = -1;
... ...
@@ -99,28 +120,52 @@ void scanner_thread(void *arg)
99 99
 		break;
100 100
 
101 101
 	    case COMMAND_SESSION:
102
-		session = TRUE;
102
+		if(!session) {
103
+		    pthread_mutex_lock(&session_mutex);
104
+		    session_count++;
105
+		    if(cfgopt(conn->copt, "MaxThreads")->numarg <= session_count) {
106
+			logg("^Active sessions(%d) consuming all available threads(%d)\n", session_count, cfgopt(conn->copt, "MaxThreads")->numarg);
107
+		    }
108
+		    pthread_mutex_unlock(&session_mutex);
109
+		    session = TRUE;
110
+		}
103 111
 		timeout = 5;
104 112
 		break;
105 113
 
106 114
 	    case COMMAND_END:
107
-		session = FALSE;
115
+		if(session) {
116
+		    pthread_mutex_lock(&session_mutex);
117
+		    session_count--;
118
+		    pthread_mutex_unlock(&session_mutex);
119
+		    session = FALSE;
120
+		}
108 121
 		break;
109 122
 	}
110 123
 	if (session) {
111 124
 	    pthread_mutex_lock(&exit_mutex);
112 125
 	    if(progexit) {
113 126
 		session = FALSE;
127
+		pthread_mutex_lock(&session_mutex);
128
+		--session_count;
129
+		pthread_mutex_unlock(&session_mutex);
114 130
 	    }
115 131
 	    pthread_mutex_unlock(&exit_mutex);
116 132
 	    pthread_mutex_lock(&reload_mutex);
117 133
 	    if (conn->root_timestamp != reloaded_time) {
118 134
 		session = FALSE;
135
+		pthread_mutex_lock(&session_mutex);
136
+		--session_count;
137
+		pthread_mutex_unlock(&session_mutex);
119 138
 	    }
120 139
 	    pthread_mutex_unlock(&reload_mutex);
121 140
 	}
122 141
     } while (session);
123 142
 
143
+    if(session) {
144
+	pthread_mutex_lock(&session_mutex);
145
+	--session_count;
146
+	pthread_mutex_unlock(&session_mutex);
147
+    }
124 148
     close(conn->sd);
125 149
     cl_free(conn->root);
126 150
     free(conn);
... ...
@@ -215,7 +260,7 @@ static struct cl_node *reload_db(struct cl_node *root, const struct cfgstruct *c
215 215
 
216 216
 int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *copt)
217 217
 {
218
-	int new_sd, max_threads;
218
+	int new_sd, max_threads, max_queue_size;
219 219
 	unsigned int options = 0;
220 220
 	threadpool_t *thr_pool;
221 221
 	struct sigaction sigact;
... ...
@@ -261,6 +306,8 @@ int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *cop
261 261
     logg("*Listening daemon: PID: %d\n", getpid());
262 262
     max_threads = cfgopt(copt, "MaxThreads")->numarg;
263 263
 
264
+    max_queue_size = cfgopt(copt, "MaxConnectionQueueLength")->numarg;
265
+
264 266
     if(cfgopt(copt, "ScanArchive")->enabled || cfgopt(copt, "ClamukoScanArchive")->enabled) {
265 267
 
266 268
 	/* set up limits */
... ...
@@ -430,21 +477,21 @@ int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *cop
430 430
 
431 431
     idletimeout = cfgopt(copt, "IdleTimeout")->numarg;
432 432
 
433
-    if((thr_pool=thrmgr_new(max_threads, idletimeout, scanner_thread)) == NULL) {
433
+    if((thr_pool=thrmgr_new(max_threads, max_queue_size, idletimeout, scanner_thread)) == NULL) {
434 434
 	logg("!thrmgr_new failed\n");
435 435
 	exit(-1);
436 436
     }
437 437
 
438 438
     time(&start_time);
439 439
 
440
-    for(;;) {				
440
+    for(;;) {
441 441
 	new_sd = accept(socketd, NULL, NULL);
442 442
 	if((new_sd == -1) && (errno != EINTR)) {
443 443
 	    /* very bad - need to exit or restart */
444 444
 #ifdef HAVE_STRERROR_R
445 445
 	    logg("!accept() failed: %s\n", strerror_r(errno, buff, BUFFSIZE));
446 446
 #else
447
-	    logg("!accept() failed\n");
447
+	    logg("!accept() failed: %d\n", errno);
448 448
 #endif
449 449
 	    continue;
450 450
 	}
... ...
@@ -464,6 +511,7 @@ int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *cop
464 464
 		client_conn->copt = copt;
465 465
 		client_conn->root = cl_dup(root);
466 466
 		client_conn->root_timestamp = reloaded_time;
467
+		time(&client_conn->queue_time);
467 468
 		client_conn->limits = &limits;
468 469
 		client_conn->mainpid = mainpid;
469 470
 		if (!thrmgr_dispatch(thr_pool, client_conn)) {
... ...
@@ -501,7 +549,7 @@ int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *cop
501 501
 	    root = reload_db(root, copt, FALSE);
502 502
 	    pthread_mutex_lock(&reload_mutex);
503 503
 	    reload = 0;
504
-	    time(&reloaded_time);
504
+	    time((time_t *)&reloaded_time);
505 505
 	    pthread_mutex_unlock(&reload_mutex);
506 506
 #ifdef CLAMUKO
507 507
 	    if(cfgopt(copt, "ClamukoScanOnAccess")->enabled) {
... ...
@@ -43,7 +43,7 @@
43 43
 #include "output.h"
44 44
 #include "memory.h"
45 45
 
46
-pthread_mutex_t ctime_mutex = PTHREAD_MUTEX_INITIALIZER;
46
+static pthread_mutex_t ctime_mutex = PTHREAD_MUTEX_INITIALIZER;
47 47
 
48 48
 int command(int desc, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt, int timeout)
49 49
 {
... ...
@@ -40,14 +40,17 @@ work_queue_t *work_queue_new()
40 40
 	return work_q;
41 41
 }
42 42
 
43
-void work_queue_add(work_queue_t *work_q, void *data)
43
+int work_queue_add(work_queue_t *work_q, void *data)
44 44
 {
45 45
 	work_item_t *work_item;
46 46
 	
47 47
 	if (!work_q) {
48
-		return;
48
+		return FALSE;
49 49
 	}
50 50
 	work_item = (work_item_t *) mmalloc(sizeof(work_item_t));
51
+	if (!work_item) {
52
+		return FALSE;
53
+	}
51 54
 	work_item->next = NULL;
52 55
 	work_item->data = data;
53 56
 	gettimeofday(&(work_item->time_queued), NULL);
... ...
@@ -60,7 +63,7 @@ void work_queue_add(work_queue_t *work_q, void *data)
60 60
 		work_q->tail = work_item;
61 61
 		work_q->item_count++;
62 62
 	}
63
-	return;
63
+	return TRUE;
64 64
 }
65 65
 
66 66
 void *work_queue_pop(work_queue_t *work_q)
... ...
@@ -112,12 +115,13 @@ void thrmgr_destroy(threadpool_t *threadpool)
112 112
 	
113 113
 	pthread_mutex_destroy(&(threadpool->pool_mutex));
114 114
 	pthread_cond_destroy(&(threadpool->pool_cond));
115
+	pthread_cond_destroy(&(threadpool->pool_notfull_cond));
115 116
 	pthread_attr_destroy(&(threadpool->pool_attr));
116 117
 	free(threadpool);
117 118
 	return;
118 119
 }
119 120
 
120
-threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void *))
121
+threadpool_t *thrmgr_new(int max_threads, int max_dispatch_queue, int idle_timeout, void (*handler)(void *))
121 122
 {
122 123
 	threadpool_t *threadpool;
123 124
 	
... ...
@@ -133,6 +137,8 @@ threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void
133 133
 		return NULL;
134 134
 	}	
135 135
 	threadpool->thr_max = max_threads;
136
+	threadpool->thr_max_queue = max_dispatch_queue;
137
+	threadpool->thr_queued = 0;
136 138
 	threadpool->thr_alive = 0;
137 139
 	threadpool->thr_idle = 0;
138 140
 	threadpool->idle_timeout = idle_timeout;
... ...
@@ -143,8 +149,15 @@ threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void
143 143
 		free(threadpool);
144 144
 		return NULL;
145 145
 	}
146
+	if (pthread_cond_init(&(threadpool->pool_notfull_cond), NULL) != 0) {
147
+		pthread_cond_destroy(&(threadpool->pool_cond));
148
+		free(threadpool);
149
+		return NULL;
150
+	}
146 151
 		
147 152
 	if (pthread_attr_init(&(threadpool->pool_attr)) != 0) {
153
+		pthread_cond_destroy(&(threadpool->pool_cond));
154
+		pthread_cond_destroy(&(threadpool->pool_notfull_cond));
148 155
 		free(threadpool);
149 156
 		return NULL;
150 157
 	}
... ...
@@ -162,7 +175,7 @@ void *thrmgr_worker(void *arg)
162 162
 {
163 163
 	threadpool_t *threadpool = (threadpool_t *) arg;
164 164
 	void *job_data;
165
-	int retval, must_exit = FALSE;
165
+	int retval;
166 166
 	struct timespec timeout;
167 167
 	
168 168
 	/* loop looking for work */
... ...
@@ -181,15 +194,20 @@ void *thrmgr_worker(void *arg)
181 181
 			retval = pthread_cond_timedwait(&(threadpool->pool_cond),
182 182
 				&(threadpool->pool_mutex), &timeout);
183 183
 			if (retval == ETIMEDOUT) {
184
-				must_exit = TRUE;
185 184
 				break;
186 185
 			}
187 186
 		}
188 187
 		threadpool->thr_idle--;
189
-		if (threadpool->state == POOL_EXIT) {
190
-			must_exit = TRUE;
188
+
189
+		if (job_data) {
190
+			if ((threadpool->thr_max_queue > 0) &&
191
+			    (threadpool->thr_queued == threadpool->thr_max_queue)) {
192
+				logg("*Thread Queue: Resuming...\n");
193
+				/* signal that queue no longer full */
194
+				pthread_cond_signal(&threadpool->pool_notfull_cond);
195
+			}
196
+			threadpool->thr_queued--;
191 197
 		}
192
-		
193 198
 		if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) {
194 199
 			/* Fatal error */
195 200
 			logg("!Fatal: mutex unlock failed\n");
... ...
@@ -197,7 +215,7 @@ void *thrmgr_worker(void *arg)
197 197
 		}
198 198
 		if (job_data) {
199 199
 			threadpool->handler(job_data);
200
-		} else if (must_exit) {
200
+		} else {
201 201
 			break;
202 202
 		}
203 203
 	}
... ...
@@ -229,33 +247,81 @@ int thrmgr_dispatch(threadpool_t *threadpool, void *user_data)
229 229
 
230 230
 	/* Lock the threadpool */
231 231
 	if (pthread_mutex_lock(&(threadpool->pool_mutex)) != 0) {
232
-		logg("!Mutex lock failed\n");
232
+		logg("!Mutex lock failed: %d\n", errno);
233 233
 		return FALSE;
234 234
 	}
235 235
 
236 236
 	if (threadpool->state != POOL_VALID) {
237 237
 		if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) {
238
-			logg("!Mutex unlock failed\n");
238
+			logg("!Mutex unlock failed (!POOL_VALID): %d\n", errno);
239 239
 			return FALSE;
240 240
 		}
241 241
 		return FALSE;
242 242
 	}
243
-	work_queue_add(threadpool->queue, user_data);
243
+	if (work_queue_add(threadpool->queue, user_data) == FALSE) {
244
+		if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) {
245
+			logg("!Mutex unlock failed (work_queue_add): %d\n", errno);
246
+			return FALSE;
247
+		}
248
+		return FALSE;
249
+	}
250
+	threadpool->thr_queued++;
251
+	while ((threadpool->thr_max_queue > 0) && 
252
+		(threadpool->thr_queued >= threadpool->thr_max_queue) &&
253
+		(threadpool->state == POOL_VALID)) {
254
+		logg("*Thread Queue: Full, waiting...\n");
255
+		if (pthread_cond_wait(&threadpool->pool_notfull_cond, &threadpool->pool_mutex) != 0) {
256
+			logg("!pthread_cond_wait failed: %d\n", errno);
257
+			pthread_mutex_unlock(&threadpool->pool_mutex);
258
+			return FALSE;
259
+		}
260
+	}
244 261
 
245 262
 	if ((threadpool->thr_idle == 0) &&
246
-			(threadpool->thr_alive < threadpool->thr_max)) {
263
+	    (threadpool->thr_alive < threadpool->thr_max) &&
264
+	    (threadpool->state == POOL_VALID)) {
247 265
 		/* Start a new thread */
248 266
 		if (pthread_create(&thr_id, &(threadpool->pool_attr),
249 267
 				thrmgr_worker, threadpool) != 0) {
250
-			logg("!pthread_create failed\n");
268
+			logg("!pthread_create failed: %d\n", errno);
251 269
 		} else {
252 270
 			threadpool->thr_alive++;
253 271
 		}
254 272
 	}
255
-	pthread_cond_signal(&(threadpool->pool_cond));
273
+	switch(threadpool->thr_idle) {
274
+		case 0:
275
+			break;
276
+		case 1:
277
+			if (pthread_cond_signal(&(threadpool->pool_cond)) != 0) {
278
+				logg("!pthread_cond_signal failed: %d\n", errno);
279
+				if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) {
280
+					logg("!Mutex unlock failed (after pthread_cond_signal failure): %d\n", errno);
281
+				}
282
+				return FALSE;
283
+			}
284
+			break;
285
+		default:
286
+			if(threadpool->thr_queued > 1) {
287
+				if (pthread_cond_broadcast(&(threadpool->pool_cond)) != 0) {
288
+					logg("!pthread_cond_broadcast failed: %d\n", errno);
289
+					if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) {
290
+						logg("!Mutex unlock failed (after pthread_cond_broadcast failure): %d\n", errno);
291
+					}
292
+					return FALSE;
293
+				}
294
+			} else {
295
+				if (pthread_cond_signal(&(threadpool->pool_cond)) != 0) {
296
+					logg("!pthread_cond_signal failed: %d\n", errno);
297
+					if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) {
298
+						logg("!Mutex unlock failed (after pthread_cond_signal failure): %d\n", errno);
299
+					}
300
+					return FALSE;
301
+				}
302
+			}
303
+	}
256 304
 
257 305
 	if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) {
258
-		logg("!Mutex unlock failed\n");
306
+		logg("!Mutex unlock failed (before complete): %d\n", errno);
259 307
 		return FALSE;
260 308
 	}
261 309
 	return TRUE;
... ...
@@ -43,12 +43,15 @@ typedef enum {
43 43
 typedef struct threadpool_tag {
44 44
 	pthread_mutex_t pool_mutex;
45 45
 	pthread_cond_t pool_cond;
46
+	pthread_cond_t pool_notfull_cond;
46 47
 	pthread_attr_t pool_attr;
47 48
 	
48
-	pool_state_t state;
49
-	int thr_max;
50
-	int thr_alive;
51
-	int thr_idle;
49
+	volatile pool_state_t state;
50
+	volatile int thr_max;
51
+	volatile int thr_max_queue;
52
+	volatile int thr_queued;
53
+	volatile int thr_alive;
54
+	volatile int thr_idle;
52 55
 	int idle_timeout;
53 56
 	
54 57
 	void (*handler)(void *);
... ...
@@ -56,7 +59,7 @@ typedef struct threadpool_tag {
56 56
 	work_queue_t *queue;
57 57
 } threadpool_t;
58 58
 
59
-threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void *));
59
+threadpool_t *thrmgr_new(int max_threads, int max_dispatch_queue, int idle_timeout, void (*handler)(void *));
60 60
 void thrmgr_destroy(threadpool_t *threadpool);
61 61
 int thrmgr_dispatch(threadpool_t *threadpool, void *user_data);
62 62
 
... ...
@@ -104,6 +104,11 @@ Maximum length the queue of pending connections may grow to.
104 104
 .br 
105 105
 Default: 15
106 106
 .TP 
107
+\fBMaxConnectionQueueTime NUMBER\fR
108
+Maximum number of seconds a connection can be queued and unprocessed without being aborted when it finally comes time to process it. The value of zero disables the limit.
109
+.br 
110
+Default: 20
111
+.TP 
107 112
 \fBMaxThreads NUMBER\fR
108 113
 Maximal number of threads running at the same time.
109 114
 .br 
... ...
@@ -90,6 +90,12 @@ LocalSocket /tmp/clamd
90 90
 # Default: 15
91 91
 #MaxConnectionQueueLength 30
92 92
 
93
+# Maximum number of seconds a connection can be queued and unprocessed without
94
+# being aborted when it finally comes time to process it. The value of zero
95
+# disables the limit.
96
+# Default: 20
97
+#MaxConnectionQueueTime 30
98
+
93 99
 # Clamd uses FTP-like protocol to receive data from remote clients.
94 100
 # If you are using clamav-milter to balance load between remote clamd daemons
95 101
 # on firewall servers you may need to tune the options below.
... ...
@@ -45,7 +45,7 @@ extern short cli_debug_flag;
45 45
 
46 46
 #ifdef CL_THREAD_SAFE
47 47
 #  include <pthread.h>
48
-pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
48
+static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
49 49
 #endif
50 50
 
51 51
 int cli_scanbuff(const char *buffer, unsigned int length, const char **virname, const struct cl_node *root, unsigned short ftype)
... ...
@@ -42,7 +42,7 @@
42 42
 
43 43
 #ifdef CL_THREAD_SAFE
44 44
 #  include <pthread.h>
45
-pthread_mutex_t cli_gentempname_mutex = PTHREAD_MUTEX_INITIALIZER;
45
+static pthread_mutex_t cli_gentempname_mutex = PTHREAD_MUTEX_INITIALIZER;
46 46
 #endif
47 47
 
48 48
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
... ...
@@ -72,6 +72,7 @@ struct cfgstruct *getcfg(const char *cfgfile, int verbose)
72 72
 	    {"TCPSocket", OPT_NUM, -1, NULL, 0},
73 73
 	    {"LocalSocket", OPT_STR, -1, NULL, 0},
74 74
 	    {"MaxConnectionQueueLength", OPT_NUM, 15, NULL, 0},
75
+	    {"MaxConnectionQueueTime", OPT_NUM, 20, NULL, 0},
75 76
 	    {"StreamMaxLength", OPT_COMPSIZE, 10485760, NULL, 0},
76 77
 	    {"StreamMinPort", OPT_NUM, 1024, NULL, 0},
77 78
 	    {"StreamMaxPort", OPT_NUM, 2048, NULL, 0},