View | Details | Raw Unified | Return to bug 135078
Collapse All | Expand All

(-)apache22-peruser-mpm3/Makefile (-5 / +5 lines)
Lines 1-15 Link Here
1
# New ports collection makefile for:	apache22-peruser
1
# New ports collection makefile for:	apache22-peruser-mpm
2
# Apache 2.2.X with peruser mpm 0.3.0-dc2
2
# Apache 2.2.X with peruser mpm 0.3.0-dc3
3
# Date created:                 13 Sep 2008
3
# Date created:                 13 Sep 2008
4
# Whom:                         Jille Timmermans <jille@quis.cx>
4
# Whom:                         Jille Timmermans <jille@quis.cx>
5
# Peruser main url:		http://telana.com/peruser.php
5
# Peruser main url:		http://telana.com/peruser.php
6
# Peruser patches:		http://source.kood.ee/
6
# Peruser patches:		http://source.kood.ee/
7
# This port contains the main patch, including the dc2 patch
7
# This port contains the main patch, including the dc3 patch
8
#
8
#
9
# $FreeBSD: ports/www/apache22-peruser-mpm/Makefile,v 1.4 2009/05/19 02:04:29 pgollucci Exp $
9
# $FreeBSD: ports/www/apache22-peruser-mpm/Makefile,v 1.4 2009/05/19 02:04:29 pgollucci Exp $
10
#
10
#
11
11
12
PORTREVISION=	2
12
PORTREVISION=	3
13
13
14
MAINTAINER=		jille@quis.cx
14
MAINTAINER=		jille@quis.cx
15
15
Lines 17-23 Link Here
17
17
18
SLAVE_PORT_MPM=		peruser
18
SLAVE_PORT_MPM=		peruser
19
EXTRA_PATCHES+=		${.CURDIR}/files/httpd-2.2.3-peruser-0.3.0.patch
19
EXTRA_PATCHES+=		${.CURDIR}/files/httpd-2.2.3-peruser-0.3.0.patch
20
EXTRA_PATCHES+=		${.CURDIR}/files/httpd-2.2.3-peruser-0.3.0-dc2.patch
20
EXTRA_PATCHES+=		${.CURDIR}/files/httpd-2.2.3-peruser-0.3.0-dc3.patch
21
WITH_MPM=		${SLAVE_PORT_MPM}
21
WITH_MPM=		${SLAVE_PORT_MPM}
22
SLAVE_DESIGNED_FOR=	2.2.11	# 2.2.3 to be honest, but works fine on 2.2.11
22
SLAVE_DESIGNED_FOR=	2.2.11	# 2.2.3 to be honest, but works fine on 2.2.11
23
23
(-)apache22-peruser-mpm3/files/httpd-2.2.3-peruser-0.3.0-dc2.patch (-844 lines)
Lines 1-844 Link Here
1
--- server/mpm/experimental/peruser/peruser.c	2008-05-16 16:10:21.000000000 +0300
2
+++ server/mpm/experimental/peruser/peruser.c	2008-05-19 09:14:04.000000000 +0300
3
@@ -206,9 +206,14 @@
4
 static int ap_min_processors=DEFAULT_MIN_PROCESSORS;
5
 static int ap_min_free_processors=DEFAULT_MIN_FREE_PROCESSORS;
6
 static int ap_max_processors=DEFAULT_MAX_PROCESSORS;
7
+static int ap_min_multiplexers=DEFAULT_MIN_MULTIPLEXERS;
8
+static int ap_max_multiplexers=DEFAULT_MAX_MULTIPLEXERS;
9
 static int ap_daemons_limit=0;      /* MaxClients */
10
-static int expire_timeout=1800;
11
-static int idle_timeout=900;
12
+static int expire_timeout=DEFAULT_EXPIRE_TIMEOUT;
13
+static int idle_timeout=DEFAULT_IDLE_TIMEOUT;
14
+static int multiplexer_idle_timeout=DEFAULT_MULTIPLEXER_IDLE_TIMEOUT;
15
+static int processor_wait_timeout=DEFAULT_PROCESSOR_WAIT_TIMEOUT;
16
+static int processor_wait_steps=DEFAULT_PROCESSOR_WAIT_STEPS;
17
 static int server_limit = DEFAULT_SERVER_LIMIT;
18
 static int first_server_limit;
19
 static int changed_limit_at_restart;
20
@@ -221,16 +226,20 @@
21
 typedef struct
22
 {
23
     int processor_id;
24
-
25
+    
26
+    const char *name;	/* Server environment's unique string identifier */
27
+    
28
     /* security settings */
29
     uid_t uid;          /* user id */
30
     gid_t gid;          /* group id */
31
     const char *chroot; /* directory to chroot() to, can be null */
32
+    int nice_lvl;
33
 
34
     /* resource settings */
35
     int min_processors;
36
     int min_free_processors;
37
     int max_processors;
38
+    int availability;
39
 
40
     /* sockets */
41
     int input;          /* The socket descriptor */
42
@@ -437,6 +446,25 @@
43
     return "UNKNOWN";
44
 }
45
 
46
+char* scoreboard_status_string(int status) {
47
+    switch(status)
48
+    {
49
+        case SERVER_DEAD:  return "DEAD";
50
+        case SERVER_STARTING: return "STARTING";
51
+        case SERVER_READY:    return "READY";
52
+        case SERVER_BUSY_READ:   return "BUSY_READ";
53
+        case SERVER_BUSY_WRITE:   return "BUSY_WRITE";
54
+        case SERVER_BUSY_KEEPALIVE:   return "BUSY_KEEPALIVE";
55
+        case SERVER_BUSY_LOG:   return "BUSY_LOG";
56
+        case SERVER_BUSY_DNS:   return "BUSY_DNS";
57
+        case SERVER_CLOSING:   return "CLOSING";
58
+        case SERVER_GRACEFUL:   return "GRACEFUL";
59
+        case SERVER_NUM_STATUS:   return "NUM_STATUS";
60
+    }
61
+
62
+    return "UNKNOWN";    
63
+}
64
+
65
 void dump_child_table()
66
 {
67
 #ifdef MPM_PERUSER_DEBUG
68
@@ -1116,7 +1144,7 @@
69
     apr_bucket *bucket;
70
     const apr_array_header_t *headers_in_array;
71
     const apr_table_entry_t *headers_in;
72
-    int counter;
73
+    int counter, wait_time, wait_step_size;
74
 
75
     apr_socket_t *thesock = ap_get_module_config(r->connection->conn_config, &core_module);
76
 
77
@@ -1137,6 +1165,63 @@
78
       apr_table_get(r->headers_in, "Host"), my_child_num, processor->senv->output);
79
     _DBG("r->the_request=\"%s\" len=%d", r->the_request, strlen(r->the_request));
80
 
81
+    wait_step_size = 100 / processor_wait_steps;
82
+
83
+    /*	Check if the processor is available */
84
+    if (total_processors(processor->id) == processor->senv->max_processors &&
85
+	    idle_processors(processor->id) == 0) {
86
+	/* The processor is currently busy, try to wait (a little) */
87
+	_DBG("processor seems to be busy, trying to wait for it");
88
+
89
+	if (processor->senv->availability == 0) {
90
+	    processor->senv->availability = 0;
91
+		
92
+	    _DBG("processor is very busy (availability = 0) - not passing request");
93
+	    /* No point in waiting for the processor, it's very busy */
94
+	    return -1;
95
+	}
96
+	
97
+	/* We sleep a little (depending how available the processor usually is) */
98
+	int i;
99
+
100
+	wait_time = (processor_wait_timeout / processor_wait_steps) * 1000000;
101
+	
102
+	for(i = 0; i <= processor->senv->availability; i += wait_step_size) {
103
+	    usleep(wait_time);
104
+
105
+	    /* Check if the processor is ready */
106
+    	    if (total_processors(processor->id) < processor->senv->max_processors ||
107
+		idle_processors(processor->id) > 0) {
108
+		/* The processor has freed - lets use it */
109
+		_DBG("processor freed before wait time expired");
110
+		break;
111
+	    }
112
+	}
113
+
114
+	if (processor->senv->availability <= wait_step_size) {
115
+	    processor->senv->availability = 0;
116
+	}
117
+	else processor->senv->availability -= wait_step_size;
118
+	
119
+	/* Check if we waited all the time */
120
+	if (i > processor->senv->availability) {
121
+	    _DBG("processor is busy - not passing request (availability = %d)",
122
+		    processor->senv->availability);
123
+	    return -1;
124
+	}
125
+
126
+	/* We could increase the availability a little here,
127
+	 * because the processor got freed eventually
128
+	 */
129
+    }
130
+    else {
131
+	/* Smoothly increment the availability back to 100 */
132
+	if (processor->senv->availability >= 100-wait_step_size) {
133
+	    processor->senv->availability = 100;
134
+	}
135
+	else processor->senv->availability += wait_step_size;
136
+    }
137
+
138
     ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, len);
139
 
140
     /* Scan the brigade looking for heap-buckets */
141
@@ -1402,6 +1487,10 @@
142
 static int peruser_setup_child(int childnum)
143
 {
144
     server_env_t *senv = CHILD_INFO_TABLE[childnum].senv;
145
+    
146
+    if (senv->nice_lvl != 0) {
147
+	nice(senv->nice_lvl);
148
+    }
149
 
150
     if(senv->chroot) {
151
       _DBG("chdir to %s", senv->chroot);
152
@@ -1599,7 +1688,8 @@
153
             _DBG("updating processor stati", 0);
154
             for(i = 0; i < NUM_CHILDS; ++i)
155
             {
156
-                if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY)
157
+                if(CHILD_INFO_TABLE[i].type != CHILD_TYPE_MULTIPLEXER &&
158
+            		CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY)
159
                     CHILD_INFO_TABLE[i].status = CHILD_STATUS_ACTIVE;
160
             }
161
 
162
@@ -1740,7 +1830,8 @@
163
         }
164
 
165
         if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_PROCESSOR ||
166
-            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER)
167
+            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER ||
168
+            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER)
169
         {
170
           _DBG("CHECKING IF WE SHOULD CLONE A CHILD...");
171
 
172
@@ -1752,10 +1843,14 @@
173
             idle_processors(my_child_num),
174
             CHILD_INFO_TABLE[my_child_num].senv->min_free_processors);
175
 
176
-          if(total_processors(my_child_num) <
177
+          if(
178
+            total_processors(my_child_num) <
179
               CHILD_INFO_TABLE[my_child_num].senv->max_processors &&
180
-            idle_processors(my_child_num) <=
181
-              CHILD_INFO_TABLE[my_child_num].senv->min_free_processors)
182
+            (idle_processors(my_child_num) <=
183
+              CHILD_INFO_TABLE[my_child_num].senv->min_free_processors ||
184
+             total_processors(my_child_num) <
185
+              CHILD_INFO_TABLE[my_child_num].senv->min_processors 
186
+            ))
187
           {
188
               _DBG("CLONING CHILD");
189
               child_clone();
190
@@ -1804,22 +1899,55 @@
191
     clean_child_exit(0);
192
 }
193
 
