Greetings From DataCode Team!

This our update patch for peruser 0.3.0. Praise and criticism is welcome.

Features it provides:
* Multiplexer pool: Multiplexer are spawned automatically if more of them are 
needed. No need to
specify duplicate Multiplexer configuration lines anymore. If you do that, then 
apache starts with
more multiplexers, but maintenance will clean them up in a while.

Configuration variables:
MinMultiplexers 3 #minimum amount of multiplexer that will be kept
alive. Defaults to 3 if unset.
MaxMultiplexers 20 #maximum amount of multiplexer that will be allowed
to spawn. Defaults to 20 if unset.


* Multiplexer now checks if the processor has any available children and will 
try to wait for it, if
it doesn't. If the multiplexer is not able to pass the request within given 
time then the request
will be dropped. With each failed pass, the multiplexer will wait less time 
(and eventually it won't
wait at all).
This should protect the multiplexer(s) from hanging if someone is hammering the 
processor with
number of requests it can't handle.
The multiplexer should actually return an error page if it can't pass the 
request , but I haven't
found a way to do that without locking up the multiplexer.
I've also added AVAIL property to the server status page, which shows how 
available the processor
has been to the multiplexer with last requests (100 = ok, 0 = not available at 
all).

Configuration variables:
ProcessorWaitTimeout 2 10 # First argument defines the maximum time multiplexer 
waits for the
processor (defaults to 2 seconds), second argument defines the number of steps 
between maximum
waiting time and no waiting time (defaults to 10 and is optional).


* Processor server environment configuration directive has been changed. We 
added this style of
directive because of new variables coming in the future that need to be 
specified per processor (eg.
nice level). This addition also allows us to set further possible restrictions 
to specific
processors (cgroups, memory limit?). If people would really like to use the old 
style, then I can
supply with a modified version of this patch (which then also lacks the feature 
of nice level).

Example processor configuration:
<Processor [unique string processor identifier]>
   User bob # Processor user
   Group  bob # Processor group
   Chroot /home/bob # Processor chroot (optional)

   # Optional nice level, this is relative to main httpd process's nice level
   NiceLevel   0

   # Additional server environment variables can be overriden here
   MaxProcessors       10  # total number of processors at the same time
   MinSpareProcessors  3   # how many idle processors to keep alive (to handle 
request spikes)
   MinProcessors       0   # minimum processors that will always be kept alive 
(idle or working).
</Processor>

In <VirtualHost> you must use "ServerEnvironment [processor identifier]". The 
MaxProcessors,
MinSpareProcessors and MinProcessors directives should still work within 
<VirtualHost> directive.


* Server status now displays the child scoreboard status. This is more useful 
for debugging.


* Bugs fixed:
- MinProcessors and MinSpareProcessors should now work correctly.
- Multiple definitions of Processors with same senv is not allowed anymore.
- Fixed "Could not pass request to proper child, request will not be honoured." 
error hanging the
multiplexer for 10-15 seconds.
- Fixed IdleTimeout and ExpireTimeout not set to default if it was removed from 
configuration and
graceful was used
- Fixed invalid error message for MinProcessors (stating that the MaxProcessors 
directive is invalid)

Note: As always, this is experimental. Use at your own risk. For information we are running this on production environment for 2 hours now. No sparks so far. Everyone can make their conclusions from there.

--
Taavi Sannik
DataCode OÜ


--- httpd-2.2.2-old/server/mpm/experimental/peruser/peruser.c   2008-05-16 
16:10:21.000000000 +0300
+++ httpd-2.2.2-new/server/mpm/experimental/peruser/peruser.c   2008-05-18 
21:43:03.000000000 +0300
@@ -206,9 +206,14 @@
 static int ap_min_processors=DEFAULT_MIN_PROCESSORS;
 static int ap_min_free_processors=DEFAULT_MIN_FREE_PROCESSORS;
 static int ap_max_processors=DEFAULT_MAX_PROCESSORS;
+static int ap_min_multiplexers=DEFAULT_MIN_MULTIPLEXERS;
+static int ap_max_multiplexers=DEFAULT_MAX_MULTIPLEXERS;
 static int ap_daemons_limit=0;      /* MaxClients */
-static int expire_timeout=1800;
-static int idle_timeout=900;
+static int expire_timeout=DEFAULT_EXPIRE_TIMEOUT;
+static int idle_timeout=DEFAULT_IDLE_TIMEOUT;
+static int multiplexer_idle_timeout=DEFAULT_MULTIPLEXER_IDLE_TIMEOUT;
+static int processor_wait_timeout=DEFAULT_PROCESSOR_WAIT_TIMEOUT;
+static int processor_wait_steps=DEFAULT_PROCESSOR_WAIT_STEPS;
 static int server_limit = DEFAULT_SERVER_LIMIT;
 static int first_server_limit;
 static int changed_limit_at_restart;
