X-Git-Url: http://www.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fcallcgi.c;h=14430aa4357580d4fd5527fe5159f6249aa061d7;hb=09c82f9c7bc563c081425141853e6ff8e402e358;hp=10bb87c9af47e6223dc35fd237a40fad51d2d98b;hpb=f0a758cc8c1337606a392db67e63cec6601d2730;p=ashd.git diff --git a/src/callcgi.c b/src/callcgi.c index 10bb87c..14430aa 100644 --- a/src/callcgi.c +++ b/src/callcgi.c @@ -23,33 +23,53 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include #endif #include #include +#include static char **environ; -static void passdata(FILE *in, FILE *out) +static int passdata(FILE *in, FILE *out) { int ret; - char *buf; + char buf[65536]; + struct pollfd pfds[2]; - buf = smalloc(65536); while(!feof(in)) { - ret = fread(buf, 1, 65536, in); - if(ferror(in)) { - flog(LOG_ERR, "sendfile: could not read input: %s", strerror(errno)); - break; + memset(pfds, 0, sizeof(struct pollfd) * 2); + pfds[0].fd = fileno(in); + pfds[0].events = POLLIN; + pfds[1].fd = fileno(out); + pfds[1].events = POLLHUP; + ret = poll(pfds, 2, -1); + if(ret < 0) { + if(errno != EINTR) { + flog(LOG_ERR, "callcgi: error in poll: %s", strerror(errno)); + return(1); + } } - if(fwrite(buf, 1, ret, out) != ret) { - flog(LOG_ERR, "sendfile: could not write output: %s", strerror(errno)); - break; + if(ret > 0) { + if(pfds[0].revents & (POLLIN | POLLERR | POLLHUP)) { + ret = fread(buf, 1, 65536, in); + if(ferror(in)) { + flog(LOG_ERR, "callcgi: could not read input: %s", strerror(errno)); + return(1); + } + if(fwrite(buf, 1, ret, out) != ret) { + flog(LOG_ERR, "callcgi: could not write output: %s", strerror(errno)); + return(1); + } + } + if(pfds[1].revents & POLLHUP) + return(1); } } - free(buf); + return(0); } static char *absolutify(char *file) @@ -63,12 +83,13 @@ static char *absolutify(char *file) return(sstrdup(file)); } -static void forkchild(int inpath, char *prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd) +static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd) { int i; char *qp, **env, *name; int inp[2], outp[2]; pid_t pid; + char *unqr; pipe(inp); pipe(outp); @@ -90,14 +111,14 @@ static void forkchild(int inpath, char *prog, char *file, char *method, char *ur if(getenv("HTTP_VERSION")) putenv(sprintf2("SERVER_PROTOCOL=%s", getenv("HTTP_VERSION"))); putenv(sprintf2("REQUEST_METHOD=%s", method)); - putenv(sprintf2("PATH_INFO=/%s", rest)); + unqr = unquoteurl(rest); + putenv(sprintf2("PATH_INFO=%s", unqr?unqr:rest)); name = url; /* XXX: This is an ugly hack (I think), but though I can think * of several alternatives, none seem to be better. */ - if(*rest && (strlen(url) > strlen(rest)) && - !strcmp(rest, url + strlen(url) - strlen(rest)) && - (url[strlen(url) - strlen(rest) - 1] == '/')) { - name = sprintf2("%.*s", (int)(strlen(url) - strlen(rest) - 1), url); + if(*rest && (strlen(url) >= strlen(rest)) && + !strcmp(rest, url + strlen(url) - strlen(rest))) { + name = sprintf2("%.*s", (int)(strlen(url) - strlen(rest)), url); } putenv(sprintf2("SCRIPT_NAME=%s", name)); putenv(sprintf2("QUERY_STRING=%s", qp?qp:"")); @@ -109,6 +130,8 @@ static void forkchild(int inpath, char *prog, char *file, char *method, char *ur putenv("HTTPS=on"); if(getenv("REQ_X_ASH_ADDRESS")) putenv(sprintf2("REMOTE_ADDR=%s", getenv("REQ_X_ASH_ADDRESS"))); + if(getenv("REQ_X_ASH_REMOTE_USER")) + putenv(sprintf2("REMOTE_USER=%s", getenv("REQ_X_ASH_REMOTE_USER"))); if(getenv("REQ_CONTENT_TYPE")) putenv(sprintf2("CONTENT_TYPE=%s", getenv("REQ_CONTENT_TYPE"))); if(getenv("REQ_CONTENT_LENGTH")) @@ -132,6 +155,7 @@ static void forkchild(int inpath, char *prog, char *file, char *method, char *ur close(outp[1]); *infd = inp[1]; *outfd = outp[0]; + return(pid); } static void trim(struct charbuf *buf) @@ -143,7 +167,7 @@ static void trim(struct charbuf *buf) for(p = buf->b + buf->d - 1; (p > buf->b) && isspace(*p); p--, buf->d--); } -static char **parseheaders(FILE *s) +static char **parsecgiheaders(FILE *s) { int c, state; struct charvbuf hbuf; @@ -286,25 +310,30 @@ static void sendheaders(char **headers, FILE *out) static void usage(void) { - flog(LOG_ERR, "usage: callcgi [-p PROGRAM] METHOD URL REST"); + flog(LOG_ERR, "usage: callcgi [-c] [-p PROGRAM] METHOD URL REST"); } int main(int argc, char **argv, char **envp) { int c; - char *file, *prog; - int inpath; + char *file, *prog, *sp; + int inpath, cd; int infd, outfd; FILE *in, *out; char **headers; + pid_t child; environ = envp; signal(SIGPIPE, SIG_IGN); prog = NULL; inpath = 0; - while((c = getopt(argc, argv, "p:")) >= 0) { + cd = 0; + while((c = getopt(argc, argv, "cp:")) >= 0) { switch(c) { + case 'c': + cd = 1; + break; case 'p': prog = optarg; inpath = 1; @@ -323,20 +352,36 @@ int main(int argc, char **argv, char **envp) flog(LOG_ERR, "callcgi: needs to be called with the X-Ash-File header"); exit(1); } + + if(cd) { + /* This behavior is encouraged by the CGI specification (RFC 3875, 7.2), + * but not strictly required, and I get the feeling it might break some + * relative paths here or there, so it's not the default for now. */ + if((sp = strrchr(file, '/')) != NULL) { + *sp = 0; + if(chdir(file)) { + *sp = '/'; + } else { + file = sp + 1; + } + } + } + if(prog == NULL) prog = file; - forkchild(inpath, prog, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd); + child = forkchild(inpath, prog, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd); in = fdopen(infd, "w"); - passdata(stdin, in); + passdata(stdin, in); /* Ignore errors, perhaps? */ fclose(in); out = fdopen(outfd, "r"); - if((headers = parseheaders(out)) == NULL) { + if((headers = parsecgiheaders(out)) == NULL) { flog(LOG_WARNING, "CGI handler returned invalid headers"); exit(1); } sendstatus(headers, stdout); sendheaders(headers, stdout); printf("\n"); - passdata(out, stdout); + if(passdata(out, stdout)) + kill(child, SIGINT); return(0); }