X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-utils.git;a=blobdiff_plain;f=cprogs%2Fcgi-fcgi-interp.c;h=7a5a5db6a3a6c3eb17d37749b1c2b65de63a06a0;hp=89b3f5deceef6d67a30ad599092eeb168539724d;hb=f5a069b4c63fd17039e9ffbec54d9e5eca73ce99;hpb=b0fae5171b2acd23e10bdc61f12004ab7105cda7 diff --git a/cprogs/cgi-fcgi-interp.c b/cprogs/cgi-fcgi-interp.c index 89b3f5d..7a5a5db 100644 --- a/cprogs/cgi-fcgi-interp.c +++ b/cprogs/cgi-fcgi-interp.c @@ -145,6 +145,7 @@ #include #include #include +#include #include @@ -159,13 +160,31 @@ static int check_interval=300; static struct sha256_ctx identsc; -const char *stage2; +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,112 @@ 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; + + /* %: 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(); } }