@@ -221,16 +226,20 @@
 typedef struct
 {
     int processor_id;
-
+    
+    const char *name;  /* Server environment's unique string identifier */
+    
     /* security settings */
     uid_t uid;          /* user id */
     gid_t gid;          /* group id */
     const char *chroot; /* directory to chroot() to, can be null */
+    int nice_lvl;
 
     /* resource settings */
     int min_processors;
     int min_free_processors;
     int max_processors;
+    int availability;
 
     /* sockets */
     int input;          /* The socket descriptor */
@@ -437,6 +446,25 @@
     return "UNKNOWN";
 }
 
+char* scoreboard_status_string(int status) {
+    switch(status)
+    {
+        case SERVER_DEAD:  return "DEAD";
+        case SERVER_STARTING: return "STARTING";
+        case SERVER_READY:    return "READY";
+        case SERVER_BUSY_READ:   return "BUSY_READ";
+        case SERVER_BUSY_WRITE:   return "BUSY_WRITE";
+        case SERVER_BUSY_KEEPALIVE:   return "BUSY_KEEPALIVE";
+        case SERVER_BUSY_LOG:   return "BUSY_LOG";
+        case SERVER_BUSY_DNS:   return "BUSY_DNS";
+        case SERVER_CLOSING:   return "CLOSING";
+        case SERVER_GRACEFUL:   return "GRACEFUL";
+        case SERVER_NUM_STATUS:   return "NUM_STATUS";
+    }
+
+    return "UNKNOWN";    
+}
+
 void dump_child_table()
 {
 #ifdef MPM_PERUSER_DEBUG
@@ -1116,7 +1144,7 @@
     apr_bucket *bucket;
     const apr_array_header_t *headers_in_array;
     const apr_table_entry_t *headers_in;
-    int counter;
+    int counter, wait_time, wait_step_size;
 
     apr_socket_t *thesock = ap_get_module_config(r->connection->conn_config, 
&core_module);
 
@@ -1137,6 +1165,63 @@
       apr_table_get(r->headers_in, "Host"), my_child_num, 
processor->senv->output);
     _DBG("r->the_request=\"%s\" len=%d", r->the_request, 
strlen(r->the_request));
 
+    wait_step_size = 100 / processor_wait_steps;
+
+    /* Check if the processor is available */
+    if (total_processors(processor->id) == processor->senv->max_processors &&
+           idle_processors(processor->id) == 0) {
+       /* The processor is currently busy, try to wait (a little) */
+       _DBG("processor seems to be busy, trying to wait for it");
+
+       if (processor->senv->availability == 0) {
+           processor->senv->availability = 0;
+               
+           _DBG("processor is very busy (availability = 0) - not passing 
request");
+           /* No point in waiting for the processor, it's very busy */
+           return -1;
+       }
+       
+       /* We sleep a little (depending how available the processor usually is) 
*/
+       int i;
+
+       wait_time = (processor_wait_timeout / processor_wait_steps) * 1000000;
+       
+       for(i = 0; i <= processor->senv->availability; i += wait_step_size) {
+           usleep(wait_time);
+
+           /* Check if the processor is ready */
+           if (total_processors(processor->id) < 
processor->senv->max_processors ||
+               idle_processors(processor->id) > 0) {
+               /* The processor has freed - lets use it */
+               _DBG("processor freed before wait time expired");
+               break;
+           }
+       }
+
+       if (processor->senv->availability <= wait_step_size) {
+           processor->senv->availability = 0;
+       }
+       else processor->senv->availability -= wait_step_size;
+       
+       /* Check if we waited all the time */
+       if (i > processor->senv->availability) {
+           _DBG("processor is busy - not passing request (availability = %d)",
+                   processor->senv->availability);
+           return -1;
+       }
+
+       /* We could increase the availability a little here,
+        * because the processor got freed eventually
+        */
+    }
+    else {
+       /* Smoothly increment the availability back to 100 */
+       if (processor->senv->availability >= 100-wait_step_size) {
+           processor->senv->availability = 100;
+       }
+       else processor->senv->availability += wait_step_size;
+    }
+
     ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, 
APR_NONBLOCK_READ, len);
 
     /* Scan the brigade looking for heap-buckets */
