+ execv(fullpath, argp);
+ }
+ error_execing_cgi:
+ /* send to stdout (even if we are not from inetd) */
+ accepted_socket = 1;
+ sendHeaders(HTTP_NOT_FOUND);
+ _exit(242);
+ } /* end child */
+
+ /* parent process */
+#if !BB_MMU
+ free(fullpath);
+#endif
+
+ buf_count = 0;
+ post_read_size = 0;
+ post_read_idx = 0; /* for gcc */
+ inFd = fromCgi[0];
+ outFd = toCgi[1];
+ close(fromCgi[1]);
+ close(toCgi[0]);
+ signal(SIGPIPE, SIG_IGN);
+
+ while (1) {
+ fd_set readSet;
+ fd_set writeSet;
+ char wbuf[128];
+ int nfound;
+ int count;
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_SET(inFd, &readSet);
+ if (bodyLen > 0 || post_read_size > 0) {
+ FD_SET(outFd, &writeSet);
+ nfound = outFd > inFd ? outFd : inFd;
+ if (post_read_size == 0) {
+ FD_SET(accepted_socket, &readSet);
+ if (nfound < accepted_socket)
+ nfound = accepted_socket;
+ }
+ /* Now wait on the set of sockets! */
+ nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
+ } else {
+ if (!bodyLen) {
+ close(outFd); /* no more POST data to CGI */
+ bodyLen = -1;
+ }
+ nfound = select(inFd + 1, &readSet, NULL, NULL, NULL);
+ }
+
+ if (nfound <= 0) {
+ if (waitpid(pid, &status, WNOHANG) <= 0) {
+ /* Weird. CGI didn't exit and no fd's
+ * are ready, yet select returned?! */
+ continue;
+ }
+ close(inFd);
+ if (DEBUG && WIFEXITED(status))
+ bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
+ if (DEBUG && WIFSIGNALED(status))
+ bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
+ break;
+ }
+
+ if (post_read_size > 0 && FD_ISSET(outFd, &writeSet)) {
+ /* Have data from peer and can write to CGI */
+ // huh? why full_write? what if we will block?
+ // (imagine that CGI does not read its stdin...)
+ count = full_write(outFd, wbuf + post_read_idx, post_read_size);
+ if (count > 0) {
+ post_read_idx += count;
+ post_read_size -= count;
+ } else {
+ post_read_size = bodyLen = 0; /* broken pipe to CGI */
+ }
+ } else if (bodyLen > 0 && post_read_size == 0
+ && FD_ISSET(accepted_socket, &readSet)
+ ) {
+ /* We expect data, prev data portion is eaten by CGI
+ * and there *is* data to read from the peer
+ * (POSTDATA?) */
+ count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
+ count = safe_read(accepted_socket, wbuf, count);
+ if (count > 0) {
+ post_read_size = count;
+ post_read_idx = 0;
+ bodyLen -= count;
+ } else {
+ bodyLen = 0; /* closed */
+ }
+ }
+
+#define PIPESIZE PIPE_BUF
+#if PIPESIZE >= MAX_MEMORY_BUFF
+# error "PIPESIZE >= MAX_MEMORY_BUFF"
+#endif
+ if (FD_ISSET(inFd, &readSet)) {
+ /* There is something to read from CGI */
+ int s = accepted_socket;
+ char *rbuf = iobuf;
+
+ /* Are we still buffering CGI output? */
+ if (buf_count >= 0) {
+ static const char HTTP_200[] = "HTTP/1.0 200 OK\r\n";
+ /* Must use safe_read, not full_read, because
+ * CGI may output a few first bytes and then wait
+ * for POSTDATA without closing stdout.
+ * With full_read we may wait here forever. */
+ count = safe_read(inFd, rbuf + buf_count, PIPESIZE - 4);
+ if (count <= 0) {
+ /* eof (or error) and there was no "HTTP",
+ * so add one and write out the received data */
+ if (buf_count) {
+ full_write(s, HTTP_200, sizeof(HTTP_200)-1);
+ full_write(s, rbuf, buf_count);
+ }
+ break; /* closed */
+ }
+ buf_count += count;
+ count = 0;
+ if (buf_count >= 4) {
+ /* check to see if CGI added "HTTP" */
+ if (memcmp(rbuf, HTTP_200, 4) != 0) {
+ /* there is no "HTTP", do it ourself */
+ if (full_write(s, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
+ break;
+ }
+ /* example of valid CGI without "Content-type:"
+ * echo -en "HTTP/1.0 302 Found\r\n"
+ * echo -en "Location: http://www.busybox.net\r\n"
+ * echo -en "\r\n"
+ if (!strstr(rbuf, "ontent-")) {
+ full_write(s, "Content-type: text/plain\r\n\r\n", 28);
+ }
+ */
+ count = buf_count;
+ buf_count = -1; /* buffering off */
+ }
+ } else {
+ count = safe_read(inFd, rbuf, PIPESIZE);
+ if (count <= 0)
+ break; /* eof (or error) */
+ }
+ if (full_write(s, rbuf, count) != count)
+ break;
+ if (DEBUG)
+ fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
+ } /* if (FD_ISSET(inFd)) */
+ } /* while (1) */
+ return 0;