Fix various memory leaks in transport-helper.c
[git] / transport-helper.c
1 #include "cache.h"
2 #include "transport.h"
3
4 #include "run-command.h"
5 #include "commit.h"
6 #include "diff.h"
7 #include "revision.h"
8 #include "remote.h"
9
10 struct helper_data
11 {
12         const char *name;
13         struct child_process *helper;
14         unsigned fetch : 1;
15         unsigned import : 1;
16         /* These go from remote name (as in "list") to private name */
17         struct refspec *refspecs;
18         int refspec_nr;
19 };
20
21 static struct child_process *get_helper(struct transport *transport)
22 {
23         struct helper_data *data = transport->data;
24         struct strbuf buf = STRBUF_INIT;
25         struct child_process *helper;
26         FILE *file;
27         const char **refspecs = NULL;
28         int refspec_nr = 0;
29         int refspec_alloc = 0;
30
31         if (data->helper)
32                 return data->helper;
33
34         helper = xcalloc(1, sizeof(*helper));
35         helper->in = -1;
36         helper->out = -1;
37         helper->err = 0;
38         helper->argv = xcalloc(4, sizeof(*helper->argv));
39         strbuf_addf(&buf, "remote-%s", data->name);
40         helper->argv[0] = strbuf_detach(&buf, NULL);
41         helper->argv[1] = transport->remote->name;
42         helper->argv[2] = transport->url;
43         helper->git_cmd = 1;
44         if (start_command(helper))
45                 die("Unable to run helper: git %s", helper->argv[0]);
46         data->helper = helper;
47
48         write_str_in_full(helper->in, "capabilities\n");
49
50         file = xfdopen(helper->out, "r");
51         while (1) {
52                 if (strbuf_getline(&buf, file, '\n') == EOF)
53                         exit(128); /* child died, message supplied already */
54
55                 if (!*buf.buf)
56                         break;
57                 if (!strcmp(buf.buf, "fetch"))
58                         data->fetch = 1;
59                 if (!strcmp(buf.buf, "import"))
60                         data->import = 1;
61                 if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) {
62                         ALLOC_GROW(refspecs,
63                                    refspec_nr + 1,
64                                    refspec_alloc);
65                         refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
66                 }
67         }
68         if (refspecs) {
69                 int i;
70                 data->refspec_nr = refspec_nr;
71                 data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
72                 for (i = 0; i < refspec_nr; i++) {
73                         free((char *)refspecs[i]);
74                 }
75                 free(refspecs);
76         }
77         strbuf_release(&buf);
78         return data->helper;
79 }
80
81 static int disconnect_helper(struct transport *transport)
82 {
83         struct helper_data *data = transport->data;
84         if (data->helper) {
85                 write_str_in_full(data->helper->in, "\n");
86                 close(data->helper->in);
87                 finish_command(data->helper);
88                 free((char *)data->helper->argv[0]);
89                 free(data->helper->argv);
90                 free(data->helper);
91                 data->helper = NULL;
92         }
93         return 0;
94 }
95
96 static int release_helper(struct transport *transport)
97 {
98         struct helper_data *data = transport->data;
99         free_refspec(data->refspec_nr, data->refspecs);
100         data->refspecs = NULL;
101         disconnect_helper(transport);
102         free(transport->data);
103         return 0;
104 }
105
106 static int fetch_with_fetch(struct transport *transport,
107                             int nr_heads, struct ref **to_fetch)
108 {
109         struct child_process *helper = get_helper(transport);
110         FILE *file = xfdopen(helper->out, "r");
111         int i;
112         struct strbuf buf = STRBUF_INIT;
113
114         for (i = 0; i < nr_heads; i++) {
115                 const struct ref *posn = to_fetch[i];
116                 if (posn->status & REF_STATUS_UPTODATE)
117                         continue;
118
119                 strbuf_addf(&buf, "fetch %s %s\n",
120                             sha1_to_hex(posn->old_sha1), posn->name);
121                 write_in_full(helper->in, buf.buf, buf.len);
122                 strbuf_reset(&buf);
123
124                 if (strbuf_getline(&buf, file, '\n') == EOF)
125                         exit(128); /* child died, message supplied already */
126         }
127         return 0;
128 }
129
130 static int get_importer(struct transport *transport, struct child_process *fastimport)
131 {
132         struct child_process *helper = get_helper(transport);
133         memset(fastimport, 0, sizeof(*fastimport));
134         fastimport->in = helper->out;
135         fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
136         fastimport->argv[0] = "fast-import";
137         fastimport->argv[1] = "--quiet";
138
139         fastimport->git_cmd = 1;
140         return start_command(fastimport);
141 }
142
143 static int fetch_with_import(struct transport *transport,
144                              int nr_heads, struct ref **to_fetch)
145 {
146         struct child_process fastimport;
147         struct child_process *helper = get_helper(transport);
148         struct helper_data *data = transport->data;
149         int i;
150         struct ref *posn;
151         struct strbuf buf = STRBUF_INIT;
152
153         if (get_importer(transport, &fastimport))
154                 die("Couldn't run fast-import");
155
156         for (i = 0; i < nr_heads; i++) {
157                 posn = to_fetch[i];
158                 if (posn->status & REF_STATUS_UPTODATE)
159                         continue;
160
161                 strbuf_addf(&buf, "import %s\n", posn->name);
162                 write_in_full(helper->in, buf.buf, buf.len);
163                 strbuf_reset(&buf);
164         }
165         disconnect_helper(transport);
166         finish_command(&fastimport);
167         free(fastimport.argv);
168         fastimport.argv = NULL;
169
170         for (i = 0; i < nr_heads; i++) {
171                 char *private;
172                 posn = to_fetch[i];
173                 if (posn->status & REF_STATUS_UPTODATE)
174                         continue;
175                 if (data->refspecs)
176                         private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
177                 else
178                         private = strdup(posn->name);
179                 read_ref(private, posn->old_sha1);
180                 free(private);
181         }
182         strbuf_release(&buf);
183         return 0;
184 }
185
186 static int fetch(struct transport *transport,
187                  int nr_heads, struct ref **to_fetch)
188 {
189         struct helper_data *data = transport->data;
190         int i, count;
191
192         count = 0;
193         for (i = 0; i < nr_heads; i++)
194                 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
195                         count++;
196
197         if (!count)
198                 return 0;
199
200         if (data->fetch)
201                 return fetch_with_fetch(transport, nr_heads, to_fetch);
202
203         if (data->import)
204                 return fetch_with_import(transport, nr_heads, to_fetch);
205
206         return -1;
207 }
208
209 static struct ref *get_refs_list(struct transport *transport, int for_push)
210 {
211         struct child_process *helper;
212         struct ref *ret = NULL;
213         struct ref **tail = &ret;
214         struct ref *posn;
215         struct strbuf buf = STRBUF_INIT;
216         FILE *file;
217
218         helper = get_helper(transport);
219
220         write_str_in_full(helper->in, "list\n");
221
222         file = xfdopen(helper->out, "r");
223         while (1) {
224                 char *eov, *eon;
225                 if (strbuf_getline(&buf, file, '\n') == EOF)
226                         exit(128); /* child died, message supplied already */
227
228                 if (!*buf.buf)
229                         break;
230
231                 eov = strchr(buf.buf, ' ');
232                 if (!eov)
233                         die("Malformed response in ref list: %s", buf.buf);
234                 eon = strchr(eov + 1, ' ');
235                 *eov = '\0';
236                 if (eon)
237                         *eon = '\0';
238                 *tail = alloc_ref(eov + 1);
239                 if (buf.buf[0] == '@')
240                         (*tail)->symref = xstrdup(buf.buf + 1);
241                 else if (buf.buf[0] != '?')
242                         get_sha1_hex(buf.buf, (*tail)->old_sha1);
243                 tail = &((*tail)->next);
244         }
245         strbuf_release(&buf);
246
247         for (posn = ret; posn; posn = posn->next)
248                 resolve_remote_symref(posn, ret);
249
250         return ret;
251 }
252
253 int transport_helper_init(struct transport *transport, const char *name)
254 {
255         struct helper_data *data = xcalloc(sizeof(*data), 1);
256         data->name = name;
257
258         transport->data = data;
259         transport->get_refs_list = get_refs_list;
260         transport->fetch = fetch;
261         transport->disconnect = release_helper;
262         return 0;
263 }