@@ -1402,6 +1487,10 @@
 static int peruser_setup_child(int childnum)
 {
     server_env_t *senv = CHILD_INFO_TABLE[childnum].senv;
+    
+    if (senv->nice_lvl != 0) {
+       nice(senv->nice_lvl);
+    }
 
     if(senv->chroot) {
       _DBG("chdir to %s", senv->chroot);
@@ -1599,7 +1688,8 @@
             _DBG("updating processor stati", 0);
             for(i = 0; i < NUM_CHILDS; ++i)
             {
-                if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY)
+                if(CHILD_INFO_TABLE[i].type != CHILD_TYPE_MULTIPLEXER &&
+                       CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY)
                     CHILD_INFO_TABLE[i].status = CHILD_STATUS_ACTIVE;
             }
 
@@ -1740,7 +1830,8 @@
         }
 
         if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_PROCESSOR ||
-            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER)
+            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER ||
+            CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER)
         {
           _DBG("CHECKING IF WE SHOULD CLONE A CHILD...");
 
@@ -1752,10 +1843,14 @@
             idle_processors(my_child_num),
             CHILD_INFO_TABLE[my_child_num].senv->min_free_processors);
 
-          if(total_processors(my_child_num) <
+          if(
+            total_processors(my_child_num) <
               CHILD_INFO_TABLE[my_child_num].senv->max_processors &&
-            idle_processors(my_child_num) <=
-              CHILD_INFO_TABLE[my_child_num].senv->min_free_processors)
+            (idle_processors(my_child_num) <
+              CHILD_INFO_TABLE[my_child_num].senv->min_free_processors ||
+             total_processors(my_child_num) <
+              CHILD_INFO_TABLE[my_child_num].senv->min_processors 
+            ))
           {
               _DBG("CLONING CHILD");
               child_clone();
@@ -1804,22 +1899,55 @@
     clean_child_exit(0);
 }
 
