From 30c399dbc542f7b4d983e196d7c5eafd5f8bffa0 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Sun, 30 Sep 2007 17:56:59 +0200 Subject: Mostly rewritten for robustness and prettier code --- Makefile | 8 ++- fcgiwrap.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 164 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index ac01a8b..90ee946 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,11 @@ all: fcgiwrap -go: all - ../../bin/spawn-fcgi -u admin -f ./fcgiwrap -a 172.16.0.2 -p 14017 +install: all + [ -f /usr/local/bin/fcgiwrap ] && mv /usr/local/bin/fcgiwrap /usr/local/bin/fcgiwrap~ + cp fcgiwrap /usr/local/bin + rm /usr/local/bin/fcgiwrap~ fcgiwrap: fcgiwrap.c - gcc -Wall -Werror -pedantic -O2 -g fcgiwrap.c -o fcgiwrap -lfcgi + gcc -Wall -Wextra -Werror -pedantic -O2 -g3 fcgiwrap.c -o fcgiwrap -lfcgi clean: -rm fcgiwrap diff --git a/fcgiwrap.c b/fcgiwrap.c index 5e9b4fe..c65f240 100644 --- a/fcgiwrap.c +++ b/fcgiwrap.c @@ -1,3 +1,6 @@ +#define NO_FCGI_DEFINES + +#include #include #include #include @@ -25,50 +28,116 @@ static int write_all(int fd, char *buf, size_t size) return size; } -static void fcgi_pass(int fd_stdin, int fd_stdout, int fd_stderr) +static int max_va(int p1, ...) { - char buf[FCGI_BUF_SIZE]; - size_t nread; - fd_set rset; - int maxfd = (fd_stdout > fd_stderr) ? fd_stdout : fd_stderr; - int nready; + va_list va; + int max = p1; + int p; + + va_start(va, p1); + do { + p = va_arg(va, int); + if (p > max) + max = p; + } while (p >= 0); + va_end(va); + + return max; +} - /* slurp the whole input and pass it to CGI */ +struct fcgi_context { + int fd_stdin; + int fd_stdout; + int fd_stderr; + int have_reply; + pid_t cgi_pid; +}; - while ((nread = fread(buf, 1, sizeof(buf), stdin))) { - if (write_all(fd_stdin, buf, nread) <= 0) return; +static void fcgi_finish(struct fcgi_context *fc, const char* msg) +{ + if (!fc->have_reply) { + FCGI_puts("Status: 502 Bad Gateway\nContent-type: text/plain\n"); + FCGI_printf("An error occurred while %s\n", msg); } - close(fd_stdin); + if (fc->fd_stdin >= 0) close(fc->fd_stdin); + if (fc->fd_stdout >= 0) close(fc->fd_stdout); + if (fc->fd_stderr >= 0) close(fc->fd_stderr); + + if (fc->cgi_pid) + kill(SIGTERM, fc->cgi_pid); +} + +static const char * fcgi_pass_fd(int *fdp, FCGI_FILE *ffp, char *buf, size_t bufsize) +{ + ssize_t nread; + + nread = read(*fdp, buf, bufsize); + if (nread > 0) { + if (FCGI_fwrite(buf, 1, nread, ffp) != (size_t) nread) { + return "writing CGI reply"; + } + } else { + if (nread < 0) { + return "reading CGI reply"; + } + close(*fdp); + *fdp = -1; + } + return NULL; +} + +static void fcgi_pass(struct fcgi_context *fc) +{ + char buf[FCGI_BUF_SIZE]; + ssize_t nread; + fd_set rset; + int maxfd = max_va(fc->fd_stdout, fc->fd_stderr, -1); + int nready; + const char *err; - /* now wait for CGI replies on stdout and stderr */ + /* eat the whole request and pass it to CGI */ + while ((nread = FCGI_fread(buf, 1, sizeof(buf), FCGI_stdin)) > 0) { + if (write_all(fc->fd_stdin, buf, nread) <= 0) { + fcgi_finish(fc, "reading the request"); + return; + } + } + close(fc->fd_stdin); + fc->fd_stdin = -1; - while (fd_stdout >= 0 && fd_stderr >= 0) { + /* now pass CGI reply back */ + while (fc->fd_stdout >= 0 && fc->fd_stderr >= 0) { FD_ZERO(&rset); - if (fd_stdout >= 0) FD_SET(fd_stdout, &rset); - if (fd_stderr >= 0) FD_SET(fd_stderr, &rset); + if (fc->fd_stdout >= 0) FD_SET(fc->fd_stdout, &rset); + if (fc->fd_stderr >= 0) FD_SET(fc->fd_stderr, &rset); nready = select(maxfd, &rset, NULL, NULL, NULL); if (nready < 0) { if (errno == EAGAIN) continue; - return; /* better error checking needed */ + fcgi_finish(fc, "waiting for CGI reply"); + return; } - if (fd_stdout >= 0 && FD_ISSET(fd_stdout, &rset)) { - nread = read(fd_stdout, buf, sizeof(buf)); - if (nread <= 0) { - close(fd_stdout); - fd_stdout = -1; + if (fc->fd_stdout >= 0 && FD_ISSET(fc->fd_stdout, &rset)) { + err = fcgi_pass_fd(&fc->fd_stdout, FCGI_stdout, buf, sizeof(buf)); + if (err) { + fcgi_finish(fc, err); + return; } - fwrite(buf, 1, nread, stdout); + fc->have_reply = 1; } - if (fd_stderr >= 0 && FD_ISSET(fd_stderr, &rset)) { - nread = read(fd_stderr, buf, sizeof(buf)); - if (nread <= 0) { - close(fd_stderr); - fd_stderr = -1; + if (fc->fd_stderr >= 0 && FD_ISSET(fc->fd_stderr, &rset)) { + err = fcgi_pass_fd(&fc->fd_stderr, FCGI_stderr, buf, sizeof(buf)); + if (err) { + fcgi_finish(fc, err); + return; } - fwrite(buf, 1, nread, stderr); + fc->have_reply = 1; /* ? */ } } + + fc->cgi_pid = 0; + + fcgi_finish(fc, "reading CGI reply (no response received)"); } int check_file_perms(const char *path) @@ -101,43 +170,63 @@ int check_file_perms(const char *path) } } -char *get_cgi_filename() +char *get_cgi_filename() /* and fixup environment */ { int buflen = 1, docrootlen; - char *buf; + char *buf = NULL; char *docroot, *scriptname, *p; + int rf_len; + char *pathinfo = NULL; + if ((p = getenv("DOCUMENT_ROOT"))) { docroot = p; buflen += docrootlen = strlen(p); } else { - return NULL; + goto err; } if ((p = getenv("SCRIPT_NAME"))) { buflen += strlen(p); scriptname = p; } else { - return NULL; + goto err; } buf = malloc(buflen); - if (!buf) return NULL; + if (!buf) goto err; strcpy(buf, docroot); strcpy(buf + docrootlen, scriptname); + pathinfo = strdup(buf); + if (!pathinfo) { + goto err; + } while(1) { switch(check_file_perms(buf)) { - case -EACCES: return NULL; - case 0: return buf; + case -EACCES: + goto err; + case 0: + rf_len = strlen(buf); + if (rf_len < buflen - 1) { + setenv("PATH_INFO", pathinfo + rf_len, 1); + setenv("SCRIPT_NAME", buf + docrootlen, 1); + } else { + unsetenv("PATH_INFO"); + } + free(pathinfo); + return buf; default: p = strrchr(buf, '/'); - if (!p) return NULL; + if (!p) goto err; *p = 0; } } +err: + free(pathinfo); + free(buf); return NULL; } @@ -147,15 +236,17 @@ static void handle_fcgi_request() int pipe_out[2]; int pipe_err[2]; char *filename; + pid_t pid; + + struct fcgi_context fc; - /* XXX error handling */ - pipe(pipe_in); - pipe(pipe_out); - pipe(pipe_err); + if (pipe(pipe_in) < 0) goto err_pipein; + if (pipe(pipe_out) < 0) goto err_pipeout; + if (pipe(pipe_err) < 0) goto err_pipeerr; - switch(fork()) { + switch((pid = fork())) { case -1: - return; + goto err_fork; case 0: /* child */ filename = get_cgi_filename(); @@ -172,7 +263,7 @@ static void handle_fcgi_request() dup2(pipe_err[1], 2); execl(filename, filename, NULL); - /* we _do_ want a 502 here probably */ + puts("Status: 502 Bad Gateway\nContent-type: text/plain\n\n502"); exit(99); default: /* parent */ @@ -180,11 +271,35 @@ static void handle_fcgi_request() close(pipe_out[1]); close(pipe_err[1]); - fcgi_pass(pipe_in[1], pipe_out[0], pipe_err[0]); + fc.fd_stdin = pipe_in[1]; + fc.fd_stdout = pipe_out[0]; + fc.fd_stderr = pipe_err[0]; + fc.have_reply = 0; + fc.cgi_pid = pid; + + fcgi_pass(&fc); } + return; + +err_fork: + close(pipe_err[0]); + close(pipe_err[1]); + +err_pipeerr: + close(pipe_out[0]); + close(pipe_out[1]); + +err_pipeout: + close(pipe_in[0]); + close(pipe_in[1]); + +err_pipein: + + FCGI_puts("Status: 502 Bad Gateway\nContent-type: text/plain\n"); + FCGI_puts("System error"); } -int main(int argc, char **argv) +int main(/* int argc, char **argv */) { signal(SIGCHLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); -- cgit v1.2.3