From 08a77c04b4574ca4b282154d4ed4ab85cf2fcaa2 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sat, 23 Jan 2010 13:11:58 -0800
Subject: [PATCH] Check for remote server exec failures and report

If remote.name.uploadpack or .receivepack is misconfigured and points
to a non-existent command on the remote system, we should receive back
exit status 127.  Report this case specially with the command we used
so the user knows what is going.

Bug: 293703
Change-Id: I7504e7b6238d5d8e698d37db7411c4817a039d08
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../jgit/transport/TransportGitSsh.java       | 56 +++++++++++++------
 1 file changed, 40 insertions(+), 16 deletions(-)

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index 55636f8dc..5ee7887f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008-2010, Google Inc.
  * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
@@ -127,6 +127,23 @@ private static void sq(final StringBuilder cmd, final String val) {
 			cmd.append(QuotedString.BOURNE.quote(val));
 	}
 
+	private String commandFor(final String exe) {
+		String path = uri.getPath();
+		if (uri.getScheme() != null && uri.getPath().startsWith("/~"))
+			path = (uri.getPath().substring(1));
+
+		final StringBuilder cmd = new StringBuilder();
+		final int gitspace = exe.indexOf("git ");
+		if (gitspace >= 0) {
+			sqMinimal(cmd, exe.substring(0, gitspace + 3));
+			cmd.append(' ');
+			sqMinimal(cmd, exe.substring(gitspace + 4));
+		} else
+			sqMinimal(cmd, exe);
+		cmd.append(' ');
+		sqAlways(cmd, path);
+		return cmd.toString();
+	}
 
 	ChannelExec exec(final String exe) throws TransportException {
 		initSession();
@@ -134,21 +151,7 @@ ChannelExec exec(final String exe) throws TransportException {
 		final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0;
 		try {
 			final ChannelExec channel = (ChannelExec) sock.openChannel("exec");
-			String path = uri.getPath();
-			if (uri.getScheme() != null && uri.getPath().startsWith("/~"))
-				path = (uri.getPath().substring(1));
-
-			final StringBuilder cmd = new StringBuilder();
-			final int gitspace = exe.indexOf("git ");
-			if (gitspace >= 0) {
-				sqMinimal(cmd, exe.substring(0, gitspace + 3));
-				cmd.append(' ');
-				sqMinimal(cmd, exe.substring(gitspace + 4));
-			} else
-				sqMinimal(cmd, exe);
-			cmd.append(' ');
-			sqAlways(cmd, path);
-			channel.setCommand(cmd.toString());
+			channel.setCommand(commandFor(exe));
 			errStream = createErrorStream();
 			channel.setErrStream(errStream, true);
 			channel.connect(tms);
@@ -158,6 +161,17 @@ ChannelExec exec(final String exe) throws TransportException {
 		}
 	}
 
+	void checkExecFailure(int status, String exe) throws TransportException {
+		if (status == 127) {
+			String why = errStream.toString();
+			IOException cause = null;
+			if (why != null && why.length() > 0)
+				cause = new IOException(why);
+			throw new TransportException(uri, "cannot execute: "
+					+ commandFor(exe), cause);
+		}
+	}
+
 	/**
 	 * @return the error stream for the channel, the stream is used to detect
 	 *         specific error reasons for exceptions.
@@ -305,6 +319,8 @@ public void run() {
 	class SshFetchConnection extends BasePackFetchConnection {
 		private ChannelExec channel;
 
+		private int exitStatus;
+
 		SshFetchConnection() throws TransportException {
 			super(TransportGitSsh.this);
 			try {
@@ -327,6 +343,8 @@ class SshFetchConnection extends BasePackFetchConnection {
 			try {
 				readAdvertisedRefs();
 			} catch (NoRemoteRepositoryException notFound) {
+				close();
+				checkExecFailure(exitStatus, getOptionUploadPack());
 				throw cleanNotFound(notFound);
 			}
 		}
@@ -337,6 +355,7 @@ public void close() {
 
 			if (channel != null) {
 				try {
+					exitStatus = channel.getExitStatus();
 					if (channel.isConnected())
 						channel.disconnect();
 				} finally {
@@ -349,6 +368,8 @@ public void close() {
 	class SshPushConnection extends BasePackPushConnection {
 		private ChannelExec channel;
 
+		private int exitStatus;
+
 		SshPushConnection() throws TransportException {
 			super(TransportGitSsh.this);
 			try {
@@ -371,6 +392,8 @@ class SshPushConnection extends BasePackPushConnection {
 			try {
 				readAdvertisedRefs();
 			} catch (NoRemoteRepositoryException notFound) {
+				close();
+				checkExecFailure(exitStatus, getOptionReceivePack());
 				throw cleanNotFound(notFound);
 			}
 		}
@@ -381,6 +404,7 @@ public void close() {
 
 			if (channel != null) {
 				try {
+					exitStatus = channel.getExitStatus();
 					if (channel.isConnected())
 						channel.disconnect();
 				} finally {
-- 
GitLab