Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

SshSupport.java 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * Copyright (C) 2018, Markus Duft <markus.duft@ssi-schaefer.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.util;
  11. import java.io.IOException;
  12. import java.text.MessageFormat;
  13. import java.util.concurrent.TimeUnit;
  14. import org.eclipse.jgit.annotations.Nullable;
  15. import org.eclipse.jgit.errors.CommandFailedException;
  16. import org.eclipse.jgit.internal.JGitText;
  17. import org.eclipse.jgit.transport.CredentialsProvider;
  18. import org.eclipse.jgit.transport.RemoteSession;
  19. import org.eclipse.jgit.transport.SshSessionFactory;
  20. import org.eclipse.jgit.transport.URIish;
  21. import org.eclipse.jgit.util.io.MessageWriter;
  22. import org.eclipse.jgit.util.io.StreamCopyThread;
  23. /**
  24. * Extra utilities to support usage of SSH.
  25. *
  26. * @since 5.0
  27. */
  28. public class SshSupport {
  29. /**
  30. * Utility to execute a remote SSH command and read the first line of
  31. * output.
  32. *
  33. * @param sshUri
  34. * the SSH remote URI
  35. * @param provider
  36. * the {@link CredentialsProvider} or <code>null</code>.
  37. * @param fs
  38. * the {@link FS} implementation passed to
  39. * {@link SshSessionFactory}
  40. * @param command
  41. * the remote command to execute.
  42. * @param timeout
  43. * a timeout in seconds. The timeout may be exceeded in corner
  44. * cases.
  45. * @return The entire output read from stdout.
  46. * @throws IOException
  47. * @throws CommandFailedException
  48. * if the ssh command execution failed, error message contains
  49. * the content of stderr.
  50. */
  51. public static String runSshCommand(URIish sshUri,
  52. @Nullable CredentialsProvider provider, FS fs, String command,
  53. int timeout) throws IOException, CommandFailedException {
  54. RemoteSession session = null;
  55. Process process = null;
  56. StreamCopyThread errorThread = null;
  57. StreamCopyThread outThread = null;
  58. CommandFailedException failure = null;
  59. @SuppressWarnings("resource")
  60. MessageWriter stderr = new MessageWriter();
  61. @SuppressWarnings("resource")
  62. MessageWriter stdout = new MessageWriter();
  63. String out;
  64. try {
  65. long start = System.nanoTime();
  66. session = SshSessionFactory.getInstance().getSession(sshUri,
  67. provider, fs, 1000 * timeout);
  68. int commandTimeout = timeout;
  69. if (timeout > 0) {
  70. commandTimeout = checkTimeout(command, timeout, start);
  71. }
  72. process = session.exec(command, commandTimeout);
  73. if (timeout > 0) {
  74. commandTimeout = checkTimeout(command, timeout, start);
  75. }
  76. errorThread = new StreamCopyThread(process.getErrorStream(),
  77. stderr.getRawStream());
  78. errorThread.start();
  79. outThread = new StreamCopyThread(process.getInputStream(),
  80. stdout.getRawStream());
  81. outThread.start();
  82. try {
  83. boolean finished = false;
  84. if (timeout <= 0) {
  85. process.waitFor();
  86. finished = true;
  87. } else {
  88. finished = process.waitFor(commandTimeout,
  89. TimeUnit.SECONDS);
  90. }
  91. if (finished) {
  92. out = stdout.toString();
  93. } else {
  94. out = null; // still running after timeout
  95. }
  96. } catch (InterruptedException e) {
  97. out = null; // error
  98. }
  99. } finally {
  100. if (errorThread != null) {
  101. try {
  102. errorThread.halt();
  103. } catch (InterruptedException e) {
  104. // Stop waiting and return anyway.
  105. } finally {
  106. errorThread = null;
  107. }
  108. }
  109. if (outThread != null) {
  110. try {
  111. outThread.halt();
  112. } catch (InterruptedException e) {
  113. // Stop waiting and return anyway.
  114. } finally {
  115. outThread = null;
  116. }
  117. }
  118. if (process != null) {
  119. try {
  120. if (process.exitValue() != 0) {
  121. failure = new CommandFailedException(
  122. process.exitValue(),
  123. MessageFormat.format(
  124. JGitText.get().sshCommandFailed,
  125. command, stderr.toString()));
  126. }
  127. // It was successful after all
  128. out = stdout.toString();
  129. } catch (IllegalThreadStateException e) {
  130. failure = new CommandFailedException(0,
  131. MessageFormat.format(
  132. JGitText.get().sshCommandTimeout, command,
  133. Integer.valueOf(timeout)));
  134. }
  135. process.destroy();
  136. }
  137. stderr.close();
  138. stdout.close();
  139. if (session != null) {
  140. SshSessionFactory.getInstance().releaseSession(session);
  141. }
  142. }
  143. if (failure != null) {
  144. throw failure;
  145. }
  146. return out;
  147. }
  148. private static int checkTimeout(String command, int timeout, long since)
  149. throws CommandFailedException {
  150. long elapsed = System.nanoTime() - since;
  151. int newTimeout = timeout
  152. - (int) TimeUnit.NANOSECONDS.toSeconds(elapsed);
  153. if (newTimeout <= 0) {
  154. // All time used up for connecting the session
  155. throw new CommandFailedException(0,
  156. MessageFormat.format(JGitText.get().sshCommandTimeout,
  157. command, Integer.valueOf(timeout)));
  158. }
  159. return newTimeout;
  160. }
  161. }