{"thread":{"id":"5582","subject":"[PATCH] Add virtualization support to git-daemon","startedAt":"2006-09-16T22:38:29Z","lastAt":"2006-09-17T16:23:30Z","messageCount":3,"participants":["Jon Loeliger","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"27025","messageId":"E1GOint-00048W-2A@jdl.com","threadId":"5582","inReplyTo":null,"subject":"[PATCH] Add virtualization support to git-daemon","fromName":"Jon Loeliger","fromEmail":"jdl@jdl.com","sentAt":"2006-09-16T22:38:29Z","receivedAt":"2006-09-16T22:38:29Z","isPatch":true,"sender":{"key":"jdl@jdl.com","avatar":"https://gravatar.com/avatar/75ce9a10b151acd2c28ec4ab2136dba7b2ff1634530bd04b155981a749d08a64?d=mp&s=160"},"body":"\nSigned-off-by: Jon Loeliger\n\n---\n\nJunio,\n\nHere is a rebase of my patches to support virtual hosts\nin git-daemon.  This rebase accounts for the changes in\nhandling the table of daemon services recently introduced.\n\nI use an inetd invocation like this example:\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\nStill room for improvements, of course.\n\nThanks,\njdl\n\n\n Makefile      |    4 ++\n daemon.c      |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++------\n interpolate.c |   83 +++++++++++++++++++++++++++++++++++++++++++++++\n interpolate.h |   13 +++++++\n 4 files changed, 189 insertions(+), 11 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 7b3114f..673ba2d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -244,7 +244,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 b14d808..7822545 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -12,6 +12,7 @@ #include <grp.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@@ -21,6 +22,7 @@ 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+\"           [--interpolated-path=path]\\n\"\n \"           [--reuseaddr] [--detach] [--pid-file=file]\\n\"\n \"           [--[enable|disable|allow-override|forbid-override]=service]\\n\"\n \"           [--user=user [[--group=group]] [directory...]\";\n@@ -34,6 +36,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@@ -45,6 +51,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@@ -152,10 +175,14 @@ 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 \n \tif (avoid_alias(dir)) {\n \t\tlogerror(\"'%s': aliased\", dir);\n@@ -184,16 +211,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@@ -257,12 +302,14 @@ static int git_daemon_config(const char \n \treturn 0;\n }\n \n-static int run_service(char *dir, struct daemon_service *service)\n+static int run_service(struct interp *itable, struct daemon_service *service)\n {\n \tconst char *path;\n \tint enabled = service->enabled;\n \n-\tloginfo(\"Request %s for '%s'\", service->name, dir);\n+\tloginfo(\"Request %s for '%s'\",\n+\t\tservice->name,\n+\t\titable[INTERP_SLOT_DIR].value);\n \n \tif (!enabled && !service->overridable) {\n \t\tlogerror(\"'%s': service not enabled.\", service->name);\n@@ -270,7 +317,7 @@ static int run_service(char *dir, struct\n \t\treturn -1;\n \t}\n \n-\tif (!(path = path_ok(dir)))\n+\tif (!(path = path_ok(itable)))\n \t\treturn -1;\n \n \t/*\n@@ -351,6 +398,29 @@ static void make_service_overridable(con\n \tdie(\"No such service %s\", name);\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@@ -391,13 +461,19 @@ #endif\n \tif (len && line[len-1] == '\\n')\n \t\tline[--len] = 0;\n \n+\tif (len != pktlen) {\n+\t    parse_extra_args(line + len + 1, pktlen - len - 1);\n+\t}\n+\n \tfor (i = 0; i < ARRAY_SIZE(daemon_service); i++) {\n \t\tstruct daemon_service *s = &(daemon_service[i]);\n \t\tint namelen = strlen(s->name);\n \t\tif (!strncmp(\"git-\", line, 4) &&\n \t\t    !strncmp(s->name, line + 4, namelen) &&\n-\t\t    line[namelen + 4] == ' ')\n-\t\t\treturn run_service(line + namelen + 5, s);\n+\t\t    line[namelen + 4] == ' ') {\n+\t\t\tinterp_table[INTERP_SLOT_DIR].value = line+namelen+5;\n+\t\t\treturn run_service(interp_table, s);\n+\t\t}\n \t}\n \n \tlogerror(\"Protocol error: '%s'\", line);\n@@ -860,6 +936,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"},{"id":"27064","messageId":"7vvenm4xi6.fsf@assigned-by-dhcp.cox.net","threadId":"5582","inReplyTo":"E1GOint-00048W-2A@jdl.com","subject":"Re: [PATCH] Add virtualization support to git-daemon","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-17T10:07:29Z","receivedAt":"2006-09-17T10:07:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I have a few stylistic comments but the contents look quite sane\nand fine.\n\n> I use an inetd invocation like this example:\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\nThis would be nice to have as an example in the documentation.\n\n> @@ -45,6 +51,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\nShouldn't this variable static to the file?\n\nWe seem to use ARRAY_SIZE() macro, which is not particularly my\nfavorite but other places already use it so probably we would\nwant to keep consistency.\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\nDo we really want to see each step of interpolation in the log?\nSounds more like logdebug than loginfo to me.\n\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\nLikewise.\n\n> @@ -351,6 +398,29 @@ static void make_service_overridable(con\n>  \tdie(\"No such service %s\", name);\n>  }\n>  \n> +void parse_extra_args(char *extra_args, int buflen)\n\nstatic?\n\n> @@ -391,13 +461,19 @@ #endif\n>  \tif (len && line[len-1] == '\\n')\n>  \t\tline[--len] = 0;\n>  \n> +\tif (len != pktlen) {\n> +\t    parse_extra_args(line + len + 1, pktlen - len - 1);\n> +\t}\n> +\n\nNo { } around single statement.\n\n> +int\n> +interpolate(char *result, int reslen,\n> +\t    char *orig,\n> +\t    struct interp *interps, int ninterps)\n> +{\n\n\"int interpolate(...\"; the return type and function name on the\nsame line (again, not my preference but just doing as Romans do).\n\n> +\tbzero(result, reslen);\n\n\tmemset(result, 0, reslen);\n\n> diff --git a/interpolate.h b/interpolate.h\n> new file mode 100644\n> index 0000000..241af7c\n> --- /dev/null\n> +++ b/interpolate.h\n> @@ -0,0 +1,13 @@\n\n        #ifndef INTERPOLATE_H\n        #define INTERPOLATE_H\n        ...\n        #endif\n\n> +/*\n> + * Copyright 2006 Jon Loeliger\n> + */\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"},{"id":"27081","messageId":"E1GOzQY-0001G7-Ju@jdl.com","threadId":"5582","inReplyTo":"7vvenm4xi6.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add virtualization support to git-daemon","fromName":"Jon Loeliger","fromEmail":"jdl@jdl.com","sentAt":"2006-09-17T16:23:30Z","receivedAt":"2006-09-17T16:23:30Z","isPatch":true,"sender":{"key":"jdl@jdl.com","avatar":"https://gravatar.com/avatar/75ce9a10b151acd2c28ec4ab2136dba7b2ff1634530bd04b155981a749d08a64?d=mp&s=160"},"body":"So, like, the other day Junio C Hamano mumbled:\n> I have a few stylistic comments but the contents look quite sane\n> and fine.\n\nLet me clean it up some and respin.\n\n> > I use an inetd invocation like this example:\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> \n> This would be nice to have as an example in the documentation.\n\nWill do.\n\njdl\n"}]}