-static server_env_t* senv_add(int uid, int gid, const char* chroot)
-{
+static server_env_t* find_senv_by_name(const char *name) {
     int i;
-    int socks[2];
+    
+    if (name == NULL) return NULL;
+    
+    _DBG("name=%s", name);
 
-    _DBG("Searching for matching senv...");
+    for(i = 0; i < NUM_SENV; i++)
+    {
+      if(SENV[i].name != NULL && !strcmp(SENV[i].name, name)) {
+          return &SENV[i];
+      }
+    }
+    
+    return NULL;
+}
+
+static server_env_t* find_matching_senv(server_env_t* senv) {
+    int i;
+
+    _DBG("name=%s uid=%d gid=%d chroot=%s", senv->name, senv->uid, senv->gid, 
senv->chroot);
 
     for(i = 0; i < NUM_SENV; i++)
     {
-      if(SENV[i].uid == uid && SENV[i].gid == gid &&
-         (SENV[i].chroot == NULL || !strcmp(SENV[i].chroot, chroot)))
-      {
-          _DBG("Found existing senv: %i", i);
+      if((senv->name != NULL && SENV[i].name != NULL && !strcmp(SENV[i].name, 
senv->name)) ||
+               (senv->name == NULL && SENV[i].uid == senv->uid && SENV[i].gid 
== senv->gid &&
+           ((SENV[i].chroot == NULL && senv->chroot == NULL) || 
((SENV[i].chroot != NULL || senv->chroot != NULL) && !strcmp(SENV[i].chroot, 
senv->chroot))))
+       ) {
           return &SENV[i];
       }
     }
+    
+    return NULL;
+}
+
+static server_env_t* senv_add(server_env_t *senv)
+{
+    int socks[2];
+    server_env_t *old_senv;
+
+    _DBG("Searching for matching senv...");
+
+    old_senv = find_matching_senv(senv);
+
+    if (old_senv) {
+        _DBG("Found existing senv");
+       senv = old_senv;
+       return old_senv;
+    }
 
     if(NUM_SENV >= server_limit)
     {
@@ -1828,22 +1956,20 @@
     }
 
     _DBG("Creating new senv");
+    
+    memcpy(&SENV[NUM_SENV], senv, sizeof(server_env_t));
 
-    SENV[NUM_SENV].uid = uid;
-    SENV[NUM_SENV].gid = gid;
-    SENV[NUM_SENV].chroot = chroot;
-
-    SENV[NUM_SENV].min_processors = ap_min_processors;
-    SENV[NUM_SENV].min_free_processors = ap_min_free_processors;
-    SENV[NUM_SENV].max_processors = ap_max_processors;
+    SENV[NUM_SENV].availability = 100;
 
     socketpair(PF_UNIX, SOCK_STREAM, 0, socks);
     SENV[NUM_SENV].input  = socks[0];
     SENV[NUM_SENV].output = socks[1];
-
+    
+    senv = &SENV[NUM_SENV];
     return &SENV[server_env_image->control->num++];
 }
 
+
 static const char* child_clone()
 {
     int i;
@@ -1869,7 +1995,12 @@
     new = &CHILD_INFO_TABLE[i];
 
     new->senv = this->senv;
-    new->type = CHILD_TYPE_WORKER;
+    if (this->type == CHILD_TYPE_MULTIPLEXER) {
+       new->type = CHILD_TYPE_MULTIPLEXER;    
+    }
+    else {
+       new->type = CHILD_TYPE_WORKER;
+    }
     new->sock_fd = this->sock_fd;
     new->status = CHILD_STATUS_STARTING;
 
@@ -1878,7 +2009,7 @@
 }
 
 static const char* child_add(int type, int status,
-                             apr_pool_t *pool, uid_t uid, gid_t gid, const 
char* chroot)
+                             apr_pool_t *pool, server_env_t *senv)
 {
     _DBG("adding child #%d", NUM_CHILDS);
 
@@ -1888,10 +2019,10 @@
                "Increase NumServers in your config file.";
     }
 
-       if (chroot && !ap_is_directory(pool, chroot))
-               return apr_psprintf(pool, "Error: chroot directory [%s] does 
not exist", chroot);
+       if (senv->chroot && !ap_is_directory(pool, senv->chroot))
+               return apr_psprintf(pool, "Error: chroot directory [%s] does 
not exist", senv->chroot);
 
-    CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(uid, gid, chroot);
+    CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(senv);
 
     if(CHILD_INFO_TABLE[NUM_CHILDS].senv == NULL)
     {
@@ -1907,10 +2038,10 @@
     CHILD_INFO_TABLE[NUM_CHILDS].status = status;
 
     _DBG("[%d] uid=%d gid=%d type=%d chroot=%s",
-         NUM_CHILDS, uid, gid, type,
-         chroot);
+         NUM_CHILDS, senv->uid, senv->gid, type,
+         senv->chroot);
 
-    if (uid == 0 || gid == 0)
+    if (senv->uid == 0 || senv->gid == 0)
     {
         _DBG("Assigning root user/group to a child.", 0);
     }
@@ -2062,19 +2193,27 @@
         if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_STARTING)
           make_child(ap_server_conf, i);
       }
-      else if(((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || 
+      else if(
+             (((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || 
                CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER)  &&
                ap_scoreboard_image->parent[i].pid > 1) &&
-               (idle_processors (i) > 1 || total_processes (i) == 1) && (
+               idle_processors (i) > 
CHILD_INFO_TABLE[i].senv->min_free_processors && total_processes (i) > 
CHILD_INFO_TABLE[i].senv->min_processors && (
                    (expire_timeout > 0 &&  
ap_scoreboard_image->servers[i][0].status != SERVER_DEAD && 
                    apr_time_sec(now - 
ap_scoreboard_image->servers[i][0].last_used) > expire_timeout) ||
                    (idle_timeout >   0 &&  
ap_scoreboard_image->servers[i][0].status == SERVER_READY &&  
-                   apr_time_sec(now - 
ap_scoreboard_image->servers[i][0].last_used) > idle_timeout)))
+                   apr_time_sec(now - 
ap_scoreboard_image->servers[i][0].last_used) > idle_timeout))
+              )
+               || (CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER &&
+                       (multiplexer_idle_timeout > 0 && 
ap_scoreboard_image->servers[i][0].status == SERVER_READY &&
+                       apr_time_sec(now - 
ap_scoreboard_image->servers[i][0].last_used) > multiplexer_idle_timeout) && 
+                       total_processors(i) > 
CHILD_INFO_TABLE[i].senv->min_processors
+                   )
+            )
       {
         CHILD_INFO_TABLE[i].pid = 0;
         CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY;
 
-        if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER)
+        if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER || 
CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER)
         {
           /* completely free up this slot */
 
@@ -2599,6 +2738,8 @@
     ap_min_processors = DEFAULT_MIN_PROCESSORS;
     ap_min_free_processors = DEFAULT_MIN_FREE_PROCESSORS;
     ap_max_processors = DEFAULT_MAX_PROCESSORS;
+    ap_min_multiplexers = DEFAULT_MIN_MULTIPLEXERS;
+    ap_max_multiplexers = DEFAULT_MAX_MULTIPLEXERS;
     ap_daemons_limit = server_limit;
     ap_pid_fname = DEFAULT_PIDLOG;
     ap_lock_fname = DEFAULT_LOCKFILE;
@@ -2608,6 +2749,13 @@
        ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
 #endif
 
+    expire_timeout = DEFAULT_EXPIRE_TIMEOUT;
+    idle_timeout = DEFAULT_IDLE_TIMEOUT;
+    multiplexer_idle_timeout = DEFAULT_MULTIPLEXER_IDLE_TIMEOUT;
+    processor_wait_timeout = DEFAULT_PROCESSOR_WAIT_TIMEOUT;
+    processor_wait_steps = DEFAULT_PROCESSOR_WAIT_STEPS;
+    
+
     apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
 
     /* we need to know ServerLimit and ThreadLimit before we start processing
@@ -2712,6 +2860,7 @@
 
         server_env_image->control->num = 0;
 
+/*
         for (i = 0; i < tmp_server_limit; i++)
         {
             SENV[i].processor_id = -1;
@@ -2721,6 +2870,7 @@
             SENV[i].input        = -1;
             SENV[i].output       = -1;
         }
+*/
     }
 
     return OK;
@@ -2782,7 +2932,6 @@
                 {
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                              ap_server_conf, "Could not pass request to proper 
"                             "child, request will not be honoured.");
-                    return DECLINED;
                 }
                 _DBG("doing longjmp",0);
                 longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1);
