chiark / gitweb /
cgi-fcgi-perl: wip, computes socket path
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 22 Mar 2016 00:27:42 +0000 (00:27 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 22 Mar 2016 00:27:42 +0000 (00:27 +0000)
cprogs/Makefile
cprogs/cgi-fcgi-perl.c
cprogs/common.c
cprogs/common.h

index c07dbdc..7bb1e27 100644 (file)
@@ -58,7 +58,6 @@ writebuffer:                  writebuffer.o   wrbufcore.o     rwbuffer.o
 trivsoundd:                    trivsoundd.o    wrbufcore.o     rwbuffer.o 
 really:                                really.o myopt.o
 acctdump:                      acctdump.o      myopt.o
-cgi-fcgi-perl:                 cgi-fcgi-perl.o myopt.o
 
 acctdump.o really.o myopt.o rcopy-repeatedly.o: myopt.h
 readbuffer.o writebuffer.o rwbuffer.o wrbufcore.o trivsoundd.o:        rwbuffer.h
@@ -77,6 +76,9 @@ rcopy-repeatedly: LDLIBS += -lm -lrt
 watershed:     watershed.o common.o
 watershed:     LDLIBS += -lnettle -lgmp
 
+cgi-fcgi-perl: cgi-fcgi-perl.o myopt.o common.o
+cgi-fcgi-perl: LDLIBS += -lnettle -lgmp
+
 watershed.txt: watershed.c
                sed '/^$$/,$$d' <$^ >$@.new && mv -f $@.new $@
 
index 037bd86..617f8f0 100644 (file)
@@ -37,6 +37,9 @@
  * used.  The substring is chosen so that the whole path is 10 bytes
  * shorter than sizeof(sun_path).  But always at least 33 characters.
  *
+ * <node> is truncated at the first `.' and after the first 32
+ * characters.
+ *
  * Algorithm:
  *  - see if /var/run/user exists
  *       if so, lstat /var/run/user/<UID> and check that
  *  - run  cgi-fcgi -connect SOCKET SCRIPT
  */
 
+#include "common.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <stdbool.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <nettle/sha.h>
 
 #include "myopt.h"
 
+#define die  common_die
+#define diee common_diee
+
+#define MINHEXHASH 33
+
 static const char *ident;
 static int numservers;
 
-static void diee(const char *m) {
+void diee(const char *m) {
   fprintf(stderr,"cgi-fcgi-: error: %s failed: %s\n", m, strerror(errno));
   exit(127);
 }
@@ -89,7 +110,119 @@ static const struct cmdinfo cmdinfos[]= {
   { 0 }
 };
 
+static uid_t us;
+static const char *run_base, *command, *socket_path;
+
+static bool find_run_base_var_run(void) {
+  struct stat stab;
+  char *try;
+  int r;
+
+  try = m_asprintf("%s/%lu", "/var/run/user", us);
+  r = lstat(try, &stab);
+  if (r<0) {
+    if (errno == ENOENT ||
+       errno == ENOTDIR ||
+       errno == EACCES ||
+       errno == EPERM)
+      return 0; /* oh well */
+    diee("stat /var/run/user/UID");
+  }
+  if (!S_ISDIR(stab.st_mode)) {
+    fprintf(stderr,"%s not a directory, falling back to ~\n", try);
+    return 0;
+  }
+  if (stab.st_uid != us) {
+    fprintf(stderr,"%s not owned by uid %lu, falling back to ~\n",
+           try, (unsigned long)us);
+    return 0;
+  }
+  if (stab.st_mode & 0077) {
+    fprintf(stderr,"%s writeable by group or other, falling back to ~\n",try);
+    return 0;
+  }
+  run_base = m_asprintf("%s/%s", try, "cgi-fcgi-perl");
+  return 1;
+}
+
+static bool find_run_base_home(void) {
+  struct passwd *pw;
+  struct utsname ut;
+  char *dot, *try;
+  int r;
+
+  pw = getpwuid(us);  if (!pw) diee("getpwent(uid)");
+
+  r = uname(&ut);   if (r) diee("uname(2)");
+  dot = strchr(ut.nodename, '.');
+  if (dot) *dot = 0;
+  if (sizeof(ut.nodename) > 32)
+    ut.nodename[32] = 0;
+
+  try = m_asprintf("%s/%s/%s", pw->pw_dir, ".cgi-fcgi-perl", ut.nodename);
+  run_base = try;
+  return 1;
+}
+
+static void find_socket_path(void) {
+  struct sockaddr_un sun;
+
+  us = getuid();  if (us==(uid_t)-1) diee("getuid");
+
+  find_run_base_var_run() ||
+    find_run_base_home() ||
+    (abort(),0);
+
+  int maxidentlen = sizeof(sun.sun_path) - strlen(run_base) - 10 - 2;
+
+  if (!ident) {
+    if (maxidentlen < MINHEXHASH) {
+      fprintf(stderr,"cgi-fcgi-perl: base directory `%s'"
+             " leaves only %d characters for command name hash"
+             " which is too little (<%d)\n",
+             run_base, maxidentlen, MINHEXHASH);
+      exit(127);
+    }
+
+    int identlen = maxidentlen > 64 ? 64 : maxidentlen;
+    char *hexident = xmalloc(identlen + 2);
+    struct sha256_ctx sc;
+    unsigned char bbuf[32];
+    int i;
+
+    sha256_init(&sc);
+    sha256_update(&sc,strlen(command)+1,command);
+    sha256_digest(&sc,sizeof(bbuf),bbuf);
+
+    for (i=0; i<identlen; i += 2)
+      sprintf(hexident+i, "%02x", bbuf[i/2]);
+
+    hexident[identlen] = 0;
+    ident = hexident;
+  }
+
+  if (strlen(ident) > maxidentlen) {
+    fprintf(stderr,"cgi-fgci-perl: base directory `%s'"
+           " plus ident `%s' too long (with spare) for socket\n",
+           run_base, ident);
+    exit(127);
+  }
+
+  socket_path = m_asprintf("%s/g%s",run_base,ident);
+}  
+
 int main(int argc, const char *const *argv) {
   myopt(&argv, cmdinfos);
+
+  command = *argv++;
+  if (!command || *argv) {
+    fprintf(stderr,"wrong number of arguments\n");
+    exit(127);
+  }
+
+  find_socket_path();
+
+  printf(">%s<\n",socket_path);
+
   exit(0);
 }
index 26dc476..b054292 100644 (file)
@@ -35,3 +35,9 @@ char *m_asprintf(const char *fmt, ...) {
   va_start(al,fmt); s= m_vasprintf(fmt,al); va_end(al);
   return s;
 }
+
+void *xmalloc(size_t sz) {
+  void *r= malloc(sz);
+  if (!r) common_diee("malloc");
+  return r;
+}
index ca0609b..a7a2cdd 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 char *m_vasprintf(const char *fmt, va_list al);
 char *m_asprintf(const char *fmt, ...);
@@ -38,4 +39,6 @@ char *m_asprintf(const char *fmt, ...);
 void common_die(const char *what);
 void common_diee(const char *what); /* prints errno */
 
+void *xmalloc(size_t sz);
+
 #endif /*COMMON_H*/