Remote helpers deadlock easily, so support debug mode which shows the interaction steps. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@xxxxxxxxxxx> Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> Signed-off-by: Ilari Liusvaara <ilari.liusvaara@xxxxxxxxxxx> --- transport-helper.c | 94 ++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 69 insertions(+), 25 deletions(-) diff --git a/transport-helper.c b/transport-helper.c index 11f3d7e..a721dc2 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -8,6 +8,8 @@ #include "quote.h" #include "remote.h" +static int debug; + struct helper_data { const char *name; @@ -22,6 +24,45 @@ struct helper_data int refspec_nr; }; +static void sendline(struct helper_data *helper, struct strbuf *buffer) +{ + if (debug) + fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf); + if (write_in_full(helper->helper->in, buffer->buf, buffer->len) + != buffer->len) + die_errno("Full write to remote helper failed"); +} + +static int recvline(struct helper_data *helper, struct strbuf *buffer) +{ + strbuf_reset(buffer); + if (debug) + fprintf(stderr, "Debug: Remote helper: Waiting...\n"); + if (strbuf_getline(buffer, helper->out, '\n') == EOF) { + if (debug) + fprintf(stderr, "Debug: Remote helper quit.\n"); + exit(128); + } + + if (debug) + fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf); + return 0; +} + +static void xchgline(struct helper_data *helper, struct strbuf *buffer) +{ + sendline(helper, buffer); + recvline(helper, buffer); +} + +static void write_constant(int fd, const char *str) +{ + if (debug) + fprintf(stderr, "Debug: Remote helper: -> %s", str); + if (write_in_full(fd, str, strlen(str)) != strlen(str)) + die_errno("Full write to remote helper failed"); +} + static struct child_process *get_helper(struct transport *transport) { struct helper_data *data = transport->data; @@ -48,15 +89,16 @@ static struct child_process *get_helper(struct transport *transport) die("Unable to run helper: git %s", helper->argv[0]); data->helper = helper; - write_str_in_full(helper->in, "capabilities\n"); + write_constant(helper->in, "capabilities\n"); data->out = xfdopen(helper->out, "r"); while (1) { - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!*buf.buf) break; + if (debug) + fprintf(stderr, "Debug: Got cap %s\n", buf.buf); if (!strcmp(buf.buf, "fetch")) data->fetch = 1; if (!strcmp(buf.buf, "option")) @@ -82,14 +124,21 @@ static struct child_process *get_helper(struct transport *transport) free(refspecs); } strbuf_release(&buf); + if (debug) + fprintf(stderr, "Debug: Capabilities complete.\n"); return data->helper; } static int disconnect_helper(struct transport *transport) { struct helper_data *data = transport->data; + struct strbuf buf = STRBUF_INIT; + if (data->helper) { - write_str_in_full(data->helper->in, "\n"); + if (debug) + fprintf(stderr, "Debug: Disconnecting.\n"); + strbuf_addf(&buf, "\n"); + sendline(data, &buf); close(data->helper->in); fclose(data->out); finish_command(data->helper); @@ -117,10 +166,11 @@ static int set_helper_option(struct transport *transport, const char *name, const char *value) { struct helper_data *data = transport->data; - struct child_process *helper = get_helper(transport); struct strbuf buf = STRBUF_INIT; int i, ret, is_bool = 0; + get_helper(transport); + if (!data->option) return 1; @@ -143,12 +193,7 @@ static int set_helper_option(struct transport *transport, quote_c_style(value, &buf, NULL, 0); strbuf_addch(&buf, '\n'); - if (write_in_full(helper->in, buf.buf, buf.len) != buf.len) - die_errno("cannot send option to %s", data->name); - - strbuf_reset(&buf); - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + xchgline(data, &buf); if (!strcmp(buf.buf, "ok")) ret = 0; @@ -208,13 +253,10 @@ static int fetch_with_fetch(struct transport *transport, } strbuf_addch(&buf, '\n'); - if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len) - die_errno("cannot send fetch to %s", data->name); + sendline(data, &buf); while (1) { - strbuf_reset(&buf); - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!prefixcmp(buf.buf, "lock ")) { const char *name = buf.buf + 5; @@ -249,12 +291,13 @@ static int fetch_with_import(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct child_process fastimport; - struct child_process *helper = get_helper(transport); struct helper_data *data = transport->data; int i; struct ref *posn; struct strbuf buf = STRBUF_INIT; + get_helper(transport); + if (get_importer(transport, &fastimport)) die("Couldn't run fast-import"); @@ -264,7 +307,7 @@ static int fetch_with_import(struct transport *transport, continue; strbuf_addf(&buf, "import %s\n", posn->name); - write_in_full(helper->in, buf.buf, buf.len); + sendline(data, &buf); strbuf_reset(&buf); } disconnect_helper(transport); @@ -369,17 +412,14 @@ static int push_refs(struct transport *transport, } strbuf_addch(&buf, '\n'); - if (write_in_full(helper->in, buf.buf, buf.len) != buf.len) - exit(128); + sendline(data, &buf); ref = remote_refs; while (1) { char *refname, *msg; int status; - strbuf_reset(&buf); - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!buf.len) break; @@ -471,8 +511,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) while (1) { char *eov, *eon; - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!*buf.buf) break; @@ -497,6 +536,8 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) } tail = &((*tail)->next); } + if (debug) + fprintf(stderr, "Debug: Read ref listing.\n"); strbuf_release(&buf); for (posn = ret; posn; posn = posn->next) @@ -510,6 +551,9 @@ int transport_helper_init(struct transport *transport, const char *name) struct helper_data *data = xcalloc(sizeof(*data), 1); data->name = name; + if (getenv("GIT_TRANSPORT_HELPER_DEBUG")) + debug = 1; + transport->data = data; transport->set_option = set_helper_option; transport->get_refs_list = get_refs_list; -- 1.6.6.rc1.300.gfbc27 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html