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"
This commit is contained in:
commit
ea1fd481b4
@ -557,7 +557,12 @@ int finish_command(struct child_process *cmd)
|
|||||||
|
|
||||||
int run_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)
|
if (code)
|
||||||
return code;
|
return code;
|
||||||
return finish_command(cmd);
|
return finish_command(cmd);
|
||||||
@ -829,3 +834,19 @@ int run_hook_le(const char *const *env, const char *name, ...)
|
|||||||
|
|
||||||
return ret;
|
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);
|
||||||
|
}
|
||||||
|
@ -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);
|
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
|
* The purpose of the following functions is to feed a pipe by running
|
||||||
* a function asynchronously and providing output that the caller reads.
|
* a function asynchronously and providing output that the caller reads.
|
||||||
|
@ -576,12 +576,10 @@ static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
|
|||||||
cp.env = local_repo_env;
|
cp.env = local_repo_env;
|
||||||
cp.git_cmd = 1;
|
cp.git_cmd = 1;
|
||||||
cp.no_stdin = 1;
|
cp.no_stdin = 1;
|
||||||
cp.out = -1;
|
|
||||||
cp.dir = path;
|
cp.dir = path;
|
||||||
if (!run_command(&cp) && !strbuf_read(&buf, cp.out, 1024))
|
if (!capture_command(&cp, &buf, 1024) && !buf.len)
|
||||||
is_present = 1;
|
is_present = 1;
|
||||||
|
|
||||||
close(cp.out);
|
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
}
|
}
|
||||||
return is_present;
|
return is_present;
|
||||||
|
18
trailer.c
18
trailer.c
@ -214,16 +214,6 @@ static struct trailer_item *remove_first(struct trailer_item **first)
|
|||||||
return item;
|
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)
|
static const char *apply_command(const char *command, const char *arg)
|
||||||
{
|
{
|
||||||
struct strbuf cmd = STRBUF_INIT;
|
struct strbuf cmd = STRBUF_INIT;
|
||||||
@ -240,14 +230,16 @@ static const char *apply_command(const char *command, const char *arg)
|
|||||||
cp.argv = argv;
|
cp.argv = argv;
|
||||||
cp.env = local_repo_env;
|
cp.env = local_repo_env;
|
||||||
cp.no_stdin = 1;
|
cp.no_stdin = 1;
|
||||||
cp.out = -1;
|
|
||||||
cp.use_shell = 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);
|
strbuf_release(&buf);
|
||||||
result = xstrdup("");
|
result = xstrdup("");
|
||||||
} else
|
} else {
|
||||||
|
strbuf_trim(&buf);
|
||||||
result = strbuf_detach(&buf, NULL);
|
result = strbuf_detach(&buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
strbuf_release(&cmd);
|
strbuf_release(&cmd);
|
||||||
return result;
|
return result;
|
||||||
|
10
wt-status.c
10
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 cmd_stdout = STRBUF_INIT;
|
||||||
struct strbuf summary = STRBUF_INIT;
|
struct strbuf summary = STRBUF_INIT;
|
||||||
char *summary_content;
|
char *summary_content;
|
||||||
size_t len;
|
|
||||||
|
|
||||||
argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s",
|
argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s",
|
||||||
s->index_file);
|
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.git_cmd = 1;
|
||||||
sm_summary.no_stdin = 1;
|
sm_summary.no_stdin = 1;
|
||||||
fflush(s->fp);
|
|
||||||
sm_summary.out = -1;
|
|
||||||
|
|
||||||
run_command(&sm_summary);
|
capture_command(&sm_summary, &cmd_stdout, 1024);
|
||||||
|
|
||||||
len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);
|
|
||||||
|
|
||||||
/* prepend header, only if there's an actual output */
|
/* prepend header, only if there's an actual output */
|
||||||
if (len) {
|
if (cmd_stdout.len) {
|
||||||
if (uncommitted)
|
if (uncommitted)
|
||||||
strbuf_addstr(&summary, _("Submodules changed but not updated:"));
|
strbuf_addstr(&summary, _("Submodules changed but not updated:"));
|
||||||
else
|
else
|
||||||
@ -764,6 +759,7 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
|
|||||||
strbuf_release(&cmd_stdout);
|
strbuf_release(&cmd_stdout);
|
||||||
|
|
||||||
if (s->display_comment_prefix) {
|
if (s->display_comment_prefix) {
|
||||||
|
size_t len;
|
||||||
summary_content = strbuf_detach(&summary, &len);
|
summary_content = strbuf_detach(&summary, &len);
|
||||||
strbuf_add_commented_lines(&summary, summary_content, len);
|
strbuf_add_commented_lines(&summary, summary_content, len);
|
||||||
free(summary_content);
|
free(summary_content);
|
||||||
|
Loading…
Reference in New Issue
Block a user