rendered paste bodystatic void do_cgi(struct http_server_instance *ser, const char *uri, struct variable *vars, struct variable *headers)
{
int env_cnt, cgi_argc, i, res;
char **cgi_env, **cgi_argv, *tmp, *path, *prog;
struct variable *v;
env_cnt = envcount(environ);
cgi_env = (char **) alloca((CGI_ENV_LEN + env_cnt + 1) * sizeof(char *));
i = 0;
cgi_env[i++] = "GATEWAY_INTERFACE=CGI/1.1";
cgi_env[i++] = "SERVER_PROTOCOL=HTTP/1.0";
cgi_env[i++] = "SERVER_SOFTWARE=OOGA"; /* FIXME */
asprintf(&cgi_env[i++], "SERVER_NAME=%s", "this.is.my.server.name.dom" /* host */);
asprintf(&cgi_env[i++], "SERVER_PORT=%s", "80" /* port */);
asprintf(&cgi_env[i++], "REQUEST_METHOD=%s", "POST" /* method_name[method] */);
asprintf(&cgi_env[i++], "REQUEST_URI=%s", uri);
asprintf(&cgi_env[i++], "SCRIPT_NAME=%s", "ooga" /* url */);
if((v = find_var(headers, "Content-Length")))
asprintf(&cgi_env[i++], "CONTENT_LENGTH=%s", v->value);
if((v = find_var(headers, "Content-Type")))
asprintf(&cgi_env[i++], "CONTENT_TYPE=%s", v->value);
cgi_env[i] = 0;
/*
* FIXME: I don't like this, not one bit.
* is there a clean way to count the number of variables without walking the list, or
* alternatively, is there a way to cleanly keep reallocating larger and larger space for the argv list?
*
* Also, I don't like the if(!strlen_zero) statement there, I was going to do %s%c%s
* and make %c a \0 and the second %s a zero-length string if there was no value, but I think that'd
* end up stinkier.
*/
/* now set up argc/argv */
i = 1;
if(vars) {
v = vars;
while(v) { v = v->next; i++; }
}
cgi_argv = (char **) alloca((i + 1) * sizeof(char *));
cgi_argc = 1;
if(vars) {
v = vars;
while(v) {
if(!strlen_zero(v->value))
asprintf(&cgi_argv[cgi_argc++], "%s=%s", v->name, v->value);
else
asprintf(&cgi_argv[cgi_argc++], "%s", v->name);
v = v->next;
}
}
cgi_argv[cgi_argc] = 0;
/* copy uri to tmp for splitting into path and program */
path = tmp = alloca(strlen(uri) + 1);
strcpy(tmp, uri);
prog = strrchr(tmp, '/');
if(prog) {
tmp[prog-tmp] = 0;
prog++; /* prog now points to the program */
}
else { /* no path?? */
path = "/bin";
prog = tmp;
}
chdir(path);
cgi_argv[0] = prog;
i = 3;
while((res = access(cgi_argv[0], X_OK) != 0) || ((res == EAGAIN) && (i > 0))) {
fprintf(stderr, "access returned %i, retries=%i\n", res, i);
usleep(250);
--i;
}
if(res == 0) {
execve(cgi_argv[0], cgi_argv, cgi_env); /* doesn't return */
fprintf(stderr, "wtf, execve returned? errno=%i\n", errno);
_exit(255);
}
else { /* can't execute */
fprintf(stderr, "can't execute %s: %i (%s)\n", cgi_argv[0], errno, strerror(errno));
_exit(255);
}
}
static char *cgi_parent_process(FILE *http, pid_t child, int infd, int outfd, int *status, char **title, struct variable *headers)
{
int res, wres, first_read = 1, clen;
struct pollfd pfd[2];
size_t size = 0;
char inbuf[1024], outbuf[1024], *result = NULL;
// TODO: add signal handlers
// signal(SIGCHLD, cgi_child);
// signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
pfd[0].fd = infd;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
if(outfd > 0) {
pfd[1].fd = outfd;
pfd[1].events = POLLOUT;
pfd[1].revents = 0;
}
/* now marshall data back and forth as needed */
while(poll(pfd, (outfd > 0) ? 2 : 1, -1) != -1) {
if(pfd[0].revents & POLLIN) { /* CGI has data to read */
res = read(infd, inbuf, sizeof(inbuf));
if(res == 0)
break; /* no data */
else if(res < 0)
goto cgi_500; /* error */
if(first_read) {
first_read = 0;
*status = 200;
fprintf(http, "HTTP/1.0 200 OK\r\nServer: OOGA\r\nPragma: no-cache\r\nConnection: close\r\n\r\n");
// signal(SIGCHLD, SIG_IGN);
}
else {
wres = fwrite(inbuf, res, 1, http);
}
size += res;
if(pfd[0].revents & POLLHUP) /* disconnect? */
break;
}
else if(outfd > 0 && pfd[1].revents & POLLOUT) { /* write POST data to cgi */
res = read(fileno(http), outbuf, sizeof(outbuf));
if(res < 1) {
if(res == 0) { /* EOF? */
close(outfd);
outfd = 0;
}
else /* error */
goto cgi_500;
}
wres = write(outfd, outbuf, res);
if(wres != res)
goto cgi_500;
}
else if(pfd[0].revents & POLLHUP) /* CGI disconnection */
break;
else { /* other (unhandled) revent */
cgi_500:
if(first_read) {
result = http_error(500, "Internal Server Error", NULL, "Looks like the CGI crashed.");
break;
}
else {
fprintf(http, "\n\nLooks like the CGI crashed.\n\n");
break;
}
}
} /* while(poll) */
/* buffer_flush(buffer_1); */
/* if we're using the TCP_CORK socket option, we've got to remove it to ensure the rest of the message is written out */
#ifdef TCP_CORK
{
int zero = 0;
setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero));
}
#endif
return result;
}
static char *cgi_callback(struct http_server_instance *ser, const char *uri, struct variable *vars, struct variable *headers, int *status, char **title, int *contentlength)
{
char *result = NULL;
pid_t pid; /* child PID */
int fd[2]; /* http to CGI pipe pair, http writes to [1] */
int df[2]; /* CGI to http pipe pair, cgi writes to [1] */
if(pipe(fd) || pipe(df)) {
return http_error(500, "Internal Server Error", NULL, "Cannot allocate pipe fds.");
}
pid = vfork();
if(pid > 0) { /* this is the parent (server) process */
close(fd[0]);
close(df[1]);
result = cgi_parent_process(ser->f, pid, df[0], fd[1], status, title, headers);
}
else if(pid == 0) { /* this is the child process that runs the CGI */
close(df[0]);
close(fd[1]);
// alarm(CGI_TIMEOUT); /* set up an alarm in case the CGI hangs */
do_cgi(ser, uri, vars, headers); /* and actually run the CGI program (doesn't return on success) */
}
else { /* fork error */
result = http_error(500, "Internal Server Error", NULL, "Cannot vfork.");
}
return result;
}