diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/config.c | 6 | ||||
-rw-r--r-- | server/core.c | 6 | ||||
-rw-r--r-- | server/core_filters.c | 4 | ||||
-rw-r--r-- | server/log.c | 10 | ||||
-rw-r--r-- | server/main.c | 10 | ||||
-rw-r--r-- | server/mpm/event/event.c | 286 | ||||
-rw-r--r-- | server/mpm/prefork/prefork.c | 6 | ||||
-rw-r--r-- | server/mpm/winnt/child.c | 16 | ||||
-rw-r--r-- | server/mpm/worker/worker.c | 30 | ||||
-rw-r--r-- | server/mpm_unix.c | 55 | ||||
-rw-r--r-- | server/protocol.c | 24 | ||||
-rw-r--r-- | server/provider.c | 36 | ||||
-rw-r--r-- | server/request.c | 10 | ||||
-rw-r--r-- | server/scoreboard.c | 5 | ||||
-rw-r--r-- | server/util.c | 2 | ||||
-rw-r--r-- | server/util_mutex.c | 2 | ||||
-rw-r--r-- | server/util_script.c | 11 |
17 files changed, 336 insertions, 183 deletions
diff --git a/server/config.c b/server/config.c index 75857046..fb07623b 100644 --- a/server/config.c +++ b/server/config.c @@ -845,9 +845,11 @@ static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, const char *errmsg = NULL; /** Have we been provided a list of acceptable directives? */ - if(parms->override_list != NULL) - if(apr_table_get(parms->override_list, cmd->name) != NULL) + if (parms->override_list != NULL) { + if (apr_table_get(parms->override_list, cmd->name) != NULL) { override_list_ok = 1; + } + } if ((parms->override & cmd->req_override) == 0 && !override_list_ok) { if (parms->override & NONFATAL_OVERRIDE) { diff --git a/server/core.c b/server/core.c index 541f6e8e..03137577 100644 --- a/server/core.c +++ b/server/core.c @@ -996,7 +996,7 @@ AP_DECLARE(const char *) ap_get_server_name_for_url(request_rec *r) #if APR_HAVE_IPV6 if (ap_strchr_c(plain_server_name, ':')) { /* IPv6 literal? */ - return apr_psprintf(r->pool, "[%s]", plain_server_name); + return apr_pstrcat(r->pool, "[", plain_server_name, "]", NULL); } #endif return plain_server_name; @@ -1509,7 +1509,7 @@ static const char *set_error_document(cmd_parms *cmd, void *conf_, * a msg and a (local) path. */ conf->response_code_strings[index_number] = (what == MSG) ? - apr_pstrcat(cmd->pool, "\"",msg,NULL) : + apr_pstrcat(cmd->pool, "\"", msg, NULL) : apr_pstrdup(cmd->pool, msg); } } @@ -1612,7 +1612,7 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l) if (v) set_allow_opts(cmd, &(d->override_opts), v); else - d->override_opts = OPT_SYM_LINKS; + d->override_opts = OPT_ALL; } else if (!strcasecmp(w, "FileInfo")) { d->override |= OR_FILEINFO; diff --git a/server/core_filters.c b/server/core_filters.c index 18a32dde..0798d2ef 100644 --- a/server/core_filters.c +++ b/server/core_filters.c @@ -391,10 +391,6 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb) if (ctx == NULL) { ctx = apr_pcalloc(c->pool, sizeof(*ctx)); net->out_ctx = (core_output_filter_ctx_t *)ctx; - rv = apr_socket_opt_set(net->client_socket, APR_SO_NONBLOCK, 1); - if (rv != APR_SUCCESS) { - return rv; - } /* * Need to create tmp brigade with correct lifetime. Passing * NULL to apr_brigade_split_ex would result in a brigade diff --git a/server/log.c b/server/log.c index de560efc..b33aa1fc 100644 --- a/server/log.c +++ b/server/log.c @@ -563,10 +563,10 @@ static int log_remote_address(const ap_errorlog_info *info, const char *arg, { if (info->r && !(arg && *arg == 'c')) return apr_snprintf(buf, buflen, "%s:%d", info->r->useragent_ip, - info->r->useragent_addr->port); + info->r->useragent_addr ? info->r->useragent_addr->port : 0); else if (info->c) return apr_snprintf(buf, buflen, "%s:%d", info->c->client_ip, - info->c->client_addr->port); + info->c->client_addr ? info->c->client_addr->port : 0); else return 0; } @@ -968,12 +968,14 @@ static int do_errorlog_default(const ap_errorlog_info *info, char *buf, if (info->r) { len += apr_snprintf(buf + len, buflen - len, info->r->connection->sbh ? "[client %s:%d] " : "[remote %s:%d] ", - info->r->useragent_ip, info->r->useragent_addr->port); + info->r->useragent_ip, + info->r->useragent_addr ? info->r->useragent_addr->port : 0); } else if (info->c) { len += apr_snprintf(buf + len, buflen - len, info->c->sbh ? "[client %s:%d] " : "[remote %s:%d] ", - info->c->client_ip, info->c->client_addr->port); + info->c->client_ip, + info->c->client_addr ? info->c->client_addr->port : 0); } /* the actual error message */ diff --git a/server/main.c b/server/main.c index 9b88943f..28d18720 100644 --- a/server/main.c +++ b/server/main.c @@ -671,6 +671,11 @@ int main(int argc, const char * const argv[]) } } + /* If our config failed, deal with that here. */ + if (rv != OK) { + destroy_and_exit_process(process, 1); + } + signal_server = APR_RETRIEVE_OPTIONAL_FN(ap_signal_server); if (signal_server) { int exit_status; @@ -680,11 +685,6 @@ int main(int argc, const char * const argv[]) } } - /* If our config failed, deal with that here. */ - if (rv != OK) { - destroy_and_exit_process(process, 1); - } - apr_pool_clear(plog); if ( ap_run_open_logs(pconf, plog, ptemp, ap_server_conf) != OK) { diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index 0ab822a8..fa724688 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -166,13 +166,18 @@ static int ap_daemons_limit = 0; static int max_workers = 0; static int server_limit = 0; static int thread_limit = 0; +static int had_healthy_child = 0; static int dying = 0; static int workers_may_exit = 0; static int start_thread_may_exit = 0; static int listener_may_exit = 0; -static int requests_this_child; static int num_listensocks = 0; -static apr_uint32_t connection_count = 0; +static apr_int32_t conns_this_child; /* MaxConnectionsPerChild, only access + in listener thread */ +static apr_uint32_t connection_count = 0; /* Number of open connections */ +static apr_uint32_t lingering_count = 0; /* Number of connections in lingering close */ +static apr_uint32_t suspended_count = 0; /* Number of suspended connections */ +static apr_uint32_t clogged_count = 0; /* Number of threads processing ssl conns */ static int resource_shortage = 0; static fd_queue_t *worker_queue; static fd_queue_info_t *worker_queue_info; @@ -372,8 +377,12 @@ static void enable_listensocks(int process_slot) int i; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00457) "Accepting new connections again: " - "%u active conns, %u idle workers", + "%u active conns (%u lingering/%u clogged/%u suspended), " + "%u idle workers", apr_atomic_read32(&connection_count), + apr_atomic_read32(&lingering_count), + apr_atomic_read32(&clogged_count), + apr_atomic_read32(&suspended_count), ap_queue_info_get_idlers(worker_queue_info)); for (i = 0; i < num_listensocks; i++) apr_pollset_add(event_pollset, &listener_pollfd[i]); @@ -587,7 +596,20 @@ static int child_fatal; static int volatile shutdown_pending; static int volatile restart_pending; -static apr_status_t decrement_connection_count(void *dummy) { +static apr_status_t decrement_connection_count(void *cs_) +{ + event_conn_state_t *cs = cs_; + switch (cs->pub.state) { + case CONN_STATE_LINGER_NORMAL: + case CONN_STATE_LINGER_SHORT: + apr_atomic_dec32(&lingering_count); + break; + case CONN_STATE_SUSPENDED: + apr_atomic_dec32(&suspended_count); + break; + default: + break; + } apr_atomic_dec32(&connection_count); return APR_SUCCESS; } @@ -735,74 +757,97 @@ static void set_signals(void) #endif } +static int start_lingering_close_common(event_conn_state_t *cs) +{ + apr_status_t rv; + struct timeout_queue *q; + apr_socket_t *csd = cs->pfd.desc.s; +#ifdef AP_DEBUG + { + rv = apr_socket_timeout_set(csd, 0); + AP_DEBUG_ASSERT(rv == APR_SUCCESS); + } +#else + apr_socket_timeout_set(csd, 0); +#endif + /* + * If some module requested a shortened waiting period, only wait for + * 2s (SECONDS_TO_LINGER). This is useful for mitigating certain + * DoS attacks. + */ + if (apr_table_get(cs->c->notes, "short-lingering-close")) { + cs->expiration_time = + apr_time_now() + apr_time_from_sec(SECONDS_TO_LINGER); + q = &short_linger_q; + cs->pub.state = CONN_STATE_LINGER_SHORT; + } + else { + cs->expiration_time = + apr_time_now() + apr_time_from_sec(MAX_SECS_TO_LINGER); + q = &linger_q; + cs->pub.state = CONN_STATE_LINGER_NORMAL; + } + apr_atomic_inc32(&lingering_count); + apr_thread_mutex_lock(timeout_mutex); + TO_QUEUE_APPEND(*q, cs); + cs->pfd.reqevents = APR_POLLIN | APR_POLLHUP | APR_POLLERR; + rv = apr_pollset_add(event_pollset, &cs->pfd); + apr_thread_mutex_unlock(timeout_mutex); + if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, + "start_lingering_close: apr_pollset_add failure"); + apr_thread_mutex_lock(timeout_mutex); + TO_QUEUE_REMOVE(*q, cs); + apr_thread_mutex_unlock(timeout_mutex); + apr_socket_close(cs->pfd.desc.s); + apr_pool_clear(cs->p); + ap_push_pool(worker_queue_info, cs->p); + return 0; + } + return 1; +} + /* - * close our side of the connection + * Close our side of the connection, flushing data to the client first. * Pre-condition: cs is not in any timeout queue and not in the pollset, * timeout_mutex is not locked * return: 0 if connection is fully closed, * 1 if connection is lingering - * may be called by listener or by worker thread + * May only be called by worker thread. */ -static int start_lingering_close(event_conn_state_t *cs) +static int start_lingering_close_blocking(event_conn_state_t *cs) { - apr_status_t rv; - - cs->c->sbh = NULL; /* prevent scoreboard updates from the listener - * worker will loop around and set SERVER_READY soon - */ - if (ap_start_lingering_close(cs->c)) { apr_pool_clear(cs->p); ap_push_pool(worker_queue_info, cs->p); return 0; } - else { - apr_socket_t *csd = ap_get_conn_socket(cs->c); - struct timeout_queue *q; + return start_lingering_close_common(cs); +} -#ifdef AP_DEBUG - { - rv = apr_socket_timeout_set(csd, 0); - AP_DEBUG_ASSERT(rv == APR_SUCCESS); - } -#else - apr_socket_timeout_set(csd, 0); -#endif - /* - * If some module requested a shortened waiting period, only wait for - * 2s (SECONDS_TO_LINGER). This is useful for mitigating certain - * DoS attacks. - */ - if (apr_table_get(cs->c->notes, "short-lingering-close")) { - cs->expiration_time = - apr_time_now() + apr_time_from_sec(SECONDS_TO_LINGER); - q = &short_linger_q; - cs->pub.state = CONN_STATE_LINGER_SHORT; - } - else { - cs->expiration_time = - apr_time_now() + apr_time_from_sec(MAX_SECS_TO_LINGER); - q = &linger_q; - cs->pub.state = CONN_STATE_LINGER_NORMAL; - } - apr_thread_mutex_lock(timeout_mutex); - TO_QUEUE_APPEND(*q, cs); - cs->pfd.reqevents = APR_POLLIN | APR_POLLHUP | APR_POLLERR; - rv = apr_pollset_add(event_pollset, &cs->pfd); - apr_thread_mutex_unlock(timeout_mutex); - if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, - "start_lingering_close: apr_pollset_add failure"); - apr_thread_mutex_lock(timeout_mutex); - TO_QUEUE_REMOVE(*q, cs); - apr_thread_mutex_unlock(timeout_mutex); - apr_socket_close(cs->pfd.desc.s); - apr_pool_clear(cs->p); - ap_push_pool(worker_queue_info, cs->p); - return 0; - } +/* + * Close our side of the connection, NOT flushing data to the client. + * This should only be called if there has been an error or if we know + * that our send buffers are empty. + * Pre-condition: cs is not in any timeout queue and not in the pollset, + * timeout_mutex is not locked + * return: 0 if connection is fully closed, + * 1 if connection is lingering + * may be called by listener thread + */ +static int start_lingering_close_nonblocking(event_conn_state_t *cs) +{ + conn_rec *c = cs->c; + apr_socket_t *csd = cs->pfd.desc.s; + + if (c->aborted + || apr_socket_shutdown(csd, APR_SHUTDOWN_WRITE) != APR_SUCCESS) { + apr_socket_close(csd); + apr_pool_clear(cs->p); + ap_push_pool(worker_queue_info, cs->p); + return 0; } - return 1; + return start_lingering_close_common(cs); } /* @@ -829,10 +874,8 @@ static int stop_lingering_close(event_conn_state_t *cs) /* * process one connection in the worker - * return: 1 if the connection has been completed, - * 0 if it is still open and waiting for some event */ -static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock, +static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock, event_conn_state_t * cs, int my_child_num, int my_thread_num) { @@ -841,6 +884,7 @@ static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock int rc; ap_sb_handle_t *sbh; + /* XXX: This will cause unbounded mem usage for long lasting connections */ ap_create_sb_handle(&sbh, p, my_child_num, my_thread_num); if (cs == NULL) { /* This is a new connection */ @@ -853,10 +897,11 @@ static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock apr_bucket_alloc_destroy(cs->bucket_alloc); apr_pool_clear(p); ap_push_pool(worker_queue_info, p); - return 1; + return; } apr_atomic_inc32(&connection_count); - apr_pool_cleanup_register(c->pool, NULL, decrement_connection_count, apr_pool_cleanup_null); + apr_pool_cleanup_register(c->pool, cs, decrement_connection_count, + apr_pool_cleanup_null); c->current_thread = thd; cs->c = c; c->cs = &(cs->pub); @@ -905,10 +950,12 @@ static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock * like mod_ssl, lets just do the normal read from input filters, * like the Worker MPM does. */ + apr_atomic_inc32(&clogged_count); ap_run_process_connection(c); if (cs->pub.state != CONN_STATE_SUSPENDED) { cs->pub.state = CONN_STATE_LINGER; } + apr_atomic_dec32(&clogged_count); } read_request: @@ -950,7 +997,7 @@ read_request: cs->pfd.reqevents = APR_POLLOUT | APR_POLLHUP | APR_POLLERR; rc = apr_pollset_add(event_pollset, &cs->pfd); apr_thread_mutex_unlock(timeout_mutex); - return 1; + return; } else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted || listener_may_exit) { @@ -966,8 +1013,8 @@ read_request: } if (cs->pub.state == CONN_STATE_LINGER) { - if (!start_lingering_close(cs)) - return 0; + if (!start_lingering_close_blocking(cs)) + return; } else if (cs->pub.state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) { apr_status_t rc; @@ -996,19 +1043,33 @@ read_request: AP_DEBUG_ASSERT(rc == APR_SUCCESS); } } - return 1; + else if (cs->pub.state == CONN_STATE_SUSPENDED) { + apr_atomic_inc32(&suspended_count); + } + /* + * Prevent this connection from writing to our connection state after it + * is no longer associated with this thread. This would happen if the EOR + * bucket is destroyed from the listener thread due to a connection abort + * or timeout. + */ + c->sbh = NULL; + + return; } -/* requests_this_child has gone to zero or below. See if the admin coded +/* conns_this_child has gone to zero or below. See if the admin coded "MaxConnectionsPerChild 0", and keep going in that case. Doing it this way simplifies the hot path in worker_thread */ static void check_infinite_requests(void) { if (ap_max_requests_per_child) { + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, + "Stopping process due to MaxConnectionsPerChild"); signal_threads(ST_GRACEFUL); } else { - requests_this_child = INT_MAX; /* keep going */ + /* keep going */ + conns_this_child = APR_INT32_MAX; } } @@ -1354,9 +1415,8 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) break; } - if (requests_this_child <= 0) { + if (conns_this_child <= 0) check_infinite_requests(); - } now = apr_time_now(); if (APLOGtrace6(ap_server_conf)) { @@ -1365,11 +1425,14 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) last_log = now; apr_thread_mutex_lock(timeout_mutex); ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf, - "connections: %d (write-completion: %d " - "keep-alive: %d lingering: %d)", - connection_count, write_completion_q.count, + "connections: %u (clogged: %u write-completion: %d " + "keep-alive: %d lingering: %d suspended: %u)", + apr_atomic_read32(&connection_count), + apr_atomic_read32(&clogged_count), + write_completion_q.count, keepalive_q.count, - linger_q.count + short_linger_q.count); + apr_atomic_read32(&lingering_count), + apr_atomic_read32(&suspended_count)); apr_thread_mutex_unlock(timeout_mutex); } } @@ -1457,7 +1520,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf, "pollset remove failed"); apr_thread_mutex_unlock(timeout_mutex); - start_lingering_close(cs); + start_lingering_close_nonblocking(cs); break; } @@ -1468,7 +1531,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) * re-connect to a different process. */ if (!have_idle_worker) { - start_lingering_close(cs); + start_lingering_close_nonblocking(cs); break; } rc = push2worker(out_pfd, event_pollset); @@ -1502,9 +1565,11 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) "All workers busy, not accepting new conns" "in this process"); } - else if (apr_atomic_read32(&connection_count) > threads_per_child - + ap_queue_info_get_idlers(worker_queue_info) * - worker_factor / WORKER_FACTOR_SCALE) + else if ( (int)apr_atomic_read32(&connection_count) + - (int)apr_atomic_read32(&lingering_count) + > threads_per_child + + ap_queue_info_get_idlers(worker_queue_info) * + worker_factor / WORKER_FACTOR_SCALE) { if (!listeners_disabled) disable_listensocks(process_slot); @@ -1559,6 +1624,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) } if (csd != NULL) { + conns_this_child--; rc = ap_queue_push(worker_queue, csd, NULL, ptrans); if (rc != APR_SUCCESS) { /* trash the connection; we couldn't queue the connected @@ -1608,14 +1674,15 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) keepalive_q.count); process_timeout_queue(&keepalive_q, timeout_time + ap_server_conf->keep_alive_timeout, - start_lingering_close); + start_lingering_close_nonblocking); } else { process_timeout_queue(&keepalive_q, timeout_time, - start_lingering_close); + start_lingering_close_nonblocking); } /* Step 2: write completion timeouts */ - process_timeout_queue(&write_completion_q, timeout_time, start_lingering_close); + process_timeout_queue(&write_completion_q, timeout_time, + start_lingering_close_nonblocking); /* Step 3: (normal) lingering close completion timeouts */ process_timeout_queue(&linger_q, timeout_time, stop_lingering_close); /* Step 4: (short) lingering close completion timeouts */ @@ -1623,17 +1690,18 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) ps = ap_get_scoreboard_process(process_slot); ps->write_completion = write_completion_q.count; - ps->lingering_close = linger_q.count + short_linger_q.count; ps->keep_alive = keepalive_q.count; apr_thread_mutex_unlock(timeout_mutex); ps->connections = apr_atomic_read32(&connection_count); - /* XXX: should count CONN_STATE_SUSPENDED and set ps->suspended */ - } - if (listeners_disabled && !workers_were_busy && - (int)apr_atomic_read32(&connection_count) < - ((int)ap_queue_info_get_idlers(worker_queue_info) - 1) * - worker_factor / WORKER_FACTOR_SCALE + threads_per_child) + ps->suspended = apr_atomic_read32(&suspended_count); + ps->lingering_close = apr_atomic_read32(&lingering_count); + } + if (listeners_disabled && !workers_were_busy + && (int)apr_atomic_read32(&connection_count) + - (int)apr_atomic_read32(&lingering_count) + < ((int)ap_queue_info_get_idlers(worker_queue_info) - 1) + * worker_factor / WORKER_FACTOR_SCALE + threads_per_child) { listeners_disabled = 0; enable_listensocks(process_slot); @@ -1741,10 +1809,7 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy) else { is_idle = 0; worker_sockets[thread_slot] = csd; - rv = process_socket(thd, ptrans, csd, cs, process_slot, thread_slot); - if (!rv) { - requests_this_child--; - } + process_socket(thd, ptrans, csd, cs, process_slot, thread_slot); worker_sockets[thread_slot] = NULL; } } @@ -2041,11 +2106,11 @@ static void child_main(int child_num_arg) } if (ap_max_requests_per_child) { - requests_this_child = ap_max_requests_per_child; + conns_this_child = ap_max_requests_per_child; } else { /* coding a value of zero means infinity */ - requests_this_child = INT_MAX; + conns_this_child = APR_INT32_MAX; } /* Setup worker threads */ @@ -2264,6 +2329,7 @@ static void perform_idle_server_maintenance(void) int any_dying_threads = 0; int any_dead_threads = 0; int all_dead_threads = 1; + int child_threads_active = 0; if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate) @@ -2298,10 +2364,11 @@ static void perform_idle_server_maintenance(void) ++idle_thread_count; } if (status >= SERVER_READY && status < SERVER_GRACEFUL) { - ++active_thread_count; + ++child_threads_active; } } } + active_thread_count += child_threads_active; if (any_dead_threads && totally_free_length < retained->idle_spawn_rate && free_length < MAX_SPAWN_RATE @@ -2325,6 +2392,9 @@ static void perform_idle_server_maintenance(void) } ++free_length; } + else if (child_threads_active == threads_per_child) { + had_healthy_child = 1; + } /* XXX if (!ps->quiescing) is probably more reliable GLA */ if (!any_dying_threads) { last_non_dead = i; @@ -2333,21 +2403,23 @@ static void perform_idle_server_maintenance(void) } if (retained->sick_child_detected) { - if (active_thread_count > 0) { - /* some child processes appear to be working. don't kill the - * whole server. + if (had_healthy_child) { + /* Assume this is a transient error, even though it may not be. Leave + * the server up in case it is able to serve some requests or the + * problem will be resolved. */ retained->sick_child_detected = 0; } else { - /* looks like a basket case. give up. + /* looks like a basket case, as no child ever fully initialized; give up. */ shutdown_pending = 1; child_fatal = 1; ap_log_error(APLOG_MARK, APLOG_ALERT, 0, - ap_server_conf, APLOGNO(00483) - "No active workers found..." - " Apache is exiting!"); + ap_server_conf, APLOGNO(02324) + "A resource shortage or other unrecoverable failure " + "was encountered before any child process initialized " + "successfully... httpd is exiting!"); /* the child already logged the failure details */ return; } @@ -2480,6 +2552,11 @@ static void server_main_loop(int remaining_children_to_start) event_note_child_killed(-1, /* already out of the scoreboard */ pid.pid, old_gen); + if (processed_status == APEXIT_CHILDSICK + && old_gen == retained->my_generation) { + /* resource shortage, minimize the fork rate */ + retained->idle_spawn_rate = 1; + } #if APR_HAS_OTHER_CHILD } else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, @@ -2795,6 +2872,7 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog, ap_daemons_limit = server_limit; threads_per_child = DEFAULT_THREADS_PER_CHILD; max_workers = ap_daemons_limit * threads_per_child; + had_healthy_child = 0; ap_extended_status = 0; return OK; diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index 5de88e32..d0cf59b4 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -180,7 +180,7 @@ static void chdir_for_gprof(void) } } else { - use_dir = ap_server_root_relative(pconf, DEFAULT_REL_RUNTIMEDIR); + use_dir = ap_runtime_dir_relative(pconf, ""); } chdir(use_dir); @@ -641,7 +641,7 @@ static void child_main(int child_num_arg) ap_log_error(APLOG_MARK, APLOG_ERR, status, ap_server_conf, APLOGNO(00158) "apr_pollset_poll: (listen)"); SAFE_ACCEPT(accept_mutex_off()); - clean_child_exit(1); + clean_child_exit(APEXIT_CHILDSICK); } /* We can always use pdesc[0], but sockets at position N @@ -678,7 +678,7 @@ static void child_main(int child_num_arg) if (status == APR_EGENERAL) { /* resource shortage or should-not-occur occured */ - clean_child_exit(1); + clean_child_exit(APEXIT_CHILDSICK); } else if (status != APR_SUCCESS) { continue; diff --git a/server/mpm/winnt/child.c b/server/mpm/winnt/child.c index 2e42071f..ae34b07b 100644 --- a/server/mpm/winnt/child.c +++ b/server/mpm/winnt/child.c @@ -352,9 +352,8 @@ reinit: /* target of data or connect upon too many AcceptEx failures */ } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00334) - "Child: Accept thread listening on %s:%d using AcceptFilter %s", - lr->bind_addr->hostname ? lr->bind_addr->hostname : "*", - lr->bind_addr->port, accf_name); + "Child: Accept thread listening on %pI using AcceptFilter %s", + lr->bind_addr, accf_name); while (!shutdown_in_progress) { if (!context) { @@ -650,11 +649,12 @@ reinit: /* target of data or connect upon too many AcceptEx failures */ } } - sockinfo.os_sock = &context->accept_socket; - sockinfo.local = context->sa_server; - sockinfo.remote = context->sa_client; - sockinfo.family = context->sa_server->sa_family; - sockinfo.type = SOCK_STREAM; + sockinfo.os_sock = &context->accept_socket; + sockinfo.local = context->sa_server; + sockinfo.remote = context->sa_client; + sockinfo.family = context->sa_server->sa_family; + sockinfo.type = SOCK_STREAM; + sockinfo.protocol = IPPROTO_TCP; /* Restore the state corresponding to apr_os_sock_make's default * assumption of timeout -1 (really, a flaw of os_sock_make and * os_sock_put that it does not query to determine ->timeout). diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 75f15ebd..548fcaec 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -122,6 +122,7 @@ static int ap_daemons_limit = 0; static int max_workers = 0; static int server_limit = 0; static int thread_limit = 0; +static int had_healthy_child = 0; static int dying = 0; static int workers_may_exit = 0; static int start_thread_may_exit = 0; @@ -1473,6 +1474,7 @@ static void perform_idle_server_maintenance(void) int any_dying_threads = 0; int any_dead_threads = 0; int all_dead_threads = 1; + int child_threads_active = 0; if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate) /* short cut if all active processes have been examined and @@ -1506,10 +1508,11 @@ static void perform_idle_server_maintenance(void) ++idle_thread_count; } if (status >= SERVER_READY && status < SERVER_GRACEFUL) { - ++active_thread_count; + ++child_threads_active; } } } + active_thread_count += child_threads_active; if (any_dead_threads && totally_free_length < retained->idle_spawn_rate && free_length < MAX_SPAWN_RATE && (!ps->pid /* no process in the slot */ @@ -1532,6 +1535,9 @@ static void perform_idle_server_maintenance(void) } ++free_length; } + else if (child_threads_active == threads_per_child) { + had_healthy_child = 1; + } /* XXX if (!ps->quiescing) is probably more reliable GLA */ if (!any_dying_threads) { last_non_dead = i; @@ -1540,21 +1546,23 @@ static void perform_idle_server_maintenance(void) } if (retained->sick_child_detected) { - if (active_thread_count > 0) { - /* some child processes appear to be working. don't kill the - * whole server. + if (had_healthy_child) { + /* Assume this is a transient error, even though it may not be. Leave + * the server up in case it is able to serve some requests or the + * problem will be resolved. */ retained->sick_child_detected = 0; } else { - /* looks like a basket case. give up. + /* looks like a basket case, as no child ever fully initialized; give up. */ shutdown_pending = 1; child_fatal = 1; ap_log_error(APLOG_MARK, APLOG_ALERT, 0, - ap_server_conf, APLOGNO(00285) - "No active workers found..." - " Apache is exiting!"); + ap_server_conf, APLOGNO(02325) + "A resource shortage or other unrecoverable failure " + "was encountered before any child process initialized " + "successfully... httpd is exiting!"); /* the child already logged the failure details */ return; } @@ -1699,6 +1707,11 @@ static void server_main_loop(int remaining_children_to_start) else if (ap_unregister_extra_mpm_process(pid.pid, &old_gen) == 1) { worker_note_child_killed(-1, /* already out of the scoreboard */ pid.pid, old_gen); + if (processed_status == APEXIT_CHILDSICK + && old_gen == retained->my_generation) { + /* resource shortage, minimize the fork rate */ + retained->idle_spawn_rate = 1; + } #if APR_HAS_OTHER_CHILD } else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, @@ -2015,6 +2028,7 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, ap_daemons_limit = server_limit; threads_per_child = DEFAULT_THREADS_PER_CHILD; max_workers = ap_daemons_limit * threads_per_child; + had_healthy_child = 0; ap_extended_status = 0; return OK; diff --git a/server/mpm_unix.c b/server/mpm_unix.c index 87df8f57..65c09407 100644 --- a/server/mpm_unix.c +++ b/server/mpm_unix.c @@ -501,14 +501,14 @@ static apr_status_t pod_signal_internal(ap_pod_t *pod) return rv; } -/* This function connects to the server, then immediately closes the connection. - * This permits the MPM to skip the poll when there is only one listening - * socket, because it provides a alternate way to unblock an accept() when - * the pod is used. - */ +/* This function connects to the server and sends enough data to + * ensure the child wakes up and processes a new connection. This + * permits the MPM to skip the poll when there is only one listening + * socket, because it provides a alternate way to unblock an accept() + * when the pod is used. */ static apr_status_t dummy_connection(ap_pod_t *pod) { - char *srequest; + const char *data; apr_status_t rv; apr_socket_t *sock; apr_pool_t *p; @@ -574,24 +574,37 @@ static apr_status_t dummy_connection(ap_pod_t *pod) return rv; } - /* Create the request string. We include a User-Agent so that - * adminstrators can track down the cause of the odd-looking - * requests in their logs. - */ - srequest = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ", + if (lp->protocol && strcasecmp(lp->protocol, "https") == 0) { + /* Send a TLS 1.0 close_notify alert. This is perhaps the + * "least wrong" way to open and cleanly terminate an SSL + * connection. It should "work" without noisy error logs if + * the server actually expects SSLv3/TLSv1. With + * SSLv23_server_method() OpenSSL's SSL_accept() fails + * ungracefully on receipt of this message, since it requires + * an 11-byte ClientHello message and this is too short. */ + static const unsigned char tls10_close_notify[7] = { + '\x15', /* TLSPlainText.type = Alert (21) */ + '\x03', '\x01', /* TLSPlainText.version = {3, 1} */ + '\x00', '\x02', /* TLSPlainText.length = 2 */ + '\x01', /* Alert.level = warning (1) */ + '\x00' /* Alert.description = close_notify (0) */ + }; + data = (const char *)tls10_close_notify; + len = sizeof(tls10_close_notify); + } + else /* ... XXX other request types here? */ { + /* Create an HTTP request string. We include a User-Agent so + * that adminstrators can track down the cause of the + * odd-looking requests in their logs. A complete request is + * used since kernel-level filtering may require that much + * data before returning from accept(). */ + data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ", ap_get_server_description(), " (internal dummy connection)\r\n\r\n", NULL); + len = strlen(data); + } - /* Since some operating systems support buffering of data or entire - * requests in the kernel, we send a simple request, to make sure - * the server pops out of a blocking accept(). - */ - /* XXX: This is HTTP specific. We should look at the Protocol for each - * listener, and send the correct type of request to trigger any Accept - * Filters. - */ - len = strlen(srequest); - apr_socket_send(sock, srequest, &len); + apr_socket_send(sock, data, &len); apr_socket_close(sock); apr_pool_destroy(p); diff --git a/server/protocol.c b/server/protocol.c index 4fcff4da..30b3cd5e 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -746,19 +746,29 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb * finding the end-of-line. This is only going to happen if it * exceeds the configured limit for a field size. */ - if (rv == APR_ENOSPC && field) { - /* ensure ap_escape_html will terminate correctly */ - field[len - 1] = '\0'; + if (rv == APR_ENOSPC) { + const char *field_escaped; + if (field) { + /* ensure ap_escape_html will terminate correctly */ + field[len - 1] = '\0'; + field_escaped = ap_escape_html(r->pool, field); + } + else { + field_escaped = field = ""; + } + apr_table_setn(r->notes, "error-notes", apr_psprintf(r->pool, "Size of a request header field " "exceeds server limit.<br />\n" "<pre>\n%.*s\n</pre>\n", - field_name_len(field), - ap_escape_html(r->pool, field))); + field_name_len(field_escaped), + field_escaped)); ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00561) - "Request header exceeds LimitRequestFieldSize: " - "%.*s", field_name_len(field), field); + "Request header exceeds LimitRequestFieldSize%s" + "%.*s", + *field ? ": " : "", + field_name_len(field), field); } return; } diff --git a/server/provider.c b/server/provider.c index a5406ab1..a102dd85 100644 --- a/server/provider.c +++ b/server/provider.c @@ -136,7 +136,7 @@ AP_DECLARE(apr_array_header_t *) ap_list_provider_names(apr_pool_t *pool, ap_list_provider_names_t *entry; apr_hash_t *provider_group_hash, *h; apr_hash_index_t *hi; - char *val, *key; + char *val; if (global_providers_names == NULL) { return ret; @@ -157,9 +157,41 @@ AP_DECLARE(apr_array_header_t *) ap_list_provider_names(apr_pool_t *pool, } for (hi = apr_hash_first(pool, h); hi; hi = apr_hash_next(hi)) { - apr_hash_this(hi, (void *)&key, NULL, (void *)&val); + apr_hash_this(hi, NULL, NULL, (void *)&val); entry = apr_array_push(ret); entry->provider_name = apr_pstrdup(pool, val); } return ret; } + +AP_DECLARE(apr_array_header_t *) ap_list_provider_groups(apr_pool_t *pool) +{ + apr_array_header_t *ret = apr_array_make(pool, 10, sizeof(ap_list_provider_groups_t)); + ap_list_provider_groups_t *entry; + apr_hash_t *provider_group_hash; + apr_hash_index_t *groups_hi, *vers_hi; + char *group, *version; + + if (global_providers_names == NULL) + return ret; + + for (groups_hi = apr_hash_first(pool, global_providers_names); + groups_hi; + groups_hi = apr_hash_next(groups_hi)) + { + apr_hash_this(groups_hi, (void *)&group, NULL, (void *)&provider_group_hash); + if (provider_group_hash == NULL) + continue; + for (vers_hi = apr_hash_first(pool, provider_group_hash); + vers_hi; + vers_hi = apr_hash_next(vers_hi)) + { + apr_hash_this(vers_hi, (void *)&version, NULL, NULL); + + entry = apr_array_push(ret); + entry->provider_group = group; + entry->provider_version = version; + } + } + return ret; +} diff --git a/server/request.c b/server/request.c index c2fdd89e..d26d4691 100644 --- a/server/request.c +++ b/server/request.c @@ -236,7 +236,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r) if (r->user == NULL) { /* don't let buggy authn module crash us in authz */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00027) - "Buggy authn provider failed to set user for %s", + "No authentication done but request not " + "allowed without authentication for %s. " + "Authentication not configured?", r->uri); access_status = HTTP_INTERNAL_SERVER_ERROR; return decl_die(access_status, "check user", r); @@ -271,7 +273,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r) if (r->user == NULL) { /* don't let buggy authn module crash us in authz */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00028) - "Buggy authn provider failed to set user for %s", + "No authentication done but request not " + "allowed without authentication for %s. " + "Authentication not configured?", r->uri); access_status = HTTP_INTERNAL_SERVER_ERROR; return decl_die(access_status, "check user", r); @@ -750,7 +754,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r) /* Set aside path_info to merge back onto path_info later. * If r->filename is a directory, we must remerge the path_info, - * before we continue! [Directories cannot, by defintion, have + * before we continue! [Directories cannot, by definition, have * path info. Either the next segment is not-found, or a file.] * * r->path_info tracks the unconsumed source path. diff --git a/server/scoreboard.c b/server/scoreboard.c index 344dc5ae..bef2b909 100644 --- a/server/scoreboard.c +++ b/server/scoreboard.c @@ -488,8 +488,9 @@ static int update_child_status_internal(int child_num, REMOTE_NOLOOKUP, NULL), sizeof(ws->client)); copy_request(ws->request, sizeof(ws->request), r); if (r->server) { - apr_cpystrn(ws->vhost, r->server->server_hostname, - sizeof(ws->vhost)); + apr_snprintf(ws->vhost, sizeof(ws->vhost), "%s:%d", + r->server->server_hostname, + r->connection->local_addr->port); } } else if (c) { diff --git a/server/util.c b/server/util.c index 177f3780..75e91a7b 100644 --- a/server/util.c +++ b/server/util.c @@ -2393,7 +2393,7 @@ AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f, /* sanity check - we only support forms for now */ ct = apr_table_get(r->headers_in, "Content-Type"); - if (!ct || strcmp("application/x-www-form-urlencoded", ct)) { + if (!ct || strncasecmp("application/x-www-form-urlencoded", ct, 33)) { return ap_discard_request_body(r); } diff --git a/server/util_mutex.c b/server/util_mutex.c index 0ff9483d..e49cca55 100644 --- a/server/util_mutex.c +++ b/server/util_mutex.c @@ -157,7 +157,7 @@ AP_DECLARE_NONSTD(void) ap_mutex_init(apr_pool_t *p) /* initialize default mutex configuration */ def = apr_pcalloc(p, sizeof *def); def->mech = APR_LOCK_DEFAULT; - def->dir = DEFAULT_REL_RUNTIMEDIR; + def->dir = ap_runtime_dir_relative(p, ""); apr_hash_set(mxcfg_by_type, "default", APR_HASH_KEY_STRING, def); } diff --git a/server/util_script.c b/server/util_script.c index 18c4aea4..703d1600 100644 --- a/server/util_script.c +++ b/server/util_script.c @@ -553,7 +553,7 @@ AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer, if (!(l = strchr(w, ':'))) { if (!buffer) { /* Soak up all the script output - may save an outright kill */ - while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) { + while ((*getsfunc)(w, MAX_STRING_LEN - 1, getsfunc_data) > 0) { continue; } } @@ -592,11 +592,11 @@ AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer, if (!ap_is_HTTP_VALID_RESPONSE(cgi_status)) ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, "Invalid status line from script '%s': %s", - apr_filepath_name_get(r->filename), w); + apr_filepath_name_get(r->filename), l); else ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r, "Status line from script '%s': %s", - apr_filepath_name_get(r->filename), w); + apr_filepath_name_get(r->filename), l); r->status_line = apr_pstrdup(r->pool, l); } else if (!strcasecmp(w, "Location")) { @@ -672,7 +672,8 @@ static int getsfunc_BRIGADE(char *buf, int len, void *arg) apr_status_t rv; int done = 0; - while ((dst < dst_end) && !done && !APR_BUCKET_IS_EOS(e)) { + while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb) + && !APR_BUCKET_IS_EOS(e)) { const char *bucket_data; apr_size_t bucket_data_len; const char *src; @@ -706,7 +707,7 @@ static int getsfunc_BRIGADE(char *buf, int len, void *arg) e = next; } *dst = 0; - return 1; + return done; } AP_DECLARE(int) ap_scan_script_header_err_brigade(request_rec *r, |