194
-static server_env_t* senv_add(int uid, int gid, const char* chroot)
195
-{
196
+static server_env_t* find_senv_by_name(const char *name) {
197
     int i;
198
-    int socks[2];
199
+    
200
+    if (name == NULL) return NULL;
201
+    
202
+    _DBG("name=%s", name);
203
 
204
-    _DBG("Searching for matching senv...");
205
+    for(i = 0; i < NUM_SENV; i++)
206
+    {
207
+      if(SENV[i].name != NULL && !strcmp(SENV[i].name, name)) {
208
+          return &SENV[i];
209
+      }
210
+    }
211
+    
212
+    return NULL;
213
+}
214
+
215
+static server_env_t* find_matching_senv(server_env_t* senv) {
216
+    int i;
217
+
218
+    _DBG("name=%s uid=%d gid=%d chroot=%s", senv->name, senv->uid, senv->gid, senv->chroot);
219
 
220
     for(i = 0; i < NUM_SENV; i++)
221
     {
222
-      if(SENV[i].uid == uid && SENV[i].gid == gid &&
223
-         (SENV[i].chroot == NULL || !strcmp(SENV[i].chroot, chroot)))
224
-      {
225
-          _DBG("Found existing senv: %i", i);
226
+      if((senv->name != NULL && SENV[i].name != NULL && !strcmp(SENV[i].name, senv->name)) ||
227
+    		(senv->name == NULL && SENV[i].uid == senv->uid && SENV[i].gid == senv->gid &&
228
+    	    ((SENV[i].chroot == NULL && senv->chroot == NULL) || ((SENV[i].chroot != NULL || senv->chroot != NULL) && !strcmp(SENV[i].chroot, senv->chroot))))
229
+    	) {
230
           return &SENV[i];
231
       }
232
     }
233
+    
234
+    return NULL;
235
+}
236
+
237
+static server_env_t* senv_add(server_env_t *senv)
238
+{
239
+    int socks[2];
240
+    server_env_t *old_senv;
241
+
242
+    _DBG("Searching for matching senv...");
243
+
244
+    old_senv = find_matching_senv(senv);
245
+
246
+    if (old_senv) {
247
+        _DBG("Found existing senv");
248
+	senv = old_senv;
249
+	return old_senv;
250
+    }
251
 
252
     if(NUM_SENV >= server_limit)
253
     {
254
@@ -1828,22 +1956,20 @@
255
     }
256
 
257
     _DBG("Creating new senv");
258
+    
259
+    memcpy(&SENV[NUM_SENV], senv, sizeof(server_env_t));
260
 
261
-    SENV[NUM_SENV].uid = uid;
262
-    SENV[NUM_SENV].gid = gid;
263
-    SENV[NUM_SENV].chroot = chroot;
264
-
265
-    SENV[NUM_SENV].min_processors = ap_min_processors;
266
-    SENV[NUM_SENV].min_free_processors = ap_min_free_processors;
267
-    SENV[NUM_SENV].max_processors = ap_max_processors;
268
+    SENV[NUM_SENV].availability = 100;
269
 
270
     socketpair(PF_UNIX, SOCK_STREAM, 0, socks);
271
     SENV[NUM_SENV].input  = socks[0];
272
     SENV[NUM_SENV].output = socks[1];
273
-
274
+    
275
+    senv = &SENV[NUM_SENV];
276
     return &SENV[server_env_image->control->num++];
277
 }
278
 
279
+
280
 static const char* child_clone()
281
 {
282
     int i;
283
@@ -1869,7 +1995,12 @@
284
     new = &CHILD_INFO_TABLE[i];
285
 
286
     new->senv = this->senv;
287
-    new->type = CHILD_TYPE_WORKER;
288
+    if (this->type == CHILD_TYPE_MULTIPLEXER) {
289
+	new->type = CHILD_TYPE_MULTIPLEXER;    
290
+    }
291
+    else {
292
+	new->type = CHILD_TYPE_WORKER;
293
+    }
294
     new->sock_fd = this->sock_fd;
295
     new->status = CHILD_STATUS_STARTING;
296
 
297
@@ -1878,7 +2009,7 @@
298
 }
299
 
300
 static const char* child_add(int type, int status,
301
-                             apr_pool_t *pool, uid_t uid, gid_t gid, const char* chroot)
302
+                             apr_pool_t *pool, server_env_t *senv)
303
 {
304
     _DBG("adding child #%d", NUM_CHILDS);
305
 
306
@@ -1888,10 +2019,10 @@
307
                "Increase NumServers in your config file.";
308
     }
309
 
310
-       if (chroot && !ap_is_directory(pool, chroot))
311
-               return apr_psprintf(pool, "Error: chroot directory [%s] does not exist", chroot);
312
+       if (senv->chroot && !ap_is_directory(pool, senv->chroot))
313
+               return apr_psprintf(pool, "Error: chroot directory [%s] does not exist", senv->chroot);
314
 
315
-    CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(uid, gid, chroot);
316
+    CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(senv);
317
 
318
     if(CHILD_INFO_TABLE[NUM_CHILDS].senv == NULL)
319
     {
320
@@ -1907,10 +2038,10 @@
321
     CHILD_INFO_TABLE[NUM_CHILDS].status = status;
322
 
323
     _DBG("[%d] uid=%d gid=%d type=%d chroot=%s",
324
-         NUM_CHILDS, uid, gid, type,
325
-         chroot);
326
+         NUM_CHILDS, senv->uid, senv->gid, type,
327
+         senv->chroot);
328
 
329
-    if (uid == 0 || gid == 0)
330
+    if (senv->uid == 0 || senv->gid == 0)
331
     {
332
         _DBG("Assigning root user/group to a child.", 0);
333
     }
334
@@ -2062,19 +2193,28 @@
335
         if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_STARTING)
336
           make_child(ap_server_conf, i);
337
       }
338
-      else if(((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || 
339
+      else if(
340
+    	      (((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || 
341
                CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER)  &&
342
                ap_scoreboard_image->parent[i].pid > 1) &&
343
-               (idle_processors (i) > 1 || total_processes (i) == 1) && (
344
+               (idle_processors (i) > CHILD_INFO_TABLE[i].senv->min_free_processors || CHILD_INFO_TABLE[i].senv->min_free_processors == 0)
345
+            		&& total_processes (i) > CHILD_INFO_TABLE[i].senv->min_processors && (
346
                    (expire_timeout > 0 &&  ap_scoreboard_image->servers[i][0].status != SERVER_DEAD && 
347
                    apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > expire_timeout) ||
348
                    (idle_timeout >   0 &&  ap_scoreboard_image->servers[i][0].status == SERVER_READY &&  
349
-                   apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout)))
350
+                   apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout))
351
+              )
352
+        	|| (CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER &&
353
+        		(multiplexer_idle_timeout > 0 && ap_scoreboard_image->servers[i][0].status == SERVER_READY &&
354
+                	apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > multiplexer_idle_timeout) && 
355
+            		total_processors(i) > CHILD_INFO_TABLE[i].senv->min_processors
356
+                   )
357
+            )
358
       {
359
         CHILD_INFO_TABLE[i].pid = 0;
360
         CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY;
361
 
362
-        if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER)
363
+        if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER || CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER)
364
         {
365
           /* completely free up this slot */
366
 
367
@@ -2599,6 +2739,8 @@
368
     ap_min_processors = DEFAULT_MIN_PROCESSORS;
369
     ap_min_free_processors = DEFAULT_MIN_FREE_PROCESSORS;
370
     ap_max_processors = DEFAULT_MAX_PROCESSORS;
371
+    ap_min_multiplexers = DEFAULT_MIN_MULTIPLEXERS;
372
+    ap_max_multiplexers = DEFAULT_MAX_MULTIPLEXERS;
373
     ap_daemons_limit = server_limit;
374
     ap_pid_fname = DEFAULT_PIDLOG;
375
     ap_lock_fname = DEFAULT_LOCKFILE;
376
@@ -2608,6 +2750,13 @@
377
 	ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
378
 #endif
379
 
380
+    expire_timeout = DEFAULT_EXPIRE_TIMEOUT;
381
+    idle_timeout = DEFAULT_IDLE_TIMEOUT;
382
+    multiplexer_idle_timeout = DEFAULT_MULTIPLEXER_IDLE_TIMEOUT;
383
+    processor_wait_timeout = DEFAULT_PROCESSOR_WAIT_TIMEOUT;
384
+    processor_wait_steps = DEFAULT_PROCESSOR_WAIT_STEPS;
385
+    
386
+
387
     apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
388
 
389
     /* we need to know ServerLimit and ThreadLimit before we start processing
390
@@ -2712,6 +2861,7 @@
391
 
392
         server_env_image->control->num = 0;
393
 
394
+/*
395
         for (i = 0; i < tmp_server_limit; i++)
396
         {
397
             SENV[i].processor_id = -1;
398
@@ -2721,6 +2871,7 @@
399
             SENV[i].input        = -1;
400
             SENV[i].output       = -1;
401
         }
402
+*/
403
     }
404
 
405
     return OK;
406
@@ -2782,7 +2933,6 @@
407
                 {
408
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0,
409
                              ap_server_conf, "Could not pass request to proper "                             "child, request will not be honoured.");
410
-                    return DECLINED;
411
                 }
412
                 _DBG("doing longjmp",0);
413
                 longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1);
414
@@ -2859,32 +3009,37 @@
415
     ap_rputs("<hr>\n", r);
416
     ap_rputs("<h2>peruser status</h2>\n", r);
417
     ap_rputs("<table border=\"0\">\n", r);
418
-    ap_rputs("<tr><td>ID</td><td>PID</td><td>STATUS</td><td>TYPE</td><td>UID</td>"
419
-                   "<td>GID</td><td>CHROOT</td><td>INPUT</td>"
420
+    ap_rputs("<tr><td>ID</td><td>PID</td><td>STATUS</td><td>SB STATUS</td><td>TYPE</td><td>UID</td>"
421
+                   "<td>GID</td><td>CHROOT</td><td>NICE</td><td>INPUT</td>"
422
                    "<td>OUTPUT</td><td>SOCK_FD</td>"
423
                    "<td>TOTAL PROCESSORS</td><td>MAX PROCESSORS</td>"
424
-                   "<td>IDLE PROCESSORS</td><td>MIN FREE PROCESSORS</td></tr>\n", r);
425
+                   "<td>IDLE PROCESSORS</td><td>MIN FREE PROCESSORS</td>"
426
+                   "<td>AVAIL</td>"
427
+                   "</tr>\n", r);
428
     for (x = 0; x < NUM_CHILDS; x++)
