Sun, 13 Nov 2022 09:41:07 +0100
parse cgi stderr output for logging and use non-blocking pipes
src/server/safs/cgi.c | file | annotate | diff | comparison | revisions | |
src/server/safs/cgi.h | file | annotate | diff | comparison | revisions |
--- a/src/server/safs/cgi.c Sat Nov 12 20:50:45 2022 +0100 +++ b/src/server/safs/cgi.c Sun Nov 13 09:41:07 2022 +0100 @@ -143,6 +143,22 @@ handler->parser = cgi_parser_new(sn, rq); + // set pipes non-blocking + int flags; + if ((flags = fcntl(handler->process.err[0], F_GETFL, 0)) == -1) { + flags = 0; + } + if (fcntl(handler->process.err[0], F_SETFL, flags | O_NONBLOCK) != 0) { + log_ereport(LOG_FAILURE, "cgi-bin: fcntl err[0] failed: %s", strerror(errno)); + } + if ((flags = fcntl(handler->process.out[0], F_GETFL, 0)) == -1) { + flags = 0; + } + if (fcntl(handler->process.out[0], F_SETFL, flags | O_NONBLOCK) != 0) { + log_ereport(LOG_FAILURE, "cgi-bin: fcntl out[0] failed: %s", strerror(errno)); + } + + // create events for reading cgi's stdout/stderr Event *readev = pool_malloc(sn->pool, sizeof(Event)); ZERO(readev, sizeof(Event)); readev->cookie = handler; @@ -161,6 +177,7 @@ // TODO: fn handler->writeev = writeev; + handler->stderrev = stderr_readev; // add poll events for cgi stdout/stderr int error = 0; @@ -230,7 +247,6 @@ } } if(r < 0 && errno == EWOULDBLOCK) { - event->events = EVENT_POLLIN; return 1; } @@ -254,11 +270,65 @@ int cgi_stderr_readevent(EventHandler *ev, Event *event) { CGIHandler *handler = event->cookie; + pool_handle_t *pool = handler->parser->sn->pool; char buf[4096]; - ssize_t r = read(handler->process.err[0], buf, 4096); - log_ereport(LOG_INFORM, "cgi pid %d %s stderr: %.*s", (int)handler->process.pid, handler->path, (int)r, buf); + char *line = buf; + int line_start = 0; + ssize_t r; + while((r = read(handler->process.err[0], buf, 4096)) > 0) { + int pos = 0; + // log stderr output lines + for(int i=0;i<r;i++) { + if(buf[i] == '\n') { + log_ereport( + LOG_INFORM, + "cgi pid %d %s stderr: %.*s%.*s", + (int)handler->process.pid, + handler->path, + (int)handler->stderr_tmplen, + handler->stderr_tmp, + i - line_start, + line + line_start); + line_start = i+1; + pos = i+1; + + if(handler->stderr_tmp) { + pool_free(pool, handler->stderr_tmp); + handler->stderr_tmp = NULL; + handler->stderr_tmplen = 0; + } + } + } + + // check for incomplete line + if(pos < r) { + int tmplen = r-pos; + if(handler->stderr_tmp) { + handler->stderr_tmp = pool_realloc(pool, handler->stderr_tmp, handler->stderr_tmplen + tmplen); + memcpy(handler->stderr_tmp + handler->stderr_tmplen, line + line_start, tmplen); + handler->stderr_tmplen += tmplen; + } else { + handler->stderr_tmp = pool_malloc(pool, tmplen); + memcpy(handler->stderr_tmp, line + line_start, tmplen); + handler->stderr_tmplen = tmplen; + } + } else { + pool_free(pool, handler->stderr_tmp); + handler->stderr_tmp = NULL; + handler->stderr_tmplen = 0; + } + } + + if(r < 0 && errno == EWOULDBLOCK) { + return 1; + } + + if(handler->stderr_tmp) { + pool_free(handler->parser->sn->pool, handler->stderr_tmp); + } + handler->stderr_finished = TRUE; return 0; } @@ -267,12 +337,19 @@ CGIResponseParser *parser = handler->parser; Session *sn = parser->sn; Request *rq = parser->rq; - + if(handler->result == REQ_ABORTED) { log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", handler->path); kill(handler->process.pid, SIGKILL); } + if(!handler->stderr_finished) { + // stderr handler is still active + // set stderr event finish function, to run the finish code later + handler->stderrev->finish = cgi_event_finish; + return 0; + } + int exit_code = cgi_close(&handler->process); if(exit_code != 0) { log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code);
--- a/src/server/safs/cgi.h Sat Nov 12 20:50:45 2022 +0100 +++ b/src/server/safs/cgi.h Sun Nov 13 09:41:07 2022 +0100 @@ -58,7 +58,10 @@ CGIResponseParser *parser; char *path; Event *writeev; + Event *stderrev; char *stderr_tmp; + int stderr_tmplen; + WSBool stderr_finished; int result; } CGIHandler;