{"thread":{"id":"5383","subject":"[PATCH/RFC] Add interpolated git-daemon virtualization support.","startedAt":"2006-08-25T15:26:53Z","lastAt":"2006-08-25T15:26:53Z","messageCount":1,"participants":["Jon Loeliger"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"25889","messageId":"E1GGda9-0003Qm-Ly@jdl.com","threadId":"5383","inReplyTo":null,"subject":"[PATCH/RFC] Add interpolated git-daemon virtualization support.","fromName":"Jon Loeliger","fromEmail":"jdl@jdl.com","sentAt":"2006-08-25T15:26:53Z","receivedAt":"2006-08-25T15:26:53Z","isPatch":true,"sender":{"key":"jdl@jdl.com","avatar":"https://gravatar.com/avatar/75ce9a10b151acd2c28ec4ab2136dba7b2ff1634530bd04b155981a749d08a64?d=mp&s=160"},"body":"\n---\n\nJunio,\n\nHere are my patches for the git daemon virtualization after\nI rewrote the interpolation code.  I think there are still\nissues with the path/directory specification mechanism, as\nI end up with crufty inetd invocation:\n\n    git  stream  tcp  nowait  nobody  /usr/bin/git-daemon git-daemon --inetd\n        --verbose --syslog\n        --export-all --interpolated-path=/pub/%H/%D\n\t/pub/software /software\n\t/pub/www.example.com/software\n\t/pub/www.example.org/software\n\t/pub\n\nA natural suggestion here is to supply %PeerIP from the connection.\n\nFeel free to extend, augment or make suggestions, of course.\n\nThanks,\njdl\n\n\n\n Makefile      |    4 ++\n daemon.c      |  101 ++++++++++++++++++++++++++++++++++++++++++++++++++-------\n interpolate.c |   83 +++++++++++++++++++++++++++++++++++++++++++++++\n interpolate.h |   13 +++++++\n 4 files changed, 188 insertions(+), 13 deletions(-)\n\n\ndiff --git a/Makefile b/Makefile\nindex b15b420..2691a58 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -242,7 +242,9 @@ DIFF_OBJS = \\\n \n LIB_OBJS = \\\n \tblob.o commit.o connect.o csum-file.o cache-tree.o base85.o \\\n-\tdate.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \\\n+\tdate.o diff-delta.o entry.o exec_cmd.o ident.o \\\n+\tinterpolate.o \\\n+\tlockfile.o \\\n \tobject.o pack-check.o patch-delta.o path.o pkt-line.o \\\n \tquote.o read-cache.o refs.o run-command.o dir.o object-refs.o \\\n \tserver-info.o setup.o sha1_file.o sha1_name.o strbuf.o \\\ndiff --git a/daemon.c b/daemon.c\nindex 012936f..67a3646 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -10,6 +10,7 @@ #include <syslog.h>\n #include \"pkt-line.h\"\n #include \"cache.h\"\n #include \"exec_cmd.h\"\n+#include \"interpolate.h\"\n \n static int log_syslog;\n static int verbose;\n@@ -18,7 +19,8 @@ static int reuseaddr;\n static const char daemon_usage[] =\n \"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\\n\"\n \"           [--timeout=n] [--init-timeout=n] [--strict-paths]\\n\"\n-\"           [--base-path=path] [--user-path | --user-path=path]\\n\"\n+\"           [--base-path=path] [--interpolated-path=path]\\n\"\n+\"           [--user-path | --user-path=path]\\n\"\n \"           [--reuseaddr] [--detach] [--pid-file=file] [directory...]\";\n \n /* List of acceptable pathname prefixes */\n@@ -30,6 +32,10 @@ static int export_all_trees;\n \n /* Take all paths relative to this one if non-NULL */\n static char *base_path;\n+static char *interpolated_path;\n+\n+/* Flag indicating client sent extra args. */\n+static int saw_extended_args;\n \n /* If defined, ~user notation is allowed and the string is inserted\n  * after ~user/.  E.g. a request to git://host/~alice/frotz would\n@@ -41,6 +47,23 @@ static const char *user_path;\n static unsigned int timeout;\n static unsigned int init_timeout;\n \n+/*\n+ * Static table for now.  Ugh.\n+ * Feel free to make dynamic as needed.\n+ */\n+#define INTERP_SLOT_HOST\t(0)\n+#define INTERP_SLOT_DIR\t\t(1)\n+#define INTERP_SLOT_PERCENT\t(2)\n+\n+struct interp interp_table[] = {\n+\t{ \"%H\", 0},\n+\t{ \"%D\", 0},\n+\t{ \"%%\", \"%\"},\n+};\n+\n+#define N_INTERPS\t(sizeof(interp_table) / sizeof(struct interp))\n+\n+\n static void logreport(int priority, const char *err, va_list params)\n {\n \t/* We should do a single write so that it is atomic and output\n@@ -148,10 +171,15 @@ static int avoid_alias(char *p)\n \t}\n }\n \n-static char *path_ok(char *dir)\n+static char *path_ok(struct interp *itable)\n {\n \tstatic char rpath[PATH_MAX];\n+\tstatic char interp_path[PATH_MAX];\n \tchar *path;\n+\tchar *dir;\n+\n+\tdir = itable[INTERP_SLOT_DIR].value;\n+\tloginfo(\"Request for '%s'\", dir);\n \n \tif (avoid_alias(dir)) {\n \t\tlogerror(\"'%s': aliased\", dir);\n@@ -180,16 +208,34 @@ static char *path_ok(char *dir)\n \t\t\tdir = rpath;\n \t\t}\n \t}\n+\telse if (interpolated_path && saw_extended_args) {\n+\t\tif (*dir != '/') {\n+\t\t\t/* Allow only absolute */\n+\t\t\tlogerror(\"'%s': Non-absolute path denied (interpolated-path active)\", dir);\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\tloginfo(\"Before interpolation '%s'\", dir);\n+\t\tloginfo(\"Interp slot 0 (%s,%s)\",\n+\t\t\tinterp_table[0].name, interp_table[0].value);\n+\t\tloginfo(\"Interp slot 1 (%s,%s)\",\n+\t\t\tinterp_table[1].name, interp_table[1].value);\n+\t\tinterpolate(interp_path, PATH_MAX, interpolated_path,\n+\t\t\t    interp_table, N_INTERPS);\n+\t\tloginfo(\"After interpolation '%s'\", interp_path);\n+\t\tdir = interp_path;\n+\t}\n \telse if (base_path) {\n \t\tif (*dir != '/') {\n \t\t\t/* Allow only absolute */\n \t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\", dir);\n \t\t\treturn NULL;\n \t\t}\n-\t\telse {\n-\t\t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n-\t\t\tdir = rpath;\n-\t\t}\n+\t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n+\t\tloginfo(\"dir was %s\", dir);\n+\t\tloginfo(\"base_path is %s\", base_path);\n+\t\tloginfo(\"rpath now %s\", rpath);\n+\t\tdir = rpath;\n \t}\n \n \tpath = enter_repo(dir, strict_paths);\n@@ -229,15 +275,13 @@ static char *path_ok(char *dir)\n \treturn NULL;\t\t/* Fallthrough. Deny by default */\n }\n \n-static int upload(char *dir)\n+static int upload(struct interp *itable)\n {\n \t/* Timeout as string */\n \tchar timeout_buf[64];\n \tconst char *path;\n \n-\tloginfo(\"Request for '%s'\", dir);\n-\n-\tif (!(path = path_ok(dir)))\n+\tif (!(path = path_ok(itable)))\n \t\treturn -1;\n \n \t/*\n@@ -270,6 +314,29 @@ static int upload(char *dir)\n \treturn -1;\n }\n \n+void parse_extra_args(char *extra_args, int buflen)\n+{\n+\tchar *val;\n+\tint vallen;\n+\tchar *end = extra_args + buflen;\n+\n+\twhile (extra_args < end && *extra_args) {\n+\t\tsaw_extended_args = 1;\n+\t\tloginfo(\"Extended arg %s\", extra_args);\n+\t\tif (strncasecmp(\"host=\", extra_args, 5) == 0) {\n+\t\t\tval = extra_args + 5;\n+\t\t\tvallen = strlen(val) + 1;\n+\t\t\tif (*val) {\n+\t\t\t\tchar *save = xmalloc(vallen);\n+\t\t\t\tinterp_table[INTERP_SLOT_HOST].value = save;\n+\t\t\t\tstrlcpy(save, val, vallen);\n+\t\t\t}\n+\t\t\t/* On to the next one */\n+\t\t\textra_args = val + vallen;\n+\t\t}\n+\t}\n+}\n+\n static int execute(struct sockaddr *addr)\n {\n \tstatic char line[1000];\n@@ -310,8 +377,14 @@ #endif\n \tif (len && line[len-1] == '\\n')\n \t\tline[--len] = 0;\n \n-\tif (!strncmp(\"git-upload-pack \", line, 16))\n-\t\treturn upload(line+16);\n+\tif (len != pktlen) {\n+\t    parse_extra_args(line + len + 1, pktlen - len - 1);\n+\t}\n+\n+\tif (!strncmp(\"git-upload-pack \", line, 16)) {\n+\t\tinterp_table[INTERP_SLOT_DIR].value = line+16;\n+\t\treturn upload(interp_table);\n+\t}\n \n \tlogerror(\"Protocol error: '%s'\", line);\n \treturn -1;\n@@ -770,6 +843,10 @@ int main(int argc, char **argv)\n \t\t\tbase_path = arg+12;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"--interpolated-path=\", 20)) {\n+\t\t\tinterpolated_path = arg+20;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--reuseaddr\")) {\n \t\t\treuseaddr = 1;\n \t\t\tcontinue;\ndiff --git a/interpolate.c b/interpolate.c\nnew file mode 100644\nindex 0000000..30fa7a1\n--- /dev/null\n+++ b/interpolate.c\n@@ -0,0 +1,83 @@\n+/*\n+ * Copyright 2006 Jon Loeliger\n+ */\n+\n+#include <string.h>\n+\n+#include \"interpolate.h\"\n+\n+\n+/*\n+ * Convert a NUL-terminated string in buffer orig\n+ * into the supplied buffer, result, whose length is reslen,\n+ * performing substitutions on %-named sub-strings from\n+ * the table, interps, with ninterps entries.\n+ *\n+ * Example interps:\n+ *    {\n+ *        { \"%H\", \"example.org\"},\n+ *        { \"%port\", \"123\"},\n+ *        { \"%%\", \"%\"},\n+ *    }\n+ *\n+ * Returns 1 on a successful substitution pass that fits in result,\n+ * Returns 0 on a failed or overflowing substitution pass.\n+ */\n+\n+int\n+interpolate(char *result, int reslen,\n+\t    char *orig,\n+\t    struct interp *interps, int ninterps)\n+{\n+\tchar *src = orig;\n+\tchar *dest = result;\n+\tint newlen = 0;\n+\tchar *name, *value;\n+\tint namelen, valuelen;\n+\tint i;\n+\tchar c;\n+\n+\tbzero(result, reslen);\n+\n+\twhile ((c = *src) && newlen < reslen - 1) {\n+\t\tif (c == '%') {\n+\t\t\t/* Try to match an interpolation string. */\n+\t\t\tfor (i = 0; i < ninterps; i++) {\n+\t\t\t\tname = interps[i].name;\n+\t\t\t\tnamelen = strlen(name);\n+\t\t\t\tif (strncmp(src, name, namelen) == 0) {\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t/* Check for valid interpolation. */\n+\t\t\tif (i < ninterps) {\n+\t\t\t\tvalue = interps[i].value;\n+\t\t\t\tvaluelen = strlen(value);\n+\n+\t\t\t\tif (newlen + valuelen < reslen - 1) {\n+\t\t\t\t\t/* Substitute. */\n+\t\t\t\t\tstrncpy(dest, value, valuelen);\n+\t\t\t\t\tnewlen += valuelen;\n+\t\t\t\t\tdest += valuelen;\n+\t\t\t\t\tsrc += namelen;\n+\t\t\t\t} else {\n+\t\t\t\t\t/* Something's not fitting. */\n+\t\t\t\t\treturn 0;\n+\t\t\t\t}\n+\n+\t\t\t} else {\n+\t\t\t\t/* Skip bogus interpolation. */\n+\t\t\t\t*dest++ = *src++;\n+\t\t\t\tnewlen++;\n+\t\t\t}\n+\n+\t\t} else {\n+\t\t\t/* Straight copy one non-interpolation character. */\n+\t\t\t*dest++ = *src++;\n+\t\t\tnewlen++;\n+\t\t}\n+\t}\n+\n+\treturn newlen < reslen - 1;\n+}\ndiff --git a/interpolate.h b/interpolate.h\nnew file mode 100644\nindex 0000000..241af7c\n--- /dev/null\n+++ b/interpolate.h\n@@ -0,0 +1,13 @@\n+/*\n+ * Copyright 2006 Jon Loeliger\n+ */\n+\n+struct interp {\n+\tchar *name;\n+\tchar *value;\n+};\n+\n+extern int interpolate(char *result, int reslen,\n+\t\t       char *orig,\n+\t\t       struct interp *interps, int ninterps);\n+\n"}]}