429
         {
430
         senv = CHILD_INFO_TABLE[x].senv;
431
-        ap_rprintf(r, "<tr><td>%3d</td><td>%5d</td><td>%8s</td><td>%12s</td>"
432
-                       "<td>%4d</td><td>%4d</td><td>%25s</td><td>%5d</td>"
433
+        ap_rprintf(r, "<tr><td>%3d</td><td>%5d</td><td>%8s</td><td>%8s</td><td>%12s</td>"
434
+                       "<td>%4d</td><td>%4d</td><td>%25s</td><td>%3d</td><td>%5d</td>"
435
                        "<td>%6d</td><td>%7d</td><td>%d</td><td>%d</td>"
436
-                       "<td>%d</td><td>%d</td></tr>\n", 
437
+                       "<td>%d</td><td>%d</td><td>%3d</td></tr>\n", 
438
                        CHILD_INFO_TABLE[x].id, 
439
                        CHILD_INFO_TABLE[x].pid, 
440
                        child_status_string(CHILD_INFO_TABLE[x].status), 
441
+                       scoreboard_status_string(SCOREBOARD_STATUS(x)), 
442
                        child_type_string(CHILD_INFO_TABLE[x].type), 
443
                        senv == NULL ? -1 : senv->uid, 
444
                        senv == NULL ? -1 : senv->gid, 
445
                        senv == NULL ? NULL : senv->chroot, 
446
+                       senv == NULL ? 0 : senv->nice_lvl,
447
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, 
448
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, 
449
                        CHILD_INFO_TABLE[x].sock_fd,
450
                        total_processors(x), 
451
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->max_processors,
452
                        idle_processors(x),
453
-                       senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors
454
+                       senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors,
455
+                       senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->availability
456
                        );
457
        }
458
     ap_rputs("</table>\n", r);
459
@@ -2938,50 +3093,162 @@
460
     APR_OPTIONAL_HOOK(ap, status_hook, peruser_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
461
 }
462
 
463
-/* we define an Processor w/ specific uid/gid */
464
-static const char *cf_Processor(cmd_parms *cmd, void *dummy,
465
-    const char *user_name, const char *group_name, const char *chroot)
466
+static const char *cf_Processor(cmd_parms *cmd, void *dummy, const char *arg)
467
 {
468
-    uid_t uid = ap_uname2id(user_name);
469
-    gid_t gid = ap_gname2id(group_name);
470
+    const char *user_name = NULL, *group_name = NULL, *directive;
471
+    server_env_t senv;
472
+    ap_directive_t *current;
473
+
474
+    char *endp = ap_strrchr_c(arg, '>');
475
+
476
+    if (endp == NULL) {
477
+	return apr_psprintf(cmd->temp_pool, 
478
+			    "Error: Directive %s> missing closing '>'", cmd->cmd->name);
479
+    }
480
+
481
+    *endp = '\0';
482
+
483
+    senv.name = ap_getword_conf(cmd->temp_pool, &arg);
484
+    _DBG("processor_name: %s", senv.name);
485
+    
486
+    if (strlen(senv.name) == 0) {
487
+	return apr_psprintf(cmd->temp_pool, 
488
+			    "Error: Directive %s> takes one argument", cmd->cmd->name);
489
+    }
490
+    
491
+    /*	Check for existing processors on first launch and between gracefuls */
492
+    if (restart_num == 1 || is_graceful) {
493
+	server_env_t *old_senv = find_senv_by_name(senv.name);
494
+    
495
+	if (old_senv) {
496
+	    return apr_psprintf(cmd->temp_pool,
497
+			    "Error: Processor %s already defined", senv.name);
498
+	}
499
+    }
500
+    
501
+    senv.nice_lvl 		= 0;
502
+    senv.chroot 		= NULL;
503
+    senv.min_processors 	= ap_min_processors;
504
+    senv.min_free_processors 	= ap_min_free_processors;
505
+    senv.max_processors 	= ap_max_processors;
506
+    
507
+    current = cmd->directive->first_child;
508
+    
509
+    int proc_temp = 0;
510
+    
511
+    for(; current != NULL; current = current->next) {
512
+        directive = current->directive;
513
+        
514
+        if (!strcasecmp(directive, "user")) {
515
+    	    user_name = current->args;
516
+        }
517
+        else if (!strcasecmp(directive, "group")) {
518
+    	    group_name = current->args;
519
+        }
520
+        else if (!strcasecmp(directive, "chroot")) {
521
+    	    senv.chroot = current->args;
522
+        }
523
+        else if (!strcasecmp(directive, "nicelevel")) {
524
+    	    senv.nice_lvl = atoi(current->args);
525
+        }
526
+        else if (!strcasecmp(directive, "maxprocessors")) {
527
+    	    proc_temp = atoi(current->args);
528
+
529
+	    if (proc_temp < 1) {
530
+    		ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
531
+                     "WARNING: Require MaxProcessors > 0, setting to 1");
532
+                proc_temp = 1;
533
+	    }
534
+	    
535
+	    senv.max_processors = proc_temp;
536
+        }
537
+        else if (!strcasecmp(directive, "minprocessors")) {
538
+    	    proc_temp = atoi(current->args);
539
+
540
+	    if (proc_temp < 0) {
541
+    		ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
542
+                     "WARNING: Require MinProcessors >= 0, setting to 0");
543
+                proc_temp = 0;
544
+	    }
545
+	    
546
+	    senv.min_processors = proc_temp;
547
+        }
548
+        else if (!strcasecmp(directive, "minspareprocessors")) {
549
+    	    proc_temp = atoi(current->args);
550
+
551
+	    if (proc_temp < 0) {
552
+    		ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
553
+                     "WARNING: Require MinSpareProcessors >= 0, setting to 0");
554
+                proc_temp = 0;
555
+	    }
556
+	    
557
+	    senv.min_free_processors = proc_temp;
558
+        }
559
+        else {
560
+    	    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
561
+                    "Unknown directive %s in %s>", directive, cmd->cmd->name);
562
+        }
563
+    }
564
+    
565
+    if (user_name == NULL || group_name == NULL) {
566
+	return apr_psprintf(cmd->temp_pool,
567
+			    "Error: User or Group must be set in %s>", cmd->cmd->name);
568
+    }
569
+    
570
+    senv.uid = ap_uname2id(user_name);
571
+    senv.gid = ap_gname2id(group_name);
572
+
573
+    _DBG("name=%s user=%s:%d group=%s:%d chroot=%s nice_lvl=%d",
574
+        senv.name, user_name, senv.uid, group_name, senv.gid, senv.chroot, senv.nice_lvl);
575
+
576
+    _DBG("min_processors=%d min_free_processors=%d max_processors=%d",
577
+        senv.min_processors, senv.min_free_processors, senv.max_processors);
578
 
579
-    _DBG("user=%s:%d group=%s:%d chroot=%s",
580
-        user_name, uid, group_name, gid, chroot);
581
 
582
     return child_add(CHILD_TYPE_PROCESSOR, CHILD_STATUS_STANDBY,
583
-                     cmd->pool, uid, gid, chroot);
584
+                     cmd->pool, &senv);
585
 }
586
 
587
 /* we define an Multiplexer child w/ specific uid/gid */
588
 static const char *cf_Multiplexer(cmd_parms *cmd, void *dummy,
589
     const char *user_name, const char *group_name, const char *chroot)
590
 {
591
-    uid_t uid = ap_uname2id(user_name);
592
-    gid_t gid = ap_gname2id(group_name);
593
+    server_env_t senv;
594
+    
595
+    senv.name 		= NULL;
596
+    
597
+    senv.uid 		= ap_uname2id(user_name);
598
+    senv.gid 		= ap_gname2id(group_name);
599
+    senv.chroot 	= chroot;
600
+    senv.nice_lvl 	= 0;
601
+
602
+    senv.min_processors 	= ap_min_multiplexers;
603
+    senv.min_free_processors 	= ap_min_free_processors;
604
+    senv.max_processors 	= ap_max_multiplexers;
605
 
606
     _DBG("user=%s:%d group=%s:%d chroot=%s [multiplexer id %d]",
607
-        user_name, uid, group_name, gid, chroot, NUM_CHILDS);
608
+        user_name, senv.uid, group_name, senv.gid, senv.chroot, NUM_CHILDS);
609
 
610
     return child_add(CHILD_TYPE_MULTIPLEXER, CHILD_STATUS_STARTING,
611
-                     cmd->pool, uid, gid, chroot);
612
+                     cmd->pool, &senv);
613
 }
614
 
615
 static const char* cf_ServerEnvironment(cmd_parms *cmd, void *dummy,
616
-    const char *user_name, const char *group_name, const char *chroot)
617
+    const char *name)
618
 {
619
-    int uid = ap_uname2id(user_name);
620
-    int gid = ap_gname2id(group_name);
621
     peruser_server_conf *sconf = PERUSER_SERVER_CONF(cmd->server->module_config);
622
 
623
     _DBG("function entered", 0);
624
 
625
-       if (chroot && !ap_is_directory(cmd->pool, chroot))
626
-               return apr_psprintf(cmd->pool, "Error: chroot directory [%s] does not exist", chroot);
627
+    sconf->senv = find_senv_by_name(name);
628
 
629
-    sconf->senv = senv_add(uid, gid, chroot);
630
+    if (sconf->senv == NULL) {
631
+	return apr_psprintf(cmd->pool, 
632
+			    "Error: Processor %s not defined", name);
633
+    }
634
 
635
-    _DBG("user=%s:%d group=%s:%d chroot=%s numchilds=%d",
636
-        user_name, uid, group_name, gid, chroot, NUM_CHILDS);
637
+    _DBG("user=%d group=%d chroot=%s numchilds=%d",
638
+        sconf->senv->uid, sconf->senv->gid, sconf->senv->chroot, NUM_CHILDS);
639
 
640
     return NULL;
641
 }
642
@@ -3046,10 +3313,10 @@
643
 
644
     min_procs = atoi(arg);
645
 
646
-    if (min_procs < 1) {
647
+    if (min_procs < 0) {
648
         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
649
-                     "WARNING: Require MaxProcessors > 0, setting to 1");
650
-        min_procs = 1;
651
+                     "WARNING: Require MinProcessors >= 0, setting to 0");
652
+        min_procs = 0;
653
     }
654
 
655
     if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) {
656
@@ -3075,10 +3342,10 @@
657
 
658
     min_free_procs = atoi(arg);
659
 
660
-    if (min_free_procs < 1) {
661
+    if (min_free_procs < 0) {
662
         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
663
-                     "WARNING: Require MinSpareProcessors > 0, setting to 1");
664
-        min_free_procs = 1;
665
+                     "WARNING: Require MinSpareProcessors >= 0, setting to 0");
666
+        min_free_procs = 0;
667
     }
668
 
669
     if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) {
670
@@ -3121,6 +3388,50 @@
671
     return NULL;
672
 }
673
 
674
+static const char *set_min_multiplexers (cmd_parms *cmd, void *dummy, const char *arg)
675
+{
676
+    int min_multiplexers;
677
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
678
+
679
+    if (err != NULL) {
680
+        return err;
681
+    }
682
+
683
+    min_multiplexers = atoi(arg);
684
+
685
+    if (min_multiplexers < 1) {
686
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
687
+                     "WARNING: Require MinMultiplexers > 0, setting to 1");
688
+        min_multiplexers = 1;
689
+    }
690
+
691
+    ap_min_multiplexers = min_multiplexers;
692
+
693
+    return NULL;
694
+}
695
+
696
+static const char *set_max_multiplexers (cmd_parms *cmd, void *dummy, const char *arg)
697
+{
698
+    int max_multiplexers;
699
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
700
+
701
+    if (err != NULL) {
702
+        return err;
703
+    }
704
+
705
+    max_multiplexers = atoi(arg);
706
+
707
+    if (max_multiplexers < 1) {
708
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
709
+                     "WARNING: Require MaxMultiplexers > 0, setting to 1");
710
+        max_multiplexers = 1;
711
+    }
712
+
713
+    ap_max_multiplexers = max_multiplexers;
714
+
715
+    return NULL;
716
+}
717
+
718
 static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *arg) 
719
 {
720
     int tmp_server_limit;
721
@@ -3183,6 +3494,40 @@
722
     return NULL;
723
 }
724
 
725
+static const char *set_multiplexer_idle_timeout (cmd_parms *cmd, void *dummy, const char *arg) {
726
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
727
+    if (err != NULL) {
728
+        return err;
729
+    }
730
+
731
+    multiplexer_idle_timeout = atoi(arg);
732
+
733
+    return NULL;
734
+}
735
+
736
+static const char *set_processor_wait_timeout (cmd_parms *cmd, void *dummy, const char *timeout, const char *steps) {
737
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
738
+    if (err != NULL) {
739
+        return err;
740
+    }
741
+    
742
+    processor_wait_timeout = atoi(timeout);
743
+    
744
+    if (steps != NULL) {
745
+	int steps_tmp = atoi(steps);
746
+	
747
+	if (steps_tmp < 1) {
748
+    	    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
749
+                     "WARNING: Require ProcessorWaitTimeout steps > 0, setting to 1");
750
+            steps_tmp = 1;
751
+	}
752
+	
753
+	processor_wait_steps = steps_tmp;
754
+    }
755
+
756
+    return NULL;
757
+}
758
+
759
 static const command_rec peruser_cmds[] = {
760
 UNIX_DAEMON_COMMANDS,
761
 LISTEN_COMMANDS,
762
@@ -3196,17 +3541,25 @@
763
               "Minimum number of processors per vhost"),
