X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=connect.c;h=4422a0d8d38c225c6c4716ce8ff826bc1acbd981;hb=0881d2d1303a067a02e3811d43a1aed04a47945e;hp=3f2d65c313418c9e6b433a18f4b7d03c6ff276c2;hpb=9086a18cb81f99fff943b36830396ac8c37c7e9f;p=git.git diff --git a/connect.c b/connect.c index 3f2d65c31..4422a0d8d 100644 --- a/connect.c +++ b/connect.c @@ -8,14 +8,44 @@ #include #include #include +#include static char *server_capabilities = NULL; +static int check_ref(const char *name, int len, unsigned int flags) +{ + if (!flags) + return 1; + + if (len > 45 || memcmp(name, "refs/", 5)) + return 0; + + /* Skip the "refs/" part */ + name += 5; + len -= 5; + + /* REF_NORMAL means that we don't want the magic fake tag refs */ + if ((flags & REF_NORMAL) && check_ref_format(name) < 0) + return 0; + + /* REF_HEADS means that we want regular branch heads */ + if ((flags & REF_HEADS) && !memcmp(name, "heads/", 6)) + return 1; + + /* REF_TAGS means that we want tags */ + if ((flags & REF_TAGS) && !memcmp(name, "tags/", 5)) + return 1; + + /* All type bits clear means that we are ok with anything */ + return !(flags & ~REF_NORMAL); +} + /* * Read all the refs from the other end */ struct ref **get_remote_heads(int in, struct ref **list, - int nr_match, char **match, int ignore_funny) + int nr_match, char **match, + unsigned int flags) { *list = NULL; for (;;) { @@ -42,10 +72,8 @@ struct ref **get_remote_heads(int in, struct ref **list, server_capabilities = strdup(name + name_len + 1); } - if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) && - check_ref_format(name + 5)) + if (!check_ref(name, name_len, flags)) continue; - if (nr_match && !path_match(name, nr_match, match)) continue; ref = xcalloc(1, sizeof(*ref) + len - 40); @@ -74,7 +102,7 @@ int get_ack(int fd, unsigned char *result_sha1) line[--len] = 0; if (!strcmp(line, "NAK")) return 0; - if (!strncmp(line, "ACK ", 3)) { + if (!strncmp(line, "ACK ", 4)) { if (!get_sha1_hex(line+4, result_sha1)) { if (strstr(line+45, "continue")) return 2; @@ -100,7 +128,7 @@ int path_match(const char *path, int nr, char **match) if (pathlen > len && path[pathlen - len - 1] != '/') continue; *s = 0; - return 1; + return (i + 1); } return 0; } @@ -322,11 +350,14 @@ static enum protocol get_protocol(const char *name) #ifndef NO_IPV6 -static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) +/* + * Returns a connected socket() fd, or else die()s. + */ +static int git_tcp_connect_sock(char *host) { - int sockfd = -1; + int sockfd = -1, saved_errno = 0; char *colon, *end; - char *port = STR(DEFAULT_GIT_PORT); + const char *port = STR(DEFAULT_GIT_PORT); struct addrinfo hints, *ai0, *ai; int gai; @@ -356,10 +387,14 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) die("Unable to look up %s (%s)", host, gai_strerror(gai)); for (ai0 = ai; ai; ai = ai->ai_next) { - sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sockfd < 0) + sockfd = socket(ai->ai_family, + ai->ai_socktype, ai->ai_protocol); + if (sockfd < 0) { + saved_errno = errno; continue; + } if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { + saved_errno = errno; close(sockfd); sockfd = -1; continue; @@ -370,19 +405,19 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) freeaddrinfo(ai0); if (sockfd < 0) - die("unable to connect a socket (%s)", strerror(errno)); + die("unable to connect a socket (%s)", strerror(saved_errno)); - fd[0] = sockfd; - fd[1] = sockfd; - packet_write(sockfd, "%s %s\n", prog, path); - return 0; + return sockfd; } #else /* NO_IPV6 */ -static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) +/* + * Returns a connected socket() fd, or else die()s. + */ +static int git_tcp_connect_sock(char *host) { - int sockfd = -1; + int sockfd = -1, saved_errno = 0; char *colon, *end; char *port = STR(DEFAULT_GIT_PORT), *ep; struct hostent *he; @@ -407,7 +442,6 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) port = colon + 1; } - he = gethostbyname(host); if (!he) die("Unable to look up %s (%s)", host, hstrerror(h_errno)); @@ -422,8 +456,10 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) for (ap = he->h_addr_list; *ap; ap++) { sockfd = socket(he->h_addrtype, SOCK_STREAM, 0); - if (sockfd < 0) + if (sockfd < 0) { + saved_errno = errno; continue; + } memset(&sa, 0, sizeof sa); sa.sin_family = he->h_addrtype; @@ -431,6 +467,7 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) memcpy(&sa.sin_addr, *ap, he->h_length); if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { + saved_errno = errno; close(sockfd); sockfd = -1; continue; @@ -439,15 +476,22 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) } if (sockfd < 0) - die("unable to connect a socket (%s)", strerror(errno)); + die("unable to connect a socket (%s)", strerror(saved_errno)); + + return sockfd; +} + +#endif /* NO_IPV6 */ + + +static void git_tcp_connect(int fd[2], char *host) +{ + int sockfd = git_tcp_connect_sock(host); fd[0] = sockfd; fd[1] = sockfd; - packet_write(sockfd, "%s %s\n", prog, path); - return 0; } -#endif /* NO_IPV6 */ static char *git_proxy_command = NULL; static const char *rhost_name = NULL; @@ -510,9 +554,9 @@ static int git_use_proxy(const char *host) return (git_proxy_command && *git_proxy_command); } -static int git_proxy_connect(int fd[2], const char *prog, char *host, char *path) +static void git_proxy_connect(int fd[2], char *host) { - char *port = STR(DEFAULT_GIT_PORT); + const char *port = STR(DEFAULT_GIT_PORT); char *colon, *end; int pipefd[2][2]; pid_t pid; @@ -547,12 +591,12 @@ static int git_proxy_connect(int fd[2], const char *prog, char *host, char *path execlp(git_proxy_command, git_proxy_command, host, port, NULL); die("exec failed"); } + if (pid < 0) + die("fork failed"); fd[0] = pipefd[0][0]; fd[1] = pipefd[1][1]; close(pipefd[0][1]); close(pipefd[1][0]); - packet_write(fd[1], "%s %s\n", prog, path); - return pid; } /* @@ -567,6 +611,12 @@ int git_connect(int fd[2], char *url, const char *prog) int pipefd[2][2]; pid_t pid; enum protocol protocol = PROTO_LOCAL; + int free_path = 0; + + /* Without this we cannot rely on waitpid() to tell + * what happened to our children. + */ + signal(SIGCHLD, SIG_DFL); host = strstr(url, "://"); if(host) { @@ -610,21 +660,42 @@ int git_connect(int fd[2], char *url, const char *prog) char *ptr = path; if (path[1] == '~') path++; - else + else { path = strdup(ptr); + free_path = 1; + } *ptr = '\0'; } if (protocol == PROTO_GIT) { + /* These underlying connection commands die() if they + * cannot connect. + */ + char *target_host = strdup(host); if (git_use_proxy(host)) - return git_proxy_connect(fd, prog, host, path); - return git_tcp_connect(fd, prog, host, path); + git_proxy_connect(fd, host); + else + git_tcp_connect(fd, host); + /* + * Separate original protocol components prog and path + * from extended components with a NUL byte. + */ + packet_write(fd[1], + "%s %s%chost=%s%c", + prog, path, 0, + target_host, 0); + free(target_host); + if (free_path) + free(path); + return 0; } if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) die("unable to create pipe pair for communication"); pid = fork(); + if (pid < 0) + die("unable to fork"); if (!pid) { snprintf(command, sizeof(command), "%s %s", prog, sq_quote(path)); @@ -659,6 +730,8 @@ int git_connect(int fd[2], char *url, const char *prog) fd[1] = pipefd[1][1]; close(pipefd[0][1]); close(pipefd[1][0]); + if (free_path) + free(path); return pid; }