* [PATCH 6/6] Redirect receive-pack hook output into sideband channel
@ 2008-02-14 6:23 Shawn O. Pearce
0 siblings, 0 replies; only message in thread
From: Shawn O. Pearce @ 2008-02-14 6:23 UTC (permalink / raw)
To: git
If both send-pack and receive-pack have selected to enable the
side-band-64k protocol extension we can redirect the stdout and
stderr from any hooks we execute into the multiplexed band #2
channel. This allows recv_sideband running as an async "thread"
in send-pack to automatically unpack the additional messages and
display them on send-pack's stderr.
There are two different styles of hooks in use within receive-pack,
so our solution for perform this redirection varies a little.
In the update and post-update hooks there is no stdin provided to
the hook so we can perform a naive loop around the stderr pipe,
copying any received messages into band #2. The stderr pipe is
also dup'd over to stdout by start_command, so we get both sets of
messages over this single fd.
In the pre-receive and post-receive hooks we need to supply the set
of commands on stdin to the hook. To avoid a deadlock with the
hook we implement a poll loop within receive-pack, watching for
when we can feed additional data into the hook and also for when
we need to copy messages off the stderr pipe to band #2. By doing
this here in receive-pack we allow the hook author to not need to
perform the same sort of complex poll based IO for its stdin and
stdout/stderr handling.
Test vectors in t5401 needed to be modified slightly as any messages
received through sideband #2 are prefixed on the send-pack side with
"remote: ", indicating their origin. As the test suite is always
run with the same version of send-pack/receive-pack code we know
they will agree to enable the side-band-64k extension.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
receive-pack.c | 91 +++++++++++++++++++++++++++++++++++++++-------
t/t5401-update-hooks.sh | 22 ++++++------
2 files changed, 88 insertions(+), 25 deletions(-)
diff --git a/receive-pack.c b/receive-pack.c
index 8962c4c..f9f080b 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -56,7 +56,14 @@ static void write_head_info(void)
for_each_ref(show_ref, NULL);
if (!capabilities_sent)
show_ref("capabilities^{}", null_sha1, 0, NULL);
+}
+static void show_hook_output(char *msgs, ssize_t n)
+{
+ if (use_sideband)
+ send_sideband(1, 2, msgs, n, use_sideband);
+ else
+ write_in_full(2, msgs, n);
}
struct command {
@@ -100,10 +107,13 @@ static int hook_status(int code, const char *hook_name)
static int run_hook(const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
+ static char msgs[128];
struct command *cmd;
struct child_process proc;
const char *argv[2];
int have_input = 0, code;
+ size_t buf_len, buf_off;
+ struct pollfd pfd[2];
for (cmd = commands; !have_input && cmd; cmd = cmd->next) {
if (!cmd->error_string)
@@ -119,28 +129,87 @@ static int run_hook(const char *hook_name)
memset(&proc, 0, sizeof(proc));
proc.argv = argv;
proc.in = -1;
+ proc.err = -1;
proc.stdout_to_stderr = 1;
code = start_command(&proc);
if (code)
return hook_status(code, hook_name);
+
+ pfd[0].fd = proc.in;
+ pfd[0].events = POLLOUT;
+ pfd[1].fd = proc.err;
+ pfd[1].events = POLLIN;
+
for (cmd = commands; cmd; cmd = cmd->next) {
- if (!cmd->error_string) {
- size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
+ if (cmd->error_string)
+ continue;
+ buf_off = 0;
+ buf_len = snprintf(buf, sizeof(buf), "%s %s %s\n",
sha1_to_hex(cmd->old_sha1),
sha1_to_hex(cmd->new_sha1),
cmd->ref_name);
- if (write_in_full(proc.in, buf, n) != n)
- break;
- }
+ do {
+ if (poll(pfd, 2, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+ goto finish;
+ }
+ if (pfd[0].revents & POLLOUT) {
+ ssize_t r = xwrite(proc.in, buf + buf_off, buf_len);
+ if (r <= 0)
+ goto finish;
+ buf_len -= r;
+ buf_off += r;
+ }
+ if (pfd[1].revents & POLLIN) {
+ ssize_t r = xread(proc.err, msgs, sizeof(msgs));
+ if (r <= 0)
+ goto finish;
+ show_hook_output(msgs, r);
+ }
+ } while (buf_len > 0);
+ }
+
+finish:
+ close(proc.in);
+ proc.close_in = 0;
+ for (;;) {
+ ssize_t r = xread(proc.err, msgs, sizeof(msgs));
+ if (r <= 0)
+ break;
+ show_hook_output(msgs, r);
}
return hook_status(finish_command(&proc), hook_name);
}
+static int run_noinput_hook(const char **argv)
+{
+ static char msgs[128];
+ struct child_process proc;
+ int code;
+
+ memset(&proc, 0, sizeof(proc));
+ proc.argv = argv;
+ proc.no_stdin = 1;
+ proc.stdout_to_stderr = 1;
+ proc.err = -1;
+
+ code = start_command(&proc);
+ if (code)
+ return code;
+ for (;;) {
+ ssize_t sz = xread(proc.err, msgs, sizeof(msgs));
+ if (sz <= 0)
+ break;
+ show_hook_output(msgs, sz);
+ }
+ return finish_command(&proc);
+}
+
static int run_update_hook(struct command *cmd)
{
static const char update_hook[] = "hooks/update";
- struct child_process proc;
const char *argv[5];
if (access(update_hook, X_OK) < 0)
@@ -152,12 +221,7 @@ static int run_update_hook(struct command *cmd)
argv[3] = sha1_to_hex(cmd->new_sha1);
argv[4] = NULL;
- memset(&proc, 0, sizeof(proc));
- proc.argv = argv;
- proc.no_stdin = 1;
- proc.stdout_to_stderr = 1;
-
- return hook_status(run_command(&proc), update_hook);
+ return hook_status(run_noinput_hook(argv), update_hook);
}
static const char *update(struct command *cmd)
@@ -264,8 +328,7 @@ static void run_update_post_hook(struct command *cmd)
argc++;
}
argv[argc] = NULL;
- run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
- | RUN_COMMAND_STDOUT_TO_STDERR);
+ run_noinput_hook(argv);
}
static void execute_commands(const char *unpacker_error)
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 9a12024..7e77a7d 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -117,19 +117,19 @@ test_expect_success 'send-pack produced no output' '
'
cat <<EOF >expect
-STDOUT pre-receive
-STDERR pre-receive
-STDOUT update refs/heads/master
-STDERR update refs/heads/master
-STDOUT update refs/heads/tofail
-STDERR update refs/heads/tofail
-STDOUT post-receive
-STDERR post-receive
-STDOUT post-update
-STDERR post-update
+remote: STDOUT pre-receive^[[K
+remote: STDERR pre-receive^[[K
+remote: STDOUT update refs/heads/master^[[K
+remote: STDERR update refs/heads/master^[[K
+remote: STDOUT update refs/heads/tofail^[[K
+remote: STDERR update refs/heads/tofail^[[K
+remote: STDOUT post-receive^[[K
+remote: STDERR post-receive^[[K
+remote: STDOUT post-update^[[K
+remote: STDERR post-update^[[K
EOF
test_expect_success 'send-pack stderr contains hook messages' '
- grep ^STD send.err >actual &&
+ grep ^remote: send.err >actual &&
git diff - actual <expect
'
--
1.5.4.1.1309.g833c2
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2008-02-14 6:23 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-14 6:23 [PATCH 6/6] Redirect receive-pack hook output into sideband channel Shawn O. Pearce
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).