parse cgi stderr output for logging and use non-blocking pipes

Sun, 13 Nov 2022 09:41:07 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 13 Nov 2022 09:41:07 +0100
changeset 431
032b0ad35ee3
parent 430
83560f32e7d5
child 432
7c9137f9e7f9

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;
     

mercurial