/*
* "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>
* 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>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
+#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#define MINHEXHASH 33
-static const char *ident;
+static const char *interp, *ident;
static int numservers;
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); }
exit(0);
}
+#define MAX_OPTS 5
+
static const struct cmdinfo cmdinfos[]= {
{ "help", 0, .call= of_help },
{ 0, 'g', 1, .sassignto= &ident },
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;
}
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;
}
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);
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);
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");
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);
}