@@ -2859,32 +3008,37 @@
     ap_rputs("<hr>\n", r);
     ap_rputs("<h2>peruser status</h2>\n", r);
     ap_rputs("<table border=\"0\">\n", r);
-    
ap_rputs("<tr><td>ID</td><td>PID</td><td>STATUS</td><td>TYPE</td><td>UID</td>"
-                   "<td>GID</td><td>CHROOT</td><td>INPUT</td>"
+    ap_rputs("<tr><td>ID</td><td>PID</td><td>STATUS</td><td>SB 
STATUS</td><td>TYPE</td><td>UID</td>"
+                   "<td>GID</td><td>CHROOT</td><td>NICE</td><td>INPUT</td>"
                    "<td>OUTPUT</td><td>SOCK_FD</td>"
                    "<td>TOTAL PROCESSORS</td><td>MAX PROCESSORS</td>"
-                   "<td>IDLE PROCESSORS</td><td>MIN FREE 
PROCESSORS</td></tr>\n", r);
+                   "<td>IDLE PROCESSORS</td><td>MIN FREE PROCESSORS</td>"
+                   "<td>AVAIL</td>"
+                   "</tr>\n", r);
     for (x = 0; x < NUM_CHILDS; x++)
         {
         senv = CHILD_INFO_TABLE[x].senv;
-        ap_rprintf(r, "<tr><td>%3d</td><td>%5d</td><td>%8s</td><td>%12s</td>"
-                       "<td>%4d</td><td>%4d</td><td>%25s</td><td>%5d</td>"
+        ap_rprintf(r, 
"<tr><td>%3d</td><td>%5d</td><td>%8s</td><td>%8s</td><td>%12s</td>"
+                       
"<td>%4d</td><td>%4d</td><td>%25s</td><td>%3d</td><td>%5d</td>"
                        "<td>%6d</td><td>%7d</td><td>%d</td><td>%d</td>"
-                       "<td>%d</td><td>%d</td></tr>\n", 
+                       "<td>%d</td><td>%d</td><td>%3d</td></tr>\n", 
                        CHILD_INFO_TABLE[x].id, 
                        CHILD_INFO_TABLE[x].pid, 
                        child_status_string(CHILD_INFO_TABLE[x].status), 
+                       scoreboard_status_string(SCOREBOARD_STATUS(x)), 
                        child_type_string(CHILD_INFO_TABLE[x].type), 
                        senv == NULL ? -1 : senv->uid, 
                        senv == NULL ? -1 : senv->gid, 
                        senv == NULL ? NULL : senv->chroot, 
+                       senv == NULL ? 0 : senv->nice_lvl,
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, 
                        senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, 
                        CHILD_INFO_TABLE[x].sock_fd,
                        total_processors(x), 
                        senv == NULL ? -1 : 
CHILD_INFO_TABLE[x].senv->max_processors,
                        idle_processors(x),
-                       senv == NULL ? -1 : 
CHILD_INFO_TABLE[x].senv->min_free_processors
+                       senv == NULL ? -1 : 
CHILD_INFO_TABLE[x].senv->min_free_processors,
+                       senv == NULL ? -1 : 
CHILD_INFO_TABLE[x].senv->availability
                        );
        }
     ap_rputs("</table>\n", r);
@@ -2938,50 +3092,162 @@
     APR_OPTIONAL_HOOK(ap, status_hook, peruser_status_hook, NULL, NULL, 
APR_HOOK_MIDDLE);
 }
 
-/* we define an Processor w/ specific uid/gid */
-static const char *cf_Processor(cmd_parms *cmd, void *dummy,
-    const char *user_name, const char *group_name, const char *chroot)
+static const char *cf_Processor(cmd_parms *cmd, void *dummy, const char *arg)
 {
-    uid_t uid = ap_uname2id(user_name);
-    gid_t gid = ap_gname2id(group_name);
+    const char *user_name = NULL, *group_name = NULL, *directive;
+    server_env_t senv;
+    ap_directive_t *current;
+
+    char *endp = ap_strrchr_c(arg, '>');
+
+    if (endp == NULL) {
+       return apr_psprintf(cmd->temp_pool, 
+                           "Error: Directive %s> missing closing '>'", 
cmd->cmd->name);
+    }
+
+    *endp = '\0';
+
+    senv.name = ap_getword_conf(cmd->temp_pool, &arg);
+    _DBG("processor_name: %s", senv.name);
+    
+    if (strlen(senv.name) == 0) {
+       return apr_psprintf(cmd->temp_pool, 
+                           "Error: Directive %s> takes one argument", 
cmd->cmd->name);
+    }
+    
+    /* Check for existing processors on first launch and between gracefuls */
+    if (restart_num == 1 || is_graceful) {
+       server_env_t *old_senv = find_senv_by_name(senv.name);
+    
+       if (old_senv) {
+           return apr_psprintf(cmd->temp_pool,
+                           "Error: Processor %s already defined", senv.name);
+       }
+    }
+    
+    senv.nice_lvl              = 0;
+    senv.chroot                = NULL;
+    senv.min_processors        = ap_min_processors;
+    senv.min_free_processors   = ap_min_free_processors;
+    senv.max_processors        = ap_max_processors;
+    
+    current = cmd->directive->first_child;
+    
+    int proc_temp = 0;
+    
+    for(; current != NULL; current = current->next) {
+        directive = current->directive;
+        
+        if (!strcasecmp(directive, "user")) {
+           user_name = current->args;
+        }
+        else if (!strcasecmp(directive, "group")) {
+           group_name = current->args;
+        }
+        else if (!strcasecmp(directive, "chroot")) {
+           senv.chroot = current->args;
+        }
+        else if (!strcasecmp(directive, "nicelevel")) {
+           senv.nice_lvl = atoi(current->args);
+        }
+        else if (!strcasecmp(directive, "maxprocessors")) {
+           proc_temp = atoi(current->args);
+
+           if (proc_temp < 1) {
+               ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "WARNING: Require MaxProcessors > 0, setting to 1");
+                proc_temp = 1;
+           }
+           
+           senv.max_processors = proc_temp;
+        }
+        else if (!strcasecmp(directive, "minprocessors")) {
+           proc_temp = atoi(current->args);
+
+           if (proc_temp < 0) {
+               ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "WARNING: Require MinProcessors >= 0, setting to 0");
+                proc_temp = 0;
+           }
+           
+           senv.min_processors = proc_temp;
+        }
+        else if (!strcasecmp(directive, "minspareprocessors")) {
+           proc_temp = atoi(current->args);
+
+           if (proc_temp < 0) {
+               ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "WARNING: Require MinSpareProcessors >= 0, setting to 0");
+                proc_temp = 0;
+           }
+           
+           senv.min_free_processors = proc_temp;
+        }
+        else {
+           ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
+                    "Unknown directive %s in %s>", directive, cmd->cmd->name);
+        }
+    }
+    
+    if (user_name == NULL || group_name == NULL) {
+       return apr_psprintf(cmd->temp_pool,
+                           "Error: User or Group must be set in %s>", 
cmd->cmd->name);
+    }
+    
+    senv.uid = ap_uname2id(user_name);
+    senv.gid = ap_gname2id(group_name);
+
+    _DBG("name=%s user=%s:%d group=%s:%d chroot=%s nice_lvl=%d",
+        senv.name, user_name, senv.uid, group_name, senv.gid, senv.chroot, 
senv.nice_lvl);
+
+    _DBG("min_processors=%d min_free_processors=%d max_processors=%d",
+        senv.min_processors, senv.min_free_processors, senv.max_processors);
 
