chiark / gitweb /
cgi-fcgi-interp: Rename from cgi-fcgi-perl, and adjust arg handling
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 22 Mar 2016 14:06:44 +0000 (14:06 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 22 Mar 2016 14:06:44 +0000 (14:06 +0000)
.gitignore
cprogs/Makefile
cprogs/cgi-fcgi-interp.c [moved from cprogs/cgi-fcgi-perl.c with 67% similarity]

index b08c589..73dbc62 100644 (file)
@@ -16,7 +16,7 @@ cprogs/watershed
 cprogs/watershed.txt
 cprogs/rcopy-repeatedly
 cprogs/acctdump
-cprogs/cgi-fcgi-perl
+cprogs/cgi-fcgi-interp
 
 debian/tmp
 debian/sv-*
index 7bb1e27..496b33e 100644 (file)
@@ -29,7 +29,7 @@ RWBUFFER_SIZE_MB=16
 
 PROGRAMS=              readbuffer writebuffer with-lock-ex xbatmon-simple \
                        summer watershed rcopy-repeatedly xduplic-copier \
-                       cgi-fcgi-perl
+                       cgi-fcgi-interp
 SUIDSBINPROGRAMS=      really
 DAEMONS=               trivsoundd
 MAN1PAGES=             readbuffer.1 writebuffer.1 with-lock-ex.1 \
@@ -76,8 +76,8 @@ 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
+cgi-fcgi-interp:       cgi-fcgi-interp.o       myopt.o common.o
+cgi-fcgi-interp:       LDLIBS += -lnettle -lgmp
 
 watershed.txt: watershed.c
                sed '/^$$/,$$d' <$^ >$@.new && mv -f $@.new $@
similarity index 67%
rename from cprogs/cgi-fcgi-perl.c
rename to cprogs/cgi-fcgi-interp.c
index 83bd962..1e368f0 100644 (file)
@@ -1,18 +1,24 @@
 /*
  * "Interpreter" that you can put in #! like this
- *   #!/usr/bin/cgi-fcgi-perl [<options>]
+ *   #!/usr/bin/cgi-fcgi-interp [<options>] <interpreter>
+ *   #!/usr/bin/cgi-fcgi-interp [<options>],<interpreter>
  *
  * The result is a program which looks, when executed via the #!
- * line, like a CGI program.  But the perl script inside will
- * be executed via /usr/bin/perl in fcgi.
+ * line, like a CGI program.  But the script inside will be executed
+ * via <interpreter> in an fcgi context.
  *
  * Options:
  *
+ *  <interpreter>
+ *          The real interpreter to use.  Eg "perl".  Need not
+ *          be an absolute path; will be fed to execvp.
+ *
  *  -g<ident>
  *          Use <ident> rather than hex(sha256(<script>))
  *          as the basename of the leafname of the fcgi rendezvous
  *          socket.  If <ident> contains only hex digit characters it
- *          ought to be no more than 32 characters.
+ *          ought to be no more than 32 characters.  <ident> should
+ *          not contain spaces or commas (see below).
  *
  *  -M<numservers>
  *         Start <numservers> instances of the program.  This
  *         speedy, the specified number of servers is started
  *         right away.)  The default is 4.
  *
- * cgi-fcgi-perl automatically expires old sockets, including
- * ones where the named perl script is out of date.
+ * <options> and <interpreter> can be put into a single argument
+ * to cgi-fcgi-interp, separated by spaces or commas.  <interpreter>
+ * must come last.
+ *
+ * cgi-fcgi-interp automatically expires old sockets, including
+ * ones where the named script is out of date.
  */
 
 /*
  * Uses one of two directories
- *   /var/run/user/<UID>/cgi-fcgi-perl/
- *   ~/.cgi-fcgi-perl/<node>/
+ *   /var/run/user/<UID>/cgi-fcgi-interp/
+ *   ~/.cgi-fcgi-interp/<node>/
  * and inside there uses these paths
  *   s<ident>
  *   g<inum>
@@ -45,9 +55,9 @@
  *       if so, lstat /var/run/user/<UID> and check that
  *         we own it and it's X700; if not, fail
  *         if it's ok then <base> is /var/run/user/<UID>
- *       otherwise, look for and maybe create ~/.cgi-fcgi-perl
+ *       otherwise, look for and maybe create ~/.cgi-fcgi-interp
  *         (where ~ is HOME or from getpwuid)
- *         and then <base> is ~/.cgi-fcgi-perl/<node>
+ *         and then <base> is ~/.cgi-fcgi-interp/<node>
  *  - calculate pathname (checking <ident> length is OK)
  *  - check for and maybe create <base>
  *  - stat and lstat the <script>
@@ -65,6 +75,7 @@
 #include <string.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <assert.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -84,7 +95,7 @@
 
 #define MINHEXHASH 33
 
-static const char *ident;
+static const char *interp, *ident;
 static int numservers;
 
 void diee(const char *m) {
@@ -92,7 +103,7 @@ void diee(const char *m) {
 }
 
 static void fusagemessage(FILE *f) {
-  fprintf(f, "usage: #!/usr/bin/cgi-fcgi-perl [<options>]\n");
+  fprintf(f, "usage: #!/usr/bin/cgi-fcgi-interp [<options>]\n");
 }
 
 void usagemessage(void) { fusagemessage(stderr); }
@@ -103,6 +114,8 @@ static void of_help(const struct cmdinfo *ci, const char *val) {
   exit(0);
 }
 
+#define MAX_OPTS 5
+
 static const struct cmdinfo cmdinfos[]= {
   { "help",   0, .call= of_help               },
   { 0, 'g',   1, .sassignto= &ident           },
@@ -141,7 +154,7 @@ static bool find_run_base_var_run(void) {
     warnx("%s writeable by group or other, falling back to ~\n", try);
     return 0;
   }
-  run_base = m_asprintf("%s/%s", try, "cgi-fcgi-perl");
+  run_base = m_asprintf("%s/%s", try, "cgi-fcgi-interp");
   return 1;
 }
 
@@ -159,7 +172,7 @@ static bool find_run_base_home(void) {
   if (sizeof(ut.nodename) > 32)
     ut.nodename[32] = 0;
 
-  try = m_asprintf("%s/%s/%s", pw->pw_dir, ".cgi-fcgi-perl", ut.nodename);
+  try = m_asprintf("%s/%s/%s", pw->pw_dir, ".cgi-fcgi-interp", ut.nodename);
   run_base = try;
   return 1;
 }
@@ -178,7 +191,7 @@ static void find_socket_path(void) {
 
   if (!ident) {
     if (maxidentlen < MINHEXHASH)
-      errx(127,"cgi-fcgi-perl: base directory `%s'"
+      errx(127,"base directory `%s'"
           " leaves only %d characters for command name hash"
           " which is too little (<%d)",
           run_base, maxidentlen, MINHEXHASH);
@@ -190,6 +203,7 @@ static void find_socket_path(void) {
     int i;
 
     sha256_init(&sc);
+    sha256_update(&sc,strlen(interp)+1,interp);
     sha256_update(&sc,strlen(command)+1,command);
     sha256_digest(&sc,sizeof(bbuf),bbuf);
 
@@ -231,8 +245,48 @@ static bool check_garbage(void) {
   return 0;
 }
 
+static void shbang_opts(const char *const **argv_io,
+                       const struct cmdinfo *cmdinfos) {
+  myopt(argv_io, cmdinfos);
+
+  interp = *(*argv_io)++;
+  if (!interp) errx(127,"need interpreter argument");
+}
+
 int main(int argc, const char *const *argv) {
-  myopt(&argv, cmdinfos);
+  const char *smashedopt;
+
+  if (argc>=2 &&
+      (smashedopt = argv[1]) &&
+      smashedopt[0]=='-' &&
+      (strchr(smashedopt,' ') || strchr(smashedopt,','))) {
+    /* single argument containg all the options and <interp> */
+    argv += 2; /* eat argv[0] and smashedopt */
+    const char *split_args[MAX_OPTS+1];
+    int split_argc = 0;
+    for (;;) {
+      if (split_argc >= MAX_OPTS) errx(127,"too many options in combined arg");
+      split_args[split_argc++] = smashedopt;
+      if (smashedopt[0] != '-') /* never true on first iteration */
+       break;
+      char *delim = strchr(smashedopt,' ');
+      if (!delim) delim = strchr(smashedopt,',');
+      if (!delim)
+       errx(127,"combined arg lacks <interpreter>");
+      *delim = 0;
+      smashedopt = delim+1;
+    }
+    assert(split_argc <= MAX_OPTS);
+    split_args[split_argc++] = 0;
+
+    const char *const *split_argv = split_args;
+
+    shbang_opts(&split_argv, cmdinfos);
+    /* sets interp */
+    if (!split_argv) errx(127,"combined arg too many non-option arguments");
+  } else {
+    shbang_opts(&argv, cmdinfos);
+  }
 
   command = *argv++;
   if (!command) errx(127,"need command argument");
@@ -242,7 +296,9 @@ int main(int argc, const char *const *argv) {
 
   check_garbage();
 
-  printf(">%s<\n",socket_path);
+  printf("socket: %s\n",socket_path);
+  printf("interp: %s\n",interp);
+  printf("command: %s\n",command);
 
   exit(0);
 }