764
 AP_INIT_TAKE1("MaxProcessors", set_max_processors, NULL, RSRC_CONF,
765
               "Maximum number of processors per vhost"),
766
+AP_INIT_TAKE1("MinMultiplexers", set_min_multiplexers, NULL, RSRC_CONF,
767
+              "Minimum number of multiplexers the server can have"),
768
+AP_INIT_TAKE1("MaxMultiplexers", set_max_multiplexers, NULL, RSRC_CONF,
769
+              "Maximum number of multiplexers the server can have"),
770
 AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
771
               "Maximum value of MaxClients for this run of Apache"),
772
 AP_INIT_TAKE1("ExpireTimeout", set_expire_timeout, NULL, RSRC_CONF,
773
-              "Maximum idle time before a child is killed, 0 to disable"),
774
+              "Maximum time a child can live, 0 to disable"),
775
 AP_INIT_TAKE1("IdleTimeout", set_idle_timeout, NULL, RSRC_CONF,
776
               "Maximum time before a child is killed after being idle, 0 to disable"),
777
+AP_INIT_TAKE1("MultiplexerIdleTimeout", set_multiplexer_idle_timeout, NULL, RSRC_CONF,
778
+              "Maximum time before a multiplexer is killed after being idle, 0 to disable"),
779
+AP_INIT_TAKE12("ProcessorWaitTimeout", set_processor_wait_timeout, NULL, RSRC_CONF,
780
+              "Maximum time a multiplexer waits for the processor if it is busy"),
781
 AP_INIT_TAKE23("Multiplexer", cf_Multiplexer, NULL, RSRC_CONF,
782
               "Specify an Multiplexer Child configuration."),
783
-AP_INIT_TAKE23("Processor", cf_Processor, NULL, RSRC_CONF,
784
-              "Specify a User and Group for a specific child process."),
785
-AP_INIT_TAKE23("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF,
786
+AP_INIT_RAW_ARGS("<Processor", cf_Processor, NULL, RSRC_CONF,
787
+              "Specify settings for processor."),
788
+AP_INIT_TAKE1("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF,
789
               "Specify the server environment for this virtual host."),
790
 { NULL }
791
 };
792
--- server/mpm/experimental/peruser/mpm_default.h	2008-05-12 00:38:04.000000000 +0300
793
+++ server/mpm/experimental/peruser/mpm_default.h	2008-05-18 21:37:29.000000000 +0300
794
@@ -107,4 +107,50 @@
795
 #define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
796
 #endif
797
 
798
+/* Maximum multiplexers */
799
+
800
+#ifndef DEFAULT_MAX_MULTIPLEXERS
801
+#define DEFAULT_MAX_MULTIPLEXERS 20
802
+#endif
803
+
804
+/* Minimum multiplexers */
805
+
806
+#ifndef DEFAULT_MIN_MULTIPLEXERS
807
+#define DEFAULT_MIN_MULTIPLEXERS 3
808
+#endif
809
+
810
+/* Amount of time a child can run before it expires (0 = turn off) */
811
+
812
+#ifndef DEFAULT_EXPIRE_TIMEOUT
813
+#define DEFAULT_EXPIRE_TIMEOUT 1800
814
+#endif
815
+
816
+/* Amount of time a child can stay idle (0 = turn off) */
817
+
818
+#ifndef DEFAULT_IDLE_TIMEOUT
819
+#define DEFAULT_IDLE_TIMEOUT 900
820
+#endif
821
+
822
+/* Amount of time a multiplexer can stay idle (0 = turn off) */
823
+
824
+#ifndef DEFAULT_MULTIPLEXER_IDLE_TIMEOUT
825
+#define DEFAULT_MULTIPLEXER_IDLE_TIMEOUT 0
826
+#endif
827
+
828
+/* Amount of maximum time a multiplexer can wait for processor if it is busy (0 = never wait)
829
+ * This is decreased with every busy request
830
+ */
831
+
832
+#ifndef DEFAULT_PROCESSOR_WAIT_TIMEOUT
833
+#define DEFAULT_PROCESSOR_WAIT_TIMEOUT 2
834
+#endif
835
+
836
+/* The number of different levels there are when a multiplexer is waiting for processor
837
+ * (between maximum waiting time and no waiting)
838
+ */
839
+
840
+#ifndef DEFAULT_PROCESSOR_WAIT_STEPS
841
+#define DEFAULT_PROCESSOR_WAIT_STEPS 10
842
+#endif
843
+
844
 #endif /* AP_MPM_DEFAULT_H */
(-)apache22-peruser-mpm3/files/httpd-2.2.3-peruser-0.3.0-dc3.patch (+1211 lines)
Line 0 Link Here
1
--- server/mpm/experimental/peruser/mpm_default.h	2009-05-27 15:09:19.000000000 +0300
2
+++ server/mpm/experimental/peruser/mpm_default.h	2009-05-28 10:10:42.000000000 +0300
3
@@ -77,6 +77,12 @@
4
 #define DEFAULT_MIN_FREE_PROCESSORS 2
5
 #endif
6
 
7
+/* Maximum --- more than this, and idle processors will be killed (0 = disable) */
8
+
9
+#ifndef DEFAULT_MAX_FREE_PROCESSORS
10
+#define DEFAULT_MAX_FREE_PROCESSORS 0
11
+#endif
12
+
13
 /* Maximum processors per ServerEnvironment */
14
 
15
 #ifndef DEFAULT_MAX_PROCESSORS
16
@@ -107,4 +113,50 @@
17
 #define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
18
 #endif
19
 
20
+/* Maximum multiplexers */
21
+
22
+#ifndef DEFAULT_MAX_MULTIPLEXERS
23
+#define DEFAULT_MAX_MULTIPLEXERS 20
24
+#endif
25
+
26
+/* Minimum multiplexers */
27
+
28
+#ifndef DEFAULT_MIN_MULTIPLEXERS
29
+#define DEFAULT_MIN_MULTIPLEXERS 3
30
+#endif
31
+
32
+/* Amount of time a child can run before it expires (0 = turn off) */
33
+
34
+#ifndef DEFAULT_EXPIRE_TIMEOUT
35
+#define DEFAULT_EXPIRE_TIMEOUT 1800
36
+#endif
37
+
38
+/* Amount of time a child can stay idle (0 = turn off) */
39
+
40
+#ifndef DEFAULT_IDLE_TIMEOUT
41
+#define DEFAULT_IDLE_TIMEOUT 900
42
+#endif
43
+
44
+/* Amount of time a multiplexer can stay idle (0 = turn off) */
45
+
46
+#ifndef DEFAULT_MULTIPLEXER_IDLE_TIMEOUT
47
+#define DEFAULT_MULTIPLEXER_IDLE_TIMEOUT 0
48
+#endif
49
+
50
+/* Amount of maximum time a multiplexer can wait for processor if it is busy (0 = never wait)
51
+ * This is decreased with every busy request
52
+ */
53
+
54
+#ifndef DEFAULT_PROCESSOR_WAIT_TIMEOUT
55
+#define DEFAULT_PROCESSOR_WAIT_TIMEOUT 5
56
+#endif
57
+
58
+/* The number of different levels there are when a multiplexer is waiting for processor
59
+ * (between maximum waiting time and no waiting)
60
+ */
61
+
62
+#ifndef DEFAULT_PROCESSOR_WAIT_STEPS
63
+#define DEFAULT_PROCESSOR_WAIT_STEPS 10
64
+#endif
65
+
66
 #endif /* AP_MPM_DEFAULT_H */
67
--- server/mpm/experimental/peruser/mpm.h	2009-03-22 22:46:45.000000000 +0200
68
+++ server/mpm/experimental/peruser/mpm.h	2009-03-22 22:39:10.000000000 +0200
69
@@ -84,6 +84,7 @@
70
 #define AP_MPM_USES_POD 1
71
 #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
72
 #define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0)
73
+#define MPM_VALID_PID(p) (getpgid(p) == getpgrp())
74
 #define MPM_ACCEPT_FUNC unixd_accept
75
 
76
 extern int ap_threads_per_child;
77
--- server/mpm/experimental/peruser/peruser.c	2009-05-27 15:09:19.000000000 +0300
78
+++ server/mpm/experimental/peruser/peruser.c	2009-05-28 13:54:27.000000000 +0300
79
@@ -195,20 +195,30 @@
80
 
81
 #define CHILD_STATUS_STANDBY  0  /* wait for a request before starting */
82
 #define CHILD_STATUS_STARTING 1  /* wait for socket creation */
83
-#define CHILD_STATUS_READY    2  /* wait for mux to restart */
84
-#define CHILD_STATUS_ACTIVE   3  /* ready to take requests */
85
+#define CHILD_STATUS_READY    2  /* is ready to take requests */
86
+#define CHILD_STATUS_ACTIVE   3  /* is currently busy handling requests */
87
 #define CHILD_STATUS_RESTART  4  /* child about to die and restart */
88
 
89
+/* cgroup settings */
90
+#define CGROUP_TASKS_FILE "/tasks"
91
+#define CGROUP_TASKS_FILE_LEN 7
92
+
93
 /* config globals */
94
 
95
 int ap_threads_per_child=0;         /* Worker threads per child */
96
 static apr_proc_mutex_t *accept_mutex;
97
 static int ap_min_processors=DEFAULT_MIN_PROCESSORS;
98
 static int ap_min_free_processors=DEFAULT_MIN_FREE_PROCESSORS;
99
+static int ap_max_free_processors=DEFAULT_MAX_FREE_PROCESSORS;
100
 static int ap_max_processors=DEFAULT_MAX_PROCESSORS;
101
+static int ap_min_multiplexers=DEFAULT_MIN_MULTIPLEXERS;
102
+static int ap_max_multiplexers=DEFAULT_MAX_MULTIPLEXERS;
103
 static int ap_daemons_limit=0;      /* MaxClients */
104
-static int expire_timeout=1800;
105
-static int idle_timeout=900;
106
+static int expire_timeout=DEFAULT_EXPIRE_TIMEOUT;
107
+static int idle_timeout=DEFAULT_IDLE_TIMEOUT;
108
+static int multiplexer_idle_timeout=DEFAULT_MULTIPLEXER_IDLE_TIMEOUT;
109
+static int processor_wait_timeout=DEFAULT_PROCESSOR_WAIT_TIMEOUT;
110
+static int processor_wait_steps=DEFAULT_PROCESSOR_WAIT_STEPS;
111
 static int server_limit = DEFAULT_SERVER_LIMIT;
112
 static int first_server_limit;
113
 static int changed_limit_at_restart;
114
@@ -222,15 +232,21 @@
115
 {
116
     int processor_id;
117
 
118
+    const char *name;	/* Server environment's unique string identifier */
119
+
120
     /* security settings */
121
     uid_t uid;          /* user id */
122
     gid_t gid;          /* group id */
123
     const char *chroot; /* directory to chroot() to, can be null */
124
+    int nice_lvl;
125
+    const char *cgroup; /* cgroup directory, can be null */
126
 
127
     /* resource settings */
128
     int min_processors;
129
     int min_free_processors;
130
+    int max_free_processors;
131
     int max_processors;
132
+    int availability;
133
 
134
     /* sockets */
135
     int input;          /* The socket descriptor */
136
@@ -437,6 +453,25 @@
137
     return "UNKNOWN";
138
 }