-    _DBG("user=%s:%d group=%s:%d chroot=%s",
-        user_name, uid, group_name, gid, chroot);
 
     return child_add(CHILD_TYPE_PROCESSOR, CHILD_STATUS_STANDBY,
-                     cmd->pool, uid, gid, chroot);
+                     cmd->pool, &senv);
 }
 
 /* we define an Multiplexer child w/ specific uid/gid */
 static const char *cf_Multiplexer(cmd_parms *cmd, void *dummy,
     const char *user_name, const char *group_name, const char *chroot)
 {
-    uid_t uid = ap_uname2id(user_name);
-    gid_t gid = ap_gname2id(group_name);
+    server_env_t senv;
+    
+    senv.name          = NULL;
+    
+    senv.uid           = ap_uname2id(user_name);
+    senv.gid           = ap_gname2id(group_name);
+    senv.chroot        = chroot;
+    senv.nice_lvl      = 0;
+
+    senv.min_processors        = ap_min_multiplexers;
+    senv.min_free_processors   = ap_min_free_processors;
+    senv.max_processors        = ap_max_multiplexers;
 
     _DBG("user=%s:%d group=%s:%d chroot=%s [multiplexer id %d]",
-        user_name, uid, group_name, gid, chroot, NUM_CHILDS);
+        user_name, senv.uid, group_name, senv.gid, senv.chroot, NUM_CHILDS);
 
     return child_add(CHILD_TYPE_MULTIPLEXER, CHILD_STATUS_STARTING,
-                     cmd->pool, uid, gid, chroot);
+                     cmd->pool, &senv);
 }
 
 static const char* cf_ServerEnvironment(cmd_parms *cmd, void *dummy,
-    const char *user_name, const char *group_name, const char *chroot)
+    const char *name)
 {
-    int uid = ap_uname2id(user_name);
-    int gid = ap_gname2id(group_name);
     peruser_server_conf *sconf = 
PERUSER_SERVER_CONF(cmd->server->module_config);
 
     _DBG("function entered", 0);
 
-       if (chroot && !ap_is_directory(cmd->pool, chroot))
-               return apr_psprintf(cmd->pool, "Error: chroot directory [%s] 
does not exist", chroot);
+    sconf->senv = find_senv_by_name(name);
 
-    sconf->senv = senv_add(uid, gid, chroot);
+    if (sconf->senv == NULL) {
+       return apr_psprintf(cmd->pool, 
+                           "Error: Processor %s not defined", name);
+    }
 
-    _DBG("user=%s:%d group=%s:%d chroot=%s numchilds=%d",
-        user_name, uid, group_name, gid, chroot, NUM_CHILDS);
+    _DBG("user=%d group=%d chroot=%s numchilds=%d",
+        sconf->senv->uid, sconf->senv->gid, sconf->senv->chroot, NUM_CHILDS);
 
     return NULL;
 }
@@ -3046,10 +3312,10 @@
 
     min_procs = atoi(arg);
 
-    if (min_procs < 1) {
+    if (min_procs < 0) {
         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
-                     "WARNING: Require MaxProcessors > 0, setting to 1");
-        min_procs = 1;
+                     "WARNING: Require MinProcessors >= 0, setting to 0");
+        min_procs = 0;
     }
 
     if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) {
@@ -3121,6 +3387,50 @@
     return NULL;
 }
 
