diff options
author | Grzegorz Nosek <root@localdomain.pl> | 2007-03-10 15:31:59 +0100 |
---|---|---|
committer | Grzegorz Nosek <root@localdomain.pl> | 2007-03-10 15:31:59 +0100 |
commit | f0a52196d1e1e54cf8db8e6781ec3127c0a411d5 (patch) | |
tree | 7f6ff763f17502b039149edb68dda47796b8244b | |
download | fcgiwrap-f0a52196d1e1e54cf8db8e6781ec3127c0a411d5.tar.xz fcgiwrap-f0a52196d1e1e54cf8db8e6781ec3127c0a411d5.zip |
Initial commit
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | fcgiwrap.c | 170 |
2 files changed, 180 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ac01a8b --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +all: fcgiwrap +go: all + ../../bin/spawn-fcgi -u admin -f ./fcgiwrap -a 172.16.0.2 -p 14017 + +fcgiwrap: fcgiwrap.c + gcc -Wall -Werror -pedantic -O2 -g fcgiwrap.c -o fcgiwrap -lfcgi + +clean: + -rm fcgiwrap + diff --git a/fcgiwrap.c b/fcgiwrap.c new file mode 100644 index 0000000..577b13c --- /dev/null +++ b/fcgiwrap.c @@ -0,0 +1,170 @@ +#include <fcgi_stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/select.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> + +#define FCGI_BUF_SIZE 4096 + +static int write_all(int fd, char *buf, size_t size) +{ + size_t nleft = size; + while (nleft > 0) { + ssize_t nwritten = write(fd, buf, nleft); + if (nwritten < 0) + return nleft - size; /* zero or negative to indicate error */ + + buf += nwritten; + nleft -= nwritten; + } + + return size; +} + +static void fcgi_pass(int fd_stdin, int fd_stdout, int fd_stderr) +{ + char buf[FCGI_BUF_SIZE]; + size_t nread; + fd_set rset; + int maxfd = (fd_stdout > fd_stderr) ? fd_stdout : fd_stderr; + int nready; + + /* slurp the whole input and pass it to CGI */ + + while ((nread = fread(buf, 1, sizeof(buf), stdin))) { + if (write_all(fd_stdin, buf, nread) <= 0) return; + } + + close(fd_stdin); + + /* now wait for CGI replies on stdout and stderr */ + + while (fd_stdout >= 0 && fd_stderr >= 0) { + FD_ZERO(&rset); + if (fd_stdout >= 0) FD_SET(fd_stdout, &rset); + if (fd_stderr >= 0) FD_SET(fd_stderr, &rset); + nready = select(maxfd, &rset, NULL, NULL, NULL); + if (nready < 0) { + if (errno == EAGAIN) continue; + return; /* better error checking needed */ + } + 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; + } + fwrite(buf, 1, nread, stdout); + } + 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; + } + fwrite(buf, 1, nread, stderr); + } + } +} + +char *get_cgi_filename() +{ + int buflen = 1, docrootlen; + char *buf; + char *docroot, *scriptname, *p; + struct stat s; + + if ((p = getenv("DOCUMENT_ROOT"))) { + docroot = p; + buflen += docrootlen = strlen(p); + } else { + return NULL; + } + + if ((p = getenv("SCRIPT_NAME"))) { + buflen += strlen(p); + scriptname = p; + } else { + return NULL; + } + + buf = malloc(buflen); + if (!buf) return NULL; + + strcpy(buf, docroot); + strcpy(buf + docrootlen, scriptname); + + while(1) { + if (lstat(buf, &s) == 0) { + if (!S_ISREG(s.st_mode) || ((s.st_mode & S_IXOTH) == 0)) + return NULL; /* 403 */ + + return buf; + } + p = strrchr(buf, '/'); + if (!p) break; + *p = 0; + } + + return NULL; +} + +static void handle_fcgi_request() +{ + int pipe_in[2]; + int pipe_out[2]; + int pipe_err[2]; + char *filename; + + /* XXX error handling */ + pipe(pipe_in); + pipe(pipe_out); + pipe(pipe_err); + + switch(fork()) { + case -1: + return; + + case 0: /* child */ + filename = get_cgi_filename(); + if (!filename) { + puts("Status: 403 Forbidden\nContent-type: text/plain\n\n403"); + exit(99); + } + close(pipe_in[1]); + close(pipe_out[0]); + close(pipe_err[0]); + + dup2(pipe_in[0], 0); + dup2(pipe_out[1], 1); + dup2(pipe_err[1], 2); + + execl(filename, filename, NULL); + /* we _do_ want a 502 here probably */ + exit(99); + + default: /* parent */ + close(pipe_in[0]); + close(pipe_out[1]); + close(pipe_err[1]); + + fcgi_pass(pipe_in[1], pipe_out[0], pipe_err[0]); + } +} + +int main(int argc, char **argv) +{ + signal(SIGCHLD, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + while (FCGI_Accept() >= 0) { + handle_fcgi_request(); + } + + return 0; +} + |