You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TransportGitSsh.java 11KB

Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  4. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  6. * and other copyright owners as documented in the project's IP log.
  7. *
  8. * This program and the accompanying materials are made available
  9. * under the terms of the Eclipse Distribution License v1.0 which
  10. * accompanies this distribution, is reproduced below, and is
  11. * available at http://www.eclipse.org/org/documents/edl-v10.php
  12. *
  13. * All rights reserved.
  14. *
  15. * Redistribution and use in source and binary forms, with or
  16. * without modification, are permitted provided that the following
  17. * conditions are met:
  18. *
  19. * - Redistributions of source code must retain the above copyright
  20. * notice, this list of conditions and the following disclaimer.
  21. *
  22. * - Redistributions in binary form must reproduce the above
  23. * copyright notice, this list of conditions and the following
  24. * disclaimer in the documentation and/or other materials provided
  25. * with the distribution.
  26. *
  27. * - Neither the name of the Eclipse Foundation, Inc. nor the
  28. * names of its contributors may be used to endorse or promote
  29. * products derived from this software without specific prior
  30. * written permission.
  31. *
  32. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  33. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  34. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  36. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  37. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  39. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  41. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  42. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  43. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  44. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  45. */
  46. package org.eclipse.jgit.transport;
  47. import java.io.File;
  48. import java.io.IOException;
  49. import java.io.InputStream;
  50. import java.text.MessageFormat;
  51. import java.util.ArrayList;
  52. import java.util.Arrays;
  53. import java.util.Collections;
  54. import java.util.EnumSet;
  55. import java.util.LinkedHashSet;
  56. import java.util.List;
  57. import java.util.Locale;
  58. import java.util.Set;
  59. import org.eclipse.jgit.errors.NoRemoteRepositoryException;
  60. import org.eclipse.jgit.errors.NotSupportedException;
  61. import org.eclipse.jgit.errors.TransportException;
  62. import org.eclipse.jgit.internal.JGitText;
  63. import org.eclipse.jgit.lib.Constants;
  64. import org.eclipse.jgit.lib.Repository;
  65. import org.eclipse.jgit.util.FS;
  66. import org.eclipse.jgit.util.QuotedString;
  67. import org.eclipse.jgit.util.SystemReader;
  68. import org.eclipse.jgit.util.io.MessageWriter;
  69. import org.eclipse.jgit.util.io.StreamCopyThread;
  70. /**
  71. * Transport through an SSH tunnel.
  72. * <p>
  73. * The SSH transport requires the remote side to have Git installed, as the
  74. * transport logs into the remote system and executes a Git helper program on
  75. * the remote side to read (or write) the remote repository's files.
  76. * <p>
  77. * This transport does not support direct SCP style of copying files, as it
  78. * assumes there are Git specific smarts on the remote side to perform object
  79. * enumeration, save file modification and hook execution.
  80. */
  81. public class TransportGitSsh extends SshTransport implements PackTransport {
  82. static final TransportProtocol PROTO_SSH = new TransportProtocol() {
  83. private final String[] schemeNames = { "ssh", "ssh+git", "git+ssh" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
  84. private final Set<String> schemeSet = Collections
  85. .unmodifiableSet(new LinkedHashSet<>(Arrays
  86. .asList(schemeNames)));
  87. @Override
  88. public String getName() {
  89. return JGitText.get().transportProtoSSH;
  90. }
  91. @Override
  92. public Set<String> getSchemes() {
  93. return schemeSet;
  94. }
  95. @Override
  96. public Set<URIishField> getRequiredFields() {
  97. return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
  98. URIishField.PATH));
  99. }
  100. @Override
  101. public Set<URIishField> getOptionalFields() {
  102. return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
  103. URIishField.PASS, URIishField.PORT));
  104. }
  105. @Override
  106. public int getDefaultPort() {
  107. return 22;
  108. }
  109. @Override
  110. public boolean canHandle(URIish uri, Repository local, String remoteName) {
  111. if (uri.getScheme() == null) {
  112. // scp-style URI "host:path" does not have scheme.
  113. return uri.getHost() != null
  114. && uri.getPath() != null
  115. && uri.getHost().length() != 0
  116. && uri.getPath().length() != 0;
  117. }
  118. return super.canHandle(uri, local, remoteName);
  119. }
  120. @Override
  121. public Transport open(URIish uri, Repository local, String remoteName)
  122. throws NotSupportedException {
  123. return new TransportGitSsh(local, uri);
  124. }
  125. @Override
  126. public Transport open(URIish uri) throws NotSupportedException, TransportException {
  127. return new TransportGitSsh(uri);
  128. }
  129. };
  130. TransportGitSsh(Repository local, URIish uri) {
  131. super(local, uri);
  132. initSshSessionFactory();
  133. }
  134. TransportGitSsh(URIish uri) {
  135. super(uri);
  136. initSshSessionFactory();
  137. }
  138. private void initSshSessionFactory() {
  139. if (useExtSession()) {
  140. setSshSessionFactory(new SshSessionFactory() {
  141. @Override
  142. public RemoteSession getSession(URIish uri2,
  143. CredentialsProvider credentialsProvider, FS fs, int tms)
  144. throws TransportException {
  145. return new ExtSession();
  146. }
  147. });
  148. }
  149. }
  150. /** {@inheritDoc} */
  151. @Override
  152. public FetchConnection openFetch() throws TransportException {
  153. return new SshFetchConnection();
  154. }
  155. /** {@inheritDoc} */
  156. @Override
  157. public PushConnection openPush() throws TransportException {
  158. return new SshPushConnection();
  159. }
  160. String commandFor(String exe) {
  161. String path = uri.getPath();
  162. if (uri.getScheme() != null && uri.getPath().startsWith("/~")) //$NON-NLS-1$
  163. path = (uri.getPath().substring(1));
  164. final StringBuilder cmd = new StringBuilder();
  165. cmd.append(exe);
  166. cmd.append(' ');
  167. cmd.append(QuotedString.BOURNE.quote(path));
  168. return cmd.toString();
  169. }
  170. void checkExecFailure(int status, String exe, String why)
  171. throws TransportException {
  172. if (status == 127) {
  173. IOException cause = null;
  174. if (why != null && why.length() > 0)
  175. cause = new IOException(why);
  176. throw new TransportException(uri, MessageFormat.format(
  177. JGitText.get().cannotExecute, commandFor(exe)), cause);
  178. }
  179. }
  180. NoRemoteRepositoryException cleanNotFound(NoRemoteRepositoryException nf,
  181. String why) {
  182. if (why == null || why.length() == 0)
  183. return nf;
  184. String path = uri.getPath();
  185. if (uri.getScheme() != null && uri.getPath().startsWith("/~")) //$NON-NLS-1$
  186. path = uri.getPath().substring(1);
  187. final StringBuilder pfx = new StringBuilder();
  188. pfx.append("fatal: "); //$NON-NLS-1$
  189. pfx.append(QuotedString.BOURNE.quote(path));
  190. pfx.append(": "); //$NON-NLS-1$
  191. if (why.startsWith(pfx.toString()))
  192. why = why.substring(pfx.length());
  193. return new NoRemoteRepositoryException(uri, why);
  194. }
  195. private static boolean useExtSession() {
  196. return SystemReader.getInstance().getenv("GIT_SSH") != null; //$NON-NLS-1$
  197. }
  198. private class ExtSession implements RemoteSession {
  199. @Override
  200. public Process exec(String command, int timeout)
  201. throws TransportException {
  202. String ssh = SystemReader.getInstance().getenv("GIT_SSH"); //$NON-NLS-1$
  203. boolean putty = ssh.toLowerCase(Locale.ROOT).contains("plink"); //$NON-NLS-1$
  204. List<String> args = new ArrayList<>();
  205. args.add(ssh);
  206. if (putty
  207. && !ssh.toLowerCase(Locale.ROOT).contains("tortoiseplink")) //$NON-NLS-1$
  208. args.add("-batch"); //$NON-NLS-1$
  209. if (0 < getURI().getPort()) {
  210. args.add(putty ? "-P" : "-p"); //$NON-NLS-1$ //$NON-NLS-2$
  211. args.add(String.valueOf(getURI().getPort()));
  212. }
  213. if (getURI().getUser() != null)
  214. args.add(getURI().getUser() + "@" + getURI().getHost()); //$NON-NLS-1$
  215. else
  216. args.add(getURI().getHost());
  217. args.add(command);
  218. ProcessBuilder pb = createProcess(args);
  219. try {
  220. return pb.start();
  221. } catch (IOException err) {
  222. throw new TransportException(err.getMessage(), err);
  223. }
  224. }
  225. private ProcessBuilder createProcess(List<String> args) {
  226. ProcessBuilder pb = new ProcessBuilder();
  227. pb.command(args);
  228. File directory = local != null ? local.getDirectory() : null;
  229. if (directory != null) {
  230. pb.environment().put(Constants.GIT_DIR_KEY,
  231. directory.getPath());
  232. }
  233. return pb;
  234. }
  235. @Override
  236. public void disconnect() {
  237. // Nothing to do
  238. }
  239. }
  240. class SshFetchConnection extends BasePackFetchConnection {
  241. private final Process process;
  242. private StreamCopyThread errorThread;
  243. SshFetchConnection() throws TransportException {
  244. super(TransportGitSsh.this);
  245. try {
  246. process = getSession().exec(commandFor(getOptionUploadPack()),
  247. getTimeout());
  248. final MessageWriter msg = new MessageWriter();
  249. setMessageWriter(msg);
  250. final InputStream upErr = process.getErrorStream();
  251. errorThread = new StreamCopyThread(upErr, msg.getRawStream());
  252. errorThread.start();
  253. init(process.getInputStream(), process.getOutputStream());
  254. } catch (TransportException err) {
  255. close();
  256. throw err;
  257. } catch (Throwable err) {
  258. close();
  259. throw new TransportException(uri,
  260. JGitText.get().remoteHungUpUnexpectedly, err);
  261. }
  262. try {
  263. readAdvertisedRefs();
  264. } catch (NoRemoteRepositoryException notFound) {
  265. final String msgs = getMessages();
  266. checkExecFailure(process.exitValue(), getOptionUploadPack(),
  267. msgs);
  268. throw cleanNotFound(notFound, msgs);
  269. }
  270. }
  271. @Override
  272. public void close() {
  273. endOut();
  274. if (process != null) {
  275. process.destroy();
  276. }
  277. if (errorThread != null) {
  278. try {
  279. errorThread.halt();
  280. } catch (InterruptedException e) {
  281. // Stop waiting and return anyway.
  282. } finally {
  283. errorThread = null;
  284. }
  285. }
  286. super.close();
  287. }
  288. }
  289. class SshPushConnection extends BasePackPushConnection {
  290. private final Process process;
  291. private StreamCopyThread errorThread;
  292. SshPushConnection() throws TransportException {
  293. super(TransportGitSsh.this);
  294. try {
  295. process = getSession().exec(commandFor(getOptionReceivePack()),
  296. getTimeout());
  297. final MessageWriter msg = new MessageWriter();
  298. setMessageWriter(msg);
  299. final InputStream rpErr = process.getErrorStream();
  300. errorThread = new StreamCopyThread(rpErr, msg.getRawStream());
  301. errorThread.start();
  302. init(process.getInputStream(), process.getOutputStream());
  303. } catch (TransportException err) {
  304. try {
  305. close();
  306. } catch (Exception e) {
  307. // ignore
  308. }
  309. throw err;
  310. } catch (Throwable err) {
  311. try {
  312. close();
  313. } catch (Exception e) {
  314. // ignore
  315. }
  316. throw new TransportException(uri,
  317. JGitText.get().remoteHungUpUnexpectedly, err);
  318. }
  319. try {
  320. readAdvertisedRefs();
  321. } catch (NoRemoteRepositoryException notFound) {
  322. final String msgs = getMessages();
  323. checkExecFailure(process.exitValue(), getOptionReceivePack(),
  324. msgs);
  325. throw cleanNotFound(notFound, msgs);
  326. }
  327. }
  328. @Override
  329. public void close() {
  330. endOut();
  331. if (process != null) {
  332. process.destroy();
  333. }
  334. if (errorThread != null) {
  335. try {
  336. errorThread.halt();
  337. } catch (InterruptedException e) {
  338. // Stop waiting and return anyway.
  339. } finally {
  340. errorThread = null;
  341. }
  342. }
  343. super.close();
  344. }
  345. }
  346. }