diff options
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | fcgiwrap.c | 203 | 
2 files changed, 164 insertions, 47 deletions
| @@ -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 @@ -1,3 +1,6 @@ +#define NO_FCGI_DEFINES + +#include <stdarg.h>  #include <fcgi_stdio.h>  #include <stdlib.h>  #include <signal.h> @@ -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); | 
