From 5123e6e7bd3b0bf3777cb77e36d141f76d8604a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Wed, 8 Feb 2023 20:21:11 +0100 Subject: [PATCH 1/5] run-command.c: remove dead assignment in while-loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove code that's been unused since it was added in c553c72eed6 (run-command: add an asynchronous parallel child processor, 2015-12-15). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- run-command.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/run-command.c b/run-command.c index 50cc011654..b439c7974c 100644 --- a/run-command.c +++ b/run-command.c @@ -1632,9 +1632,7 @@ static void pp_buffer_stderr(struct parallel_processes *pp, const struct run_process_parallel_opts *opts, int output_timeout) { - int i; - - while ((i = poll(pp->pfd, opts->processes, output_timeout) < 0)) { + while (poll(pp->pfd, opts->processes, output_timeout) < 0) { if (errno == EINTR) continue; pp_cleanup(pp, opts); From 540267304d37d6257edb3144e770693071d8fbb7 Mon Sep 17 00:00:00 2001 From: Emily Shaffer Date: Wed, 8 Feb 2023 20:21:12 +0100 Subject: [PATCH 2/5] run-command: allow stdin for run_processes_parallel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While it makes sense not to inherit stdin from the parent process to avoid deadlocking, it's not necessary to completely ban stdin to children. An informed user should be able to configure stdin safely. By setting `some_child.process.no_stdin=1` before calling `get_next_task()` we provide a reasonable default behavior but enable users to set up stdin streaming for themselves during the callback. `some_child.process.stdout_to_stderr`, however, remains unmodifiable by `get_next_task()` - the rest of the run_processes_parallel() API depends on child output in stderr. Signed-off-by: Emily Shaffer Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- run-command.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index b439c7974c..6bd16acb06 100644 --- a/run-command.c +++ b/run-command.c @@ -1586,6 +1586,14 @@ static int pp_start_one(struct parallel_processes *pp, if (i == opts->processes) BUG("bookkeeping is hard"); + /* + * By default, do not inherit stdin from the parent process - otherwise, + * all children would share stdin! Users may overwrite this to provide + * something to the child's stdin by having their 'get_next_task' + * callback assign 0 to .no_stdin and an appropriate integer to .in. + */ + pp->children[i].process.no_stdin = 1; + code = opts->get_next_task(&pp->children[i].process, opts->ungroup ? NULL : &pp->children[i].err, opts->data, @@ -1601,7 +1609,6 @@ static int pp_start_one(struct parallel_processes *pp, pp->children[i].process.err = -1; pp->children[i].process.stdout_to_stderr = 1; } - pp->children[i].process.no_stdin = 1; if (start_command(&pp->children[i].process)) { if (opts->start_failure) From 917e0802493a39d77c4bdbdf9aaa5d8d69b7a7b0 Mon Sep 17 00:00:00 2001 From: Emily Shaffer Date: Wed, 8 Feb 2023 20:21:13 +0100 Subject: [PATCH 3/5] hook API: support passing stdin to hooks, convert am's 'post-rewrite' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the invocation of the 'post-rewrite' hook run by 'git am' to use the hook.h library. To do this we need to add a "path_to_stdin" member to "struct run_hooks_opt". In our API this is supported by asking for a file path, rather than by reading stdin. Reading directly from stdin would involve caching the entire stdin (to memory or to disk) once the hook API is made to support "jobs" larger than 1, along with support for executing N hooks at a time (i.e. the upcoming config-based hooks). Signed-off-by: Emily Shaffer Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/am.c | 20 ++++---------------- hook.c | 5 +++++ hook.h | 5 +++++ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 82a41cbfc4..8be91617fe 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -495,24 +495,12 @@ static int run_applypatch_msg_hook(struct am_state *state) */ static int run_post_rewrite_hook(const struct am_state *state) { - struct child_process cp = CHILD_PROCESS_INIT; - const char *hook = find_hook("post-rewrite"); - int ret; + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; - if (!hook) - return 0; + strvec_push(&opt.args, "rebase"); + opt.path_to_stdin = am_path(state, "rewritten"); - strvec_push(&cp.args, hook); - strvec_push(&cp.args, "rebase"); - - cp.in = xopen(am_path(state, "rewritten"), O_RDONLY); - cp.stdout_to_stderr = 1; - cp.trace2_hook_name = "post-rewrite"; - - ret = run_command(&cp); - - close(cp.in); - return ret; + return run_hooks_opt("post-rewrite", &opt); } /** diff --git a/hook.c b/hook.c index a4fa1031f2..1a84831863 100644 --- a/hook.c +++ b/hook.c @@ -55,6 +55,11 @@ static int pick_next_hook(struct child_process *cp, cp->no_stdin = 1; strvec_pushv(&cp->env, hook_cb->options->env.v); + /* reopen the file for stdin; run_command closes it. */ + if (hook_cb->options->path_to_stdin) { + cp->no_stdin = 0; + cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY); + } cp->stdout_to_stderr = 1; cp->trace2_hook_name = hook_cb->hook_name; cp->dir = hook_cb->options->dir; diff --git a/hook.h b/hook.h index 4258b13da0..19ab9a5806 100644 --- a/hook.h +++ b/hook.h @@ -30,6 +30,11 @@ struct run_hooks_opt * was invoked. */ int *invoked_hook; + + /** + * Path to file which should be piped to stdin for each hook. + */ + const char *path_to_stdin; }; #define RUN_HOOKS_OPT_INIT { \ From 96af564d2781e76139474051c7651136d5c74652 Mon Sep 17 00:00:00 2001 From: Emily Shaffer Date: Wed, 8 Feb 2023 20:21:14 +0100 Subject: [PATCH 4/5] sequencer: use the new hook API for the simpler "post-rewrite" call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the invocation of the "post-rewrite" hook added in 795160457db (sequencer (rebase -i): run the post-rewrite hook, if needed, 2017-01-02) to use the new hook API. This leaves the more complex "post-rewrite" invocation added in a87a6f3c98e (commit: move post-rewrite code to libgit, 2017-11-17) here in sequencer.c unconverted. Here we can pass in a file's via the "in" file descriptor, in that case we don't have a file, but will need to write_in_full() to an "in" provide by the API. Support for that will be added to the hook API in the future, but we're not there yet. Signed-off-by: Emily Shaffer Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- sequencer.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/sequencer.c b/sequencer.c index 3e4a197289..d8d59d05dd 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4834,8 +4834,7 @@ cleanup_head_ref: if (!stat(rebase_path_rewritten_list(), &st) && st.st_size > 0) { struct child_process child = CHILD_PROCESS_INIT; - const char *post_rewrite_hook = - find_hook("post-rewrite"); + struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT; child.in = open(rebase_path_rewritten_list(), O_RDONLY); child.git_cmd = 1; @@ -4845,18 +4844,9 @@ cleanup_head_ref: /* we don't care if this copying failed */ run_command(&child); - if (post_rewrite_hook) { - struct child_process hook = CHILD_PROCESS_INIT; - - hook.in = open(rebase_path_rewritten_list(), - O_RDONLY); - hook.stdout_to_stderr = 1; - hook.trace2_hook_name = "post-rewrite"; - strvec_push(&hook.args, post_rewrite_hook); - strvec_push(&hook.args, "rebase"); - /* we don't care if this hook failed */ - run_command(&hook); - } + hook_opt.path_to_stdin = rebase_path_rewritten_list(); + strvec_push(&hook_opt.args, "rebase"); + run_hooks_opt("post-rewrite", &hook_opt); } apply_autostash(rebase_path_autostash()); From 0414b3891cd3adb80b879c7be49d9b727e2b23f5 Mon Sep 17 00:00:00 2001 From: Emily Shaffer Date: Wed, 8 Feb 2023 20:21:15 +0100 Subject: [PATCH 5/5] hook: support a --to-stdin= option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the "path_to_stdin" API added in the preceding commit in the "git hook run" command. For now we won't be using this command interface outside of the tests, but exposing this functionality makes it easier to test the hook API. The plan is to use this to extend the "sendemail-validate" hook[1][2]. 1. https://lore.kernel.org/git/ad152e25-4061-9955-d3e6-a2c8b1bd24e7@amd.com 2. https://lore.kernel.org/git/20230120012459.920932-1-michael.strawbridge@amd.com Signed-off-by: Emily Shaffer Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/git-hook.txt | 7 ++++++- builtin/hook.c | 4 +++- t/t1800-hook.sh | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index 77c3a8ad90..3407f3c2c0 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -8,7 +8,7 @@ git-hook - Run git hooks SYNOPSIS -------- [verse] -'git hook' run [--ignore-missing] [-- ] +'git hook' run [--ignore-missing] [--to-stdin=] [-- ] DESCRIPTION ----------- @@ -31,6 +31,11 @@ linkgit:githooks[5] for arguments hooks might expect (if any). OPTIONS ------- +--to-stdin:: + For "run"; Specify a file which will be streamed into the + hook's stdin. The hook will receive the entire file from + beginning to EOF. + --ignore-missing:: Ignore any missing hook by quietly returning zero. Used for tools that want to do a blind one-shot run of a hook that may diff --git a/builtin/hook.c b/builtin/hook.c index b6530d189a..f95b7965c5 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -7,7 +7,7 @@ #include "strvec.h" #define BUILTIN_HOOK_RUN_USAGE \ - N_("git hook run [--ignore-missing] [-- ]") + N_("git hook run [--ignore-missing] [--to-stdin=] [-- ]") static const char * const builtin_hook_usage[] = { BUILTIN_HOOK_RUN_USAGE, @@ -28,6 +28,8 @@ static int run(int argc, const char **argv, const char *prefix) struct option run_options[] = { OPT_BOOL(0, "ignore-missing", &ignore_missing, N_("silently ignore missing requested ")), + OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"), + N_("file to read into hooks' stdin")), OPT_END(), }; int ret; diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh index 2ef3579fa7..3506f627b6 100755 --- a/t/t1800-hook.sh +++ b/t/t1800-hook.sh @@ -177,4 +177,22 @@ test_expect_success 'git hook run a hook with a bad shebang' ' test_cmp expect actual ' +test_expect_success 'stdin to hooks' ' + write_script .git/hooks/test-hook <<-\EOF && + echo BEGIN stdin + cat + echo END stdin + EOF + + cat >expect <<-EOF && + BEGIN stdin + hello + END stdin + EOF + + echo hello >input && + git hook run --to-stdin=input test-hook 2>actual && + test_cmp expect actual +' + test_done