Project

General

Profile

Bug #1825 » r2632.mod_fastcgi.patch

mod_fastcgi patch to 1.4.23 - azilber, 2009-10-15 21:30

View differences:

mod_fastcgi.c 2009-10-14 16:25:27.000000000 -0500
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <signal.h>
#include "buffer.h"
#include "server.h"
#include "keyvalue.h"
......
#include "stat_cache.h"
#include "status_counter.h"
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <signal.h>
#ifdef HAVE_FASTCGI_FASTCGI_H
#include <fastcgi/fastcgi.h>
# include <fastcgi/fastcgi.h>
#else
#ifdef HAVE_FASTCGI_H
#include <fastcgi.h>
#else
#include "fastcgi.h"
#endif
# ifdef HAVE_FASTCGI_H
# include <fastcgi.h>
# else
# include "fastcgi.h"
# endif
#endif /* HAVE_FASTCGI_FASTCGI_H */
#include <stdio.h>
#ifdef HAVE_SYS_FILIO_H
......
*
*/
unsigned short min_procs;
unsigned short max_procs;
size_t num_procs; /* how many procs are started */
size_t active_procs; /* how many of them are really running */
unsigned short max_load_per_proc;
/*
* kick the process from the list if it was not
* used for idle_timeout until min_procs is
* reached. this helps to get the processlist
* small again we had a small peak load.
*
*/
unsigned short idle_timeout;
/*
* time after a disabled remote connection is tried to be re-enabled
*
......
/* ok, we need a prototype */
static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents);
static void reset_signals(void) {
#ifdef SIGTTOU
signal(SIGTTOU, SIG_DFL);
#endif
#ifdef SIGTTIN
signal(SIGTTIN, SIG_DFL);
#endif
#ifdef SIGTSTP
signal(SIGTSTP, SIG_DFL);
#endif
signal(SIGHUP, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
signal(SIGUSR1, SIG_DFL);
}
static void fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) {
buffer_copy_string_len(b, CONST_STR_LEN("fastcgi.backend."));
buffer_append_string_buffer(b, host->id);
......
close(fcgi_fd);
}
openDevNull(STDERR_FILENO);
/* we don't need the client socket */
for (i = 3; i < 256; i++) {
close(i);
......
*c = '/';
}
reset_signals();
/* exec the cgi */
execve(arg.ptr[0], arg.ptr, env.ptr);
......
{ "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
{ "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
{ "min-procs-not-working", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 this is broken for now */
{ "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
{ "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
{ "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
{ "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
{ "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
{ "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
{ "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
{ "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
{ "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
{ "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */
{ "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
{ "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
{ "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
{ "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
{ "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
{ "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
{ "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
{ "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
{ "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
{ "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
......
buffer_copy_string_buffer(host->id, da_host->key);
host->check_local = 1;
host->min_procs = 4;
host->max_procs = 4;
host->max_load_per_proc = 1;
host->idle_timeout = 60;
host->mode = FCGI_RESPONDER;
host->disable_time = 60;
host->disable_time = 1;
host->break_scriptfilename_for_php = 0;
host->allow_xsendfile = 0; /* handle X-LIGHTTPD-send-file */
host->kill_signal = SIGTERM;
......
fcv[5].destination = &(host->check_local);
fcv[6].destination = &(host->port);
fcv[7].destination = &(host->min_procs);
fcv[8].destination = &(host->max_procs);
fcv[9].destination = &(host->max_load_per_proc);
fcv[10].destination = &(host->idle_timeout);
fcv[11].destination = &(host->disable_time);
fcv[12].destination = host->bin_env;
fcv[13].destination = host->bin_env_copy;
fcv[14].destination = &(host->break_scriptfilename_for_php);
fcv[15].destination = &(host->allow_xsendfile);
fcv[16].destination = host->strip_request_uri;
fcv[17].destination = &(host->kill_signal);
fcv[18].destination = &(host->fix_root_path_name);
fcv[7].destination = &(host->max_procs);
fcv[8].destination = &(host->disable_time);
fcv[9].destination = host->bin_env;
fcv[10].destination = host->bin_env_copy;
fcv[11].destination = &(host->break_scriptfilename_for_php);
fcv[12].destination = &(host->allow_xsendfile);
fcv[13].destination = host->strip_request_uri;
fcv[14].destination = &(host->kill_signal);
fcv[15].destination = &(host->fix_root_path_name);
if (0 != config_insert_values_internal(srv, da_host->value, fcv)) {
return HANDLER_ERROR;
......
/* a local socket + self spawning */
size_t pno;
/* HACK: just to make sure the adaptive spawing is disabled */
host->min_procs = host->max_procs;
if (host->min_procs > host->max_procs) host->max_procs = host->min_procs;
if (host->max_load_per_proc < 1) host->max_load_per_proc = 0;
if (s->debug) {
log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",
log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsd",
"--- fastcgi spawning local",
"\n\tproc:", host->bin_path,
"\n\tport:", host->port,
"\n\tsocket", host->unixsocket,
"\n\tmin-procs:", host->min_procs,
"\n\tmax-procs:", host->max_procs);
}
for (pno = 0; pno < host->min_procs; pno++) {
for (pno = 0; pno < host->max_procs; pno++) {
fcgi_proc *proc;
proc = fastcgi_process_init();
......
"--- fastcgi spawning",
"\n\tport:", host->port,
"\n\tsocket", host->unixsocket,
"\n\tcurrent:", pno, "/", host->min_procs);
"\n\tcurrent:", pno, "/", host->max_procs);
}
if (fcgi_spawn_connection(srv, p, host, proc)) {
......
host->first = proc;
host->min_procs = 1;
host->max_procs = 1;
}
......
log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
"--- fastcgi spawning",
"\n\tsocket", proc->connection_name,
"\n\tcurrent:", 1, "/", host->min_procs);
"\n\tcurrent:", 1, "/", host->max_procs);
}
if (fcgi_spawn_connection(srv, p, host, proc)) {
......
* - tcp socket (do not check host->host->uses, as it may be not set which means INADDR_LOOPBACK)
* - unix socket
*/
if (!host ||
(!host->port && !host->unixsocket->used)) {
log_error_write(srv, __FILE__, __LINE__, "sxddd",
"write-req: error",
host,
host->host->used,
host->port,
host->unixsocket->used);
hctx->proc->disabled_until = srv->cur_ts + 10;
hctx->proc->state = PROC_STATE_DIED;
if (!host) {
log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL");
return HANDLER_ERROR;
}
if ((!host->port && !host->unixsocket->used)) {
log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: neither host->port nor host->unixsocket is set");
return HANDLER_ERROR;
}
......
log_error_write(srv, __FILE__, __LINE__, "ss",
"getsockopt failed:", strerror(errno));
hctx->proc->disabled_until = srv->cur_ts + 10;
hctx->proc->state = PROC_STATE_DIED;
if (hctx->host->disable_time) {
hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
hctx->proc->state = PROC_STATE_DIED;
}
return HANDLER_ERROR;
}
......
"socket:", hctx->proc->connection_name);
}
hctx->proc->disabled_until = srv->cur_ts + 5;
if (hctx->proc->is_local) {
hctx->proc->state = PROC_STATE_DIED_WAIT_FOR_PID;
} else {
if (hctx->host->disable_time) {
hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
hctx->proc->state = PROC_STATE_DIED;
}
hctx->proc->state = PROC_STATE_DIED;
fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
......
/* cool down the backend, it is overloaded
* -> EAGAIN */
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
"backend is overloaded; we'll disable it for 2 seconds and send the request to another backend instead:",
"reconnects:", hctx->reconnects,
"load:", host->load);
if (hctx->host->disable_time) {
log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
"backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
"reconnects:", hctx->reconnects,
"load:", host->load);
hctx->proc->disabled_until = srv->cur_ts + 2;
hctx->proc->state = PROC_STATE_OVERLOADED;
hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
hctx->proc->state = PROC_STATE_OVERLOADED;
}
fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
buffer_append_string_len(p->statuskey, CONST_STR_LEN(".overloaded"));
......
* - ECONNREFUSED for tcp-ip sockets
* - ENOENT for unix-domain-sockets
*
* for check if the host is back in 5 seconds
* for check if the host is back in hctx->host->disable_time seconds
* */
hctx->proc->disabled_until = srv->cur_ts + 5;
if (hctx->proc->is_local) {
hctx->proc->state = PROC_STATE_DIED_WAIT_FOR_PID;
} else {
hctx->proc->state = PROC_STATE_DIED;
}
if (hctx->host->disable_time) {
hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
if (hctx->proc->is_local) {
hctx->proc->state = PROC_STATE_DIED_WAIT_FOR_PID;
} else {
hctx->proc->state = PROC_STATE_DIED;
}
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
"backend died; we'll disable it for 5 seconds and send the request to another backend instead:",
"reconnects:", hctx->reconnects,
"load:", host->load);
log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
"backend died; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
"reconnects:", hctx->reconnects,
"load:", host->load);
}
fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
......
"reconnect attempts:", hctx->reconnects);
return HANDLER_ERROR;
case EAGAIN:
case EINTR:
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
return HANDLER_WAIT_FOR_EVENT;
default:
log_error_write(srv, __FILE__, __LINE__, "ssd",
"write failed:", strerror(errno), errno);
......
log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
"--- fastcgi spawning",
"\n\tsocket", proc->connection_name,
"\n\tcurrent:", 1, "/", host->min_procs);
"\n\tcurrent:", 1, "/", host->max_procs);
}
if (fcgi_spawn_connection(srv, p, host, proc)) {
......
hctx->reconnects < 5) {
fcgi_reconnect(srv, hctx);
log_error_write(srv, __FILE__, __LINE__, "ssbsbs",
log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
"response not received, request not sent",
"on socket:", proc->connection_name,
"for", con->uri.path, ", reconnecting");
"for", con->uri.path, "?", con->uri.query, ", reconnecting");
return HANDLER_WAIT_FOR_FD;
}
log_error_write(srv, __FILE__, __LINE__, "sosbsbs",
log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
"response not received, request sent:", hctx->wb->bytes_out,
"on socket:", proc->connection_name,
"for", con->uri.path, ", closing connection");
"for", con->uri.path, "?", con->uri.query, ", closing connection");
fcgi_connection_close(srv, hctx);
......
/* response might have been already started, kill the connection */
fcgi_connection_close(srv, hctx);
log_error_write(srv, __FILE__, __LINE__, "ssbsbs",
log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
"response already sent out, but backend returned error",
"on socket:", proc->connection_name,
"for", con->uri.path, ", terminating connection");
"for", con->uri.path, "?", con->uri.query, ", terminating connection");
connection_set_state(srv, con, CON_STATE_ERROR);
}
......
* even if the FCGI_FIN packet is not received yet
*/
} else {
log_error_write(srv, __FILE__, __LINE__, "sbsbsd",
log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd",
"error: unexpected close of fastcgi connection for",
con->uri.path,
con->uri.path, "?", con->uri.query,
"(no fastcgi process on socket:", proc->connection_name, "?)",
hctx->state);
......
/* check if extension matches */
for (k = 0; k < p->conf.exts->used; k++) {
size_t ct_len; /* length of the config entry */
fcgi_extension *ext = p->conf.exts->exts[k];
extension = p->conf.exts->exts[k];
if (ext->key->used == 0) continue;
if (extension->key->used == 0) continue;
ct_len = extension->key->used - 1;
ct_len = ext->key->used - 1;
/* check _url_ in the form "/fcgi_pattern" */
if (extension->key->ptr[0] == '/') {
if (ext->key->ptr[0] == '/') {
if ((ct_len <= con->uri.path->used -1) &&
(strncmp(con->uri.path->ptr, extension->key->ptr, ct_len) == 0))
(strncmp(con->uri.path->ptr, ext->key->ptr, ct_len) == 0)) {
extension = ext;
break;
} else if ((ct_len <= s_len) && (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len))) {
}
} else if ((ct_len <= s_len) && (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len))) {
/* check extension in the form ".fcg" */
extension = ext;
break;
}
}
/* extension doesn't match */
if (k == p->conf.exts->used) {
if (NULL == extension) {
return HANDLER_GO_ON;
}
}
......
if (!extension->note_is_sent) {
extension->note_is_sent = 1;
log_error_write(srv, __FILE__, __LINE__, "sbsbs",
"all handlers for ", con->uri.path,
log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
"all handlers for", con->uri.path, "?", con->uri.query,
"on", extension->key,
"are down.");
}
......
for (n = 0; n < ex->used; n++) {
fcgi_proc *proc;
unsigned long sum_load = 0;
fcgi_extension_host *host;
host = ex->hosts[n];
fcgi_restart_dead_procs(srv, p, host);
for (proc = host->first; proc; proc = proc->next) {
sum_load += proc->load;
}
if (host->num_procs &&
host->num_procs < host->max_procs &&
(sum_load / host->num_procs) > host->max_load_per_proc) {
/* overload, spawn new child */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s",
"overload detected, spawning a new child");
}
for (proc = host->unused_procs; proc && proc->pid != 0; proc = proc->next);
if (proc) {
if (proc == host->unused_procs) host->unused_procs = proc->next;
if (proc->next) proc->next->prev = NULL;
host->max_id++;
} else {
proc = fastcgi_process_init();
proc->id = host->max_id++;
}
host->num_procs++;
if (buffer_is_empty(host->unixsocket)) {
proc->port = host->port + proc->id;
} else {
buffer_copy_string_buffer(proc->unixsocket, host->unixsocket);
buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
buffer_append_long(proc->unixsocket, proc->id);
}
if (fcgi_spawn_connection(srv, p, host, proc)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"ERROR: spawning fcgi failed.");
return HANDLER_ERROR;
}
proc->prev = NULL;
proc->next = host->first;
if (host->first) {
host->first->prev = proc;
}
host->first = proc;
}
for (proc = host->first; proc; proc = proc->next) {
if (proc->load != 0) break;
if (host->num_procs <= host->min_procs) break;
if (proc->pid == 0) continue;
if (srv->cur_ts - proc->last_used > host->idle_timeout) {
/* a proc is idling for a long time now,
* terminate it */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "ssbsd",
"idle-timeout reached; terminating child:",
"socket:", proc->connection_name,
"pid", proc->pid);
}
if (proc->next) proc->next->prev = proc->prev;
if (proc->prev) proc->prev->next = proc->next;
if (proc->prev == NULL) host->first = proc->next;
proc->prev = NULL;
proc->next = host->unused_procs;
if (host->unused_procs) host->unused_procs->prev = proc;
host->unused_procs = proc;
kill(proc->pid, SIGTERM);
proc->state = PROC_STATE_KILLED;
log_error_write(srv, __FILE__, __LINE__, "ssbsd",
"killed:",
"socket:", proc->connection_name,
"pid", proc->pid);
host->num_procs--;
/* proc is now in unused, let the next second handle the next process */
break;
}
}
for (proc = host->unused_procs; proc; proc = proc->next) {
int status;
(1-1/3)