#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
+#include <signal.h>
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 (;;) {
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);
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;
if (pathlen > len && path[pathlen - len - 1] != '/')
continue;
*s = 0;
- return 1;
+ return (i + 1);
}
return 0;
}
#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;
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;
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;
port = colon + 1;
}
-
he = gethostbyname(host);
if (!he)
die("Unable to look up %s (%s)", host, hstrerror(h_errno));
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;
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;
}
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;
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;
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;
}
/*
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) {
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));
fd[1] = pipefd[1][1];
close(pipefd[0][1]);
close(pipefd[1][0]);
+ if (free_path)
+ free(path);
return pid;
}