139
 
140
+char* scoreboard_status_string(int status) {
141
+    switch(status)
142
+    {
143
+        case SERVER_DEAD:  return "DEAD";
144
+        case SERVER_STARTING: return "STARTING";
145
+        case SERVER_READY:    return "READY";
146
+        case SERVER_BUSY_READ:   return "BUSY_READ";
147
+        case SERVER_BUSY_WRITE:   return "BUSY_WRITE";
148
+        case SERVER_BUSY_KEEPALIVE:   return "BUSY_KEEPALIVE";
149
+        case SERVER_BUSY_LOG:   return "BUSY_LOG";
150
+        case SERVER_BUSY_DNS:   return "BUSY_DNS";
151
+        case SERVER_CLOSING:   return "CLOSING";
152
+        case SERVER_GRACEFUL:   return "GRACEFUL";
153
+        case SERVER_NUM_STATUS:   return "NUM_STATUS";
154
+    }
155
+
156
+    return "UNKNOWN";
157
+}
158
+
159
 void dump_child_table()
160
 {
161
 #ifdef MPM_PERUSER_DEBUG
162
@@ -511,10 +546,6 @@
163
 
164
 static void accept_mutex_on(void)
165
 {
166
-/* for some reason this fails if we listen on the pipe_of_death.
167
-   fortunately I don't think we currently need it */
168
-
169
-#if 0
170
     apr_status_t rv = apr_proc_mutex_lock(accept_mutex);
171
     if (rv != APR_SUCCESS) {
172
         const char *msg = "couldn't grab the accept mutex";
173
@@ -529,12 +560,10 @@
174
             exit(APEXIT_CHILDFATAL);
175
         }
176
     }
177
-#endif
178
 }
179
 
180
 static void accept_mutex_off(void)
181
 {
182
-#if 0
183
     apr_status_t rv = apr_proc_mutex_unlock(accept_mutex);
184
     if (rv != APR_SUCCESS) {
185
         const char *msg = "couldn't release the accept mutex";
186
@@ -552,7 +581,6 @@
187
             exit(APEXIT_CHILDFATAL);
188
         }
189
     }
190
-#endif
191
 }
192
 
193
 /* On some architectures it's safe to do unserialized accept()s in the single
194
@@ -715,8 +743,10 @@
195
     ret = apr_socket_recv(lr->sd, &pipe_read_char, &n);
196
     if (APR_STATUS_IS_EAGAIN(ret))
197
     {
198
-            /* It lost the lottery. It must continue to suffer
199
-             * through a life of servitude. */
200
+       /* It lost the lottery. It must continue to suffer
201
+        * through a life of servitude. */
202
+       _DBG("POD read EAGAIN");
203
+       return ret;
204
     }
205
     else
206
     {
207
@@ -1087,8 +1117,7 @@
208
     for(i = 0, total = 0; i < NUM_CHILDS; ++i)
209
     {
210
         if(CHILD_INFO_TABLE[i].senv == CHILD_INFO_TABLE[child_num].senv &&
211
-           (SCOREBOARD_STATUS(i) == SERVER_STARTING ||
212
-            SCOREBOARD_STATUS(i) == SERVER_READY))
213
+           (CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY))
214
         {
215
             total++;
216
         }
217
@@ -1116,7 +1145,7 @@
218
     apr_bucket *bucket;
219
     const apr_array_header_t *headers_in_array;
220
     const apr_table_entry_t *headers_in;
221
-    int counter;
222
+    int counter, wait_time, wait_step_size;
223
 
224
     apr_socket_t *thesock = ap_get_module_config(r->connection->conn_config, &core_module);
225
 
226
@@ -1137,10 +1166,73 @@
227
       apr_table_get(r->headers_in, "Host"), my_child_num, processor->senv->output);
228
     _DBG("r->the_request=\"%s\" len=%d", r->the_request, strlen(r->the_request));
229
 
230
-    ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, len);
231
+    wait_step_size = 100 / processor_wait_steps;
232
 
233
-    /* Scan the brigade looking for heap-buckets */
234
+    /*	Check if the processor is available */
235
+    if (total_processors(processor->id) == processor->senv->max_processors &&
236
+        idle_processors(processor->id) == 0) {
237
+        /* The processor is currently busy, try to wait (a little) */
238
+        _DBG("processor seems to be busy, trying to wait for it");
239
+
240
+        if (processor->senv->availability == 0) {
241
+            processor->senv->availability = 0;
242
+
243
+            _DBG("processor is very busy (availability = 0) - not passing request");
244
+
245
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
246
+                         "Too many requests for processor %s, increase MaxProcessors", processor->senv->name);
247
+	    
248
+            /* No point in waiting for the processor, it's very busy */
249
+            return -1;
250
+        }
251
+        
252
+        /* We sleep a little (depending how available the processor usually is) */
253
+        int i;
254
+        
255
+        wait_time = (processor_wait_timeout / processor_wait_steps) * 1000000;
256
+
257
+        for(i = 0; i <= processor->senv->availability; i += wait_step_size) {
258
+            usleep(wait_time);
259
 
260
+            /* Check if the processor is ready */
261
+            if (total_processors(processor->id) < processor->senv->max_processors ||
262
+                idle_processors(processor->id) > 0) {
263
+                /* The processor has freed - lets use it */
264
+                _DBG("processor freed before wait time expired");
265
+                break;
266
+            }
267
+        }
268
+        
269
+        if (processor->senv->availability <= wait_step_size) {
270
+            processor->senv->availability = 0;
271
+        }
272
+        else processor->senv->availability -= wait_step_size;
273
+        
274
+        /* Check if we waited all the time */
275
+        if (i > processor->senv->availability) {
276
+            _DBG("processor is busy - not passing request (availability = %d)",
277
+                 processor->senv->availability);
278
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
279
+                         "Too many requests for processor %s, increase MaxProcessors", processor->senv->name);
280
+            return -1;
281
+        }
282
+
283
+        /* We could increase the availability a little here,
284
+         * because the processor got freed eventually
285
+         */
286
+    }
287
+    else {
288
+        /* Smoothly increment the availability back to 100 */
289
+        if (processor->senv->availability >= 100-wait_step_size) {
290
+            processor->senv->availability = 100;
291
+        }
292
+        else processor->senv->availability += wait_step_size;
293
+    }
294
+    
295
+    ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, len);
296
+    
297
+    /* Scan the brigade looking for heap-buckets */
298
+    
299
     _DBG("Scanning the brigade",0);
300
     bucket = APR_BRIGADE_FIRST(bb);
301
     while (bucket != APR_BRIGADE_SENTINEL(bb) &&
302
@@ -1294,12 +1386,22 @@
303
     /* -- receive data from socket -- */
304
     apr_os_sock_get(&ctrl_sock_fd, lr->sd);
305
     _DBG("receiving from sock_fd=%d", ctrl_sock_fd);
306
-    ret = recvmsg(ctrl_sock_fd, &msg, 0);
307
 
308
-    if(ret == -1)
309
-      _DBG("recvmsg failed with error \"%s\"", strerror(errno));
310
-    else
311
-      _DBG("recvmsg returned %d", ret);
312
+    // Don't block
313
+    ret = recvmsg(ctrl_sock_fd, &msg, MSG_DONTWAIT);
314
+
315
+    if (ret == -1 && errno == EAGAIN) {
316
+        _DBG("receive_from_multiplexer recvmsg() EAGAIN, someone was faster");
317
+        
318
+        return APR_EAGAIN;
319
+    }
320
+    else if (ret == -1) {
321
+        _DBG("recvmsg failed with error \"%s\"", strerror(errno));
322
+        
323
+        // Error, better kill this child to be on the safe side
324
+        return APR_EGENERAL;
325
+    }
326
+    else _DBG("recvmsg returned %d", ret);
327
 
328
     /* -- extract socket from the cmsg -- */
329
     memcpy(&trans_sock_fd, CMSG_DATA(cmsg), sizeof(trans_sock_fd));
330
@@ -1399,10 +1501,58 @@
331
     return 0;
332
 }
333
 
334
-static int peruser_setup_child(int childnum)
335
+static int peruser_setup_cgroup(int childnum, server_env_t *senv, apr_pool_t *pool)
336
+{
337
+    apr_file_t *file;
338
+    int length;
339
+    apr_size_t content_len;
340
+    char *tasks_file, *content, *pos;
341
+
342
+    _DBG("starting to add pid to cgroup %s", senv->cgroup);
343
+
344
+    length = strlen(senv->cgroup) + CGROUP_TASKS_FILE_LEN;
345
+    tasks_file = malloc(length);
346
+
347
+    if (!tasks_file) return -1;
348
+
349
+    pos = apr_cpystrn(tasks_file, senv->cgroup, length);
350
+    apr_cpystrn(pos, CGROUP_TASKS_FILE, CGROUP_TASKS_FILE_LEN);
351
+
352
+    /* Prepare the data to be written to tasks file */
353
+    content = apr_itoa(pool, ap_my_pid);
354
+    content_len  = strlen(content);
355
+
356
+    _DBG("writing pid %s to tasks file %s", content, tasks_file);
357
+
358
+    if (apr_file_open(&file, tasks_file, APR_WRITE, APR_OS_DEFAULT, pool)) {
359
+        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
360
+                     "cgroup: unable to open file %s",
361
+                     tasks_file);
362
+        free(tasks_file);
363
+        return OK; /* don't fail if cgroup not available */
364
+    }
365
+
366
+    if (apr_file_write(file, content, &content_len)) {
367
+   	ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
368
+                     "cgroup: unable to write pid to file %s",
369
+                     tasks_file);
370
+    }
371
+
372
+    apr_file_close(file);
373
+
374
+    free(tasks_file);
375
+
376
+    return OK;
377
+}
378
+
379
+static int peruser_setup_child(int childnum, apr_pool_t *pool)
380
 {
381
     server_env_t *senv = CHILD_INFO_TABLE[childnum].senv;
382
 
383
+    if (senv->nice_lvl != 0) {
384
+        nice(senv->nice_lvl);
385
+    }
386
+
387
     if(senv->chroot) {
388
       _DBG("chdir to %s", senv->chroot);
389
       if(chdir(senv->chroot)) {
390
@@ -1421,6 +1571,10 @@
391
       }
392
     }
393
 
394
+    if(senv->cgroup) {
395
+   	peruser_setup_cgroup(childnum, senv, pool);
396
+    }
397
+
398
     if (senv->uid == -1 && senv->gid == -1) {
399
         return unixd_setup_child();
400
     }
401
@@ -1594,15 +1748,6 @@
402
     {
403
         case CHILD_TYPE_MULTIPLEXER:
404
             _DBG("MULTIPLEXER %d", my_child_num);
405
-
406
-            /* update status on processors that are ready to accept requests */
407
-            _DBG("updating processor stati", 0);
408
-            for(i = 0; i < NUM_CHILDS; ++i)
409
-            {
410
-                if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY)
411
-                    CHILD_INFO_TABLE[i].status = CHILD_STATUS_ACTIVE;
412
-            }
413
-
414
             break;
415
 
416
         case CHILD_TYPE_PROCESSOR:
417
@@ -1626,7 +1771,7 @@
418
     apr_os_sock_put(&pod_sock, &fd, pconf);
419
     listen_add(pconf, pod_sock, check_pipe_of_death);
420
 
421
-    if(peruser_setup_child(my_child_num) != 0)
422
+    if(peruser_setup_child(my_child_num, pchild) != 0)
423
         clean_child_exit(APEXIT_CHILDFATAL);
424
 
425
     ap_run_child_init(pchild, ap_server_conf);
426
@@ -1670,14 +1815,19 @@
427
 	    clean_child_exit(0);
428
 	}
