]>
Commit | Line | Data |
---|---|---|
f7192598 | 1 | #include "cache.h" |
41cb7488 | 2 | #include "pkt-line.h" |
b10d0ec7 | 3 | #include "quote.h" |
f7192598 | 4 | #include <sys/wait.h> |
2386d658 LT |
5 | #include <sys/socket.h> |
6 | #include <netinet/in.h> | |
7 | #include <arpa/inet.h> | |
8 | #include <netdb.h> | |
f7192598 | 9 | |
d1c133f5 LT |
10 | /* |
11 | * Read all the refs from the other end | |
12 | */ | |
13 | struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match) | |
14 | { | |
15 | *list = NULL; | |
16 | for (;;) { | |
17 | struct ref *ref; | |
18 | unsigned char old_sha1[20]; | |
19 | static char buffer[1000]; | |
20 | char *name; | |
21 | int len; | |
22 | ||
23 | len = packet_read_line(in, buffer, sizeof(buffer)); | |
24 | if (!len) | |
25 | break; | |
26 | if (buffer[len-1] == '\n') | |
27 | buffer[--len] = 0; | |
28 | ||
29 | if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ') | |
30 | die("protocol error: expected sha/ref, got '%s'", buffer); | |
31 | name = buffer + 41; | |
32 | if (nr_match && !path_match(name, nr_match, match)) | |
33 | continue; | |
34 | ref = xmalloc(sizeof(*ref) + len - 40); | |
35 | memcpy(ref->old_sha1, old_sha1, 20); | |
36 | memset(ref->new_sha1, 0, 20); | |
37 | memcpy(ref->name, buffer + 41, len - 40); | |
38 | ref->next = NULL; | |
39 | *list = ref; | |
40 | list = &ref->next; | |
41 | } | |
42 | return list; | |
43 | } | |
44 | ||
41cb7488 LT |
45 | int get_ack(int fd, unsigned char *result_sha1) |
46 | { | |
47 | static char line[1000]; | |
48 | int len = packet_read_line(fd, line, sizeof(line)); | |
49 | ||
50 | if (!len) | |
51 | die("git-fetch-pack: expected ACK/NAK, got EOF"); | |
52 | if (line[len-1] == '\n') | |
53 | line[--len] = 0; | |
54 | if (!strcmp(line, "NAK")) | |
55 | return 0; | |
56 | if (!strncmp(line, "ACK ", 3)) { | |
57 | if (!get_sha1_hex(line+4, result_sha1)) | |
58 | return 1; | |
59 | } | |
60 | die("git-fetch_pack: expected ACK/NAK, got '%s'", line); | |
61 | } | |
62 | ||
013e7c7f LT |
63 | int path_match(const char *path, int nr, char **match) |
64 | { | |
65 | int i; | |
66 | int pathlen = strlen(path); | |
67 | ||
68 | for (i = 0; i < nr; i++) { | |
69 | char *s = match[i]; | |
70 | int len = strlen(s); | |
71 | ||
72 | if (!len || len > pathlen) | |
73 | continue; | |
74 | if (memcmp(path + pathlen - len, s, len)) | |
75 | continue; | |
76 | if (pathlen > len && path[pathlen - len - 1] != '/') | |
77 | continue; | |
78 | *s = 0; | |
79 | return 1; | |
80 | } | |
81 | return 0; | |
82 | } | |
83 | ||
2386d658 LT |
84 | enum protocol { |
85 | PROTO_LOCAL = 1, | |
86 | PROTO_SSH, | |
87 | PROTO_GIT, | |
88 | }; | |
89 | ||
90 | static enum protocol get_protocol(const char *name) | |
91 | { | |
92 | if (!strcmp(name, "ssh")) | |
93 | return PROTO_SSH; | |
94 | if (!strcmp(name, "git")) | |
95 | return PROTO_GIT; | |
96 | die("I don't handle protocol '%s'", name); | |
97 | } | |
98 | ||
5ba88448 YH |
99 | #define STR_(s) # s |
100 | #define STR(s) STR_(s) | |
2386d658 LT |
101 | |
102 | static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) | |
103 | { | |
5ba88448 YH |
104 | int sockfd = -1; |
105 | char *colon, *end; | |
106 | char *port = STR(DEFAULT_GIT_PORT); | |
107 | struct addrinfo hints, *ai0, *ai; | |
108 | int gai; | |
109 | ||
110 | if (host[0] == '[') { | |
111 | end = strchr(host + 1, ']'); | |
112 | if (end) { | |
113 | *end = 0; | |
114 | end++; | |
115 | host++; | |
116 | } else | |
117 | end = host; | |
118 | } else | |
119 | end = host; | |
120 | colon = strchr(end, ':'); | |
121 | ||
ce6f8e7e LT |
122 | if (colon) { |
123 | *colon = 0; | |
5ba88448 | 124 | port = colon + 1; |
ce6f8e7e | 125 | } |
5ba88448 YH |
126 | |
127 | memset(&hints, 0, sizeof(hints)); | |
128 | hints.ai_socktype = SOCK_STREAM; | |
129 | hints.ai_protocol = IPPROTO_TCP; | |
130 | ||
131 | gai = getaddrinfo(host, port, &hints, &ai); | |
132 | if (gai) | |
133 | die("Unable to look up %s (%s)", host, gai_strerror(gai)); | |
134 | ||
135 | for (ai0 = ai; ai; ai = ai->ai_next) { | |
136 | sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | |
137 | if (sockfd < 0) | |
138 | continue; | |
139 | if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { | |
140 | close(sockfd); | |
141 | sockfd = -1; | |
142 | continue; | |
2386d658 | 143 | } |
5ba88448 | 144 | break; |
2386d658 LT |
145 | } |
146 | ||
5ba88448 | 147 | freeaddrinfo(ai0); |
2386d658 | 148 | |
2386d658 | 149 | if (sockfd < 0) |
5ba88448 YH |
150 | die("unable to connect a socket (%s)", strerror(errno)); |
151 | ||
2386d658 LT |
152 | fd[0] = sockfd; |
153 | fd[1] = sockfd; | |
154 | packet_write(sockfd, "%s %s\n", prog, path); | |
155 | return 0; | |
156 | } | |
157 | ||
f7192598 LT |
158 | /* |
159 | * Yeah, yeah, fixme. Need to pass in the heads etc. | |
160 | */ | |
161 | int git_connect(int fd[2], char *url, const char *prog) | |
162 | { | |
163 | char command[1024]; | |
2386d658 | 164 | char *host, *path; |
f7192598 LT |
165 | char *colon; |
166 | int pipefd[2][2]; | |
167 | pid_t pid; | |
2386d658 | 168 | enum protocol protocol; |
f7192598 | 169 | |
f7192598 LT |
170 | host = NULL; |
171 | path = url; | |
172 | colon = strchr(url, ':'); | |
2386d658 | 173 | protocol = PROTO_LOCAL; |
f7192598 LT |
174 | if (colon) { |
175 | *colon = 0; | |
176 | host = url; | |
177 | path = colon+1; | |
2386d658 LT |
178 | protocol = PROTO_SSH; |
179 | if (!memcmp(path, "//", 2)) { | |
180 | char *slash = strchr(path + 2, '/'); | |
181 | if (slash) { | |
182 | int nr = slash - path - 2; | |
183 | memmove(path, path+2, nr); | |
184 | path[nr] = 0; | |
185 | protocol = get_protocol(url); | |
186 | host = path; | |
187 | path = slash; | |
188 | } | |
189 | } | |
f7192598 | 190 | } |
2386d658 LT |
191 | |
192 | if (protocol == PROTO_GIT) | |
193 | return git_tcp_connect(fd, prog, host, path); | |
194 | ||
f7192598 LT |
195 | if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) |
196 | die("unable to create pipe pair for communication"); | |
197 | pid = fork(); | |
198 | if (!pid) { | |
b10d0ec7 JH |
199 | snprintf(command, sizeof(command), "%s %s", prog, |
200 | sq_quote(path)); | |
f7192598 LT |
201 | dup2(pipefd[1][0], 0); |
202 | dup2(pipefd[0][1], 1); | |
203 | close(pipefd[0][0]); | |
204 | close(pipefd[0][1]); | |
205 | close(pipefd[1][0]); | |
206 | close(pipefd[1][1]); | |
2386d658 | 207 | if (protocol == PROTO_SSH) |
f7192598 LT |
208 | execlp("ssh", "ssh", host, command, NULL); |
209 | else | |
210 | execlp("sh", "sh", "-c", command, NULL); | |
211 | die("exec failed"); | |
212 | } | |
213 | fd[0] = pipefd[0][0]; | |
214 | fd[1] = pipefd[1][1]; | |
215 | close(pipefd[0][1]); | |
216 | close(pipefd[1][0]); | |
217 | return pid; | |
218 | } | |
219 | ||
220 | int finish_connect(pid_t pid) | |
221 | { | |
222 | int ret; | |
223 | ||
224 | for (;;) { | |
225 | ret = waitpid(pid, NULL, 0); | |
226 | if (!ret) | |
227 | break; | |
228 | if (errno != EINTR) | |
229 | break; | |
230 | } | |
231 | return ret; | |
232 | } |