chiark / gitweb /
changelog: Document -t option
[chiark-utils.git] / cprogs / cgi-fcgi-interp.c
index 1612e4fe70f9dcb141a9742240cfff4525a1cd63..63db94b35fed7a1d3afb3f11c96858a31d3c70d7 100644 (file)
@@ -4,7 +4,7 @@
  *   #!/usr/bin/cgi-fcgi-interp [<options>],<interpreter>
  */
 /*
- * cgi-fcgi-interp.[ch] - C helpers common to the whole of chiark-utils
+ * cgi-fcgi-interp.[ch] - Convenience wrapper for cgi-fcgi
  *
  * Copyright 2016 Ian Jackson
  * Copyright 1982,1986,1993 The Regents of the University of California
@@ -441,12 +441,12 @@ static bool stab_isnewer(const struct stat *a, const struct stat *b) {
     fprintf(stderr,"stab_isnewer mtime %lu %lu\n",
            (unsigned long)a->st_mtime,
            (unsigned long)b->st_mtime);
-  return a->st_mtime > &b->st_mtime;
+  return a->st_mtime > b->st_mtime;
 }
 
 static void stab_mtimenow(struct stat *out) {
   out->st_mtime = time(NULL);
-  if (baseline_time.st_mtime == (time_t)-1) diee("(stage2) time()");
+  if (out->st_mtime == (time_t)-1) diee("(stage2) time()");
   if (debugmode)
     fprintf(stderr,"stab_mtimenow mtime %lu\n",
            (unsigned long)out->st_mtime);
@@ -604,7 +604,6 @@ int main(int argc, const char *const *argv) {
 
   script = *argv++;
   if (!script) badusage("need script argument");
-  if (*argv) badusage("too many arguments");
 
   if (!stage2) {
     
@@ -637,10 +636,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 +656,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 +743,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 +784,71 @@ 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;
+
+  /* %: does not contain newlines
+   * _: empty (garbage)
+   */ 
+
+  /*           %%%%%%%%%%%__________________ */
+  /*                      ^ 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) return;
+      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;
+      scanned = eaten;
+    }
+    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();
   }
 }