chiark / gitweb /
Bump version to 7.0.1~iwj0
[chiark-utils.git] / cprogs / cgi-fcgi-interp.c
index 86df07d882f37cd6355d13d47a89416bca01e227..6ea886e0f101ea1dcaf4ce5d826d284e0919f40c 100644 (file)
@@ -1,6 +1,11 @@
 /*
  * "Interpreter" that you can put in #! like this
  *   #!/usr/bin/cgi-fcgi-interp [<options>] <interpreter>
+ *
+ * Usages:
+ *   cgi-fcgi-interp  [<option> ..] <interpreter>  <script> [<ignored> ...]
+ *   cgi-fcgi-interp  [<option>,..],<interpreter>  <script> [<ignored> ...]
+ *   cgi-fcgi-interp '[<option> ..] <interpreter>' <script> [<ignored> ...]
  */
 /*
  * cgi-fcgi-interp.[ch] - Convenience wrapper for cgi-fcgi
  */
 
 #include "prefork.h"
+#include "timespeccmp.h"
 
 #define STAGE2_VAR "CHIARKUTILS_CGIFCGIINTERP_STAGE2"
 
@@ -130,11 +136,11 @@ static const char *stage2;
 
 const char our_name[] = "cgi-fcgi-interp";
 
+static int numservers=4, debugmode;
+static int check_interval=300;
+
 const struct cmdinfo cmdinfos[]= {
-  { "help",   0, .call=of_help                                         },
-  { 0, 'g',   1,                    .sassignto= &ident                 },
-  { 0, 'G',   1, .call= ident_addstring                                },
-  { 0, 'E',   1, .call= off_ident_addenv                               },
+  PREFORK_CMDINFOS
   { 0, 'M',   1, .call=of_iassign,  .iassignto= &numservers            },
   { 0, 'D',   0,                    .iassignto= &debugmode,    .arg= 1 },
   { 0, 'c',   1, .call=of_iassign,  .iassignto= &check_interval        },
@@ -145,6 +151,9 @@ void fusagemessage(FILE *f) {
   fprintf(f, "usage: #!/usr/bin/cgi-fcgi-interp [<options>]\n");
 }
 
+void ident_addinit(void) {
+}
+
 static int stderr_copy;
 
 static void make_stderr_copy(void) {
@@ -160,6 +169,104 @@ static void prep_stage2(void) {
   if (r) diee("set %s (to announce to stage2)", STAGE2_VAR);
 }
 
+#ifdef st_mtime
+
+static bool stab_isnewer(const struct stat *a, const struct stat *b) {
+  if (debugmode)
+    fprintf(stderr,"stab_isnewer mtim %lu.%06lu %lu.06%lu\n",
+           (unsigned long)a->st_mtim.tv_sec,
+           (unsigned long)a->st_mtim.tv_nsec,
+           (unsigned long)b->st_mtim.tv_sec,
+           (unsigned long)b->st_mtim.tv_nsec);
+  return timespeccmp(&a->st_mtim, &b->st_mtim, >);
+}
+
+static void stab_mtimenow(struct stat *out) {
+  int r = clock_gettime(CLOCK_REALTIME, &out->st_mtim);
+  if (r) diee("(stage2) clock_gettime");
+  if (debugmode)
+    fprintf(stderr,"stab_mtimenow mtim %lu.%06lu\n",
+           (unsigned long)out->st_mtim.tv_sec,
+           (unsigned long)out->st_mtim.tv_nsec);
+}
+
+#else /* !defined(st_mtime) */
+
+static bool stab_isnewer(const struct stat *a, const struct stat *b) {
+  if (debugmode)
+    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;
+}
+
+static void stab_mtimenow(struct stat *out) {
+  out->st_mtime = time(NULL);
+  if (out->st_mtime == (time_t)-1) diee("(stage2) time()");
+  if (debugmode)
+    fprintf(stderr,"stab_mtimenow mtime %lu\n",
+           (unsigned long)out->st_mtime);
+}
+
+#endif /* !defined(st_mtime) */
+
+static bool check_garbage_vs(const struct stat *started) {
+  struct stat script_stab;
+  int r;
+
+  r = lstat(script, &script_stab);
+  if (r) diee("lstat script (%s)",script);
+
+  if (stab_isnewer(&script_stab, started))
+    return 1;
+
+  if (S_ISLNK(script_stab.st_mode)) {
+    r = stat(script, &script_stab);
+    if (r) diee("stat script (%s0",script);
+
+    if (stab_isnewer(&script_stab, started))
+      return 1;
+  }
+
+  return 0;
+}
+
+static bool check_garbage(void) {
+  struct stat sock_stab;
+  int r;
+
+  r = lstat(socket_path, &sock_stab);
+  if (r) {
+    if ((errno == ENOENT))
+      return 0; /* well, no garbage then */
+    diee("stat socket (%s)",socket_path);
+  }
+
+  return check_garbage_vs(&sock_stab);
+}
+
+static void tidy_garbage(void) {
+  /* We lock l<ident> and re-check.  The effect of this is that each
+   * stale socket is removed only once.  So unless multiple updates to
+   * the script happen rapidly, we can't be racing with the cgi-fcgi
+   * (which is recreating the socket */
+  int lockfd = -1;
+  int r;
+
+  lockfd = acquire_lock();
+
+  if (check_garbage()) {
+    r = unlink(socket_path);
+    if (r) {
+      if (!(errno == ENOENT))
+       diee("remove out-of-date socket (%s)", socket_path);
+    }
+  }
+
+  r = close(lockfd);
+  if (r) diee("close lock (%s)", lock_path);
+}
+
 /* stage2 predeclarations */
 static void record_baseline_time(void);
 static void become_pgrp(void);
@@ -169,7 +276,7 @@ static void queue_alarm(void);
 static void start_logging(void);
 static void await_something(void);
 
-int main(int argc, const char *const *argv) {
+int main(int unused_argc, const char *const *argv) {
   int r;
 
   stage2 = getenv(STAGE2_VAR);
@@ -189,7 +296,8 @@ int main(int argc, const char *const *argv) {
     if (r) diee("close saved stderr fd");
   }
 
-  script = process_opts(argc, argv);
+  process_opts(&argv);
+  if (!script) badusage("need script argument");
 
   if (!stage2) {