chiark / gitweb /
prefork-interp: add copyright licences
[chiark-utils.git] / cprogs / prefork.c
index e352238973e6736c3f24560728d2047ff84748f7..b8f4c8a2b1834d8eeca8405eb56da798f4d39524 100644 (file)
@@ -1,17 +1,19 @@
 /* common stuff for cgi-fcgi-interp and prefork-interp */
+/*
+ * Copyright 2016-2022 Ian Jackson and contributors to chiark-utils
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * There is NO WARRANTY.
+ */
 
 #include "prefork.h"
 
-const char *interp, *ident;
-int numservers=4, debugmode;
-int check_interval=300;
-
+const char *interp, *ident, *script, *socket_path, *lock_path;
+bool logging;
 struct sha256_ctx identsc;
+const char *run_base;
 
-uid_t us;
-const char *run_base, *script, *socket_path, *lock_path;
-const char *run_base_mkdir_p;
-bool logging;
+static uid_t us;
+static const char *run_base_mkdir_p;
 
 void common_diee(const char *m) { diee("%s", m); }
 void common_die (const char *m) { die ("%s", m); }
@@ -22,7 +24,7 @@ void vmsgcore(int estatus, int errnoval, const char *fmt, va_list al) {
   if (logging) {
     const char *fmt_use = fmt;
     char *fmt_free = 0;
-    if (errnoval) {
+    if (errnoval!=-1) {
       r = asprintf(&fmt_free, "%s: %%m", fmt);
       if (r) {
        fmt_free = 0;
@@ -58,22 +60,34 @@ void of_iassign(const struct cmdinfo *ci, const char *val) {
   *ci->iassignto = v;
 }
 
-void ident_addstring(const struct cmdinfo *ci, const char *string) {
-  /* ci may be 0 and is provided so this can be .call */
+void ident_add_key_byte(char key) {
+  sha256_update(&identsc,1,&key);
+}
+
+void ident_addstring(char key, const char *string) {
+  ident_add_key_byte(key);
   sha256_update(&identsc,strlen(string)+1,string);
 }
 
+void off_ident_addstring(const struct cmdinfo *ci, const char *string) {
+  ident_addstring('G', string);
+}
+
 void off_ident_addenv(const struct cmdinfo *ci, const char *name) {
+  ident_addstring('E', name);
   const char *val = getenv(name);
   if (val) {
-    sha256_update(&identsc,strlen(name),name); /* no nul */
-    sha256_update(&identsc,1,"=");
-    ident_addstring(0,val);
+    ident_addstring('v', val);
   } else {
-    ident_addstring(0,name);
+    ident_add_key_byte(0);
   }
 }
 
+bool stabs_same_inode(struct stat *a, struct stat *b) {
+  return (a->st_dev == b->st_dev &&
+         a->st_ino == b->st_ino);
+}
+
 bool find_run_base_var_run(void) {
   struct stat stab;
   char *try;
@@ -150,8 +164,9 @@ void find_socket_path(void) {
     unsigned char bbuf[32];
     int i;
 
-    ident_addstring(0,interp);
-    ident_addstring(0,script);
+    ident_addstring('i', interp);
+    if (script)
+      ident_addstring('s', script);
     sha256_digest(&identsc,sizeof(bbuf),bbuf);
 
     for (i=0; i<identlen; i += 2)
@@ -180,118 +195,41 @@ void find_socket_path(void) {
   socket_path = m_asprintf("%s/s%s",run_base,ident);
 }  
 
-#ifdef st_mtime
-
-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, >);
-}
-
-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) */
-
-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;
-}
-
-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) */
-
-bool check_garbage_vs(const struct stat *started) {
-  struct stat script_stab;
+// Returns fd
+int flock_file(const char *lock_path) {
   int r;
+  int lockfd = -1;
+  struct stat stab_fd;
+  struct stat stab_path;
 
-  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);
+  for (;;) {
+    if (lockfd >= 0) { close(lockfd); lockfd = -1; }
 
-    if (stab_isnewer(&script_stab, started))
-      return 1;
-  }
+    lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
+    if (lockfd<0) diee("create lock (%s)", lock_path);
 
-  return 0;
-}
+    r = flock(lockfd, LOCK_EX);
+    if (r && errno == EINTR) continue;
+    if (r) diee("lock lock (%s)", lock_path);
 
-bool check_garbage(void) {
-  struct stat sock_stab;
-  int r;
+    r = fstat(lockfd, &stab_fd);
+    if (r) diee("fstat locked lock");
 
-  r = lstat(socket_path, &sock_stab);
-  if (r) {
-    if ((errno == ENOENT))
-      return 0; /* well, no garbage then */
-    diee("stat socket (%s)",socket_path);
+    r = stat(lock_path, &stab_path);
+    if (!r) {
+      if (stabs_same_inode(&stab_path, &stab_fd)) break;
+    } else {
+      if (!(errno == ENOENT)) diee("re-stat locked lock (%s)", lock_path);
+    }
   }
 
-  return check_garbage_vs(&sock_stab);
+  return lockfd;
 }
 
 // Returns fd
 int acquire_lock(void) {
-  int r;
-  int lockfd = -1;
-
   lock_path = m_asprintf("%s/l%s",run_base,ident);
-
-  lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
-  if (lockfd<0) diee("create lock (%s)", lock_path);
-
-  r = flock(lockfd, LOCK_EX);
-  if (r) diee("lock lock (%s)", lock_path);
-
-  return lockfd;
-}
-
-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);
+  return flock_file(lock_path);
 }
 
 static void shbang_opts(const char *const **argv_io,
@@ -302,20 +240,21 @@ static void shbang_opts(const char *const **argv_io,
   if (!interp) badusage("need interpreter argument");
 }
 
-const char *process_opts(int argc, const char *const *argv) {
+void process_opts(const char *const **argv_io) {
   const char *smashedopt;
 
   sha256_init(&identsc);
+  ident_addinit();
 
-  if (argc>=2 &&
-      (smashedopt = argv[1]) &&
+  if ((*argv_io)[0] &&
+      (smashedopt = (*argv_io)[1]) &&
       smashedopt[0]=='-' &&
       (strchr(smashedopt,' ') || strchr(smashedopt,','))) {
     /* single argument containg all the options and <interp> */
-    argv += 2; /* eat argv[0] and smashedopt */
+    *argv_io += 2; /* eat argv[0] and smashedopt */
     const char *split_args[MAX_OPTS+1];
     int split_argc = 0;
-    split_args[split_argc++] = argv[0];
+    split_args[split_argc++] = (*argv_io)[0];
     for (;;) {
       if (split_argc >= MAX_OPTS) die("too many options in combined arg");
       split_args[split_argc++] = smashedopt;
@@ -334,13 +273,13 @@ const char *process_opts(int argc, const char *const *argv) {
 
     shbang_opts(&split_argv, cmdinfos);
     /* sets interp */
-    if (!split_argv) badusage("combined arg too many non-option arguments");
+
+    if (!**argv_io)
+      badusage("no script argument (expected after combined #! options)");
   } else {
-    shbang_opts(&argv, cmdinfos);
+    shbang_opts(argv_io, cmdinfos);
   }
 
-  script = *argv++;
-  if (!script) badusage("need script argument");
-
-  return script;
+  if (**argv_io)
+    script = *(*argv_io)++;
 }