eris/cgi.c

214 lines
5.7 KiB
C
Raw Normal View History

2012-03-07 20:55:27 -07:00
void
sigchld(int sig)
{
while (waitpid(0, NULL, WNOHANG) > 0);
}
2012-03-19 15:04:45 -06:00
void
sigalarm_cgi(int sig)
{
/* send this out regardless of whether we've already sent a header,
* to maybe help with debugging */
badrequest(504, "Gateway Timeout", "The CGI is being too slow.");
}
2012-03-07 20:55:27 -07:00
static void
cgi_child(const char *relpath)
{
env("GATEWAY_INTERFACE", "CGI/1.1");
env("SERVER_SOFTWARE", FNORD);
env("REQUEST_URI", path);
env("SERVER_NAME", host);
env("SCRIPT_NAME", relpath);
2012-05-16 12:02:51 -06:00
env("REMOTE_ADDR", remote_addr);
env("REMOTE_IDENT", remote_ident);
2012-03-07 22:16:02 -07:00
if (content_length) {
2012-03-07 20:55:27 -07:00
char cl[20];
snprintf(cl, sizeof cl, "%llu", (unsigned long long) content_length);
env("CONTENT_LENGTH", cl);
env("CONTENT_TYPE", content_type);
2012-03-07 20:55:27 -07:00
}
2012-10-30 14:38:58 -06:00
/* Change to CGI's directory */
{
char *delim = strrchr(relpath, '/');
if (delim) {
*delim = '\0';
chdir(relpath);
relpath = delim + 1;
}
}
2012-03-07 20:55:27 -07:00
execl(relpath, relpath, NULL);
exit(1);
}
void
2012-03-07 22:16:02 -07:00
cgi_parent(int cin, int cout, int passthru)
2012-03-07 20:55:27 -07:00
{
2012-03-07 22:16:02 -07:00
char cgiheader[BUFFER_SIZE];
size_t cgiheaderlen = 0;
2012-03-07 20:55:27 -07:00
FILE *cinf = fdopen(cin, "rb");
2012-03-07 22:16:02 -07:00
size_t size = 0;
int header_sent = 0;
2012-10-09 11:36:06 -06:00
int code = 200;
2012-03-07 20:55:27 -07:00
2012-03-07 22:16:02 -07:00
fcntl(cin, F_SETFL, O_NONBLOCK);
2012-03-07 20:55:27 -07:00
signal(SIGCHLD, sigchld);
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
while (1) {
int nfds;
fd_set rfds, wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
2012-03-07 22:16:02 -07:00
FD_SET(cin, &rfds);
nfds = cin;
2012-03-07 20:55:27 -07:00
2012-03-07 22:16:02 -07:00
if (content_length) {
2012-03-07 20:55:27 -07:00
/* have post data */
2012-03-07 22:16:02 -07:00
FD_SET(cout, &wfds);
if (cout > nfds) {
nfds = cout;
2012-03-07 20:55:27 -07:00
}
2012-03-07 22:16:02 -07:00
} else if (cout >= 0) {
close(cout); /* no post data */
cout = -1;
2012-03-07 20:55:27 -07:00
}
if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) {
break;
}
2012-03-07 22:16:02 -07:00
if (FD_ISSET(cin, &rfds)) {
2012-03-07 20:55:27 -07:00
if (passthru) {
2012-03-07 22:16:02 -07:00
/* Pass everything through verbatim */
2012-03-07 20:55:27 -07:00
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);
2012-07-17 11:35:19 -06:00
/* Naively assume the CGI knows best about sending stuff */
fflush(stdout);
2012-03-07 20:55:27 -07:00
size += len;
} else {
2012-03-07 22:16:02 -07:00
/* Interpret header fields */
size_t readlen = (sizeof cgiheader) - cgiheaderlen;
2012-03-07 20:55:27 -07:00
2012-03-07 22:16:02 -07:00
if (NULL == fgets(cgiheader + cgiheaderlen, readlen, cinf)) {
2012-03-07 20:55:27 -07:00
/* EOF or error */
2012-03-07 22:16:02 -07:00
badrequest(500, "CGI Error", "CGI output too weird");
}
cgiheaderlen = strlen(cgiheader);
if ('\n' == cgiheader[cgiheaderlen - 1]) {
/* We read a whole line */
size_t len;
char *val;
len = extract_header_field(cgiheader, &val, 0);
if (! len) {
/* We've read the entire header block */
passthru = 1;
eoh();
} else {
if (! header_sent) {
if (! strcasecmp(cgiheader, "Location")) {
header(302, "CGI Redirect");
2012-09-17 15:35:52 -06:00
printf("%s: %s\r\n\r\n", cgiheader, val);
2012-03-07 22:16:02 -07:00
dolog(302, 0);
exit(0);
2012-10-09 11:36:06 -06:00
} else if (! strcasecmp(cgiheader, "Status")) {
char *txt = val + 4;
code = atoi(val);
header(code, txt);
} else {
header(200, "OK");
printf("Pragma: no-cache\r\n");
2012-03-07 22:16:02 -07:00
}
header_sent = 1;
}
printf("%s: %s\r\n", cgiheader, val);
cgiheaderlen = 0;
2012-03-07 20:55:27 -07:00
}
}
}
2012-03-07 22:16:02 -07:00
} else if (FD_ISSET(cout, &wfds)) {
2012-03-07 20:55:27 -07:00
/*
* write to cgi the post data
*/
2012-03-07 22:16:02 -07:00
if (content_length) {
2012-03-07 20:55:27 -07:00
size_t len;
char buf[BUFFER_SIZE];
2012-03-07 22:16:02 -07:00
size_t nmemb = min(BUFFER_SIZE, content_length);
2012-03-07 20:55:27 -07:00
len = fread(buf, 1, nmemb, stdin);
if (len < 1) {
break;
}
2012-03-07 22:16:02 -07:00
content_length -= len;
write(cout, buf, len);
2012-03-07 20:55:27 -07:00
} else {
2012-03-07 22:16:02 -07:00
close(cout);
2012-03-07 20:55:27 -07:00
}
}
}
fflush(stdout);
2012-03-07 22:16:02 -07:00
dolog(200, size);
2012-03-07 20:55:27 -07:00
cork(0);
}
2012-03-07 22:16:02 -07:00
2012-03-07 20:55:27 -07:00
void
serve_cgi(char *relpath)
{
int pid;
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]);
2012-03-07 22:16:02 -07:00
/* Eris is not this smart yet */
keepalive = 0;
alarm(CGI_TIMEOUT);
2012-03-19 15:04:45 -06:00
signal(SIGALRM, sigalarm_cgi);
2012-03-07 22:16:02 -07:00
cgi_parent(cin[0], cout[1], 0);
exit(0);
2012-03-07 20:55:27 -07:00
} 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);
}
}