+static const char *set_min_multiplexers (cmd_parms *cmd, void *dummy, const 
char *arg)
+{
+    int min_multiplexers;
+    const char *err = ap_check_cmd_context(cmd, 
NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+
+    if (err != NULL) {
+        return err;
+    }
+
+    min_multiplexers = atoi(arg);
+
+    if (min_multiplexers < 1) {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "WARNING: Require MinMultiplexers > 0, setting to 1");
+        min_multiplexers = 1;
+    }
+
+    ap_min_multiplexers = min_multiplexers;
+
+    return NULL;
+}
+
+static const char *set_max_multiplexers (cmd_parms *cmd, void *dummy, const 
char *arg)
+{
+    int max_multiplexers;
+    const char *err = ap_check_cmd_context(cmd, 
NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+
+    if (err != NULL) {
+        return err;
+    }
+
+    max_multiplexers = atoi(arg);
+
+    if (max_multiplexers < 1) {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "WARNING: Require MaxMultiplexers > 0, setting to 1");
+        max_multiplexers = 1;
+    }
+
+    ap_max_multiplexers = max_multiplexers;
+
+    return NULL;
+}
+
 static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char 
*arg) 
 {
     int tmp_server_limit;
@@ -3183,6 +3493,40 @@
     return NULL;
 }
 
+static const char *set_multiplexer_idle_timeout (cmd_parms *cmd, void *dummy, 
const char *arg) {
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    multiplexer_idle_timeout = atoi(arg);
+
+    return NULL;
+}
+
+static const char *set_processor_wait_timeout (cmd_parms *cmd, void *dummy, 
const char *timeout, const char *steps) {
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+    
+    processor_wait_timeout = atoi(timeout);
+    
+    if (steps != NULL) {
+       int steps_tmp = atoi(steps);
+       
+       if (steps_tmp < 1) {
+           ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "WARNING: Require ProcessorWaitTimeout steps > 0, setting 
to 1");
+            steps_tmp = 1;
+       }
+       
+       processor_wait_steps = steps_tmp;
+    }
+
+    return NULL;
+}
+
 static const command_rec peruser_cmds[] = {
 UNIX_DAEMON_COMMANDS,
 LISTEN_COMMANDS,
@@ -3196,17 +3540,25 @@
               "Minimum number of processors per vhost"),
 AP_INIT_TAKE1("MaxProcessors", set_max_processors, NULL, RSRC_CONF,
               "Maximum number of processors per vhost"),
