From 266ef2f94449d82a73846e216af5088cc5651d0c Mon Sep 17 00:00:00 2001 From: Barry Jaspan Date: Wed, 30 Apr 2014 18:41:05 -0400 Subject: [PATCH 1/3] Fix child process forking bug in ondemand, and support ondemand on all platforms. --- sapi/fpm/DESIGN.md | 186 +++++++++++++++++++++++++++++++++ sapi/fpm/fpm/fastcgi.c | 65 ++++++++++-- sapi/fpm/fpm/fastcgi.h | 2 +- sapi/fpm/fpm/fpm_children.c | 30 +++++- sapi/fpm/fpm/fpm_conf.c | 5 - sapi/fpm/fpm/fpm_main.c | 14 ++- sapi/fpm/fpm/fpm_process_ctl.c | 55 +++++++++- sapi/fpm/fpm/fpm_process_ctl.h | 3 + sapi/fpm/fpm/fpm_request.c | 18 ++++ sapi/fpm/fpm/fpm_request.h | 1 + sapi/fpm/fpm/fpm_scoreboard.c | 1 + sapi/fpm/fpm/fpm_worker_pool.h | 3 +- 12 files changed, 365 insertions(+), 18 deletions(-) create mode 100644 sapi/fpm/DESIGN.md diff --git a/sapi/fpm/DESIGN.md b/sapi/fpm/DESIGN.md new file mode 100644 index 0000000000000..370f0a220d9b6 --- /dev/null +++ b/sapi/fpm/DESIGN.md @@ -0,0 +1,186 @@ +# Introduction + +This document describes the design of PHP-FPM. It is woefully incomplete, but +at least we are now starting. + +## Request stages + +FPM defines a set of request stages for tracking a child process' current +status: + +* FPM_REQUEST_ACCEPTING: About to call or blocked in accept() on the pool's + listening socket. The parent considers a child in this stage to be idle. +* FPM_REQUEST_ACCEPTED: Just returned from accept() but not yet in + READING_HEADERS. This stage exists so that ondemand workers can enter it + before signaling the parent that they returned from accept(). Without it, a + race condition exists in which the parent could decide the child was idle + just *before* it began processing a request, and thus not start listening on + the pool's socket again. +* FPM_REQUEST_READING_HEADERS: +* FPM_REQUEST_INFO: +* FPM_REQUEST_EXECUTING: +* FPM_REQUEST_END: +* FPM_REQUEST_FINISHED: + + +## Scoreboard + +### Allocation + +FPM maintains a scoreboard data structure that tracks information about the +current state and accumulated activities of workers. Each fpm_worker_pool_s +contains one fpm_scoreboard_s structure, and wp->scoreboard->procs is an array +of wp->config->mp_max_children pointers to fpm_scoreboard_proc_s +structures. All of these structures are stored in shared memory allocated with +mmap(MAP_ANONYMOUS|MAP_SHARED). + +The scoreboard proc slots are allocated as needed to children by +fpm_scoreboard_proc_alloc() and _free(), which are non-locked functions that +can only be called by the parent. Each scoreboard proc has a boolean used +flag. The scoreboard maintains an index, free_proc, which indicates that a proc +that might be unused. A scoreboard proc is allocated to a specific child +process by fpm_scoreboard_proc_alloc(). This function first checks whether +scoreboard->procs[scoreboard->free_proc] is unused, and if it is scans through +the array looking for one that is unused. Whichever way it finds one, it marks +that proc used and provides the now-allocated index to its caller. The +free_proc hint is then incremented one (circularly) to suggest a new free +proc. fpm_scoreboard_proc_free() releases a proc slot by memset()ing it to +zero, and setting scoreboard->free_proc to the just-released index. + +Just before the parent forks a new child, it allocated a scoreboard proc for +the impending child in fpm_resources_prepare(). Once the child is forked, +fpm_child_resources_use() first frees the scoreboard for all other worker pools +(to prevent accidentally updating them, presumably), and calls +fpm_scoreboard_child_use() which sets the global fpm_scoreboard pointer to the +worker's scoreboard and fpm_scoreboard_i to the child's allocated index in the +worker's scoreboard->procs array. Finally, the child's scoreboard proc is +updated with the child's pid and start time. + +A scoreboard proc is atomically locked and unlocked with +fpm_scoreboard_proc_acquire() and _release(). The parent specifies the worker +pool scoreboard and proc index to lock. A child specifies sentinel values that +instruct the functions to use the global fpm_scoreboard and fpm_scoreboard_i +variables. + +### Tracking request stages + +Each scoreboard proc structure has a field, enum fpm_request_stage_e +request_stage, to track the current stage. Functions such as +fpm_request_accepting() are called from a child to lock the child's scoreboard +proc, update the request stage, set whatever other proc values are appropriate +for the stage, and release the proc. + +The parent can inspect a child's current stage via functions like +fpm_request_is_idle(), which accepts a specific fpm_child_s structure, +retrieves its scoreboard proc, and checks the proc's request_stage. + +Note that fpm_request_is_idle() explicitly DOES NOT lock the child's proc +structure before reading the request_stage. In Java, this would mean that the +parent had no guarantee of reading the current value. In C, with no specific +memory model, ... all bets are off, I suspect. + +## Process management styles + +There are three supported process management styles, but they all share a +common flow. In main(), the parent calls fpm_run(). fpm_run() forks initial +worker processes for each pool, if any, with fpm_children_create_initial(), and +calls fpm_event_loop() which runs forever. + +The forked children return from fpm_run(), inheriting their listening socket +from the parent. They do a little more initialization, and enter their main loop +calling fcgi_accept_request(). Each call to that function calls accept() on +the socket and then reads and processes a single FastCGI request. + +### Static + +### Dynamic + +### Ondemand + +No initial worker processes are started. Instead, fpm_children_create_initial() +installs an FPM_EV_READ event on the pool's listening socket. When a connection +arrives, the event calls fpm_pctl_on_socket_accept(). If there is an idle child +available to run the request, the function does nothing; an idle child is +already waiting in accept() and will respond. Otherwise, if the current system +state allows it, the function forks a new child; if not, the request function +does nothing, and the request stays queued until the next child is +available. Either way, the parent trusts that a newly launched or existing +child will eventually handle the request. + +The strategy for deciding when to fork is subtle. The quick summary: + +* fpm_pctl_on_socket_accept() always removes the pool's listening socket, +* a child always restores its pool's listening socket after calling accept(), +* a child's first call to accept() is non-blocking, +* a pool's listening socket is always restore when a child exits, and +* a timer restores listening sockets removed due to external system state. + +In detail: + +Suppose FPM uses a normal level-triggered select()/poll() on the listening +socket. When a connection arrives, fpm_pctl_on_socket_accept() is triggered by +the event system. It finds no idle children, so it forks one, and returns. If +the parent polls again before the child makes it to accept(), the connection is +still pending so fpm_pctl_on_socket_accept() is called again. The child is +idle, so the function returns, but until the child calls accept(), the parent +will spin in this loop. Also, there are a race conditions in which loop occurs +before the child is marked idle (which happens immediately prior to its calling +accept()), in which case the parent will rapidly fork extra processes. + +To avoid this, the initial version of ondemand used edge-triggering on the +listening socket (and thus only worked on systems that support epoll() or +kqueue()). This prevents the spin loop because each pending connection is only +returned once, and thus only triggers one call to fpm_pctl_on_socket_accept() +no matter how long the child takes to accept() it. Unfortunately, it causes +different problems. Edge triggering semantics requires that the polling process +read the edge-triggered file until it is empty; in this case, that requires +calling accept() until it returns EAGAIN/EWOULDBLOCK. However, with ondemand, +the parent process polls the listening socket, while the child processes call +accept(), so there is no way for the parent to comply with the edge-triggering +contract. As a result, multiple requests can arrive, generate only a single +edge-triggered event, and launch insufficient processes to handle the requests. + +The solution is to use level-triggered polling in a way that avoids spinning +the CPU. The trick is for the parent to remove a pool's listening socket from +its polled set during necessary periods, and to restore it when appropriate. To +facilitate restoring the listening socket, during +fpm_children_create_initial(), the parent creates a per-pool child_accept pipe +and registers an event to call fpm_pctl_on_child_accept() when the pipe is +readable. When invoked, this function reads sizeof(pid_t) from one of the +children and adds the listening socket event back to the polling set. + +For removing the listening socket event, there are three cases to +consider. Each time a request arrives and fpm_pctl_on_socket_accept() is +invoked: + +* An idle child is available. The parent removes the listening socket. When the +idle child returns from accept(), it writes to the child_accept pipe, restoring +the listening socket. + +* No idle child is available, and the parent forks a new child. The parent +removes the listening socket. The child starts and, after calling accept(), it +writes to the child_accept pipe. For this to work, the first call to accept() +must be non-blocking---a sibling process may grab the request first, and the +new child must inform the parent it called accept() so new requests can be +processed whether accept() suceeded or failed. + +* No idle child is available, but the parent cannot fork a new child due to +system state (such as already having pm_max_procs children). If there are zero +children and the parent cannot fork one, the parent exits with an error +status. Otherwise, the parent again removes the listening socket, because there +is no point in acting on a pending request when there is no child process to +run it (since level triggering is in place, any pending requests will again +generate a poll event when the socket is restored). The listening socket event +is restored when: + + * A child exits for any reason, since now a new one can possiby be + started. The SIGCHLD triggers a call to fpm_children_bury(), which looks + up the child's fpm_child_s record, and writes to the child's child_accept + pipe regardless of why it exited. + + * A child becomes idle, since it may empty the pending queue. An idle child + immediately calls accept(), by definition. If there is still a pending + request, accept() returns, and as per above, the child writes to its + child_accept pipe. If there is no pending request, it is okay that the + parent continues ignoring listening socket; the next request to arrive + will go to an idle child which will write to its child_accept pipe. diff --git a/sapi/fpm/fpm/fastcgi.c b/sapi/fpm/fpm/fastcgi.c index d77b6f8ca709d..8db600fee1e61 100644 --- a/sapi/fpm/fpm/fastcgi.c +++ b/sapi/fpm/fpm/fastcgi.c @@ -759,7 +759,35 @@ void fcgi_close(fcgi_request *req, int force, int destroy) } } -int fcgi_accept_request(fcgi_request *req) +/** + * Set a file descriptor to be blocking or non-blocking. + * + * @param fd: the file descriptor + * @param nonblocking: zero to set blocking, non-zero to set nonblocking + * @returns 0 on success, <0 on error + */ +int fcgi_set_nonblocking(int fd, int nonblocking) +{ + int flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + return flags; + } + flags = nonblocking ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK); + return fcntl(fd, F_SETFL, flags); +} + +/** + * Accept a connect and parse a FastCGI request. + * + * @param req: the request structure to fill in + * @param nonblock_once: if non-zero, the listening socket is set nonblocking + * for one call to accept(); if accept() fails with EAGAIN/EWOULDBLOCK, it is + * called a second time in blocking mode. + * @param accept_pipe: if non-negative, write the pid to this fd every time + * accept() returns, regardless of its return value. + * @returns the accepted fd, or -1 on error. + */ +int fcgi_accept_request(fcgi_request *req, int nonblock_once, int accept_pipe) { #ifdef _WIN32 HANDLE pipe; @@ -801,12 +829,37 @@ int fcgi_accept_request(fcgi_request *req) #endif sa_t sa; socklen_t len = sizeof(sa); + int accept_errno; + do { + fpm_request_accepting(); - fpm_request_accepting(); - - FCGI_LOCK(req->listen_socket); - req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len); - FCGI_UNLOCK(req->listen_socket); + FCGI_LOCK(req->listen_socket); + if (nonblock_once) { + fcgi_set_nonblocking(req->listen_socket, 1); + } + req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len); + accept_errno = errno; + if (nonblock_once) { + fcgi_set_nonblocking(req->listen_socket, 0); + nonblock_once = 0; + } + // Mark ourselves as no longer ACCEPTING before telling + // the parent to restore our listening + // socket. Otherwise, the parent can decide we are + // still idle and about to grab a new request when we + // are not. + fpm_request_accepted(); + if (accept_pipe >= 0) { + pid_t pid = getpid(); + if (write(accept_pipe, &pid, sizeof(pid)) != sizeof(pid)) { + zlog(ZLOG_ERROR, "Failed writing to accept_pipe; something bad is going to happen."); + } + } + if (req->fd < 0 && (accept_errno == EAGAIN || accept_errno == EWOULDBLOCK)) { + zlog(ZLOG_DEBUG, "Child pid %d lost the accept() race, retrying", getpid()); + } + FCGI_UNLOCK(req->listen_socket); + } while (req->fd < 0 && (accept_errno == EAGAIN || accept_errno == EWOULDBLOCK)); client_sa = sa; if (sa.sa.sa_family == AF_INET && req->fd >= 0 && allowed_clients) { diff --git a/sapi/fpm/fpm/fastcgi.h b/sapi/fpm/fpm/fastcgi.h index 34f9eef9da9ea..f34ede93b96fa 100644 --- a/sapi/fpm/fpm/fastcgi.h +++ b/sapi/fpm/fpm/fastcgi.h @@ -115,7 +115,7 @@ typedef struct _fcgi_request { int fcgi_init(void); void fcgi_shutdown(void); void fcgi_init_request(fcgi_request *req, int listen_socket); -int fcgi_accept_request(fcgi_request *req); +int fcgi_accept_request(fcgi_request *req, int nonblock_once, int accept_pipe); int fcgi_finish_request(fcgi_request *req, int force_close); void fcgi_set_in_shutdown(int); diff --git a/sapi/fpm/fpm/fpm_children.c b/sapi/fpm/fpm/fpm_children.c index 45cc075b42a6c..81bd21d7b6d27 100644 --- a/sapi/fpm/fpm/fpm_children.c +++ b/sapi/fpm/fpm/fpm_children.c @@ -146,8 +146,9 @@ static struct fpm_child_s *fpm_child_find(pid_t pid) /* {{{ */ static void fpm_child_init(struct fpm_worker_pool_s *wp) /* {{{ */ { fpm_globals.max_requests = wp->config->pm_max_requests; - - if (0 > fpm_stdio_init_child(wp) || + + if (0 > fpm_pctl_init_child(wp) || + 0 > fpm_stdio_init_child(wp) || 0 > fpm_log_init_child(wp) || 0 > fpm_status_init_child(wp) || 0 > fpm_unix_init_child(wp) || @@ -187,6 +188,13 @@ void fpm_children_bury() /* {{{ */ child = fpm_child_find(pid); + if (child->wp->config->pm == PM_STYLE_ONDEMAND) { + pid_t pid = getpid(); + if (write(child->wp->child_accept_pipe[1], &pid, sizeof(pid)) != sizeof(pid)) { + zlog(ZLOG_ERROR, "Failed writing to accept_pipe; something bad is going to happen."); + } + } + if (WIFEXITED(status)) { snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status)); @@ -444,10 +452,26 @@ int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */ } memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s)); - fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp); + fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ, fpm_pctl_on_socket_accept, wp); wp->socket_event_set = 1; fpm_event_add(wp->ondemand_event, 0); + if (pipe(wp->child_accept_pipe) < 0) { + zlog(ZLOG_ERROR, "[pool %s] unable to create child_accept_pipe", wp->config->name); + // FIXME handle crash + return 1; + } + + wp->child_accept_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s)); + if (!wp->child_accept_event) { + zlog(ZLOG_ERROR, "[pool %s] unable to malloc the child_accept socket event", wp->config->name); + // FIXME handle crash + return 1; + } + memset(wp->child_accept_event, 0, sizeof(struct fpm_event_s)); + fpm_event_set(wp->child_accept_event, wp->child_accept_pipe[0], FPM_EV_READ, fpm_pctl_on_child_accept, wp); + fpm_event_add(wp->child_accept_event, 0); + return 1; } return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1); diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index 688c6402505e5..2d9809c4ec7d2 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -822,11 +822,6 @@ static int fpm_conf_process_all_pools() /* {{{ */ } else if (wp->config->pm == PM_STYLE_ONDEMAND) { struct fpm_worker_pool_config_s *config = wp->config; - if (!fpm_event_support_edge_trigger()) { - zlog(ZLOG_ALERT, "[pool %s] ondemand process manager can ONLY be used when events.mechanisme is either epoll (Linux) or kqueue (*BSD).", wp->config->name); - return -1; - } - if (config->pm_process_idle_timeout < 1) { zlog(ZLOG_ALERT, "[pool %s] pm.process_idle_timeout(%ds) must be greater than 0s", wp->config->name, config->pm_process_idle_timeout); return -1; diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c index e8793259b4c50..59ccf9da0d875 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -1857,7 +1857,19 @@ consult the installation file that came with this distribution, or visit \n\ fcgi_init_request(&request, fcgi_fd); zend_first_try { - while (fcgi_accept_request(&request) >= 0) { + // TODO: There has to be a better way to pass this info from the parent + // to the child. I should be able to access the fpm_worker_pool_s. + int pm, accept_pipe[2]; + fpm_pctl_child_info(&pm, accept_pipe); + int first_accept_nonblocking = 0, child_accept_fd = -1; + if (pm == PM_STYLE_ONDEMAND) { + // TODO: move this close to some setup routine somewhere + close(accept_pipe[0]); + first_accept_nonblocking = 1; + child_accept_fd = accept_pipe[1]; + } + while (fcgi_accept_request(&request, first_accept_nonblocking, child_accept_fd) >= 0) { + first_accept_nonblocking = 0; request_body_fd = -1; SG(server_context) = (void *) &request; init_request_info(TSRMLS_C); diff --git a/sapi/fpm/fpm/fpm_process_ctl.c b/sapi/fpm/fpm/fpm_process_ctl.c index 76ea4d358e67d..4fb23e4336cbb 100644 --- a/sapi/fpm/fpm/fpm_process_ctl.c +++ b/sapi/fpm/fpm/fpm_process_ctl.c @@ -507,6 +507,10 @@ void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) / wp->socket_event_set = 0; + // Remove the listening socket from the epoll set. We'll add it back + // when we read from wp->child_accept_pipe. + fpm_event_del(wp->ondemand_event); + /* zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/ if (wp->running_children >= wp->config->pm_max_children) { @@ -520,7 +524,7 @@ void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) / } for (child = wp->children; child; child = child->next) { - /* if there is at least on idle child, it will handle the connection, stop here */ + /* if there is at least one idle child, it will handle the connection, stop here */ if (fpm_request_is_idle(child)) { return; } @@ -537,3 +541,52 @@ void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) / } /* }}} */ +void fpm_pctl_on_child_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */ +{ + struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg; + pid_t pid; + + if (fpm_globals.parent_pid != getpid()) { + /* prevent a event race condition when child process + * have not set up its own event loop */ + return; + } + + // TODO: Isn't this redundant with the check above? + if (fpm_globals.is_child) { + return; + } + + if (read(wp->child_accept_pipe[0], &pid, sizeof(pid)) != sizeof(pid)) { + zlog(ZLOG_ERROR, "[pool %s] error %d reading child accept pipe", wp->config->name, errno); + } + + // Add the listening socket back to the epoll set. + fpm_event_add(wp->ondemand_event, 0); + + zlog(ZLOG_DEBUG, "[pool %s] listening on pool after child %d signaled", wp->config->name, pid); +} +/* }}} */ + +// TODO: There has to be a better way to pass this info from the parent +// to the child. I should be able to access the fpm_worker_pool_s. +int fpm_pctl_child_pm, fpm_pctl_child_accept_pipe[2]; +int fpm_pctl_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ +{ + fpm_pctl_child_pm = wp->config->pm; + memcpy(fpm_pctl_child_accept_pipe, wp->child_accept_pipe, sizeof(fpm_pctl_child_accept_pipe)); + return 0; +} +/* }}} */ + +void fpm_pctl_child_info(int *pm, int *pipe) /* {{{ */ +{ + *pm = fpm_pctl_child_pm; + memcpy(pipe, fpm_pctl_child_accept_pipe, sizeof(fpm_pctl_child_accept_pipe)); +} +/* }}} */ + + + + + diff --git a/sapi/fpm/fpm/fpm_process_ctl.h b/sapi/fpm/fpm/fpm_process_ctl.h index 86a6ef0dfbb4b..0ff9249242bb0 100644 --- a/sapi/fpm/fpm/fpm_process_ctl.h +++ b/sapi/fpm/fpm/fpm_process_ctl.h @@ -24,9 +24,12 @@ void fpm_pctl_kill_all(int signo); void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg); void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg); void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg); +void fpm_pctl_on_child_accept(struct fpm_event_s *ev, short which, void *arg); int fpm_pctl_child_exited(); int fpm_pctl_init_main(); +int fpm_pctl_init_child(struct fpm_worker_pool_s *wp); +void fpm_pctl_child_info(int *pm, int *pipe); enum { FPM_PCTL_STATE_UNSPECIFIED, diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c index bf431a08d0366..93c71095b9cd6 100644 --- a/sapi/fpm/fpm/fpm_request.c +++ b/sapi/fpm/fpm/fpm_request.c @@ -58,6 +58,24 @@ void fpm_request_accepting() /* {{{ */ } /* }}} */ +void fpm_request_accepted() /* {{{ */ +{ + struct fpm_scoreboard_proc_s *proc; + struct timeval now; + + fpm_clock_get(&now); + + proc = fpm_scoreboard_proc_acquire(NULL, -1, 0); + if (proc == NULL) { + zlog(ZLOG_WARNING, "failed to acquire proc scoreboard"); + return; + } + + proc->request_stage = FPM_REQUEST_ACCEPTED; + fpm_scoreboard_proc_release(proc); +} +/* }}} */ + void fpm_request_reading_headers() /* {{{ */ { struct fpm_scoreboard_proc_s *proc; diff --git a/sapi/fpm/fpm/fpm_request.h b/sapi/fpm/fpm/fpm_request.h index aebd36cff4f8e..815e21aa17c96 100644 --- a/sapi/fpm/fpm/fpm_request.h +++ b/sapi/fpm/fpm/fpm_request.h @@ -22,6 +22,7 @@ int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv); enum fpm_request_stage_e { FPM_REQUEST_ACCEPTING = 1, + FPM_REQUEST_ACCEPTED, FPM_REQUEST_READING_HEADERS, FPM_REQUEST_INFO, FPM_REQUEST_EXECUTING, diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c index 24463a90dddfd..4f922ada8a5b6 100644 --- a/sapi/fpm/fpm/fpm_scoreboard.c +++ b/sapi/fpm/fpm/fpm_scoreboard.c @@ -255,6 +255,7 @@ void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_ind } proc->pid = pid; proc->start_epoch = time(NULL); + zlog(ZLOG_DEBUG, "[pool %s] child %d using scoreboard proc %d", scoreboard->pool, (int) pid, child_index); } /* }}} */ diff --git a/sapi/fpm/fpm/fpm_worker_pool.h b/sapi/fpm/fpm/fpm_worker_pool.h index 05c993de4e8c1..96c78214f1ab7 100644 --- a/sapi/fpm/fpm/fpm_worker_pool.h +++ b/sapi/fpm/fpm/fpm_worker_pool.h @@ -40,8 +40,9 @@ struct fpm_worker_pool_s { char **limit_extensions; /* for ondemand PM */ - struct fpm_event_s *ondemand_event; + struct fpm_event_s *ondemand_event, *child_accept_event; int socket_event_set; + int child_accept_pipe[2]; }; struct fpm_worker_pool_s *fpm_worker_pool_alloc(); From 49159b14f1fc956b1b12442fba66bb6275478da2 Mon Sep 17 00:00:00 2001 From: Barry Jaspan Date: Mon, 13 Apr 2015 13:26:14 -0400 Subject: [PATCH 2/3] revert whitespace-only changes from upstream --- sapi/fpm/fpm/fastcgi.c | 1594 ++++++++++++++++++++-------------------- 1 file changed, 797 insertions(+), 797 deletions(-) diff --git a/sapi/fpm/fpm/fastcgi.c b/sapi/fpm/fpm/fastcgi.c index edfc2423860e1..31f4d9aa1a921 100644 --- a/sapi/fpm/fpm/fastcgi.c +++ b/sapi/fpm/fpm/fastcgi.c @@ -48,21 +48,21 @@ static int is_impersonate = 0; #define FCGI_LOCK(fd) \ - if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ - DWORD ret; \ - while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \ - if (in_shutdown) return -1; \ - } \ - if (ret == WAIT_FAILED) { \ - fprintf(stderr, "WaitForSingleObject() failed\n"); \ - return -1; \ - } \ - } + if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ + DWORD ret; \ + while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \ + if (in_shutdown) return -1; \ + } \ + if (ret == WAIT_FAILED) { \ + fprintf(stderr, "WaitForSingleObject() failed\n"); \ + return -1; \ + } \ + } #define FCGI_UNLOCK(fd) \ - if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ - ReleaseMutex(fcgi_accept_mutex); \ - } + if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ + ReleaseMutex(fcgi_accept_mutex); \ + } #else @@ -91,41 +91,41 @@ #endif # ifndef HAVE_SOCKLEN_T - typedef unsigned int socklen_t; + typedef unsigned int socklen_t; # endif # ifdef USE_LOCKING # define FCGI_LOCK(fd) \ - do { \ - struct flock lock; \ - lock.l_type = F_WRLCK; \ - lock.l_start = 0; \ - lock.l_whence = SEEK_SET; \ - lock.l_len = 0; \ - if (fcntl(fd, F_SETLKW, &lock) != -1) { \ - break; \ - } else if (errno != EINTR || in_shutdown) { \ - return -1; \ - } \ - } while (1) + do { \ + struct flock lock; \ + lock.l_type = F_WRLCK; \ + lock.l_start = 0; \ + lock.l_whence = SEEK_SET; \ + lock.l_len = 0; \ + if (fcntl(fd, F_SETLKW, &lock) != -1) { \ + break; \ + } else if (errno != EINTR || in_shutdown) { \ + return -1; \ + } \ + } while (1) # define FCGI_UNLOCK(fd) \ - do { \ - int orig_errno = errno; \ - while (1) { \ - struct flock lock; \ - lock.l_type = F_UNLCK; \ - lock.l_start = 0; \ - lock.l_whence = SEEK_SET; \ - lock.l_len = 0; \ - if (fcntl(fd, F_SETLK, &lock) != -1) { \ - break; \ - } else if (errno != EINTR) { \ - return -1; \ - } \ - } \ - errno = orig_errno; \ - } while (0) + do { \ + int orig_errno = errno; \ + while (1) { \ + struct flock lock; \ + lock.l_type = F_UNLCK; \ + lock.l_start = 0; \ + lock.l_whence = SEEK_SET; \ + lock.l_len = 0; \ + if (fcntl(fd, F_SETLK, &lock) != -1) { \ + break; \ + } else if (errno != EINTR) { \ + return -1; \ + } \ + } \ + errno = orig_errno; \ + } while (0) # else # define FCGI_LOCK(fd) # define FCGI_UNLOCK(fd) @@ -134,10 +134,10 @@ #endif typedef union _sa_t { - struct sockaddr sa; - struct sockaddr_un sa_unix; - struct sockaddr_in sa_inet; - struct sockaddr_in6 sa_inet6; + struct sockaddr sa; + struct sockaddr_un sa_unix; + struct sockaddr_in sa_inet; + struct sockaddr_in6 sa_inet6; } sa_t; static HashTable fcgi_mgmt_vars; @@ -152,102 +152,102 @@ static sa_t client_sa; static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg) { - HANDLE shutdown_event = (HANDLE) arg; - WaitForSingleObject(shutdown_event, INFINITE); - in_shutdown = 1; - return 0; + HANDLE shutdown_event = (HANDLE) arg; + WaitForSingleObject(shutdown_event, INFINITE); + in_shutdown = 1; + return 0; } #else static void fcgi_signal_handler(int signo) { - if (signo == SIGUSR1 || signo == SIGTERM) { - in_shutdown = 1; - } + if (signo == SIGUSR1 || signo == SIGTERM) { + in_shutdown = 1; + } } static void fcgi_setup_signals(void) { - struct sigaction new_sa, old_sa; - - sigemptyset(&new_sa.sa_mask); - new_sa.sa_flags = 0; - new_sa.sa_handler = fcgi_signal_handler; - sigaction(SIGUSR1, &new_sa, NULL); - sigaction(SIGTERM, &new_sa, NULL); - sigaction(SIGPIPE, NULL, &old_sa); - if (old_sa.sa_handler == SIG_DFL) { - sigaction(SIGPIPE, &new_sa, NULL); - } + struct sigaction new_sa, old_sa; + + sigemptyset(&new_sa.sa_mask); + new_sa.sa_flags = 0; + new_sa.sa_handler = fcgi_signal_handler; + sigaction(SIGUSR1, &new_sa, NULL); + sigaction(SIGTERM, &new_sa, NULL); + sigaction(SIGPIPE, NULL, &old_sa); + if (old_sa.sa_handler == SIG_DFL) { + sigaction(SIGPIPE, &new_sa, NULL); + } } #endif int fcgi_init(void) { - if (!is_initialized) { - zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1); - fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS") - 1, "0", sizeof("0")-1); + if (!is_initialized) { + zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1); + fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS") - 1, "0", sizeof("0")-1); - is_initialized = 1; + is_initialized = 1; #ifdef _WIN32 # if 0 - /* TODO: Support for TCP sockets */ - WSADATA wsaData; + /* TODO: Support for TCP sockets */ + WSADATA wsaData; - if (WSAStartup(MAKEWORD(2,0), &wsaData)) { - fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); - return 0; - } + if (WSAStartup(MAKEWORD(2,0), &wsaData)) { + fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); + return 0; + } # endif - { - char *str; - DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT; - HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE); - - SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL); - - str = getenv("_FCGI_SHUTDOWN_EVENT_"); - if (str != NULL) { - HANDLE shutdown_event = (HANDLE) atoi(str); - if (!CreateThread(NULL, 0, fcgi_shutdown_thread, - shutdown_event, 0, NULL)) { - return -1; - } - } - str = getenv("_FCGI_MUTEX_"); - if (str != NULL) { - fcgi_accept_mutex = (HANDLE) atoi(str); - } - return 1; - } + { + char *str; + DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT; + HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE); + + SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL); + + str = getenv("_FCGI_SHUTDOWN_EVENT_"); + if (str != NULL) { + HANDLE shutdown_event = (HANDLE) atoi(str); + if (!CreateThread(NULL, 0, fcgi_shutdown_thread, + shutdown_event, 0, NULL)) { + return -1; + } + } + str = getenv("_FCGI_MUTEX_"); + if (str != NULL) { + fcgi_accept_mutex = (HANDLE) atoi(str); + } + return 1; + } #else - fcgi_setup_signals(); - return 1; + fcgi_setup_signals(); + return 1; #endif - } - return 1; + } + return 1; } void fcgi_set_in_shutdown(int new_value) { - in_shutdown = new_value; + in_shutdown = new_value; } void fcgi_shutdown(void) { - if (is_initialized) { - zend_hash_destroy(&fcgi_mgmt_vars); - } - if (allowed_clients) { - free(allowed_clients); - } + if (is_initialized) { + zend_hash_destroy(&fcgi_mgmt_vars); + } + if (allowed_clients) { + free(allowed_clients); + } } void fcgi_set_allowed_clients(char *ip) { - char *cur, *end; - int n; + char *cur, *end; + int n; if (ip) { ip = strdup(ip); @@ -285,520 +285,520 @@ void fcgi_set_allowed_clients(char *ip) void fcgi_init_request(fcgi_request *req, int listen_socket) { - memset(req, 0, sizeof(fcgi_request)); - req->listen_socket = listen_socket; - req->fd = -1; - req->id = -1; + memset(req, 0, sizeof(fcgi_request)); + req->listen_socket = listen_socket; + req->fd = -1; + req->id = -1; - req->in_len = 0; - req->in_pad = 0; + req->in_len = 0; + req->in_pad = 0; - req->out_hdr = NULL; - req->out_pos = req->out_buf; + req->out_hdr = NULL; + req->out_pos = req->out_buf; #ifdef _WIN32 - req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL); + req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL); #endif } static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count) { - int ret; - size_t n = 0; + int ret; + size_t n = 0; - do { - errno = 0; + do { + errno = 0; #ifdef _WIN32 - if (!req->tcp) { - ret = write(req->fd, ((char*)buf)+n, count-n); - } else { - ret = send(req->fd, ((char*)buf)+n, count-n, 0); - if (ret <= 0) { - errno = WSAGetLastError(); - } - } + if (!req->tcp) { + ret = write(req->fd, ((char*)buf)+n, count-n); + } else { + ret = send(req->fd, ((char*)buf)+n, count-n, 0); + if (ret <= 0) { + errno = WSAGetLastError(); + } + } #else - ret = write(req->fd, ((char*)buf)+n, count-n); + ret = write(req->fd, ((char*)buf)+n, count-n); #endif - if (ret > 0) { - n += ret; - } else if (ret <= 0 && errno != 0 && errno != EINTR) { - return ret; - } - } while (n != count); - return n; + if (ret > 0) { + n += ret; + } else if (ret <= 0 && errno != 0 && errno != EINTR) { + return ret; + } + } while (n != count); + return n; } static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count) { - int ret; - size_t n = 0; + int ret; + size_t n = 0; - do { - errno = 0; + do { + errno = 0; #ifdef _WIN32 - if (!req->tcp) { - ret = read(req->fd, ((char*)buf)+n, count-n); - } else { - ret = recv(req->fd, ((char*)buf)+n, count-n, 0); - if (ret <= 0) { - errno = WSAGetLastError(); - } - } + if (!req->tcp) { + ret = read(req->fd, ((char*)buf)+n, count-n); + } else { + ret = recv(req->fd, ((char*)buf)+n, count-n, 0); + if (ret <= 0) { + errno = WSAGetLastError(); + } + } #else - ret = read(req->fd, ((char*)buf)+n, count-n); + ret = read(req->fd, ((char*)buf)+n, count-n); #endif - if (ret > 0) { - n += ret; - } else if (ret == 0 && errno == 0) { - return n; - } else if (ret <= 0 && errno != 0 && errno != EINTR) { - return ret; - } - } while (n != count); - return n; + if (ret > 0) { + n += ret; + } else if (ret == 0 && errno == 0) { + return n; + } else if (ret <= 0 && errno != 0 && errno != EINTR) { + return ret; + } + } while (n != count); + return n; } static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len) { - int pad = ((len + 7) & ~7) - len; - - hdr->contentLengthB0 = (unsigned char)(len & 0xff); - hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff); - hdr->paddingLength = (unsigned char)pad; - hdr->requestIdB0 = (unsigned char)(req_id & 0xff); - hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff); - hdr->reserved = 0; - hdr->type = type; - hdr->version = FCGI_VERSION_1; - if (pad) { - memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad); - } - return pad; + int pad = ((len + 7) & ~7) - len; + + hdr->contentLengthB0 = (unsigned char)(len & 0xff); + hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff); + hdr->paddingLength = (unsigned char)pad; + hdr->requestIdB0 = (unsigned char)(req_id & 0xff); + hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff); + hdr->reserved = 0; + hdr->type = type; + hdr->version = FCGI_VERSION_1; + if (pad) { + memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad); + } + return pad; } static inline size_t fcgi_get_params_len( int *result, unsigned char *p, unsigned char *end) { - size_t ret = 0; - - if (p < end) { - *result = p[0]; - if (*result < 128) { - ret = 1; - } - else if (p + 3 < end) { - *result = ((*result & 0x7f) << 24); - *result |= (p[1] << 16); - *result |= (p[2] << 8); - *result |= p[3]; - ret = 4; - } - } - if (*result < 0) { - ret = 0; - } - return ret; + size_t ret = 0; + + if (p < end) { + *result = p[0]; + if (*result < 128) { + ret = 1; + } + else if (p + 3 < end) { + *result = ((*result & 0x7f) << 24); + *result |= (p[1] << 16); + *result |= (p[2] << 8); + *result |= p[3]; + ret = 4; + } + } + if (*result < 0) { + ret = 0; + } + return ret; } static inline int fcgi_param_get_eff_len( unsigned char *p, unsigned char *end, uint *eff_len) { - int ret = 1; - int zero_found = 0; - *eff_len = 0; - for (; p != end; ++p) { - if (*p == '\0') { - zero_found = 1; - } - else { - if (zero_found) { - ret = 0; - break; - } - if (*eff_len < ((uint)-1)) { - ++*eff_len; - } - else { - ret = 0; - break; - } - } - } - return ret; + int ret = 1; + int zero_found = 0; + *eff_len = 0; + for (; p != end; ++p) { + if (*p == '\0') { + zero_found = 1; + } + else { + if (zero_found) { + ret = 0; + break; + } + if (*eff_len < ((uint)-1)) { + ++*eff_len; + } + else { + ret = 0; + break; + } + } + } + return ret; } static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end) { - char buf[128]; - char *tmp = buf; - size_t buf_size = sizeof(buf); - int name_len = 0; - int val_len = 0; - uint eff_name_len = 0; - char *s; - int ret = 1; - size_t bytes_consumed; - - while (p < end) { - bytes_consumed = fcgi_get_params_len(&name_len, p, end); - if (!bytes_consumed) { - /* Malformated request */ - ret = 0; - break; - } - p += bytes_consumed; - bytes_consumed = fcgi_get_params_len(&val_len, p, end); - if (!bytes_consumed) { - /* Malformated request */ - ret = 0; - break; - } - p += bytes_consumed; - if (name_len > (INT_MAX - val_len) || /* would the addition overflow? */ - name_len + val_len > end - p) { /* would we exceed the buffer? */ - /* Malformated request */ - ret = 0; - break; - } - - /* - * get the effective length of the name in case it's not a valid string - * don't do this on the value because it can be binary data - */ - if (!fcgi_param_get_eff_len(p, p+name_len, &eff_name_len)){ - /* Malicious request */ - ret = 0; - break; - } - if (eff_name_len >= buf_size-1) { - if (eff_name_len > ((uint)-1)-64) { - ret = 0; - break; - } - buf_size = eff_name_len + 64; - tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size)); - if (tmp == NULL) { - ret = 0; - break; - } - } - memcpy(tmp, p, eff_name_len); - tmp[eff_name_len] = 0; - s = estrndup((char*)p + name_len, val_len); - if (s == NULL) { - ret = 0; - break; - } - zend_hash_update(req->env, tmp, eff_name_len+1, &s, sizeof(char*), NULL); - p += name_len + val_len; - } - if (tmp != buf && tmp != NULL) { - efree(tmp); - } - return ret; + char buf[128]; + char *tmp = buf; + size_t buf_size = sizeof(buf); + int name_len = 0; + int val_len = 0; + uint eff_name_len = 0; + char *s; + int ret = 1; + size_t bytes_consumed; + + while (p < end) { + bytes_consumed = fcgi_get_params_len(&name_len, p, end); + if (!bytes_consumed) { + /* Malformated request */ + ret = 0; + break; + } + p += bytes_consumed; + bytes_consumed = fcgi_get_params_len(&val_len, p, end); + if (!bytes_consumed) { + /* Malformated request */ + ret = 0; + break; + } + p += bytes_consumed; + if (name_len > (INT_MAX - val_len) || /* would the addition overflow? */ + name_len + val_len > end - p) { /* would we exceed the buffer? */ + /* Malformated request */ + ret = 0; + break; + } + + /* + * get the effective length of the name in case it's not a valid string + * don't do this on the value because it can be binary data + */ + if (!fcgi_param_get_eff_len(p, p+name_len, &eff_name_len)){ + /* Malicious request */ + ret = 0; + break; + } + if (eff_name_len >= buf_size-1) { + if (eff_name_len > ((uint)-1)-64) { + ret = 0; + break; + } + buf_size = eff_name_len + 64; + tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size)); + if (tmp == NULL) { + ret = 0; + break; + } + } + memcpy(tmp, p, eff_name_len); + tmp[eff_name_len] = 0; + s = estrndup((char*)p + name_len, val_len); + if (s == NULL) { + ret = 0; + break; + } + zend_hash_update(req->env, tmp, eff_name_len+1, &s, sizeof(char*), NULL); + p += name_len + val_len; + } + if (tmp != buf && tmp != NULL) { + efree(tmp); + } + return ret; } static void fcgi_free_var(char **s) { - efree(*s); + efree(*s); } static int fcgi_read_request(fcgi_request *req) { - fcgi_header hdr; - int len, padding; - unsigned char buf[FCGI_MAX_LENGTH+8]; - - req->keep = 0; - req->closed = 0; - req->in_len = 0; - req->out_hdr = NULL; - req->out_pos = req->out_buf; - ALLOC_HASHTABLE(req->env); - zend_hash_init(req->env, 0, NULL, (void (*)(void *)) fcgi_free_var, 0); - - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - return 0; - } - - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - - while (hdr.type == FCGI_STDIN && len == 0) { - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - return 0; - } - - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - } - - if (len + padding > FCGI_MAX_LENGTH) { - return 0; - } - - req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0; - - if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) { - char *val; - - if (safe_read(req, buf, len+padding) != len+padding) { - return 0; - } - - req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN); - switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) { - case FCGI_RESPONDER: - val = estrdup("RESPONDER"); - zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); - break; - case FCGI_AUTHORIZER: - val = estrdup("AUTHORIZER"); - zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); - break; - case FCGI_FILTER: - val = estrdup("FILTER"); - zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); - break; - default: - return 0; - } - - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - return 0; - } - - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - - while (hdr.type == FCGI_PARAMS && len > 0) { - if (len + padding > FCGI_MAX_LENGTH) { - return 0; - } - - if (safe_read(req, buf, len+padding) != len+padding) { - req->keep = 0; - return 0; - } - - if (!fcgi_get_params(req, buf, buf+len)) { - req->keep = 0; - return 0; - } - - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - req->keep = 0; - return 0; - } - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - } - } else if (hdr.type == FCGI_GET_VALUES) { - unsigned char *p = buf + sizeof(fcgi_header); - HashPosition pos; - char * str_index; - uint str_length; - ulong num_index; - int key_type; - zval ** value; - - if (safe_read(req, buf, len+padding) != len+padding) { - req->keep = 0; - return 0; - } - - if (!fcgi_get_params(req, buf, buf+len)) { - req->keep = 0; - return 0; - } - - zend_hash_internal_pointer_reset_ex(req->env, &pos); - while ((key_type = zend_hash_get_current_key_ex(req->env, &str_index, &str_length, &num_index, 0, &pos)) != HASH_KEY_NON_EXISTENT) { - int zlen; - zend_hash_move_forward_ex(req->env, &pos); - if (key_type != HASH_KEY_IS_STRING) { - continue; - } - if (zend_hash_find(&fcgi_mgmt_vars, str_index, str_length, (void**) &value) != SUCCESS) { - continue; - } - --str_length; - zlen = Z_STRLEN_PP(value); - if ((p + 4 + 4 + str_length + zlen) >= (buf + sizeof(buf))) { - break; - } - if (str_length < 0x80) { - *p++ = str_length; - } else { - *p++ = ((str_length >> 24) & 0xff) | 0x80; - *p++ = (str_length >> 16) & 0xff; - *p++ = (str_length >> 8) & 0xff; - *p++ = str_length & 0xff; - } - if (zlen < 0x80) { - *p++ = zlen; - } else { - *p++ = ((zlen >> 24) & 0xff) | 0x80; - *p++ = (zlen >> 16) & 0xff; - *p++ = (zlen >> 8) & 0xff; - *p++ = zlen & 0xff; - } - memcpy(p, str_index, str_length); - p += str_length; - memcpy(p, Z_STRVAL_PP(value), zlen); - p += zlen; - } - len = p - buf - sizeof(fcgi_header); - len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len); - if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) { - req->keep = 0; - return 0; - } - return 0; - } else { - return 0; - } - - return 1; + fcgi_header hdr; + int len, padding; + unsigned char buf[FCGI_MAX_LENGTH+8]; + + req->keep = 0; + req->closed = 0; + req->in_len = 0; + req->out_hdr = NULL; + req->out_pos = req->out_buf; + ALLOC_HASHTABLE(req->env); + zend_hash_init(req->env, 0, NULL, (void (*)(void *)) fcgi_free_var, 0); + + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + return 0; + } + + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + + while (hdr.type == FCGI_STDIN && len == 0) { + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + return 0; + } + + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + } + + if (len + padding > FCGI_MAX_LENGTH) { + return 0; + } + + req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0; + + if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) { + char *val; + + if (safe_read(req, buf, len+padding) != len+padding) { + return 0; + } + + req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN); + switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) { + case FCGI_RESPONDER: + val = estrdup("RESPONDER"); + zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); + break; + case FCGI_AUTHORIZER: + val = estrdup("AUTHORIZER"); + zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); + break; + case FCGI_FILTER: + val = estrdup("FILTER"); + zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL); + break; + default: + return 0; + } + + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + return 0; + } + + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + + while (hdr.type == FCGI_PARAMS && len > 0) { + if (len + padding > FCGI_MAX_LENGTH) { + return 0; + } + + if (safe_read(req, buf, len+padding) != len+padding) { + req->keep = 0; + return 0; + } + + if (!fcgi_get_params(req, buf, buf+len)) { + req->keep = 0; + return 0; + } + + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + req->keep = 0; + return 0; + } + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + } + } else if (hdr.type == FCGI_GET_VALUES) { + unsigned char *p = buf + sizeof(fcgi_header); + HashPosition pos; + char * str_index; + uint str_length; + ulong num_index; + int key_type; + zval ** value; + + if (safe_read(req, buf, len+padding) != len+padding) { + req->keep = 0; + return 0; + } + + if (!fcgi_get_params(req, buf, buf+len)) { + req->keep = 0; + return 0; + } + + zend_hash_internal_pointer_reset_ex(req->env, &pos); + while ((key_type = zend_hash_get_current_key_ex(req->env, &str_index, &str_length, &num_index, 0, &pos)) != HASH_KEY_NON_EXISTENT) { + int zlen; + zend_hash_move_forward_ex(req->env, &pos); + if (key_type != HASH_KEY_IS_STRING) { + continue; + } + if (zend_hash_find(&fcgi_mgmt_vars, str_index, str_length, (void**) &value) != SUCCESS) { + continue; + } + --str_length; + zlen = Z_STRLEN_PP(value); + if ((p + 4 + 4 + str_length + zlen) >= (buf + sizeof(buf))) { + break; + } + if (str_length < 0x80) { + *p++ = str_length; + } else { + *p++ = ((str_length >> 24) & 0xff) | 0x80; + *p++ = (str_length >> 16) & 0xff; + *p++ = (str_length >> 8) & 0xff; + *p++ = str_length & 0xff; + } + if (zlen < 0x80) { + *p++ = zlen; + } else { + *p++ = ((zlen >> 24) & 0xff) | 0x80; + *p++ = (zlen >> 16) & 0xff; + *p++ = (zlen >> 8) & 0xff; + *p++ = zlen & 0xff; + } + memcpy(p, str_index, str_length); + p += str_length; + memcpy(p, Z_STRVAL_PP(value), zlen); + p += zlen; + } + len = p - buf - sizeof(fcgi_header); + len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len); + if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) { + req->keep = 0; + return 0; + } + return 0; + } else { + return 0; + } + + return 1; } int fcgi_read(fcgi_request *req, char *str, int len) { - int ret, n, rest; - fcgi_header hdr; - unsigned char buf[255]; - - n = 0; - rest = len; - while (rest > 0) { - if (req->in_len == 0) { - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1 || - hdr.type != FCGI_STDIN) { - req->keep = 0; - return 0; - } - req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - req->in_pad = hdr.paddingLength; - if (req->in_len == 0) { - return n; - } - } - - if (req->in_len >= rest) { - ret = safe_read(req, str, rest); - } else { - ret = safe_read(req, str, req->in_len); - } - if (ret < 0) { - req->keep = 0; - return ret; - } else if (ret > 0) { - req->in_len -= ret; - rest -= ret; - n += ret; - str += ret; - if (req->in_len == 0) { - if (req->in_pad) { - if (safe_read(req, buf, req->in_pad) != req->in_pad) { - req->keep = 0; - return ret; - } - } - } else { - return n; - } - } else { - return n; - } - } - return n; + int ret, n, rest; + fcgi_header hdr; + unsigned char buf[255]; + + n = 0; + rest = len; + while (rest > 0) { + if (req->in_len == 0) { + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1 || + hdr.type != FCGI_STDIN) { + req->keep = 0; + return 0; + } + req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + req->in_pad = hdr.paddingLength; + if (req->in_len == 0) { + return n; + } + } + + if (req->in_len >= rest) { + ret = safe_read(req, str, rest); + } else { + ret = safe_read(req, str, req->in_len); + } + if (ret < 0) { + req->keep = 0; + return ret; + } else if (ret > 0) { + req->in_len -= ret; + rest -= ret; + n += ret; + str += ret; + if (req->in_len == 0) { + if (req->in_pad) { + if (safe_read(req, buf, req->in_pad) != req->in_pad) { + req->keep = 0; + return ret; + } + } + } else { + return n; + } + } else { + return n; + } + } + return n; } void fcgi_close(fcgi_request *req, int force, int destroy) { - if (destroy && req->env) { - zend_hash_destroy(req->env); - FREE_HASHTABLE(req->env); - req->env = NULL; - } + if (destroy && req->env) { + zend_hash_destroy(req->env); + FREE_HASHTABLE(req->env); + req->env = NULL; + } #ifdef _WIN32 - if (is_impersonate && !req->tcp) { - RevertToSelf(); - } + if (is_impersonate && !req->tcp) { + RevertToSelf(); + } #endif - if ((force || !req->keep) && req->fd >= 0) { + if ((force || !req->keep) && req->fd >= 0) { #ifdef _WIN32 - if (!req->tcp) { - HANDLE pipe = (HANDLE)_get_osfhandle(req->fd); - - if (!force) { - FlushFileBuffers(pipe); - } - DisconnectNamedPipe(pipe); - } else { - if (!force) { - char buf[8]; - - shutdown(req->fd, 1); - while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} - } - closesocket(req->fd); - } + if (!req->tcp) { + HANDLE pipe = (HANDLE)_get_osfhandle(req->fd); + + if (!force) { + FlushFileBuffers(pipe); + } + DisconnectNamedPipe(pipe); + } else { + if (!force) { + char buf[8]; + + shutdown(req->fd, 1); + while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} + } + closesocket(req->fd); + } #else - if (!force) { - char buf[8]; + if (!force) { + char buf[8]; - shutdown(req->fd, 1); - while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} - } - close(req->fd); + shutdown(req->fd, 1); + while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} + } + close(req->fd); #endif - req->fd = -1; - fpm_request_finished(); - } + req->fd = -1; + fpm_request_finished(); + } } static int fcgi_is_allowed() { - int i; - - if (client_sa.sa.sa_family == AF_UNIX) { - return 1; - } - if (!allowed_clients) { - return 1; - } - if (client_sa.sa.sa_family == AF_INET) { - for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { - if (allowed_clients[i].sa.sa_family == AF_INET - && !memcmp(&client_sa.sa_inet.sin_addr, &allowed_clients[i].sa_inet.sin_addr, 4)) { - return 1; - } - } - } - if (client_sa.sa.sa_family == AF_INET6) { - for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { - if (allowed_clients[i].sa.sa_family == AF_INET6 - && !memcmp(&client_sa.sa_inet6.sin6_addr, &allowed_clients[i].sa_inet6.sin6_addr, 12)) { - return 1; - } + int i; + + if (client_sa.sa.sa_family == AF_UNIX) { + return 1; + } + if (!allowed_clients) { + return 1; + } + if (client_sa.sa.sa_family == AF_INET) { + for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { + if (allowed_clients[i].sa.sa_family == AF_INET + && !memcmp(&client_sa.sa_inet.sin_addr, &allowed_clients[i].sa_inet.sin_addr, 4)) { + return 1; + } + } + } + if (client_sa.sa.sa_family == AF_INET6) { + for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { + if (allowed_clients[i].sa.sa_family == AF_INET6 + && !memcmp(&client_sa.sa_inet6.sin6_addr, &allowed_clients[i].sa_inet6.sin6_addr, 12)) { + return 1; + } #ifdef IN6_IS_ADDR_V4MAPPED - if (allowed_clients[i].sa.sa_family == AF_INET - && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr) - && !memcmp(((char *)&client_sa.sa_inet6.sin6_addr)+12, &allowed_clients[i].sa_inet.sin_addr, 4)) { - return 1; - } + if (allowed_clients[i].sa.sa_family == AF_INET + && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr) + && !memcmp(((char *)&client_sa.sa_inet6.sin6_addr)+12, &allowed_clients[i].sa_inet.sin_addr, 4)) { + return 1; + } #endif - } - } + } + } - zlog(ZLOG_ERROR, "Connection disallowed: IP address '%s' has been dropped.", fcgi_get_last_client_ip()); - return 0; + zlog(ZLOG_ERROR, "Connection disallowed: IP address '%s' has been dropped.", fcgi_get_last_client_ip()); + return 0; } /** @@ -832,42 +832,42 @@ int fcgi_set_nonblocking(int fd, int nonblocking) int fcgi_accept_request(fcgi_request *req, int nonblock_once, int accept_pipe) { #ifdef _WIN32 - HANDLE pipe; - OVERLAPPED ov; + HANDLE pipe; + OVERLAPPED ov; #endif - while (1) { - if (req->fd < 0) { - while (1) { - if (in_shutdown) { - return -1; - } + while (1) { + if (req->fd < 0) { + while (1) { + if (in_shutdown) { + return -1; + } #ifdef _WIN32 - if (!req->tcp) { - pipe = (HANDLE)_get_osfhandle(req->listen_socket); - FCGI_LOCK(req->listen_socket); - ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if (!ConnectNamedPipe(pipe, &ov)) { - errno = GetLastError(); - if (errno == ERROR_IO_PENDING) { - while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) { - if (in_shutdown) { - CloseHandle(ov.hEvent); - FCGI_UNLOCK(req->listen_socket); - return -1; - } - } - } else if (errno != ERROR_PIPE_CONNECTED) { - } - } - CloseHandle(ov.hEvent); - req->fd = req->listen_socket; - FCGI_UNLOCK(req->listen_socket); - } else { - SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket); + if (!req->tcp) { + pipe = (HANDLE)_get_osfhandle(req->listen_socket); + FCGI_LOCK(req->listen_socket); + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!ConnectNamedPipe(pipe, &ov)) { + errno = GetLastError(); + if (errno == ERROR_IO_PENDING) { + while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) { + if (in_shutdown) { + CloseHandle(ov.hEvent); + FCGI_UNLOCK(req->listen_socket); + return -1; + } + } + } else if (errno != ERROR_PIPE_CONNECTED) { + } + } + CloseHandle(ov.hEvent); + req->fd = req->listen_socket; + FCGI_UNLOCK(req->listen_socket); + } else { + SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket); #else - { - int listen_socket = req->listen_socket; + { + int listen_socket = req->listen_socket; #endif sa_t sa; socklen_t len = sizeof(sa); @@ -912,108 +912,108 @@ int fcgi_accept_request(fcgi_request *req, int nonblock_once, int accept_pipe) } #ifdef _WIN32 - if (req->fd < 0 && (in_shutdown || errno != EINTR)) { + if (req->fd < 0 && (in_shutdown || errno != EINTR)) { #else - if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) { + if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) { #endif - return -1; - } + return -1; + } #ifdef _WIN32 - break; + break; #else - if (req->fd >= 0) { + if (req->fd >= 0) { #if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL) - struct pollfd fds; - int ret; - - fpm_request_reading_headers(); - - fds.fd = req->fd; - fds.events = POLLIN; - fds.revents = 0; - do { - errno = 0; - ret = poll(&fds, 1, 5000); - } while (ret < 0 && errno == EINTR); - if (ret > 0 && (fds.revents & POLLIN)) { - break; - } - fcgi_close(req, 1, 0); + struct pollfd fds; + int ret; + + fpm_request_reading_headers(); + + fds.fd = req->fd; + fds.events = POLLIN; + fds.revents = 0; + do { + errno = 0; + ret = poll(&fds, 1, 5000); + } while (ret < 0 && errno == EINTR); + if (ret > 0 && (fds.revents & POLLIN)) { + break; + } + fcgi_close(req, 1, 0); #else - fpm_request_reading_headers(); - - if (req->fd < FD_SETSIZE) { - struct timeval tv = {5,0}; - fd_set set; - int ret; - - FD_ZERO(&set); - FD_SET(req->fd, &set); - do { - errno = 0; - ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0; - } while (ret < 0 && errno == EINTR); - if (ret > 0 && FD_ISSET(req->fd, &set)) { - break; - } - fcgi_close(req, 1, 0); - } else { - zlog(ZLOG_ERROR, "Too many open file descriptors. FD_SETSIZE limit exceeded."); - fcgi_close(req, 1, 0); - } + fpm_request_reading_headers(); + + if (req->fd < FD_SETSIZE) { + struct timeval tv = {5,0}; + fd_set set; + int ret; + + FD_ZERO(&set); + FD_SET(req->fd, &set); + do { + errno = 0; + ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0; + } while (ret < 0 && errno == EINTR); + if (ret > 0 && FD_ISSET(req->fd, &set)) { + break; + } + fcgi_close(req, 1, 0); + } else { + zlog(ZLOG_ERROR, "Too many open file descriptors. FD_SETSIZE limit exceeded."); + fcgi_close(req, 1, 0); + } #endif - } + } #endif - } - } else if (in_shutdown) { - return -1; - } - if (fcgi_read_request(req)) { + } + } else if (in_shutdown) { + return -1; + } + if (fcgi_read_request(req)) { #ifdef _WIN32 - if (is_impersonate && !req->tcp) { - pipe = (HANDLE)_get_osfhandle(req->fd); - if (!ImpersonateNamedPipeClient(pipe)) { - fcgi_close(req, 1, 1); - continue; - } - } + if (is_impersonate && !req->tcp) { + pipe = (HANDLE)_get_osfhandle(req->fd); + if (!ImpersonateNamedPipeClient(pipe)) { + fcgi_close(req, 1, 1); + continue; + } + } #endif - return req->fd; - } else { - fcgi_close(req, 1, 1); - } - } + return req->fd; + } else { + fcgi_close(req, 1, 1); + } + } } static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type) { - req->out_hdr = (fcgi_header*) req->out_pos; - req->out_hdr->type = type; - req->out_pos += sizeof(fcgi_header); - return req->out_hdr; + req->out_hdr = (fcgi_header*) req->out_pos; + req->out_hdr->type = type; + req->out_pos += sizeof(fcgi_header); + return req->out_hdr; } static inline void close_packet(fcgi_request *req) { - if (req->out_hdr) { - int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header)); + if (req->out_hdr) { + int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header)); - req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len); - req->out_hdr = NULL; - } + req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len); + req->out_hdr = NULL; + } } int fcgi_flush(fcgi_request *req, int close) { - int len; + int len; - close_packet(req); + close_packet(req); - len = req->out_pos - req->out_buf; + len = req->out_pos - req->out_buf; - if (close) { - fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos); + if (close) { + fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos); fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request)); rec->body.appStatusB3 = 0; @@ -1029,174 +1029,174 @@ int fcgi_flush(fcgi_request *req, int close) return 0; } - req->out_pos = req->out_buf; - return 1; + req->out_pos = req->out_buf; + return 1; } ssize_t fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len) { - int limit, rest; - - if (len <= 0) { - return 0; - } - - if (req->out_hdr && req->out_hdr->type != type) { - close_packet(req); - } - - /* Optimized version */ - limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf); - if (!req->out_hdr) { - limit -= sizeof(fcgi_header); - if (limit < 0) limit = 0; - } - - if (len < limit) { - if (!req->out_hdr) { - open_packet(req, type); - } - memcpy(req->out_pos, str, len); - req->out_pos += len; - } else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) { - if (!req->out_hdr) { - open_packet(req, type); - } - if (limit > 0) { - memcpy(req->out_pos, str, limit); - req->out_pos += limit; - } - if (!fcgi_flush(req, 0)) { - return -1; - } - if (len > limit) { - open_packet(req, type); - memcpy(req->out_pos, str + limit, len - limit); - req->out_pos += len - limit; - } - } else { - int pos = 0; - int pad; - - close_packet(req); - while ((len - pos) > 0xffff) { - open_packet(req, type); - fcgi_make_header(req->out_hdr, type, req->id, 0xfff8); - req->out_hdr = NULL; - if (!fcgi_flush(req, 0)) { - return -1; - } - if (safe_write(req, str + pos, 0xfff8) != 0xfff8) { - req->keep = 0; - return -1; - } - pos += 0xfff8; - } - - pad = (((len - pos) + 7) & ~7) - (len - pos); - rest = pad ? 8 - pad : 0; - - open_packet(req, type); - fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest); - req->out_hdr = NULL; - if (!fcgi_flush(req, 0)) { - return -1; - } - if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) { - req->keep = 0; - return -1; - } - if (pad) { - open_packet(req, type); - memcpy(req->out_pos, str + len - rest, rest); - req->out_pos += rest; - } - } - - return len; + int limit, rest; + + if (len <= 0) { + return 0; + } + + if (req->out_hdr && req->out_hdr->type != type) { + close_packet(req); + } + + /* Optimized version */ + limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf); + if (!req->out_hdr) { + limit -= sizeof(fcgi_header); + if (limit < 0) limit = 0; + } + + if (len < limit) { + if (!req->out_hdr) { + open_packet(req, type); + } + memcpy(req->out_pos, str, len); + req->out_pos += len; + } else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) { + if (!req->out_hdr) { + open_packet(req, type); + } + if (limit > 0) { + memcpy(req->out_pos, str, limit); + req->out_pos += limit; + } + if (!fcgi_flush(req, 0)) { + return -1; + } + if (len > limit) { + open_packet(req, type); + memcpy(req->out_pos, str + limit, len - limit); + req->out_pos += len - limit; + } + } else { + int pos = 0; + int pad; + + close_packet(req); + while ((len - pos) > 0xffff) { + open_packet(req, type); + fcgi_make_header(req->out_hdr, type, req->id, 0xfff8); + req->out_hdr = NULL; + if (!fcgi_flush(req, 0)) { + return -1; + } + if (safe_write(req, str + pos, 0xfff8) != 0xfff8) { + req->keep = 0; + return -1; + } + pos += 0xfff8; + } + + pad = (((len - pos) + 7) & ~7) - (len - pos); + rest = pad ? 8 - pad : 0; + + open_packet(req, type); + fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest); + req->out_hdr = NULL; + if (!fcgi_flush(req, 0)) { + return -1; + } + if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) { + req->keep = 0; + return -1; + } + if (pad) { + open_packet(req, type); + memcpy(req->out_pos, str + len - rest, rest); + req->out_pos += rest; + } + } + + return len; } int fcgi_finish_request(fcgi_request *req, int force_close) { - int ret = 1; - - if (req->fd >= 0) { - if (!req->closed) { - ret = fcgi_flush(req, 1); - req->closed = 1; - } - fcgi_close(req, force_close, 1); - } - return ret; + int ret = 1; + + if (req->fd >= 0) { + if (!req->closed) { + ret = fcgi_flush(req, 1); + req->closed = 1; + } + fcgi_close(req, force_close, 1); + } + return ret; } char* fcgi_getenv(fcgi_request *req, const char* var, int var_len) { - char **val; + char **val; - if (!req) return NULL; + if (!req) return NULL; - if (zend_hash_find(req->env, (char*)var, var_len+1, (void**)&val) == SUCCESS) { - return *val; - } - return NULL; + if (zend_hash_find(req->env, (char*)var, var_len+1, (void**)&val) == SUCCESS) { + return *val; + } + return NULL; } char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val) { - if (var && req) { - if (val == NULL) { - zend_hash_del(req->env, var, var_len+1); - } else { - char **ret; - - val = estrdup(val); - if (zend_hash_update(req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) { - return *ret; - } - } - } - return NULL; + if (var && req) { + if (val == NULL) { + zend_hash_del(req->env, var, var_len+1); + } else { + char **ret; + + val = estrdup(val); + if (zend_hash_update(req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) { + return *ret; + } + } + } + return NULL; } void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len) { - zval * zvalue; - zvalue = pemalloc(sizeof(*zvalue), 1); - Z_TYPE_P(zvalue) = IS_STRING; - Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1); - Z_STRLEN_P(zvalue) = value_len; - zend_hash_add(&fcgi_mgmt_vars, name, name_len + 1, &zvalue, sizeof(zvalue), NULL); + zval * zvalue; + zvalue = pemalloc(sizeof(*zvalue), 1); + Z_TYPE_P(zvalue) = IS_STRING; + Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1); + Z_STRLEN_P(zvalue) = value_len; + zend_hash_add(&fcgi_mgmt_vars, name, name_len + 1, &zvalue, sizeof(zvalue), NULL); } void fcgi_free_mgmt_var_cb(void * ptr) { - zval ** var = (zval **)ptr; - pefree(Z_STRVAL_PP(var), 1); - pefree(*var, 1); + zval ** var = (zval **)ptr; + pefree(Z_STRVAL_PP(var), 1); + pefree(*var, 1); } const char *fcgi_get_last_client_ip() /* {{{ */ { - static char str[INET6_ADDRSTRLEN]; + static char str[INET6_ADDRSTRLEN]; - /* Ipv4 */ - if (client_sa.sa.sa_family == AF_INET) { - return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet.sin_addr, str, INET6_ADDRSTRLEN); - } + /* Ipv4 */ + if (client_sa.sa.sa_family == AF_INET) { + return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet.sin_addr, str, INET6_ADDRSTRLEN); + } #ifdef IN6_IS_ADDR_V4MAPPED - /* Ipv4-Mapped-Ipv6 */ - if (client_sa.sa.sa_family == AF_INET6 - && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr)) { - return inet_ntop(AF_INET, ((char *)&client_sa.sa_inet6.sin6_addr)+12, str, INET6_ADDRSTRLEN); - } + /* Ipv4-Mapped-Ipv6 */ + if (client_sa.sa.sa_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr)) { + return inet_ntop(AF_INET, ((char *)&client_sa.sa_inet6.sin6_addr)+12, str, INET6_ADDRSTRLEN); + } #endif - /* Ipv6 */ - if (client_sa.sa.sa_family == AF_INET6) { - return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet6.sin6_addr, str, INET6_ADDRSTRLEN); - } - /* Unix socket */ - return NULL; + /* Ipv6 */ + if (client_sa.sa.sa_family == AF_INET6) { + return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet6.sin6_addr, str, INET6_ADDRSTRLEN); + } + /* Unix socket */ + return NULL; } /* }}} */ /* From 0b2b179cfcf6f4b4bbbb3cbd0d4382e358957619 Mon Sep 17 00:00:00 2001 From: Barry Jaspan Date: Mon, 13 Apr 2015 13:31:38 -0400 Subject: [PATCH 3/3] fix (hopefully) remaining merge conflicts caused by whitespace --- sapi/fpm/fpm/fastcgi.c | 107 +++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/sapi/fpm/fpm/fastcgi.c b/sapi/fpm/fpm/fastcgi.c index 31f4d9aa1a921..b0a7488f33f4c 100644 --- a/sapi/fpm/fpm/fastcgi.c +++ b/sapi/fpm/fpm/fastcgi.c @@ -37,15 +37,13 @@ #include - typedef unsigned int in_addr_t; + struct sockaddr_un { + short sun_family; + char sun_path[MAXPATHLEN]; + }; - struct sockaddr_un { - short sun_family; - char sun_path[MAXPATHLEN]; - }; - - static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE; - static int is_impersonate = 0; + static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE; + static int is_impersonate = 0; #define FCGI_LOCK(fd) \ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ @@ -249,38 +247,42 @@ void fcgi_set_allowed_clients(char *ip) char *cur, *end; int n; - if (ip) { - ip = strdup(ip); - cur = ip; - n = 0; - while (*cur) { - if (*cur == ',') n++; - cur++; - } - if (allowed_clients) free(allowed_clients); - allowed_clients = malloc(sizeof(in_addr_t) * (n+2)); - n = 0; - cur = ip; - while (cur) { - end = strchr(cur, ','); - if (end) { - *end = 0; - end++; - } - if (inet_pton(AF_INET, cur, &allowed_clients[n].sa_inet.sin_addr)>0) { - allowed_clients[n].sa.sa_family = AF_INET; - n++; - } else if (inet_pton(AF_INET6, cur, &allowed_clients[n].sa_inet6.sin6_addr)>0) { - allowed_clients[n].sa.sa_family = AF_INET6; - n++; - } else { - zlog(ZLOG_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur); - } - cur = end; - } - allowed_clients[n].sa.sa_family = 0; - free(ip); - } + if (ip) { + ip = strdup(ip); + cur = ip; + n = 0; + while (*cur) { + if (*cur == ',') n++; + cur++; + } + if (allowed_clients) free(allowed_clients); + allowed_clients = malloc(sizeof(sa_t) * (n+2)); + n = 0; + cur = ip; + while (cur) { + end = strchr(cur, ','); + if (end) { + *end = 0; + end++; + } + if (inet_pton(AF_INET, cur, &allowed_clients[n].sa_inet.sin_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET; + n++; + } else if (inet_pton(AF_INET6, cur, &allowed_clients[n].sa_inet6.sin6_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET6; + n++; + } else { + zlog(ZLOG_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur); + } + cur = end; + } + allowed_clients[n].sa.sa_family = 0; + free(ip); + if (!n) { + zlog(ZLOG_ERROR, "There are no allowed addresses for this pool"); + /* don't clear allowed_clients as it will create an "open for all" security issue */ + } + } } void fcgi_init_request(fcgi_request *req, int listen_socket) @@ -1015,19 +1017,20 @@ int fcgi_flush(fcgi_request *req, int close) if (close) { fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos); - fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request)); - rec->body.appStatusB3 = 0; - rec->body.appStatusB2 = 0; - rec->body.appStatusB1 = 0; - rec->body.appStatusB0 = 0; - rec->body.protocolStatus = FCGI_REQUEST_COMPLETE; - len += sizeof(fcgi_end_request_rec); - } + fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request)); + rec->body.appStatusB3 = 0; + rec->body.appStatusB2 = 0; + rec->body.appStatusB1 = 0; + rec->body.appStatusB0 = 0; + rec->body.protocolStatus = FCGI_REQUEST_COMPLETE; + len += sizeof(fcgi_end_request_rec); + } - if (safe_write(req, req->out_buf, len) != len) { - req->keep = 0; - return 0; - } + if (safe_write(req, req->out_buf, len) != len) { + req->keep = 0; + req->out_pos = req->out_buf; + return 0; + } req->out_pos = req->out_buf; return 1;