429
 
430
-	(void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL);
431
+        (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL);
432
+
433
+        CHILD_INFO_TABLE[my_child_num].status = CHILD_STATUS_READY;
434
+        _DBG("Child %d (%s) is now ready", my_child_num, child_type_string(CHILD_INFO_TABLE[my_child_num].type));
435
 
436
 	/*
437
 	 * Wait for an acceptable connection to arrive.
438
 	 */
439
 
440
-	/* Lock around "accept", if necessary */
441
-	SAFE_ACCEPT(accept_mutex_on());
442
+        /* Lock around "accept", if necessary */
443
+        if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER) {
444
+            SAFE_ACCEPT(accept_mutex_on());
445
+        }
446
 
447
         if (num_listensocks == 1) {
448
             offset = 0;
449
@@ -1729,18 +1879,27 @@
450
          * defer the exit
451
          */
452
         status = listensocks[offset].accept_func((void *)&sock, &listensocks[offset], ptrans);
453
-        SAFE_ACCEPT(accept_mutex_off()); 	/* unlock after "accept" */
454
+
455
+        if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER) {
456
+            SAFE_ACCEPT(accept_mutex_off()); 	/* unlock after "accept" */
457
+        }
458
 
459
         if (status == APR_EGENERAL) {
460
             /* resource shortage or should-not-occur occured */
461
             clean_child_exit(1);
462
         }
463
-        else if (status != APR_SUCCESS || die_now) {
464
+        else if (status != APR_SUCCESS || die_now || sock == NULL) {
465
             continue;
466
         }
467
 
468
+        if (CHILD_INFO_TABLE[my_child_num].status == CHILD_STATUS_READY) {
469
+            CHILD_INFO_TABLE[my_child_num].status = CHILD_STATUS_ACTIVE;
470
+            _DBG("Child %d (%s) is now active", my_child_num, child_type_string(CHILD_INFO_TABLE[my_child_num].type));
471
+        }
472
+
473
         if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_PROCESSOR ||
474
-            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER)
475
+            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER ||
476
+            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER)
477
         {
478
           _DBG("CHECKING IF WE SHOULD CLONE A CHILD...");
479
 
480
@@ -1754,8 +1913,11 @@
481
 
482
           if(total_processors(my_child_num) <
483
               CHILD_INFO_TABLE[my_child_num].senv->max_processors &&
484
-            idle_processors(my_child_num) <=
485
-              CHILD_INFO_TABLE[my_child_num].senv->min_free_processors)
486
+            (idle_processors(my_child_num) <=
487
+              CHILD_INFO_TABLE[my_child_num].senv->min_free_processors ||
488
+             total_processors(my_child_num) <
489
+              CHILD_INFO_TABLE[my_child_num].senv->min_processors
490
+            ))
491
           {
492
               _DBG("CLONING CHILD");
493
               child_clone();
494
@@ -1804,46 +1966,80 @@
495
     clean_child_exit(0);
496
 }
497
 
498
-static server_env_t* senv_add(int uid, int gid, const char* chroot)
499
-{
500
+static server_env_t* find_senv_by_name(const char *name) {
501
     int i;
502
-    int socks[2];
503
 
504
-    _DBG("Searching for matching senv...");
505
+    if (name == NULL) return NULL;
506
+
507
+    _DBG("name=%s", name);
508
+
509
+    for(i = 0; i < NUM_SENV; i++)
510
+      {
511
+          if(SENV[i].name != NULL && !strcmp(SENV[i].name, name)) {
512
+              return &SENV[i];
513
+          }
514
+      }
515
+
516
+    return NULL;
517
+}
518
+
519
+static server_env_t* find_matching_senv(server_env_t* senv) {
520
+    int i;
521
+
522
+    _DBG("name=%s uid=%d gid=%d chroot=%s", senv->name, senv->uid, senv->gid, senv->chroot);
523
 
524
     for(i = 0; i < NUM_SENV; i++)
525
-    {
526
-      if(SENV[i].uid == uid && SENV[i].gid == gid &&
527
-         (SENV[i].chroot == NULL || !strcmp(SENV[i].chroot, chroot)))
528
       {
529
-          _DBG("Found existing senv: %i", i);
530
-          return &SENV[i];
531
+          if((senv->name != NULL && SENV[i].name != NULL && !strcmp(SENV[i].name, senv->name)) ||
532
+             (senv->name == NULL && SENV[i].uid == senv->uid && SENV[i].gid == senv->gid &&
533
+              (
534
+               (SENV[i].chroot == NULL && senv->chroot == NULL) ||
535
+               ((SENV[i].chroot != NULL || senv->chroot != NULL) && !strcmp(SENV[i].chroot, senv->chroot)))
536
+              )
537
+             ) {
538
+              return &SENV[i];
539
+          }
540
       }
541
+
542
+    return NULL;
543
+}
544
+
545
+static server_env_t* senv_add(server_env_t *senv)
546
+{
547
+    int socks[2];
548
+    server_env_t *old_senv;
549
+
550
+    _DBG("Searching for matching senv...");
551
+
552
+    old_senv = find_matching_senv(senv);
553
+
554
+    if (old_senv) {
555
+        _DBG("Found existing senv");
556
+        senv = old_senv;
557
+        return old_senv;
558
     }
559
 
560
     if(NUM_SENV >= server_limit)
561
-    {
562
-      _DBG("server_limit reached!");
563
-      return NULL;
564
-    }
565
+      {
566
+          _DBG("server_limit reached!");
567
+          return NULL;
568
+      }
569
 
570
     _DBG("Creating new senv");
571
 
572
-    SENV[NUM_SENV].uid = uid;
573
-    SENV[NUM_SENV].gid = gid;
574
-    SENV[NUM_SENV].chroot = chroot;
575
-
576
-    SENV[NUM_SENV].min_processors = ap_min_processors;
577
-    SENV[NUM_SENV].min_free_processors = ap_min_free_processors;
578
-    SENV[NUM_SENV].max_processors = ap_max_processors;
579
+    memcpy(&SENV[NUM_SENV], senv, sizeof(server_env_t));
580
+
581
+    SENV[NUM_SENV].availability = 100;
582
 
583
     socketpair(PF_UNIX, SOCK_STREAM, 0, socks);
584
     SENV[NUM_SENV].input  = socks[0];
585
     SENV[NUM_SENV].output = socks[1];
586
 
587
+    senv = &SENV[NUM_SENV];
588
     return &SENV[server_env_image->control->num++];
589
 }
590
 
591
+
592
 static const char* child_clone()
593
 {
594
     int i;
595
@@ -1869,7 +2065,14 @@
596
     new = &CHILD_INFO_TABLE[i];
597
 
598
     new->senv = this->senv;
599
-    new->type = CHILD_TYPE_WORKER;
600
+
601
+    if (this->type == CHILD_TYPE_MULTIPLEXER) {
602
+        new->type = CHILD_TYPE_MULTIPLEXER;
603
+    }
604
+    else {
605
+        new->type = CHILD_TYPE_WORKER;
606
+    }
607
+
608
     new->sock_fd = this->sock_fd;
609
     new->status = CHILD_STATUS_STARTING;
610
 
611
@@ -1878,7 +2081,7 @@
612
 }
613
 
614
 static const char* child_add(int type, int status,
615
-                             apr_pool_t *pool, uid_t uid, gid_t gid, const char* chroot)
616
+                             apr_pool_t *pool, server_env_t *senv)
617
 {
618
     _DBG("adding child #%d", NUM_CHILDS);
619
 
620
@@ -1888,10 +2091,10 @@
621
                "Increase NumServers in your config file.";
622
     }
623
 
624
-       if (chroot && !ap_is_directory(pool, chroot))
625
-               return apr_psprintf(pool, "Error: chroot directory [%s] does not exist", chroot);
626
+       if (senv->chroot && !ap_is_directory(pool, senv->chroot))
627
+               return apr_psprintf(pool, "Error: chroot directory [%s] does not exist", senv->chroot);
628
 
629
-    CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(uid, gid, chroot);
630
+    CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(senv);
631
 
632
     if(CHILD_INFO_TABLE[NUM_CHILDS].senv == NULL)
633
     {
634
@@ -1907,10 +2110,10 @@
635
     CHILD_INFO_TABLE[NUM_CHILDS].status = status;
636
 
637
     _DBG("[%d] uid=%d gid=%d type=%d chroot=%s",
638
-         NUM_CHILDS, uid, gid, type,
639
-         chroot);
640
+         NUM_CHILDS, senv->uid, senv->gid, type,
641
+         senv->chroot);
642
 
643
-    if (uid == 0 || gid == 0)
644
+    if (senv->uid == 0 || senv->gid == 0)
645
     {
646
         _DBG("Assigning root user/group to a child.", 0);
647
     }
648
@@ -1957,7 +2160,7 @@
649
     (void) ap_update_child_status_from_indexes(slot, 0, SERVER_STARTING,
650
                                                (request_rec *) NULL);
651
 
652
-    CHILD_INFO_TABLE[slot].status = CHILD_STATUS_ACTIVE;
653
+    CHILD_INFO_TABLE[slot].status = CHILD_STATUS_READY;
654
 
655
 
656
 #ifdef _OSD_POSIX
657
@@ -2062,19 +2265,31 @@
658
         if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_STARTING)
659
           make_child(ap_server_conf, i);
660
       }
661
-      else if(((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || 
662
-               CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER)  &&
663
-               ap_scoreboard_image->parent[i].pid > 1) &&
664
-               (idle_processors (i) > 1 || total_processes (i) == 1) && (
665
-                   (expire_timeout > 0 &&  ap_scoreboard_image->servers[i][0].status != SERVER_DEAD && 
666
-                   apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > expire_timeout) ||
667
-                   (idle_timeout >   0 &&  ap_scoreboard_image->servers[i][0].status == SERVER_READY &&  
668
-                   apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout)))
669
+      else if(
670
+    	      (((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR ||
671
+                 CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER)  &&
672
+                ap_scoreboard_image->parent[i].pid > 1) &&
673
+               (idle_processors (i) > CHILD_INFO_TABLE[i].senv->min_free_processors || CHILD_INFO_TABLE[i].senv->min_free_processors == 0) &&
674
+               total_processes (i) > CHILD_INFO_TABLE[i].senv->min_processors && 
675
+               (
676
+                (expire_timeout > 0 &&  ap_scoreboard_image->servers[i][0].status != SERVER_DEAD && 
677
+                 apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > expire_timeout) ||
678
+                (idle_timeout >   0 &&  ap_scoreboard_image->servers[i][0].status == SERVER_READY &&  
679
+                 apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout) ||
680
+                (CHILD_INFO_TABLE[i].senv->max_free_processors > 0 && CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY &&
681
+                 idle_processors(i) > CHILD_INFO_TABLE[i].senv->max_free_processors))
682
+               )
683
+              || (CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER &&
684
+                  (multiplexer_idle_timeout > 0 && ap_scoreboard_image->servers[i][0].status == SERVER_READY &&
685
+                   apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > multiplexer_idle_timeout) &&
686
+                  total_processors(i) > CHILD_INFO_TABLE[i].senv->min_processors
687
+                  )
688
+            )
689
       {
690
         CHILD_INFO_TABLE[i].pid = 0;
691
         CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY;
692
 
693
-        if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER)
694
+        if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER || CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER)
695
         {
696
           /* completely free up this slot */
697
 
698
@@ -2173,7 +2388,6 @@
699
         return 1;
700
     }
