Merge git-tools repository under "tools" subdirectory
[git] / connect.c
1 #include "cache.h"
2 #include "pkt-line.h"
3 #include "quote.h"
4 #include <sys/wait.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <arpa/inet.h>
8 #include <netdb.h>
9
10 int get_ack(int fd, unsigned char *result_sha1)
11 {
12         static char line[1000];
13         int len = packet_read_line(fd, line, sizeof(line));
14
15         if (!len)
16                 die("git-fetch-pack: expected ACK/NAK, got EOF");
17         if (line[len-1] == '\n')
18                 line[--len] = 0;
19         if (!strcmp(line, "NAK"))
20                 return 0;
21         if (!strncmp(line, "ACK ", 3)) {
22                 if (!get_sha1_hex(line+4, result_sha1))
23                         return 1;
24         }
25         die("git-fetch_pack: expected ACK/NAK, got '%s'", line);
26 }
27
28 int path_match(const char *path, int nr, char **match)
29 {
30         int i;
31         int pathlen = strlen(path);
32
33         for (i = 0; i < nr; i++) {
34                 char *s = match[i];
35                 int len = strlen(s);
36
37                 if (!len || len > pathlen)
38                         continue;
39                 if (memcmp(path + pathlen - len, s, len))
40                         continue;
41                 if (pathlen > len && path[pathlen - len - 1] != '/')
42                         continue;
43                 *s = 0;
44                 return 1;
45         }
46         return 0;
47 }
48
49 enum protocol {
50         PROTO_LOCAL = 1,
51         PROTO_SSH,
52         PROTO_GIT,
53 };
54
55 static enum protocol get_protocol(const char *name)
56 {
57         if (!strcmp(name, "ssh"))
58                 return PROTO_SSH;
59         if (!strcmp(name, "git"))
60                 return PROTO_GIT;
61         die("I don't handle protocol '%s'", name);
62 }
63
64 static void lookup_host(const char *host, struct sockaddr *in)
65 {
66         struct addrinfo *res;
67         int ret;
68
69         ret = getaddrinfo(host, NULL, NULL, &res);
70         if (ret)
71                 die("Unable to look up %s (%s)", host, gai_strerror(ret));
72         *in = *res->ai_addr;
73         freeaddrinfo(res);
74 }
75
76 static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
77 {
78         struct sockaddr addr;
79         int port = DEFAULT_GIT_PORT, sockfd;
80         char *colon;
81
82         colon = strchr(host, ':');
83         if (colon) {
84                 char *end;
85                 unsigned long n = strtoul(colon+1, &end, 0);
86                 if (colon[1] && !*end) {
87                         *colon = 0;
88                         port = n;
89                 }
90         }
91
92         lookup_host(host, &addr);
93         ((struct sockaddr_in *)&addr)->sin_port = htons(port);
94
95         sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
96         if (sockfd < 0)
97                 die("unable to create socket (%s)", strerror(errno));
98         if (connect(sockfd, (void *)&addr, sizeof(addr)) < 0)
99                 die("unable to connect (%s)", strerror(errno));
100         fd[0] = sockfd;
101         fd[1] = sockfd;
102         packet_write(sockfd, "%s %s\n", prog, path);
103         return 0;
104 }
105
106 /*
107  * Yeah, yeah, fixme. Need to pass in the heads etc.
108  */
109 int git_connect(int fd[2], char *url, const char *prog)
110 {
111         char command[1024];
112         char *host, *path;
113         char *colon;
114         int pipefd[2][2];
115         pid_t pid;
116         enum protocol protocol;
117
118         host = NULL;
119         path = url;
120         colon = strchr(url, ':');
121         protocol = PROTO_LOCAL;
122         if (colon) {
123                 *colon = 0;
124                 host = url;
125                 path = colon+1;
126                 protocol = PROTO_SSH;
127                 if (!memcmp(path, "//", 2)) {
128                         char *slash = strchr(path + 2, '/');
129                         if (slash) {
130                                 int nr = slash - path - 2;
131                                 memmove(path, path+2, nr);
132                                 path[nr] = 0;
133                                 protocol = get_protocol(url);
134                                 host = path;
135                                 path = slash;
136                         }
137                 }
138         }
139
140         if (protocol == PROTO_GIT)
141                 return git_tcp_connect(fd, prog, host, path);
142
143         if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
144                 die("unable to create pipe pair for communication");
145         pid = fork();
146         if (!pid) {
147                 snprintf(command, sizeof(command), "%s %s", prog,
148                          sq_quote(path));
149                 dup2(pipefd[1][0], 0);
150                 dup2(pipefd[0][1], 1);
151                 close(pipefd[0][0]);
152                 close(pipefd[0][1]);
153                 close(pipefd[1][0]);
154                 close(pipefd[1][1]);
155                 if (protocol == PROTO_SSH)
156                         execlp("ssh", "ssh", host, command, NULL);
157                 else
158                         execlp("sh", "sh", "-c", command, NULL);
159                 die("exec failed");
160         }               
161         fd[0] = pipefd[0][0];
162         fd[1] = pipefd[1][1];
163         close(pipefd[0][1]);
164         close(pipefd[1][0]);
165         return pid;
166 }
167
168 int finish_connect(pid_t pid)
169 {
170         int ret;
171
172         for (;;) {
173                 ret = waitpid(pid, NULL, 0);
174                 if (!ret)
175                         break;
176                 if (errno != EINTR)
177                         break;
178         }
179         return ret;
180 }