|
|
|
@ -72,7 +72,9 @@ typedef struct {
|
|
|
|
|
typedef struct {
|
|
|
|
|
pid_t pid;
|
|
|
|
|
int fd;
|
|
|
|
|
int fdtocgi;
|
|
|
|
|
int fde_ndx; /* index into the fd-event buffer */
|
|
|
|
|
int fde_ndx_tocgi; /* index into the fd-event buffer */
|
|
|
|
|
|
|
|
|
|
connection *remote_conn; /* dumb pointer */
|
|
|
|
|
plugin_data *plugin_data; /* dumb pointer */
|
|
|
|
@ -89,6 +91,7 @@ static handler_ctx * cgi_handler_ctx_init(void) {
|
|
|
|
|
hctx->response = buffer_init();
|
|
|
|
|
hctx->response_header = buffer_init();
|
|
|
|
|
hctx->fd = -1;
|
|
|
|
|
hctx->fdtocgi = -1;
|
|
|
|
|
|
|
|
|
|
return hctx;
|
|
|
|
|
}
|
|
|
|
@ -519,6 +522,17 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
|
|
|
|
|
return FDEVENT_HANDLED_NOT_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cgi_connection_close_fdtocgi(server *srv, handler_ctx *hctx) {
|
|
|
|
|
/*(closes only hctx->fdtocgi)*/
|
|
|
|
|
fdevent_event_del(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi);
|
|
|
|
|
fdevent_unregister(srv->ev, hctx->fdtocgi);
|
|
|
|
|
|
|
|
|
|
if (close(hctx->fdtocgi)) {
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", hctx->fdtocgi, strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
hctx->fdtocgi = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cgi_connection_close(server *srv, handler_ctx *hctx) {
|
|
|
|
|
int status;
|
|
|
|
|
pid_t pid;
|
|
|
|
@ -541,16 +555,16 @@ static void cgi_connection_close(server *srv, handler_ctx *hctx) {
|
|
|
|
|
if (close(hctx->fd)) {
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hctx->fd = -1;
|
|
|
|
|
hctx->fde_ndx = -1;
|
|
|
|
|
if (hctx->fdtocgi != -1) {
|
|
|
|
|
cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid = hctx->pid;
|
|
|
|
|
|
|
|
|
|
con->plugin_ctx[p->id] = NULL;
|
|
|
|
|
|
|
|
|
|
/* is this a good idea ? */
|
|
|
|
|
cgi_handler_ctx_free(hctx);
|
|
|
|
|
|
|
|
|
|
/* if waitpid hasn't been called by response.c yet, do it here */
|
|
|
|
@ -631,6 +645,43 @@ static handler_t cgi_connection_close_callback(server *srv, connection *con, voi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int cgi_write_request(server *srv, handler_ctx *hctx, int fd);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static handler_t cgi_handle_fdevent_send (server *srv, void *ctx, int revents) {
|
|
|
|
|
handler_ctx *hctx = ctx;
|
|
|
|
|
connection *con = hctx->remote_conn;
|
|
|
|
|
|
|
|
|
|
/*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
|
|
|
|
|
joblist_append(srv, con);
|
|
|
|
|
|
|
|
|
|
if (revents & FDEVENT_OUT) {
|
|
|
|
|
if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) {
|
|
|
|
|
cgi_connection_close(srv, hctx);
|
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
|
}
|
|
|
|
|
/* more request body to be sent to CGI */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (revents & FDEVENT_HUP) {
|
|
|
|
|
/* skip sending remaining data to CGI */
|
|
|
|
|
chunkqueue *cq = con->request_content_queue;
|
|
|
|
|
chunkqueue_mark_written(cq, chunkqueue_length(cq));
|
|
|
|
|
|
|
|
|
|
cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
|
|
|
|
|
} else if (revents & FDEVENT_ERR) {
|
|
|
|
|
/* kill all connections to the cgi process */
|
|
|
|
|
#if 1
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
|
|
|
|
|
#endif
|
|
|
|
|
cgi_connection_close(srv, hctx);
|
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return HANDLER_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
|
|
|
|
|
handler_ctx *hctx = ctx;
|
|
|
|
|
connection *con = hctx->remote_conn;
|
|
|
|
@ -724,7 +775,7 @@ static int cgi_env_add(char_array *env, const char *key, size_t key_len, const c
|
|
|
|
|
*
|
|
|
|
|
* Also always use mmap; the files are "trusted", as we created them.
|
|
|
|
|
*/
|
|
|
|
|
static int cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) {
|
|
|
|
|
static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) {
|
|
|
|
|
chunk* const c = cq->first;
|
|
|
|
|
off_t offset, toSend, file_end;
|
|
|
|
|
ssize_t r;
|
|
|
|
@ -793,6 +844,89 @@ static int cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunk
|
|
|
|
|
chunkqueue_mark_written(cq, r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) {
|
|
|
|
|
connection *con = hctx->remote_conn;
|
|
|
|
|
chunkqueue *cq = con->request_content_queue;
|
|
|
|
|
chunk *c;
|
|
|
|
|
|
|
|
|
|
/* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
|
|
|
|
|
* solution: if this is still a problem on windows, then substitute
|
|
|
|
|
* socketpair() for pipe() and closesocket() for close() on windows.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
for (c = cq->first; c; c = cq->first) {
|
|
|
|
|
ssize_t r = -1;
|
|
|
|
|
|
|
|
|
|
switch(c->type) {
|
|
|
|
|
case FILE_CHUNK:
|
|
|
|
|
r = cgi_write_file_chunk_mmap(srv, con, fd, cq);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MEM_CHUNK:
|
|
|
|
|
if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
|
|
|
|
|
switch(errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
case EINTR:
|
|
|
|
|
/* ignore and try again */
|
|
|
|
|
r = 0;
|
|
|
|
|
break;
|
|
|
|
|
case EPIPE:
|
|
|
|
|
case ECONNRESET:
|
|
|
|
|
/* connection closed */
|
|
|
|
|
r = -2;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* fatal error */
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
|
|
|
|
|
r = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (r > 0) {
|
|
|
|
|
chunkqueue_mark_written(cq, r);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (0 == r) break; /*(might block)*/
|
|
|
|
|
|
|
|
|
|
switch (r) {
|
|
|
|
|
case -1:
|
|
|
|
|
/* fatal error */
|
|
|
|
|
return -1;
|
|
|
|
|
case -2:
|
|
|
|
|
/* connection reset */
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
|
|
|
|
|
/* skip all remaining data */
|
|
|
|
|
chunkqueue_mark_written(cq, chunkqueue_length(cq));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chunkqueue_is_empty(cq)) {
|
|
|
|
|
/* sent all request body input */
|
|
|
|
|
/* close connection to the cgi-script */
|
|
|
|
|
if (-1 == hctx->fdtocgi) { /*(entire request body sent in initial send to pipe buffer)*/
|
|
|
|
|
if (close(fd)) {
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* more request body remains to be sent to CGI so register for fdevents */
|
|
|
|
|
if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
|
|
|
|
|
hctx->fdtocgi = fd;
|
|
|
|
|
hctx->fde_ndx_tocgi = -1;
|
|
|
|
|
fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
|
|
|
|
|
fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1099,82 +1233,30 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_
|
|
|
|
|
close(from_cgi_fds[1]);
|
|
|
|
|
close(to_cgi_fds[0]);
|
|
|
|
|
|
|
|
|
|
if (con->request.content_length) {
|
|
|
|
|
chunkqueue *cq = con->request_content_queue;
|
|
|
|
|
chunk *c;
|
|
|
|
|
/* register PID and wait for them asynchronously */
|
|
|
|
|
|
|
|
|
|
assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
|
|
|
|
|
|
|
|
|
|
/* NOTE: yes, this is synchronous sending of CGI post data;
|
|
|
|
|
* if you need something asynchronous (recommended with large
|
|
|
|
|
* request bodies), use mod_fastcgi + fcgi-cgi.
|
|
|
|
|
*
|
|
|
|
|
* Also: windows doesn't support select() on pipes - wouldn't be
|
|
|
|
|
* easy to fix for all platforms.
|
|
|
|
|
*/
|
|
|
|
|
hctx->pid = pid;
|
|
|
|
|
hctx->fd = from_cgi_fds[0];
|
|
|
|
|
hctx->fde_ndx = -1;
|
|
|
|
|
|
|
|
|
|
if (0 == con->request.content_length) {
|
|
|
|
|
close(to_cgi_fds[1]);
|
|
|
|
|
} else {
|
|
|
|
|
/* there is content to send */
|
|
|
|
|
for (c = cq->first; c; c = cq->first) {
|
|
|
|
|
int r = -1;
|
|
|
|
|
|
|
|
|
|
switch(c->type) {
|
|
|
|
|
case FILE_CHUNK:
|
|
|
|
|
r = cgi_write_file_chunk_mmap(srv, con, to_cgi_fds[1], cq);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MEM_CHUNK:
|
|
|
|
|
if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
|
|
|
|
|
switch(errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
case EINTR:
|
|
|
|
|
/* ignore and try again */
|
|
|
|
|
r = 0;
|
|
|
|
|
break;
|
|
|
|
|
case EPIPE:
|
|
|
|
|
case ECONNRESET:
|
|
|
|
|
/* connection closed */
|
|
|
|
|
r = -2;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* fatal error */
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
|
|
|
|
|
r = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (r > 0) {
|
|
|
|
|
chunkqueue_mark_written(cq, r);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (-1 == fdevent_fcntl_set(srv->ev, to_cgi_fds[1])) {
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
|
|
|
|
|
close(to_cgi_fds[1]);
|
|
|
|
|
cgi_connection_close(srv, hctx);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (r) {
|
|
|
|
|
case -1:
|
|
|
|
|
/* fatal error */
|
|
|
|
|
close(from_cgi_fds[0]);
|
|
|
|
|
close(to_cgi_fds[1]);
|
|
|
|
|
kill(pid, SIGTERM);
|
|
|
|
|
cgi_pid_add(srv, p, pid);
|
|
|
|
|
return -1;
|
|
|
|
|
case -2:
|
|
|
|
|
/* connection reset */
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
|
|
|
|
|
/* skip all remaining data */
|
|
|
|
|
chunkqueue_mark_written(cq, chunkqueue_length(cq));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) {
|
|
|
|
|
close(to_cgi_fds[1]);
|
|
|
|
|
cgi_connection_close(srv, hctx);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close(to_cgi_fds[1]);
|
|
|
|
|
|
|
|
|
|
/* register PID and wait for them asyncronously */
|
|
|
|
|
|
|
|
|
|
hctx->pid = pid;
|
|
|
|
|
hctx->fd = from_cgi_fds[0];
|
|
|
|
|
hctx->fde_ndx = -1;
|
|
|
|
|
|
|
|
|
|
fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
|
|
|
|
|
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
|
|
|
|
|
|
|
|
|
|