* #!/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
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
+#include <syslog.h>
#include <nettle/sha.h>
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);
}
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) {
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);
script = *argv++;
if (!script) badusage("need script argument");
- if (*argv) badusage("too many arguments");
if (!stage2) {
setup_handlers();
spawn_script();
queue_alarm();
+ start_logging();
await_something();
abort();
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);
}
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();
}
}