All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Shawn O. Pearce" <spearce@spearce.org>
To: git@vger.kernel.org
Subject: [PATCH 6/6] Redirect receive-pack hook output into sideband channel
Date: Thu, 14 Feb 2008 01:23:09 -0500	[thread overview]
Message-ID: <20080214062309.GF30516@spearce.org> (raw)

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

                 reply	other threads:[~2008-02-14  6:23 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080214062309.GF30516@spearce.org \
    --to=spearce@spearce.org \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.