chiark / gitweb /
cgi-fcgi-perl: wip new stderr logging, does not work properly right now
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 30 Mar 2016 01:25:51 +0000 (02:25 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 30 Mar 2016 01:25:51 +0000 (02:25 +0100)
cprogs/cgi-fcgi-interp.c

index 1612e4f..2c157d2 100644 (file)
@@ -637,10 +637,10 @@ int main(int argc, const char *const *argv) {
 
     record_baseline_time();
     become_pgrp();
-    start_logging();
     setup_handlers();
     spawn_script();
     queue_alarm();
+    start_logging();
     await_something();
     abort();
 
@@ -657,6 +657,7 @@ int main(int argc, const char *const *argv) {
 static struct stat baseline_time;
 static pid_t script_child, stage2_pgrp;
 static bool out_of_date;
+static int errpipe;
 
 static void record_baseline_time(void) {
   stab_mtimenow(&baseline_time);
@@ -743,13 +744,32 @@ static void setup_handlers(void) {
 }
 
 static void spawn_script(void) {
+  int r;
+  int errpipes[2];
+
+  r = pipe(errpipes);
+  if (r) diee("(stage2) pipe");
+
   script_child = fork();
   if (script_child == (pid_t)-1) diee("(stage2) fork");
   if (!script_child) {
+    r = close(errpipes[0]);
+    if (r) diee("(stage2 child) close errpipes[0]");
+
+    r = dup2(errpipes[1], 2);
+    if (r != 2) diee("(stage2 child) dup2 stderr");
+
     execlp(interp,
           interp, script, (char*)0);
     diee("(stage2) exec interpreter (`%s', for `%s')\n",interp,script);
   }
+
+  r = close(errpipes[1]);
+  if (r) diee("(stage2) close errpipes[1]");
+
+  errpipe = errpipes[0];
+  r = fcntl(errpipe, F_SETFL, O_NONBLOCK);
+  if (r) diee("(stage2) set errpipe nonblocking");
 }
 
 static void queue_alarm(void) {
@@ -765,14 +785,63 @@ static void start_logging(void) {
   if (r!=2) diee("dup2 stdout to stderr");
 }
 
+static void errpipe_readable(void) {
+  static char buf[1024];
+  static int pending;
+
+  for (;;) {
+    int avail = sizeof(buf) - pending;
+    ssize_t got = read(errpipe, buf+pending, avail);
+    if (got==-1) {
+      if (errno==EINTR) continue;
+      else if (errno==EWOULDBLOCK || errno==EAGAIN) /*ok*/;
+      else diee("(stage2) errpipe read");
+      got = 0;
+    } else if (got==0) {
+      warning("program closed its stderr fd");
+      errpipe = -1;
+      return;
+    }
+    int scanned = pending;
+    pending += got;
+    int eaten = 0;
+    for (;;) {
+      const char *newline = memchr(buf+scanned, '\n', pending-scanned);
+      int printupto, eat;
+      if (newline) {
+       printupto = newline-buf;
+       eat = printupto + 1;
+      } else if (!eaten && pending==sizeof(buf)) { /* overflow */
+       printupto = pending;
+       eat = printupto;
+      } else {
+       break;
+      }
+      syslog(LOG_ERR,"stderr: %.*s", printupto-eaten, buf+eaten);
+      eaten += eat;
+    }
+    pending -= eaten;
+    memmove(buf, buf+eaten, pending);
+  }
+}     
+
 static void await_something(void) {
   int r;
   sigset_t mask;
   sigemptyset(&mask);
 
   for (;;) {
-    r = sigsuspend(&mask);
-    assert(r==-1);
-    if (errno != EINTR) diee("(stage2) sigsuspend");
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    if (errpipe >= 0)
+      FD_SET(errpipe, &rfds);
+    r = pselect(errpipe+1, &rfds,0,0, 0, &mask);
+    if (r==-1) {
+      if (errno != EINTR) diee("(stage2) sigsuspend");
+      continue;
+    }
+    assert(r>0);
+    assert(FD_ISSET(errpipe, &rfds));
+    errpipe_readable();
   }
 }