701
 
702
-#if 0
703
 #if APR_USE_SYSVSEM_SERIALIZE
704
     if (ap_accept_lock_mech == APR_LOCK_DEFAULT || 
705
         ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
706
@@ -2189,7 +2403,6 @@
707
             return 1;
708
         }
709
     }
710
-#endif
711
 
712
     if (!is_graceful) {
713
         if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
714
@@ -2598,7 +2811,10 @@
715
     ap_listen_pre_config();
716
     ap_min_processors = DEFAULT_MIN_PROCESSORS;
717
     ap_min_free_processors = DEFAULT_MIN_FREE_PROCESSORS;
718
+    ap_max_free_processors = DEFAULT_MAX_FREE_PROCESSORS;
719
     ap_max_processors = DEFAULT_MAX_PROCESSORS;
720
+    ap_min_multiplexers = DEFAULT_MIN_MULTIPLEXERS;
721
+    ap_max_multiplexers = DEFAULT_MAX_MULTIPLEXERS;
722
     ap_daemons_limit = server_limit;
723
     ap_pid_fname = DEFAULT_PIDLOG;
724
     ap_lock_fname = DEFAULT_LOCKFILE;
725
@@ -2608,6 +2824,13 @@
726
 	ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
727
 #endif
728
 
729
+    expire_timeout = DEFAULT_EXPIRE_TIMEOUT;
730
+    idle_timeout = DEFAULT_IDLE_TIMEOUT;
731
+    multiplexer_idle_timeout = DEFAULT_MULTIPLEXER_IDLE_TIMEOUT;
732
+    processor_wait_timeout = DEFAULT_PROCESSOR_WAIT_TIMEOUT;
733
+    processor_wait_steps = DEFAULT_PROCESSOR_WAIT_STEPS;
734
+
735
+
736
     apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
737
 
738
     /* we need to know ServerLimit and ThreadLimit before we start processing
739
@@ -2709,11 +2932,13 @@
740
         server_env_image->control = (server_env_control*)shmem;
741
         shmem += sizeof(server_env_control*);
742
         server_env_image->table = (server_env_t*)shmem;
743
+    }
744
 
745
+    if(restart_num <= 2) {
746
+        _DBG("Cleaning server environments table");
747
+    
748
         server_env_image->control->num = 0;
749
-
750
-        for (i = 0; i < tmp_server_limit; i++)
751
-        {
752
+        for (i = 0; i < tmp_server_limit; i++) {
753
             SENV[i].processor_id = -1;
754
             SENV[i].uid          = -1;
755
             SENV[i].gid          = -1;
756
@@ -2781,8 +3006,8 @@
757
                 if (pass_request(r, processor) == -1)
758
                 {
759
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0,
760
-                             ap_server_conf, "Could not pass request to proper "                             "child, request will not be honoured.");
761
-                    return DECLINED;
762
+                                 ap_server_conf, "Could not pass request to processor %s (virtualhost %s), request will not be honoured.",
763
+                                 processor->senv->name, r->hostname);
764
                 }
765
                 _DBG("doing longjmp",0);
766
                 longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1);
767
@@ -2859,32 +3084,37 @@
768
     ap_rputs("<hr>\n", r);
769
     ap_rputs("<h2>peruser status</h2>\n", r);
770
     ap_rputs("<table border=\"0\">\n", r);
771
-    ap_rputs("<tr><td>ID</td><td>PID</td><td>STATUS</td><td>TYPE</td><td>UID</td>"
772
-                   "<td>GID</td><td>CHROOT</td><td>INPUT</td>"
773
+    ap_rputs("<tr><td>ID</td><td>PID</td><td>STATUS</td><td>SB STATUS</td><td>TYPE</td><td>UID</td>"
774
+                   "<td>GID</td><td>CHROOT</td><td>NICE</td><td>INPUT</td>"
775
                    "<td>OUTPUT</td><td>SOCK_FD</td>"
776
                    "<td>TOTAL PROCESSORS</td><td>MAX PROCESSORS</td>"
777
-                   "<td>IDLE PROCESSORS</td><td>MIN FREE PROCESSORS</td></tr>\n", r);
778
+                   "<td>IDLE PROCESSORS</td><td>MIN FREE PROCESSORS</td>"
779
+                   "<td>AVAIL</td>"
780
+                   "</tr>\n", r);
781
     for (x = 0; x < NUM_CHILDS; x++)
782
         {
783
         senv = CHILD_INFO_TABLE[x].senv;
784
-        ap_rprintf(r, "<tr><td>%3d</td><td>%5d</td><td>%8s</td><td>%12s</td>"
785
-                       "<td>%4d</td><td>%4d</td><td>%25s</td><td>%5d</td>"
786
+        ap_rprintf(r, "<tr><td>%3d</td><td>%5d</td><td>%8s</td><td>%8s</td><td>%12s</td>"
787
+                       "<td>%4d</td><td>%4d</td><td>%25s</td><td>%3d</td><td>%5d</td>"
788
                        "<td>%6d</td><td>%7d</td><td>%d</td><td>%d</td>"
789
-                       "<td>%d</td><td>%d</td></tr>\n", 
790
+                       "<td>%d</td><td>%d</td><td>%3d</td></tr>\n",
791
                        CHILD_INFO_TABLE[x].id, 
792
                        CHILD_INFO_TABLE[x].pid, 
793
                        child_status_string(CHILD_INFO_TABLE[x].status), 
794
+                       scoreboard_status_string(SCOREBOARD_STATUS(x)),
795
                        child_type_string(CHILD_INFO_TABLE[x].type), 
796
                        senv == NULL ? -1 : senv->uid, 
797
                        senv == NULL ? -1 : senv->gid, 
798
                        senv == NULL ? NULL : senv->chroot, 
799
+                       senv == NULL ? 0 : senv->nice_lvl,
800
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, 
801
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, 
802
                        CHILD_INFO_TABLE[x].sock_fd,
803
                        total_processors(x), 
804
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->max_processors,
805
                        idle_processors(x),
806
-                       senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors
807
+                       senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors,
808
+                       senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->availability
809
                        );
810
        }
811
     ap_rputs("</table>\n", r);
812
@@ -2938,50 +3168,183 @@
813
     APR_OPTIONAL_HOOK(ap, status_hook, peruser_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
814
 }
815
 
816
-/* we define an Processor w/ specific uid/gid */
817
-static const char *cf_Processor(cmd_parms *cmd, void *dummy,
818
-    const char *user_name, const char *group_name, const char *chroot)
819
+static const char *cf_Processor(cmd_parms *cmd, void *dummy, const char *arg)
820
 {
821
-    uid_t uid = ap_uname2id(user_name);
822
-    gid_t gid = ap_gname2id(group_name);
823
+    const char *user_name = NULL, *group_name = NULL, *directive;
824
+    server_env_t senv;
825
+    ap_directive_t *current;
826
+
827
+    const char *endp = ap_strrchr_c(arg, '>');
828
+
829
+    if (endp == NULL) {
830
+	return apr_psprintf(cmd->temp_pool,
831
+			    "Error: Directive %s> missing closing '>'", cmd->cmd->name);
832
+    }
833
+
834
+    arg = apr_pstrndup(cmd->pool, arg, endp - arg);
835
+
836
+    if (!arg) {
837
+   	return apr_psprintf(cmd->temp_pool,
838
+                            "Error: %s> must specify a processor name", cmd->cmd->name);
839
+    }
840
+
841
+    senv.name = ap_getword_conf(cmd->pool, &arg);
842
+    _DBG("processor_name: %s", senv.name);
843
+
844
+    if (strlen(senv.name) == 0) {
845
+        return apr_psprintf(cmd->temp_pool,
846
+                            "Error: Directive %s> takes one argument", cmd->cmd->name);
847
+    }
848
+
849
+    /*	Check for existing processors on first launch and between gracefuls */
850
+    if (restart_num == 1 || is_graceful) {
851
+        server_env_t *old_senv = find_senv_by_name(senv.name);
852
+
853
+        if (old_senv) {
854
+            return apr_psprintf(cmd->temp_pool,
855
+                                "Error: Processor %s already defined", senv.name);
856
+        }
857
+    }
858
+
859
+    senv.nice_lvl 		= 0;
860
+    senv.chroot 		= NULL;
861
+    senv.cgroup			= NULL;
862
+    senv.min_processors 	= ap_min_processors;
863
+    senv.min_free_processors 	= ap_min_free_processors;
864
+    senv.max_free_processors    = ap_max_free_processors;
865
+    senv.max_processors 	= ap_max_processors;
866
+
867
+    current = cmd->directive->first_child;
868
+
869
+    int proc_temp = 0;
870
+    for(; current != NULL; current = current->next) {
871
+        directive = current->directive;
872
+        
873
+        if (!strcasecmp(directive, "user")) {
874
+            user_name = current->args;
875
+        }
876
+        else if (!strcasecmp(directive, "group")) {
877
+   	    group_name = current->args;
878
+        }
879
+        else if (!strcasecmp(directive, "chroot")) {
880
+            senv.chroot = ap_getword_conf(cmd->pool, &current->args);
881
+        }
882
+        else if (!strcasecmp(directive, "nicelevel")) {
883
+    	    senv.nice_lvl = atoi(current->args);
884
+        }
885
+        else if (!strcasecmp(directive, "maxprocessors")) {
886
+            proc_temp = atoi(current->args);
887
+
888
+            if (proc_temp < 1) {
889
+                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
890
+                             "WARNING: Require MaxProcessors > 0, setting to 1");
891
+                proc_temp = 1;
892
+            }
893
+
894
+            senv.max_processors = proc_temp;
895
+        }
896
+        else if (!strcasecmp(directive, "minprocessors")) {
897
+            proc_temp = atoi(current->args);
898
+
899
+            if (proc_temp < 0) {
900
+                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
901
+                             "WARNING: Require MinProcessors >= 0, setting to 0");
902
+                proc_temp = 0;
903
+            }
904
+
905
+            senv.min_processors = proc_temp;
906
+        }
907
+        else if (!strcasecmp(directive, "minspareprocessors")) {
908
+            proc_temp = atoi(current->args);
909
 
910
-    _DBG("user=%s:%d group=%s:%d chroot=%s",
911
-        user_name, uid, group_name, gid, chroot);
912
+            if (proc_temp < 0) {
913
+                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
914
+                             "WARNING: Require MinSpareProcessors >= 0, setting to 0");
915
+                proc_temp = 0;
916
+            }
917
+
918
+            senv.min_free_processors = proc_temp;
919
+        }
920
+        else if (!strcasecmp(directive, "maxspareprocessors")) {
921
+            proc_temp = atoi(current->args);
922
+            
923
+            if (proc_temp < 0) {
924
+                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
925
+                             "WARNING: Require MaxSpareProcessors >= 0, setting to 0");
926
+                proc_temp = 0;
927
+            }
928
+
929
+            senv.max_free_processors = proc_temp;
930
+        }
931
+        else if (!strcasecmp(directive, "cgroup")) {
932
+            senv.cgroup = ap_getword_conf(cmd->pool, &current->args);
933
+        }
934
+        else {
935
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
936
+                         "Unknown directive %s in %s>", directive, cmd->cmd->name);
937
+        }
938
+    }
939
+
940
+    if (user_name == NULL || group_name == NULL) {
941
+        return apr_psprintf(cmd->temp_pool,
942
+                            "Error: User or Group must be set in %s>", cmd->cmd->name);
943
+    }
944
+
945
+    senv.uid = ap_uname2id(user_name);
946
+    senv.gid = ap_gname2id(group_name);
947
+
948
+    _DBG("name=%s user=%s:%d group=%s:%d chroot=%s nice_lvl=%d",
949
+         senv.name, user_name, senv.uid, group_name, senv.gid, senv.chroot, senv.nice_lvl);
950
+
951
+    _DBG("min_processors=%d min_free_processors=%d max_spare_processors=%d max_processors=%d",
952
+         senv.min_processors, senv.min_free_processors, senv.max_free_processors, senv.max_processors);
953
 
