--- 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-19 09:14:04.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,28 @@ 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 || CHILD_INFO_TABLE[i].senv->min_free_processors == 0) + && 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 +2739,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 +2750,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 +2861,7 @@ server_env_image->control->num = 0; +/* for (i = 0; i < tmp_server_limit; i++) { SENV[i].processor_id = -1; @@ -2721,6 +2871,7 @@ SENV[i].input = -1; SENV[i].output = -1; } +*/ } return OK; @@ -2782,7 +2933,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 +3009,37 @@ ap_rputs("
\n", r); ap_rputs("

peruser status

\n", r); ap_rputs("\n", r); - ap_rputs("" - "" + ap_rputs("" + "" "" "" - "\n", r); + "" + "" + "\n", r); for (x = 0; x < NUM_CHILDS; x++) { senv = CHILD_INFO_TABLE[x].senv; - ap_rprintf(r, "" - "" + ap_rprintf(r, "" + "" "" - "\n", + "\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("
IDPIDSTATUSTYPEUIDGIDCHROOTINPUT
IDPIDSTATUSSB STATUSTYPEUIDGIDCHROOTNICEINPUTOUTPUTSOCK_FDTOTAL PROCESSORSMAX PROCESSORSIDLE PROCESSORSMIN FREE PROCESSORS
IDLE PROCESSORSMIN FREE PROCESSORSAVAIL
%3d%5d%8s%12s%4d%4d%25s%5d
%3d%5d%8s%8s%12s%4d%4d%25s%3d%5d%6d%7d%d%d%d%d
%d%d%3d
\n", r); @@ -2938,50 +3093,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 +3313,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) { @@ -3075,10 +3342,10 @@ min_free_procs = atoi(arg); - if (min_free_procs < 1) { + if (min_free_procs < 0) { ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, - "WARNING: Require MinSpareProcessors > 0, setting to 1"); - min_free_procs = 1; + "WARNING: Require MinSpareProcessors >= 0, setting to 0"); + min_free_procs = 0; } if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) { @@ -3121,6 +3388,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 +3494,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 +3541,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("