aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2015-03-25 12:54:27 -0700
committerJunio C Hamano <gitster@pobox.com>2015-03-25 12:54:27 -0700
commitea1fd481b4e689f143142662a82fb62c9b2efb65 (patch)
treea9e2475bf714d67eafee037aa710023495cad21e
parentd78374e578a1837ee73c45f944c420c6f3f64deb (diff)
parentc29b3962af3df80a43fab4ead4875bd2ca275e4c (diff)
downloadgit-ea1fd481b4e689f143142662a82fb62c9b2efb65.tar.gz
git-ea1fd481b4e689f143142662a82fb62c9b2efb65.tar.xz
Merge branch 'jk/run-command-capture'
The run-command interface was easy to abuse and make a pipe for us to read from the process, wait for the process to finish and then attempt to read its output, which is a pattern that lead to a deadlock. Fix such uses by introducing a helper to do this correctly (i.e. we need to read first and then wait the process to finish) and also add code to prevent such abuse in the run-command helper. * jk/run-command-capture: run-command: forbid using run_command with piped output trailer: use capture_command submodule: use capture_command wt-status: use capture_command run-command: introduce capture_command helper wt_status: fix signedness mismatch in strbuf_read call wt-status: don't flush before running "submodule status"
-rw-r--r--run-command.c23
-rw-r--r--run-command.h13
-rw-r--r--submodule.c4
-rw-r--r--trailer.c18
-rw-r--r--wt-status.c10
5 files changed, 44 insertions, 24 deletions
diff --git a/run-command.c b/run-command.c
index 3afb124c7..aad03ab70 100644
--- a/run-command.c
+++ b/run-command.c
@@ -557,7 +557,12 @@ int finish_command(struct child_process *cmd)
int run_command(struct child_process *cmd)
{
- int code = start_command(cmd);
+ int code;
+
+ if (cmd->out < 0 || cmd->err < 0)
+ die("BUG: run_command with a pipe can cause deadlock");
+
+ code = start_command(cmd);
if (code)
return code;
return finish_command(cmd);
@@ -829,3 +834,19 @@ int run_hook_le(const char *const *env, const char *name, ...)
return ret;
}
+
+int capture_command(struct child_process *cmd, struct strbuf *buf, size_t hint)
+{
+ cmd->out = -1;
+ if (start_command(cmd) < 0)
+ return -1;
+
+ if (strbuf_read(buf, cmd->out, hint) < 0) {
+ close(cmd->out);
+ finish_command(cmd); /* throw away exit code */
+ return -1;
+ }
+
+ close(cmd->out);
+ return finish_command(cmd);
+}
diff --git a/run-command.h b/run-command.h
index d6868dc8c..263b9662a 100644
--- a/run-command.h
+++ b/run-command.h
@@ -71,6 +71,19 @@ int run_command_v_opt(const char **argv, int opt);
*/
int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
+/**
+ * Execute the given command, capturing its stdout in the given strbuf.
+ * Returns -1 if starting the command fails or reading fails, and otherwise
+ * returns the exit code of the command. The output collected in the
+ * buffer is kept even if the command returns a non-zero exit. The hint field
+ * gives a starting size for the strbuf allocation.
+ *
+ * The fields of "cmd" should be set up as they would for a normal run_command
+ * invocation. But note that there is no need to set cmd->out; the function
+ * sets it up for the caller.
+ */
+int capture_command(struct child_process *cmd, struct strbuf *buf, size_t hint);
+
/*
* The purpose of the following functions is to feed a pipe by running
* a function asynchronously and providing output that the caller reads.
diff --git a/submodule.c b/submodule.c
index d37d400b2..c0e6c81fc 100644
--- a/submodule.c
+++ b/submodule.c
@@ -576,12 +576,10 @@ static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
cp.env = local_repo_env;
cp.git_cmd = 1;
cp.no_stdin = 1;
- cp.out = -1;
cp.dir = path;
- if (!run_command(&cp) && !strbuf_read(&buf, cp.out, 1024))
+ if (!capture_command(&cp, &buf, 1024) && !buf.len)
is_present = 1;
- close(cp.out);
strbuf_release(&buf);
}
return is_present;
diff --git a/trailer.c b/trailer.c
index 05b3859b4..4b14a567b 100644
--- a/trailer.c
+++ b/trailer.c
@@ -214,16 +214,6 @@ static struct trailer_item *remove_first(struct trailer_item **first)
return item;
}
-static int read_from_command(struct child_process *cp, struct strbuf *buf)
-{
- if (run_command(cp))
- return error("running trailer command '%s' failed", cp->argv[0]);
- if (strbuf_read(buf, cp->out, 1024) < 1)
- return error("reading from trailer command '%s' failed", cp->argv[0]);
- strbuf_trim(buf);
- return 0;
-}
-
static const char *apply_command(const char *command, const char *arg)
{
struct strbuf cmd = STRBUF_INIT;
@@ -240,14 +230,16 @@ static const char *apply_command(const char *command, const char *arg)
cp.argv = argv;
cp.env = local_repo_env;
cp.no_stdin = 1;
- cp.out = -1;
cp.use_shell = 1;
- if (read_from_command(&cp, &buf)) {
+ if (capture_command(&cp, &buf, 1024)) {
+ error("running trailer command '%s' failed", cmd.buf);
strbuf_release(&buf);
result = xstrdup("");
- } else
+ } else {
+ strbuf_trim(&buf);
result = strbuf_detach(&buf, NULL);
+ }
strbuf_release(&cmd);
return result;
diff --git a/wt-status.c b/wt-status.c
index 7036fa28d..853419f05 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -729,7 +729,6 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
struct strbuf cmd_stdout = STRBUF_INIT;
struct strbuf summary = STRBUF_INIT;
char *summary_content;
- size_t len;
argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s",
s->index_file);
@@ -745,15 +744,11 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
sm_summary.git_cmd = 1;
sm_summary.no_stdin = 1;
- fflush(s->fp);
- sm_summary.out = -1;
- run_command(&sm_summary);
-
- len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);
+ capture_command(&sm_summary, &cmd_stdout, 1024);
/* prepend header, only if there's an actual output */
- if (len) {
+ if (cmd_stdout.len) {
if (uncommitted)
strbuf_addstr(&summary, _("Submodules changed but not updated:"));
else
@@ -764,6 +759,7 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
strbuf_release(&cmd_stdout);
if (s->display_comment_prefix) {
+ size_t len;
summary_content = strbuf_detach(&summary, &len);
strbuf_add_commented_lines(&summary, summary_content, len);
free(summary_content);