* 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);
}
{ 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);
}