From 16652170bf80542fd77de75fb88da2f7761f65c4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 12:34:37 -0700 Subject: [PATCH 1/6] An illustration of rev-list --parents --pretty=raw This script creates two separate histories, A and B, each of which does: (A0, B0): create fileA and subdir/fileB (A1, B1): modify fileA (A2, B2): modify subdir/fileB and then grafts them together to make B0 a child of A2. So the final history looks like (time flows from top to bottom): true parent touches subdir? A0 none yes (creates it) A1 A0 no A2 A1 yes B0 none yes (different from what's in A2) B1 B0 no B2 B1 yes "git rev-list --parents --pretty=raw B2" would give "fake" parents on the "commit " header lines while "parent " header lines show the parent as recorded in the commit object (i.e. B0 appears to have A2 as its parent on "commit " header but there is no "parent A2" header line in it). When you have path limiters, we simplify history to omit commits that do not affect the specified paths. So "git rev-list --parents --pretty=raw B2 subdir" would return "B2 B0 A2 A0" (because B1 and A1 do not touch the path). When it does so, the "commit " header lines have "fake" parents (i.e. B2 appears to have B0 as its parent on "commit " header), but you can still get the true parents by looking at "parent " header. Signed-off-by: Junio C Hamano --- t/t6001-rev-list-graft.sh | 113 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100755 t/t6001-rev-list-graft.sh diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh new file mode 100755 index 0000000000..b2131cdacd --- /dev/null +++ b/t/t6001-rev-list-graft.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +test_description='Revision traversal vs grafts and path limiter' + +. ./test-lib.sh + +test_expect_success setup ' + mkdir subdir && + echo >fileA fileA && + echo >subdir/fileB fileB && + git add fileA subdir/fileB && + git commit -a -m "Initial in one history." && + A0=`git rev-parse --verify HEAD` && + + echo >fileA fileA modified && + git commit -a -m "Second in one history." && + A1=`git rev-parse --verify HEAD` && + + echo >subdir/fileB fileB modified && + git commit -a -m "Third in one history." && + A2=`git rev-parse --verify HEAD` && + + rm -f .git/refs/heads/master .git/index && + + echo >fileA fileA again && + echo >subdir/fileB fileB again && + git add fileA subdir/fileB && + git commit -a -m "Initial in alternate history." && + B0=`git rev-parse --verify HEAD` && + + echo >fileA fileA modified in alternate history && + git commit -a -m "Second in alternate history." && + B1=`git rev-parse --verify HEAD` && + + echo >subdir/fileB fileB modified in alternate history && + git commit -a -m "Third in alternate history." && + B2=`git rev-parse --verify HEAD` && + : done +' + +check () { + type=$1 + shift + + arg= + which=arg + rm -f test.expect + for a + do + if test "z$a" = z-- + then + which=expect + child= + continue + fi + if test "$which" = arg + then + arg="$arg$a " + continue + fi + if test "$type" = basic + then + echo "$a" + else + if test "z$child" != z + then + echo "$child $a" + fi + child="$a" + fi + done >test.expect + if test "$type" != basic && test "z$child" != z + then + echo >>test.expect $child + fi + if test $type = basic + then + git rev-list $arg >test.actual + elif test $type = parents + then + git rev-list --parents $arg >test.actual + elif test $type = parents-raw + then + git rev-list --parents --pretty=raw $arg | + sed -n -e 's/^commit //p' >test.actual + fi + diff test.expect test.actual +} + +for type in basic parents parents-raw +do + test_expect_success 'without grafts' " + rm -f .git/info/grafts + check $type $B2 -- $B2 $B1 $B0 + " + + test_expect_success 'with grafts' " + echo '$B0 $A2' >.git/info/grafts + check $type $B2 -- $B2 $B1 $B0 $A2 $A1 $A0 + " + + test_expect_success 'without grafts, with pathlimit' " + rm -f .git/info/grafts + check $type $B2 subdir -- $B2 $B0 + " + + test_expect_success 'with grafts, with pathlimit' " + echo '$B0 $A2' >.git/info/grafts + check $type $B2 subdir -- $B2 $B0 $A2 $A0 + " + +done +test_done From 209e7569313aa045da6d55e333c884e49e7d8fb2 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Wed, 27 Sep 2006 11:18:49 -0400 Subject: [PATCH 2/6] Corrected copy-and-paste thinko in ignore executable bit test case. This test should be testing update-index --add, not git-add as the latter is implemented in terms of the former. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- t/t3700-add.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t3700-add.sh b/t/t3700-add.sh index d36f22d7de..c20e4c29fc 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -35,7 +35,7 @@ test_expect_success \ 'git repo-config core.filemode 0 && echo foo >xfoo2 && chmod 755 xfoo2 && - git-add xfoo2 && + git-update-index --add xfoo2 && case "`git-ls-files --stage xfoo2`" in 100644" "*xfoo2) echo ok;; *) echo fail; git-ls-files --stage xfoo2; exit 1;; From b48fb5b6a950a6757b790e9160967065a3e03978 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 16:27:10 -0700 Subject: [PATCH 3/6] grep: free expressions and patterns when done. Signed-off-by: Junio C Hamano --- builtin-grep.c | 2 ++ grep.c | 42 ++++++++++++++++++++++++++++++++++++++++++ grep.h | 1 + 3 files changed, 45 insertions(+) diff --git a/builtin-grep.c b/builtin-grep.c index 6718788173..4205e5d38d 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -325,6 +325,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) else hit |= grep_file(opt, ce->name); } + free_grep_patterns(opt); return hit; } @@ -694,5 +695,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (grep_object(&opt, paths, real_obj, list.objects[i].name)) hit = 1; } + free_grep_patterns(&opt); return !hit; } diff --git a/grep.c b/grep.c index cc8d6846a5..2c740bde50 100644 --- a/grep.c +++ b/grep.c @@ -167,6 +167,46 @@ void compile_grep_patterns(struct grep_opt *opt) die("incomplete pattern expression: %s", p->pattern); } +static void free_pattern_expr(struct grep_expr *x) +{ + switch (x->node) { + case GREP_NODE_ATOM: + break; + case GREP_NODE_NOT: + free_pattern_expr(x->u.unary); + break; + case GREP_NODE_AND: + case GREP_NODE_OR: + free_pattern_expr(x->u.binary.left); + free_pattern_expr(x->u.binary.right); + break; + } + free(x); +} + +void free_grep_patterns(struct grep_opt *opt) +{ + struct grep_pat *p, *n; + + for (p = opt->pattern_list; p; p = n) { + n = p->next; + switch (p->token) { + case GREP_PATTERN: /* atom */ + case GREP_PATTERN_HEAD: + case GREP_PATTERN_BODY: + regfree(&p->regexp); + break; + default: + break; + } + free(p); + } + + if (!opt->extended) + return; + free_pattern_expr(opt->pattern_expression); +} + static char *end_of_line(char *cp, unsigned long *left) { unsigned long l = *left; @@ -439,6 +479,8 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long lno++; } + free(prev); + if (opt->status_only) return 0; if (opt->unmatch_name_only) { diff --git a/grep.h b/grep.h index 0b503ea665..af9098cfe8 100644 --- a/grep.h +++ b/grep.h @@ -73,6 +73,7 @@ struct grep_opt { extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t); extern void compile_grep_patterns(struct grep_opt *opt); +extern void free_grep_patterns(struct grep_opt *opt); extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size); #endif From a3f5d02edb2c1a037ed3ed8d2ebd3f3e5da9d198 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 16:42:53 -0700 Subject: [PATCH 4/6] grep: fix --fixed-strings combined with expression. "git grep --fixed-strings -e GIT --and -e VERSION .gitignore" misbehaved because we did not notice this needs to grab lines that have the given two fixed strings at the same time. Signed-off-by: Junio C Hamano --- grep.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/grep.c b/grep.c index 2c740bde50..c411ddd4d5 100644 --- a/grep.c +++ b/grep.c @@ -138,16 +138,13 @@ void compile_grep_patterns(struct grep_opt *opt) { struct grep_pat *p; - if (opt->fixed) - return; - - /* First compile regexps */ for (p = opt->pattern_list; p; p = p->next) { switch (p->token) { case GREP_PATTERN: /* atom */ case GREP_PATTERN_HEAD: case GREP_PATTERN_BODY: - compile_regexp(p, opt); + if (!opt->fixed) + compile_regexp(p, opt); break; default: opt->extended = 1; From dd4676299dde0a4c6f8a471e6353170f86a78c8a Mon Sep 17 00:00:00 2001 From: Jon Loeliger Date: Tue, 26 Sep 2006 09:47:43 -0500 Subject: [PATCH 5/6] Cleaned up git-daemon virtual hosting support. Standardized on lowercase hostnames from client. Added interpolation values for the IP address, port and canonical hostname of the server as it is contacted and named by the client and passed in via the extended args. Added --listen=host_or_ipaddr option suport. Renamed port variable as "listen_port" correspondingly as well. Documented mutual exclusivity of --inetd option with --user, --group, --listen and --port options. Added compat/inet_pton.c from Paul Vixie as needed. Small memory leaks need to be cleaned up still. Signed-off-by: Jon Loeliger Signed-off-by: Junio C Hamano --- Documentation/git-daemon.txt | 43 ++++++- Makefile | 3 + compat/inet_pton.c | 220 +++++++++++++++++++++++++++++++++++ daemon.c | 151 ++++++++++++++++++++---- 4 files changed, 389 insertions(+), 28 deletions(-) create mode 100644 compat/inet_pton.c diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 51d7c94d7d..d562232e52 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -8,14 +8,15 @@ git-daemon - A really simple server for git repositories SYNOPSIS -------- [verse] -'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all] +'git-daemon' [--verbose] [--syslog] [--export-all] [--timeout=n] [--init-timeout=n] [--strict-paths] [--base-path=path] [--user-path | --user-path=path] [--interpolated-path=pathtemplate] + [--reuseaddr] [--detach] [--pid-file=file] [--enable=service] [--disable=service] [--allow-override=service] [--forbid-override=service] - [--reuseaddr] [--detach] [--pid-file=file] - [--user=user [--group=group]] [directory...] + [--inetd | [--listen=host_or_ipaddr] [--port=n] [--user=user [--group=group]] + [directory...] DESCRIPTION ----------- @@ -54,8 +55,12 @@ OPTIONS --interpolated-path=pathtemplate:: To support virtual hosting, an interpolated path template can be used to dynamically construct alternate paths. The template - supports %H for the target hostname as supplied by the client, + supports %H for the target hostname as supplied by the client but + converted to all lowercase, %CH for the canonical hostname, + %IP for the server's IP address, %P for the port number, and %D for the absolute path of the named repository. + After interpolation, the path is validated against the directory + whitelist. --export-all:: Allow pulling from all directories that look like GIT repositories @@ -64,9 +69,17 @@ OPTIONS --inetd:: Have the server run as an inetd service. Implies --syslog. + Incompatible with --port, --listen, --user and --group options. ---port:: - Listen on an alternative port. +--listen=host_or_ipaddr:: + Listen on an a specific IP address or hostname. IP addresses can + be either an IPv4 address or an IPV6 address if supported. If IPv6 + is not supported, then --listen=hostname is also not supported and + --listen must be given an IPv4 address. + Incompatible with '--inetd' option. + +--port=n:: + Listen on an alternative port. Incompatible with '--inetd' option. --init-timeout:: Timeout between the moment the connection is established and the @@ -182,6 +195,24 @@ clients, a symlink from `/software` into the appropriate default repository could be made as well. +git-daemon as regular daemon for virtual hosts:: + To set up `git-daemon` as a regular, non-inetd service that + handles repositories for multiple virtual hosts based on + their IP addresses, start the daemon like this: ++ +------------------------------------------------ + git-daemon --verbose --export-all + --interpolated-path=/pub/%IP/%D + /pub/192.168.1.200/software + /pub/10.10.220.23/software +------------------------------------------------ ++ +In this example, the root-level directory `/pub` will contain +a subdirectory for each virtual host IP address supported. +Repositories can still be accessed by hostname though, assuming +they correspond to these IP addresses. + + Author ------ Written by Linus Torvalds , YOSHIFUJI Hideaki diff --git a/Makefile b/Makefile index 28091d6be0..e68b4c0747 100644 --- a/Makefile +++ b/Makefile @@ -524,6 +524,9 @@ endif ifdef NO_INET_NTOP LIB_OBJS += compat/inet_ntop.o endif +ifdef NO_INET_PTON + LIB_OBJS += compat/inet_pton.o +endif ifdef NO_ICONV ALL_CFLAGS += -DNO_ICONV diff --git a/compat/inet_pton.c b/compat/inet_pton.c new file mode 100644 index 0000000000..5704e0d2b6 --- /dev/null +++ b/compat/inet_pton.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 1996-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NS_INT16SZ +#define NS_INT16SZ 2 +#endif + +#ifndef NS_INADDRSZ +#define NS_INADDRSZ 4 +#endif + +#ifndef NS_IN6ADDRSZ +#define NS_IN6ADDRSZ 16 +#endif + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static int inet_pton4(const char *src, unsigned char *dst); +static int inet_pton6(const char *src, unsigned char *dst); + +/* int + * inet_pton4(src, dst) + * like inet_aton() but without all the hexadecimal and shorthand. + * return: + * 1 if `src' is a valid dotted quad, else 0. + * notice: + * does not touch `dst' unless it's returning 1. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton4(const char *src, unsigned char *dst) +{ + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + unsigned char tmp[NS_INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + unsigned int new = *tp * 10 + (pch - digits); + + if (new > 255) + return (0); + *tp = new; + if (! saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + memcpy(dst, tmp, NS_INADDRSZ); + return (1); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ + +#ifndef NO_IPV6 +static int +inet_pton6(const char *src, unsigned char *dst) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + unsigned int val; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_pton4(curtok, tp) > 0) { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (saw_xdigit) { + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, NS_IN6ADDRSZ); + return (1); +} +#endif + +/* int + * isc_net_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address wasn't valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * author: + * Paul Vixie, 1996. + */ +int +inet_pton(int af, const char *src, void *dst) +{ + switch (af) { + case AF_INET: + return (inet_pton4(src, dst)); +#ifndef NO_IPV6 + case AF_INET6: + return (inet_pton6(src, dst)); +#endif + default: + errno = EAFNOSUPPORT; + return (-1); + } + /* NOTREACHED */ +} diff --git a/daemon.c b/daemon.c index eb4f3f1e9f..69ea35c22d 100644 --- a/daemon.c +++ b/daemon.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "pkt-line.h" #include "cache.h" #include "exec_cmd.h" @@ -19,13 +20,15 @@ static int verbose; static int reuseaddr; static const char daemon_usage[] = -"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n" +"git-daemon [--verbose] [--syslog] [--export-all]\n" " [--timeout=n] [--init-timeout=n] [--strict-paths]\n" " [--base-path=path] [--user-path | --user-path=path]\n" " [--interpolated-path=path]\n" " [--reuseaddr] [--detach] [--pid-file=file]\n" " [--[enable|disable|allow-override|forbid-override]=service]\n" -" [--user=user [[--group=group]] [directory...]"; +" [--inetd | [--listen=host_or_ipaddr] [--port=n]\n" +" [--user=user [--group=group]]\n" +" [directory...]"; /* List of acceptable pathname prefixes */ static char **ok_paths; @@ -56,11 +59,17 @@ static unsigned int init_timeout; * Feel free to make dynamic as needed. */ #define INTERP_SLOT_HOST (0) -#define INTERP_SLOT_DIR (1) -#define INTERP_SLOT_PERCENT (2) +#define INTERP_SLOT_CANON_HOST (1) +#define INTERP_SLOT_IP (2) +#define INTERP_SLOT_PORT (3) +#define INTERP_SLOT_DIR (4) +#define INTERP_SLOT_PERCENT (5) static struct interp interp_table[] = { { "%H", 0}, + { "%CH", 0}, + { "%IP", 0}, + { "%P", 0}, { "%D", 0}, { "%%", "%"}, }; @@ -408,9 +417,17 @@ static void parse_extra_args(char *extra_args, int buflen) val = extra_args + 5; vallen = strlen(val) + 1; if (*val) { - char *save = xmalloc(vallen); + char *port; + char *save = xmalloc(vallen); /* FIXME: Leak */ + interp_table[INTERP_SLOT_HOST].value = save; strlcpy(save, val, vallen); + port = strrchr(save, ':'); + if (port) { + *port = 0; + port++; + interp_table[INTERP_SLOT_PORT].value = port; + } } /* On to the next one */ extra_args = val + vallen; @@ -418,6 +435,73 @@ static void parse_extra_args(char *extra_args, int buflen) } } +void fill_in_extra_table_entries(struct interp *itable) +{ + char *hp; + char *canon_host = NULL; + char *ipaddr = NULL; + + /* + * Replace literal host with lowercase-ized hostname. + */ + hp = interp_table[INTERP_SLOT_HOST].value; + for ( ; *hp; hp++) + *hp = tolower(*hp); + + /* + * Locate canonical hostname and its IP address. + */ +#ifndef NO_IPV6 + { + struct addrinfo hints; + struct addrinfo *ai, *ai0; + int gai; + static char addrbuf[HOST_NAME_MAX + 1]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + + gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0); + if (!gai) { + for (ai = ai0; ai; ai = ai->ai_next) { + struct sockaddr_in *sin_addr = (void *)ai->ai_addr; + + canon_host = xstrdup(ai->ai_canonname); + inet_ntop(AF_INET, &sin_addr->sin_addr, + addrbuf, sizeof(addrbuf)); + ipaddr = addrbuf; + break; + } + freeaddrinfo(ai0); + } + } +#else + { + struct hostent *hent; + struct sockaddr_in sa; + char **ap; + static char addrbuf[HOST_NAME_MAX + 1]; + + hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value); + canon_host = xstrdup(hent->h_name); + + ap = hent->h_addr_list; + memset(&sa, 0, sizeof sa); + sa.sin_family = hent->h_addrtype; + sa.sin_port = htons(0); + memcpy(&sa.sin_addr, *ap, hent->h_length); + + inet_ntop(hent->h_addrtype, &sa.sin_addr, + addrbuf, sizeof(addrbuf)); + ipaddr = addrbuf; + } +#endif + + interp_table[INTERP_SLOT_CANON_HOST].value = canon_host; /* FIXME: Leak */ + interp_table[INTERP_SLOT_IP].value = xstrdup(ipaddr); /* FIXME: Leak */ +} + + static int execute(struct sockaddr *addr) { static char line[1000]; @@ -458,8 +542,10 @@ static int execute(struct sockaddr *addr) if (len && line[len-1] == '\n') line[--len] = 0; - if (len != pktlen) + if (len != pktlen) { parse_extra_args(line + len + 1, pktlen - len - 1); + fill_in_extra_table_entries(interp_table); + } for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); @@ -663,23 +749,22 @@ static int set_reuse_addr(int sockfd) #ifndef NO_IPV6 -static int socksetup(int port, int **socklist_p) +static int socksetup(char *listen_addr, int listen_port, int **socklist_p) { int socknum = 0, *socklist = NULL; int maxfd = -1; char pbuf[NI_MAXSERV]; - struct addrinfo hints, *ai0, *ai; int gai; - sprintf(pbuf, "%d", port); + sprintf(pbuf, "%d", listen_port); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; - gai = getaddrinfo(NULL, pbuf, &hints, &ai0); + gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0); if (gai) die("getaddrinfo() failed: %s\n", gai_strerror(gai)); @@ -733,20 +818,27 @@ static int socksetup(int port, int **socklist_p) #else /* NO_IPV6 */ -static int socksetup(int port, int **socklist_p) +static int socksetup(char *lisen_addr, int listen_port, int **socklist_p) { struct sockaddr_in sin; int sockfd; + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(listen_port); + + if (listen_addr) { + /* Well, host better be an IP address here. */ + if (inet_pton(AF_INET, listen_addr, &sin.sin_addr.s_addr) <= 0) + return 0; + } else { + sin.sin_addr.s_addr = htonl(INADDR_ANY); + } + sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) return 0; - memset(&sin, 0, sizeof sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(port); - if (set_reuse_addr(sockfd)) { close(sockfd); return 0; @@ -855,13 +947,14 @@ static void store_pid(const char *path) fclose(f); } -static int serve(int port, struct passwd *pass, gid_t gid) +static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid) { int socknum, *socklist; - socknum = socksetup(port, &socklist); + socknum = socksetup(listen_addr, listen_port, &socklist); if (socknum == 0) - die("unable to allocate any listen sockets on port %u", port); + die("unable to allocate any listen sockets on host %s port %u", + listen_addr, listen_port); if (pass && gid && (initgroups(pass->pw_name, gid) || setgid (gid) || @@ -873,7 +966,8 @@ static int serve(int port, struct passwd *pass, gid_t gid) int main(int argc, char **argv) { - int port = DEFAULT_GIT_PORT; + int listen_port = 0; + char *listen_addr = NULL; int inetd_mode = 0; const char *pid_file = NULL, *user_name = NULL, *group_name = NULL; int detach = 0; @@ -890,12 +984,20 @@ int main(int argc, char **argv) for (i = 1; i < argc; i++) { char *arg = argv[i]; + if (!strncmp(arg, "--listen=", 9)) { + char *p = arg + 9; + char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1); + while (*p) + *ph++ = tolower(*p++); + *ph = 0; + continue; + } if (!strncmp(arg, "--port=", 7)) { char *end; unsigned long n; n = strtoul(arg+7, &end, 0); if (arg[7] && !*end) { - port = n; + listen_port = n; continue; } } @@ -995,6 +1097,11 @@ int main(int argc, char **argv) if (inetd_mode && (group_name || user_name)) die("--user and --group are incompatible with --inetd"); + if (inetd_mode && (listen_port || listen_addr)) + die("--listen= and --port= are incompatible with --inetd"); + else if (listen_port == 0) + listen_port = DEFAULT_GIT_PORT; + if (group_name && !user_name) die("--group supplied without --user"); @@ -1043,5 +1150,5 @@ int main(int argc, char **argv) if (pid_file) store_pid(pid_file); - return serve(port, pass, gid); + return serve(listen_addr, listen_port, pass, gid); } From eb30aed7c69190fd648947d54bbb9ebe53c67715 Mon Sep 17 00:00:00 2001 From: Jon Loeliger Date: Wed, 27 Sep 2006 11:16:10 -0500 Subject: [PATCH 6/6] Removed memory leaks from interpolation table uses. Clarified that parse_extra_args()s results in interpolation table entries. Removed a few trailing whitespace occurrences. Signed-off-by: Jon Loeliger Signed-off-by: Junio C Hamano --- daemon.c | 52 +++++++++++++++++++++++++++++++-------------------- interpolate.c | 26 ++++++++++++++++++++++++++ interpolate.h | 3 +++ 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/daemon.c b/daemon.c index 69ea35c22d..5335d212c3 100644 --- a/daemon.c +++ b/daemon.c @@ -71,7 +71,7 @@ static struct interp interp_table[] = { { "%IP", 0}, { "%P", 0}, { "%D", 0}, - { "%%", "%"}, + { "%%", 0}, }; @@ -405,7 +405,11 @@ static void make_service_overridable(const char *name, int ena) { die("No such service %s", name); } -static void parse_extra_args(char *extra_args, int buflen) +/* + * Separate the "extra args" information as supplied by the client connection. + * Any resulting data is squirrelled away in the given interpolation table. + */ +static void parse_extra_args(struct interp *table, char *extra_args, int buflen) { char *val; int vallen; @@ -417,18 +421,17 @@ static void parse_extra_args(char *extra_args, int buflen) val = extra_args + 5; vallen = strlen(val) + 1; if (*val) { - char *port; - char *save = xmalloc(vallen); /* FIXME: Leak */ - - interp_table[INTERP_SLOT_HOST].value = save; - strlcpy(save, val, vallen); - port = strrchr(save, ':'); + /* Split : at colon. */ + char *host = val; + char *port = strrchr(host, ':'); if (port) { *port = 0; port++; - interp_table[INTERP_SLOT_PORT].value = port; + interp_set_entry(table, INTERP_SLOT_PORT, port); } + interp_set_entry(table, INTERP_SLOT_HOST, host); } + /* On to the next one */ extra_args = val + vallen; } @@ -438,8 +441,6 @@ static void parse_extra_args(char *extra_args, int buflen) void fill_in_extra_table_entries(struct interp *itable) { char *hp; - char *canon_host = NULL; - char *ipaddr = NULL; /* * Replace literal host with lowercase-ized hostname. @@ -466,10 +467,12 @@ void fill_in_extra_table_entries(struct interp *itable) for (ai = ai0; ai; ai = ai->ai_next) { struct sockaddr_in *sin_addr = (void *)ai->ai_addr; - canon_host = xstrdup(ai->ai_canonname); inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf)); - ipaddr = addrbuf; + interp_set_entry(interp_table, + INTERP_SLOT_CANON_HOST, ai->ai_canonname); + interp_set_entry(interp_table, + INTERP_SLOT_IP, addrbuf); break; } freeaddrinfo(ai0); @@ -483,7 +486,6 @@ void fill_in_extra_table_entries(struct interp *itable) static char addrbuf[HOST_NAME_MAX + 1]; hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value); - canon_host = xstrdup(hent->h_name); ap = hent->h_addr_list; memset(&sa, 0, sizeof sa); @@ -493,12 +495,11 @@ void fill_in_extra_table_entries(struct interp *itable) inet_ntop(hent->h_addrtype, &sa.sin_addr, addrbuf, sizeof(addrbuf)); - ipaddr = addrbuf; + + interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name); + interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf); } #endif - - interp_table[INTERP_SLOT_CANON_HOST].value = canon_host; /* FIXME: Leak */ - interp_table[INTERP_SLOT_IP].value = xstrdup(ipaddr); /* FIXME: Leak */ } @@ -542,8 +543,14 @@ static int execute(struct sockaddr *addr) if (len && line[len-1] == '\n') line[--len] = 0; + /* + * Initialize the path interpolation table for this connection. + */ + interp_clear_table(interp_table, ARRAY_SIZE(interp_table)); + interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%"); + if (len != pktlen) { - parse_extra_args(line + len + 1, pktlen - len - 1); + parse_extra_args(interp_table, line + len + 1, pktlen - len - 1); fill_in_extra_table_entries(interp_table); } @@ -553,7 +560,12 @@ static int execute(struct sockaddr *addr) if (!strncmp("git-", line, 4) && !strncmp(s->name, line + 4, namelen) && line[namelen + 4] == ' ') { - interp_table[INTERP_SLOT_DIR].value = line+namelen+5; + /* + * Note: The directory here is probably context sensitive, + * and might depend on the actual service being performed. + */ + interp_set_entry(interp_table, + INTERP_SLOT_DIR, line + namelen + 5); return run_service(interp_table, s); } } diff --git a/interpolate.c b/interpolate.c index 4570c123dc..62701d8435 100644 --- a/interpolate.c +++ b/interpolate.c @@ -4,9 +4,35 @@ #include +#include "git-compat-util.h" #include "interpolate.h" +void interp_set_entry(struct interp *table, int slot, char *value) +{ + char *oldval = table[slot].value; + char *newval = value; + + if (oldval) + free(oldval); + + if (value) + newval = xstrdup(value); + + table[slot].value = newval; +} + + +void interp_clear_table(struct interp *table, int ninterps) +{ + int i; + + for (i = 0; i < ninterps; i++) { + interp_set_entry(table, i, NULL); + } +} + + /* * Convert a NUL-terminated string in buffer orig * into the supplied buffer, result, whose length is reslen, diff --git a/interpolate.h b/interpolate.h index d16f9244f3..a55fb8e071 100644 --- a/interpolate.h +++ b/interpolate.h @@ -16,6 +16,9 @@ struct interp { char *value; }; +extern void interp_set_entry(struct interp *table, int slot, char *value); +extern void interp_clear_table(struct interp *table, int ninterps); + extern int interpolate(char *result, int reslen, const char *orig, const struct interp *interps, int ninterps);