chiark / gitweb /
cgi-fcgi-perl: wip new stderr logging, does not work properly right now
[chiark-utils.git] / cprogs / cgi-fcgi-interp.c
index 5c32efe7e68871332a449376c1e8744074e15d99..2c157d218033a372c6e53c144cb091e29952ed18 100644 (file)
 #include <time.h>
 #include <signal.h>
 #include <sys/wait.h>
+#include <syslog.h>
        
 #include <nettle/sha.h>
 
@@ -159,13 +160,31 @@ static int check_interval=300;
 
 static struct sha256_ctx identsc;
 
+static bool logging;
 static const char *stage2;
 
 static void vmsgcore(int estatus, int errnoval, const char *fmt, va_list al) {
-  fputs("cgi-fcgi-interp: ",stderr);
-  vfprintf(stderr,fmt,al);
-  if (errnoval!=-1) fprintf(stderr,": %s",strerror(errnoval));
-  fputc('\n',stderr);
+  int r;
+
+  if (logging) {
+    const char *fmt_use = fmt;
+    char *fmt_free = 0;
+    if (errnoval) {
+      r = asprintf(&fmt_free, "%s: %%m", fmt);
+      if (r) {
+       fmt_free = 0;
+      } else {
+       fmt_use = fmt_free;
+      }
+    }
+    vsyslog(LOG_ERR, fmt_use, al);
+    free(fmt_free);
+  } else {
+    fputs("cgi-fcgi-interp: ",stderr);
+    vfprintf(stderr,fmt,al);
+    if (errnoval!=-1) fprintf(stderr,": %s",strerror(errnoval));
+    fputc('\n',stderr);
+  }
   if (estatus) exit(estatus);
 }
 
@@ -525,6 +544,7 @@ static void become_pgrp(void);
 static void setup_handlers(void);
 static void spawn_script(void);
 static void queue_alarm(void);
+static void start_logging(void);
 static void await_something(void);
 
 int main(int argc, const char *const *argv) {
@@ -543,6 +563,9 @@ int main(int argc, const char *const *argv) {
     if (r<0) diee("open /dev/null as stdout");
     if (r>=3) close(r);
     else if (r!=1) die("open /dev/null for stdout gave bad fd %d",r);
+
+    r = close(stderrfd);
+    if (r) diee("close saved stderr fd");
   }
 
   sha256_init(&identsc);
@@ -617,6 +640,7 @@ int main(int argc, const char *const *argv) {
     setup_handlers();
     spawn_script();
     queue_alarm();
+    start_logging();
     await_something();
     abort();
 
@@ -633,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);
@@ -719,27 +744,104 @@ 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) {
   alarm(check_interval);
 }
 
+static void start_logging(void) {
+  int r;
+
+  openlog(script, LOG_NOWAIT|LOG_PID, LOG_USER);
+  logging = 1;
+  r = dup2(1,2);
+  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();
   }
 }