+AP_INIT_TAKE1("MinMultiplexers", set_min_multiplexers, NULL, RSRC_CONF,
+              "Minimum number of multiplexers the server can have"),
+AP_INIT_TAKE1("MaxMultiplexers", set_max_multiplexers, NULL, RSRC_CONF,
+              "Maximum number of multiplexers the server can have"),
 AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
               "Maximum value of MaxClients for this run of Apache"),
 AP_INIT_TAKE1("ExpireTimeout", set_expire_timeout, NULL, RSRC_CONF,
-              "Maximum idle time before a child is killed, 0 to disable"),
+              "Maximum time a child can live, 0 to disable"),
 AP_INIT_TAKE1("IdleTimeout", set_idle_timeout, NULL, RSRC_CONF,
               "Maximum time before a child is killed after being idle, 0 to 
disable"),
+AP_INIT_TAKE1("MultiplexerIdleTimeout", set_multiplexer_idle_timeout, NULL, 
RSRC_CONF,
+              "Maximum time before a multiplexer is killed after being idle, 0 
to disable"),
+AP_INIT_TAKE12("ProcessorWaitTimeout", set_processor_wait_timeout, NULL, 
RSRC_CONF,
+              "Maximum time a multiplexer waits for the processor if it is 
busy"),
 AP_INIT_TAKE23("Multiplexer", cf_Multiplexer, NULL, RSRC_CONF,
               "Specify an Multiplexer Child configuration."),
-AP_INIT_TAKE23("Processor", cf_Processor, NULL, RSRC_CONF,
-              "Specify a User and Group for a specific child process."),
-AP_INIT_TAKE23("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF,
+AP_INIT_RAW_ARGS("<Processor", cf_Processor, NULL, RSRC_CONF,
+              "Specify settings for processor."),
+AP_INIT_TAKE1("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF,
               "Specify the server environment for this virtual host."),
 { NULL }
 };
--- httpd-2.2.2-old/server/mpm/experimental/peruser/mpm_default.h       
2008-05-12 00:38:04.000000000 +0300
+++ httpd-2.2.2-new/server/mpm/experimental/peruser/mpm_default.h       
2008-05-18 21:37:29.000000000 +0300
@@ -107,4 +107,50 @@
 #define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
 #endif
 
+/* Maximum multiplexers */
+
+#ifndef DEFAULT_MAX_MULTIPLEXERS
+#define DEFAULT_MAX_MULTIPLEXERS 20
+#endif
+
+/* Minimum multiplexers */
+
+#ifndef DEFAULT_MIN_MULTIPLEXERS
+#define DEFAULT_MIN_MULTIPLEXERS 3
+#endif
+
+/* Amount of time a child can run before it expires (0 = turn off) */
+
+#ifndef DEFAULT_EXPIRE_TIMEOUT
+#define DEFAULT_EXPIRE_TIMEOUT 1800
+#endif
+
+/* Amount of time a child can stay idle (0 = turn off) */
+
+#ifndef DEFAULT_IDLE_TIMEOUT
+#define DEFAULT_IDLE_TIMEOUT 900
+#endif
+
+/* Amount of time a multiplexer can stay idle (0 = turn off) */
+
+#ifndef DEFAULT_MULTIPLEXER_IDLE_TIMEOUT
+#define DEFAULT_MULTIPLEXER_IDLE_TIMEOUT 0
+#endif
+
+/* Amount of maximum time a multiplexer can wait for processor if it is busy 
(0 = never wait)
+ * This is decreased with every busy request
+ */
+
+#ifndef DEFAULT_PROCESSOR_WAIT_TIMEOUT
+#define DEFAULT_PROCESSOR_WAIT_TIMEOUT 2
+#endif
+
+/* The number of different levels there are when a multiplexer is waiting for 
processor
+ * (between maximum waiting time and no waiting)
+ */
+
+#ifndef DEFAULT_PROCESSOR_WAIT_STEPS
+#define DEFAULT_PROCESSOR_WAIT_STEPS 10
+#endif
+
 #endif /* AP_MPM_DEFAULT_H */
_______________________________________________
Peruser mailing list
[email protected]
http://www.telana.com/mailman/listinfo/peruser

Reply via email to