954
     return child_add(CHILD_TYPE_PROCESSOR, CHILD_STATUS_STANDBY,
955
-                     cmd->pool, uid, gid, chroot);
956
+                     cmd->pool, &senv);
957
 }
958
 
959
 /* we define an Multiplexer child w/ specific uid/gid */
960
 static const char *cf_Multiplexer(cmd_parms *cmd, void *dummy,
961
     const char *user_name, const char *group_name, const char *chroot)
962
 {
963
-    uid_t uid = ap_uname2id(user_name);
964
-    gid_t gid = ap_gname2id(group_name);
965
+    server_env_t senv;
966
+
967
+    senv.name 		= NULL;
968
+
969
+    senv.uid 		= ap_uname2id(user_name);
970
+    senv.gid 		= ap_gname2id(group_name);
971
+    senv.nice_lvl 	= 0;
972
+    senv.cgroup		= NULL;
973
+    senv.chroot         = chroot;
974
+
975
+    senv.min_processors 	= ap_min_multiplexers;
976
+    senv.min_free_processors 	= ap_min_free_processors;
977
+    senv.max_free_processors    = ap_max_free_processors;
978
+    senv.max_processors 	= ap_max_multiplexers;
979
 
980
     _DBG("user=%s:%d group=%s:%d chroot=%s [multiplexer id %d]",
981
-        user_name, uid, group_name, gid, chroot, NUM_CHILDS);
982
+        user_name, senv.uid, group_name, senv.gid, senv.chroot, NUM_CHILDS);
983
 
984
     return child_add(CHILD_TYPE_MULTIPLEXER, CHILD_STATUS_STARTING,
985
-                     cmd->pool, uid, gid, chroot);
986
+                     cmd->pool, &senv);
987
 }
988
 
989
 static const char* cf_ServerEnvironment(cmd_parms *cmd, void *dummy,
990
-    const char *user_name, const char *group_name, const char *chroot)
991
+    const char *name)
992
 {
993
-    int uid = ap_uname2id(user_name);
994
-    int gid = ap_gname2id(group_name);
995
     peruser_server_conf *sconf = PERUSER_SERVER_CONF(cmd->server->module_config);
996
 
997
     _DBG("function entered", 0);
998
 
999
-       if (chroot && !ap_is_directory(cmd->pool, chroot))
1000
-               return apr_psprintf(cmd->pool, "Error: chroot directory [%s] does not exist", chroot);
1001
+    sconf->senv = find_senv_by_name(name);
1002
 
1003
-    sconf->senv = senv_add(uid, gid, chroot);
1004
+    if (sconf->senv == NULL) {
1005
+        return apr_psprintf(cmd->pool,
1006
+                            "Error: Processor %s not defined", name);
1007
+    }
1008
 
1009
-    _DBG("user=%s:%d group=%s:%d chroot=%s numchilds=%d",
1010
-        user_name, uid, group_name, gid, chroot, NUM_CHILDS);
1011
+    _DBG("user=%d group=%d chroot=%s numchilds=%d",
1012
+        sconf->senv->uid, sconf->senv->gid, sconf->senv->chroot, NUM_CHILDS);
1013
 
1014
     return NULL;
1015
 }
1016
@@ -3046,10 +3409,10 @@
1017
 
1018
     min_procs = atoi(arg);
1019
 
1020
-    if (min_procs < 1) {
1021
+    if (min_procs < 0) {
1022
         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1023
-                     "WARNING: Require MaxProcessors > 0, setting to 1");
1024
-        min_procs = 1;
1025
+                     "WARNING: Require MinProcessors >= 0, setting to 0");
1026
+        min_procs = 0;
1027
     }
1028
 
1029
     if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) {
1030
@@ -3075,10 +3438,10 @@
1031
 
1032
     min_free_procs = atoi(arg);
1033
 
1034
-    if (min_free_procs < 1) {
1035
+    if (min_free_procs < 0) {
1036
         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1037
-                     "WARNING: Require MinSpareProcessors > 0, setting to 1");
1038
-        min_free_procs = 1;
1039
+                     "WARNING: Require MinSpareProcessors >= 0, setting to 0");
1040
+        min_free_procs = 0;
1041
     }
1042
 
1043
     if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) {
1044
@@ -3092,6 +3455,35 @@
1045
     return NULL;
1046
 }
1047
 
1048
+static const char *set_max_free_processors (cmd_parms *cmd, void *dummy, const char *arg)
1049
+{
1050
+     peruser_server_conf *sconf;
1051
+     int max_free_procs;
1052
+     const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
1053
+
1054
+     if (err != NULL) {
1055
+         return err;
1056
+     }
1057
+
1058
+     max_free_procs = atoi(arg);
1059
+
1060
+     if (max_free_procs < 0) {
1061
+         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1062
+                      "WARNING: Require MaxSpareProcessors >= 0, setting to 0");
1063
+         max_free_procs = 0;
1064
+     }
1065
+
1066
+     if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) {
1067
+         sconf = PERUSER_SERVER_CONF(cmd->server->module_config);
1068
+         sconf->senv->max_free_processors = max_free_procs;
1069
+     }
1070
+     else {
1071
+         ap_max_free_processors = max_free_procs;
1072
+     }
1073
+
1074
+     return NULL;
1075
+}
1076
+
1077
 static const char *set_max_processors (cmd_parms *cmd, void *dummy, const char *arg)
1078
 {
1079
     peruser_server_conf *sconf;
1080
@@ -3121,6 +3513,50 @@
1081
     return NULL;
1082
 }
1083
 
1084
+static const char *set_min_multiplexers (cmd_parms *cmd, void *dummy, const char *arg)
1085
+{
1086
+    int min_multiplexers;
1087
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
1088
+
1089
+    if (err != NULL) {
1090
+        return err;
1091
+    }
1092
+
1093
+    min_multiplexers = atoi(arg);
1094
+
1095
+    if (min_multiplexers < 1) {
1096
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1097
+                     "WARNING: Require MinMultiplexers > 0, setting to 1");
1098
+        min_multiplexers = 1;
1099
+    }
1100
+
1101
+    ap_min_multiplexers = min_multiplexers;
1102
+
1103
+    return NULL;
1104
+}
1105
+
1106
+static const char *set_max_multiplexers (cmd_parms *cmd, void *dummy, const char *arg)
1107
+{
1108
+    int max_multiplexers;
1109
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
1110
+
1111
+    if (err != NULL) {
1112
+        return err;
1113
+    }
1114
+
1115
+    max_multiplexers = atoi(arg);
1116
+
1117
+    if (max_multiplexers < 1) {
1118
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1119
+                     "WARNING: Require MaxMultiplexers > 0, setting to 1");
1120
+        max_multiplexers = 1;
1121
+    }
1122
+
1123
+    ap_max_multiplexers = max_multiplexers;
1124
+
1125
+    return NULL;
1126
+}
1127
+
1128
 static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *arg) 
1129
 {
1130
     int tmp_server_limit;
1131
@@ -3183,6 +3619,42 @@
1132
     return NULL;
1133
 }
1134
 
1135
+static const char *set_multiplexer_idle_timeout (cmd_parms *cmd, void *dummy, const char *arg) {
1136
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1137
+
1138
+    if (err != NULL) {
1139
+        return err;
1140
+    }
1141
+
1142
+    multiplexer_idle_timeout = atoi(arg);
1143
+
1144
+    return NULL;
1145
+}
1146
+
1147
+static const char *set_processor_wait_timeout (cmd_parms *cmd, void *dummy, const char *timeout, const char *steps) {
1148
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1149
+    
1150
+    if (err != NULL) {
1151
+        return err;
1152
+    }
1153
+
1154
+    processor_wait_timeout = atoi(timeout);
1155
+
1156
+    if (steps != NULL) {
1157
+        int steps_tmp = atoi(steps);
1158
+
1159
+        if (steps_tmp < 1) {
1160
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1161
+                         "WARNING: Require ProcessorWaitTimeout steps > 0, setting to 1");
1162
+            steps_tmp = 1;
1163
+        }
1164
+
1165
+        processor_wait_steps = steps_tmp;
1166
+    }
1167
+
1168
+    return NULL;
1169
+}
1170
+
1171
 static const command_rec peruser_cmds[] = {
1172
 UNIX_DAEMON_COMMANDS,
1173
 LISTEN_COMMANDS,
1174
@@ -3190,23 +3662,33 @@
1175
               "Minimum number of idle children, to handle request spikes"),
1176
 AP_INIT_TAKE1("MinSpareServers", set_min_free_servers, NULL, RSRC_CONF,
1177
               "Minimum number of idle children, to handle request spikes"),
1178
+AP_INIT_TAKE1("MaxSpareProcessors", set_max_free_processors, NULL, RSRC_CONF,
1179
+              "Maximum number of idle children, 0 to disable"),
1180
 AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF,
1181
               "Maximum number of children alive at the same time"),
1182
 AP_INIT_TAKE1("MinProcessors", set_min_processors, NULL, RSRC_CONF,
1183
               "Minimum number of processors per vhost"),
1184
 AP_INIT_TAKE1("MaxProcessors", set_max_processors, NULL, RSRC_CONF,
1185
               "Maximum number of processors per vhost"),
1186
+AP_INIT_TAKE1("MinMultiplexers", set_min_multiplexers, NULL, RSRC_CONF,
1187
+              "Minimum number of multiplexers the server can have"),
1188
+AP_INIT_TAKE1("MaxMultiplexers", set_max_multiplexers, NULL, RSRC_CONF,
1189
+              "Maximum number of multiplexers the server can have"),
1190
 AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
1191
               "Maximum value of MaxClients for this run of Apache"),
1192
 AP_INIT_TAKE1("ExpireTimeout", set_expire_timeout, NULL, RSRC_CONF,
1193
-              "Maximum idle time before a child is killed, 0 to disable"),
1194
+              "Maximum time a child can live, 0 to disable"),
1195
 AP_INIT_TAKE1("IdleTimeout", set_idle_timeout, NULL, RSRC_CONF,
1196
               "Maximum time before a child is killed after being idle, 0 to disable"),
1197
+AP_INIT_TAKE1("MultiplexerIdleTimeout", set_multiplexer_idle_timeout, NULL, RSRC_CONF,
1198
+              "Maximum time before a multiplexer is killed after being idle, 0 to disable"),
1199
+AP_INIT_TAKE12("ProcessorWaitTimeout", set_processor_wait_timeout, NULL, RSRC_CONF,
1200
+              "Maximum time a multiplexer waits for the processor if it is busy"),
1201
 AP_INIT_TAKE23("Multiplexer", cf_Multiplexer, NULL, RSRC_CONF,
1202
               "Specify an Multiplexer Child configuration."),
1203
-AP_INIT_TAKE23("Processor", cf_Processor, NULL, RSRC_CONF,
1204
-              "Specify a User and Group for a specific child process."),
1205
-AP_INIT_TAKE23("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF,
1206
+AP_INIT_RAW_ARGS("<Processor", cf_Processor, NULL, RSRC_CONF,
1207
+              "Specify settings for processor."),
1208
+AP_INIT_TAKE1("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF,
1209
               "Specify the server environment for this virtual host."),
1210
 { NULL }
1211
 };

Return to bug 135078