diff options
Diffstat (limited to 'fcgiwrap.c')
| -rw-r--r-- | fcgiwrap.c | 170 | 
1 files changed, 170 insertions, 0 deletions
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; +} +  | 
