{"thread":{"id":"5615","subject":"[PATCH V2] Add virtualization support to git-daemon","startedAt":"2006-09-20T01:31:51Z","lastAt":"2006-09-25T21:19:00Z","messageCount":3,"participants":["Jon Loeliger","Alex Riesen"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"27208","messageId":"E1GPqwJ-0002xt-Bt@jdl.com","threadId":"5615","inReplyTo":null,"subject":"[PATCH V2] Add virtualization support to git-daemon","fromName":"Jon Loeliger","fromEmail":"jdl@jdl.com","sentAt":"2006-09-20T01:31:51Z","receivedAt":"2006-09-20T01:31:51Z","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\nAvast Matey! [*1*]\n\nPhat loot!\n\nHere be the promised respin o' me patch.  We be supportin'\ngit-daemon virtualization with this one.  No more dreaded\nhackery for multiple hosts, eh?  A flaggon of mead around!\nAye, it's a bloody good start, here, eh?, but she could\nsure use some keel-haulin' and testin'!\n\nCap'n jdl\n\n\n[*1*] http://talklikeapirate.com/\n\n\n Documentation/git-daemon.txt |   47 ++++++++++++++++++++++\n Makefile                     |    4 +-\n daemon.c                     |   89 +++++++++++++++++++++++++++++++++++++-----\n interpolate.c                |   82 +++++++++++++++++++++++++++++++++++++++\n interpolate.h                |   18 ++++++++\n 5 files changed, 229 insertions(+), 11 deletions(-)\n\n\ndiff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt\nindex 741f2c6..87444b4 100644\n--- a/Documentation/git-daemon.txt\n+++ b/Documentation/git-daemon.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n              [--timeout=n] [--init-timeout=n] [--strict-paths]\n              [--base-path=path] [--user-path | --user-path=path]\n+             [--interpolated-path=pathtemplate]\n              [--enable=service] [--disable=service]\n \t     [--allow-override=service] [--forbid-override=service]\n              [--reuseaddr] [--detach] [--pid-file=file]\n@@ -50,6 +51,12 @@ OPTIONS\n \t'git://example.com/hello.git', `git-daemon` will interpret the path\n \tas '/srv/git/hello.git'.\n \n+--interpolated-path=pathtemplate::\n+\tTo support virtual hosting, an interpolated path template can be\n+\tused to dynamically construct alternate paths.  The template\n+\tsupports %H for the target hostname as supplied by the client,\n+\tand %D for the absolute path of the named repository.\t\n+\n --export-all::\n \tAllow pulling from all directories that look like GIT repositories\n \t(have the 'objects' and 'refs' subdirectories), even if they\n@@ -135,6 +142,46 @@ upload-pack::\n \tdisable it by setting `daemon.uploadpack` configuration\n \titem to `false`.\n \n+EXAMPLES\n+--------\n+git-daemon as inetd server::\n+\tTo set up `git-daemon` as an inetd service that handles any\n+\trepository under the whitelisted set of directories, /pub/foo\n+\tand /pub/bar, place an entry like the following into\n+\t/etc/inetd all on one line:\n++\n+------------------------------------------------\n+\tgit stream tcp nowait nobody  /usr/bin/git-daemon\n+\t\tgit-daemon --inetd --verbose\n+\t\t--syslog --export-all\n+\t\t/pub/foo /pub/bar\n+------------------------------------------------\n+\n+\n+git-daemon as inetd server for virtual hosts::\n+\tTo set up `git-daemon` as an inetd service that handles\n+\trepositories for different virtual hosts, `www.example.com`\n+\tand `www.example.org`, place an entry like the following into\n+\t`/etc/inetd` all on one line:\n++\n+------------------------------------------------\n+\tgit stream tcp nowait nobody /usr/bin/git-daemon\n+\t\tgit-daemon --inetd --verbose\n+\t\t--syslog --export-all\n+\t\t--interpolated-path=/pub/%H%D\n+\t\t/pub/www.example.org/software\n+\t\t/pub/www.example.com/software\n+\t\t/software\n+------------------------------------------------\n++\n+In this example, the root-level directory `/pub` will contain\n+a subdirectory for each virtual host name supported.\n+Further, both hosts advertise repositories simply as\n+`git://www.example.com/software/repo.git`.  For pre-1.4.0\n+clients, a symlink from `/software` into the appropriate\n+default repository could be made as well.\n+\n+\n Author\n ------\n Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki\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..ef3a955 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,21 @@ 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+static struct interp interp_table[] = {\n+\t{ \"%H\", 0},\n+\t{ \"%D\", 0},\n+\t{ \"%%\", \"%\"},\n+};\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 +173,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 +209,27 @@ 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\tinterpolate(interp_path, PATH_MAX, interpolated_path,\n+\t\t\t    interp_table, ARRAY_SIZE(interp_table));\n+\t\tloginfo(\"Interpolated dir '%s'\", interp_path);\n+\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\tdir = rpath;\n \t}\n \n \tpath = enter_repo(dir, strict_paths);\n@@ -257,12 +293,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 +308,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 +389,28 @@ static void make_service_overridable(con\n \tdie(\"No such service %s\", name);\n }\n \n+static 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\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 +451,18 @@ #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+\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 +925,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..d82f1b5\n--- /dev/null\n+++ b/interpolate.c\n@@ -0,0 +1,82 @@\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 interpolate(char *result, int reslen,\n+\t\tchar *orig,\n+\t\tstruct 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+        memset(result, 0, 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..00c63a5\n--- /dev/null\n+++ b/interpolate.h\n@@ -0,0 +1,18 @@\n+/*\n+ * Copyright 2006 Jon Loeliger\n+ */\n+\n+#ifndef INTERPOLATE_H\n+#define INTERPOLATE_H\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+#endif /* INTERPOLATE_H */\n"},{"id":"27637","messageId":"20060925210222.GA6125@steel.home","threadId":"5615","inReplyTo":"E1GPqwJ-0002xt-Bt@jdl.com","subject":"[PATCH] Use const for interpolate arguments","fromName":"Alex Riesen","fromEmail":"fork0@t-online.de","sentAt":"2006-09-25T21:02:22Z","receivedAt":"2006-09-25T21:02:22Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Signed-off-by: Alex Riesen <raa.lkml@gmail.com>\n\n---\n interpolate.c |    6 +++---\n interpolate.h |    9 +++++++--\n 2 files changed, 10 insertions(+), 5 deletions(-)\n\ndiff --git a/interpolate.c b/interpolate.c\nindex d82f1b5..4570c12 100644\n--- a/interpolate.c\n+++ b/interpolate.c\n@@ -25,10 +25,10 @@ #include \"interpolate.h\"\n  */\n \n int interpolate(char *result, int reslen,\n-\t\tchar *orig,\n-\t\tstruct interp *interps, int ninterps)\n+\t\tconst char *orig,\n+\t\tconst struct interp *interps, int ninterps)\n {\n-\tchar *src = orig;\n+\tconst char *src = orig;\n \tchar *dest = result;\n \tint newlen = 0;\n \tchar *name, *value;\ndiff --git a/interpolate.h b/interpolate.h\nindex 00c63a5..d16f924 100644\n--- a/interpolate.h\n+++ b/interpolate.h\n@@ -5,6 +5,11 @@\n #ifndef INTERPOLATE_H\n #define INTERPOLATE_H\n \n+/*\n+ * Convert a NUL-terminated string in buffer orig,\n+ * performing substitutions on %-named sub-strings from\n+ * the interpretation table.\n+ */\n \n struct interp {\n \tchar *name;\n@@ -12,7 +17,7 @@ struct interp {\n };\n \n extern int interpolate(char *result, int reslen,\n-\t\t       char *orig,\n-\t\t       struct interp *interps, int ninterps);\n+\t\t       const char *orig,\n+\t\t       const struct interp *interps, int ninterps);\n \n #endif /* INTERPOLATE_H */\n-- \n1.4.2.1.g6b47-dirty\n"},{"id":"27639","messageId":"20060925211900.GB6125@steel.home","threadId":"5615","inReplyTo":"E1GPqwJ-0002xt-Bt@jdl.com","subject":"[PATCH] Use const for interpolate arguments","fromName":"Alex Riesen","fromEmail":"fork0@t-online.de","sentAt":"2006-09-25T21:19:00Z","receivedAt":"2006-09-25T21:19:00Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Signed-off-by: Alex Riesen <raa.lkml@gmail.com>\n\n---\n\nResending. jdl.com blacklisted my IP, for whatever it has done to him.\n\n interpolate.c |    6 +++---\n interpolate.h |    9 +++++++--\n 2 files changed, 10 insertions(+), 5 deletions(-)\n\ndiff --git a/interpolate.c b/interpolate.c\nindex d82f1b5..4570c12 100644\n--- a/interpolate.c\n+++ b/interpolate.c\n@@ -25,10 +25,10 @@ #include \"interpolate.h\"\n  */\n \n int interpolate(char *result, int reslen,\n-\t\tchar *orig,\n-\t\tstruct interp *interps, int ninterps)\n+\t\tconst char *orig,\n+\t\tconst struct interp *interps, int ninterps)\n {\n-\tchar *src = orig;\n+\tconst char *src = orig;\n \tchar *dest = result;\n \tint newlen = 0;\n \tchar *name, *value;\ndiff --git a/interpolate.h b/interpolate.h\nindex 00c63a5..d16f924 100644\n--- a/interpolate.h\n+++ b/interpolate.h\n@@ -5,6 +5,11 @@\n #ifndef INTERPOLATE_H\n #define INTERPOLATE_H\n \n+/*\n+ * Convert a NUL-terminated string in buffer orig,\n+ * performing substitutions on %-named sub-strings from\n+ * the interpretation table.\n+ */\n \n struct interp {\n \tchar *name;\n@@ -12,7 +17,7 @@ struct interp {\n };\n \n extern int interpolate(char *result, int reslen,\n-\t\t       char *orig,\n-\t\t       struct interp *interps, int ninterps);\n+\t\t       const char *orig,\n+\t\t       const struct interp *interps, int ninterps);\n \n #endif /* INTERPOLATE_H */\n-- \n1.4.2.1.g6b47-dirty\n"}]}