eris/cgi.c

180 lines
4.8 KiB
C

void
sigchld(int sig)
{
while (waitpid(0, NULL, WNOHANG) > 0);
}
static void
cgi_child(const char *relpath)
{
setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
setenv("SERVER_SOFTWARE", FNORD, 1);
setenv("REQUEST_URI", path, 1);
setenv("SERVER_NAME", host, 1);
setenv("SCRIPT_NAME", relpath, 1);
setenv("REMOTE_ADDR", remote_ip, 1);
setenv("REMOTE_PORT", remote_port, 1);
setenv("REMOTE_IDENT", remote_ident, 1);
setenv("CONTENT_TYPE", content_type, 1);
{
char cl[20];
snprintf(cl, sizeof cl, "%llu", (unsigned long long) content_length);
setenv("CONTENT_LENGTH", cl, 1);
}
execl(relpath, relpath, NULL);
exit(1);
}
void
cgi_parent(int cin, int cout)
{
FILE *cinf = fdopen(cin, "rb");
int passthru = nph;
fcntl(child_in, F_SETFL, O_NONBLOCK);
signal(SIGCHLD, sigchld);
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
/* Eris is not this smart yet */
keepalive = 0;
while (1) {
int nfds;
fd_set rfds, wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(cin[0], &rfds);
nfds = cin[0];
if (post_len) {
/* have post data */
FD_SET(cout[1], &wfds);
if (cout[1] > nfds) {
nfds = cout[1];
}
} else if (cout[1] >= 0) {
close(cout[1]); /* no post data */
cout[1] = -1;
}
if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) {
break;
}
if (FD_ISSET(cin[0], &rfds)) {
if (passthru) {
size_t len;
/* Re-use this big buffer */
len = fread(cgiheader, 1, sizeof cgiheader, cinf);
if (0 == len) {
/* CGI is done */
break;
}
fwrite(cgiheader, 1, len, stdout);
size += len;
} else {
int ret;
ret = read_header(cinf, cgiheader, &cgiheaderlen);
if (0 == ret) {
/* Call read_header again */
} else if (-1 == ret) {
/* EOF or error */
badrequest(500, "CGI Error",
"CGI output too weird");
} else {
/* Entire header is in memory now */
passthru = 1;
/* XXX: I think we need to look for Location:
* anywhere, but fnord got away with checking
* only the first header field, so I will too.
*/
if (memcmp(cgiheader, "Location: ", 10) == 0) {
retcode = 302;
printf
("HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n");
fwrite(cgiheader, 1, cgiheaderlen, stdout);
dolog(0);
exit(0);
}
retcode = 200;
printf("HTTP/1.0 200 OK\r\nServer: "
FNORD
"\r\nPragma: no-cache\r\nConnection: close\r\n");
signal(SIGCHLD, SIG_IGN);
fwrite(cgiheader, 1, cgiheaderlen, stdout);
}
}
} else if (FD_ISSET(cout[1], &wfds)) {
/*
* write to cgi the post data
*/
if (post_len) {
size_t len;
char buf[BUFFER_SIZE];
size_t nmemb = min(BUFFER_SIZE, post_len);
len = fread(buf, 1, nmemb, stdin);
if (len < 1) {
break;
}
post_len -= len;
write(cout[1], buf, len);
} else {
close(cout[1]);
}
}
}
alarm(0);
fflush(stdout);
dolog(size);
cork(0);
}
void
serve_cgi(char *relpath)
{
size_t size = 0;
int pid;
char buf[BUFFER_SIZE];
int cin[2];
int cout[2];
if (pipe(cin) || pipe(cout)) {
badrequest(500, "Internal Server Error", "Server Resource problem.");
}
pid = fork();
if (-1 == pid) {
badrequest(500, "Internal Server Error", "Unable to fork.");
}
if (pid) {
close(cin[1]);
close(cout[0]);
alarm(CHILD_TIMEOUT);
cgi_parent(cin[0], cout[1]);
alarm(0);
} else {
close(cwd);
close(cout[1]);
close(cin[0]);
dup2(cout[0], 0);
dup2(cin[1], 1);
close(cout[0]);
close(cin[1]);
cgi_child(relpath);
}
}